Commit ff8d635a by Carlos Martín Nieto

Merge pull request #3139 from ethomson/diff_conflicts

Include conflicts when diffing
parents fb92b48d 9b3e41f7
...@@ -49,6 +49,13 @@ support for HTTPS connections insead of OpenSSL. ...@@ -49,6 +49,13 @@ support for HTTPS connections insead of OpenSSL.
the error message, which allows you to get the "repository not the error message, which allows you to get the "repository not
found" messages. 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 ### API additions
...@@ -98,6 +105,18 @@ support for HTTPS connections insead of OpenSSL. ...@@ -98,6 +105,18 @@ support for HTTPS connections insead of OpenSSL.
configuration of the server, and tools can use this to show messages configuration of the server, and tools can use this to show messages
about failing to communicate with the server. 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 ### API removals
* `git_remote_save()` and `git_remote_clear_refspecs()` have been * `git_remote_save()` and `git_remote_clear_refspecs()` have been
......
...@@ -226,6 +226,7 @@ typedef enum { ...@@ -226,6 +226,7 @@ typedef enum {
GIT_DIFF_FLAG_BINARY = (1u << 0), /**< file(s) treated as binary data */ 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_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_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; } git_diff_flag_t;
/** /**
...@@ -239,16 +240,17 @@ typedef enum { ...@@ -239,16 +240,17 @@ typedef enum {
* DELETED pairs). * DELETED pairs).
*/ */
typedef enum { typedef enum {
GIT_DELTA_UNMODIFIED = 0, /**< no changes */ GIT_DELTA_UNMODIFIED = 0, /**< no changes */
GIT_DELTA_ADDED = 1, /**< entry does not exist in old version */ GIT_DELTA_ADDED = 1, /**< entry does not exist in old version */
GIT_DELTA_DELETED = 2, /**< entry does not exist in new 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_MODIFIED = 3, /**< entry content changed between old and new */
GIT_DELTA_RENAMED = 4, /**< entry was renamed 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_COPIED = 5, /**< entry was copied from another old entry */
GIT_DELTA_IGNORED = 6, /**< entry is ignored item in workdir */ GIT_DELTA_IGNORED = 6, /**< entry is ignored item in workdir */
GIT_DELTA_UNTRACKED = 7, /**< entry is untracked 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_TYPECHANGE = 8, /**< type of entry changed between old and new */
GIT_DELTA_UNREADABLE = 9, /**< entry is unreadable */ GIT_DELTA_UNREADABLE = 9, /**< entry is unreadable */
GIT_DELTA_CONFLICTED = 10, /**< entry in the index is conflicted */
} git_delta_t; } git_delta_t;
/** /**
......
...@@ -430,6 +430,15 @@ GIT_EXTERN(int) git_index_add(git_index *index, const git_index_entry *source_en ...@@ -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); 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 /** @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 ...@@ -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 * 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 * entry may be null to indicate that that file was not present in the
......
...@@ -46,6 +46,7 @@ typedef enum { ...@@ -46,6 +46,7 @@ typedef enum {
GIT_STATUS_WT_UNREADABLE = (1u << 12), GIT_STATUS_WT_UNREADABLE = (1u << 12),
GIT_STATUS_IGNORED = (1u << 14), GIT_STATUS_IGNORED = (1u << 14),
GIT_STATUS_CONFLICTED = (1u << 15),
} git_status_t; } git_status_t;
/** /**
......
...@@ -77,11 +77,24 @@ static int diff_insert_delta( ...@@ -77,11 +77,24 @@ static int diff_insert_delta(
static int diff_delta__from_one( static int diff_delta__from_one(
git_diff *diff, git_diff *diff,
git_delta_t status, 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; git_diff_delta *delta;
const char *matched_pathspec; 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) if ((entry->flags & GIT_IDXENTRY_VALID) != 0)
return 0; return 0;
...@@ -111,20 +124,21 @@ static int diff_delta__from_one( ...@@ -111,20 +124,21 @@ static int diff_delta__from_one(
assert(status != GIT_DELTA_MODIFIED); assert(status != GIT_DELTA_MODIFIED);
delta->nfiles = 1; delta->nfiles = 1;
if (delta->status == GIT_DELTA_DELETED) { if (has_old) {
delta->old_file.mode = entry->mode; delta->old_file.mode = entry->mode;
delta->old_file.size = entry->file_size; delta->old_file.size = entry->file_size;
delta->old_file.flags |= GIT_DIFF_FLAG_EXISTS;
git_oid_cpy(&delta->old_file.id, &entry->id); git_oid_cpy(&delta->old_file.id, &entry->id);
} else /* ADDED, IGNORED, UNTRACKED */ { } else /* ADDED, IGNORED, UNTRACKED */ {
delta->new_file.mode = entry->mode; delta->new_file.mode = entry->mode;
delta->new_file.size = entry->file_size; delta->new_file.size = entry->file_size;
delta->new_file.flags |= GIT_DIFF_FLAG_EXISTS;
git_oid_cpy(&delta->new_file.id, &entry->id); git_oid_cpy(&delta->new_file.id, &entry->id);
} }
delta->old_file.flags |= GIT_DIFF_FLAG_VALID_ID; delta->old_file.flags |= GIT_DIFF_FLAG_VALID_ID;
if (delta->status == GIT_DELTA_DELETED || if (has_old || !git_oid_iszero(&delta->new_file.id))
!git_oid_iszero(&delta->new_file.id))
delta->new_file.flags |= GIT_DIFF_FLAG_VALID_ID; delta->new_file.flags |= GIT_DIFF_FLAG_VALID_ID;
return diff_insert_delta(diff, delta, matched_pathspec); return diff_insert_delta(diff, delta, matched_pathspec);
...@@ -137,9 +151,10 @@ static int diff_delta__from_two( ...@@ -137,9 +151,10 @@ static int diff_delta__from_two(
uint32_t old_mode, uint32_t old_mode,
const git_index_entry *new_entry, const git_index_entry *new_entry,
uint32_t new_mode, uint32_t new_mode,
git_oid *new_oid, const git_oid *new_id,
const char *matched_pathspec) const char *matched_pathspec)
{ {
const git_oid *old_id = &old_entry->id;
git_diff_delta *delta; git_diff_delta *delta;
const char *canonical_path = old_entry->path; const char *canonical_path = old_entry->path;
...@@ -147,38 +162,45 @@ static int diff_delta__from_two( ...@@ -147,38 +162,45 @@ static int diff_delta__from_two(
DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNMODIFIED)) DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNMODIFIED))
return 0; return 0;
if (!new_id)
new_id = &new_entry->id;
if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) { if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) {
uint32_t temp_mode = old_mode; uint32_t temp_mode = old_mode;
const git_index_entry *temp_entry = old_entry; const git_index_entry *temp_entry = old_entry;
const git_oid *temp_id = old_id;
old_entry = new_entry; old_entry = new_entry;
new_entry = temp_entry; new_entry = temp_entry;
old_mode = new_mode; old_mode = new_mode;
new_mode = temp_mode; new_mode = temp_mode;
old_id = new_id;
new_id = temp_id;
} }
delta = diff_delta__alloc(diff, status, canonical_path); delta = diff_delta__alloc(diff, status, canonical_path);
GITERR_CHECK_ALLOC(delta); GITERR_CHECK_ALLOC(delta);
delta->nfiles = 2; delta->nfiles = 2;
git_oid_cpy(&delta->old_file.id, &old_entry->id); if (!git_index_entry_is_conflict(old_entry)) {
delta->old_file.size = old_entry->file_size; delta->old_file.size = old_entry->file_size;
delta->old_file.mode = old_mode; delta->old_file.mode = old_mode;
delta->old_file.flags |= GIT_DIFF_FLAG_VALID_ID; 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); if (!git_index_entry_is_conflict(new_entry)) {
delta->new_file.size = new_entry->file_size; git_oid_cpy(&delta->new_file.id, new_id);
delta->new_file.mode = new_mode; 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 (!git_oid_iszero(&new_entry->id))
if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) delta->new_file.flags |= GIT_DIFF_FLAG_VALID_ID;
git_oid_cpy(&delta->old_file.id, new_oid);
else
git_oid_cpy(&delta->new_file.id, new_oid);
} }
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); return diff_insert_delta(diff, delta, matched_pathspec);
} }
...@@ -327,6 +349,22 @@ static const char *diff_mnemonic_prefix( ...@@ -327,6 +349,22 @@ static const char *diff_mnemonic_prefix(
return pfx; 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) static void diff_set_ignore_case(git_diff *diff, bool ignore_case)
{ {
if (!ignore_case) { if (!ignore_case) {
...@@ -335,7 +373,7 @@ static void diff_set_ignore_case(git_diff *diff, bool ignore_case) ...@@ -335,7 +373,7 @@ static void diff_set_ignore_case(git_diff *diff, bool ignore_case)
diff->strcomp = git__strcmp; diff->strcomp = git__strcmp;
diff->strncomp = git__strncmp; diff->strncomp = git__strncmp;
diff->pfxcomp = git__prefixcmp; 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); git_vector_set_cmp(&diff->deltas, git_diff_delta__cmp);
} else { } else {
...@@ -344,7 +382,7 @@ static void diff_set_ignore_case(git_diff *diff, bool ignore_case) ...@@ -344,7 +382,7 @@ static void diff_set_ignore_case(git_diff *diff, bool ignore_case)
diff->strcomp = git__strcasecmp; diff->strcomp = git__strcasecmp;
diff->strncomp = git__strncasecmp; diff->strncomp = git__strncasecmp;
diff->pfxcomp = git__prefixcmp_icase; 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); git_vector_set_cmp(&diff->deltas, git_diff_delta__casecmp);
} }
...@@ -731,40 +769,47 @@ static int maybe_modified( ...@@ -731,40 +769,47 @@ static int maybe_modified(
new_is_workdir) new_is_workdir)
nmode = (nmode & ~MODE_BITS_MASK) | (omode & MODE_BITS_MASK); 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) */ /* 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; status = GIT_DELTA_UNMODIFIED;
/* support "skip worktree" index bit */ /* 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; status = GIT_DELTA_UNMODIFIED;
/* if basic type of file changed, then split into delete and add */ /* if basic type of file changed, then split into delete and add */
else if (GIT_MODE_TYPE(omode) != GIT_MODE_TYPE(nmode)) { } else if (GIT_MODE_TYPE(omode) != GIT_MODE_TYPE(nmode)) {
if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE)) if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE)) {
status = GIT_DELTA_TYPECHANGE; status = GIT_DELTA_TYPECHANGE;
}
else if (nmode == GIT_FILEMODE_UNREADABLE) { else if (nmode == GIT_FILEMODE_UNREADABLE) {
if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem))) if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem, NULL)))
error = diff_delta__from_one(diff, GIT_DELTA_UNREADABLE, nitem); error = diff_delta__from_one(diff, GIT_DELTA_UNREADABLE, NULL, nitem);
return error; return error;
} }
else { else {
if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem))) if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem, NULL)))
error = diff_delta__from_one(diff, GIT_DELTA_ADDED, nitem); error = diff_delta__from_one(diff, GIT_DELTA_ADDED, NULL, nitem);
return error; return error;
} }
}
/* if oids and modes match (and are valid), then file is unmodified */ /* 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 && omode == nmode &&
!git_oid_iszero(&oitem->id)) !git_oid_iszero(&oitem->id)) {
status = GIT_DELTA_UNMODIFIED; status = GIT_DELTA_UNMODIFIED;
/* if we have an unknown OID and a workdir iterator, then check some /* if we have an unknown OID and a workdir iterator, then check some
* circumstances that can accelerate things or need special handling * 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_ctime = ((diff->diffcaps & GIT_DIFFCAPS_TRUST_CTIME) != 0);
bool use_nanos = ((diff->diffcaps & GIT_DIFFCAPS_TRUST_NANOSECS) != 0); bool use_nanos = ((diff->diffcaps & GIT_DIFFCAPS_TRUST_NANOSECS) != 0);
...@@ -795,12 +840,12 @@ static int maybe_modified( ...@@ -795,12 +840,12 @@ static int maybe_modified(
status = GIT_DELTA_MODIFIED; status = GIT_DELTA_MODIFIED;
modified_uncertain = true; modified_uncertain = true;
} }
}
/* if mode is GITLINK and submodules are ignored, then skip */ /* if mode is GITLINK and submodules are ignored, then skip */
else if (S_ISGITLINK(nmode) && } else if (S_ISGITLINK(nmode) &&
DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES)) DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES)) {
status = GIT_DELTA_UNMODIFIED; status = GIT_DELTA_UNMODIFIED;
}
/* if we got here and decided that the files are modified, but we /* 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 * haven't calculated the OID of the new item, then calculate it now
...@@ -809,6 +854,7 @@ static int maybe_modified( ...@@ -809,6 +854,7 @@ static int maybe_modified(
const git_oid *update_check = const git_oid *update_check =
DIFF_FLAG_IS_SET(diff, GIT_DIFF_UPDATE_INDEX) && omode == nmode ? DIFF_FLAG_IS_SET(diff, GIT_DIFF_UPDATE_INDEX) && omode == nmode ?
&oitem->id : NULL; &oitem->id : NULL;
if ((error = git_diff__oid_for_entry( if ((error = git_diff__oid_for_entry(
&noid, diff, nitem, update_check)) < 0) &noid, diff, nitem, update_check)) < 0)
return error; return error;
...@@ -830,8 +876,9 @@ static int maybe_modified( ...@@ -830,8 +876,9 @@ static int maybe_modified(
DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_CASECHANGE) && DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_CASECHANGE) &&
strcmp(oitem->path, nitem->path) != 0) { strcmp(oitem->path, nitem->path) != 0) {
if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem))) if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem, NULL)))
error = diff_delta__from_one(diff, GIT_DELTA_ADDED, nitem); error = diff_delta__from_one(diff, GIT_DELTA_ADDED, NULL, nitem);
return error; return error;
} }
...@@ -857,6 +904,84 @@ static bool entry_is_prefixed( ...@@ -857,6 +904,84 @@ static bool entry_is_prefixed(
item->path[pathlen] == '/'); 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( static int handle_unmatched_new_item(
git_diff *diff, diff_in_progress *info) git_diff *diff, diff_in_progress *info)
{ {
...@@ -868,8 +993,12 @@ static int handle_unmatched_new_item( ...@@ -868,8 +993,12 @@ static int handle_unmatched_new_item(
/* check if this is a prefix of the other side */ /* check if this is a prefix of the other side */
contains_oitem = entry_is_prefixed(diff, info->oitem, nitem); 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 */ /* 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; delta_type = GIT_DELTA_IGNORED;
if (nitem->mode == GIT_FILEMODE_TREE) { if (nitem->mode == GIT_FILEMODE_TREE) {
...@@ -904,18 +1033,17 @@ static int handle_unmatched_new_item( ...@@ -904,18 +1033,17 @@ static int handle_unmatched_new_item(
git_iterator_status_t untracked_state; git_iterator_status_t untracked_state;
/* attempt to insert record for this directory */ /* 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; return error;
/* if delta wasn't created (because of rules), just skip ahead */ /* if delta wasn't created (because of rules), just skip ahead */
last = diff_delta__last_for_item(diff, nitem); last = diff_delta__last_for_item(diff, nitem);
if (!last) 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 */ /* iterate into dir looking for an actual untracked file */
if ((error = git_iterator_advance_over_with_status( if ((error = iterator_advance_over_with_status(
&info->nitem, &untracked_state, info->new_iter)) < 0 && &info->nitem, &untracked_state, info->new_iter)) < 0)
error != GIT_ITEROVER)
return error; return error;
/* if we found nothing or just ignored items, update the record */ /* if we found nothing or just ignored items, update the record */
...@@ -935,7 +1063,7 @@ static int handle_unmatched_new_item( ...@@ -935,7 +1063,7 @@ static int handle_unmatched_new_item(
/* try to advance into directory if necessary */ /* try to advance into directory if necessary */
if (recurse_into_dir) { 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 real error or no error, proceed with iteration */
if (error != GIT_ENOTFOUND) if (error != GIT_ENOTFOUND)
...@@ -946,7 +1074,7 @@ static int handle_unmatched_new_item( ...@@ -946,7 +1074,7 @@ static int handle_unmatched_new_item(
* it or ignore it * it or ignore it
*/ */
if (contains_oitem) 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; delta_type = GIT_DELTA_IGNORED;
} }
} }
...@@ -955,7 +1083,7 @@ static int handle_unmatched_new_item( ...@@ -955,7 +1083,7 @@ static int handle_unmatched_new_item(
DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS) && DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS) &&
git_iterator_current_tree_is_ignored(info->new_iter)) git_iterator_current_tree_is_ignored(info->new_iter))
/* item contained in ignored directory, so skip over it */ /* 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) else if (info->new_iter->type != GIT_ITERATOR_TYPE_WORKDIR)
delta_type = GIT_DELTA_ADDED; delta_type = GIT_DELTA_ADDED;
...@@ -968,12 +1096,12 @@ static int handle_unmatched_new_item( ...@@ -968,12 +1096,12 @@ static int handle_unmatched_new_item(
/* if this contains a tracked item, treat as normal TREE */ /* if this contains a tracked item, treat as normal TREE */
if (contains_oitem) { 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) if (error != GIT_ENOTFOUND)
return error; return error;
giterr_clear(); 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( ...@@ -986,7 +1114,7 @@ static int handle_unmatched_new_item(
} }
/* Actually create the record for this item if necessary */ /* 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; return error;
/* If user requested TYPECHANGE records, then check for that instead of /* If user requested TYPECHANGE records, then check for that instead of
...@@ -1004,14 +1132,20 @@ static int handle_unmatched_new_item( ...@@ -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( static int handle_unmatched_old_item(
git_diff *diff, diff_in_progress *info) git_diff *diff, diff_in_progress *info)
{ {
int error = diff_delta__from_one(diff, GIT_DELTA_DELETED, info->oitem); git_delta_t delta_type = GIT_DELTA_DELETED;
if (error != 0) 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; return error;
/* if we are generating TYPECHANGE records then check for that /* if we are generating TYPECHANGE records then check for that
...@@ -1033,10 +1167,10 @@ static int handle_unmatched_old_item( ...@@ -1033,10 +1167,10 @@ static int handle_unmatched_old_item(
*/ */
if (S_ISDIR(info->nitem->mode) && if (S_ISDIR(info->nitem->mode) &&
DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_UNTRACKED_DIRS)) 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( static int handle_matched_item(
...@@ -1047,9 +1181,8 @@ static int handle_matched_item( ...@@ -1047,9 +1181,8 @@ static int handle_matched_item(
if ((error = maybe_modified(diff, info)) < 0) if ((error = maybe_modified(diff, info)) < 0)
return error; return error;
if (!(error = git_iterator_advance(&info->oitem, info->old_iter)) || if (!(error = iterator_advance(&info->oitem, info->old_iter)))
error == GIT_ITEROVER) error = iterator_advance(&info->nitem, info->new_iter);
error = git_iterator_advance(&info->nitem, info->new_iter);
return error; return error;
} }
...@@ -1085,13 +1218,9 @@ int git_diff__from_iterators( ...@@ -1085,13 +1218,9 @@ int git_diff__from_iterators(
if ((error = diff_list_apply_options(diff, opts)) < 0) if ((error = diff_list_apply_options(diff, opts)) < 0)
goto cleanup; goto cleanup;
if ((error = git_iterator_current(&info.oitem, old_iter)) < 0 && if ((error = iterator_current(&info.oitem, old_iter)) < 0 ||
error != GIT_ITEROVER) (error = iterator_current(&info.nitem, new_iter)) < 0)
goto cleanup; goto cleanup;
if ((error = git_iterator_current(&info.nitem, new_iter)) < 0 &&
error != GIT_ITEROVER)
goto cleanup;
error = 0;
/* run iterators building diffs */ /* run iterators building diffs */
while (!error && (info.oitem || info.nitem)) { while (!error && (info.oitem || info.nitem)) {
...@@ -1113,10 +1242,6 @@ int git_diff__from_iterators( ...@@ -1113,10 +1242,6 @@ int git_diff__from_iterators(
*/ */
else else
error = handle_matched_item(diff, &info); 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; diff->perf.stat_calls += old_iter->stat_calls + new_iter->stat_calls;
...@@ -1186,6 +1311,8 @@ int git_diff_tree_to_index( ...@@ -1186,6 +1311,8 @@ int git_diff_tree_to_index(
{ {
int error = 0; int error = 0;
bool index_ignore_case = false; bool index_ignore_case = false;
git_iterator_flag_t iflag = GIT_ITERATOR_DONT_IGNORE_CASE |
GIT_ITERATOR_INCLUDE_CONFLICTS;
assert(diff && repo); assert(diff && repo);
...@@ -1195,10 +1322,8 @@ int git_diff_tree_to_index( ...@@ -1195,10 +1322,8 @@ int git_diff_tree_to_index(
index_ignore_case = index->ignore_case; index_ignore_case = index->ignore_case;
DIFF_FROM_ITERATORS( DIFF_FROM_ITERATORS(
git_iterator_for_tree( git_iterator_for_tree(&a, old_tree, iflag, pfx, pfx),
&a, old_tree, GIT_ITERATOR_DONT_IGNORE_CASE, pfx, pfx), git_iterator_for_index(&b, index, iflag, pfx, pfx)
git_iterator_for_index(
&b, index, GIT_ITERATOR_DONT_IGNORE_CASE, pfx, pfx)
); );
/* if index is in case-insensitive order, re-sort deltas to match */ /* if index is in case-insensitive order, re-sort deltas to match */
...@@ -1222,7 +1347,8 @@ int git_diff_index_to_workdir( ...@@ -1222,7 +1347,8 @@ int git_diff_index_to_workdir(
return error; return error;
DIFF_FROM_ITERATORS( 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( git_iterator_for_workdir(
&b, repo, index, NULL, GIT_ITERATOR_DONT_AUTOEXPAND, pfx, pfx) &b, repo, index, NULL, GIT_ITERATOR_DONT_AUTOEXPAND, pfx, pfx)
); );
......
...@@ -1173,6 +1173,9 @@ int git_index_remove_bypath(git_index *index, const char *path) ...@@ -1173,6 +1173,9 @@ int git_index_remove_bypath(git_index *index, const char *path)
ret != GIT_ENOTFOUND)) ret != GIT_ENOTFOUND))
return ret; return ret;
if (ret == GIT_ENOTFOUND)
giterr_clear();
return 0; return 0;
} }
...@@ -1314,6 +1317,30 @@ int git_index_conflict_add(git_index *index, ...@@ -1314,6 +1317,30 @@ int git_index_conflict_add(git_index *index,
(ret = index_entry_dup(&entries[2], INDEX_OWNER(index), their_entry)) < 0) (ret = index_entry_dup(&entries[2], INDEX_OWNER(index), their_entry)) < 0)
goto on_error; 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++) { for (i = 0; i < 3; i++) {
if (entries[i] == NULL) if (entries[i] == NULL)
continue; continue;
...@@ -1321,7 +1348,7 @@ int git_index_conflict_add(git_index *index, ...@@ -1321,7 +1348,7 @@ int git_index_conflict_add(git_index *index,
/* Make sure stage is correct */ /* Make sure stage is correct */
GIT_IDXENTRY_STAGE_SET(entries[i], i + 1); 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; goto on_error;
entries[i] = NULL; /* don't free if later entry fails */ entries[i] = NULL; /* don't free if later entry fails */
...@@ -1510,7 +1537,7 @@ int git_index_conflict_next( ...@@ -1510,7 +1537,7 @@ int git_index_conflict_next(
while (iterator->cur < iterator->index->entries.length) { while (iterator->cur < iterator->index->entries.length) {
entry = git_index_get_byindex(iterator->index, iterator->cur); 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( if ((len = index_conflict__get_byindex(
ancestor_out, ancestor_out,
our_out, our_out,
...@@ -2356,6 +2383,11 @@ int git_index_entry_stage(const git_index_entry *entry) ...@@ -2356,6 +2383,11 @@ int git_index_entry_stage(const git_index_entry *entry)
return GIT_IDXENTRY_STAGE(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 { typedef struct read_tree_data {
git_index *index; git_index *index;
git_vector *old_entries; git_vector *old_entries;
...@@ -2638,7 +2670,8 @@ static int apply_each_file(const git_diff_delta *delta, float progress, void *pa ...@@ -2638,7 +2670,8 @@ static int apply_each_file(const git_diff_delta *delta, float progress, void *pa
if (error < 0) /* actual error */ if (error < 0) /* actual error */
return 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); error = git_index_remove_bypath(data->index, path);
else else
error = git_index_add_bypath(data->index, delta->new_file.path); error = git_index_add_bypath(data->index, delta->new_file.path);
......
...@@ -46,6 +46,7 @@ ...@@ -46,6 +46,7 @@
#define iterator__include_trees(I) iterator__flag(I,INCLUDE_TREES) #define iterator__include_trees(I) iterator__flag(I,INCLUDE_TREES)
#define iterator__dont_autoexpand(I) iterator__flag(I,DONT_AUTOEXPAND) #define iterator__dont_autoexpand(I) iterator__flag(I,DONT_AUTOEXPAND)
#define iterator__do_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 GIT_ITERATOR_FIRST_ACCESS (1 << 15)
#define iterator__has_been_accessed(I) iterator__flag(I,FIRST_ACCESS) #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) ...@@ -668,13 +669,16 @@ static const git_index_entry *index_iterator__index_entry(index_iterator *ii)
return ie; 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 && if (!iterator__include_conflicts(ii)) {
git_index_entry_stage(ie) != 0) while (ie && git_index_entry_is_conflict(ie)) {
ii->current++; ii->current++;
ie = index_iterator__index_entry(ii);
}
}
return ie; return ie;
} }
...@@ -702,7 +706,7 @@ static void index_iterator__next_prefix_tree(index_iterator *ii) ...@@ -702,7 +706,7 @@ static void index_iterator__next_prefix_tree(index_iterator *ii)
static int index_iterator__first_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; const char *scan, *prior, *slash;
if (!ie || !iterator__include_trees(ii)) if (!ie || !iterator__include_trees(ii))
...@@ -825,7 +829,7 @@ static int index_iterator__reset( ...@@ -825,7 +829,7 @@ static int index_iterator__reset(
git_index_snapshot_find( git_index_snapshot_find(
&ii->current, &ii->entries, ii->entry_srch, ii->base.start, 0, 0); &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; return 0;
if (git_buf_sets(&ii->partial, ie->path) < 0) if (git_buf_sets(&ii->partial, ie->path) < 0)
......
...@@ -33,6 +33,8 @@ typedef enum { ...@@ -33,6 +33,8 @@ typedef enum {
GIT_ITERATOR_DONT_AUTOEXPAND = (1u << 3), GIT_ITERATOR_DONT_AUTOEXPAND = (1u << 3),
/** convert precomposed unicode to decomposed unicode */ /** convert precomposed unicode to decomposed unicode */
GIT_ITERATOR_PRECOMPOSE_UNICODE = (1u << 4), GIT_ITERATOR_PRECOMPOSE_UNICODE = (1u << 4),
/** include conflicts */
GIT_ITERATOR_INCLUDE_CONFLICTS = (1u << 5),
} git_iterator_flag_t; } git_iterator_flag_t;
typedef struct { typedef struct {
......
...@@ -2492,7 +2492,7 @@ int git_merge__check_result(git_repository *repo, git_index *index_new) ...@@ -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++) { for (i = 0; i < git_index_entrycount(index_new); i++) {
e = git_index_get_byindex(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 || (git_vector_last(&paths) == NULL ||
strcmp(git_vector_last(&paths), e->path) != 0)) { strcmp(git_vector_last(&paths), e->path) != 0)) {
...@@ -2544,7 +2544,7 @@ int git_merge__append_conflicts_to_merge_msg( ...@@ -2544,7 +2544,7 @@ int git_merge__append_conflicts_to_merge_msg(
for (i = 0; i < git_index_entrycount(index); i++) { for (i = 0; i < git_index_entrycount(index); i++) {
const git_index_entry *e = git_index_get_byindex(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; continue;
if (last == NULL || strcmp(e->path, last) != 0) if (last == NULL || strcmp(e->path, last) != 0)
......
...@@ -63,6 +63,7 @@ int git_reset_default( ...@@ -63,6 +63,7 @@ int git_reset_default(
assert(delta->status == GIT_DELTA_ADDED || assert(delta->status == GIT_DELTA_ADDED ||
delta->status == GIT_DELTA_MODIFIED || delta->status == GIT_DELTA_MODIFIED ||
delta->status == GIT_DELTA_CONFLICTED ||
delta->status == GIT_DELTA_DELETED); delta->status == GIT_DELTA_DELETED);
error = git_index_conflict_remove(index, delta->old_file.path); error = git_index_conflict_remove(index, delta->old_file.path);
......
...@@ -44,6 +44,9 @@ static unsigned int index_delta2status(const git_diff_delta *head2idx) ...@@ -44,6 +44,9 @@ static unsigned int index_delta2status(const git_diff_delta *head2idx)
case GIT_DELTA_TYPECHANGE: case GIT_DELTA_TYPECHANGE:
st = GIT_STATUS_INDEX_TYPECHANGE; st = GIT_STATUS_INDEX_TYPECHANGE;
break; break;
case GIT_DELTA_CONFLICTED:
st = GIT_STATUS_CONFLICTED;
break;
default: default:
break; break;
} }
...@@ -102,6 +105,9 @@ static unsigned int workdir_delta2status( ...@@ -102,6 +105,9 @@ static unsigned int workdir_delta2status(
case GIT_DELTA_TYPECHANGE: case GIT_DELTA_TYPECHANGE:
st = GIT_STATUS_WT_TYPECHANGE; st = GIT_STATUS_WT_TYPECHANGE;
break; break;
case GIT_DELTA_CONFLICTED:
st = GIT_STATUS_CONFLICTED;
break;
default: default:
break; break;
} }
......
...@@ -102,7 +102,7 @@ static void create_index(struct checkout_index_entry *entries, size_t entries_le ...@@ -102,7 +102,7 @@ static void create_index(struct checkout_index_entry *entries, size_t entries_le
memset(&entry, 0x0, sizeof(git_index_entry)); memset(&entry, 0x0, sizeof(git_index_entry));
entry.mode = entries[i].mode; 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); git_oid_fromstr(&entry.id, entries[i].oid_str);
entry.path = entries[i].path; entry.path = entries[i].path;
......
...@@ -685,15 +685,15 @@ static void add_conflict(git_index *index, const char *path) ...@@ -685,15 +685,15 @@ static void add_conflict(git_index *index, const char *path)
entry.path = path; entry.path = path;
git_oid_fromstr(&entry.id, "d427e0b2e138501a3d15cc376077a3631e15bd46"); 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)); cl_git_pass(git_index_add(index, &entry));
git_oid_fromstr(&entry.id, "4e886e602529caa9ab11d71f86634bd1b6e0de10"); 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)); cl_git_pass(git_index_add(index, &entry));
git_oid_fromstr(&entry.id, "2bd0a343aeef7a2cf0d158478966a6e587ff3863"); 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)); cl_git_pass(git_index_add(index, &entry));
} }
......
...@@ -893,16 +893,16 @@ static void create_conflict(const char *path) ...@@ -893,16 +893,16 @@ static void create_conflict(const char *path)
memset(&entry, 0x0, sizeof(git_index_entry)); memset(&entry, 0x0, sizeof(git_index_entry));
entry.mode = 0100644; entry.mode = 0100644;
entry.flags = 1 << GIT_IDXENTRY_STAGESHIFT; GIT_IDXENTRY_STAGE_SET(&entry, 1);
git_oid_fromstr(&entry.id, "d427e0b2e138501a3d15cc376077a3631e15bd46"); git_oid_fromstr(&entry.id, "d427e0b2e138501a3d15cc376077a3631e15bd46");
entry.path = path; entry.path = path;
cl_git_pass(git_index_add(index, &entry)); 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"); git_oid_fromstr(&entry.id, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf");
cl_git_pass(git_index_add(index, &entry)); 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"); git_oid_fromstr(&entry.id, "2bd0a343aeef7a2cf0d158478966a6e587ff3863");
cl_git_pass(git_index_add(index, &entry)); cl_git_pass(git_index_add(index, &entry));
......
...@@ -197,6 +197,14 @@ git_repository *cl_git_sandbox_init(const char *sandbox) ...@@ -197,6 +197,14 @@ git_repository *cl_git_sandbox_init(const char *sandbox)
return _cl_repo; 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) git_repository *cl_git_sandbox_reopen(void)
{ {
if (_cl_repo) { if (_cl_repo) {
......
...@@ -127,6 +127,7 @@ int cl_rename(const char *source, const char *dest); ...@@ -127,6 +127,7 @@ int cl_rename(const char *source, const char *dest);
/* Git sandbox setup helpers */ /* Git sandbox setup helpers */
git_repository *cl_git_sandbox_init(const char *sandbox); 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); void cl_git_sandbox_cleanup(void);
git_repository *cl_git_sandbox_reopen(void); git_repository *cl_git_sandbox_reopen(void);
......
...@@ -68,7 +68,7 @@ int diff_file_cb( ...@@ -68,7 +68,7 @@ int diff_file_cb(
if ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0) if ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0)
e->files_binary++; e->files_binary++;
cl_assert(delta->status <= GIT_DELTA_TYPECHANGE); cl_assert(delta->status <= GIT_DELTA_CONFLICTED);
e->file_status[delta->status] += 1; e->file_status[delta->status] += 1;
......
...@@ -8,7 +8,7 @@ typedef struct { ...@@ -8,7 +8,7 @@ typedef struct {
int files; int files;
int files_binary; 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 hunks;
int hunk_new_lines; int hunk_new_lines;
......
...@@ -163,3 +163,79 @@ void test_diff_index__checks_options_version(void) ...@@ -163,3 +163,79 @@ void test_diff_index__checks_options_version(void)
git_tree_free(a); 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);
}
...@@ -68,6 +68,51 @@ void test_diff_workdir__to_index(void) ...@@ -68,6 +68,51 @@ void test_diff_workdir__to_index(void)
git_diff_free(diff); 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) void test_diff_workdir__to_index_with_assume_unchanged(void)
{ {
git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
......
...@@ -12,10 +12,7 @@ void test_index_addall__initialize(void) ...@@ -12,10 +12,7 @@ void test_index_addall__initialize(void)
void test_index_addall__cleanup(void) void test_index_addall__cleanup(void)
{ {
git_repository_free(g_repo); cl_git_sandbox_cleanup();
g_repo = NULL;
cl_fixture_cleanup(TEST_DIR);
} }
#define STATUS_INDEX_FLAGS \ #define STATUS_INDEX_FLAGS \
...@@ -36,6 +33,7 @@ typedef struct { ...@@ -36,6 +33,7 @@ typedef struct {
size_t wt_dels; size_t wt_dels;
size_t wt_mods; size_t wt_mods;
size_t ignores; size_t ignores;
size_t conflicts;
} index_status_counts; } index_status_counts;
static int index_status_cb( static int index_status_cb(
...@@ -67,6 +65,8 @@ static int index_status_cb( ...@@ -67,6 +65,8 @@ static int index_status_cb(
if (status_flags & GIT_STATUS_IGNORED) if (status_flags & GIT_STATUS_IGNORED)
vals->ignores++; vals->ignores++;
if (status_flags & GIT_STATUS_CONFLICTED)
vals->conflicts++;
return 0; return 0;
} }
...@@ -75,7 +75,7 @@ static void check_status_at_line( ...@@ -75,7 +75,7 @@ static void check_status_at_line(
git_repository *repo, git_repository *repo,
size_t index_adds, size_t index_dels, size_t index_mods, 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, 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; index_status_counts vals;
...@@ -97,10 +97,12 @@ static void check_status_at_line( ...@@ -97,10 +97,12 @@ static void check_status_at_line(
file,line,"wrong workdir mods", 1, "%"PRIuZ, wt_mods, vals.wt_mods); file,line,"wrong workdir mods", 1, "%"PRIuZ, wt_mods, vals.wt_mods);
clar__assert_equal( clar__assert_equal(
file,line,"wrong ignores", 1, "%"PRIuZ, ignores, vals.ignores); 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) \ #define check_status(R,IA,ID,IM,WA,WD,WM,IG,C) \
check_status_at_line(R,IA,ID,IM,WA,WD,WM,IG,__FILE__,__LINE__) 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) 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) ...@@ -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) 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) 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"); cl_git_mkfile(TEST_DIR "/file.foo", "a file");
if (check_every_step) 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"); cl_git_mkfile(TEST_DIR "/.gitignore", "*.foo\n");
if (check_every_step) 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"); cl_git_mkfile(TEST_DIR "/file.bar", "another file");
if (check_every_step) 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) void test_index_addall__repo_lifecycle(void)
...@@ -171,107 +174,107 @@ 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)); cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL));
check_stat_data(index, TEST_DIR "/file.bar", true); 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"); cl_git_rewritefile(TEST_DIR "/file.bar", "new content for file");
check_stat_data(index, TEST_DIR "/file.bar", false); 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 "/file.zzz", "yet another one");
cl_git_mkfile(TEST_DIR "/other.zzz", "yet another one"); cl_git_mkfile(TEST_DIR "/other.zzz", "yet another one");
cl_git_mkfile(TEST_DIR "/more.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)); cl_git_pass(git_index_update_all(index, NULL, NULL, NULL));
check_stat_data(index, TEST_DIR "/file.bar", true); 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)); cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL));
check_stat_data(index, TEST_DIR "/file.zzz", true); 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"); 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")) { if (cl_repo_get_bool(g_repo, "core.filemode")) {
cl_git_pass(git_index_update_all(index, NULL, NULL, NULL)); cl_git_pass(git_index_update_all(index, NULL, NULL, NULL));
cl_must_pass(p_chmod(TEST_DIR "/file.zzz", 0777)); cl_must_pass(p_chmod(TEST_DIR "/file.zzz", 0777));
cl_git_pass(git_index_update_all(index, NULL, NULL, NULL)); 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 */ /* go back to what we had before */
cl_must_pass(p_chmod(TEST_DIR "/file.zzz", 0666)); cl_must_pass(p_chmod(TEST_DIR "/file.zzz", 0666));
cl_git_pass(git_index_update_all(index, NULL, NULL, NULL)); 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 */ /* attempt to add an ignored file - does nothing */
strs[0] = "file.foo"; strs[0] = "file.foo";
cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL)); 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 */ /* add with check - should generate error */
error = git_index_add_all( error = git_index_add_all(
index, &paths, GIT_INDEX_ADD_CHECK_PATHSPEC, NULL, NULL); index, &paths, GIT_INDEX_ADD_CHECK_PATHSPEC, NULL, NULL);
cl_assert_equal_i(GIT_EINVALIDSPEC, error); 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 */ /* add with force - should allow */
cl_git_pass(git_index_add_all( cl_git_pass(git_index_add_all(
index, &paths, GIT_INDEX_ADD_FORCE, NULL, NULL)); index, &paths, GIT_INDEX_ADD_FORCE, NULL, NULL));
check_stat_data(index, TEST_DIR "/file.foo", true); 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 */ /* now it's in the index, so regular add should work */
cl_git_rewritefile(TEST_DIR "/file.foo", "new content for file"); cl_git_rewritefile(TEST_DIR "/file.foo", "new content for file");
check_stat_data(index, TEST_DIR "/file.foo", false); 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)); cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL));
check_stat_data(index, TEST_DIR "/file.foo", true); 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")); cl_git_pass(git_index_add_bypath(index, "more.zzz"));
check_stat_data(index, TEST_DIR "/more.zzz", true); 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"); 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")); cl_git_pass(git_index_add_bypath(index, "file.zzz"));
check_stat_data(index, TEST_DIR "/file.zzz", true); 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"; strs[0] = "*.zzz";
cl_git_pass(git_index_remove_all(index, &paths, NULL, NULL)); 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")); 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"); 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")); 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 */ /* update_all should be able to remove entries */
cl_git_pass(git_index_update_all(index, NULL, NULL, NULL)); 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] = "*"; strs[0] = "*";
cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL)); 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 */ /* must be able to remove at any position while still updating other files */
cl_must_pass(p_unlink(TEST_DIR "/.gitignore")); cl_must_pass(p_unlink(TEST_DIR "/.gitignore"));
cl_git_rewritefile(TEST_DIR "/file.zzz", "reconstructed file"); cl_git_rewritefile(TEST_DIR "/file.zzz", "reconstructed file");
cl_git_rewritefile(TEST_DIR "/more.zzz", "altered file reality"); 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)); 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 /* this behavior actually matches 'git add -u' where "file.zzz" has
* been removed from the index, so when you go to update, even though * 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 * 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) ...@@ -293,14 +296,14 @@ void test_index_addall__files_in_folders(void)
cl_git_pass(git_index_add_all(index, NULL, 0, NULL, NULL)); cl_git_pass(git_index_add_all(index, NULL, 0, NULL, NULL));
check_stat_data(index, TEST_DIR "/file.bar", true); 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_must_pass(p_mkdir(TEST_DIR "/subdir", 0777));
cl_git_mkfile(TEST_DIR "/subdir/file", "hello!\n"); 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)); 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); git_index_free(index);
} }
...@@ -337,7 +340,7 @@ void test_index_addall__callback_filtering(void) ...@@ -337,7 +340,7 @@ void test_index_addall__callback_filtering(void)
cl_git_pass( cl_git_pass(
git_index_add_all(index, NULL, 0, addall_match_prefix, "file.")); git_index_add_all(index, NULL, 0, addall_match_prefix, "file."));
check_stat_data(index, TEST_DIR "/file.bar", true); 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 "/file.zzz", "yet another one");
cl_git_mkfile(TEST_DIR "/more.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) ...@@ -345,32 +348,32 @@ void test_index_addall__callback_filtering(void)
cl_git_pass(git_index_update_all(index, NULL, NULL, NULL)); cl_git_pass(git_index_update_all(index, NULL, NULL, NULL));
check_stat_data(index, TEST_DIR "/file.bar", true); 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( cl_git_pass(
git_index_add_all(index, NULL, 0, addall_match_prefix, "other")); git_index_add_all(index, NULL, 0, addall_match_prefix, "other"));
check_stat_data(index, TEST_DIR "/other.zzz", true); 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( cl_git_pass(
git_index_add_all(index, NULL, 0, addall_match_suffix, ".zzz")); 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( cl_git_pass(
git_index_remove_all(index, NULL, addall_match_suffix, ".zzz")); 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( cl_git_fail_with(
git_index_add_all(index, NULL, 0, addall_cancel_at, "more.zzz"), -123); 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( cl_git_fail_with(
git_index_add_all(index, NULL, 0, addall_cancel_at, "other.zzz"), -123); 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( cl_git_pass(
git_index_add_all(index, NULL, 0, addall_match_suffix, ".zzz")); 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 "/file.zzz"));
cl_must_pass(p_unlink(TEST_DIR "/more.zzz")); cl_must_pass(p_unlink(TEST_DIR "/more.zzz"));
...@@ -380,13 +383,65 @@ void test_index_addall__callback_filtering(void) ...@@ -380,13 +383,65 @@ void test_index_addall__callback_filtering(void)
git_index_update_all(index, NULL, addall_cancel_at, "more.zzz"), -123); git_index_update_all(index, NULL, addall_cancel_at, "more.zzz"), -123);
/* file.zzz removed from index (so Index Adds 5 -> 4) and /* file.zzz removed from index (so Index Adds 5 -> 4) and
* more.zzz + other.zzz removed (so Worktree Dels 0 -> 2) */ * 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( cl_git_fail_with(
git_index_update_all(index, NULL, addall_cancel_at, "other.zzz"), -123); git_index_update_all(index, NULL, addall_cancel_at, "other.zzz"), -123);
/* more.zzz removed from index (so Index Adds 4 -> 3) and /* more.zzz removed from index (so Index Adds 4 -> 3) and
* Just other.zzz removed (so Worktree Dels 2 -> 1) */ * 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); 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
...@@ -57,7 +57,7 @@ void test_index_collision__add_with_highstage_1(void) ...@@ -57,7 +57,7 @@ void test_index_collision__add_with_highstage_1(void)
git_oid_fromstr(&entry.id, "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"); git_oid_fromstr(&entry.id, "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391");
entry.path = "a/b"; entry.path = "a/b";
entry.flags = (2 << GIT_IDXENTRY_STAGESHIFT); GIT_IDXENTRY_STAGE_SET(&entry, 2);
cl_git_pass(git_index_add(index, &entry)); cl_git_pass(git_index_add(index, &entry));
/* create a blob beneath the previous tree entry */ /* create a blob beneath the previous tree entry */
...@@ -67,7 +67,7 @@ void test_index_collision__add_with_highstage_1(void) ...@@ -67,7 +67,7 @@ void test_index_collision__add_with_highstage_1(void)
/* create another tree entry above the blob */ /* create another tree entry above the blob */
entry.path = "a/b"; entry.path = "a/b";
entry.flags = (1 << GIT_IDXENTRY_STAGESHIFT); GIT_IDXENTRY_STAGE_SET(&entry, 1);
cl_git_pass(git_index_add(index, &entry)); cl_git_pass(git_index_add(index, &entry));
git_index_free(index); git_index_free(index);
...@@ -89,17 +89,17 @@ void test_index_collision__add_with_highstage_2(void) ...@@ -89,17 +89,17 @@ void test_index_collision__add_with_highstage_2(void)
git_oid_fromstr(&entry.id, "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"); git_oid_fromstr(&entry.id, "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391");
entry.path = "a/b/c"; 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)); cl_git_pass(git_index_add(index, &entry));
/* create a blob beneath the previous tree entry */ /* create a blob beneath the previous tree entry */
entry.path = "a/b/c"; 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)); cl_git_pass(git_index_add(index, &entry));
/* create another tree entry above the blob */ /* create another tree entry above the blob */
entry.path = "a/b"; entry.path = "a/b";
entry.flags = (3 << GIT_IDXENTRY_STAGESHIFT); GIT_IDXENTRY_STAGE_SET(&entry, 3);
cl_git_pass(git_index_add(index, &entry)); cl_git_pass(git_index_add(index, &entry));
git_index_free(index); git_index_free(index);
......
...@@ -16,6 +16,7 @@ static git_index *repo_index; ...@@ -16,6 +16,7 @@ static git_index *repo_index;
#define CONFLICTS_TWO_OUR_OID "8b3f43d2402825c200f835ca1762413e386fd0b2" #define CONFLICTS_TWO_OUR_OID "8b3f43d2402825c200f835ca1762413e386fd0b2"
#define CONFLICTS_TWO_THEIR_OID "220bd62631c8cf7a83ef39c6b94595f00517211e" #define CONFLICTS_TWO_THEIR_OID "220bd62631c8cf7a83ef39c6b94595f00517211e"
#define TEST_STAGED_OID "beefdadafeedabedcafedeedbabedeadbeaddeaf"
#define TEST_ANCESTOR_OID "f00ff00ff00ff00ff00ff00ff00ff00ff00ff00f" #define TEST_ANCESTOR_OID "f00ff00ff00ff00ff00ff00ff00ff00ff00ff00f"
#define TEST_OUR_OID "b44bb44bb44bb44bb44bb44bb44bb44bb44bb44b" #define TEST_OUR_OID "b44bb44bb44bb44bb44bb44bb44bb44bb44bb44b"
#define TEST_THEIR_OID "0123456789abcdef0123456789abcdef01234567" #define TEST_THEIR_OID "0123456789abcdef0123456789abcdef01234567"
...@@ -46,15 +47,18 @@ void test_index_conflicts__add(void) ...@@ -46,15 +47,18 @@ void test_index_conflicts__add(void)
memset(&their_entry, 0x0, sizeof(git_index_entry)); memset(&their_entry, 0x0, sizeof(git_index_entry));
ancestor_entry.path = "test-one.txt"; 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); git_oid_fromstr(&ancestor_entry.id, TEST_ANCESTOR_OID);
our_entry.path = "test-one.txt"; 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); git_oid_fromstr(&our_entry.id, TEST_OUR_OID);
their_entry.path = "test-one.txt"; 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); 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_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) ...@@ -74,15 +78,18 @@ void test_index_conflicts__add_fixes_incorrect_stage(void)
memset(&their_entry, 0x0, sizeof(git_index_entry)); memset(&their_entry, 0x0, sizeof(git_index_entry));
ancestor_entry.path = "test-one.txt"; 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); git_oid_fromstr(&ancestor_entry.id, TEST_ANCESTOR_OID);
our_entry.path = "test-one.txt"; 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); git_oid_fromstr(&our_entry.id, TEST_OUR_OID);
their_entry.path = "test-one.txt"; 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); 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_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) ...@@ -96,6 +103,55 @@ void test_index_conflicts__add_fixes_incorrect_stage(void)
cl_assert(git_index_entry_stage(conflict_entry[2]) == 3); 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) void test_index_conflicts__get(void)
{ {
const git_index_entry *conflict_entry[3]; const git_index_entry *conflict_entry[3];
...@@ -216,7 +272,7 @@ void test_index_conflicts__moved_to_reuc_on_add(void) ...@@ -216,7 +272,7 @@ void test_index_conflicts__moved_to_reuc_on_add(void)
cl_assert(entry = git_index_get_byindex(repo_index, i)); cl_assert(entry = git_index_get_byindex(repo_index, i));
if (strcmp(entry->path, "conflicts-one.txt") == 0) 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) ...@@ -256,7 +312,7 @@ void test_index_conflicts__remove_all_conflicts(void)
for (i = 0; i < git_index_entrycount(repo_index); i++) { for (i = 0; i < git_index_entrycount(repo_index); i++) {
cl_assert(entry = git_index_get_byindex(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) ...@@ -272,7 +328,8 @@ void test_index_conflicts__partial(void)
memset(&their_entry, 0x0, sizeof(git_index_entry)); memset(&their_entry, 0x0, sizeof(git_index_entry));
ancestor_entry.path = "test-one.txt"; 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); git_oid_fromstr(&ancestor_entry.id, TEST_ANCESTOR_OID);
cl_git_pass(git_index_conflict_add(repo_index, &ancestor_entry, NULL, NULL)); cl_git_pass(git_index_conflict_add(repo_index, &ancestor_entry, NULL, NULL));
......
...@@ -71,7 +71,7 @@ static int merge_trivial_conflict_entrycount(git_index *index) ...@@ -71,7 +71,7 @@ static int merge_trivial_conflict_entrycount(git_index *index)
for (i = 0; i < git_index_entrycount(index); i++) { for (i = 0; i < git_index_entrycount(index); i++) {
cl_assert(entry = git_index_get_byindex(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++; count++;
} }
......
...@@ -66,7 +66,7 @@ static size_t merge_trivial_conflict_entrycount(void) ...@@ -66,7 +66,7 @@ static size_t merge_trivial_conflict_entrycount(void)
for (i = 0; i < git_index_entrycount(repo_index); i++) { for (i = 0; i < git_index_entrycount(repo_index); i++) {
cl_assert(entry = git_index_get_byindex(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++; count++;
} }
......
...@@ -126,17 +126,17 @@ static void add_fake_conflicts(git_index *index) ...@@ -126,17 +126,17 @@ static void add_fake_conflicts(git_index *index)
ancestor_entry.path = "duplicate"; ancestor_entry.path = "duplicate";
ancestor_entry.mode = GIT_FILEMODE_BLOB; 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"); git_oid_fromstr(&ancestor_entry.id, "a8233120f6ad708f843d861ce2b7228ec4e3dec6");
our_entry.path = "duplicate"; our_entry.path = "duplicate";
our_entry.mode = GIT_FILEMODE_BLOB; 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"); git_oid_fromstr(&our_entry.id, "45b983be36b73c0788dc9cbcb76cbb80fc7bb057");
their_entry.path = "duplicate"; their_entry.path = "duplicate";
their_entry.mode = GIT_FILEMODE_BLOB; 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"); git_oid_fromstr(&their_entry.id, "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd");
cl_git_pass(git_index_conflict_add(index, &ancestor_entry, &our_entry, &their_entry)); cl_git_pass(git_index_conflict_add(index, &ancestor_entry, &our_entry, &their_entry));
......
...@@ -108,7 +108,7 @@ static void index_entry_init(git_index *index, int side, git_oid *oid) ...@@ -108,7 +108,7 @@ static void index_entry_init(git_index *index, int side, git_oid *oid)
memset(&entry, 0x0, sizeof(git_index_entry)); memset(&entry, 0x0, sizeof(git_index_entry));
entry.path = "conflicting_file"; entry.path = "conflicting_file";
entry.flags = (side << GIT_IDXENTRY_STAGESHIFT); GIT_IDXENTRY_STAGE_SET(&entry, side);
entry.mode = 0100644; entry.mode = 0100644;
git_oid_cpy(&entry.id, oid); git_oid_cpy(&entry.id, oid);
......
...@@ -461,14 +461,17 @@ void test_status_worktree__conflict_with_diff3(void) ...@@ -461,14 +461,17 @@ void test_status_worktree__conflict_with_diff3(void)
memset(&their_entry, 0x0, sizeof(git_index_entry)); memset(&their_entry, 0x0, sizeof(git_index_entry));
ancestor_entry.path = "modified_file"; ancestor_entry.path = "modified_file";
ancestor_entry.mode = 0100644;
git_oid_fromstr(&ancestor_entry.id, git_oid_fromstr(&ancestor_entry.id,
"452e4244b5d083ddf0460acf1ecc74db9dcfa11a"); "452e4244b5d083ddf0460acf1ecc74db9dcfa11a");
our_entry.path = "modified_file"; our_entry.path = "modified_file";
our_entry.mode = 0100644;
git_oid_fromstr(&our_entry.id, git_oid_fromstr(&our_entry.id,
"452e4244b5d083ddf0460acf1ecc74db9dcfa11a"); "452e4244b5d083ddf0460acf1ecc74db9dcfa11a");
their_entry.path = "modified_file"; their_entry.path = "modified_file";
their_entry.mode = 0100644;
git_oid_fromstr(&their_entry.id, git_oid_fromstr(&their_entry.id,
"452e4244b5d083ddf0460acf1ecc74db9dcfa11a"); "452e4244b5d083ddf0460acf1ecc74db9dcfa11a");
...@@ -484,7 +487,7 @@ void test_status_worktree__conflict_with_diff3(void) ...@@ -484,7 +487,7 @@ void test_status_worktree__conflict_with_diff3(void)
cl_git_pass(git_status_file(&status, repo, "modified_file")); 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[] = { static const char *filemode_paths[] = {
...@@ -614,14 +617,17 @@ void test_status_worktree__conflicted_item(void) ...@@ -614,14 +617,17 @@ void test_status_worktree__conflicted_item(void)
memset(&our_entry, 0x0, sizeof(git_index_entry)); memset(&our_entry, 0x0, sizeof(git_index_entry));
memset(&their_entry, 0x0, sizeof(git_index_entry)); memset(&their_entry, 0x0, sizeof(git_index_entry));
ancestor_entry.mode = 0100644;
ancestor_entry.path = "modified_file"; ancestor_entry.path = "modified_file";
git_oid_fromstr(&ancestor_entry.id, git_oid_fromstr(&ancestor_entry.id,
"452e4244b5d083ddf0460acf1ecc74db9dcfa11a"); "452e4244b5d083ddf0460acf1ecc74db9dcfa11a");
our_entry.mode = 0100644;
our_entry.path = "modified_file"; our_entry.path = "modified_file";
git_oid_fromstr(&our_entry.id, git_oid_fromstr(&our_entry.id,
"452e4244b5d083ddf0460acf1ecc74db9dcfa11a"); "452e4244b5d083ddf0460acf1ecc74db9dcfa11a");
their_entry.mode = 0100644;
their_entry.path = "modified_file"; their_entry.path = "modified_file";
git_oid_fromstr(&their_entry.id, git_oid_fromstr(&their_entry.id,
"452e4244b5d083ddf0460acf1ecc74db9dcfa11a"); "452e4244b5d083ddf0460acf1ecc74db9dcfa11a");
...@@ -634,9 +640,55 @@ void test_status_worktree__conflicted_item(void) ...@@ -634,9 +640,55 @@ void test_status_worktree__conflicted_item(void)
&our_entry, &their_entry)); &our_entry, &their_entry));
cl_git_pass(git_status_file(&status, repo, "modified_file")); 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_index_free(index);
git_status_list_free(statuslist);
} }
static void stage_and_commit(git_repository *repo, const char *path) static void stage_and_commit(git_repository *repo, const char *path)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment