git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: "Nguyễn Thái Ngọc Duy" <pclouds@gmail.com>
To: git@vger.kernel.org
Cc: "Nguyễn Thái Ngọc Duy" <pclouds@gmail.com>
Subject: [PATCH 26/32] Add upload-narrow-base command
Date: Wed, 25 Aug 2010 08:20:16 +1000	[thread overview]
Message-ID: <1282688422-7738-27-git-send-email-pclouds@gmail.com> (raw)
In-Reply-To: <1282688422-7738-1-git-send-email-pclouds@gmail.com>

A narrow repo only has enough trees within a given subtree. That would
mean even a trivial three-way merge is impossible because it needs
full tree walk.

On the other hand, doing merge remotely is non-sense because conflicts
can happen, and there would be no index on remote side for people to
inspect/resolve conflicts. Such a merge in narrow repo would be
splitted into two phases: 3-way trivial merge for everything outside
narrow tree, and real merge within narrow tree.

The first phase is done in server by this command. Given two commits,
it will do 3-way merge and return result trees of the merge (no blob
should be created because this is trivial merge). No new objects will
be stored in server after this operation.

For local repo, parent trees of narrow tree should be enough for
join_narrow_tree() to do its job. But because remote side does not
store any result objects, in order to push a merge, we would need to
send all necessary trees that the remote side does not have.

For this reason, upload-narrow-base would return a pack of all new
trees (those that are not in either merge parents). It is expected
that git client would push all these trees back to server when it does
a push.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Makefile                     |    1 +
 builtin.h                    |    1 +
 builtin/upload-narrow-base.c |  215 ++++++++++++++++++++++++++++++++++++++++++
 git.c                        |    1 +
 4 files changed, 218 insertions(+), 0 deletions(-)
 create mode 100644 builtin/upload-narrow-base.c

diff --git a/Makefile b/Makefile
index 54c435e..7b33a0e 100644
--- a/Makefile
+++ b/Makefile
@@ -737,6 +737,7 @@ BUILTIN_OBJS += builtin/update-index.o
 BUILTIN_OBJS += builtin/update-ref.o
 BUILTIN_OBJS += builtin/update-server-info.o
 BUILTIN_OBJS += builtin/upload-archive.o
+BUILTIN_OBJS += builtin/upload-narrow-base.o
 BUILTIN_OBJS += builtin/var.o
 BUILTIN_OBJS += builtin/verify-pack.o
 BUILTIN_OBJS += builtin/verify-tag.o
diff --git a/builtin.h b/builtin.h
index ed6ee26..0383328 100644
--- a/builtin.h
+++ b/builtin.h
@@ -131,6 +131,7 @@ extern int cmd_update_index(int argc, const char **argv, const char *prefix);
 extern int cmd_update_ref(int argc, const char **argv, const char *prefix);
 extern int cmd_update_server_info(int argc, const char **argv, const char *prefix);
 extern int cmd_upload_archive(int argc, const char **argv, const char *prefix);
+extern int cmd_upload_narrow_base(int argc, const char **argv, const char *prefix);
 extern int cmd_upload_tar(int argc, const char **argv, const char *prefix);
 extern int cmd_var(int argc, const char **argv, const char *prefix);
 extern int cmd_verify_tag(int argc, const char **argv, const char *prefix);
diff --git a/builtin/upload-narrow-base.c b/builtin/upload-narrow-base.c
new file mode 100644
index 0000000..f31f03e
--- /dev/null
+++ b/builtin/upload-narrow-base.c
@@ -0,0 +1,215 @@
+#include "cache.h"
+#include "builtin.h"
+#include "cache-tree.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "commit.h"
+#include "revision.h"
+#include "unpack-trees.h"
+#include "pkt-line.h"
+#include "pack.h"
+#include "transport.h"
+#include "sideband.h"
+
+const char *narrow_prefix;
+
+#define PARENT1		(1u<<16)
+#define PARENT2		(1u<<17)
+
+static void write_object(struct tree *t,
+			 const char *base,
+			 struct simple_pack *pack)
+{
+	struct name_entry entry;
+	struct tree *subtree;
+	struct tree_desc desc;
+	enum object_type type;
+	unsigned long size;
+	int len = 0;
+	void *buffer;
+	char *path = xmalloc(PATH_MAX);
+
+	if (base) {
+		len = strlen(base);
+		memcpy(path, base, len);
+		path[len++] = '/';
+	}
+
+	buffer = read_sha1_file(t->object.sha1, &type, &size);
+	if (!buffer || type != OBJ_TREE)
+		die("%s is not a tree", sha1_to_hex(t->object.sha1));
+
+	init_tree_desc(&desc, buffer, size);
+	while (tree_entry(&desc, &entry)) {
+		if (!S_ISDIR(entry.mode))
+			continue;
+
+		subtree = lookup_tree(entry.sha1);
+		strcpy(path+len, entry.path);
+		if (!strcmp(path, narrow_prefix))
+			;
+		else if (!prefixcmp(narrow_prefix, path) && /* subtree predecessor */
+			 narrow_prefix[strlen(path)] == '/')
+			;
+		else if (!(subtree->object.flags & (PARENT1 | PARENT2))) {
+			subtree->object.flags |= TMP_MARK; /* non recursive */
+			write_object_to_pack(pack, entry.sha1, subtree->buffer,
+					     subtree->size, OBJ_TREE);
+		}
+
+		write_object(subtree, path, pack);
+	}
+	free(path);
+	free(buffer);
+}
+
+static int read_tree_trivial(struct tree *trees[3])
+{
+	int i;
+	struct tree_desc t[3];
+	struct unpack_trees_options opts;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.head_idx = 2;
+	opts.src_index = &the_index;
+	opts.dst_index = &the_index;
+	opts.merge = 1;
+	opts.fn = threeway_merge;
+	cache_tree_free(&active_cache_tree);
+	for (i = 0; i < 3; i++) {
+		parse_tree(trees[i]);
+		init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
+	}
+	if (unpack_trees(3, t, &opts))
+		return -1;
+	return 0;
+}
+
+static int create_tree_object(const void *buf, unsigned long len,
+			      const char *type, unsigned char *sha1)
+{
+	struct tree *t;
+	if (type != tree_type)
+		die("Invalid type %s", type);
+	hash_sha1_file(buf, len, tree_type, sha1);
+	t = lookup_tree(sha1);
+	if (!t->object.parsed) {
+		t->buffer = xmalloc(len);
+		memcpy(t->buffer, buf, len);
+		t->size = len;
+		t->object.parsed = 1;
+	}
+	return 0;
+}
+
+int cmd_upload_narrow_base(int argc, const char **argv, const char *prefix)
+{
+	char buf[LARGE_PACKET_MAX];
+	unsigned char sha1[2][20];
+	struct tree *t[3];
+	struct commit_list *common;
+	struct simple_pack pack;
+	int i, len;
+
+	strcpy(buf, argv[1]); /* enter-repo smudges its argument */
+
+	if (!enter_repo(buf, 0))
+		die("'%s' does not appear to be a git repository", buf);
+
+	len = packet_read_line(0, buf, sizeof(buf));
+	if (!len)
+		die("narrow-merge: narrow-tree missing");
+	if (!prefixcmp(buf, "narrow-tree ")) {
+		narrow_prefix = xstrdup(buf+12);
+		len = strlen(narrow_prefix);
+		if (!len)
+			die("narrow-merge: empty narrow-tree");
+		if (narrow_prefix[len-1] == '/')
+			die("narrow-merge: trailing slash not allowed %s", narrow_prefix);
+	}
+	else
+		die("narrow-merge: invalid narrow-tree %s", buf);
+
+	for (i = 0; i < 2; i++) {
+		len = packet_read_line(0, buf, sizeof(buf));
+		if (!len)
+			die("narrow-merge: parent missing");
+		if (!prefixcmp(buf, "parent ")) {
+			if (get_sha1_hex(buf+7, sha1[i]))
+				die("narrow-merge: invalid SHA1 %s", buf);
+			if (!lookup_commit_reference(sha1[i]))
+				return 1;
+		}
+		else
+			die("narrow-merge: invalid parent %s", buf);
+	}
+	len = packet_read_line(0, buf, sizeof(buf));
+	if (len)
+		die("narrow-merge: expected a flush");
+
+	common = get_merge_bases(lookup_commit(sha1[0]),
+				 lookup_commit(sha1[1]),
+				 1);
+	if (!common || common->next)
+		die("narrow-merge: no common or too many common found, can't merge");
+
+	t[0] = common->item->tree;
+	t[1] = lookup_commit(sha1[0])->tree;
+	t[2] = lookup_commit(sha1[1])->tree;
+
+	/* Three way merge to index */
+	discard_index(&the_index);
+	if (read_tree_trivial(t))
+		die("narrow-merge: non trivial merge");
+
+	/* Make sure merge is good (no conflicts outside subtree) */
+	len = strlen(narrow_prefix);
+	for (i = 0; i < the_index.cache_nr; i++) {
+		struct cache_entry *ce = the_index.cache[i];
+
+		/*
+		 * No staged entries within subtree, prefer stage 1:
+		 * (the goal is no staged entries, the content is
+		 * not really important as subtree client must do
+		 * a real merge within subtree)
+		 * stage 0: move on
+		 * stage 1: turn it to stage 0,
+		 * stage 2: mark CE_REMOVE
+		 */
+		if (!prefixcmp(ce->name, narrow_prefix) &&
+		    ce->name[len] == '/') {
+			switch (ce_stage(ce)) {
+			case 1: ce->ce_flags &= ~CE_STAGEMASK; break;
+			case 2: ce->ce_flags |= CE_REMOVE; break;
+			}
+			continue;
+		}
+		if (ce_stage(ce))
+			die("git upload-pack: unmerged entry %s", ce->name);
+	}
+
+	/* Generate trees in memory */
+	the_index.cache_tree = cache_tree();
+	if (cache_tree_update_fn(the_index.cache_tree,
+				 the_index.cache,
+				 the_index.cache_nr,
+				 create_tree_object,
+				 0, 0))
+		die("narrow-merge: error generating trees");
+
+	/* Everything is OK. Send root SHA-1 */
+	packet_write(1, "ACK %s\n", sha1_to_hex(the_index.cache_tree->sha1));
+
+	/* Traverse and send only missing trees */
+	memset(&pack, 0, sizeof(pack));
+	pack.fd = 1;
+	create_pack(&pack);
+
+	t[0] = lookup_tree(the_index.cache_tree->sha1);
+	set_tree_marks(t[1], PARENT1);
+	set_tree_marks(t[2], PARENT2);
+	write_object(t[0], NULL, &pack);
+
+	close_pack(&pack);
+	return 0;
+}
diff --git a/git.c b/git.c
index f37028b..edb379f 100644
--- a/git.c
+++ b/git.c
@@ -392,6 +392,7 @@ static void handle_internal_command(int argc, const char **argv)
 		{ "update-ref", cmd_update_ref, RUN_SETUP },
 		{ "update-server-info", cmd_update_server_info, RUN_SETUP },
 		{ "upload-archive", cmd_upload_archive },
+		{ "upload-narrow-base", cmd_upload_narrow_base },
 		{ "var", cmd_var },
 		{ "verify-tag", cmd_verify_tag, RUN_SETUP },
 		{ "version", cmd_version },
-- 
1.7.1.rc1.69.g24c2f7

  parent reply	other threads:[~2010-08-24 22:24 UTC|newest]

Thread overview: 66+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-08-24 22:19 [RFD PATCH 00/32] subtree clone v2 Nguyễn Thái Ngọc Duy
2010-08-24 22:19 ` [PATCH 01/32] add const to ce_write() Nguyễn Thái Ngọc Duy
2010-08-24 22:19 ` [PATCH 02/32] cache-tree: abstract out write_sha1_file from cache_tree_update() Nguyễn Thái Ngọc Duy
2010-08-24 22:41   ` Jonathan Nieder
2010-08-24 22:19 ` [PATCH 03/32] cache-tree: ignore CE_REMOVE entries in verify_cache() Nguyễn Thái Ngọc Duy
2010-08-24 23:15   ` Jonathan Nieder
2010-08-25  0:23     ` Nguyen Thai Ngoc Duy
2010-08-25  0:48       ` Jonathan Nieder
2010-08-24 22:19 ` [PATCH 04/32] move do_compress() from pack-objects.c to pack-write.c Nguyễn Thái Ngọc Duy
2010-08-24 23:25   ` Jonathan Nieder
2010-08-25  3:19     ` Nguyen Thai Ngoc Duy
2010-08-24 22:19 ` [PATCH 05/32] pack-write: add functions for creating simple packs Nguyễn Thái Ngọc Duy
2010-08-24 22:19 ` [PATCH 06/32] tree.c: Add {set,clear}_tree_marks Nguyễn Thái Ngọc Duy
2010-08-24 22:19 ` [PATCH 07/32] tree.c: find_subtree() to search for a tree Nguyễn Thái Ngọc Duy
2010-08-25  3:35   ` Elijah Newren
2010-08-25  3:43     ` Nguyen Thai Ngoc Duy
2010-08-25  5:35       ` Elijah Newren
2010-08-24 22:19 ` [PATCH 08/32] Add $GIT_DIR/narrow check Nguyễn Thái Ngọc Duy
2010-08-24 22:19 ` [PATCH 09/32] index: make narrow index incompatible with older git Nguyễn Thái Ngọc Duy
2010-08-24 23:43   ` Jonathan Nieder
2010-08-25  0:25     ` Nguyen Thai Ngoc Duy
2010-08-24 22:20 ` [PATCH 10/32] rev-list: support traversing in narrow repository mode Nguyễn Thái Ngọc Duy
2010-08-25  4:11   ` Elijah Newren
2010-08-24 22:20 ` [PATCH 11/32] rev-list: support --narrow-tree Nguyễn Thái Ngọc Duy
2010-08-25  3:59   ` Elijah Newren
2010-08-25 22:11     ` Nguyen Thai Ngoc Duy
2010-08-24 22:20 ` [PATCH 12/32] pack-objects: " Nguyễn Thái Ngọc Duy
2010-08-24 22:20 ` [PATCH 13/32] upload-pack: support narrow-tree capability Nguyễn Thái Ngọc Duy
2010-08-24 22:20 ` [PATCH 14/32] fetch-pack: support --narrow-tree Nguyễn Thái Ngọc Duy
2010-08-24 22:20 ` [PATCH 15/32] unpack_trees: only unpack $GIT_DIR/narrow subtree in narrow repository Nguyễn Thái Ngọc Duy
2010-08-25  5:04   ` Elijah Newren
2010-08-25  5:38     ` Nguyen Thai Ngoc Duy
2010-08-24 22:20 ` [PATCH 16/32] cache-tree: only cache tree within narrow area Nguyễn Thái Ngọc Duy
2010-08-24 22:20 ` [PATCH 17/32] tree-diff: add narrow versions of diff_{root_,}tree_sha1 Nguyễn Thái Ngọc Duy
2010-08-24 22:20 ` [PATCH 18/32] log-tree: use narrow version of diff_tree_sha1 Nguyễn Thái Ngọc Duy
2010-08-24 22:20 ` [PATCH 19/32] clone: support --narrow option Nguyễn Thái Ngọc Duy
2010-08-24 22:20 ` [PATCH 20/32] narrow-tree: add join_narrow_tree to do tree fixup for commits Nguyễn Thái Ngọc Duy
2010-08-24 22:20 ` [PATCH 21/32] commit: add narrow's commit_tree version Nguyễn Thái Ngọc Duy
2010-08-24 22:20 ` [PATCH 22/32] commit: use commit_narrow_tree() to support narrow repo Nguyễn Thái Ngọc Duy
2010-08-24 22:20 ` [PATCH 23/32] commit-tree: require --narrow-base in " Nguyễn Thái Ngọc Duy
2010-08-24 22:20 ` [PATCH 24/32] merge: refuse to merge if narrow bases are different Nguyễn Thái Ngọc Duy
2010-08-24 22:20 ` [PATCH 25/32] merge: prepare commit properly in narrow mode Nguyễn Thái Ngọc Duy
2010-08-24 22:20 ` Nguyễn Thái Ngọc Duy [this message]
2010-08-24 22:20 ` [PATCH 27/32] rev-list: traverse some more trees to make upload-narrow-base happy Nguyễn Thái Ngọc Duy
2010-08-24 22:20 ` [PATCH 28/32] narrow-tree: add oldest_narrow_base() Nguyễn Thái Ngọc Duy
2010-08-24 22:20 ` [PATCH 29/32] Add command fetch-narrow-base Nguyễn Thái Ngọc Duy
2010-08-24 22:20 ` [PATCH 30/32] merge: support merging when narrow bases are different Nguyễn Thái Ngọc Duy
2010-08-24 22:20 ` [PATCH 31/32] send-pack: do not use thin pack in narrow mode Nguyễn Thái Ngọc Duy
2010-08-24 22:20 ` [PATCH 32/32] daemon: support upload-narrow-base Nguyễn Thái Ngọc Duy
2010-08-24 22:37 ` [RFD PATCH 00/32] subtree clone v2 Jonathan Nieder
2010-08-24 22:47   ` Nguyen Thai Ngoc Duy
2010-08-24 23:09     ` Jonathan Nieder
2010-08-25  0:20       ` Nguyen Thai Ngoc Duy
2010-08-25  4:37     ` Elijah Newren
2010-08-25  5:21       ` Nguyen Thai Ngoc Duy
2010-08-25  5:31         ` Elijah Newren
2010-08-25  6:21           ` Nguyen Thai Ngoc Duy
2010-08-25 13:06             ` Elijah Newren
2010-08-25 22:13           ` Nguyen Thai Ngoc Duy
2010-08-26  2:50             ` Elijah Newren
2010-08-26  3:52               ` Nguyen Thai Ngoc Duy
2010-08-26  4:39                 ` Elijah Newren
2010-08-26  4:45                   ` Nguyen Thai Ngoc Duy
2010-08-25  5:21       ` Elijah Newren
2010-08-25 19:27       ` Junio C Hamano
2010-08-25 20:43         ` Elijah Newren

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=1282688422-7738-27-git-send-email-pclouds@gmail.com \
    --to=pclouds@gmail.com \
    --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).