Beispiel #1
0
Datei: rerere.c Projekt: emk/git
static int merge(const char *name, const char *path)
{
	int ret;
	mmfile_t cur, base, other;
	mmbuffer_t result = {NULL, 0};
	xpparam_t xpp = {XDF_NEED_MINIMAL};

	if (handle_file(path, NULL, rr_path(name, "thisimage")) < 0)
		return 1;

	if (read_mmfile(&cur, rr_path(name, "thisimage")) ||
			read_mmfile(&base, rr_path(name, "preimage")) ||
			read_mmfile(&other, rr_path(name, "postimage")))
		return 1;
	ret = xdl_merge(&base, &cur, "", &other, "",
			&xpp, XDL_MERGE_ZEALOUS, &result);
	if (!ret) {
		FILE *f = fopen(path, "w");
		if (!f)
			return error("Could not open %s: %s", path,
				     strerror(errno));
		if (fwrite(result.ptr, result.size, 1, f) != 1)
			error("Could not write %s: %s", path, strerror(errno));
		if (fclose(f))
			return error("Writing %s failed: %s", path,
				     strerror(errno));
	}

	free(cur.ptr);
	free(base.ptr);
	free(other.ptr);
	free(result.ptr);

	return ret;
}
Beispiel #2
0
static int diff_two(const char *file1, const char *label1,
		const char *file2, const char *label2)
{
	xpparam_t xpp;
	xdemitconf_t xecfg;
	xdemitcb_t ecb;
	mmfile_t minus, plus;
	int ret;

	if (read_mmfile(&minus, file1) || read_mmfile(&plus, file2))
		return -1;

	printf("--- a/%s\n+++ b/%s\n", label1, label2);
	fflush(stdout);
	memset(&xpp, 0, sizeof(xpp));
	xpp.flags = 0;
	memset(&xecfg, 0, sizeof(xecfg));
	xecfg.ctxlen = 3;
	ecb.outf = outf;
	ret = xdi_diff(&minus, &plus, &xpp, &xecfg, &ecb);

	free(minus.ptr);
	free(plus.ptr);
	return ret;
}
Beispiel #3
0
/*
 * Find the conflict identified by "id"; the change between its
 * "preimage" (i.e. a previous contents with conflict markers) and its
 * "postimage" (i.e. the corresponding contents with conflicts
 * resolved) may apply cleanly to the contents stored in "path", i.e.
 * the conflict this time around.
 *
 * Returns 0 for successful replay of recorded resolution, or non-zero
 * for failure.
 */
static int merge(const struct rerere_id *id, const char *path)
{
	FILE *f;
	int ret;
	mmfile_t cur = {NULL, 0}, base = {NULL, 0}, other = {NULL, 0};
	mmbuffer_t result = {NULL, 0};

	/*
	 * Normalize the conflicts in path and write it out to
	 * "thisimage" temporary file.
	 */
	if (handle_file(path, NULL, rerere_path(id, "thisimage")) < 0) {
		ret = 1;
		goto out;
	}

	if (read_mmfile(&cur, rerere_path(id, "thisimage")) ||
	    read_mmfile(&base, rerere_path(id, "preimage")) ||
	    read_mmfile(&other, rerere_path(id, "postimage"))) {
		ret = 1;
		goto out;
	}

	/*
	 * A three-way merge. Note that this honors user-customizable
	 * low-level merge driver settings.
	 */
	ret = ll_merge(&result, path, &base, NULL, &cur, "", &other, "", NULL);
	if (ret)
		goto out;

	/*
	 * A successful replay of recorded resolution.
	 * Mark that "postimage" was used to help gc.
	 */
	if (utime(rerere_path(id, "postimage"), NULL) < 0)
		warning("failed utime() on %s: %s",
			rerere_path(id, "postimage"),
			strerror(errno));

	/* Update "path" with the resolution */
	f = fopen(path, "w");
	if (!f)
		return error("Could not open %s: %s", path,
			     strerror(errno));
	if (fwrite(result.ptr, result.size, 1, f) != 1)
		error("Could not write %s: %s", path, strerror(errno));
	if (fclose(f))
		return error("Writing %s failed: %s", path,
			     strerror(errno));

out:
	free(cur.ptr);
	free(base.ptr);
	free(other.ptr);
	free(result.ptr);

	return ret;
}
Beispiel #4
0
static int merge(const char *name, const char *path)
{
	int ret;
	mmfile_t cur = {NULL, 0}, base = {NULL, 0}, other = {NULL, 0};
	mmbuffer_t result = {NULL, 0};

	if (handle_file(path, NULL, rerere_path(name, "thisimage")) < 0)
		return 1;

	if (read_mmfile(&cur, rerere_path(name, "thisimage")) ||
			read_mmfile(&base, rerere_path(name, "preimage")) ||
			read_mmfile(&other, rerere_path(name, "postimage"))) {
		ret = 1;
		goto out;
	}
	ret = ll_merge(&result, path, &base, NULL, &cur, "", &other, "", NULL);
	if (!ret) {
		FILE *f;

		if (utime(rerere_path(name, "postimage"), NULL) < 0)
			warning("failed utime() on %s: %s",
					rerere_path(name, "postimage"),
					strerror(errno));
		f = fopen(path, "w");
		if (!f)
			return error("Could not open %s: %s", path,
				     strerror(errno));
		if (fwrite(result.ptr, result.size, 1, f) != 1)
			error("Could not write %s: %s", path, strerror(errno));
		if (fclose(f))
			return error("Writing %s failed: %s", path,
				     strerror(errno));
	}

out:
	free(cur.ptr);
	free(base.ptr);
	free(other.ptr);
	free(result.ptr);

	return ret;
}
Beispiel #5
0
/*
 * Try using the given conflict resolution "ID" to see
 * if that recorded conflict resolves cleanly what we
 * got in the "cur".
 */
static int try_merge(const struct rerere_id *id, const char *path,
		     mmfile_t *cur, mmbuffer_t *result)
{
	int ret;
	mmfile_t base = {NULL, 0}, other = {NULL, 0};

	if (read_mmfile(&base, rerere_path(id, "preimage")) ||
	    read_mmfile(&other, rerere_path(id, "postimage")))
		ret = 1;
	else
		/*
		 * A three-way merge. Note that this honors user-customizable
		 * low-level merge driver settings.
		 */
		ret = ll_merge(result, path, &base, NULL, cur, "", &other, "", NULL);

	free(base.ptr);
	free(other.ptr);

	return ret;
}
Beispiel #6
0
/*
 * Find the conflict identified by "id"; the change between its
 * "preimage" (i.e. a previous contents with conflict markers) and its
 * "postimage" (i.e. the corresponding contents with conflicts
 * resolved) may apply cleanly to the contents stored in "path", i.e.
 * the conflict this time around.
 *
 * Returns 0 for successful replay of recorded resolution, or non-zero
 * for failure.
 */
static int merge(const struct rerere_id *id, const char *path)
{
	FILE *f;
	int ret;
	mmfile_t cur = {NULL, 0};
	mmbuffer_t result = {NULL, 0};

	/*
	 * Normalize the conflicts in path and write it out to
	 * "thisimage" temporary file.
	 */
	if ((handle_file(path, NULL, rerere_path(id, "thisimage")) < 0) ||
	    read_mmfile(&cur, rerere_path(id, "thisimage"))) {
		ret = 1;
		goto out;
	}

	ret = try_merge(id, path, &cur, &result);
	if (ret)
		goto out;

	/*
	 * A successful replay of recorded resolution.
	 * Mark that "postimage" was used to help gc.
	 */
	if (utime(rerere_path(id, "postimage"), NULL) < 0)
		warning_errno(_("failed utime() on '%s'"),
			      rerere_path(id, "postimage"));

	/* Update "path" with the resolution */
	f = fopen(path, "w");
	if (!f)
		return error_errno(_("could not open '%s'"), path);
	if (fwrite(result.ptr, result.size, 1, f) != 1)
		error_errno(_("could not write '%s'"), path);
	if (fclose(f))
		return error_errno(_("writing '%s' failed"), path);

out:
	free(cur.ptr);
	free(result.ptr);

	return ret;
}
Beispiel #7
0
static int rerere_forget_one_path(const char *path, struct string_list *rr)
{
	const char *filename;
	struct rerere_id *id;
	unsigned char sha1[20];
	int ret;
	struct string_list_item *item;

	/*
	 * Recreate the original conflict from the stages in the
	 * index and compute the conflict ID
	 */
	ret = handle_cache(path, sha1, NULL);
	if (ret < 1)
		return error(_("could not parse conflict hunks in '%s'"), path);

	/* Nuke the recorded resolution for the conflict */
	id = new_rerere_id(sha1);

	for (id->variant = 0;
	     id->variant < id->collection->status_nr;
	     id->variant++) {
		mmfile_t cur = { NULL, 0 };
		mmbuffer_t result = {NULL, 0};
		int cleanly_resolved;

		if (!has_rerere_resolution(id))
			continue;

		handle_cache(path, sha1, rerere_path(id, "thisimage"));
		if (read_mmfile(&cur, rerere_path(id, "thisimage"))) {
			free(cur.ptr);
			error(_("failed to update conflicted state in '%s'"), path);
			goto fail_exit;
		}
		cleanly_resolved = !try_merge(id, path, &cur, &result);
		free(result.ptr);
		free(cur.ptr);
		if (cleanly_resolved)
			break;
	}

	if (id->collection->status_nr <= id->variant) {
		error(_("no remembered resolution for '%s'"), path);
		goto fail_exit;
	}

	filename = rerere_path(id, "postimage");
	if (unlink(filename)) {
		if (errno == ENOENT)
			error(_("no remembered resolution for '%s'"), path);
		else
			error_errno(_("cannot unlink '%s'"), filename);
		goto fail_exit;
	}

	/*
	 * Update the preimage so that the user can resolve the
	 * conflict in the working tree, run us again to record
	 * the postimage.
	 */
	handle_cache(path, sha1, rerere_path(id, "preimage"));
	fprintf_ln(stderr, _("Updated preimage for '%s'"), path);

	/*
	 * And remember that we can record resolution for this
	 * conflict when the user is done.
	 */
	item = string_list_insert(rr, path);
	free_rerere_id(item);
	item->util = id;
	fprintf(stderr, _("Forgot resolution for '%s'\n"), path);
	return 0;

fail_exit:
	free(id);
	return -1;
}
Beispiel #8
0
int cmd_merge_file(int argc, const char **argv, const char *prefix)
{
	const char *names[3] = { NULL, NULL, NULL };
	mmfile_t mmfs[3];
	mmbuffer_t result = {NULL, 0};
	xmparam_t xmp = {{0}};
	int ret = 0, i = 0, to_stdout = 0;
	int quiet = 0;
	int prefixlen = 0;
	struct option options[] = {
		OPT_BOOLEAN('p', "stdout", &to_stdout, "send results to standard output"),
		OPT_SET_INT(0, "diff3", &xmp.style, "use a diff3 based merge", XDL_MERGE_DIFF3),
		OPT_SET_INT(0, "ours", &xmp.favor, "for conflicts, use our version",
			    XDL_MERGE_FAVOR_OURS),
		OPT_SET_INT(0, "theirs", &xmp.favor, "for conflicts, use their version",
			    XDL_MERGE_FAVOR_THEIRS),
		OPT_SET_INT(0, "union", &xmp.favor, "for conflicts, use a union version",
			    XDL_MERGE_FAVOR_UNION),
		OPT_INTEGER(0, "marker-size", &xmp.marker_size,
			    "for conflicts, use this marker size"),
		OPT__QUIET(&quiet, "do not warn about conflicts"),
		OPT_CALLBACK('L', NULL, names, "name",
			     "set labels for file1/orig_file/file2", &label_cb),
		OPT_END(),
	};

	xmp.level = XDL_MERGE_ZEALOUS_ALNUM;
	xmp.style = 0;
	xmp.favor = 0;

	if (startup_info->have_repository) {
		/* Read the configuration file */
		git_config(git_xmerge_config, NULL);
		if (0 <= git_xmerge_style)
			xmp.style = git_xmerge_style;
	}

	argc = parse_options(argc, argv, prefix, options, merge_file_usage, 0);
	if (argc != 3)
		usage_with_options(merge_file_usage, options);
	if (quiet) {
		if (!freopen("/dev/null", "w", stderr))
			return error("failed to redirect stderr to /dev/null: "
				     "%s", strerror(errno));
	}

	if (prefix)
		prefixlen = strlen(prefix);

	for (i = 0; i < 3; i++) {
		const char *fname = prefix_filename(prefix, prefixlen, argv[i]);
		if (!names[i])
			names[i] = argv[i];
		if (read_mmfile(mmfs + i, fname))
			return -1;
		if (buffer_is_binary(mmfs[i].ptr, mmfs[i].size))
			return error("Cannot merge binary files: %s",
					argv[i]);
	}

	xmp.ancestor = names[1];
	xmp.file1 = names[0];
	xmp.file2 = names[2];
	ret = xdl_merge(mmfs + 1, mmfs + 0, mmfs + 2, &xmp, &result);

	for (i = 0; i < 3; i++)
		free(mmfs[i].ptr);

	if (ret >= 0) {
		const char *filename = argv[0];
		FILE *f = to_stdout ? stdout : fopen(filename, "wb");

		if (!f)
			ret = error("Could not open %s for writing", filename);
		else if (result.size &&
			 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;
}
Beispiel #9
0
int cmd_merge_file(int argc, const char **argv, const char *prefix)
{
	const char *names[3] = { NULL, NULL, NULL };
	mmfile_t mmfs[3];
	mmbuffer_t result = {NULL, 0};
	xmparam_t xmp = {{XDF_NEED_MINIMAL}};
	int ret = 0, i = 0, to_stdout = 0;
	int level = XDL_MERGE_ZEALOUS_ALNUM;
	int style = 0, quiet = 0;
	int favor = 0;
	int nongit;

	struct option options[] = {
		OPT_BOOLEAN('p', "stdout", &to_stdout, "send results to standard output"),
		OPT_SET_INT(0, "diff3", &style, "use a diff3 based merge", XDL_MERGE_DIFF3),
		OPT_SET_INT(0, "ours", &favor, "for conflicts, use our version",
			    XDL_MERGE_FAVOR_OURS),
		OPT_SET_INT(0, "theirs", &favor, "for conflicts, use their version",
			    XDL_MERGE_FAVOR_THEIRS),
		OPT__QUIET(&quiet),
		OPT_CALLBACK('L', NULL, names, "name",
			     "set labels for file1/orig_file/file2", &label_cb),
		OPT_END(),
	};

	prefix = setup_git_directory_gently(&nongit);
	if (!nongit) {
		/* Read the configuration file */
		git_config(git_xmerge_config, NULL);
		if (0 <= git_xmerge_style)
			style = git_xmerge_style;
	}

	argc = parse_options(argc, argv, prefix, options, merge_file_usage, 0);
	if (argc != 3)
		usage_with_options(merge_file_usage, options);
	if (quiet) {
		if (!freopen("/dev/null", "w", stderr))
			return error("failed to redirect stderr to /dev/null: "
				     "%s\n", strerror(errno));
	}

	for (i = 0; i < 3; i++) {
		if (!names[i])
			names[i] = argv[i];
		if (read_mmfile(mmfs + i, argv[i]))
			return -1;
		if (buffer_is_binary(mmfs[i].ptr, mmfs[i].size))
			return error("Cannot merge binary files: %s\n",
					argv[i]);
	}

	ret = xdl_merge(mmfs + 1, mmfs + 0, names[0], mmfs + 2, names[2],
			&xmp, XDL_MERGE_FLAGS(level, style, favor), &result);

	for (i = 0; i < 3; i++)
		free(mmfs[i].ptr);

	if (ret >= 0) {
		const char *filename = argv[0];
		FILE *f = to_stdout ? stdout : fopen(filename, "wb");

		if (!f)
			ret = error("Could not open %s for writing", filename);
		else if (result.size &&
			 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;
}