git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH 12/16] subtree: rewriting outgoing commits

From
Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Date
Jul 31, 2010, 16:18 UTC
Message-ID
<1280593105-22015-13-git-send-email-pclouds@gmail.com>
In-Reply-To
<1280593105-22015-1-git-send-email-pclouds@gmail.com>
Which is exactly the opposite of rewriting incoming commits.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 subtree.c |  173 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 subtree.h |    1 +
 2 files changed, 174 insertions(+), 0 deletions(-)
diff --git a/subtree.c b/subtree.c
index 8c075be..739ff5f 100644
--- a/subtree.c
+++ b/subtree.c
@@ -359,3 +359,176 @@ void subtree_import()
 	if (revs.pending.nr)
 		free(revs.pending.objects);
 }
+
+/*
+ * The opposite of narrow_tree(). Put the subtree back to the original tree.
+ */
+static int widen_tree(const unsigned char *sha1,
+		      unsigned char *newsha1,
+		      const unsigned char *subtree_sha1,
+		      const char *prefix)
+{
+	struct tree_desc desc;
+	struct name_entry entry;
+	struct strbuf buffer;
+	const char *slash;
+	int subtree_len;
+	enum object_type type;
+	unsigned long size;
+	char *tree;
+
+	slash = strchr(prefix, '/');
+	subtree_len = slash ? slash - prefix : strlen(prefix);
+
+	tree = read_sha1_file(sha1, &type, &size);
+	if (type != OBJ_TREE)
+		die("%s is not a tree", sha1_to_hex(sha1));
+
+	init_tree_desc(&desc, tree, size);
+	strbuf_init(&buffer, 8192);
+	while (tree_entry(&desc, &entry)) {
+		strbuf_addf(&buffer, "%o %.*s%c", entry.mode, strlen(entry.path), entry.path, '\0');
+
+		if (S_ISDIR(entry.mode) &&
+		    subtree_len == strlen(entry.path) &&
+		    !strncmp(entry.path, prefix, subtree_len)) {
+			unsigned char newtree_sha1[20];
+
+			if (slash && slash[1]) /* trailing slash does not count */
+				widen_tree(entry.sha1, newtree_sha1, subtree_sha1,
+					   prefix+subtree_len+1);
+			else
+				/* replace the tree */
+				memcpy(newtree_sha1, subtree_sha1, 20);
+
+			strbuf_add(&buffer, newtree_sha1, 20);
+		}
+		else
+			strbuf_add(&buffer, entry.sha1, 20);
+	}
+	free(tree);
+
+	if (write_sha1_file(buffer.buf, buffer.len, tree_type, newsha1)) {
+		error("Could not write replaced tree for %s", sha1_to_hex(sha1));
+		strbuf_release(&buffer);
+		return 1;
+	}
+	strbuf_release(&buffer);
+	return 0;
+}
+
+static int find_subtree(const unsigned char *sha1, unsigned char *newsha1, const char *prefix)
+{
+	struct tree_desc desc;
+	struct name_entry entry;
+	const char *slash;
+	enum object_type type;
+	unsigned long size;
+	int subtree_len;
+	char *tree;
+
+	slash = strchr(prefix, '/');
+	subtree_len = slash ? slash - prefix : strlen(prefix);
+
+	tree = read_sha1_file(sha1, &type, &size);
+	if (type != OBJ_TREE)
+		die("%s is not a tree", sha1_to_hex(sha1));
+
+	init_tree_desc(&desc, tree, size);
+	while (tree_entry(&desc, &entry)) {
+		if (!S_ISDIR(entry.mode))
+			continue;
+
+		if (subtree_len == strlen(entry.path) &&
+		    !strncmp(entry.path, prefix, subtree_len)) {
+
+			if (slash && slash[1]) { /* trailing slash does not count */
+				if (find_subtree(entry.sha1, newsha1, prefix+subtree_len+1))
+					return 1;
+			}
+			else
+				memcpy(newsha1, entry.sha1, 20);
+			free(tree);
+			return 0;
+		}
+	}
+	free(tree);
+
+	return 1;
+}
+
+/* The opposite of shadow_commit() */
+static int expose_commit(const unsigned char *sha1, unsigned char *newsha1,
+			 const unsigned char *basesha1,
+			 const char *prefix, FILE *fp)
+{
+	unsigned char treesha1[20], subtree_sha1[20];
+	enum object_type type;
+	unsigned long size, base_size;
+	void *base_buffer, *buffer;
+	int saved_read_replace_refs = read_replace_refs;
+
+	/* Get subtree from the new commit, sha1 */
+	read_replace_refs = 0;
+	buffer = read_sha1_file(sha1, &type, &size);
+	read_replace_refs = saved_read_replace_refs;
+	get_sha1_hex(buffer+5, treesha1);
+
+	if (!buffer || type != OBJ_COMMIT ||
+	    find_subtree(treesha1, subtree_sha1, prefix)) {
+		free(buffer);
+		error("Failed to find subtree tree in base commit %s", sha1_to_hex(sha1));
+		return 1;
+	}
+
+	/* Get the old base tree from basesha1 */
+	read_replace_refs = 0;
+	base_buffer = read_sha1_file(basesha1, &type, &base_size);
+	read_replace_refs = saved_read_replace_refs;
+	get_sha1_hex(base_buffer+5, treesha1);
+
+	if (!buffer || type != OBJ_COMMIT ||
+	    widen_tree(treesha1, newsha1, subtree_sha1, prefix)) {
+		free(buffer);
+		error("Failed to widen tree for commit %s", sha1_to_hex(sha1));
+		return 1;
+	}
+	free(base_buffer);
+
+	/* replace new tree in */
+	memcpy((char*)buffer+5, sha1_to_hex(newsha1), 40);
+
+	if (write_sha1_file(buffer, size, commit_type, newsha1)) {
+		free(buffer);
+		error("Could not write replaced commit for %s", sha1_to_hex(sha1));
+		return 1;
+	}
+
+	if (fp) {
+		char buf[82];
+		memcpy(buf, sha1_to_hex(newsha1), 40);
+		buf[40] = ' ';
+		memcpy(buf+41, sha1_to_hex(sha1), 40);
+		buf[81] = '\n';
+		fwrite(buf, 82, 1, fp);
+	}
+	free(buffer);
+
+	return 0;
+}
+
+int subtree_export(unsigned char *sha1, unsigned char *basesha1, unsigned char *newsha1)
+{
+	FILE *fp;
+
+	fp = fopen(git_path("subtree"), "a+");
+	if (expose_commit(sha1, newsha1, basesha1, core_subtree, fp))
+		die("Failed to rewrite commit %s", sha1_to_hex(sha1));
+	fclose(fp);
+
+	if (subtree_commit)
+		free(subtree_commit);
+	prepare_subtree_commit();
+
+	return 0;
+}
diff --git a/subtree.h b/subtree.h
index 3512e2a..081838f 100644
--- a/subtree.h
+++ b/subtree.h
@@ -1,3 +1,4 @@
 void prepare_subtree_commit();
 const unsigned char *subtree_lookup_object(const unsigned char *sha1);
 void subtree_import();
+int subtree_export(unsigned char *sha1, unsigned char *basesha1, unsigned char *newsha1);
-- 
1.7.1.rc1.69.g24c2f7
Previous: Nguyễn Thái Ngọc DuyNext: Elijah Newren
Message 22 of 33 in “Subtree clone proof of concept”
  1. 00/16 Subtree clone proof of conceptNguyễn Thái Ngọc Duy, Jul 31, 2010
  2. 01/16 Add core.subtreeNguyễn Thái Ngọc Duy, Jul 31, 2010
  3. 02/16 list-objects: limit traversing within the given subtree if core.subtree is setNguyễn Thái Ngọc Duy, Jul 31, 2010
  4. Ævar Arnfjörð BjarmasonAug 1, 2010
  5. Nguyen Thai Ngoc DuyAug 1, 2010
  6. Elijah NewrenAug 2, 2010
  7. Nguyen Thai Ngoc DuyAug 2, 2010
  8. 03/16 parse_object: keep sha1 even when parsing replaced oneNguyễn Thái Ngọc Duy, Jul 31, 2010
  9. 04/16 Allow to invalidate a commit in in-memory object storeNguyễn Thái Ngọc Duy, Jul 31, 2010
  10. 05/16 Hook up replace-object to allow bulk commit replacementNguyễn Thái Ngọc Duy, Jul 31, 2010
  11. Junio C HamanoAug 2, 2010
  12. Nguyen Thai Ngoc DuyAug 2, 2010
  13. 06/16 upload-pack: use a separate variable to control whether internal rev-list is usedNguyễn Thái Ngọc Duy, Jul 31, 2010
  14. Elijah NewrenAug 2, 2010
  15. 07/16 upload-pack: support subtree packNguyễn Thái Ngọc Duy, Jul 31, 2010
  16. Elijah NewrenAug 2, 2010
  17. 08/16 fetch-pack: support --subtreeNguyễn Thái Ngọc Duy, Jul 31, 2010
  18. 09/16 subtree: rewrite incoming commitsNguyễn Thái Ngọc Duy, Jul 31, 2010
  19. Elijah NewrenAug 2, 2010
  20. 10/16 clone: support subtree clone with parameter --subtreeNguyễn Thái Ngọc Duy, Jul 31, 2010
  21. 11/16 pack-objects: add --subtree (for pushing)Nguyễn Thái Ngọc Duy, Jul 31, 2010
  22. 12/16 subtree: rewriting outgoing commitsNguyễn Thái Ngọc Duy, Jul 31, 2010
  23. Elijah NewrenAug 2, 2010
  24. 13/16 Update commit_tree() interface to take base tree tooNguyễn Thái Ngọc Duy, Jul 31, 2010
  25. 14/16 commit_tree(): rewriting/replacing new commitsNguyễn Thái Ngọc Duy, Jul 31, 2010
  26. 15/16 commit: rewrite outgoing commitsNguyễn Thái Ngọc Duy, Jul 31, 2010
  27. 16/16 do not use thin packs and subtree together (just a bad feeling about this)Nguyễn Thái Ngọc Duy, Jul 31, 2010
  28. Sverre RabbelierAug 1, 2010
  29. Nguyen Thai Ngoc DuyAug 1, 2010
  30. Sverre RabbelierAug 1, 2010
  31. Elijah NewrenAug 2, 2010
  32. Nguyen Thai Ngoc DuyAug 2, 2010
  33. Nguyen Thai Ngoc DuyAug 2, 2010

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.