diff options
Diffstat (limited to 'merge-recursive.c')
-rw-r--r-- | merge-recursive.c | 173 |
1 files changed, 77 insertions, 96 deletions
diff --git a/merge-recursive.c b/merge-recursive.c index 32e186c15e..bac16f577c 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -3,13 +3,6 @@ * Fredrik Kuivinen. * The thieves were Alex Riesen and Johannes Schindelin, in June/July 2006 */ -#include <stdarg.h> -#include <string.h> -#include <assert.h> -#include <sys/wait.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <time.h> #include "cache.h" #include "cache-tree.h" #include "commit.h" @@ -21,6 +14,7 @@ #include "tag.h" #include "unpack-trees.h" #include "path-list.h" +#include "xdiff-interface.h" /* * A virtual commit has @@ -604,24 +598,21 @@ struct merge_file_info merge:1; }; -static char *git_unpack_file(const unsigned char *sha1, char *path) +static void fill_mm(const unsigned char *sha1, mmfile_t *mm) { - void *buf; - char type[20]; unsigned long size; - int fd; + char type[20]; - buf = read_sha1_file(sha1, type, &size); - if (!buf || strcmp(type, blob_type)) - die("unable to read blob object %s", sha1_to_hex(sha1)); + if (!hashcmp(sha1, null_sha1)) { + mm->ptr = xstrdup(""); + mm->size = 0; + return; + } - strcpy(path, ".merge_file_XXXXXX"); - fd = mkstemp(path); - if (fd < 0) - die("unable to create temp-file"); - flush_buffer(fd, buf, size); - close(fd); - return path; + mm->ptr = read_sha1_file(sha1, type, &size); + if (!mm->ptr || strcmp(type, blob_type)) + die("unable to read blob object %s", sha1_to_hex(sha1)); + mm->size = size; } static struct merge_file_info merge_file(struct diff_filespec *o, @@ -652,49 +643,41 @@ static struct merge_file_info merge_file(struct diff_filespec *o, else if (sha_eq(b->sha1, o->sha1)) hashcpy(result.sha, a->sha1); else if (S_ISREG(a->mode)) { - int code = 1, fd; - struct stat st; - char orig[PATH_MAX]; - char src1[PATH_MAX]; - char src2[PATH_MAX]; - const char *argv[] = { - "merge", "-L", NULL, "-L", NULL, "-L", NULL, - NULL, NULL, NULL, - NULL - }; - char *la, *lb, *lo; - - git_unpack_file(o->sha1, orig); - git_unpack_file(a->sha1, src1); - git_unpack_file(b->sha1, src2); - - argv[2] = la = xstrdup(mkpath("%s/%s", branch1, a->path)); - argv[6] = lb = xstrdup(mkpath("%s/%s", branch2, b->path)); - argv[4] = lo = xstrdup(mkpath("orig/%s", o->path)); - argv[7] = src1; - argv[8] = orig; - argv[9] = src2, - - code = run_command_v(10, argv); - - free(la); - free(lb); - free(lo); - if (code && code < -256) { - die("Failed to execute 'merge'. merge(1) is used as the " - "file-level merge tool. Is 'merge' in your path?"); - } - fd = open(src1, O_RDONLY); - if (fd < 0 || fstat(fd, &st) < 0 || - index_fd(result.sha, fd, &st, 1, - "blob")) - die("Unable to add %s to database", src1); - - unlink(orig); - unlink(src1); - unlink(src2); - - result.clean = WEXITSTATUS(code) == 0; + mmfile_t orig, src1, src2; + mmbuffer_t result_buf; + xpparam_t xpp; + char *name1, *name2; + int merge_status; + + name1 = xstrdup(mkpath("%s:%s", branch1, a->path)); + name2 = xstrdup(mkpath("%s:%s", branch2, b->path)); + + fill_mm(o->sha1, &orig); + fill_mm(a->sha1, &src1); + fill_mm(b->sha1, &src2); + + memset(&xpp, 0, sizeof(xpp)); + merge_status = xdl_merge(&orig, + &src1, name1, + &src2, name2, + &xpp, XDL_MERGE_ZEALOUS, + &result_buf); + free(name1); + free(name2); + free(orig.ptr); + free(src1.ptr); + free(src2.ptr); + + if ((merge_status < 0) || !result_buf.ptr) + die("Failed to execute internal merge"); + + if (write_sha1_file(result_buf.ptr, result_buf.size, + blob_type, result.sha)) + die("Unable to add %s to database", + a->path); + + free(result_buf.ptr); + result.clean = (merge_status == 0); } else { if (!(S_ISLNK(a->mode) || S_ISLNK(b->mode))) die("cannot merge modes?"); @@ -889,7 +872,7 @@ static int process_renames(struct path_list *a_renames, struct diff_filespec src_other, dst_other; int try_merge, stage = a_renames == renames1 ? 3: 2; - remove_file(1, ren1_src, 1); + remove_file(1, ren1_src, index_only); hashcpy(src_other.sha1, ren1->src_entry->stages[stage].sha); src_other.mode = ren1->src_entry->stages[stage].mode; @@ -1061,38 +1044,17 @@ static int process_entry(const char *path, struct stage_data *entry, output("Adding %s", path); update_file(1, sha, mode, path); } - } else if (!o_sha && a_sha && b_sha) { - /* Case C: Added in both (check for same permissions). */ - if (sha_eq(a_sha, b_sha)) { - if (a_mode != b_mode) { - clean_merge = 0; - output("CONFLICT: File %s added identically in both branches, " - "but permissions conflict %06o->%06o", - path, a_mode, b_mode); - output("CONFLICT: adding with permission: %06o", a_mode); - update_file(0, a_sha, a_mode, path); - } else { - /* This case is handled by git-read-tree */ - assert(0 && "This case must be handled by git-read-tree"); - } - } else { - const char *new_path1, *new_path2; - clean_merge = 0; - new_path1 = unique_path(path, branch1); - new_path2 = unique_path(path, branch2); - output("CONFLICT (add/add): File %s added non-identically " - "in both branches. Adding as %s and %s instead.", - path, new_path1, new_path2); - remove_file(0, path, 0); - update_file(0, a_sha, a_mode, new_path1); - update_file(0, b_sha, b_mode, new_path2); - } - - } else if (o_sha && a_sha && b_sha) { + } else if (a_sha && b_sha) { + /* Case C: Added in both (check for same permissions) and */ /* case D: Modified in both, but differently. */ + const char *reason = "content"; struct merge_file_info mfi; struct diff_filespec o, a, b; + if (!o_sha) { + reason = "add/add"; + o_sha = (unsigned char *)null_sha1; + } output("Auto-merging %s", path); o.path = a.path = b.path = (char *)path; hashcpy(o.sha1, o_sha); @@ -1109,7 +1071,8 @@ static int process_entry(const char *path, struct stage_data *entry, update_file(1, mfi.sha, mfi.mode, path); else { clean_merge = 0; - output("CONFLICT (content): Merge conflict in %s", path); + output("CONFLICT (%s): Merge conflict in %s", + reason, path); if (index_only) update_file(0, mfi.sha, mfi.mode, path); @@ -1285,6 +1248,18 @@ static int merge(struct commit *h1, return clean; } +static const char *better_branch_name(const char *branch) +{ + static char githead_env[8 + 40 + 1]; + char *name; + + if (strlen(branch) != 40) + return branch; + sprintf(githead_env, "GITHEAD_%s", branch); + name = getenv(githead_env); + return name ? name : branch; +} + static struct commit *get_ref(const char *ref) { unsigned char sha1[20]; @@ -1293,6 +1268,9 @@ static struct commit *get_ref(const char *ref) if (get_sha1(ref, sha1)) die("Could not resolve ref '%s'", ref); object = deref_tag(parse_object(sha1), ref, strlen(ref)); + if (object->type == OBJ_TREE) + return make_virtual_commit((struct tree*)object, + better_branch_name(ref)); if (object->type != OBJ_COMMIT) return NULL; if (parse_commit((struct commit *)object)) @@ -1309,7 +1287,7 @@ int main(int argc, char *argv[]) struct commit *result, *h1, *h2; git_config(git_default_config); /* core.filemode */ - original_index_file = getenv("GIT_INDEX_FILE"); + original_index_file = getenv(INDEX_ENVIRONMENT); if (!original_index_file) original_index_file = xstrdup(git_path("index")); @@ -1330,11 +1308,14 @@ int main(int argc, char *argv[]) branch1 = argv[++i]; branch2 = argv[++i]; - printf("Merging %s with %s\n", branch1, branch2); h1 = get_ref(branch1); h2 = get_ref(branch2); + branch1 = better_branch_name(branch1); + branch2 = better_branch_name(branch2); + printf("Merging %s with %s\n", branch1, branch2); + if (bases_count == 1) { struct commit *ancestor = get_ref(bases[0]); clean = merge(h1, h2, branch1, branch2, 0, ancestor, &result); |