$GIT_DIR/subtree contains commit mapping in subtree mode. It's quite large that putting it in $GIT_DIR/refs/replace may slow git down significantly. Even with this, there will be a split second delay for every git command. Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@xxxxxxxxx> --- Makefile | 2 + replace_object.c | 5 ++ subtree.c | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ subtree.h | 2 + 4 files changed, 126 insertions(+), 0 deletions(-) create mode 100644 subtree.c create mode 100644 subtree.h diff --git a/Makefile b/Makefile index f33648d..0d13538 100644 --- a/Makefile +++ b/Makefile @@ -525,6 +525,7 @@ LIB_H += sigchain.h LIB_H += strbuf.h LIB_H += string-list.h LIB_H += submodule.h +LIB_H += subtree.h LIB_H += tag.h LIB_H += transport.h LIB_H += tree.h @@ -629,6 +630,7 @@ LIB_OBJS += sigchain.o LIB_OBJS += strbuf.o LIB_OBJS += string-list.o LIB_OBJS += submodule.o +LIB_OBJS += subtree.o LIB_OBJS += symlinks.o LIB_OBJS += tag.o LIB_OBJS += trace.o diff --git a/replace_object.c b/replace_object.c index eb59604..5fe4099 100644 --- a/replace_object.c +++ b/replace_object.c @@ -1,6 +1,7 @@ #include "cache.h" #include "sha1-lookup.h" #include "refs.h" +#include "subtree.h" static struct replace_object { unsigned char sha1[2][20]; @@ -82,6 +83,7 @@ static void prepare_replace_object(void) if (replace_object_prepared) return; + prepare_subtree_commit(); for_each_replace_ref(register_replace_ref, NULL); replace_object_prepared = 1; } @@ -99,6 +101,9 @@ const unsigned char *lookup_replace_object(const unsigned char *sha1) prepare_replace_object(); + if (core_subtree) + cur = subtree_lookup_object(cur); + /* Try to recursively replace the object */ do { if (--depth < 0) diff --git a/subtree.c b/subtree.c new file mode 100644 index 0000000..601d827 --- /dev/null +++ b/subtree.c @@ -0,0 +1,117 @@ +#include "cache.h" +#include "commit.h" +#include "tree.h" +#include "diff.h" +#include "revision.h" +#include "refs.h" +#include "tag.h" +#include "progress.h" +#include "pack.h" +#include "sha1-lookup.h" +#include "csum-file.h" + +static struct replace_object { + unsigned char sha1[2][20]; +} **subtree_commit; + +static struct replace_object **subtree_commit, **subtree_commit_r; +static int subtree_commit_nr, subtree_commit_r_nr, subtree_commit_alloc; + +static const unsigned char *replace_sha1_access(size_t index, void *table) +{ + struct replace_object **replace = table; + return replace[index]->sha1[0]; +} + +static int subtree_replace_object_pos(struct replace_object **store, int nr, + const unsigned char *sha1) +{ + return sha1_pos(sha1, store, nr, replace_sha1_access); +} + +static int subtree_register_object(struct replace_object **store, + int *nr, + const unsigned char *sha1, + struct replace_object *replace, + int ignore_dups) +{ + int pos = subtree_replace_object_pos(store, *nr, sha1); + + if (0 <= pos) { + if (ignore_dups) + free(replace); + else { + free(store[pos]); + store[pos] = replace; + } + return 1; + } + pos = -pos - 1; + (*nr)++; + if (pos < *nr) + memmove(store + pos + 1, + store + pos, + (*nr - pos - 1) * + sizeof(*store)); + store[pos] = replace; + return 0; +} + +void prepare_subtree_commit() +{ + int fd; + struct stat stat; + struct replace_object *ro; + int ro_size, ro_table_size; + char *subtree, *entry; + + if (!core_subtree) + return; + + fd = open(git_path("subtree"), O_RDONLY); + if (fd == -1) + return; + + if (fstat(fd, &stat)) + die("Could not stat .git/subtree"); + + if (stat.st_size % 82) + die("Invalid .git/subtree size"); + + subtree_commit_alloc = stat.st_size / 82; + ro_size = sizeof(struct replace_object) * subtree_commit_alloc; + ro_table_size = sizeof(struct replace_object*) * subtree_commit_alloc; + subtree_commit_nr = 0; + subtree_commit_r_nr = 0; + + subtree_commit = xmalloc(ro_size + ro_table_size*2); + subtree_commit_r = (struct replace_object **)(((char*)subtree_commit) + ro_table_size); + ro = (struct replace_object *)(((char*)subtree_commit) + 2*ro_table_size); + + entry = subtree = xmmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + + for (entry = subtree; subtree_commit_nr < subtree_commit_alloc; entry += 82, ro++) { + if (entry[40] != ' ' || entry[81] != '\n') + die("Broken .git/subtree"); + + get_sha1_hex(entry, ro->sha1[0]); + get_sha1_hex(entry+41, ro->sha1[1]); + if (subtree_register_object(subtree_commit, &subtree_commit_nr, + ro->sha1[0], ro, 1) || + subtree_register_object(subtree_commit_r, &subtree_commit_r_nr, + ro->sha1[1], ro, 1)) + die("duplicate replace ref: %s", sha1_to_hex(ro->sha1[0])); + } + munmap(subtree, stat.st_size); + close(fd); +} + +const unsigned char *subtree_lookup_object(const unsigned char *sha1) +{ + int pos = subtree_replace_object_pos(subtree_commit, + subtree_commit_nr, + sha1); + if (0 <= pos) + return subtree_commit[pos]->sha1[1]; + return sha1; +} diff --git a/subtree.h b/subtree.h new file mode 100644 index 0000000..157153a --- /dev/null +++ b/subtree.h @@ -0,0 +1,2 @@ +void prepare_subtree_commit(); +const unsigned char *subtree_lookup_object(const unsigned char *sha1); -- 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