Merge branch 'js/merge'
* js/merge: merge-recursive: add/add really is modify/modify with an empty base Get rid of the dependency on RCS' merge program merge-file: support -p and -q; fix compile warnings Add builtin merge-file, a minimal replacement for RCS merge xdl_merge(): fix and simplify conflict handling xdl_merge(): fix thinko xdl_merge(): fix an off-by-one bug merge-recursive: use xdl_merge(). xmerge: make return value from xdl_merge() more usable. xdiff: add xdl_merge()maint
						commit
						e4d45dd3bb
					
				|  | @ -60,6 +60,7 @@ git-mailsplit | |||
| git-merge | ||||
| git-merge-base | ||||
| git-merge-index | ||||
| git-merge-file | ||||
| git-merge-tree | ||||
| git-merge-octopus | ||||
| git-merge-one-file | ||||
|  |  | |||
|  | @ -40,8 +40,8 @@ If "git-merge-index" is called with multiple <file>s (or -a) then it | |||
| processes them in turn only stopping if merge returns a non-zero exit | ||||
| code. | ||||
|  | ||||
| Typically this is run with the a script calling the merge command from | ||||
| the RCS package. | ||||
| Typically this is run with the a script calling git's imitation of | ||||
| the merge command from the RCS package. | ||||
|  | ||||
| A sample script called "git-merge-one-file" is included in the | ||||
| distribution. | ||||
|  |  | |||
							
								
								
									
										9
									
								
								INSTALL
								
								
								
								
							
							
						
						
									
										9
									
								
								INSTALL
								
								
								
								
							|  | @ -82,15 +82,6 @@ Issues of note: | |||
| 	  do that even if it wasn't for git.  There's no point in living | ||||
| 	  in the dark ages any more.  | ||||
|  | ||||
| 	- "merge", the standard UNIX three-way merge program.  It usually | ||||
| 	  comes with the "rcs" package on most Linux distributions, so if | ||||
| 	  you have a developer install you probably have it already, but a | ||||
| 	  "graphical user desktop" install might have left it out. | ||||
|  | ||||
| 	  You'll only need the merge program if you do development using | ||||
| 	  git, and if you only use git to track other peoples work you'll | ||||
| 	  never notice the lack of it.  | ||||
|  | ||||
|         - "wish", the Tcl/Tk windowing shell is used in gitk to show the | ||||
|           history graphically | ||||
|  | ||||
|  |  | |||
							
								
								
									
										4
									
								
								Makefile
								
								
								
								
							
							
						
						
									
										4
									
								
								Makefile
								
								
								
								
							|  | @ -279,6 +279,7 @@ BUILTIN_OBJS = \ | |||
| 	builtin-ls-tree.o \ | ||||
| 	builtin-mailinfo.o \ | ||||
| 	builtin-mailsplit.o \ | ||||
| 	builtin-merge-file.o \ | ||||
| 	builtin-mv.o \ | ||||
| 	builtin-name-rev.o \ | ||||
| 	builtin-pack-objects.o \ | ||||
|  | @ -737,7 +738,8 @@ $(DIFF_OBJS): diffcore.h | |||
| $(LIB_FILE): $(LIB_OBJS) | ||||
| 	rm -f $@ && $(AR) rcs $@ $(LIB_OBJS) | ||||
|  | ||||
| XDIFF_OBJS=xdiff/xdiffi.o xdiff/xprepare.o xdiff/xutils.o xdiff/xemit.o | ||||
| XDIFF_OBJS=xdiff/xdiffi.o xdiff/xprepare.o xdiff/xutils.o xdiff/xemit.o \ | ||||
| 	xdiff/xmerge.o | ||||
| $(XDIFF_OBJS): xdiff/xinclude.h xdiff/xmacros.h xdiff/xdiff.h xdiff/xtypes.h \ | ||||
| 	xdiff/xutils.h xdiff/xprepare.h xdiff/xdiffi.h xdiff/xemit.h | ||||
|  | ||||
|  |  | |||
|  | @ -0,0 +1,79 @@ | |||
| #include "cache.h" | ||||
| #include "xdiff/xdiff.h" | ||||
|  | ||||
| static const char merge_file_usage[] = | ||||
| "git merge-file [-p | --stdout] [-q | --quiet] [-L name1 [-L orig [-L name2]]] file1 orig_file file2"; | ||||
|  | ||||
| static int read_file(mmfile_t *ptr, const char *filename) | ||||
| { | ||||
| 	struct stat st; | ||||
| 	FILE *f; | ||||
|  | ||||
| 	if (stat(filename, &st)) | ||||
| 		return error("Could not stat %s", filename); | ||||
| 	if ((f = fopen(filename, "rb")) == NULL) | ||||
| 		return error("Could not open %s", filename); | ||||
| 	ptr->ptr = xmalloc(st.st_size); | ||||
| 	if (fread(ptr->ptr, st.st_size, 1, f) != 1) | ||||
| 		return error("Could not read %s", filename); | ||||
| 	fclose(f); | ||||
| 	ptr->size = st.st_size; | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int cmd_merge_file(int argc, char **argv, char **envp) | ||||
| { | ||||
| 	char *names[3]; | ||||
| 	mmfile_t mmfs[3]; | ||||
| 	mmbuffer_t result = {NULL, 0}; | ||||
| 	xpparam_t xpp = {XDF_NEED_MINIMAL}; | ||||
| 	int ret = 0, i = 0, to_stdout = 0; | ||||
|  | ||||
| 	while (argc > 4) { | ||||
| 		if (!strcmp(argv[1], "-L") && i < 3) { | ||||
| 			names[i++] = argv[2]; | ||||
| 			argc--; | ||||
| 			argv++; | ||||
| 		} else if (!strcmp(argv[1], "-p") || | ||||
| 				!strcmp(argv[1], "--stdout")) | ||||
| 			to_stdout = 1; | ||||
| 		else if (!strcmp(argv[1], "-q") || | ||||
| 				!strcmp(argv[1], "--quiet")) | ||||
| 			freopen("/dev/null", "w", stderr); | ||||
| 		else | ||||
| 			usage(merge_file_usage); | ||||
| 		argc--; | ||||
| 		argv++; | ||||
| 	} | ||||
|  | ||||
| 	if (argc != 4) | ||||
| 		usage(merge_file_usage); | ||||
|  | ||||
| 	for (; i < 3; i++) | ||||
| 		names[i] = argv[i + 1]; | ||||
|  | ||||
| 	for (i = 0; i < 3; i++) | ||||
| 		if (read_file(mmfs + i, argv[i + 1])) | ||||
| 			return -1; | ||||
|  | ||||
| 	ret = xdl_merge(mmfs + 1, mmfs + 0, names[0], mmfs + 2, names[2], | ||||
| 			&xpp, XDL_MERGE_ZEALOUS, &result); | ||||
|  | ||||
| 	for (i = 0; i < 3; i++) | ||||
| 		free(mmfs[i].ptr); | ||||
|  | ||||
| 	if (ret >= 0) { | ||||
| 		char *filename = argv[1]; | ||||
| 		FILE *f = to_stdout ? stdout : fopen(filename, "wb"); | ||||
|  | ||||
| 		if (!f) | ||||
| 			ret = error("Could not open %s for writing", filename); | ||||
| 		else if (fwrite(result.ptr, result.size, 1, f) != 1) | ||||
| 			ret = error("Could not write to %s", filename); | ||||
| 		else if (fclose(f)) | ||||
| 			ret = error("Could not close %s", filename); | ||||
| 		free(result.ptr); | ||||
| 	} | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
|  | @ -42,6 +42,7 @@ extern int cmd_ls_files(int argc, const char **argv, const char *prefix); | |||
| extern int cmd_ls_tree(int argc, const char **argv, const char *prefix); | ||||
| extern int cmd_mailinfo(int argc, const char **argv, const char *prefix); | ||||
| extern int cmd_mailsplit(int argc, const char **argv, const char *prefix); | ||||
| extern int cmd_merge_file(int argc, const char **argv, const char *prefix); | ||||
| extern int cmd_mv(int argc, const char **argv, const char *prefix); | ||||
| extern int cmd_name_rev(int argc, const char **argv, const char *prefix); | ||||
| extern int cmd_pack_objects(int argc, const char **argv, const char *prefix); | ||||
|  |  | |||
|  | @ -946,7 +946,7 @@ sub req_update | |||
|  | ||||
|             $log->debug("Temporary directory for merge is $dir"); | ||||
|  | ||||
|             my $return = system("merge", $file_local, $file_old, $file_new); | ||||
|             my $return = system("git merge-file", $file_local, $file_old, $file_new); | ||||
|             $return >>= 8; | ||||
|  | ||||
|             if ( $return == 0 ) | ||||
|  |  | |||
|  | @ -154,7 +154,7 @@ sub find_conflict { | |||
| sub merge { | ||||
| 	my ($name, $path) = @_; | ||||
| 	record_preimage($path, "$rr_dir/$name/thisimage"); | ||||
| 	unless (system('merge', map { "$rr_dir/$name/${_}image" } | ||||
| 	unless (system('git merge-file', map { "$rr_dir/$name/${_}image" } | ||||
| 		       qw(this pre post))) { | ||||
| 		my $in; | ||||
| 		open $in, "<$rr_dir/$name/thisimage" or | ||||
|  |  | |||
							
								
								
									
										1
									
								
								git.c
								
								
								
								
							
							
						
						
									
										1
									
								
								git.c
								
								
								
								
							|  | @ -247,6 +247,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp) | |||
| 		{ "ls-tree", cmd_ls_tree, RUN_SETUP }, | ||||
| 		{ "mailinfo", cmd_mailinfo }, | ||||
| 		{ "mailsplit", cmd_mailsplit }, | ||||
| 		{ "merge-file", cmd_merge_file }, | ||||
| 		{ "mv", cmd_mv, RUN_SETUP }, | ||||
| 		{ "name-rev", cmd_name_rev, RUN_SETUP }, | ||||
| 		{ "pack-objects", cmd_pack_objects, RUN_SETUP }, | ||||
|  |  | |||
|  | @ -24,7 +24,7 @@ This is a dummy package which brings in all subpackages. | |||
| %package core | ||||
| Summary:	Core git tools | ||||
| Group:		Development/Tools | ||||
| Requires:	zlib >= 1.2, rsync, rcs, curl, less, openssh-clients, expat | ||||
| Requires:	zlib >= 1.2, rsync, curl, less, openssh-clients, expat | ||||
| %description core | ||||
| This is a stupid (but extremely fast) directory content manager.  It | ||||
| doesn't do a whole lot, but what it _does_ do is track directory | ||||
|  |  | |||
							
								
								
									
										73
									
								
								merge-file.c
								
								
								
								
							
							
						
						
									
										73
									
								
								merge-file.c
								
								
								
								
							|  | @ -3,52 +3,6 @@ | |||
| #include "xdiff-interface.h" | ||||
| #include "blob.h" | ||||
|  | ||||
| static void rm_temp_file(const char *filename) | ||||
| { | ||||
| 	unlink(filename); | ||||
| 	free((void *)filename); | ||||
| } | ||||
|  | ||||
| static const char *write_temp_file(mmfile_t *f) | ||||
| { | ||||
| 	int fd; | ||||
| 	const char *tmp = getenv("TMPDIR"); | ||||
| 	char *filename; | ||||
|  | ||||
| 	if (!tmp) | ||||
| 		tmp = "/tmp"; | ||||
| 	filename = mkpath("%s/%s", tmp, "git-tmp-XXXXXX"); | ||||
| 	fd = mkstemp(filename); | ||||
| 	if (fd < 0) | ||||
| 		return NULL; | ||||
| 	filename = xstrdup(filename); | ||||
| 	if (f->size != xwrite(fd, f->ptr, f->size)) { | ||||
| 		rm_temp_file(filename); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	close(fd); | ||||
| 	return filename; | ||||
| } | ||||
|  | ||||
| static void *read_temp_file(const char *filename, unsigned long *size) | ||||
| { | ||||
| 	struct stat st; | ||||
| 	char *buf = NULL; | ||||
| 	int fd = open(filename, O_RDONLY); | ||||
| 	if (fd < 0) | ||||
| 		return NULL; | ||||
| 	if (!fstat(fd, &st)) { | ||||
| 		*size = st.st_size; | ||||
| 		buf = xmalloc(st.st_size); | ||||
| 		if (st.st_size != xread(fd, buf, st.st_size)) { | ||||
| 			free(buf); | ||||
| 			buf = NULL; | ||||
| 		} | ||||
| 	} | ||||
| 	close(fd); | ||||
| 	return buf; | ||||
| } | ||||
|  | ||||
| static int fill_mmfile_blob(mmfile_t *f, struct blob *obj) | ||||
| { | ||||
| 	void *buf; | ||||
|  | @ -72,22 +26,19 @@ static void free_mmfile(mmfile_t *f) | |||
|  | ||||
| static void *three_way_filemerge(mmfile_t *base, mmfile_t *our, mmfile_t *their, unsigned long *size) | ||||
| { | ||||
| 	void *res; | ||||
| 	const char *t1, *t2, *t3; | ||||
| 	mmbuffer_t res; | ||||
| 	xpparam_t xpp; | ||||
| 	int merge_status; | ||||
|  | ||||
| 	t1 = write_temp_file(base); | ||||
| 	t2 = write_temp_file(our); | ||||
| 	t3 = write_temp_file(their); | ||||
| 	res = NULL; | ||||
| 	if (t1 && t2 && t3) { | ||||
| 		int code = run_command("merge", t2, t1, t3, NULL); | ||||
| 		if (!code || code == -1) | ||||
| 			res = read_temp_file(t2, size); | ||||
| 	} | ||||
| 	rm_temp_file(t1); | ||||
| 	rm_temp_file(t2); | ||||
| 	rm_temp_file(t3); | ||||
| 	return res; | ||||
| 	memset(&xpp, 0, sizeof(xpp)); | ||||
| 	merge_status = xdl_merge(base, our, ".our", their, ".their", | ||||
| 		&xpp, XDL_MERGE_ZEALOUS, &res); | ||||
|  | ||||
| 	if (merge_status < 0) | ||||
| 		return NULL; | ||||
|  | ||||
| 	*size = res.size; | ||||
| 	return res.ptr; | ||||
| } | ||||
|  | ||||
| static int common_outf(void *priv_, mmbuffer_t *mb, int nbuf) | ||||
|  |  | |||
|  | @ -21,6 +21,7 @@ | |||
| #include "tag.h" | ||||
| #include "unpack-trees.h" | ||||
| #include "path-list.h" | ||||
| #include "xdiff-interface.h" | ||||
|  | ||||
| /* | ||||
|  * A virtual commit has | ||||
|  | @ -604,24 +605,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)) | ||||
| 	if (!hashcmp(sha1, null_sha1)) { | ||||
| 		mm->ptr = xstrdup(""); | ||||
| 		mm->size = 0; | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	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)); | ||||
|  | ||||
| 	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->size = size; | ||||
| } | ||||
|  | ||||
| static struct merge_file_info merge_file(struct diff_filespec *o, | ||||
|  | @ -652,49 +650,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; | ||||
| 			mmfile_t orig, src1, src2; | ||||
| 			mmbuffer_t result_buf; | ||||
| 			xpparam_t xpp; | ||||
| 			char *name1, *name2; | ||||
| 			int merge_status; | ||||
|  | ||||
| 			git_unpack_file(o->sha1, orig); | ||||
| 			git_unpack_file(a->sha1, src1); | ||||
| 			git_unpack_file(b->sha1, src2); | ||||
| 			name1 = xstrdup(mkpath("%s/%s", branch1, a->path)); | ||||
| 			name2 = xstrdup(mkpath("%s/%s", branch2, b->path)); | ||||
|  | ||||
| 			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, | ||||
| 			fill_mm(o->sha1, &orig); | ||||
| 			fill_mm(a->sha1, &src1); | ||||
| 			fill_mm(b->sha1, &src2); | ||||
|  | ||||
| 			code = run_command_v(10, argv); | ||||
| 			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); | ||||
|  | ||||
| 			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); | ||||
| 			if ((merge_status < 0) || !result_buf.ptr) | ||||
| 				die("Failed to execute internal merge"); | ||||
|  | ||||
| 			unlink(orig); | ||||
| 			unlink(src1); | ||||
| 			unlink(src2); | ||||
| 			if (write_sha1_file(result_buf.ptr, result_buf.size, | ||||
| 					    blob_type, result.sha)) | ||||
| 				die("Unable to add %s to database", | ||||
| 				    a->path); | ||||
|  | ||||
| 			result.clean = WEXITSTATUS(code) == 0; | ||||
| 			free(result_buf.ptr); | ||||
| 			result.clean = (merge_status == 0); | ||||
| 		} else { | ||||
| 			if (!(S_ISLNK(a->mode) || S_ISLNK(b->mode))) | ||||
| 				die("cannot merge modes?"); | ||||
|  | @ -1061,38 +1051,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 +1078,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); | ||||
|  |  | |||
|  | @ -19,11 +19,7 @@ modification *should* take notice and update the test vectors here. | |||
| ' | ||||
|  | ||||
| ################################################################ | ||||
| # It appears that people are getting bitten by not installing | ||||
| # 'merge' (usually part of RCS package in binary distributions). | ||||
| # Check this and error out before running any tests.  Also catch | ||||
| # the bogosity of trying to run tests without building while we | ||||
| # are at it. | ||||
| # It appears that people try to run tests without building... | ||||
|  | ||||
| ../git >/dev/null | ||||
| if test $? != 1 | ||||
|  | @ -32,14 +28,6 @@ then | |||
| 	exit 1 | ||||
| fi | ||||
|  | ||||
| merge >/dev/null 2>/dev/null | ||||
| if test $? = 127 | ||||
| then | ||||
| 	echo >&2 'You do not seem to have "merge" installed. | ||||
| Please check INSTALL document.' | ||||
| 	exit 1 | ||||
| fi | ||||
|  | ||||
| . ./test-lib.sh | ||||
|  | ||||
| ################################################################ | ||||
|  |  | |||
|  | @ -0,0 +1,116 @@ | |||
| #!/bin/sh | ||||
|  | ||||
| test_description='RCS merge replacement: merge-file' | ||||
| . ./test-lib.sh | ||||
|  | ||||
| cat > orig.txt << EOF | ||||
| Dominus regit me, | ||||
| et nihil mihi deerit. | ||||
| In loco pascuae ibi me collocavit, | ||||
| super aquam refectionis educavit me; | ||||
| animam meam convertit, | ||||
| deduxit me super semitas jusitiae, | ||||
| propter nomen suum. | ||||
| EOF | ||||
|  | ||||
| cat > new1.txt << EOF | ||||
| Dominus regit me, | ||||
| et nihil mihi deerit. | ||||
| In loco pascuae ibi me collocavit, | ||||
| super aquam refectionis educavit me; | ||||
| animam meam convertit, | ||||
| deduxit me super semitas jusitiae, | ||||
| propter nomen suum. | ||||
| Nam et si ambulavero in medio umbrae mortis, | ||||
| non timebo mala, quoniam tu mecum es: | ||||
| virga tua et baculus tuus ipsa me consolata sunt. | ||||
| EOF | ||||
|  | ||||
| cat > new2.txt << EOF | ||||
| Dominus regit me, et nihil mihi deerit. | ||||
| In loco pascuae ibi me collocavit, | ||||
| super aquam refectionis educavit me; | ||||
| animam meam convertit, | ||||
| deduxit me super semitas jusitiae, | ||||
| propter nomen suum. | ||||
| EOF | ||||
|  | ||||
| cat > new3.txt << EOF | ||||
| DOMINUS regit me, | ||||
| et nihil mihi deerit. | ||||
| In loco pascuae ibi me collocavit, | ||||
| super aquam refectionis educavit me; | ||||
| animam meam convertit, | ||||
| deduxit me super semitas jusitiae, | ||||
| propter nomen suum. | ||||
| EOF | ||||
|  | ||||
| cat > new4.txt << EOF | ||||
| Dominus regit me, et nihil mihi deerit. | ||||
| In loco pascuae ibi me collocavit, | ||||
| super aquam refectionis educavit me; | ||||
| animam meam convertit, | ||||
| deduxit me super semitas jusitiae, | ||||
| EOF | ||||
| echo -n "propter nomen suum." >> new4.txt | ||||
|  | ||||
| cp new1.txt test.txt | ||||
| test_expect_success "merge without conflict" \ | ||||
| 	"git-merge-file test.txt orig.txt new2.txt" | ||||
|  | ||||
| cp new1.txt test2.txt | ||||
| test_expect_success "merge without conflict (missing LF at EOF)" \ | ||||
| 	"git-merge-file test2.txt orig.txt new2.txt" | ||||
|  | ||||
| test_expect_success "merge result added missing LF" \ | ||||
| 	"diff -u test.txt test2.txt" | ||||
|  | ||||
| cp test.txt backup.txt | ||||
| test_expect_failure "merge with conflicts" \ | ||||
| 	"git-merge-file test.txt orig.txt new3.txt" | ||||
|  | ||||
| cat > expect.txt << EOF | ||||
| <<<<<<< test.txt | ||||
| Dominus regit me, et nihil mihi deerit. | ||||
| ======= | ||||
| DOMINUS regit me, | ||||
| et nihil mihi deerit. | ||||
| >>>>>>> new3.txt | ||||
| In loco pascuae ibi me collocavit, | ||||
| super aquam refectionis educavit me; | ||||
| animam meam convertit, | ||||
| deduxit me super semitas jusitiae, | ||||
| propter nomen suum. | ||||
| Nam et si ambulavero in medio umbrae mortis, | ||||
| non timebo mala, quoniam tu mecum es: | ||||
| virga tua et baculus tuus ipsa me consolata sunt. | ||||
| EOF | ||||
|  | ||||
| test_expect_success "expected conflict markers" "diff -u test.txt expect.txt" | ||||
|  | ||||
| cp backup.txt test.txt | ||||
| test_expect_failure "merge with conflicts, using -L" \ | ||||
| 	"git-merge-file -L 1 -L 2 test.txt orig.txt new3.txt" | ||||
|  | ||||
| cat > expect.txt << EOF | ||||
| <<<<<<< 1 | ||||
| Dominus regit me, et nihil mihi deerit. | ||||
| ======= | ||||
| DOMINUS regit me, | ||||
| et nihil mihi deerit. | ||||
| >>>>>>> new3.txt | ||||
| In loco pascuae ibi me collocavit, | ||||
| super aquam refectionis educavit me; | ||||
| animam meam convertit, | ||||
| deduxit me super semitas jusitiae, | ||||
| propter nomen suum. | ||||
| Nam et si ambulavero in medio umbrae mortis, | ||||
| non timebo mala, quoniam tu mecum es: | ||||
| virga tua et baculus tuus ipsa me consolata sunt. | ||||
| EOF | ||||
|  | ||||
| test_expect_success "expected conflict markers, with -L" \ | ||||
| 	"diff -u test.txt expect.txt" | ||||
|  | ||||
| test_done | ||||
|  | ||||
|  | @ -58,9 +58,19 @@ GIT_AUTHOR_DATE="2006-12-12 23:00:08" git commit -m F | |||
|  | ||||
| test_expect_failure "combined merge conflicts" "git merge -m final G" | ||||
|  | ||||
| cat > expect << EOF | ||||
| <<<<<<< HEAD/a1 | ||||
| F | ||||
| ======= | ||||
| G | ||||
| >>>>>>> 26f86b677eb03d4d956dbe108b29cb77061c1e73/a1 | ||||
| EOF | ||||
|  | ||||
| test_expect_success "result contains a conflict" "diff -u expect a1" | ||||
|  | ||||
| git ls-files --stage > out | ||||
| cat > expect << EOF | ||||
| 100644 f70f10e4db19068f79bc43844b49f3eece45c4e8 1	a1 | ||||
| 100644 f16f906ab60483c100d1241dfc39868de9ec9fcb 1	a1 | ||||
| 100644 cf84443e49e1b366fac938711ddf4be2d4d1d9e9 2	a1 | ||||
| 100644 fd7923529855d0b274795ae3349c5e0438333979 3	a1 | ||||
| EOF | ||||
|  |  | |||
|  | @ -49,6 +49,9 @@ extern "C" { | |||
| #define XDL_BDOP_CPY 2 | ||||
| #define XDL_BDOP_INSB 3 | ||||
|  | ||||
| #define XDL_MERGE_MINIMAL 0 | ||||
| #define XDL_MERGE_EAGER 1 | ||||
| #define XDL_MERGE_ZEALOUS 2 | ||||
|  | ||||
| typedef struct s_mmfile { | ||||
| 	char *ptr; | ||||
|  | @ -90,6 +93,10 @@ long xdl_mmfile_size(mmfile_t *mmf); | |||
| int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, | ||||
| 	     xdemitconf_t const *xecfg, xdemitcb_t *ecb); | ||||
|  | ||||
| int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1, | ||||
| 		mmfile_t *mf2, const char *name2, | ||||
| 		xpparam_t const *xpp, int level, mmbuffer_t *result); | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif /* #ifdef __cplusplus */ | ||||
|  |  | |||
|  | @ -45,7 +45,6 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1, | |||
| 		      long *kvdf, long *kvdb, int need_min, xdpsplit_t *spl, | ||||
| 		      xdalgoenv_t *xenv); | ||||
| static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1, long chg2); | ||||
| static int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags); | ||||
|  | ||||
|  | ||||
|  | ||||
|  | @ -397,7 +396,7 @@ static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1, | |||
| } | ||||
|  | ||||
|  | ||||
| static int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) { | ||||
| int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) { | ||||
| 	long ix, ixo, ixs, ixref, grpsiz, nrec = xdf->nrec; | ||||
| 	char *rchg = xdf->rchg, *rchgo = xdfo->rchg; | ||||
| 	xrecord_t **recs = xdf->recs; | ||||
|  |  | |||
|  | @ -50,6 +50,7 @@ int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1, | |||
| 		 long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv); | ||||
| int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, | ||||
| 		xdfenv_t *xe); | ||||
| int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags); | ||||
| int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr); | ||||
| void xdl_free_script(xdchange_t *xscr); | ||||
| int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb, | ||||
|  |  | |||
|  | @ -0,0 +1,419 @@ | |||
| /* | ||||
|  *  LibXDiff by Davide Libenzi ( File Differential Library ) | ||||
|  *  Copyright (C) 2003-2006 Davide Libenzi, Johannes E. Schindelin | ||||
|  * | ||||
|  *  This library is free software; you can redistribute it and/or | ||||
|  *  modify it under the terms of the GNU Lesser General Public | ||||
|  *  License as published by the Free Software Foundation; either | ||||
|  *  version 2.1 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  *  This library is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  *  Lesser General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU Lesser General Public | ||||
|  *  License along with this library; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  *  Davide Libenzi <davidel@xmailserver.org> | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "xinclude.h" | ||||
|  | ||||
| typedef struct s_xdmerge { | ||||
| 	struct s_xdmerge *next; | ||||
| 	/* | ||||
| 	 * 0 = conflict, | ||||
| 	 * 1 = no conflict, take first, | ||||
| 	 * 2 = no conflict, take second. | ||||
| 	 */ | ||||
| 	int mode; | ||||
| 	long i1, i2; | ||||
| 	long chg1, chg2; | ||||
| } xdmerge_t; | ||||
|  | ||||
| static int xdl_append_merge(xdmerge_t **merge, int mode, | ||||
| 		long i1, long chg1, long i2, long chg2) | ||||
| { | ||||
| 	xdmerge_t *m = *merge; | ||||
| 	if (m && (i1 <= m->i1 + m->chg1 || i2 <= m->i2 + m->chg2)) { | ||||
| 		if (mode != m->mode) | ||||
| 			m->mode = 0; | ||||
| 		m->chg1 = i1 + chg1 - m->i1; | ||||
| 		m->chg2 = i2 + chg2 - m->i2; | ||||
| 	} else { | ||||
| 		m = xdl_malloc(sizeof(xdmerge_t)); | ||||
| 		if (!m) | ||||
| 			return -1; | ||||
| 		m->next = NULL; | ||||
| 		m->mode = mode; | ||||
| 		m->i1 = i1; | ||||
| 		m->chg1 = chg1; | ||||
| 		m->i2 = i2; | ||||
| 		m->chg2 = chg2; | ||||
| 		if (*merge) | ||||
| 			(*merge)->next = m; | ||||
| 		*merge = m; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int xdl_cleanup_merge(xdmerge_t *c) | ||||
| { | ||||
| 	int count = 0; | ||||
| 	xdmerge_t *next_c; | ||||
|  | ||||
| 	/* were there conflicts? */ | ||||
| 	for (; c; c = next_c) { | ||||
| 		if (c->mode == 0) | ||||
| 			count++; | ||||
| 		next_c = c->next; | ||||
| 		free(c); | ||||
| 	} | ||||
| 	return count; | ||||
| } | ||||
|  | ||||
| static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2, | ||||
| 		int line_count, long flags) | ||||
| { | ||||
| 	int i; | ||||
| 	xrecord_t **rec1 = xe1->xdf2.recs + i1; | ||||
| 	xrecord_t **rec2 = xe2->xdf2.recs + i2; | ||||
|  | ||||
| 	for (i = 0; i < line_count; i++) { | ||||
| 		int result = xdl_recmatch(rec1[i]->ptr, rec1[i]->size, | ||||
| 			rec2[i]->ptr, rec2[i]->size, flags); | ||||
| 		if (!result) | ||||
| 			return -1; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int xdl_recs_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest) | ||||
| { | ||||
| 	xrecord_t **recs = xe->xdf2.recs + i; | ||||
| 	int size = 0; | ||||
|  | ||||
| 	if (count < 1) | ||||
| 		return 0; | ||||
|  | ||||
| 	for (i = 0; i < count; size += recs[i++]->size) | ||||
| 		if (dest) | ||||
| 			memcpy(dest + size, recs[i]->ptr, recs[i]->size); | ||||
| 	if (add_nl) { | ||||
| 		i = recs[count - 1]->size; | ||||
| 		if (i == 0 || recs[count - 1]->ptr[i - 1] != '\n') { | ||||
| 			if (dest) | ||||
| 				dest[size] = '\n'; | ||||
| 			size++; | ||||
| 		} | ||||
| 	} | ||||
| 	return size; | ||||
| } | ||||
|  | ||||
| static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1, | ||||
| 		xdfenv_t *xe2, const char *name2, xdmerge_t *m, char *dest) | ||||
| { | ||||
| 	const int marker_size = 7; | ||||
| 	int marker1_size = (name1 ? strlen(name1) + 1 : 0); | ||||
| 	int marker2_size = (name2 ? strlen(name2) + 1 : 0); | ||||
| 	int conflict_marker_size = 3 * (marker_size + 1) | ||||
| 		+ marker1_size + marker2_size; | ||||
| 	int size, i1, j; | ||||
|  | ||||
| 	for (size = i1 = 0; m; m = m->next) { | ||||
| 		if (m->mode == 0) { | ||||
| 			size += xdl_recs_copy(xe1, i1, m->i1 - i1, 0, | ||||
| 					dest ? dest + size : NULL); | ||||
| 			if (dest) { | ||||
| 				for (j = 0; j < marker_size; j++) | ||||
| 					dest[size++] = '<'; | ||||
| 				if (marker1_size) { | ||||
| 					dest[size] = ' '; | ||||
| 					memcpy(dest + size + 1, name1, | ||||
| 							marker1_size - 1); | ||||
| 					size += marker1_size; | ||||
| 				} | ||||
| 				dest[size++] = '\n'; | ||||
| 			} else | ||||
| 				size += conflict_marker_size; | ||||
| 			size += xdl_recs_copy(xe1, m->i1, m->chg1, 1, | ||||
| 					dest ? dest + size : NULL); | ||||
| 			if (dest) { | ||||
| 				for (j = 0; j < marker_size; j++) | ||||
| 					dest[size++] = '='; | ||||
| 				dest[size++] = '\n'; | ||||
| 			} | ||||
| 			size += xdl_recs_copy(xe2, m->i2, m->chg2, 1, | ||||
| 					dest ? dest + size : NULL); | ||||
| 			if (dest) { | ||||
| 				for (j = 0; j < marker_size; j++) | ||||
| 					dest[size++] = '>'; | ||||
| 				if (marker2_size) { | ||||
| 					dest[size] = ' '; | ||||
| 					memcpy(dest + size + 1, name2, | ||||
| 							marker2_size - 1); | ||||
| 					size += marker2_size; | ||||
| 				} | ||||
| 				dest[size++] = '\n'; | ||||
| 			} | ||||
| 		} else if (m->mode == 1) | ||||
| 			size += xdl_recs_copy(xe1, i1, m->i1 + m->chg1 - i1, 0, | ||||
| 					dest ? dest + size : NULL); | ||||
| 		else if (m->mode == 2) | ||||
| 			size += xdl_recs_copy(xe2, m->i2 - m->i1 + i1, | ||||
| 					m->i1 + m->chg2 - i1, 0, | ||||
| 					dest ? dest + size : NULL); | ||||
| 		i1 = m->i1 + m->chg1; | ||||
| 	} | ||||
| 	size += xdl_recs_copy(xe1, i1, xe1->xdf2.nrec - i1, 0, | ||||
| 			dest ? dest + size : NULL); | ||||
| 	return size; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Sometimes, changes are not quite identical, but differ in only a few | ||||
|  * lines. Try hard to show only these few lines as conflicting. | ||||
|  */ | ||||
| static int xdl_refine_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m, | ||||
| 		xpparam_t const *xpp) | ||||
| { | ||||
| 	for (; m; m = m->next) { | ||||
| 		mmfile_t t1, t2; | ||||
| 		xdfenv_t xe; | ||||
| 		xdchange_t *xscr, *x; | ||||
| 		int i1 = m->i1, i2 = m->i2; | ||||
|  | ||||
| 		/* let's handle just the conflicts */ | ||||
| 		if (m->mode) | ||||
| 			continue; | ||||
|  | ||||
| 		/* | ||||
| 		 * This probably does not work outside git, since | ||||
| 		 * we have a very simple mmfile structure. | ||||
| 		 */ | ||||
| 		t1.ptr = (char *)xe1->xdf2.recs[m->i1]->ptr; | ||||
| 		t1.size = xe1->xdf2.recs[m->i1 + m->chg1 - 1]->ptr | ||||
| 			+ xe1->xdf2.recs[m->i1 + m->chg1 - 1]->size - t1.ptr; | ||||
| 		t2.ptr = (char *)xe2->xdf2.recs[m->i2]->ptr; | ||||
| 		t2.size = xe2->xdf2.recs[m->i2 + m->chg2 - 1]->ptr | ||||
| 			+ xe2->xdf2.recs[m->i2 + m->chg2 - 1]->size - t2.ptr; | ||||
| 		if (xdl_do_diff(&t1, &t2, xpp, &xe) < 0) | ||||
| 			return -1; | ||||
| 		if (xdl_change_compact(&xe.xdf1, &xe.xdf2, xpp->flags) < 0 || | ||||
| 		    xdl_change_compact(&xe.xdf2, &xe.xdf1, xpp->flags) < 0 || | ||||
| 		    xdl_build_script(&xe, &xscr) < 0) { | ||||
| 			xdl_free_env(&xe); | ||||
| 			return -1; | ||||
| 		} | ||||
| 		if (!xscr) { | ||||
| 			/* If this happens, it's a bug. */ | ||||
| 			xdl_free_env(&xe); | ||||
| 			return -2; | ||||
| 		} | ||||
| 		x = xscr; | ||||
| 		m->i1 = xscr->i1 + i1; | ||||
| 		m->chg1 = xscr->chg1; | ||||
| 		m->i2 = xscr->i2 + i2; | ||||
| 		m->chg2 = xscr->chg2; | ||||
| 		while (xscr->next) { | ||||
| 			xdmerge_t *m2 = xdl_malloc(sizeof(xdmerge_t)); | ||||
| 			if (!m2) { | ||||
| 				xdl_free_env(&xe); | ||||
| 				xdl_free_script(x); | ||||
| 				return -1; | ||||
| 			} | ||||
| 			xscr = xscr->next; | ||||
| 			m2->next = m->next; | ||||
| 			m->next = m2; | ||||
| 			m = m2; | ||||
| 			m->mode = 0; | ||||
| 			m->i1 = xscr->i1 + i1; | ||||
| 			m->chg1 = xscr->chg1; | ||||
| 			m->i2 = xscr->i2 + i2; | ||||
| 			m->chg2 = xscr->chg2; | ||||
| 		} | ||||
| 		xdl_free_env(&xe); | ||||
| 		xdl_free_script(x); | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * level == 0: mark all overlapping changes as conflict | ||||
|  * level == 1: mark overlapping changes as conflict only if not identical | ||||
|  * level == 2: analyze non-identical changes for minimal conflict set | ||||
|  * | ||||
|  * returns < 0 on error, == 0 for no conflicts, else number of conflicts | ||||
|  */ | ||||
| static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1, | ||||
| 		xdfenv_t *xe2, xdchange_t *xscr2, const char *name2, | ||||
| 		int level, xpparam_t const *xpp, mmbuffer_t *result) { | ||||
| 	xdmerge_t *changes, *c; | ||||
| 	int i1, i2, chg1, chg2; | ||||
|  | ||||
| 	c = changes = NULL; | ||||
|  | ||||
| 	while (xscr1 && xscr2) { | ||||
| 		if (!changes) | ||||
| 			changes = c; | ||||
| 		if (xscr1->i1 + xscr1->chg1 < xscr2->i1) { | ||||
| 			i1 = xscr1->i2; | ||||
| 			i2 = xscr2->i2 - xscr2->i1 + xscr1->i1; | ||||
| 			chg1 = xscr1->chg2; | ||||
| 			chg2 = xscr1->chg1; | ||||
| 			if (xdl_append_merge(&c, 1, i1, chg1, i2, chg2)) { | ||||
| 				xdl_cleanup_merge(changes); | ||||
| 				return -1; | ||||
| 			} | ||||
| 			xscr1 = xscr1->next; | ||||
| 			continue; | ||||
| 		} | ||||
| 		if (xscr2->i1 + xscr2->chg1 < xscr1->i1) { | ||||
| 			i1 = xscr1->i2 - xscr1->i1 + xscr2->i1; | ||||
| 			i2 = xscr2->i2; | ||||
| 			chg1 = xscr2->chg1; | ||||
| 			chg2 = xscr2->chg2; | ||||
| 			if (xdl_append_merge(&c, 2, i1, chg1, i2, chg2)) { | ||||
| 				xdl_cleanup_merge(changes); | ||||
| 				return -1; | ||||
| 			} | ||||
| 			xscr2 = xscr2->next; | ||||
| 			continue; | ||||
| 		} | ||||
| 		if (level < 1 || xscr1->i1 != xscr2->i1 || | ||||
| 				xscr1->chg1 != xscr2->chg1 || | ||||
| 				xscr1->chg2 != xscr2->chg2 || | ||||
| 				xdl_merge_cmp_lines(xe1, xscr1->i2, | ||||
| 					xe2, xscr2->i2, | ||||
| 					xscr1->chg2, xpp->flags)) { | ||||
| 			/* conflict */ | ||||
| 			int off = xscr1->i1 - xscr2->i1; | ||||
| 			int ffo = off + xscr1->chg1 - xscr2->chg1; | ||||
|  | ||||
| 			i1 = xscr1->i2; | ||||
| 			i2 = xscr2->i2; | ||||
| 			if (off > 0) | ||||
| 				i1 -= off; | ||||
| 			else | ||||
| 				i2 += off; | ||||
| 			chg1 = xscr1->i2 + xscr1->chg2 - i1; | ||||
| 			chg2 = xscr2->i2 + xscr2->chg2 - i2; | ||||
| 			if (ffo > 0) | ||||
| 				chg2 += ffo; | ||||
| 			else | ||||
| 				chg1 -= ffo; | ||||
| 			if (xdl_append_merge(&c, 0, i1, chg1, i2, chg2)) { | ||||
| 				xdl_cleanup_merge(changes); | ||||
| 				return -1; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		i1 = xscr1->i1 + xscr1->chg1; | ||||
| 		i2 = xscr2->i1 + xscr2->chg1; | ||||
|  | ||||
| 		if (i1 >= i2) | ||||
| 			xscr2 = xscr2->next; | ||||
| 		if (i2 >= i1) | ||||
| 			xscr1 = xscr1->next; | ||||
| 	} | ||||
| 	while (xscr1) { | ||||
| 		if (!changes) | ||||
| 			changes = c; | ||||
| 		i1 = xscr1->i2; | ||||
| 		i2 = xscr1->i1 + xe2->xdf2.nrec - xe2->xdf1.nrec; | ||||
| 		chg1 = xscr1->chg2; | ||||
| 		chg2 = xscr1->chg1; | ||||
| 		if (xdl_append_merge(&c, 1, i1, chg1, i2, chg2)) { | ||||
| 			xdl_cleanup_merge(changes); | ||||
| 			return -1; | ||||
| 		} | ||||
| 		xscr1 = xscr1->next; | ||||
| 	} | ||||
| 	while (xscr2) { | ||||
| 		if (!changes) | ||||
| 			changes = c; | ||||
| 		i1 = xscr2->i1 + xe1->xdf2.nrec - xe1->xdf1.nrec; | ||||
| 		i2 = xscr2->i2; | ||||
| 		chg1 = xscr2->chg1; | ||||
| 		chg2 = xscr2->chg2; | ||||
| 		if (xdl_append_merge(&c, 2, i1, chg1, i2, chg2)) { | ||||
| 			xdl_cleanup_merge(changes); | ||||
| 			return -1; | ||||
| 		} | ||||
| 		xscr2 = xscr2->next; | ||||
| 	} | ||||
| 	if (!changes) | ||||
| 		changes = c; | ||||
| 	/* refine conflicts */ | ||||
| 	if (level > 1 && xdl_refine_conflicts(xe1, xe2, changes, xpp) < 0) { | ||||
| 		xdl_cleanup_merge(changes); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	/* output */ | ||||
| 	if (result) { | ||||
| 		int size = xdl_fill_merge_buffer(xe1, name1, xe2, name2, | ||||
| 			changes, NULL); | ||||
| 		result->ptr = xdl_malloc(size); | ||||
| 		if (!result->ptr) { | ||||
| 			xdl_cleanup_merge(changes); | ||||
| 			return -1; | ||||
| 		} | ||||
| 		result->size = size; | ||||
| 		xdl_fill_merge_buffer(xe1, name1, xe2, name2, changes, | ||||
| 				result->ptr); | ||||
| 	} | ||||
| 	return xdl_cleanup_merge(changes); | ||||
| } | ||||
|  | ||||
| int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1, | ||||
| 		mmfile_t *mf2, const char *name2, | ||||
| 		xpparam_t const *xpp, int level, mmbuffer_t *result) { | ||||
| 	xdchange_t *xscr1, *xscr2; | ||||
| 	xdfenv_t xe1, xe2; | ||||
| 	int status; | ||||
|  | ||||
| 	result->ptr = NULL; | ||||
| 	result->size = 0; | ||||
|  | ||||
| 	if (xdl_do_diff(orig, mf1, xpp, &xe1) < 0 || | ||||
| 			xdl_do_diff(orig, mf2, xpp, &xe2) < 0) { | ||||
| 		return -1; | ||||
| 	} | ||||
| 	if (xdl_change_compact(&xe1.xdf1, &xe1.xdf2, xpp->flags) < 0 || | ||||
| 	    xdl_change_compact(&xe1.xdf2, &xe1.xdf1, xpp->flags) < 0 || | ||||
| 	    xdl_build_script(&xe1, &xscr1) < 0) { | ||||
| 		xdl_free_env(&xe1); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	if (xdl_change_compact(&xe2.xdf1, &xe2.xdf2, xpp->flags) < 0 || | ||||
| 	    xdl_change_compact(&xe2.xdf2, &xe2.xdf1, xpp->flags) < 0 || | ||||
| 	    xdl_build_script(&xe2, &xscr2) < 0) { | ||||
| 		xdl_free_env(&xe2); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	status = 0; | ||||
| 	if (xscr1 || xscr2) { | ||||
| 		if (!xscr1) { | ||||
| 			result->ptr = xdl_malloc(mf2->size); | ||||
| 			memcpy(result->ptr, mf2->ptr, mf2->size); | ||||
| 			result->size = mf2->size; | ||||
| 		} else if (!xscr2) { | ||||
| 			result->ptr = xdl_malloc(mf1->size); | ||||
| 			memcpy(result->ptr, mf1->ptr, mf1->size); | ||||
| 			result->size = mf1->size; | ||||
| 		} else { | ||||
| 			status = xdl_do_merge(&xe1, xscr1, name1, | ||||
| 					      &xe2, xscr2, name2, | ||||
| 					      level, xpp, result); | ||||
| 		} | ||||
| 		xdl_free_script(xscr1); | ||||
| 		xdl_free_script(xscr2); | ||||
| 	} | ||||
| 	xdl_free_env(&xe1); | ||||
| 	xdl_free_env(&xe2); | ||||
|  | ||||
| 	return status; | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	 Junio C Hamano
						Junio C Hamano