summaryrefslogtreecommitdiff
path: root/src/checkout.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/checkout.c')
-rw-r--r--src/checkout.c205
1 files changed, 154 insertions, 51 deletions
diff --git a/src/checkout.c b/src/checkout.c
index fb425bb0c..8203c39ea 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -37,9 +37,10 @@ enum {
CHECKOUT_ACTION__UPDATE_BLOB = 2,
CHECKOUT_ACTION__UPDATE_SUBMODULE = 4,
CHECKOUT_ACTION__CONFLICT = 8,
- CHECKOUT_ACTION__UPDATE_CONFLICT = 16,
- CHECKOUT_ACTION__MAX = 16,
- CHECKOUT_ACTION__DEFER_REMOVE = 32,
+ CHECKOUT_ACTION__REMOVE_CONFLICT = 16,
+ CHECKOUT_ACTION__UPDATE_CONFLICT = 32,
+ CHECKOUT_ACTION__MAX = 32,
+ CHECKOUT_ACTION__DEFER_REMOVE = 64,
CHECKOUT_ACTION__REMOVE_AND_UPDATE =
(CHECKOUT_ACTION__UPDATE_BLOB | CHECKOUT_ACTION__REMOVE),
};
@@ -54,9 +55,10 @@ typedef struct {
git_index *index;
git_pool pool;
git_vector removes;
- git_vector conflicts;
- git_vector *reuc;
- git_vector *names;
+ git_vector remove_conflicts;
+ git_vector update_conflicts;
+ git_vector *update_reuc;
+ git_vector *update_names;
git_buf path;
size_t workdir_len;
git_buf tmp;
@@ -793,49 +795,60 @@ done:
return error;
}
-static int checkout_conflicts_load(checkout_data *data, git_iterator *workdir, git_vector *pathspec)
+static int checkout_conflict_append_update(
+ const git_index_entry *ancestor,
+ const git_index_entry *ours,
+ const git_index_entry *theirs,
+ void *payload)
+{
+ checkout_data *data = payload;
+ checkout_conflictdata *conflict;
+ int error;
+
+ conflict = git__calloc(1, sizeof(checkout_conflictdata));
+ GITERR_CHECK_ALLOC(conflict);
+
+ conflict->ancestor = ancestor;
+ conflict->ours = ours;
+ conflict->theirs = theirs;
+
+ if ((error = checkout_conflict_detect_submodule(conflict)) < 0 ||
+ (error = checkout_conflict_detect_binary(data->repo, conflict)) < 0)
+ {
+ git__free(conflict);
+ return error;
+ }
+
+ if (git_vector_insert(&data->update_conflicts, conflict))
+ return -1;
+
+ return 0;
+}
+
+static int checkout_conflicts_foreach(
+ checkout_data *data,
+ git_index *index,
+ git_iterator *workdir,
+ git_vector *pathspec,
+ int (*cb)(const git_index_entry *, const git_index_entry *, const git_index_entry *, void *),
+ void *payload)
{
git_index_conflict_iterator *iterator = NULL;
- git_index *index;
const git_index_entry *ancestor, *ours, *theirs;
- checkout_conflictdata *conflict;
int error = 0;
- /* Only write conficts from sources that have them: indexes. */
- if ((index = git_iterator_get_index(data->target)) == NULL)
- return 0;
-
if ((error = git_index_conflict_iterator_new(&iterator, index)) < 0)
goto done;
- data->conflicts._cmp = checkout_conflictdata_cmp;
-
/* Collect the conflicts */
while ((error = git_index_conflict_next(&ancestor, &ours, &theirs, iterator)) == 0) {
if (!conflict_pathspec_match(data, workdir, pathspec, ancestor, ours, theirs))
continue;
- conflict = git__calloc(1, sizeof(checkout_conflictdata));
- GITERR_CHECK_ALLOC(conflict);
-
- conflict->ancestor = ancestor;
- conflict->ours = ours;
- conflict->theirs = theirs;
-
- if ((error = checkout_conflict_detect_submodule(conflict)) < 0 ||
- (error = checkout_conflict_detect_binary(data->repo, conflict)) < 0)
- {
- git__free(conflict);
+ if ((error = cb(ancestor, ours, theirs, payload)) < 0)
goto done;
- }
-
- git_vector_insert(&data->conflicts, conflict);
}
- /* Collect the REUC and NAME entries */
- data->reuc = &index->reuc;
- data->names = &index->names;
-
if (error == GIT_ITEROVER)
error = 0;
@@ -845,6 +858,26 @@ done:
return error;
}
+static int checkout_conflicts_load(checkout_data *data, git_iterator *workdir, git_vector *pathspec)
+{
+ git_index *index;
+
+ /* Only write conficts from sources that have them: indexes. */
+ if ((index = git_iterator_get_index(data->target)) == NULL)
+ return 0;
+
+ data->update_conflicts._cmp = checkout_conflictdata_cmp;
+
+ if (checkout_conflicts_foreach(data, index, workdir, pathspec, checkout_conflict_append_update, data) < 0)
+ return -1;
+
+ /* Collect the REUC and NAME entries */
+ data->update_reuc = &index->reuc;
+ data->update_names = &index->names;
+
+ return 0;
+}
+
GIT_INLINE(int) checkout_conflicts_cmp_entry(
const char *path,
const git_index_entry *entry)
@@ -869,10 +902,10 @@ static checkout_conflictdata *checkout_conflicts_search_ancestor(
{
size_t pos;
- if (git_vector_bsearch2(&pos, &data->conflicts, checkout_conflicts_cmp_ancestor, path) < 0)
+ if (git_vector_bsearch2(&pos, &data->update_conflicts, checkout_conflicts_cmp_ancestor, path) < 0)
return NULL;
- return git_vector_get(&data->conflicts, pos);
+ return git_vector_get(&data->update_conflicts, pos);
}
static checkout_conflictdata *checkout_conflicts_search_branch(
@@ -882,7 +915,7 @@ static checkout_conflictdata *checkout_conflicts_search_branch(
checkout_conflictdata *conflict;
size_t i;
- git_vector_foreach(&data->conflicts, i, conflict) {
+ git_vector_foreach(&data->update_conflicts, i, conflict) {
int cmp = -1;
if (conflict->ancestor)
@@ -1022,7 +1055,7 @@ static int checkout_conflicts_coalesce_renames(
}
git_vector_remove_matching(
- &data->conflicts, checkout_conflictdata_empty, NULL);
+ &data->update_conflicts, checkout_conflictdata_empty, NULL);
done:
return error;
@@ -1044,7 +1077,7 @@ static int checkout_conflicts_mark_directoryfile(
len = git_index_entrycount(index);
/* Find d/f conflicts */
- git_vector_foreach(&data->conflicts, i, conflict) {
+ git_vector_foreach(&data->update_conflicts, i, conflict) {
if ((conflict->ours && conflict->theirs) ||
(!conflict->ours && !conflict->theirs))
continue;
@@ -1084,7 +1117,7 @@ done:
return error;
}
-static int checkout_get_conflicts(
+static int checkout_get_update_conflicts(
checkout_data *data,
git_iterator *workdir,
git_vector *pathspec)
@@ -1103,6 +1136,38 @@ done:
return error;
}
+static int checkout_conflict_append_remove(
+ const git_index_entry *ancestor,
+ const git_index_entry *ours,
+ const git_index_entry *theirs,
+ void *payload)
+{
+ checkout_data *data = payload;
+ const char *name;
+
+ if (ancestor)
+ name = git__strdup(ancestor->path);
+ else if (ours)
+ name = git__strdup(ours->path);
+ else if (theirs)
+ name = git__strdup(theirs->path);
+
+ GITERR_CHECK_ALLOC(name);
+
+ return git_vector_insert(&data->remove_conflicts, (char *)name);
+}
+
+static int checkout_get_remove_conflicts(
+ checkout_data *data,
+ git_iterator *workdir,
+ git_vector *pathspec)
+{
+ if ((data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) != 0)
+ return 0;
+
+ return checkout_conflicts_foreach(data, data->index, workdir, pathspec, checkout_conflict_append_remove, data);
+}
+
static int checkout_get_actions(
uint32_t **actions_ptr,
size_t **counts_ptr,
@@ -1170,10 +1235,12 @@ static int checkout_get_actions(
}
- if ((error = checkout_get_conflicts(data, workdir, &pathspec)) < 0)
+ if ((error = checkout_get_remove_conflicts(data, workdir, &pathspec)) < 0 ||
+ (error = checkout_get_update_conflicts(data, workdir, &pathspec)) < 0)
goto fail;
- counts[CHECKOUT_ACTION__UPDATE_CONFLICT] = git_vector_length(&data->conflicts);
+ counts[CHECKOUT_ACTION__REMOVE_CONFLICT] = git_vector_length(&data->remove_conflicts);
+ counts[CHECKOUT_ACTION__UPDATE_CONFLICT] = git_vector_length(&data->update_conflicts);
git_pathspec__vfree(&pathspec);
git_pool_clear(&pathpool);
@@ -1845,6 +1912,20 @@ done:
return error;
}
+static int checkout_conflict_add(
+ checkout_data *data,
+ const git_index_entry *conflict)
+{
+ int error = git_index_remove(data->index, conflict->path, 0);
+
+ if (error == GIT_ENOTFOUND)
+ giterr_clear();
+ else if (error < 0)
+ return error;
+
+ return git_index_add(data->index, conflict);
+}
+
static int checkout_conflict_update_index(
checkout_data *data,
checkout_conflictdata *conflict)
@@ -1852,13 +1933,13 @@ static int checkout_conflict_update_index(
int error = 0;
if (conflict->ancestor)
- error = git_index_add(data->index, conflict->ancestor);
+ error = checkout_conflict_add(data, conflict->ancestor);
if (!error && conflict->ours)
- error = git_index_add(data->index, conflict->ours);
+ error = checkout_conflict_add(data, conflict->ours);
if (!error && conflict->theirs)
- error = git_index_add(data->index, conflict->theirs);
+ error = checkout_conflict_add(data, conflict->theirs);
return error;
}
@@ -1869,7 +1950,7 @@ static int checkout_create_conflicts(checkout_data *data)
size_t i;
int error = 0;
- git_vector_foreach(&data->conflicts, i, conflict) {
+ git_vector_foreach(&data->update_conflicts, i, conflict) {
/* Both deleted: nothing to do */
if (conflict->ours == NULL && conflict->theirs == NULL)
@@ -1943,6 +2024,21 @@ static int checkout_create_conflicts(checkout_data *data)
return error;
}
+static int checkout_remove_conflicts(checkout_data *data)
+{
+ const char *conflict;
+ size_t i;
+
+ git_vector_foreach(&data->remove_conflicts, i, conflict) {
+ if (git_index_conflict_remove(data->index, conflict) < 0)
+ return -1;
+
+ data->completed_steps++;
+ }
+
+ return 0;
+}
+
static int checkout_extensions_update_index(checkout_data *data)
{
const git_index_reuc_entry *reuc_entry;
@@ -1953,8 +2049,8 @@ static int checkout_extensions_update_index(checkout_data *data)
if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0)
return 0;
- if (data->reuc) {
- git_vector_foreach(data->reuc, i, reuc_entry) {
+ if (data->update_reuc) {
+ git_vector_foreach(data->update_reuc, i, reuc_entry) {
if ((error = git_index_reuc_add(data->index, reuc_entry->path,
reuc_entry->mode[0], &reuc_entry->oid[0],
reuc_entry->mode[1], &reuc_entry->oid[1],
@@ -1963,8 +2059,8 @@ static int checkout_extensions_update_index(checkout_data *data)
}
}
- if (data->names) {
- git_vector_foreach(data->names, i, name_entry) {
+ if (data->update_names) {
+ git_vector_foreach(data->update_names, i, name_entry) {
if ((error = git_index_name_add(data->index, name_entry->ancestor,
name_entry->ours, name_entry->theirs)) < 0)
goto done;
@@ -1985,7 +2081,8 @@ static void checkout_data_clear(checkout_data *data)
git_vector_free(&data->removes);
git_pool_clear(&data->pool);
- git_vector_free_deep(&data->conflicts);
+ git_vector_free_deep(&data->remove_conflicts);
+ git_vector_free_deep(&data->update_conflicts);
git__free(data->pfx);
data->pfx = NULL;
@@ -2127,7 +2224,8 @@ static int checkout_data_init(
}
if ((error = git_vector_init(&data->removes, 0, git__strcmp_cb)) < 0 ||
- (error = git_vector_init(&data->conflicts, 0, NULL)) < 0 ||
+ (error = git_vector_init(&data->remove_conflicts, 0, NULL)) < 0 ||
+ (error = git_vector_init(&data->update_conflicts, 0, NULL)) < 0 ||
(error = git_pool_init(&data->pool, 1, 0)) < 0 ||
(error = git_buf_puts(&data->path, data->opts.target_directory)) < 0 ||
(error = git_path_to_dir(&data->path)) < 0)
@@ -2206,6 +2304,7 @@ int git_checkout_iterator(
goto cleanup;
data.total_steps = counts[CHECKOUT_ACTION__REMOVE] +
+ counts[CHECKOUT_ACTION__REMOVE_CONFLICT] +
counts[CHECKOUT_ACTION__UPDATE_BLOB] +
counts[CHECKOUT_ACTION__UPDATE_SUBMODULE] +
counts[CHECKOUT_ACTION__UPDATE_CONFLICT];
@@ -2219,6 +2318,10 @@ int git_checkout_iterator(
(error = checkout_remove_the_old(actions, &data)) < 0)
goto cleanup;
+ if (counts[CHECKOUT_ACTION__REMOVE_CONFLICT] > 0 &&
+ (error = checkout_remove_conflicts(&data)) < 0)
+ goto cleanup;
+
if (counts[CHECKOUT_ACTION__UPDATE_BLOB] > 0 &&
(error = checkout_create_the_new(actions, &data)) < 0)
goto cleanup;