diff options
author | Carlos Martín Nieto <cmn@dwim.me> | 2015-05-28 18:45:57 +0200 |
---|---|---|
committer | Carlos Martín Nieto <cmn@dwim.me> | 2015-05-28 18:45:57 +0200 |
commit | ff8d635adbf1208927c7319178d29b0ed973a107 (patch) | |
tree | 329dd098bfefdadf9115c4fa4cff4beddd67c696 | |
parent | fb92b48d541edde845e8cde176152f13592db71b (diff) | |
parent | 9b3e41f72bd9ab62e8d129713755c3a3a71359bc (diff) | |
download | libgit2-ff8d635adbf1208927c7319178d29b0ed973a107.tar.gz |
Merge pull request #3139 from ethomson/diff_conflicts
Include conflicts when diffing
-rw-r--r-- | CHANGELOG.md | 19 | ||||
-rw-r--r-- | include/git2/diff.h | 22 | ||||
-rw-r--r-- | include/git2/index.h | 12 | ||||
-rw-r--r-- | include/git2/status.h | 1 | ||||
-rw-r--r-- | src/diff.c | 274 | ||||
-rw-r--r-- | src/index.c | 39 | ||||
-rw-r--r-- | src/iterator.c | 18 | ||||
-rw-r--r-- | src/iterator.h | 2 | ||||
-rw-r--r-- | src/merge.c | 4 | ||||
-rw-r--r-- | src/reset.c | 1 | ||||
-rw-r--r-- | src/status.c | 6 | ||||
-rw-r--r-- | tests/checkout/conflict.c | 2 | ||||
-rw-r--r-- | tests/checkout/index.c | 6 | ||||
-rw-r--r-- | tests/checkout/tree.c | 6 | ||||
-rw-r--r-- | tests/clar_libgit2.c | 8 | ||||
-rw-r--r-- | tests/clar_libgit2.h | 1 | ||||
-rw-r--r-- | tests/diff/diff_helpers.c | 2 | ||||
-rw-r--r-- | tests/diff/diff_helpers.h | 2 | ||||
-rw-r--r-- | tests/diff/index.c | 76 | ||||
-rw-r--r-- | tests/diff/workdir.c | 45 | ||||
-rw-r--r-- | tests/index/addall.c | 153 | ||||
-rw-r--r-- | tests/index/collision.c | 10 | ||||
-rw-r--r-- | tests/index/conflicts.c | 75 | ||||
-rw-r--r-- | tests/merge/trees/trivial.c | 2 | ||||
-rw-r--r-- | tests/merge/workdir/trivial.c | 2 | ||||
-rw-r--r-- | tests/object/tree/duplicateentries.c | 6 | ||||
-rw-r--r-- | tests/reset/hard.c | 2 | ||||
-rw-r--r-- | tests/status/worktree.c | 56 |
28 files changed, 675 insertions, 177 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 19463420e..99e375902 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,13 @@ support for HTTPS connections insead of OpenSSL. the error message, which allows you to get the "repository not found" messages. +* `git_index_conflict_add()` will remove staged entries that exist for + conflicted paths. + +* The flags for a `git_diff_file` will now have the `GIT_DIFF_FLAG_EXISTS` + bit set when a file exists on that side of the diff. This is useful + for understanding whether a side of the diff exists in the presence of + a conflict. ### API additions @@ -98,6 +105,18 @@ support for HTTPS connections insead of OpenSSL. configuration of the server, and tools can use this to show messages about failing to communicate with the server. +* `git_diff_index_to_workdir()` and `git_diff_tree_to_index()` will now + produce deltas of type `GIT_DELTA_CONFLICTED` to indicate that the index + side of the delta is a conflict. + +* The `git_status` family of functions will now produce status of type + `GIT_STATUS_CONFLICTED` to indicate that a conflict exists for that file + in the index. + +* `git_index_entry_is_conflict()` is a utility function to determine if + a given index entry has a non-zero stage entry, indicating that it is + one side of a conflict. + ### API removals * `git_remote_save()` and `git_remote_clear_refspecs()` have been diff --git a/include/git2/diff.h b/include/git2/diff.h index cac3b268a..c0d42e30e 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -226,6 +226,7 @@ typedef enum { GIT_DIFF_FLAG_BINARY = (1u << 0), /**< file(s) treated as binary data */ GIT_DIFF_FLAG_NOT_BINARY = (1u << 1), /**< file(s) treated as text data */ GIT_DIFF_FLAG_VALID_ID = (1u << 2), /**< `id` value is known correct */ + GIT_DIFF_FLAG_EXISTS = (1u << 3), /**< file exists at this side of the delta */ } git_diff_flag_t; /** @@ -239,16 +240,17 @@ typedef enum { * DELETED pairs). */ typedef enum { - GIT_DELTA_UNMODIFIED = 0, /**< no changes */ - GIT_DELTA_ADDED = 1, /**< entry does not exist in old version */ - GIT_DELTA_DELETED = 2, /**< entry does not exist in new version */ - GIT_DELTA_MODIFIED = 3, /**< entry content changed between old and new */ - GIT_DELTA_RENAMED = 4, /**< entry was renamed between old and new */ - GIT_DELTA_COPIED = 5, /**< entry was copied from another old entry */ - GIT_DELTA_IGNORED = 6, /**< entry is ignored item in workdir */ - GIT_DELTA_UNTRACKED = 7, /**< entry is untracked item in workdir */ - GIT_DELTA_TYPECHANGE = 8, /**< type of entry changed between old and new */ - GIT_DELTA_UNREADABLE = 9, /**< entry is unreadable */ + GIT_DELTA_UNMODIFIED = 0, /**< no changes */ + GIT_DELTA_ADDED = 1, /**< entry does not exist in old version */ + GIT_DELTA_DELETED = 2, /**< entry does not exist in new version */ + GIT_DELTA_MODIFIED = 3, /**< entry content changed between old and new */ + GIT_DELTA_RENAMED = 4, /**< entry was renamed between old and new */ + GIT_DELTA_COPIED = 5, /**< entry was copied from another old entry */ + GIT_DELTA_IGNORED = 6, /**< entry is ignored item in workdir */ + GIT_DELTA_UNTRACKED = 7, /**< entry is untracked item in workdir */ + GIT_DELTA_TYPECHANGE = 8, /**< type of entry changed between old and new */ + GIT_DELTA_UNREADABLE = 9, /**< entry is unreadable */ + GIT_DELTA_CONFLICTED = 10, /**< entry in the index is conflicted */ } git_delta_t; /** diff --git a/include/git2/index.h b/include/git2/index.h index 52032f7fd..49bbe1614 100644 --- a/include/git2/index.h +++ b/include/git2/index.h @@ -430,6 +430,15 @@ GIT_EXTERN(int) git_index_add(git_index *index, const git_index_entry *source_en */ GIT_EXTERN(int) git_index_entry_stage(const git_index_entry *entry); +/** + * Return whether the given index entry is a conflict (has a high stage + * entry). This is simply shorthand for `git_index_entry_stage > 0`. + * + * @param entry The entry + * @return 1 if the entry is a conflict entry, 0 otherwise + */ +GIT_EXTERN(int) git_index_entry_is_conflict(const git_index_entry *entry); + /**@}*/ /** @name Workdir Index Entry Functions @@ -631,7 +640,8 @@ GIT_EXTERN(int) git_index_find(size_t *at_pos, git_index *index, const char *pat /**@{*/ /** - * Add or update index entries to represent a conflict + * Add or update index entries to represent a conflict. Any staged + * entries that exist at the given paths will be removed. * * The entries are the entries from the tree included in the merge. Any * entry may be null to indicate that that file was not present in the diff --git a/include/git2/status.h b/include/git2/status.h index 5f211810d..671113955 100644 --- a/include/git2/status.h +++ b/include/git2/status.h @@ -46,6 +46,7 @@ typedef enum { GIT_STATUS_WT_UNREADABLE = (1u << 12), GIT_STATUS_IGNORED = (1u << 14), + GIT_STATUS_CONFLICTED = (1u << 15), } git_status_t; /** diff --git a/src/diff.c b/src/diff.c index f7e1c8ee4..c9d1d8730 100644 --- a/src/diff.c +++ b/src/diff.c @@ -77,11 +77,24 @@ static int diff_insert_delta( static int diff_delta__from_one( git_diff *diff, git_delta_t status, - const git_index_entry *entry) + const git_index_entry *oitem, + const git_index_entry *nitem) { + const git_index_entry *entry = nitem; + bool has_old = false; git_diff_delta *delta; const char *matched_pathspec; + assert((oitem != NULL) ^ (nitem != NULL)); + + if (oitem) { + entry = oitem; + has_old = true; + } + + if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) + has_old = !has_old; + if ((entry->flags & GIT_IDXENTRY_VALID) != 0) return 0; @@ -111,20 +124,21 @@ static int diff_delta__from_one( assert(status != GIT_DELTA_MODIFIED); delta->nfiles = 1; - if (delta->status == GIT_DELTA_DELETED) { + if (has_old) { delta->old_file.mode = entry->mode; delta->old_file.size = entry->file_size; + delta->old_file.flags |= GIT_DIFF_FLAG_EXISTS; git_oid_cpy(&delta->old_file.id, &entry->id); } else /* ADDED, IGNORED, UNTRACKED */ { delta->new_file.mode = entry->mode; delta->new_file.size = entry->file_size; + delta->new_file.flags |= GIT_DIFF_FLAG_EXISTS; git_oid_cpy(&delta->new_file.id, &entry->id); } delta->old_file.flags |= GIT_DIFF_FLAG_VALID_ID; - if (delta->status == GIT_DELTA_DELETED || - !git_oid_iszero(&delta->new_file.id)) + if (has_old || !git_oid_iszero(&delta->new_file.id)) delta->new_file.flags |= GIT_DIFF_FLAG_VALID_ID; return diff_insert_delta(diff, delta, matched_pathspec); @@ -137,9 +151,10 @@ static int diff_delta__from_two( uint32_t old_mode, const git_index_entry *new_entry, uint32_t new_mode, - git_oid *new_oid, + const git_oid *new_id, const char *matched_pathspec) { + const git_oid *old_id = &old_entry->id; git_diff_delta *delta; const char *canonical_path = old_entry->path; @@ -147,38 +162,45 @@ static int diff_delta__from_two( DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNMODIFIED)) return 0; + if (!new_id) + new_id = &new_entry->id; + if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) { uint32_t temp_mode = old_mode; const git_index_entry *temp_entry = old_entry; + const git_oid *temp_id = old_id; + old_entry = new_entry; new_entry = temp_entry; old_mode = new_mode; new_mode = temp_mode; + old_id = new_id; + new_id = temp_id; } delta = diff_delta__alloc(diff, status, canonical_path); GITERR_CHECK_ALLOC(delta); delta->nfiles = 2; - git_oid_cpy(&delta->old_file.id, &old_entry->id); - delta->old_file.size = old_entry->file_size; - delta->old_file.mode = old_mode; - delta->old_file.flags |= GIT_DIFF_FLAG_VALID_ID; + if (!git_index_entry_is_conflict(old_entry)) { + delta->old_file.size = old_entry->file_size; + delta->old_file.mode = old_mode; + git_oid_cpy(&delta->old_file.id, old_id); + delta->old_file.flags |= GIT_DIFF_FLAG_VALID_ID | + GIT_DIFF_FLAG_EXISTS; + } - git_oid_cpy(&delta->new_file.id, &new_entry->id); - delta->new_file.size = new_entry->file_size; - delta->new_file.mode = new_mode; + if (!git_index_entry_is_conflict(new_entry)) { + git_oid_cpy(&delta->new_file.id, new_id); + delta->new_file.size = new_entry->file_size; + delta->new_file.mode = new_mode; + delta->old_file.flags |= GIT_DIFF_FLAG_EXISTS; + delta->new_file.flags |= GIT_DIFF_FLAG_EXISTS; - if (new_oid) { - if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) - git_oid_cpy(&delta->old_file.id, new_oid); - else - git_oid_cpy(&delta->new_file.id, new_oid); + if (!git_oid_iszero(&new_entry->id)) + delta->new_file.flags |= GIT_DIFF_FLAG_VALID_ID; } - if (new_oid || !git_oid_iszero(&new_entry->id)) - delta->new_file.flags |= GIT_DIFF_FLAG_VALID_ID; - return diff_insert_delta(diff, delta, matched_pathspec); } @@ -327,6 +349,22 @@ static const char *diff_mnemonic_prefix( return pfx; } +static int diff_entry_cmp(const void *a, const void *b) +{ + const git_index_entry *entry_a = a; + const git_index_entry *entry_b = b; + + return strcmp(entry_a->path, entry_b->path); +} + +static int diff_entry_icmp(const void *a, const void *b) +{ + const git_index_entry *entry_a = a; + const git_index_entry *entry_b = b; + + return strcasecmp(entry_a->path, entry_b->path); +} + static void diff_set_ignore_case(git_diff *diff, bool ignore_case) { if (!ignore_case) { @@ -335,7 +373,7 @@ static void diff_set_ignore_case(git_diff *diff, bool ignore_case) diff->strcomp = git__strcmp; diff->strncomp = git__strncmp; diff->pfxcomp = git__prefixcmp; - diff->entrycomp = git_index_entry_cmp; + diff->entrycomp = diff_entry_cmp; git_vector_set_cmp(&diff->deltas, git_diff_delta__cmp); } else { @@ -344,7 +382,7 @@ static void diff_set_ignore_case(git_diff *diff, bool ignore_case) diff->strcomp = git__strcasecmp; diff->strncomp = git__strncasecmp; diff->pfxcomp = git__prefixcmp_icase; - diff->entrycomp = git_index_entry_icmp; + diff->entrycomp = diff_entry_icmp; git_vector_set_cmp(&diff->deltas, git_diff_delta__casecmp); } @@ -731,40 +769,47 @@ static int maybe_modified( new_is_workdir) nmode = (nmode & ~MODE_BITS_MASK) | (omode & MODE_BITS_MASK); + /* if one side is a conflict, mark the whole delta as conflicted */ + if (git_index_entry_is_conflict(oitem) || + git_index_entry_is_conflict(nitem)) { + status = GIT_DELTA_CONFLICTED; + /* support "assume unchanged" (poorly, b/c we still stat everything) */ - if ((oitem->flags & GIT_IDXENTRY_VALID) != 0) + } else if ((oitem->flags & GIT_IDXENTRY_VALID) != 0) { status = GIT_DELTA_UNMODIFIED; /* support "skip worktree" index bit */ - else if ((oitem->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE) != 0) + } else if ((oitem->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE) != 0) { status = GIT_DELTA_UNMODIFIED; /* if basic type of file changed, then split into delete and add */ - else if (GIT_MODE_TYPE(omode) != GIT_MODE_TYPE(nmode)) { - if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE)) + } else if (GIT_MODE_TYPE(omode) != GIT_MODE_TYPE(nmode)) { + if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE)) { status = GIT_DELTA_TYPECHANGE; + } + else if (nmode == GIT_FILEMODE_UNREADABLE) { - if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem))) - error = diff_delta__from_one(diff, GIT_DELTA_UNREADABLE, nitem); + if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem, NULL))) + error = diff_delta__from_one(diff, GIT_DELTA_UNREADABLE, NULL, nitem); return error; } + else { - if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem))) - error = diff_delta__from_one(diff, GIT_DELTA_ADDED, nitem); + if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem, NULL))) + error = diff_delta__from_one(diff, GIT_DELTA_ADDED, NULL, nitem); return error; } - } /* if oids and modes match (and are valid), then file is unmodified */ - else if (git_oid_equal(&oitem->id, &nitem->id) && + } else if (git_oid_equal(&oitem->id, &nitem->id) && omode == nmode && - !git_oid_iszero(&oitem->id)) + !git_oid_iszero(&oitem->id)) { status = GIT_DELTA_UNMODIFIED; /* if we have an unknown OID and a workdir iterator, then check some * circumstances that can accelerate things or need special handling */ - else if (git_oid_iszero(&nitem->id) && new_is_workdir) { + } else if (git_oid_iszero(&nitem->id) && new_is_workdir) { bool use_ctime = ((diff->diffcaps & GIT_DIFFCAPS_TRUST_CTIME) != 0); bool use_nanos = ((diff->diffcaps & GIT_DIFFCAPS_TRUST_NANOSECS) != 0); @@ -795,12 +840,12 @@ static int maybe_modified( status = GIT_DELTA_MODIFIED; modified_uncertain = true; } - } /* if mode is GITLINK and submodules are ignored, then skip */ - else if (S_ISGITLINK(nmode) && - DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES)) + } else if (S_ISGITLINK(nmode) && + DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES)) { status = GIT_DELTA_UNMODIFIED; + } /* if we got here and decided that the files are modified, but we * haven't calculated the OID of the new item, then calculate it now @@ -809,6 +854,7 @@ static int maybe_modified( const git_oid *update_check = DIFF_FLAG_IS_SET(diff, GIT_DIFF_UPDATE_INDEX) && omode == nmode ? &oitem->id : NULL; + if ((error = git_diff__oid_for_entry( &noid, diff, nitem, update_check)) < 0) return error; @@ -830,8 +876,9 @@ static int maybe_modified( DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_CASECHANGE) && strcmp(oitem->path, nitem->path) != 0) { - if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem))) - error = diff_delta__from_one(diff, GIT_DELTA_ADDED, nitem); + if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem, NULL))) + error = diff_delta__from_one(diff, GIT_DELTA_ADDED, NULL, nitem); + return error; } @@ -857,6 +904,84 @@ static bool entry_is_prefixed( item->path[pathlen] == '/'); } +static int iterator_current( + const git_index_entry **entry, + git_iterator *iterator) +{ + int error; + + if ((error = git_iterator_current(entry, iterator)) == GIT_ITEROVER) { + *entry = NULL; + error = 0; + } + + return error; +} + +static int iterator_advance( + const git_index_entry **entry, + git_iterator *iterator) +{ + const git_index_entry *prev_entry = *entry; + int cmp, error; + + /* if we're looking for conflicts, we only want to report + * one conflict for each file, instead of all three sides. + * so if this entry is a conflict for this file, and the + * previous one was a conflict for the same file, skip it. + */ + while ((error = git_iterator_advance(entry, iterator)) == 0) { + if (!(iterator->flags & GIT_ITERATOR_INCLUDE_CONFLICTS) || + !git_index_entry_is_conflict(prev_entry) || + !git_index_entry_is_conflict(*entry)) + break; + + cmp = (iterator->flags & GIT_ITERATOR_IGNORE_CASE) ? + strcasecmp(prev_entry->path, (*entry)->path) : + strcmp(prev_entry->path, (*entry)->path); + + if (cmp) + break; + } + + if (error == GIT_ITEROVER) { + *entry = NULL; + error = 0; + } + + return error; +} + +static int iterator_advance_into( + const git_index_entry **entry, + git_iterator *iterator) +{ + int error; + + if ((error = git_iterator_advance_into(entry, iterator)) == GIT_ITEROVER) { + *entry = NULL; + error = 0; + } + + return error; +} + +static int iterator_advance_over_with_status( + const git_index_entry **entry, + git_iterator_status_t *status, + git_iterator *iterator) +{ + int error; + + if ((error = git_iterator_advance_over_with_status( + entry, status, iterator)) == GIT_ITEROVER) { + *entry = NULL; + error = 0; + } + + return error; +} + static int handle_unmatched_new_item( git_diff *diff, diff_in_progress *info) { @@ -868,8 +993,12 @@ static int handle_unmatched_new_item( /* check if this is a prefix of the other side */ contains_oitem = entry_is_prefixed(diff, info->oitem, nitem); + /* update delta_type if this item is conflicted */ + if (git_index_entry_is_conflict(nitem)) + delta_type = GIT_DELTA_CONFLICTED; + /* update delta_type if this item is ignored */ - if (git_iterator_current_is_ignored(info->new_iter)) + else if (git_iterator_current_is_ignored(info->new_iter)) delta_type = GIT_DELTA_IGNORED; if (nitem->mode == GIT_FILEMODE_TREE) { @@ -904,18 +1033,17 @@ static int handle_unmatched_new_item( git_iterator_status_t untracked_state; /* attempt to insert record for this directory */ - if ((error = diff_delta__from_one(diff, delta_type, nitem)) != 0) + if ((error = diff_delta__from_one(diff, delta_type, NULL, nitem)) != 0) return error; /* if delta wasn't created (because of rules), just skip ahead */ last = diff_delta__last_for_item(diff, nitem); if (!last) - return git_iterator_advance(&info->nitem, info->new_iter); + return iterator_advance(&info->nitem, info->new_iter); /* iterate into dir looking for an actual untracked file */ - if ((error = git_iterator_advance_over_with_status( - &info->nitem, &untracked_state, info->new_iter)) < 0 && - error != GIT_ITEROVER) + if ((error = iterator_advance_over_with_status( + &info->nitem, &untracked_state, info->new_iter)) < 0) return error; /* if we found nothing or just ignored items, update the record */ @@ -935,7 +1063,7 @@ static int handle_unmatched_new_item( /* try to advance into directory if necessary */ if (recurse_into_dir) { - error = git_iterator_advance_into(&info->nitem, info->new_iter); + error = iterator_advance_into(&info->nitem, info->new_iter); /* if real error or no error, proceed with iteration */ if (error != GIT_ENOTFOUND) @@ -946,7 +1074,7 @@ static int handle_unmatched_new_item( * it or ignore it */ if (contains_oitem) - return git_iterator_advance(&info->nitem, info->new_iter); + return iterator_advance(&info->nitem, info->new_iter); delta_type = GIT_DELTA_IGNORED; } } @@ -955,7 +1083,7 @@ static int handle_unmatched_new_item( DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS) && git_iterator_current_tree_is_ignored(info->new_iter)) /* item contained in ignored directory, so skip over it */ - return git_iterator_advance(&info->nitem, info->new_iter); + return iterator_advance(&info->nitem, info->new_iter); else if (info->new_iter->type != GIT_ITERATOR_TYPE_WORKDIR) delta_type = GIT_DELTA_ADDED; @@ -968,12 +1096,12 @@ static int handle_unmatched_new_item( /* if this contains a tracked item, treat as normal TREE */ if (contains_oitem) { - error = git_iterator_advance_into(&info->nitem, info->new_iter); + error = iterator_advance_into(&info->nitem, info->new_iter); if (error != GIT_ENOTFOUND) return error; giterr_clear(); - return git_iterator_advance(&info->nitem, info->new_iter); + return iterator_advance(&info->nitem, info->new_iter); } } } @@ -986,7 +1114,7 @@ static int handle_unmatched_new_item( } /* Actually create the record for this item if necessary */ - if ((error = diff_delta__from_one(diff, delta_type, nitem)) != 0) + if ((error = diff_delta__from_one(diff, delta_type, NULL, nitem)) != 0) return error; /* If user requested TYPECHANGE records, then check for that instead of @@ -1004,14 +1132,20 @@ static int handle_unmatched_new_item( } } - return git_iterator_advance(&info->nitem, info->new_iter); + return iterator_advance(&info->nitem, info->new_iter); } static int handle_unmatched_old_item( git_diff *diff, diff_in_progress *info) { - int error = diff_delta__from_one(diff, GIT_DELTA_DELETED, info->oitem); - if (error != 0) + git_delta_t delta_type = GIT_DELTA_DELETED; + int error; + + /* update delta_type if this item is conflicted */ + if (git_index_entry_is_conflict(info->oitem)) + delta_type = GIT_DELTA_CONFLICTED; + + if ((error = diff_delta__from_one(diff, delta_type, info->oitem, NULL)) < 0) return error; /* if we are generating TYPECHANGE records then check for that @@ -1033,10 +1167,10 @@ static int handle_unmatched_old_item( */ if (S_ISDIR(info->nitem->mode) && DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_UNTRACKED_DIRS)) - return git_iterator_advance(&info->nitem, info->new_iter); + return iterator_advance(&info->nitem, info->new_iter); } - return git_iterator_advance(&info->oitem, info->old_iter); + return iterator_advance(&info->oitem, info->old_iter); } static int handle_matched_item( @@ -1047,9 +1181,8 @@ static int handle_matched_item( if ((error = maybe_modified(diff, info)) < 0) return error; - if (!(error = git_iterator_advance(&info->oitem, info->old_iter)) || - error == GIT_ITEROVER) - error = git_iterator_advance(&info->nitem, info->new_iter); + if (!(error = iterator_advance(&info->oitem, info->old_iter))) + error = iterator_advance(&info->nitem, info->new_iter); return error; } @@ -1085,13 +1218,9 @@ int git_diff__from_iterators( if ((error = diff_list_apply_options(diff, opts)) < 0) goto cleanup; - if ((error = git_iterator_current(&info.oitem, old_iter)) < 0 && - error != GIT_ITEROVER) + if ((error = iterator_current(&info.oitem, old_iter)) < 0 || + (error = iterator_current(&info.nitem, new_iter)) < 0) goto cleanup; - if ((error = git_iterator_current(&info.nitem, new_iter)) < 0 && - error != GIT_ITEROVER) - goto cleanup; - error = 0; /* run iterators building diffs */ while (!error && (info.oitem || info.nitem)) { @@ -1113,10 +1242,6 @@ int git_diff__from_iterators( */ else error = handle_matched_item(diff, &info); - - /* because we are iterating over two lists, ignore ITEROVER */ - if (error == GIT_ITEROVER) - error = 0; } diff->perf.stat_calls += old_iter->stat_calls + new_iter->stat_calls; @@ -1186,6 +1311,8 @@ int git_diff_tree_to_index( { int error = 0; bool index_ignore_case = false; + git_iterator_flag_t iflag = GIT_ITERATOR_DONT_IGNORE_CASE | + GIT_ITERATOR_INCLUDE_CONFLICTS; assert(diff && repo); @@ -1195,10 +1322,8 @@ int git_diff_tree_to_index( index_ignore_case = index->ignore_case; DIFF_FROM_ITERATORS( - git_iterator_for_tree( - &a, old_tree, GIT_ITERATOR_DONT_IGNORE_CASE, pfx, pfx), - git_iterator_for_index( - &b, index, GIT_ITERATOR_DONT_IGNORE_CASE, pfx, pfx) + git_iterator_for_tree(&a, old_tree, iflag, pfx, pfx), + git_iterator_for_index(&b, index, iflag, pfx, pfx) ); /* if index is in case-insensitive order, re-sort deltas to match */ @@ -1222,7 +1347,8 @@ int git_diff_index_to_workdir( return error; DIFF_FROM_ITERATORS( - git_iterator_for_index(&a, index, 0, pfx, pfx), + git_iterator_for_index( + &a, index, GIT_ITERATOR_INCLUDE_CONFLICTS, pfx, pfx), git_iterator_for_workdir( &b, repo, index, NULL, GIT_ITERATOR_DONT_AUTOEXPAND, pfx, pfx) ); diff --git a/src/index.c b/src/index.c index 561fe73c4..0422ff1d4 100644 --- a/src/index.c +++ b/src/index.c @@ -1173,6 +1173,9 @@ int git_index_remove_bypath(git_index *index, const char *path) ret != GIT_ENOTFOUND)) return ret; + if (ret == GIT_ENOTFOUND) + giterr_clear(); + return 0; } @@ -1314,6 +1317,30 @@ int git_index_conflict_add(git_index *index, (ret = index_entry_dup(&entries[2], INDEX_OWNER(index), their_entry)) < 0) goto on_error; + /* Validate entries */ + for (i = 0; i < 3; i++) { + if (entries[i] && !valid_filemode(entries[i]->mode)) { + giterr_set(GITERR_INDEX, "invalid filemode for stage %d entry", + i); + return -1; + } + } + + /* Remove existing index entries for each path */ + for (i = 0; i < 3; i++) { + if (entries[i] == NULL) + continue; + + if ((ret = git_index_remove(index, entries[i]->path, 0)) != 0) { + if (ret != GIT_ENOTFOUND) + goto on_error; + + giterr_clear(); + ret = 0; + } + } + + /* Add the conflict entries */ for (i = 0; i < 3; i++) { if (entries[i] == NULL) continue; @@ -1321,7 +1348,7 @@ int git_index_conflict_add(git_index *index, /* Make sure stage is correct */ GIT_IDXENTRY_STAGE_SET(entries[i], i + 1); - if ((ret = index_insert(index, &entries[i], 1, true)) < 0) + if ((ret = index_insert(index, &entries[i], 0, true)) < 0) goto on_error; entries[i] = NULL; /* don't free if later entry fails */ @@ -1510,7 +1537,7 @@ int git_index_conflict_next( while (iterator->cur < iterator->index->entries.length) { entry = git_index_get_byindex(iterator->index, iterator->cur); - if (git_index_entry_stage(entry) > 0) { + if (git_index_entry_is_conflict(entry)) { if ((len = index_conflict__get_byindex( ancestor_out, our_out, @@ -2356,6 +2383,11 @@ int git_index_entry_stage(const git_index_entry *entry) return GIT_IDXENTRY_STAGE(entry); } +int git_index_entry_is_conflict(const git_index_entry *entry) +{ + return (GIT_IDXENTRY_STAGE(entry) > 0); +} + typedef struct read_tree_data { git_index *index; git_vector *old_entries; @@ -2638,7 +2670,8 @@ static int apply_each_file(const git_diff_delta *delta, float progress, void *pa if (error < 0) /* actual error */ return error; - if (delta->status == GIT_DELTA_DELETED) + /* If the workdir item does not exist, remove it from the index. */ + if ((delta->new_file.flags & GIT_DIFF_FLAG_EXISTS) == 0) error = git_index_remove_bypath(data->index, path); else error = git_index_add_bypath(data->index, delta->new_file.path); diff --git a/src/iterator.c b/src/iterator.c index c5c5fd7ce..93303a87d 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -46,6 +46,7 @@ #define iterator__include_trees(I) iterator__flag(I,INCLUDE_TREES) #define iterator__dont_autoexpand(I) iterator__flag(I,DONT_AUTOEXPAND) #define iterator__do_autoexpand(I) !iterator__flag(I,DONT_AUTOEXPAND) +#define iterator__include_conflicts(I) iterator__flag(I, INCLUDE_CONFLICTS) #define GIT_ITERATOR_FIRST_ACCESS (1 << 15) #define iterator__has_been_accessed(I) iterator__flag(I,FIRST_ACCESS) @@ -668,13 +669,16 @@ static const git_index_entry *index_iterator__index_entry(index_iterator *ii) return ie; } -static const git_index_entry *index_iterator__skip_conflicts(index_iterator *ii) +static const git_index_entry *index_iterator__advance_over_conflicts(index_iterator *ii) { - const git_index_entry *ie; + const git_index_entry *ie = index_iterator__index_entry(ii); - while ((ie = index_iterator__index_entry(ii)) != NULL && - git_index_entry_stage(ie) != 0) - ii->current++; + if (!iterator__include_conflicts(ii)) { + while (ie && git_index_entry_is_conflict(ie)) { + ii->current++; + ie = index_iterator__index_entry(ii); + } + } return ie; } @@ -702,7 +706,7 @@ static void index_iterator__next_prefix_tree(index_iterator *ii) static int index_iterator__first_prefix_tree(index_iterator *ii) { - const git_index_entry *ie = index_iterator__skip_conflicts(ii); + const git_index_entry *ie = index_iterator__advance_over_conflicts(ii); const char *scan, *prior, *slash; if (!ie || !iterator__include_trees(ii)) @@ -825,7 +829,7 @@ static int index_iterator__reset( git_index_snapshot_find( &ii->current, &ii->entries, ii->entry_srch, ii->base.start, 0, 0); - if ((ie = index_iterator__skip_conflicts(ii)) == NULL) + if ((ie = index_iterator__advance_over_conflicts(ii)) == NULL) return 0; if (git_buf_sets(&ii->partial, ie->path) < 0) diff --git a/src/iterator.h b/src/iterator.h index 1520bffc2..db1f325a7 100644 --- a/src/iterator.h +++ b/src/iterator.h @@ -33,6 +33,8 @@ typedef enum { GIT_ITERATOR_DONT_AUTOEXPAND = (1u << 3), /** convert precomposed unicode to decomposed unicode */ GIT_ITERATOR_PRECOMPOSE_UNICODE = (1u << 4), + /** include conflicts */ + GIT_ITERATOR_INCLUDE_CONFLICTS = (1u << 5), } git_iterator_flag_t; typedef struct { diff --git a/src/merge.c b/src/merge.c index 5e7727429..eaf7eee1f 100644 --- a/src/merge.c +++ b/src/merge.c @@ -2492,7 +2492,7 @@ int git_merge__check_result(git_repository *repo, git_index *index_new) for (i = 0; i < git_index_entrycount(index_new); i++) { e = git_index_get_byindex(index_new, i); - if (git_index_entry_stage(e) != 0 && + if (git_index_entry_is_conflict(e) && (git_vector_last(&paths) == NULL || strcmp(git_vector_last(&paths), e->path) != 0)) { @@ -2544,7 +2544,7 @@ int git_merge__append_conflicts_to_merge_msg( for (i = 0; i < git_index_entrycount(index); i++) { const git_index_entry *e = git_index_get_byindex(index, i); - if (git_index_entry_stage(e) == 0) + if (!git_index_entry_is_conflict(e)) continue; if (last == NULL || strcmp(e->path, last) != 0) diff --git a/src/reset.c b/src/reset.c index d08e48cd7..0ffa51b66 100644 --- a/src/reset.c +++ b/src/reset.c @@ -63,6 +63,7 @@ int git_reset_default( assert(delta->status == GIT_DELTA_ADDED || delta->status == GIT_DELTA_MODIFIED || + delta->status == GIT_DELTA_CONFLICTED || delta->status == GIT_DELTA_DELETED); error = git_index_conflict_remove(index, delta->old_file.path); diff --git a/src/status.c b/src/status.c index 720ccb7eb..b206b0e2f 100644 --- a/src/status.c +++ b/src/status.c @@ -44,6 +44,9 @@ static unsigned int index_delta2status(const git_diff_delta *head2idx) case GIT_DELTA_TYPECHANGE: st = GIT_STATUS_INDEX_TYPECHANGE; break; + case GIT_DELTA_CONFLICTED: + st = GIT_STATUS_CONFLICTED; + break; default: break; } @@ -102,6 +105,9 @@ static unsigned int workdir_delta2status( case GIT_DELTA_TYPECHANGE: st = GIT_STATUS_WT_TYPECHANGE; break; + case GIT_DELTA_CONFLICTED: + st = GIT_STATUS_CONFLICTED; + break; default: break; } diff --git a/tests/checkout/conflict.c b/tests/checkout/conflict.c index 3d763af12..006dc6522 100644 --- a/tests/checkout/conflict.c +++ b/tests/checkout/conflict.c @@ -102,7 +102,7 @@ static void create_index(struct checkout_index_entry *entries, size_t entries_le memset(&entry, 0x0, sizeof(git_index_entry)); entry.mode = entries[i].mode; - entry.flags = entries[i].stage << GIT_IDXENTRY_STAGESHIFT; + GIT_IDXENTRY_STAGE_SET(&entry, entries[i].stage); git_oid_fromstr(&entry.id, entries[i].oid_str); entry.path = entries[i].path; diff --git a/tests/checkout/index.c b/tests/checkout/index.c index de0770dba..89b014586 100644 --- a/tests/checkout/index.c +++ b/tests/checkout/index.c @@ -685,15 +685,15 @@ static void add_conflict(git_index *index, const char *path) entry.path = path; git_oid_fromstr(&entry.id, "d427e0b2e138501a3d15cc376077a3631e15bd46"); - entry.flags = (1 << GIT_IDXENTRY_STAGESHIFT); + GIT_IDXENTRY_STAGE_SET(&entry, 1); cl_git_pass(git_index_add(index, &entry)); git_oid_fromstr(&entry.id, "4e886e602529caa9ab11d71f86634bd1b6e0de10"); - entry.flags = (2 << GIT_IDXENTRY_STAGESHIFT); + GIT_IDXENTRY_STAGE_SET(&entry, 2); cl_git_pass(git_index_add(index, &entry)); git_oid_fromstr(&entry.id, "2bd0a343aeef7a2cf0d158478966a6e587ff3863"); - entry.flags = (3 << GIT_IDXENTRY_STAGESHIFT); + GIT_IDXENTRY_STAGE_SET(&entry, 3); cl_git_pass(git_index_add(index, &entry)); } diff --git a/tests/checkout/tree.c b/tests/checkout/tree.c index 50541a703..462323186 100644 --- a/tests/checkout/tree.c +++ b/tests/checkout/tree.c @@ -893,16 +893,16 @@ static void create_conflict(const char *path) memset(&entry, 0x0, sizeof(git_index_entry)); entry.mode = 0100644; - entry.flags = 1 << GIT_IDXENTRY_STAGESHIFT; + GIT_IDXENTRY_STAGE_SET(&entry, 1); git_oid_fromstr(&entry.id, "d427e0b2e138501a3d15cc376077a3631e15bd46"); entry.path = path; cl_git_pass(git_index_add(index, &entry)); - entry.flags = 2 << GIT_IDXENTRY_STAGESHIFT; + GIT_IDXENTRY_STAGE_SET(&entry, 2); git_oid_fromstr(&entry.id, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf"); cl_git_pass(git_index_add(index, &entry)); - entry.flags = 3 << GIT_IDXENTRY_STAGESHIFT; + GIT_IDXENTRY_STAGE_SET(&entry, 3); git_oid_fromstr(&entry.id, "2bd0a343aeef7a2cf0d158478966a6e587ff3863"); cl_git_pass(git_index_add(index, &entry)); diff --git a/tests/clar_libgit2.c b/tests/clar_libgit2.c index 6087c2a67..dabc47a09 100644 --- a/tests/clar_libgit2.c +++ b/tests/clar_libgit2.c @@ -197,6 +197,14 @@ git_repository *cl_git_sandbox_init(const char *sandbox) return _cl_repo; } +git_repository *cl_git_sandbox_init_new(const char *sandbox) +{ + cl_git_pass(git_repository_init(&_cl_repo, sandbox, false)); + _cl_sandbox = sandbox; + + return _cl_repo; +} + git_repository *cl_git_sandbox_reopen(void) { if (_cl_repo) { diff --git a/tests/clar_libgit2.h b/tests/clar_libgit2.h index 86c90b049..9ab0da4f6 100644 --- a/tests/clar_libgit2.h +++ b/tests/clar_libgit2.h @@ -127,6 +127,7 @@ int cl_rename(const char *source, const char *dest); /* Git sandbox setup helpers */ git_repository *cl_git_sandbox_init(const char *sandbox); +git_repository *cl_git_sandbox_init_new(const char *name); void cl_git_sandbox_cleanup(void); git_repository *cl_git_sandbox_reopen(void); diff --git a/tests/diff/diff_helpers.c b/tests/diff/diff_helpers.c index 47e06f0d8..03862d6b4 100644 --- a/tests/diff/diff_helpers.c +++ b/tests/diff/diff_helpers.c @@ -68,7 +68,7 @@ int diff_file_cb( if ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0) e->files_binary++; - cl_assert(delta->status <= GIT_DELTA_TYPECHANGE); + cl_assert(delta->status <= GIT_DELTA_CONFLICTED); e->file_status[delta->status] += 1; diff --git a/tests/diff/diff_helpers.h b/tests/diff/diff_helpers.h index bf21f4b1f..6bcd1e671 100644 --- a/tests/diff/diff_helpers.h +++ b/tests/diff/diff_helpers.h @@ -8,7 +8,7 @@ typedef struct { int files; int files_binary; - int file_status[10]; /* indexed by git_delta_t value */ + int file_status[11]; /* indexed by git_delta_t value */ int hunks; int hunk_new_lines; diff --git a/tests/diff/index.c b/tests/diff/index.c index 21afe8da2..a544b83c7 100644 --- a/tests/diff/index.c +++ b/tests/diff/index.c @@ -163,3 +163,79 @@ void test_diff_index__checks_options_version(void) git_tree_free(a); } +static void do_conflicted_diff(diff_expects *exp, unsigned long flags) +{ + const char *a_commit = "26a125ee1bf"; /* the current HEAD */ + git_tree *a = resolve_commit_oid_to_tree(g_repo, a_commit); + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + git_index_entry ancestor = {0}, ours = {0}, theirs = {0}; + git_diff *diff = NULL; + git_index *index; + + cl_assert(a); + + opts.context_lines = 1; + opts.interhunk_lines = 1; + opts.flags |= flags; + + memset(exp, 0, sizeof(diff_expects)); + + cl_git_pass(git_repository_index(&index, g_repo)); + + ancestor.path = ours.path = theirs.path = "staged_changes"; + ancestor.mode = ours.mode = theirs.mode = 0100644; + + git_oid_fromstr(&ancestor.id, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"); + git_oid_fromstr(&ours.id, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"); + git_oid_fromstr(&theirs.id, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"); + + cl_git_pass(git_index_conflict_add(index, &ancestor, &ours, &theirs)); + cl_git_pass(git_diff_tree_to_index(&diff, g_repo, a, index, &opts)); + + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, exp)); + + git_diff_free(diff); + git_tree_free(a); + git_index_free(index); +} + +void test_diff_index__reports_conflicts(void) +{ + diff_expects exp; + + do_conflicted_diff(&exp, 0); + + cl_assert_equal_i(8, exp.files); + cl_assert_equal_i(3, exp.file_status[GIT_DELTA_ADDED]); + cl_assert_equal_i(2, exp.file_status[GIT_DELTA_DELETED]); + cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_CONFLICTED]); + + cl_assert_equal_i(7, exp.hunks); + + cl_assert_equal_i(9, exp.lines); + cl_assert_equal_i(2, exp.line_ctxt); + cl_assert_equal_i(5, exp.line_adds); + cl_assert_equal_i(2, exp.line_dels); +} + +void test_diff_index__reports_conflicts_when_reversed(void) +{ + diff_expects exp; + + do_conflicted_diff(&exp, GIT_DIFF_REVERSE); + + cl_assert_equal_i(8, exp.files); + cl_assert_equal_i(2, exp.file_status[GIT_DELTA_ADDED]); + cl_assert_equal_i(3, exp.file_status[GIT_DELTA_DELETED]); + cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_CONFLICTED]); + + cl_assert_equal_i(7, exp.hunks); + + cl_assert_equal_i(9, exp.lines); + cl_assert_equal_i(2, exp.line_ctxt); + cl_assert_equal_i(2, exp.line_adds); + cl_assert_equal_i(5, exp.line_dels); +} diff --git a/tests/diff/workdir.c b/tests/diff/workdir.c index 963be9481..460f1b592 100644 --- a/tests/diff/workdir.c +++ b/tests/diff/workdir.c @@ -68,6 +68,51 @@ void test_diff_workdir__to_index(void) git_diff_free(diff); } +void test_diff_workdir__to_index_with_conflicts(void) +{ + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + git_diff *diff = NULL; + git_index *index; + git_index_entry our_entry = {0}, their_entry = {0}; + diff_expects exp = {0}; + + g_repo = cl_git_sandbox_init("status"); + + opts.context_lines = 3; + opts.interhunk_lines = 1; + + /* Adding an entry that represents a rename gets two files in conflict */ + our_entry.path = "subdir/modified_file"; + our_entry.mode = 0100644; + + their_entry.path = "subdir/rename_conflict"; + their_entry.mode = 0100644; + + cl_git_pass(git_repository_index(&index, g_repo)); + cl_git_pass(git_index_conflict_add(index, NULL, &our_entry, &their_entry)); + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, index, &opts)); + + cl_git_pass(diff_foreach_via_iterator( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + + cl_assert_equal_i(9, exp.files); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); + cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]); + cl_assert_equal_i(3, exp.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(2, exp.file_status[GIT_DELTA_CONFLICTED]); + + cl_assert_equal_i(7, exp.hunks); + + cl_assert_equal_i(12, exp.lines); + cl_assert_equal_i(4, exp.line_ctxt); + cl_assert_equal_i(3, exp.line_adds); + cl_assert_equal_i(5, exp.line_dels); + + git_diff_free(diff); + git_index_free(index); +} + void test_diff_workdir__to_index_with_assume_unchanged(void) { git_diff_options opts = GIT_DIFF_OPTIONS_INIT; diff --git a/tests/index/addall.c b/tests/index/addall.c index 377d2079e..211e76228 100644 --- a/tests/index/addall.c +++ b/tests/index/addall.c @@ -12,10 +12,7 @@ void test_index_addall__initialize(void) void test_index_addall__cleanup(void) { - git_repository_free(g_repo); - g_repo = NULL; - - cl_fixture_cleanup(TEST_DIR); + cl_git_sandbox_cleanup(); } #define STATUS_INDEX_FLAGS \ @@ -36,6 +33,7 @@ typedef struct { size_t wt_dels; size_t wt_mods; size_t ignores; + size_t conflicts; } index_status_counts; static int index_status_cb( @@ -67,6 +65,8 @@ static int index_status_cb( if (status_flags & GIT_STATUS_IGNORED) vals->ignores++; + if (status_flags & GIT_STATUS_CONFLICTED) + vals->conflicts++; return 0; } @@ -75,7 +75,7 @@ static void check_status_at_line( git_repository *repo, size_t index_adds, size_t index_dels, size_t index_mods, size_t wt_adds, size_t wt_dels, size_t wt_mods, size_t ignores, - const char *file, int line) + size_t conflicts, const char *file, int line) { index_status_counts vals; @@ -97,10 +97,12 @@ static void check_status_at_line( file,line,"wrong workdir mods", 1, "%"PRIuZ, wt_mods, vals.wt_mods); clar__assert_equal( file,line,"wrong ignores", 1, "%"PRIuZ, ignores, vals.ignores); + clar__assert_equal( + file,line,"wrong conflicts", 1, "%"PRIuZ, conflicts, vals.conflicts); } -#define check_status(R,IA,ID,IM,WA,WD,WM,IG) \ - check_status_at_line(R,IA,ID,IM,WA,WD,WM,IG,__FILE__,__LINE__) +#define check_status(R,IA,ID,IM,WA,WD,WM,IG,C) \ + check_status_at_line(R,IA,ID,IM,WA,WD,WM,IG,C,__FILE__,__LINE__) static void check_stat_data(git_index *index, const char *path, bool match) { @@ -137,21 +139,22 @@ static void check_stat_data(git_index *index, const char *path, bool match) static void addall_create_test_repo(bool check_every_step) { - cl_git_pass(git_repository_init(&g_repo, TEST_DIR, false)); + g_repo = cl_git_sandbox_init_new(TEST_DIR); + if (check_every_step) - check_status(g_repo, 0, 0, 0, 0, 0, 0, 0); + check_status(g_repo, 0, 0, 0, 0, 0, 0, 0, 0); cl_git_mkfile(TEST_DIR "/file.foo", "a file"); if (check_every_step) - check_status(g_repo, 0, 0, 0, 1, 0, 0, 0); + check_status(g_repo, 0, 0, 0, 1, 0, 0, 0, 0); cl_git_mkfile(TEST_DIR "/.gitignore", "*.foo\n"); if (check_every_step) - check_status(g_repo, 0, 0, 0, 1, 0, 0, 1); + check_status(g_repo, 0, 0, 0, 1, 0, 0, 1, 0); cl_git_mkfile(TEST_DIR "/file.bar", "another file"); if (check_every_step) - check_status(g_repo, 0, 0, 0, 2, 0, 0, 1); + check_status(g_repo, 0, 0, 0, 2, 0, 0, 1, 0); } void test_index_addall__repo_lifecycle(void) @@ -171,107 +174,107 @@ void test_index_addall__repo_lifecycle(void) cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL)); check_stat_data(index, TEST_DIR "/file.bar", true); - check_status(g_repo, 1, 0, 0, 1, 0, 0, 1); + check_status(g_repo, 1, 0, 0, 1, 0, 0, 1, 0); cl_git_rewritefile(TEST_DIR "/file.bar", "new content for file"); check_stat_data(index, TEST_DIR "/file.bar", false); - check_status(g_repo, 1, 0, 0, 1, 0, 1, 1); + check_status(g_repo, 1, 0, 0, 1, 0, 1, 1, 0); cl_git_mkfile(TEST_DIR "/file.zzz", "yet another one"); cl_git_mkfile(TEST_DIR "/other.zzz", "yet another one"); cl_git_mkfile(TEST_DIR "/more.zzz", "yet another one"); - check_status(g_repo, 1, 0, 0, 4, 0, 1, 1); + check_status(g_repo, 1, 0, 0, 4, 0, 1, 1, 0); cl_git_pass(git_index_update_all(index, NULL, NULL, NULL)); check_stat_data(index, TEST_DIR "/file.bar", true); - check_status(g_repo, 1, 0, 0, 4, 0, 0, 1); + check_status(g_repo, 1, 0, 0, 4, 0, 0, 1, 0); cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL)); check_stat_data(index, TEST_DIR "/file.zzz", true); - check_status(g_repo, 2, 0, 0, 3, 0, 0, 1); + check_status(g_repo, 2, 0, 0, 3, 0, 0, 1, 0); cl_repo_commit_from_index(NULL, g_repo, NULL, 0, "first commit"); - check_status(g_repo, 0, 0, 0, 3, 0, 0, 1); + check_status(g_repo, 0, 0, 0, 3, 0, 0, 1, 0); if (cl_repo_get_bool(g_repo, "core.filemode")) { cl_git_pass(git_index_update_all(index, NULL, NULL, NULL)); cl_must_pass(p_chmod(TEST_DIR "/file.zzz", 0777)); cl_git_pass(git_index_update_all(index, NULL, NULL, NULL)); - check_status(g_repo, 0, 0, 1, 3, 0, 0, 1); + check_status(g_repo, 0, 0, 1, 3, 0, 0, 1, 0); /* go back to what we had before */ cl_must_pass(p_chmod(TEST_DIR "/file.zzz", 0666)); cl_git_pass(git_index_update_all(index, NULL, NULL, NULL)); - check_status(g_repo, 0, 0, 0, 3, 0, 0, 1); + check_status(g_repo, 0, 0, 0, 3, 0, 0, 1, 0); } /* attempt to add an ignored file - does nothing */ strs[0] = "file.foo"; cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL)); - check_status(g_repo, 0, 0, 0, 3, 0, 0, 1); + check_status(g_repo, 0, 0, 0, 3, 0, 0, 1, 0); /* add with check - should generate error */ error = git_index_add_all( index, &paths, GIT_INDEX_ADD_CHECK_PATHSPEC, NULL, NULL); cl_assert_equal_i(GIT_EINVALIDSPEC, error); - check_status(g_repo, 0, 0, 0, 3, 0, 0, 1); + check_status(g_repo, 0, 0, 0, 3, 0, 0, 1, 0); /* add with force - should allow */ cl_git_pass(git_index_add_all( index, &paths, GIT_INDEX_ADD_FORCE, NULL, NULL)); check_stat_data(index, TEST_DIR "/file.foo", true); - check_status(g_repo, 1, 0, 0, 3, 0, 0, 0); + check_status(g_repo, 1, 0, 0, 3, 0, 0, 0, 0); /* now it's in the index, so regular add should work */ cl_git_rewritefile(TEST_DIR "/file.foo", "new content for file"); check_stat_data(index, TEST_DIR "/file.foo", false); - check_status(g_repo, 1, 0, 0, 3, 0, 1, 0); + check_status(g_repo, 1, 0, 0, 3, 0, 1, 0, 0); cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL)); check_stat_data(index, TEST_DIR "/file.foo", true); - check_status(g_repo, 1, 0, 0, 3, 0, 0, 0); + check_status(g_repo, 1, 0, 0, 3, 0, 0, 0, 0); cl_git_pass(git_index_add_bypath(index, "more.zzz")); check_stat_data(index, TEST_DIR "/more.zzz", true); - check_status(g_repo, 2, 0, 0, 2, 0, 0, 0); + check_status(g_repo, 2, 0, 0, 2, 0, 0, 0, 0); cl_git_rewritefile(TEST_DIR "/file.zzz", "new content for file"); - check_status(g_repo, 2, 0, 0, 2, 0, 1, 0); + check_status(g_repo, 2, 0, 0, 2, 0, 1, 0, 0); cl_git_pass(git_index_add_bypath(index, "file.zzz")); check_stat_data(index, TEST_DIR "/file.zzz", true); - check_status(g_repo, 2, 0, 1, 2, 0, 0, 0); + check_status(g_repo, 2, 0, 1, 2, 0, 0, 0, 0); strs[0] = "*.zzz"; cl_git_pass(git_index_remove_all(index, &paths, NULL, NULL)); - check_status(g_repo, 1, 1, 0, 4, 0, 0, 0); + check_status(g_repo, 1, 1, 0, 4, 0, 0, 0, 0); cl_git_pass(git_index_add_bypath(index, "file.zzz")); - check_status(g_repo, 1, 0, 1, 3, 0, 0, 0); + check_status(g_repo, 1, 0, 1, 3, 0, 0, 0, 0); cl_repo_commit_from_index(NULL, g_repo, NULL, 0, "second commit"); - check_status(g_repo, 0, 0, 0, 3, 0, 0, 0); + check_status(g_repo, 0, 0, 0, 3, 0, 0, 0, 0); cl_must_pass(p_unlink(TEST_DIR "/file.zzz")); - check_status(g_repo, 0, 0, 0, 3, 1, 0, 0); + check_status(g_repo, 0, 0, 0, 3, 1, 0, 0, 0); /* update_all should be able to remove entries */ cl_git_pass(git_index_update_all(index, NULL, NULL, NULL)); - check_status(g_repo, 0, 1, 0, 3, 0, 0, 0); + check_status(g_repo, 0, 1, 0, 3, 0, 0, 0, 0); strs[0] = "*"; cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL)); - check_status(g_repo, 3, 1, 0, 0, 0, 0, 0); + check_status(g_repo, 3, 1, 0, 0, 0, 0, 0, 0); /* must be able to remove at any position while still updating other files */ cl_must_pass(p_unlink(TEST_DIR "/.gitignore")); cl_git_rewritefile(TEST_DIR "/file.zzz", "reconstructed file"); cl_git_rewritefile(TEST_DIR "/more.zzz", "altered file reality"); - check_status(g_repo, 3, 1, 0, 1, 1, 1, 0); + check_status(g_repo, 3, 1, 0, 1, 1, 1, 0, 0); cl_git_pass(git_index_update_all(index, NULL, NULL, NULL)); - check_status(g_repo, 2, 1, 0, 1, 0, 0, 0); + check_status(g_repo, 2, 1, 0, 1, 0, 0, 0, 0); /* this behavior actually matches 'git add -u' where "file.zzz" has * been removed from the index, so when you go to update, even though * it exists in the HEAD, it is not re-added to the index, leaving it @@ -293,14 +296,14 @@ void test_index_addall__files_in_folders(void) cl_git_pass(git_index_add_all(index, NULL, 0, NULL, NULL)); check_stat_data(index, TEST_DIR "/file.bar", true); - check_status(g_repo, 2, 0, 0, 0, 0, 0, 1); + check_status(g_repo, 2, 0, 0, 0, 0, 0, 1, 0); cl_must_pass(p_mkdir(TEST_DIR "/subdir", 0777)); cl_git_mkfile(TEST_DIR "/subdir/file", "hello!\n"); - check_status(g_repo, 2, 0, 0, 1, 0, 0, 1); + check_status(g_repo, 2, 0, 0, 1, 0, 0, 1, 0); cl_git_pass(git_index_add_all(index, NULL, 0, NULL, NULL)); - check_status(g_repo, 3, 0, 0, 0, 0, 0, 1); + check_status(g_repo, 3, 0, 0, 0, 0, 0, 1, 0); git_index_free(index); } @@ -337,7 +340,7 @@ void test_index_addall__callback_filtering(void) cl_git_pass( git_index_add_all(index, NULL, 0, addall_match_prefix, "file.")); check_stat_data(index, TEST_DIR "/file.bar", true); - check_status(g_repo, 1, 0, 0, 1, 0, 0, 1); + check_status(g_repo, 1, 0, 0, 1, 0, 0, 1, 0); cl_git_mkfile(TEST_DIR "/file.zzz", "yet another one"); cl_git_mkfile(TEST_DIR "/more.zzz", "yet another one"); @@ -345,32 +348,32 @@ void test_index_addall__callback_filtering(void) cl_git_pass(git_index_update_all(index, NULL, NULL, NULL)); check_stat_data(index, TEST_DIR "/file.bar", true); - check_status(g_repo, 1, 0, 0, 4, 0, 0, 1); + check_status(g_repo, 1, 0, 0, 4, 0, 0, 1, 0); cl_git_pass( git_index_add_all(index, NULL, 0, addall_match_prefix, "other")); check_stat_data(index, TEST_DIR "/other.zzz", true); - check_status(g_repo, 2, 0, 0, 3, 0, 0, 1); + check_status(g_repo, 2, 0, 0, 3, 0, 0, 1, 0); cl_git_pass( git_index_add_all(index, NULL, 0, addall_match_suffix, ".zzz")); - check_status(g_repo, 4, 0, 0, 1, 0, 0, 1); + check_status(g_repo, 4, 0, 0, 1, 0, 0, 1, 0); cl_git_pass( git_index_remove_all(index, NULL, addall_match_suffix, ".zzz")); - check_status(g_repo, 1, 0, 0, 4, 0, 0, 1); + check_status(g_repo, 1, 0, 0, 4, 0, 0, 1, 0); cl_git_fail_with( git_index_add_all(index, NULL, 0, addall_cancel_at, "more.zzz"), -123); - check_status(g_repo, 3, 0, 0, 2, 0, 0, 1); + check_status(g_repo, 3, 0, 0, 2, 0, 0, 1, 0); cl_git_fail_with( git_index_add_all(index, NULL, 0, addall_cancel_at, "other.zzz"), -123); - check_status(g_repo, 4, 0, 0, 1, 0, 0, 1); + check_status(g_repo, 4, 0, 0, 1, 0, 0, 1, 0); cl_git_pass( git_index_add_all(index, NULL, 0, addall_match_suffix, ".zzz")); - check_status(g_repo, 5, 0, 0, 0, 0, 0, 1); + check_status(g_repo, 5, 0, 0, 0, 0, 0, 1, 0); cl_must_pass(p_unlink(TEST_DIR "/file.zzz")); cl_must_pass(p_unlink(TEST_DIR "/more.zzz")); @@ -380,13 +383,65 @@ void test_index_addall__callback_filtering(void) git_index_update_all(index, NULL, addall_cancel_at, "more.zzz"), -123); /* file.zzz removed from index (so Index Adds 5 -> 4) and * more.zzz + other.zzz removed (so Worktree Dels 0 -> 2) */ - check_status(g_repo, 4, 0, 0, 0, 2, 0, 1); + check_status(g_repo, 4, 0, 0, 0, 2, 0, 1, 0); cl_git_fail_with( git_index_update_all(index, NULL, addall_cancel_at, "other.zzz"), -123); /* more.zzz removed from index (so Index Adds 4 -> 3) and * Just other.zzz removed (so Worktree Dels 2 -> 1) */ - check_status(g_repo, 3, 0, 0, 0, 1, 0, 1); + check_status(g_repo, 3, 0, 0, 0, 1, 0, 1, 0); git_index_free(index); } + +void test_index_addall__adds_conflicts(void) +{ + git_index *index; + git_reference *ref; + git_annotated_commit *annotated; + + g_repo = cl_git_sandbox_init("merge-resolve"); + cl_git_pass(git_repository_index(&index, g_repo)); + + check_status(g_repo, 0, 0, 0, 0, 0, 0, 0, 0); + + cl_git_pass(git_reference_lookup(&ref, g_repo, "refs/heads/branch")); + cl_git_pass(git_annotated_commit_from_ref(&annotated, g_repo, ref)); + + cl_git_pass(git_merge(g_repo, &annotated, 1, NULL, NULL)); + check_status(g_repo, 0, 1, 2, 0, 0, 0, 0, 1); + + cl_git_pass(git_index_add_all(index, NULL, 0, NULL, NULL)); + check_status(g_repo, 0, 1, 3, 0, 0, 0, 0, 0); + + git_annotated_commit_free(annotated); + git_reference_free(ref); + git_index_free(index); +} + +void test_index_addall__removes_deleted_conflicted_files(void) +{ + git_index *index; + git_reference *ref; + git_annotated_commit *annotated; + + g_repo = cl_git_sandbox_init("merge-resolve"); + cl_git_pass(git_repository_index(&index, g_repo)); + + check_status(g_repo, 0, 0, 0, 0, 0, 0, 0, 0); + + cl_git_pass(git_reference_lookup(&ref, g_repo, "refs/heads/branch")); + cl_git_pass(git_annotated_commit_from_ref(&annotated, g_repo, ref)); + + cl_git_pass(git_merge(g_repo, &annotated, 1, NULL, NULL)); + check_status(g_repo, 0, 1, 2, 0, 0, 0, 0, 1); + + cl_git_rmfile("merge-resolve/conflicting.txt"); + + cl_git_pass(git_index_add_all(index, NULL, 0, NULL, NULL)); + check_status(g_repo, 0, 2, 2, 0, 0, 0, 0, 0); + + git_annotated_commit_free(annotated); + git_reference_free(ref); + git_index_free(index); +}
\ No newline at end of file diff --git a/tests/index/collision.c b/tests/index/collision.c index 1f002e8d3..19c1548e9 100644 --- a/tests/index/collision.c +++ b/tests/index/collision.c @@ -57,7 +57,7 @@ void test_index_collision__add_with_highstage_1(void) git_oid_fromstr(&entry.id, "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"); entry.path = "a/b"; - entry.flags = (2 << GIT_IDXENTRY_STAGESHIFT); + GIT_IDXENTRY_STAGE_SET(&entry, 2); cl_git_pass(git_index_add(index, &entry)); /* create a blob beneath the previous tree entry */ @@ -67,7 +67,7 @@ void test_index_collision__add_with_highstage_1(void) /* create another tree entry above the blob */ entry.path = "a/b"; - entry.flags = (1 << GIT_IDXENTRY_STAGESHIFT); + GIT_IDXENTRY_STAGE_SET(&entry, 1); cl_git_pass(git_index_add(index, &entry)); git_index_free(index); @@ -89,17 +89,17 @@ void test_index_collision__add_with_highstage_2(void) git_oid_fromstr(&entry.id, "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"); entry.path = "a/b/c"; - entry.flags = (1 << GIT_IDXENTRY_STAGESHIFT); + GIT_IDXENTRY_STAGE_SET(&entry, 1); cl_git_pass(git_index_add(index, &entry)); /* create a blob beneath the previous tree entry */ entry.path = "a/b/c"; - entry.flags = (2 << GIT_IDXENTRY_STAGESHIFT); + GIT_IDXENTRY_STAGE_SET(&entry, 2); cl_git_pass(git_index_add(index, &entry)); /* create another tree entry above the blob */ entry.path = "a/b"; - entry.flags = (3 << GIT_IDXENTRY_STAGESHIFT); + GIT_IDXENTRY_STAGE_SET(&entry, 3); cl_git_pass(git_index_add(index, &entry)); git_index_free(index); diff --git a/tests/index/conflicts.c b/tests/index/conflicts.c index 427351693..b7a2456eb 100644 --- a/tests/index/conflicts.c +++ b/tests/index/conflicts.c @@ -16,6 +16,7 @@ static git_index *repo_index; #define CONFLICTS_TWO_OUR_OID "8b3f43d2402825c200f835ca1762413e386fd0b2" #define CONFLICTS_TWO_THEIR_OID "220bd62631c8cf7a83ef39c6b94595f00517211e" +#define TEST_STAGED_OID "beefdadafeedabedcafedeedbabedeadbeaddeaf" #define TEST_ANCESTOR_OID "f00ff00ff00ff00ff00ff00ff00ff00ff00ff00f" #define TEST_OUR_OID "b44bb44bb44bb44bb44bb44bb44bb44bb44bb44b" #define TEST_THEIR_OID "0123456789abcdef0123456789abcdef01234567" @@ -46,15 +47,18 @@ void test_index_conflicts__add(void) memset(&their_entry, 0x0, sizeof(git_index_entry)); ancestor_entry.path = "test-one.txt"; - ancestor_entry.flags |= (1 << GIT_IDXENTRY_STAGESHIFT); + ancestor_entry.mode = 0100644; + GIT_IDXENTRY_STAGE_SET(&ancestor_entry, 1); git_oid_fromstr(&ancestor_entry.id, TEST_ANCESTOR_OID); our_entry.path = "test-one.txt"; - ancestor_entry.flags |= (2 << GIT_IDXENTRY_STAGESHIFT); + our_entry.mode = 0100644; + GIT_IDXENTRY_STAGE_SET(&our_entry, 2); git_oid_fromstr(&our_entry.id, TEST_OUR_OID); their_entry.path = "test-one.txt"; - ancestor_entry.flags |= (3 << GIT_IDXENTRY_STAGESHIFT); + their_entry.mode = 0100644; + GIT_IDXENTRY_STAGE_SET(&ancestor_entry, 2); git_oid_fromstr(&their_entry.id, TEST_THEIR_OID); cl_git_pass(git_index_conflict_add(repo_index, &ancestor_entry, &our_entry, &their_entry)); @@ -74,15 +78,18 @@ void test_index_conflicts__add_fixes_incorrect_stage(void) memset(&their_entry, 0x0, sizeof(git_index_entry)); ancestor_entry.path = "test-one.txt"; - ancestor_entry.flags |= (3 << GIT_IDXENTRY_STAGESHIFT); + ancestor_entry.mode = 0100644; + GIT_IDXENTRY_STAGE_SET(&ancestor_entry, 3); git_oid_fromstr(&ancestor_entry.id, TEST_ANCESTOR_OID); our_entry.path = "test-one.txt"; - ancestor_entry.flags |= (1 << GIT_IDXENTRY_STAGESHIFT); + our_entry.mode = 0100644; + GIT_IDXENTRY_STAGE_SET(&our_entry, 1); git_oid_fromstr(&our_entry.id, TEST_OUR_OID); their_entry.path = "test-one.txt"; - ancestor_entry.flags |= (2 << GIT_IDXENTRY_STAGESHIFT); + their_entry.mode = 0100644; + GIT_IDXENTRY_STAGE_SET(&their_entry, 2); git_oid_fromstr(&their_entry.id, TEST_THEIR_OID); cl_git_pass(git_index_conflict_add(repo_index, &ancestor_entry, &our_entry, &their_entry)); @@ -96,6 +103,55 @@ void test_index_conflicts__add_fixes_incorrect_stage(void) cl_assert(git_index_entry_stage(conflict_entry[2]) == 3); } +void test_index_conflicts__add_removes_stage_zero(void) +{ + git_index_entry staged, ancestor_entry, our_entry, their_entry; + const git_index_entry *conflict_entry[3]; + + cl_assert(git_index_entrycount(repo_index) == 8); + + memset(&staged, 0x0, sizeof(git_index_entry)); + memset(&ancestor_entry, 0x0, sizeof(git_index_entry)); + memset(&our_entry, 0x0, sizeof(git_index_entry)); + memset(&their_entry, 0x0, sizeof(git_index_entry)); + + staged.path = "test-one.txt"; + staged.mode = 0100644; + git_oid_fromstr(&staged.id, TEST_STAGED_OID); + cl_git_pass(git_index_add(repo_index, &staged)); + cl_assert(git_index_entrycount(repo_index) == 9); + + ancestor_entry.path = "test-one.txt"; + ancestor_entry.mode = 0100644; + GIT_IDXENTRY_STAGE_SET(&ancestor_entry, 3); + git_oid_fromstr(&ancestor_entry.id, TEST_ANCESTOR_OID); + + our_entry.path = "test-one.txt"; + our_entry.mode = 0100644; + GIT_IDXENTRY_STAGE_SET(&our_entry, 1); + git_oid_fromstr(&our_entry.id, TEST_OUR_OID); + + their_entry.path = "test-one.txt"; + their_entry.mode = 0100644; + GIT_IDXENTRY_STAGE_SET(&their_entry, 2); + git_oid_fromstr(&their_entry.id, TEST_THEIR_OID); + + cl_git_pass(git_index_conflict_add(repo_index, &ancestor_entry, &our_entry, &their_entry)); + + cl_assert(git_index_entrycount(repo_index) == 11); + + cl_assert_equal_p(NULL, git_index_get_bypath(repo_index, "test-one.txt", 0)); + + cl_git_pass(git_index_conflict_get(&conflict_entry[0], &conflict_entry[1], &conflict_entry[2], repo_index, "test-one.txt")); + + cl_assert_equal_oid(&ancestor_entry.id, &conflict_entry[0]->id); + cl_assert_equal_i(1, git_index_entry_stage(conflict_entry[0])); + cl_assert_equal_oid(&our_entry.id, &conflict_entry[1]->id); + cl_assert_equal_i(2, git_index_entry_stage(conflict_entry[1])); + cl_assert_equal_oid(&their_entry.id, &conflict_entry[2]->id); + cl_assert_equal_i(3, git_index_entry_stage(conflict_entry[2])); +} + void test_index_conflicts__get(void) { const git_index_entry *conflict_entry[3]; @@ -216,7 +272,7 @@ void test_index_conflicts__moved_to_reuc_on_add(void) cl_assert(entry = git_index_get_byindex(repo_index, i)); if (strcmp(entry->path, "conflicts-one.txt") == 0) - cl_assert(git_index_entry_stage(entry) == 0); + cl_assert(!git_index_entry_is_conflict(entry)); } } @@ -256,7 +312,7 @@ void test_index_conflicts__remove_all_conflicts(void) for (i = 0; i < git_index_entrycount(repo_index); i++) { cl_assert(entry = git_index_get_byindex(repo_index, i)); - cl_assert(git_index_entry_stage(entry) == 0); + cl_assert(!git_index_entry_is_conflict(entry)); } } @@ -272,7 +328,8 @@ void test_index_conflicts__partial(void) memset(&their_entry, 0x0, sizeof(git_index_entry)); ancestor_entry.path = "test-one.txt"; - ancestor_entry.flags |= (1 << GIT_IDXENTRY_STAGESHIFT); + ancestor_entry.mode = 0100644; + GIT_IDXENTRY_STAGE_SET(&ancestor_entry, 1); git_oid_fromstr(&ancestor_entry.id, TEST_ANCESTOR_OID); cl_git_pass(git_index_conflict_add(repo_index, &ancestor_entry, NULL, NULL)); diff --git a/tests/merge/trees/trivial.c b/tests/merge/trees/trivial.c index 55f38248f..2262edda6 100644 --- a/tests/merge/trees/trivial.c +++ b/tests/merge/trees/trivial.c @@ -71,7 +71,7 @@ static int merge_trivial_conflict_entrycount(git_index *index) for (i = 0; i < git_index_entrycount(index); i++) { cl_assert(entry = git_index_get_byindex(index, i)); - if (git_index_entry_stage(entry) > 0) + if (git_index_entry_is_conflict(entry)) count++; } diff --git a/tests/merge/workdir/trivial.c b/tests/merge/workdir/trivial.c index 5cc20f746..4ddaf233d 100644 --- a/tests/merge/workdir/trivial.c +++ b/tests/merge/workdir/trivial.c @@ -66,7 +66,7 @@ static size_t merge_trivial_conflict_entrycount(void) for (i = 0; i < git_index_entrycount(repo_index); i++) { cl_assert(entry = git_index_get_byindex(repo_index, i)); - if (git_index_entry_stage(entry) > 0) + if (git_index_entry_is_conflict(entry)) count++; } diff --git a/tests/object/tree/duplicateentries.c b/tests/object/tree/duplicateentries.c index 65be12e9b..35dd383ec 100644 --- a/tests/object/tree/duplicateentries.c +++ b/tests/object/tree/duplicateentries.c @@ -126,17 +126,17 @@ static void add_fake_conflicts(git_index *index) ancestor_entry.path = "duplicate"; ancestor_entry.mode = GIT_FILEMODE_BLOB; - ancestor_entry.flags |= (1 << GIT_IDXENTRY_STAGESHIFT); + GIT_IDXENTRY_STAGE_SET(&ancestor_entry, 1); git_oid_fromstr(&ancestor_entry.id, "a8233120f6ad708f843d861ce2b7228ec4e3dec6"); our_entry.path = "duplicate"; our_entry.mode = GIT_FILEMODE_BLOB; - ancestor_entry.flags |= (2 << GIT_IDXENTRY_STAGESHIFT); + GIT_IDXENTRY_STAGE_SET(&our_entry, 2); git_oid_fromstr(&our_entry.id, "45b983be36b73c0788dc9cbcb76cbb80fc7bb057"); their_entry.path = "duplicate"; their_entry.mode = GIT_FILEMODE_BLOB; - ancestor_entry.flags |= (3 << GIT_IDXENTRY_STAGESHIFT); + GIT_IDXENTRY_STAGE_SET(&their_entry, 3); git_oid_fromstr(&their_entry.id, "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd"); cl_git_pass(git_index_conflict_add(index, &ancestor_entry, &our_entry, &their_entry)); diff --git a/tests/reset/hard.c b/tests/reset/hard.c index 86d4be2ed..88055adda 100644 --- a/tests/reset/hard.c +++ b/tests/reset/hard.c @@ -108,7 +108,7 @@ static void index_entry_init(git_index *index, int side, git_oid *oid) memset(&entry, 0x0, sizeof(git_index_entry)); entry.path = "conflicting_file"; - entry.flags = (side << GIT_IDXENTRY_STAGESHIFT); + GIT_IDXENTRY_STAGE_SET(&entry, side); entry.mode = 0100644; git_oid_cpy(&entry.id, oid); diff --git a/tests/status/worktree.c b/tests/status/worktree.c index 8897bf9b5..e272c0a10 100644 --- a/tests/status/worktree.c +++ b/tests/status/worktree.c @@ -461,14 +461,17 @@ void test_status_worktree__conflict_with_diff3(void) memset(&their_entry, 0x0, sizeof(git_index_entry)); ancestor_entry.path = "modified_file"; + ancestor_entry.mode = 0100644; git_oid_fromstr(&ancestor_entry.id, "452e4244b5d083ddf0460acf1ecc74db9dcfa11a"); our_entry.path = "modified_file"; + our_entry.mode = 0100644; git_oid_fromstr(&our_entry.id, "452e4244b5d083ddf0460acf1ecc74db9dcfa11a"); their_entry.path = "modified_file"; + their_entry.mode = 0100644; git_oid_fromstr(&their_entry.id, "452e4244b5d083ddf0460acf1ecc74db9dcfa11a"); @@ -484,7 +487,7 @@ void test_status_worktree__conflict_with_diff3(void) cl_git_pass(git_status_file(&status, repo, "modified_file")); - cl_assert_equal_i(GIT_STATUS_INDEX_DELETED | GIT_STATUS_WT_NEW, status); + cl_assert_equal_i(GIT_STATUS_CONFLICTED, status); } static const char *filemode_paths[] = { @@ -614,14 +617,17 @@ void test_status_worktree__conflicted_item(void) memset(&our_entry, 0x0, sizeof(git_index_entry)); memset(&their_entry, 0x0, sizeof(git_index_entry)); + ancestor_entry.mode = 0100644; ancestor_entry.path = "modified_file"; git_oid_fromstr(&ancestor_entry.id, "452e4244b5d083ddf0460acf1ecc74db9dcfa11a"); + our_entry.mode = 0100644; our_entry.path = "modified_file"; git_oid_fromstr(&our_entry.id, "452e4244b5d083ddf0460acf1ecc74db9dcfa11a"); + their_entry.mode = 0100644; their_entry.path = "modified_file"; git_oid_fromstr(&their_entry.id, "452e4244b5d083ddf0460acf1ecc74db9dcfa11a"); @@ -634,9 +640,55 @@ void test_status_worktree__conflicted_item(void) &our_entry, &their_entry)); cl_git_pass(git_status_file(&status, repo, "modified_file")); - cl_assert_equal_i(GIT_STATUS_WT_MODIFIED, status); + cl_assert_equal_i(GIT_STATUS_CONFLICTED, status); + + git_index_free(index); +} + +void test_status_worktree__conflict_has_no_oid(void) +{ + git_repository *repo = cl_git_sandbox_init("status"); + git_index *index; + git_index_entry entry = {0}; + git_status_list *statuslist; + const git_status_entry *status; + git_oid zero_id = {0}; + + entry.mode = 0100644; + entry.path = "modified_file"; + git_oid_fromstr(&entry.id, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"); + + cl_git_pass(git_repository_index(&index, repo)); + cl_git_pass(git_index_conflict_add(index, &entry, &entry, &entry)); + + git_status_list_new(&statuslist, repo, NULL); + + cl_assert_equal_i(16, git_status_list_entrycount(statuslist)); + + status = git_status_byindex(statuslist, 2); + + cl_assert_equal_i(GIT_STATUS_CONFLICTED, status->status); + cl_assert_equal_s("modified_file", status->head_to_index->old_file.path); + cl_assert(!git_oid_equal(&zero_id, &status->head_to_index->old_file.id)); + cl_assert(0 != status->head_to_index->old_file.mode); + cl_assert_equal_s("modified_file", status->head_to_index->new_file.path); + cl_assert_equal_oid(&zero_id, &status->head_to_index->new_file.id); + cl_assert_equal_i(0, status->head_to_index->new_file.mode); + cl_assert_equal_i(0, status->head_to_index->new_file.size); + + cl_assert_equal_s("modified_file", status->index_to_workdir->old_file.path); + cl_assert_equal_oid(&zero_id, &status->index_to_workdir->old_file.id); + cl_assert_equal_i(0, status->index_to_workdir->old_file.mode); + cl_assert_equal_i(0, status->index_to_workdir->old_file.size); + cl_assert_equal_s("modified_file", status->index_to_workdir->new_file.path); + cl_assert( + !git_oid_equal(&zero_id, &status->index_to_workdir->new_file.id) || + !(status->index_to_workdir->new_file.flags & GIT_DIFF_FLAG_VALID_ID)); + cl_assert(0 != status->index_to_workdir->new_file.mode); + cl_assert(0 != status->index_to_workdir->new_file.size); git_index_free(index); + git_status_list_free(statuslist); } static void stage_and_commit(git_repository *repo, const char *path) |