summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Martín Nieto <cmn@dwim.me>2015-05-28 18:45:57 +0200
committerCarlos Martín Nieto <cmn@dwim.me>2015-05-28 18:45:57 +0200
commitff8d635adbf1208927c7319178d29b0ed973a107 (patch)
tree329dd098bfefdadf9115c4fa4cff4beddd67c696
parentfb92b48d541edde845e8cde176152f13592db71b (diff)
parent9b3e41f72bd9ab62e8d129713755c3a3a71359bc (diff)
downloadlibgit2-ff8d635adbf1208927c7319178d29b0ed973a107.tar.gz
Merge pull request #3139 from ethomson/diff_conflicts
Include conflicts when diffing
-rw-r--r--CHANGELOG.md19
-rw-r--r--include/git2/diff.h22
-rw-r--r--include/git2/index.h12
-rw-r--r--include/git2/status.h1
-rw-r--r--src/diff.c274
-rw-r--r--src/index.c39
-rw-r--r--src/iterator.c18
-rw-r--r--src/iterator.h2
-rw-r--r--src/merge.c4
-rw-r--r--src/reset.c1
-rw-r--r--src/status.c6
-rw-r--r--tests/checkout/conflict.c2
-rw-r--r--tests/checkout/index.c6
-rw-r--r--tests/checkout/tree.c6
-rw-r--r--tests/clar_libgit2.c8
-rw-r--r--tests/clar_libgit2.h1
-rw-r--r--tests/diff/diff_helpers.c2
-rw-r--r--tests/diff/diff_helpers.h2
-rw-r--r--tests/diff/index.c76
-rw-r--r--tests/diff/workdir.c45
-rw-r--r--tests/index/addall.c153
-rw-r--r--tests/index/collision.c10
-rw-r--r--tests/index/conflicts.c75
-rw-r--r--tests/merge/trees/trivial.c2
-rw-r--r--tests/merge/workdir/trivial.c2
-rw-r--r--tests/object/tree/duplicateentries.c6
-rw-r--r--tests/reset/hard.c2
-rw-r--r--tests/status/worktree.c56
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)