summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md8
-rw-r--r--include/git2/diff.h21
-rw-r--r--include/git2/status.h1
-rw-r--r--src/diff.c77
-rw-r--r--src/reset.c1
-rw-r--r--src/status.c6
-rw-r--r--tests/status/worktree.c4
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);
}