summaryrefslogtreecommitdiff
path: root/merge-recursive.c
diff options
context:
space:
mode:
Diffstat (limited to 'merge-recursive.c')
-rw-r--r--merge-recursive.c180
1 files changed, 130 insertions, 50 deletions
diff --git a/merge-recursive.c b/merge-recursive.c
index d945779..8ff29ed 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -3,14 +3,10 @@
* Fredrik Kuivinen.
* The thieves were Alex Riesen and Johannes Schindelin, in June/July 2006
*/
-#include "cache.h"
+#include "git-compat-util.h"
#include "merge-recursive.h"
-#include "advice.h"
#include "alloc.h"
-#include "attr.h"
-#include "blob.h"
-#include "builtin.h"
#include "cache-tree.h"
#include "commit.h"
#include "commit-reach.h"
@@ -18,14 +14,22 @@
#include "diff.h"
#include "diffcore.h"
#include "dir.h"
-#include "ll-merge.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
+#include "merge-ll.h"
#include "lockfile.h"
-#include "object-store.h"
+#include "match-trees.h"
+#include "name-hash.h"
+#include "object-file.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "path.h"
#include "repository.h"
#include "revision.h"
+#include "sparse-index.h"
#include "string-list.h"
-#include "submodule-config.h"
-#include "submodule.h"
+#include "symlinks.h"
#include "tag.h"
#include "tree-walk.h"
#include "unpack-trees.h"
@@ -45,7 +49,7 @@ struct path_hashmap_entry {
char path[FLEX_ARRAY];
};
-static int path_hashmap_cmp(const void *cmp_data,
+static int path_hashmap_cmp(const void *cmp_data UNUSED,
const struct hashmap_entry *eptr,
const struct hashmap_entry *entry_or_key,
const void *keydata)
@@ -82,17 +86,17 @@ static struct dir_rename_entry *dir_rename_find_entry(struct hashmap *hashmap,
{
struct dir_rename_entry key;
- if (dir == NULL)
+ if (!dir)
return NULL;
hashmap_entry_init(&key.ent, strhash(dir));
key.dir = dir;
return hashmap_get_entry(hashmap, &key, ent, NULL);
}
-static int dir_rename_cmp(const void *unused_cmp_data,
+static int dir_rename_cmp(const void *cmp_data UNUSED,
const struct hashmap_entry *eptr,
const struct hashmap_entry *entry_or_key,
- const void *unused_keydata)
+ const void *keydata UNUSED)
{
const struct dir_rename_entry *e1, *e2;
@@ -134,10 +138,10 @@ static struct collision_entry *collision_find_entry(struct hashmap *hashmap,
return hashmap_get_entry(hashmap, &key, ent, NULL);
}
-static int collision_cmp(const void *unused_cmp_data,
+static int collision_cmp(const void *cmp_data UNUSED,
const struct hashmap_entry *eptr,
const struct hashmap_entry *entry_or_key,
- const void *unused_keydata)
+ const void *keydata UNUSED)
{
const struct collision_entry *e1, *e2;
@@ -401,8 +405,9 @@ static inline int merge_detect_rename(struct merge_options *opt)
static void init_tree_desc_from_tree(struct tree_desc *desc, struct tree *tree)
{
- parse_tree(tree);
- init_tree_desc(desc, tree->buffer, tree->size);
+ if (parse_tree(tree) < 0)
+ exit(128);
+ init_tree_desc(desc, &tree->object.oid, tree->buffer, tree->size);
}
static int unpack_trees_start(struct merge_options *opt,
@@ -412,7 +417,7 @@ static int unpack_trees_start(struct merge_options *opt,
{
int rc;
struct tree_desc t[3];
- struct index_state tmp_index = { NULL };
+ struct index_state tmp_index = INDEX_STATE_INIT(opt->repo);
memset(&opt->priv->unpack_opts, 0, sizeof(opt->priv->unpack_opts));
if (opt->priv->call_depth)
@@ -456,7 +461,7 @@ static void unpack_trees_finish(struct merge_options *opt)
clear_unpack_trees_porcelain(&opt->priv->unpack_opts);
}
-static int save_files_dirs(const struct object_id *oid,
+static int save_files_dirs(const struct object_id *oid UNUSED,
struct strbuf *base, const char *path,
unsigned int mode, void *context)
{
@@ -522,10 +527,10 @@ static struct stage_data *insert_stage_data(struct repository *r,
*/
static struct string_list *get_unmerged(struct index_state *istate)
{
- struct string_list *unmerged = xcalloc(1, sizeof(struct string_list));
+ struct string_list *unmerged = xmalloc(sizeof(struct string_list));
int i;
- unmerged->strdup_strings = 1;
+ string_list_init_dup(unmerged);
/* TODO: audit for interaction with sparse-index. */
ensure_full_index(istate);
@@ -951,7 +956,8 @@ static int update_file_flags(struct merge_options *opt,
goto update_index;
}
- buf = read_object_file(&contents->oid, &type, &size);
+ buf = repo_read_object_file(the_repository, &contents->oid,
+ &type, &size);
if (!buf) {
ret = err(opt, _("cannot read object %s '%s'"),
oid_to_hex(&contents->oid), path);
@@ -1042,13 +1048,14 @@ static int merge_3way(struct merge_options *opt,
const int extra_marker_size)
{
mmfile_t orig, src1, src2;
- struct ll_merge_options ll_opts = {0};
+ struct ll_merge_options ll_opts = LL_MERGE_OPTIONS_INIT;
char *base, *name1, *name2;
- int merge_status;
+ enum ll_merge_result merge_status;
ll_opts.renormalize = opt->renormalize;
ll_opts.extra_marker_size = extra_marker_size;
ll_opts.xdl_opts = opt->xdl_opts;
+ ll_opts.conflict_style = opt->conflict_style;
if (opt->priv->call_depth) {
ll_opts.virtual_ancestor = 1;
@@ -1090,6 +1097,9 @@ static int merge_3way(struct merge_options *opt,
merge_status = ll_merge(result_buf, a->path, &orig, base,
&src1, name1, &src2, name2,
opt->repo->index, &ll_opts);
+ if (merge_status == LL_MERGE_BINARY_CONFLICT)
+ warning("Cannot merge binary files: %s (%s vs. %s)",
+ a->path, name1, name2);
free(base);
free(name1);
@@ -1131,7 +1141,13 @@ static int find_first_merges(struct repository *repo,
die("revision walk setup failed");
while ((commit = get_revision(&revs)) != NULL) {
struct object *o = &(commit->object);
- if (repo_in_merge_bases(repo, b, commit))
+ int ret = repo_in_merge_bases(repo, b, commit);
+ if (ret < 0) {
+ object_array_clear(&merges);
+ release_revisions(&revs);
+ return ret;
+ }
+ if (ret)
add_object_array(o, NULL, &merges);
}
reset_revision_walk();
@@ -1146,9 +1162,17 @@ static int find_first_merges(struct repository *repo,
contains_another = 0;
for (j = 0; j < merges.nr; j++) {
struct commit *m2 = (struct commit *) merges.objects[j].item;
- if (i != j && repo_in_merge_bases(repo, m2, m1)) {
- contains_another = 1;
- break;
+ if (i != j) {
+ int ret = repo_in_merge_bases(repo, m2, m1);
+ if (ret < 0) {
+ object_array_clear(&merges);
+ release_revisions(&revs);
+ return ret;
+ }
+ if (ret > 0) {
+ contains_another = 1;
+ break;
+ }
}
}
@@ -1157,6 +1181,7 @@ static int find_first_merges(struct repository *repo,
}
object_array_clear(&merges);
+ release_revisions(&revs);
return result->nr;
}
@@ -1183,7 +1208,7 @@ static int merge_submodule(struct merge_options *opt,
const struct object_id *b)
{
struct repository subrepo;
- int ret = 0;
+ int ret = 0, ret2;
struct commit *commit_base, *commit_a, *commit_b;
int parent_count;
struct object_array merges;
@@ -1220,14 +1245,32 @@ static int merge_submodule(struct merge_options *opt,
}
/* check whether both changes are forward */
- if (!repo_in_merge_bases(&subrepo, commit_base, commit_a) ||
- !repo_in_merge_bases(&subrepo, commit_base, commit_b)) {
+ ret2 = repo_in_merge_bases(&subrepo, commit_base, commit_a);
+ if (ret2 < 0) {
+ output(opt, 1, _("Failed to merge submodule %s (repository corrupt)"), path);
+ ret = -1;
+ goto cleanup;
+ }
+ if (ret2 > 0)
+ ret2 = repo_in_merge_bases(&subrepo, commit_base, commit_b);
+ if (ret2 < 0) {
+ output(opt, 1, _("Failed to merge submodule %s (repository corrupt)"), path);
+ ret = -1;
+ goto cleanup;
+ }
+ if (!ret2) {
output(opt, 1, _("Failed to merge submodule %s (commits don't follow merge-base)"), path);
goto cleanup;
}
/* Case #1: a is contained in b or vice versa */
- if (repo_in_merge_bases(&subrepo, commit_a, commit_b)) {
+ ret2 = repo_in_merge_bases(&subrepo, commit_a, commit_b);
+ if (ret2 < 0) {
+ output(opt, 1, _("Failed to merge submodule %s (repository corrupt)"), path);
+ ret = -1;
+ goto cleanup;
+ }
+ if (ret2) {
oidcpy(result, b);
if (show(opt, 3)) {
output(opt, 3, _("Fast-forwarding submodule %s to the following commit:"), path);
@@ -1240,7 +1283,13 @@ static int merge_submodule(struct merge_options *opt,
ret = 1;
goto cleanup;
}
- if (repo_in_merge_bases(&subrepo, commit_b, commit_a)) {
+ ret2 = repo_in_merge_bases(&subrepo, commit_b, commit_a);
+ if (ret2 < 0) {
+ output(opt, 1, _("Failed to merge submodule %s (repository corrupt)"), path);
+ ret = -1;
+ goto cleanup;
+ }
+ if (ret2) {
oidcpy(result, a);
if (show(opt, 3)) {
output(opt, 3, _("Fast-forwarding submodule %s to the following commit:"), path);
@@ -1269,6 +1318,10 @@ static int merge_submodule(struct merge_options *opt,
parent_count = find_first_merges(&subrepo, &merges, path,
commit_a, commit_b);
switch (parent_count) {
+ case -1:
+ output(opt, 1,_("Failed to merge submodule %s (repository corrupt)"), path);
+ ret = -1;
+ break;
case 0:
output(opt, 1, _("Failed to merge submodule %s (merge following commits not found)"), path);
break;
@@ -1369,12 +1422,12 @@ static int merge_mode_and_contents(struct merge_options *opt,
extra_marker_size);
if ((merge_status < 0) || !result_buf.ptr)
- ret = err(opt, _("Failed to execute internal merge"));
+ ret = err(opt, _("failed to execute internal merge"));
if (!ret &&
write_object_file(result_buf.ptr, result_buf.size,
- blob_type, &result->blob.oid))
- ret = err(opt, _("Unable to add %s to database"),
+ OBJ_BLOB, &result->blob.oid))
+ ret = err(opt, _("unable to add %s to database"),
a->path);
free(result_buf.ptr);
@@ -1383,11 +1436,14 @@ static int merge_mode_and_contents(struct merge_options *opt,
/* FIXME: bug, what if modes didn't match? */
result->clean = (merge_status == 0);
} else if (S_ISGITLINK(a->mode)) {
- result->clean = merge_submodule(opt, &result->blob.oid,
- o->path,
- &o->oid,
- &a->oid,
- &b->oid);
+ int clean = merge_submodule(opt, &result->blob.oid,
+ o->path,
+ &o->oid,
+ &a->oid,
+ &b->oid);
+ if (clean < 0)
+ return -1;
+ result->clean = clean;
} else if (S_ISLNK(a->mode)) {
switch (opt->recursive_variant) {
case MERGE_VARIANT_NORMAL:
@@ -1987,14 +2043,14 @@ static void get_renamed_dir_portion(const char *old_path, const char *new_path,
* renamed means the root directory can never be renamed -- because
* the root directory always exists).
*/
- if (end_of_old == NULL)
+ if (!end_of_old)
return; /* Note: *old_dir and *new_dir are still NULL */
/*
* If new_path contains no directory (end_of_new is NULL), then we
* have a rename of old_path's directory to the root directory.
*/
- if (end_of_new == NULL) {
+ if (!end_of_new) {
*old_dir = xstrndup(old_path, end_of_old - old_path);
*new_dir = xstrdup("");
return;
@@ -2096,7 +2152,7 @@ static char *handle_path_level_conflicts(struct merge_options *opt,
if (!new_path) {
/* This should only happen when entry->non_unique_new_dir set */
if (!entry->non_unique_new_dir)
- BUG("entry->non_unqiue_dir not set and !new_path");
+ BUG("entry->non_unique_new_dir not set and !new_path");
output(opt, 1, _("CONFLICT (directory rename split): "
"Unclear where to place %s because directory "
"%s was renamed to multiple other directories, "
@@ -2113,7 +2169,7 @@ static char *handle_path_level_conflicts(struct merge_options *opt,
* to ensure that's the case.
*/
collision_ent = collision_find_entry(collisions, new_path);
- if (collision_ent == NULL)
+ if (!collision_ent)
BUG("collision_ent is NULL");
/*
@@ -2993,7 +3049,7 @@ static void final_cleanup_rename(struct string_list *rename)
const struct rename *re;
int i;
- if (rename == NULL)
+ if (!rename)
return;
for (i = 0; i < rename->nr; i++) {
@@ -3017,7 +3073,7 @@ static int read_oid_strbuf(struct merge_options *opt,
void *buf;
enum object_type type;
unsigned long size;
- buf = read_object_file(oid, &type, &size);
+ buf = repo_read_object_file(the_repository, oid, &type, &size);
if (!buf)
return err(opt, _("cannot read object %s"), oid_to_hex(oid));
if (type != OBJ_BLOB) {
@@ -3588,7 +3644,9 @@ static int merge_recursive_internal(struct merge_options *opt,
}
if (!merge_bases) {
- merge_bases = get_merge_bases(h1, h2);
+ if (repo_get_merge_bases(the_repository, h1, h2,
+ &merge_bases) < 0)
+ return -1;
merge_bases = reverse_commit_list(merge_bases);
}
@@ -3602,7 +3660,7 @@ static int merge_recursive_internal(struct merge_options *opt,
}
merged_merge_bases = pop_commit(&merge_bases);
- if (merged_merge_bases == NULL) {
+ if (!merged_merge_bases) {
/* if there is no common ancestor, use an empty tree */
struct tree *tree;
@@ -3711,6 +3769,10 @@ static int merge_start(struct merge_options *opt, struct tree *head)
assert(opt->priv == NULL);
+ /* Not supported; option specific to merge-ort */
+ assert(!opt->record_conflict_msgs_as_headers);
+ assert(!opt->msg_header_prefix);
+
/* Sanity check on repo state; index must match head */
if (repo_index_has_changes(opt->repo, head, &sb)) {
err(opt, _("Your local changes to the following files would be overwritten by merge:\n %s"),
@@ -3789,7 +3851,7 @@ static struct commit *get_ref(struct repository *repo,
return make_virtual_commit(repo, (struct tree*)object, name);
if (object->type != OBJ_COMMIT)
return NULL;
- if (parse_commit((struct commit *)object))
+ if (repo_parse_commit(repo, (struct commit *)object))
return NULL;
return (struct commit *)object;
}
@@ -3886,6 +3948,8 @@ void init_merge_options(struct merge_options *opt,
opt->renormalize = 0;
+ opt->conflict_style = -1;
+
merge_recursive_config(opt);
merge_verbosity = getenv("GIT_MERGE_VERBOSITY");
if (merge_verbosity)
@@ -3894,6 +3958,22 @@ void init_merge_options(struct merge_options *opt,
opt->buffer_output = 0;
}
+/*
+ * For now, members of merge_options do not need deep copying, but
+ * it may change in the future, in which case we would need to update
+ * this, and also make a matching change to clear_merge_options() to
+ * release the resources held by a copied instance.
+ */
+void copy_merge_options(struct merge_options *dst, struct merge_options *src)
+{
+ *dst = *src;
+}
+
+void clear_merge_options(struct merge_options *opt UNUSED)
+{
+ ; /* no-op as our copy is shallow right now */
+}
+
int parse_merge_opt(struct merge_options *opt, const char *s)
{
const char *arg;