git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Martin Waitz <tali@admingilde.org>
To: git@vger.kernel.org
Subject: [PATCH v2] Submodule merge support
Date: Sun, 20 May 2007 17:42:27 +0200	[thread overview]
Message-ID: <20070520154227.GG5412@admingilde.org> (raw)

When merge-recursive gets to a dirlink, it starts an automatic submodule
merge and then uses the resulting merge commit for the top-level tree.
The submodule merge is done in another process to decouple object databases.

Submodule merges are done solely in the submodules' history, without taking
the supermodule (and it's merge base) into account.  If the submodule merge
is successful then the new submodule version will be used in the merged
supermodule.

If one side of the merge removed any submodule commits (e.g. by switching to
a different branch) then the automatic merge is stopped so that the user can
take a closer look on what happened.

Signed-off-by: Martin Waitz <tali@admingilde.org>
---

This patch is based on my previous submodule checkout patch and the
start-commands-in-submodule patch.

This version takes index_only into account and does not need a new
helper script as all code is done in C now.

The entire ll_merge code in merge-recursive still should be moved to
some generic place, but that is for another patch.

 merge-recursive.c |  122 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 122 insertions(+), 0 deletions(-)

diff --git a/merge-recursive.c b/merge-recursive.c
index 8f72b2c..72562a8 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -11,6 +11,7 @@
 #include "diff.h"
 #include "diffcore.h"
 #include "run-command.h"
+#include "refs.h"
 #include "tag.h"
 #include "unpack-trees.h"
 #include "path-list.h"
@@ -574,6 +575,21 @@ static void update_file_flags(const unsigned char *sha,
 		void *buf;
 		unsigned long size;
 
+		if (S_ISDIRLNK(mode)) {
+			/* defer dirlinks to another process, don't try to */
+			/* read the object "sha" here */
+			const char *dirlink_checkout[] = {
+				"dirlink-checkout", path, sha1_to_hex(sha), NULL
+			};
+			struct child_process cmd = {
+				.argv = dirlink_checkout,
+				.git_cmd = 1,
+			};
+
+			run_command(&cmd);
+			goto update_index;
+		}
+
 		buf = read_sha1_file(sha, &type, &size);
 		if (!buf)
 			die("cannot read object %s '%s'", sha1_to_hex(sha), path);
@@ -1025,6 +1041,105 @@ static int ll_merge(mmbuffer_t *result_buf,
 	return merge_status;
 }
 
+
+static int ll_dirlink_merge_base(const char *path,
+                            const unsigned char *a,
+                            const unsigned char *b,
+                            unsigned char *result)
+{
+	const char *merge_base[] = {
+		"merge-base",
+		sha1_to_hex(a),
+		sha1_to_hex(b),
+		NULL
+	};
+	struct child_process cmd = {
+		.argv = merge_base,
+		.submodule = path,
+		.git_cmd = 1,
+		.out = -1,
+	};
+	char hex[40];
+	int status;
+
+	status = start_command(&cmd);
+	if (status) return status;
+
+	status = read(cmd.out, hex, sizeof(hex));
+	if (status != 40) return status;
+
+	status = finish_command(&cmd);
+	if (status) return status;
+
+	status = get_sha1_hex(hex, result);
+
+	return status;
+}
+
+static int ll_dirlink_merge(const char *path,
+                            const unsigned char *o,
+                            const unsigned char *a,
+                            const unsigned char *b,
+                            unsigned char *result)
+{
+	char b_hex[40+1];
+	const char *merge[] = {
+		"merge", b_hex, NULL
+	};
+	struct child_process cmd = {
+		.argv = merge,
+		.submodule = path,
+		.git_cmd = 1,
+	};
+	int status;
+	unsigned char base[20];
+	unsigned char test[20];
+
+	if (index_only)  {
+		/* as submodules have their own history we don't have to   */
+		/* try to do the index_only intermediate merges.           */
+		/* however we still want to get a submodule version        */
+		/* which is suitable as merge-base, just to make sure that */
+		/* all merge parents contain this base.                    */
+		/* The real merge (below) aborts if this check fails       */
+		return ll_dirlink_merge_base(path, a, b, result);
+	}
+
+	strcpy(b_hex, sha1_to_hex(b));
+	output(3, "merging submodule %s:", path);
+	output(3, " o=%s", sha1_to_hex(o));
+	output(3, " a=%s", sha1_to_hex(a));
+	output(3, " b=%s", sha1_to_hex(b));
+
+	/* first check that the submodule is in the current state  */
+	/* so that it can be merged.                               */
+	status = resolve_gitlink_ref(path, "HEAD", test);
+	if (hashcmp(test, a)) {
+		return error("can't merge submodule %s: not up to date.", path);
+	}
+
+	/* check that both sides of the superproject only did a    */
+	/* fast forward of the subproject so that it can be merged */
+	/* automatically.                                          */
+	status = ll_dirlink_merge_base(path, a, b, base);
+	if (status) return status;
+	status = ll_dirlink_merge_base(path, o, base, test);
+	if (status) return status;
+	if (hashcmp(test, o)) {
+		return error("can't merge submodule %s: conflicting history",
+		             path);
+	}
+
+	/* now start another merge process for the submodule */
+	status = run_command(&cmd);
+	if (status) return status;
+
+	/* get the new merged version */
+	status = resolve_gitlink_ref(path, "HEAD", result);
+
+	return status;
+}
+
 static struct merge_file_info merge_file(struct diff_filespec *o,
 		struct diff_filespec *a, struct diff_filespec *b,
 		const char *branch1, const char *branch2)
@@ -1069,6 +1184,13 @@ static struct merge_file_info merge_file(struct diff_filespec *o,
 
 			free(result_buf.ptr);
 			result.clean = (merge_status == 0);
+		} else if (S_ISDIRLNK(a->mode)) {
+			int merge_status;
+
+			merge_status = ll_dirlink_merge(a->path,
+				o->sha1, a->sha1, b->sha1, result.sha);
+
+			result.clean = (merge_status == 0);
 		} else {
 			if (!(S_ISLNK(a->mode) || S_ISLNK(b->mode)))
 				die("cannot merge modes?");
-- 
1.5.2.2.g081e


-- 
Martin Waitz

             reply	other threads:[~2007-05-20 15:42 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-05-20 15:42 Martin Waitz [this message]
2007-05-21  6:20 ` [PATCH v2] Submodule merge support Shawn O. Pearce
2007-05-21  7:32   ` Martin Waitz
2007-05-21  7:37     ` Shawn O. Pearce
2007-05-21  7:55       ` Junio C Hamano
2007-05-21 15:42       ` Alex Riesen
2007-05-21  7:54     ` Junio C Hamano
2007-05-21 12:48       ` [PATCH] SubmittingPatches: mention older C compiler compatibility Johannes Schindelin
2007-05-27 14:39         ` [PATCH] Add -Wdeclaration-after-statement to CFLAGS to help enforce the instructions in SubmittingPatches Johan Herland
2007-05-27 14:58           ` Morten Welinder

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20070520154227.GG5412@admingilde.org \
    --to=tali@admingilde.org \
    --cc=git@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).