aboutsummaryrefslogtreecommitdiff
path: root/merge-recursive.c
diff options
context:
space:
mode:
Diffstat (limited to 'merge-recursive.c')
-rw-r--r--merge-recursive.c181
1 files changed, 120 insertions, 61 deletions
diff --git a/merge-recursive.c b/merge-recursive.c
index 9bf5cc717..195ebf974 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -3,6 +3,7 @@
* Fredrik Kuivinen.
* The thieves were Alex Riesen and Johannes Schindelin, in June/July 2006
*/
+#include "advice.h"
#include "cache.h"
#include "cache-tree.h"
#include "commit.h"
@@ -20,15 +21,17 @@
#include "merge-recursive.h"
#include "dir.h"
-static struct tree *shift_tree_object(struct tree *one, struct tree *two)
+static struct tree *shift_tree_object(struct tree *one, struct tree *two,
+ const char *subtree_shift)
{
unsigned char shifted[20];
- /*
- * NEEDSWORK: this limits the recursion depth to hardcoded
- * value '2' to avoid excessive overhead.
- */
- shift_tree(one->object.sha1, two->object.sha1, shifted, 2);
+ if (!*subtree_shift) {
+ shift_tree(one->object.sha1, two->object.sha1, shifted, 0);
+ } else {
+ shift_tree_by(one->object.sha1, two->object.sha1, shifted,
+ subtree_shift);
+ }
if (!hashcmp(two->object.sha1, shifted))
return two;
return lookup_tree(shifted);
@@ -38,7 +41,7 @@ static struct tree *shift_tree_object(struct tree *one, struct tree *two)
* A virtual commit has (const char *)commit->util set to the name.
*/
-struct commit *make_virtual_commit(struct tree *tree, const char *comment)
+static struct commit *make_virtual_commit(struct tree *tree, const char *comment)
{
struct commit *commit = xcalloc(1, sizeof(struct commit));
commit->tree = tree;
@@ -86,6 +89,7 @@ static void flush_output(struct merge_options *o)
}
}
+__attribute__((format (printf, 3, 4)))
static void output(struct merge_options *o, int v, const char *fmt, ...)
{
int len;
@@ -181,6 +185,7 @@ static int git_merge_trees(int index_only,
opts.fn = threeway_merge;
opts.src_index = &the_index;
opts.dst_index = &the_index;
+ opts.msgs = get_porcelain_error_msgs();
init_tree_desc_from_tree(t+0, common);
init_tree_desc_from_tree(t+1, head);
@@ -197,13 +202,14 @@ struct tree *write_tree_from_memory(struct merge_options *o)
if (unmerged_cache()) {
int i;
- output(o, 0, "There are unmerged index entries:");
+ fprintf(stderr, "BUG: There are unmerged index entries:\n");
for (i = 0; i < active_nr; i++) {
struct cache_entry *ce = active_cache[i];
if (ce_stage(ce))
- output(o, 0, "%d %.*s", ce_stage(ce), ce_namelen(ce), ce->name);
+ fprintf(stderr, "BUG: %d %.*s", ce_stage(ce),
+ (int)ce_namelen(ce), ce->name);
}
- return NULL;
+ die("Bug in merge-recursive.c");
}
if (!active_cache_tree)
@@ -438,7 +444,7 @@ static void flush_buffer(int fd, const char *buf, unsigned long size)
/* Ignore epipe */
if (errno == EPIPE)
break;
- die("merge-recursive: %s", strerror(errno));
+ die_errno("merge-recursive");
} else if (!ret) {
die("merge-recursive: disk full?");
}
@@ -520,8 +526,12 @@ static void update_file_flags(struct merge_options *o,
unsigned long size;
if (S_ISGITLINK(mode))
- die("cannot read object %s '%s': It is a submodule!",
- sha1_to_hex(sha), path);
+ /*
+ * We may later decide to recursively descend into
+ * the submodule directory and update its index
+ * and/or work tree, but we do not do that now.
+ */
+ goto update_index;
buf = read_sha1_file(sha, &type, &size);
if (!buf)
@@ -550,7 +560,7 @@ static void update_file_flags(struct merge_options *o,
mode = 0666;
fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);
if (fd < 0)
- die("failed to open %s: %s", path, strerror(errno));
+ die_errno("failed to open '%s'", path);
flush_buffer(fd, buf, size);
close(fd);
} else if (S_ISLNK(mode)) {
@@ -558,7 +568,7 @@ static void update_file_flags(struct merge_options *o,
safe_create_leading_directories_const(path);
unlink(path);
if (symlink(lnk, path))
- die("failed to symlink %s: %s", path, strerror(errno));
+ die_errno("failed to symlink '%s'", path);
free(lnk);
} else
die("do not know what to do with %06o %s '%s'",
@@ -589,23 +599,6 @@ struct merge_file_info
merge:1;
};
-static void fill_mm(const unsigned char *sha1, mmfile_t *mm)
-{
- unsigned long size;
- enum object_type type;
-
- if (!hashcmp(sha1, null_sha1)) {
- mm->ptr = xstrdup("");
- mm->size = 0;
- return;
- }
-
- mm->ptr = read_sha1_file(sha1, &type, &size);
- if (!mm->ptr || type != OBJ_BLOB)
- die("unable to read blob object %s", sha1_to_hex(sha1));
- mm->size = size;
-}
-
static int merge_3way(struct merge_options *o,
mmbuffer_t *result_buf,
struct diff_filespec *one,
@@ -617,17 +610,39 @@ static int merge_3way(struct merge_options *o,
mmfile_t orig, src1, src2;
char *name1, *name2;
int merge_status;
+ int favor;
- name1 = xstrdup(mkpath("%s:%s", branch1, a->path));
- name2 = xstrdup(mkpath("%s:%s", branch2, b->path));
+ if (o->call_depth)
+ favor = 0;
+ else {
+ switch (o->recursive_variant) {
+ case MERGE_RECURSIVE_OURS:
+ favor = XDL_MERGE_FAVOR_OURS;
+ break;
+ case MERGE_RECURSIVE_THEIRS:
+ favor = XDL_MERGE_FAVOR_THEIRS;
+ break;
+ default:
+ favor = 0;
+ break;
+ }
+ }
- fill_mm(one->sha1, &orig);
- fill_mm(a->sha1, &src1);
- fill_mm(b->sha1, &src2);
+ if (strcmp(a->path, b->path)) {
+ name1 = xstrdup(mkpath("%s:%s", branch1, a->path));
+ name2 = xstrdup(mkpath("%s:%s", branch2, b->path));
+ } else {
+ name1 = xstrdup(mkpath("%s", branch1));
+ name2 = xstrdup(mkpath("%s", branch2));
+ }
+
+ read_mmblob(&orig, one->sha1);
+ read_mmblob(&src1, a->sha1);
+ read_mmblob(&src2, b->sha1);
merge_status = ll_merge(result_buf, a->path, &orig,
&src1, name1, &src2, name2,
- o->call_depth);
+ (!!o->call_depth) | (favor << 1));
free(name1);
free(name2);
@@ -801,22 +816,19 @@ static int process_renames(struct merge_options *o,
}
for (i = 0, j = 0; i < a_renames->nr || j < b_renames->nr;) {
- int compare;
char *src;
- struct string_list *renames1, *renames2, *renames2Dst;
+ struct string_list *renames1, *renames2Dst;
struct rename *ren1 = NULL, *ren2 = NULL;
const char *branch1, *branch2;
const char *ren1_src, *ren1_dst;
if (i >= a_renames->nr) {
- compare = 1;
ren2 = b_renames->items[j++].util;
} else if (j >= b_renames->nr) {
- compare = -1;
ren1 = a_renames->items[i++].util;
} else {
- compare = strcmp(a_renames->items[i].string,
- b_renames->items[j].string);
+ int compare = strcmp(a_renames->items[i].string,
+ b_renames->items[j].string);
if (compare <= 0)
ren1 = a_renames->items[i++].util;
if (compare >= 0)
@@ -826,14 +838,12 @@ static int process_renames(struct merge_options *o,
/* TODO: refactor, so that 1/2 are not needed */
if (ren1) {
renames1 = a_renames;
- renames2 = b_renames;
renames2Dst = &b_by_dst;
branch1 = o->branch1;
branch2 = o->branch2;
} else {
struct rename *tmp;
renames1 = b_renames;
- renames2 = a_renames;
renames2Dst = &a_by_dst;
branch1 = o->branch2;
branch2 = o->branch1;
@@ -934,11 +944,12 @@ static int process_renames(struct merge_options *o,
ren1_src, ren1_dst, branch1,
branch2);
update_file(o, 0, ren1->pair->two->sha1, ren1->pair->two->mode, ren1_dst);
- update_stages(ren1_dst, NULL,
- branch1 == o->branch1 ?
- ren1->pair->two : NULL,
- branch1 == o->branch1 ?
- NULL : ren1->pair->two, 1);
+ if (!o->call_depth)
+ update_stages(ren1_dst, NULL,
+ branch1 == o->branch1 ?
+ ren1->pair->two : NULL,
+ branch1 == o->branch1 ?
+ NULL : ren1->pair->two, 1);
} else if (!sha_eq(dst_other.sha1, null_sha1)) {
const char *new_path;
clean_merge = 0;
@@ -947,9 +958,31 @@ static int process_renames(struct merge_options *o,
"%s added in %s",
ren1_src, ren1_dst, branch1,
ren1_dst, branch2);
- new_path = unique_path(o, ren1_dst, branch2);
- output(o, 1, "Adding as %s instead", new_path);
- update_file(o, 0, dst_other.sha1, dst_other.mode, new_path);
+ if (o->call_depth) {
+ struct merge_file_info mfi;
+ struct diff_filespec one, a, b;
+
+ one.path = a.path = b.path =
+ (char *)ren1_dst;
+ hashcpy(one.sha1, null_sha1);
+ one.mode = 0;
+ hashcpy(a.sha1, ren1->pair->two->sha1);
+ a.mode = ren1->pair->two->mode;
+ hashcpy(b.sha1, dst_other.sha1);
+ b.mode = dst_other.mode;
+ mfi = merge_file(o, &one, &a, &b,
+ branch1,
+ branch2);
+ output(o, 1, "Adding merged %s", ren1_dst);
+ update_file(o, 0,
+ mfi.sha,
+ mfi.mode,
+ ren1_dst);
+ } else {
+ new_path = unique_path(o, ren1_dst, branch2);
+ output(o, 1, "Adding as %s instead", new_path);
+ update_file(o, 0, dst_other.sha1, dst_other.mode, new_path);
+ }
} else if ((item = string_list_lookup(ren1_dst, renames2Dst))) {
ren2 = item->util;
clean_merge = 0;
@@ -1140,6 +1173,28 @@ static int process_entry(struct merge_options *o,
return clean_merge;
}
+struct unpack_trees_error_msgs get_porcelain_error_msgs(void)
+{
+ struct unpack_trees_error_msgs msgs = {
+ /* would_overwrite */
+ "Your local changes to '%s' would be overwritten by merge. Aborting.",
+ /* not_uptodate_file */
+ "Your local changes to '%s' would be overwritten by merge. Aborting.",
+ /* not_uptodate_dir */
+ "Updating '%s' would lose untracked files in it. Aborting.",
+ /* would_lose_untracked */
+ "Untracked working tree file '%s' would be %s by merge. Aborting",
+ /* bind_overlap -- will not happen here */
+ NULL,
+ };
+ if (advice_commit_before_merge) {
+ msgs.would_overwrite = msgs.not_uptodate_file =
+ "Your local changes to '%s' would be overwritten by merge. Aborting.\n"
+ "Please, commit your changes or stash them before you can merge.";
+ }
+ return msgs;
+}
+
int merge_trees(struct merge_options *o,
struct tree *head,
struct tree *merge,
@@ -1148,9 +1203,9 @@ int merge_trees(struct merge_options *o,
{
int code, clean;
- if (o->subtree_merge) {
- merge = shift_tree_object(head, merge);
- common = shift_tree_object(head, common);
+ if (o->subtree_shift) {
+ merge = shift_tree_object(head, merge, o->subtree_shift);
+ common = shift_tree_object(head, common, o->subtree_shift);
}
if (sha_eq(common->object.sha1, merge->object.sha1)) {
@@ -1161,10 +1216,14 @@ int merge_trees(struct merge_options *o,
code = git_merge_trees(o->call_depth, common, head, merge);
- if (code != 0)
- die("merging of trees %s and %s failed",
- sha1_to_hex(head->object.sha1),
- sha1_to_hex(merge->object.sha1));
+ if (code != 0) {
+ if (show(o, 4) || o->call_depth)
+ die("merging of trees %s and %s failed",
+ sha1_to_hex(head->object.sha1),
+ sha1_to_hex(merge->object.sha1));
+ else
+ exit(128);
+ }
if (unmerged_cache()) {
struct string_list *entries, *re_head, *re_merge;