mirror of
https://git.proxmox.com/git/libgit2
synced 2025-07-02 10:44:38 +00:00
Merge pull request #3139 from ethomson/diff_conflicts
Include conflicts when diffing
This commit is contained in:
commit
ff8d635adb
19
CHANGELOG.md
19
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
|
||||
|
@ -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;
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
/**
|
||||
|
276
src/diff.c
276
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,37 +162,44 @@ 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;
|
||||
|
||||
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 (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_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;
|
||||
}
|
||||
|
||||
if (new_oid || !git_oid_iszero(&new_entry->id))
|
||||
delta->new_file.flags |= GIT_DIFF_FLAG_VALID_ID;
|
||||
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 (!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)
|
||||
);
|
||||
|
39
src/index.c
39
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);
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
@ -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);
|
||||
|
@ -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));
|
||||
|
@ -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++;
|
||||
}
|
||||
|
||||
|
@ -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++;
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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,11 +640,57 @@ 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)
|
||||
{
|
||||
git_index *index;
|
||||
|
Loading…
Reference in New Issue
Block a user