diff options
-rw-r--r-- | CHANGELOG.md | 8 | ||||
-rw-r--r-- | include/git2/diff.h | 21 | ||||
-rw-r--r-- | include/git2/status.h | 1 | ||||
-rw-r--r-- | src/diff.c | 77 | ||||
-rw-r--r-- | src/reset.c | 1 | ||||
-rw-r--r-- | src/status.c | 6 | ||||
-rw-r--r-- | tests/status/worktree.c | 4 |
7 files changed, 93 insertions, 25 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e4bdf919..03e5273ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -100,6 +100,14 @@ 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. + ### API removals * `git_remote_save()` and `git_remote_clear_refspecs()` has been diff --git a/include/git2/diff.h b/include/git2/diff.h index cac3b268a..7505c08c2 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -239,16 +239,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/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 f829fb07c..765956a29 100644 --- a/src/diff.c +++ b/src/diff.c @@ -327,6 +327,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 +351,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 +360,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,8 +747,12 @@ 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_stage(oitem) > 0 || git_index_entry_stage(nitem) > 0) + 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 */ @@ -875,9 +895,29 @@ static int iterator_advance( const git_index_entry **entry, git_iterator *iterator) { - int error; + 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_stage(prev_entry) == 0 || + git_index_entry_stage(*entry) == 0) + 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_iterator_advance(entry, iterator)) == GIT_ITEROVER) { + if (error == GIT_ITEROVER) { *entry = NULL; error = 0; } @@ -926,8 +966,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_stage(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) { @@ -1067,8 +1111,14 @@ static int handle_unmatched_new_item( 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_stage(info->oitem)) + delta_type = GIT_DELTA_CONFLICTED; + + if ((error = diff_delta__from_one(diff, delta_type, info->oitem)) < 0) return error; /* if we are generating TYPECHANGE records then check for that @@ -1234,6 +1284,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); @@ -1243,10 +1295,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 */ @@ -1270,7 +1320,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/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/status/worktree.c b/tests/status/worktree.c index de21a1c31..9be0a4172 100644 --- a/tests/status/worktree.c +++ b/tests/status/worktree.c @@ -487,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[] = { @@ -640,7 +640,7 @@ 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_INDEX_DELETED|GIT_STATUS_WT_NEW, status); + cl_assert_equal_i(GIT_STATUS_CONFLICTED, status); git_index_free(index); } |