Which is exactly the opposite of rewriting incoming commits. Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@xxxxxxxxx> --- 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 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html