{"thread":{"id":"2176","subject":"Split up tree diff functions into tree-diff.c library","startedAt":"2005-10-21T04:05:05Z","lastAt":"2005-10-21T15:19:30Z","messageCount":3,"participants":["Linus Torvalds","Junio C Hamano"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"10406","messageId":"Pine.LNX.4.64.0510202058030.10477@g5.osdl.org","threadId":"2176","inReplyTo":null,"subject":"Split up tree diff functions into tree-diff.c library","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-10-21T04:05:05Z","receivedAt":"2005-10-21T04:05:05Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\nThis makes the tree diff functionality independent of the \"git-diff-tree\" \nprogram, by splitting the core functionality up into a library file.\n\nThis will be needed for when we teach git-rev-list to only follow a \nspecified set of pathnames, rather than the global revision history.\n\nMost of it is a fairly straightforward code move, but it also involves \nsome calling convention cleanup, and moving some of the static variables \nfrom diff-tree.c into the options structure.\n\nThe actual tree change callback routines also become paramterized by the \ndiff_options structure, allowing the library functionality to do something \nelse than just show the diff on stdout.\n\nRight now the only user of this functionality remains git-diff-tree \nitself.\n\nSigned-off-by: Linus Torvalds <torvalds@osdl.org>\n---\n\nNOTE! I think I did everything right, and things still seem to work, but \nthis is worth having a few people look at more closely. git-diff-tree is \nsome pretty damn core functionality.\n\nThe next email will contain a first cut at changing git-rev-list to take \nadvantage of the new library interface.\n\n\ndiff --git a/Makefile b/Makefile\nindex 5ee72bc..95f8c57 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -151,7 +151,7 @@ LIB_H = \\\n \n DIFF_OBJS = \\\n \tdiff.o diffcore-break.o diffcore-order.o diffcore-pathspec.o \\\n-\tdiffcore-pickaxe.o diffcore-rename.o\n+\tdiffcore-pickaxe.o diffcore-rename.o tree-diff.o\n \n LIB_OBJS = \\\n \tblob.o commit.o connect.o count-delta.o csum-file.o \\\ndiff --git a/diff-tree.c b/diff-tree.c\nindex 8517220..382011a 100644\n--- a/diff-tree.c\n+++ b/diff-tree.c\n@@ -5,256 +5,13 @@\n static int show_root_diff = 0;\n static int verbose_header = 0;\n static int ignore_merges = 1;\n-static int recursive = 0;\n-static int show_tree_entry_in_recursive = 0;\n static int read_stdin = 0;\n \n-static struct diff_options diff_options;\n-\n static const char *header = NULL;\n static const char *header_prefix = \"\";\n static enum cmit_fmt commit_format = CMIT_FMT_RAW;\n \n-// What paths are we interested in?\n-static int nr_paths = 0;\n-static const char **paths = NULL;\n-static int *pathlens = NULL;\n-\n-static int diff_tree_sha1(const unsigned char *old, const unsigned char *new, const char *base);\n-\n-static void update_tree_entry(void **bufp, unsigned long *sizep)\n-{\n-\tvoid *buf = *bufp;\n-\tunsigned long size = *sizep;\n-\tint len = strlen(buf) + 1 + 20;\n-\n-\tif (size < len)\n-\t\tdie(\"corrupt tree file\");\n-\t*bufp = buf + len;\n-\t*sizep = size - len;\n-}\n-\n-static const unsigned char *extract(void *tree, unsigned long size, const char **pathp, unsigned int *modep)\n-{\n-\tint len = strlen(tree)+1;\n-\tconst unsigned char *sha1 = tree + len;\n-\tconst char *path = strchr(tree, ' ');\n-\tunsigned int mode;\n-\n-\tif (!path || size < len + 20 || sscanf(tree, \"%o\", &mode) != 1)\n-\t\tdie(\"corrupt tree file\");\n-\t*pathp = path+1;\n-\t*modep = DIFF_FILE_CANON_MODE(mode);\n-\treturn sha1;\n-}\n-\n-static char *malloc_base(const char *base, const char *path, int pathlen)\n-{\n-\tint baselen = strlen(base);\n-\tchar *newbase = xmalloc(baselen + pathlen + 2);\n-\tmemcpy(newbase, base, baselen);\n-\tmemcpy(newbase + baselen, path, pathlen);\n-\tmemcpy(newbase + baselen + pathlen, \"/\", 2);\n-\treturn newbase;\n-}\n-\n-static void show_file(const char *prefix, void *tree, unsigned long size, const char *base);\n-static void show_tree(const char *prefix, void *tree, unsigned long size, const char *base);\n-\n-/* A file entry went away or appeared */\n-static void show_file(const char *prefix, void *tree, unsigned long size, const char *base)\n-{\n-\tunsigned mode;\n-\tconst char *path;\n-\tconst unsigned char *sha1 = extract(tree, size, &path, &mode);\n-\n-\tif (recursive && S_ISDIR(mode)) {\n-\t\tchar type[20];\n-\t\tunsigned long size;\n-\t\tchar *newbase = malloc_base(base, path, strlen(path));\n-\t\tvoid *tree;\n-\n-\t\ttree = read_sha1_file(sha1, type, &size);\n-\t\tif (!tree || strcmp(type, \"tree\"))\n-\t\t\tdie(\"corrupt tree sha %s\", sha1_to_hex(sha1));\n-\n-\t\tshow_tree(prefix, tree, size, newbase);\n-\n-\t\tfree(tree);\n-\t\tfree(newbase);\n-\t\treturn;\n-\t}\n-\n-\tdiff_addremove(&diff_options, prefix[0], mode, sha1, base, path);\n-}\n-\n-static int compare_tree_entry(void *tree1, unsigned long size1, void *tree2, unsigned long size2, const char *base)\n-{\n-\tunsigned mode1, mode2;\n-\tconst char *path1, *path2;\n-\tconst unsigned char *sha1, *sha2;\n-\tint cmp, pathlen1, pathlen2;\n-\n-\tsha1 = extract(tree1, size1, &path1, &mode1);\n-\tsha2 = extract(tree2, size2, &path2, &mode2);\n-\n-\tpathlen1 = strlen(path1);\n-\tpathlen2 = strlen(path2);\n-\tcmp = base_name_compare(path1, pathlen1, mode1, path2, pathlen2, mode2);\n-\tif (cmp < 0) {\n-\t\tshow_file(\"-\", tree1, size1, base);\n-\t\treturn -1;\n-\t}\n-\tif (cmp > 0) {\n-\t\tshow_file(\"+\", tree2, size2, base);\n-\t\treturn 1;\n-\t}\n-\tif (!diff_options.find_copies_harder &&\n-\t    !memcmp(sha1, sha2, 20) && mode1 == mode2)\n-\t\treturn 0;\n-\n-\t/*\n-\t * If the filemode has changed to/from a directory from/to a regular\n-\t * file, we need to consider it a remove and an add.\n-\t */\n-\tif (S_ISDIR(mode1) != S_ISDIR(mode2)) {\n-\t\tshow_file(\"-\", tree1, size1, base);\n-\t\tshow_file(\"+\", tree2, size2, base);\n-\t\treturn 0;\n-\t}\n-\n-\tif (recursive && S_ISDIR(mode1)) {\n-\t\tint retval;\n-\t\tchar *newbase = malloc_base(base, path1, pathlen1);\n-\t\tif (show_tree_entry_in_recursive)\n-\t\t\tdiff_change(&diff_options, mode1, mode2,\n-\t\t\t\t    sha1, sha2, base, path1);\n-\t\tretval = diff_tree_sha1(sha1, sha2, newbase);\n-\t\tfree(newbase);\n-\t\treturn retval;\n-\t}\n-\n-\tdiff_change(&diff_options, mode1, mode2, sha1, sha2, base, path1);\n-\treturn 0;\n-}\n-\n-static int interesting(void *tree, unsigned long size, const char *base)\n-{\n-\tconst char *path;\n-\tunsigned mode;\n-\tint i;\n-\tint baselen, pathlen;\n-\n-\tif (!nr_paths)\n-\t\treturn 1;\n-\n-\t(void)extract(tree, size, &path, &mode);\n-\n-\tpathlen = strlen(path);\n-\tbaselen = strlen(base);\n-\n-\tfor (i=0; i < nr_paths; i++) {\n-\t\tconst char *match = paths[i];\n-\t\tint matchlen = pathlens[i];\n-\n-\t\tif (baselen >= matchlen) {\n-\t\t\t/* If it doesn't match, move along... */\n-\t\t\tif (strncmp(base, match, matchlen))\n-\t\t\t\tcontinue;\n-\n-\t\t\t/* The base is a subdirectory of a path which was specified. */\n-\t\t\treturn 1;\n-\t\t}\n-\n-\t\t/* Does the base match? */\n-\t\tif (strncmp(base, match, baselen))\n-\t\t\tcontinue;\n-\n-\t\tmatch += baselen;\n-\t\tmatchlen -= baselen;\n-\n-\t\tif (pathlen > matchlen)\n-\t\t\tcontinue;\n-\n-\t\tif (matchlen > pathlen) {\n-\t\t\tif (match[pathlen] != '/')\n-\t\t\t\tcontinue;\n-\t\t\tif (!S_ISDIR(mode))\n-\t\t\t\tcontinue;\n-\t\t}\n-\n-\t\tif (strncmp(path, match, pathlen))\n-\t\t\tcontinue;\n-\n-\t\treturn 1;\n-\t}\n-\treturn 0; /* No matches */\n-}\n-\n-/* A whole sub-tree went away or appeared */\n-static void show_tree(const char *prefix, void *tree, unsigned long size, const char *base)\n-{\n-\twhile (size) {\n-\t\tif (interesting(tree, size, base))\n-\t\t\tshow_file(prefix, tree, size, base);\n-\t\tupdate_tree_entry(&tree, &size);\n-\t}\n-}\n-\n-static int diff_tree(void *tree1, unsigned long size1, void *tree2, unsigned long size2, const char *base)\n-{\n-\twhile (size1 | size2) {\n-\t\tif (nr_paths && size1 && !interesting(tree1, size1, base)) {\n-\t\t\tupdate_tree_entry(&tree1, &size1);\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (nr_paths && size2 && !interesting(tree2, size2, base)) {\n-\t\t\tupdate_tree_entry(&tree2, &size2);\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!size1) {\n-\t\t\tshow_file(\"+\", tree2, size2, base);\n-\t\t\tupdate_tree_entry(&tree2, &size2);\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!size2) {\n-\t\t\tshow_file(\"-\", tree1, size1, base);\n-\t\t\tupdate_tree_entry(&tree1, &size1);\n-\t\t\tcontinue;\n-\t\t}\n-\t\tswitch (compare_tree_entry(tree1, size1, tree2, size2, base)) {\n-\t\tcase -1:\n-\t\t\tupdate_tree_entry(&tree1, &size1);\n-\t\t\tcontinue;\n-\t\tcase 0:\n-\t\t\tupdate_tree_entry(&tree1, &size1);\n-\t\t\t/* Fallthrough */\n-\t\tcase 1:\n-\t\t\tupdate_tree_entry(&tree2, &size2);\n-\t\t\tcontinue;\n-\t\t}\n-\t\tdie(\"git-diff-tree: internal error\");\n-\t}\n-\treturn 0;\n-}\n-\n-static int diff_tree_sha1(const unsigned char *old, const unsigned char *new, const char *base)\n-{\n-\tvoid *tree1, *tree2;\n-\tunsigned long size1, size2;\n-\tint retval;\n-\n-\ttree1 = read_object_with_reference(old, \"tree\", &size1, NULL);\n-\tif (!tree1)\n-\t\tdie(\"unable to read source tree (%s)\", sha1_to_hex(old));\n-\ttree2 = read_object_with_reference(new, \"tree\", &size2, NULL);\n-\tif (!tree2)\n-\t\tdie(\"unable to read destination tree (%s)\", sha1_to_hex(new));\n-\tretval = diff_tree(tree1, size1, tree2, size2, base);\n-\tfree(tree1);\n-\tfree(tree2);\n-\treturn retval;\n-}\n+static struct diff_options diff_options;\n \n static void call_diff_setup_done(void)\n {\n@@ -285,7 +42,7 @@ static int diff_tree_sha1_top(const unsi\n \tint ret;\n \n \tcall_diff_setup_done();\n-\tret = diff_tree_sha1(old, new, base);\n+\tret = diff_tree_sha1(old, new, base, &diff_options);\n \tcall_diff_flush();\n \treturn ret;\n }\n@@ -294,13 +51,17 @@ static int diff_root_tree(const unsigned\n {\n \tint retval;\n \tvoid *tree;\n-\tunsigned long size;\n+\tstruct tree_desc empty, real;\n \n \tcall_diff_setup_done();\n-\ttree = read_object_with_reference(new, \"tree\", &size, NULL);\n+\ttree = read_object_with_reference(new, \"tree\", &real.size, NULL);\n \tif (!tree)\n \t\tdie(\"unable to read root tree (%s)\", sha1_to_hex(new));\n-\tretval = diff_tree(\"\", 0, tree, size, base);\n+\treal.buf = tree;\n+\n+\tempty.buf = \"\";\n+\tempty.size = 0;\n+\tretval = diff_tree(&empty, &real, base, &diff_options);\n \tfree(tree);\n \tcall_diff_flush();\n \treturn retval;\n@@ -387,14 +148,6 @@ static int diff_tree_stdin(char *line)\n \treturn diff_tree_commit(commit, line);\n }\n \n-static int count_paths(const char **paths)\n-{\n-\tint i = 0;\n-\twhile (*paths++)\n-\t\ti++;\n-\treturn i;\n-}\n-\n static const char diff_tree_usage[] =\n \"git-diff-tree [--stdin] [-m] [-s] [-v] [--pretty] [-t] \"\n \"[<common diff options>] <tree-ish> <tree-ish>\"\n@@ -445,11 +198,12 @@ int main(int argc, const char **argv)\n \t\t\tbreak;\n \t\t}\n \t\tif (!strcmp(arg, \"-r\")) {\n-\t\t\trecursive = 1;\n+\t\t\tdiff_options.recursive = 1;\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strcmp(arg, \"-t\")) {\n-\t\t\trecursive = show_tree_entry_in_recursive = 1;\n+\t\t\tdiff_options.recursive = 1;\n+\t\t\tdiff_options.tree_in_recursive = 1;\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strcmp(arg, \"-m\")) {\n@@ -478,17 +232,9 @@ int main(int argc, const char **argv)\n \t\tusage(diff_tree_usage);\n \t}\n \tif (diff_options.output_format == DIFF_FORMAT_PATCH)\n-\t\trecursive = 1;\n+\t\tdiff_options.recursive = 1;\n \n-\tpaths = get_pathspec(prefix, argv);\n-\tif (paths) {\n-\t\tint i;\n-\n-\t\tnr_paths = count_paths(paths);\n-\t\tpathlens = xmalloc(nr_paths * sizeof(int));\n-\t\tfor (i=0; i<nr_paths; i++)\n-\t\t\tpathlens[i] = strlen(paths[i]);\n-\t}\n+\tdiff_tree_setup_paths(get_pathspec(prefix, argv));\n \n \tswitch (nr_sha1) {\n \tcase 0:\ndiff --git a/diff.c b/diff.c\nindex 306bcd9..ec94a96 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -754,6 +754,9 @@ void diff_setup(struct diff_options *opt\n \toptions->line_termination = '\\n';\n \toptions->break_opt = -1;\n \toptions->rename_limit = -1;\n+\n+\toptions->change = diff_change;\n+\toptions->add_remove = diff_addremove;\n }\n \n int diff_setup_done(struct diff_options *options)\ndiff --git a/diff.h b/diff.h\nindex 2f4a7b4..5115547 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -8,11 +8,31 @@\n \t(S_ISREG(mode) ? (S_IFREG | ce_permissions(mode)) : \\\n \tS_ISLNK(mode) ? S_IFLNK : S_IFDIR)\n \n+struct tree_desc {\n+\tvoid *buf;\n+\tunsigned long size;\n+};\n+\n+struct diff_options;\n+\n+typedef void (*change_fn_t)(struct diff_options *options,\n+\t\t unsigned old_mode, unsigned new_mode,\n+\t\t const unsigned char *old_sha1,\n+\t\t const unsigned char *new_sha1,\n+\t\t const char *base, const char *path);\n+\n+typedef void (*add_remove_fn_t)(struct diff_options *options,\n+\t\t    int addremove, unsigned mode,\n+\t\t    const unsigned char *sha1,\n+\t\t    const char *base, const char *path);\n+\n struct diff_options {\n \tconst char **paths;\n \tconst char *filter;\n \tconst char *orderfile;\n \tconst char *pickaxe;\n+\tunsigned recursive:1,\n+\t\t tree_in_recursive:1;\n \tint break_opt;\n \tint detect_rename;\n \tint find_copies_harder;\n@@ -23,8 +43,17 @@ struct diff_options {\n \tint reverse_diff;\n \tint rename_limit;\n \tint setup;\n+\n+\tchange_fn_t change;\n+\tadd_remove_fn_t add_remove;\n };\n \n+extern void diff_tree_setup_paths(const char **paths);\n+extern int diff_tree(struct tree_desc *t1, struct tree_desc *t2,\n+\t\t     const char *base, struct diff_options *opt);\n+extern int diff_tree_sha1(const unsigned char *old, const unsigned char *new,\n+\t\t\t  const char *base, struct diff_options *opt);\n+\n extern void diff_addremove(struct diff_options *,\n \t\t\t   int addremove,\n \t\t\t   unsigned mode,\ndiff --git a/tree-diff.c b/tree-diff.c\nnew file mode 100644\nindex 0000000..0ef06a9\n--- /dev/null\n+++ b/tree-diff.c\n@@ -0,0 +1,270 @@\n+/*\n+ * Helper functions for tree diff generation\n+ */\n+#include \"cache.h\"\n+#include \"diff.h\"\n+\n+// What paths are we interested in?\n+static int nr_paths = 0;\n+static const char **paths = NULL;\n+static int *pathlens = NULL;\n+\n+static void update_tree_entry(struct tree_desc *desc)\n+{\n+\tvoid *buf = desc->buf;\n+\tunsigned long size = desc->size;\n+\tint len = strlen(buf) + 1 + 20;\n+\n+\tif (size < len)\n+\t\tdie(\"corrupt tree file\");\n+\tdesc->buf = buf + len;\n+\tdesc->size = size - len;\n+}\n+\n+static const unsigned char *extract(struct tree_desc *desc, const char **pathp, unsigned int *modep)\n+{\n+\tvoid *tree = desc->buf;\n+\tunsigned long size = desc->size;\n+\tint len = strlen(tree)+1;\n+\tconst unsigned char *sha1 = tree + len;\n+\tconst char *path = strchr(tree, ' ');\n+\tunsigned int mode;\n+\n+\tif (!path || size < len + 20 || sscanf(tree, \"%o\", &mode) != 1)\n+\t\tdie(\"corrupt tree file\");\n+\t*pathp = path+1;\n+\t*modep = DIFF_FILE_CANON_MODE(mode);\n+\treturn sha1;\n+}\n+\n+static char *malloc_base(const char *base, const char *path, int pathlen)\n+{\n+\tint baselen = strlen(base);\n+\tchar *newbase = xmalloc(baselen + pathlen + 2);\n+\tmemcpy(newbase, base, baselen);\n+\tmemcpy(newbase + baselen, path, pathlen);\n+\tmemcpy(newbase + baselen + pathlen, \"/\", 2);\n+\treturn newbase;\n+}\n+\n+static int show_entry(struct diff_options *opt, const char *prefix, struct tree_desc *desc, const char *base);\n+\n+static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const char *base, struct diff_options *opt)\n+{\n+\tunsigned mode1, mode2;\n+\tconst char *path1, *path2;\n+\tconst unsigned char *sha1, *sha2;\n+\tint cmp, pathlen1, pathlen2;\n+\n+\tsha1 = extract(t1, &path1, &mode1);\n+\tsha2 = extract(t2, &path2, &mode2);\n+\n+\tpathlen1 = strlen(path1);\n+\tpathlen2 = strlen(path2);\n+\tcmp = base_name_compare(path1, pathlen1, mode1, path2, pathlen2, mode2);\n+\tif (cmp < 0) {\n+\t\tshow_entry(opt, \"-\", t1, base);\n+\t\treturn -1;\n+\t}\n+\tif (cmp > 0) {\n+\t\tshow_entry(opt, \"+\", t2, base);\n+\t\treturn 1;\n+\t}\n+\tif (!opt->find_copies_harder &&\n+\t    !memcmp(sha1, sha2, 20) && mode1 == mode2)\n+\t\treturn 0;\n+\n+\t/*\n+\t * If the filemode has changed to/from a directory from/to a regular\n+\t * file, we need to consider it a remove and an add.\n+\t */\n+\tif (S_ISDIR(mode1) != S_ISDIR(mode2)) {\n+\t\tshow_entry(opt, \"-\", t1, base);\n+\t\tshow_entry(opt, \"+\", t2, base);\n+\t\treturn 0;\n+\t}\n+\n+\tif (opt->recursive && S_ISDIR(mode1)) {\n+\t\tint retval;\n+\t\tchar *newbase = malloc_base(base, path1, pathlen1);\n+\t\tif (opt->tree_in_recursive)\n+\t\t\topt->change(opt, mode1, mode2,\n+\t\t\t\t    sha1, sha2, base, path1);\n+\t\tretval = diff_tree_sha1(sha1, sha2, newbase, opt);\n+\t\tfree(newbase);\n+\t\treturn retval;\n+\t}\n+\n+\topt->change(opt, mode1, mode2, sha1, sha2, base, path1);\n+\treturn 0;\n+}\n+\n+static int interesting(struct tree_desc *desc, const char *base)\n+{\n+\tconst char *path;\n+\tunsigned mode;\n+\tint i;\n+\tint baselen, pathlen;\n+\n+\tif (!nr_paths)\n+\t\treturn 1;\n+\n+\t(void)extract(desc, &path, &mode);\n+\n+\tpathlen = strlen(path);\n+\tbaselen = strlen(base);\n+\n+\tfor (i=0; i < nr_paths; i++) {\n+\t\tconst char *match = paths[i];\n+\t\tint matchlen = pathlens[i];\n+\n+\t\tif (baselen >= matchlen) {\n+\t\t\t/* If it doesn't match, move along... */\n+\t\t\tif (strncmp(base, match, matchlen))\n+\t\t\t\tcontinue;\n+\n+\t\t\t/* The base is a subdirectory of a path which was specified. */\n+\t\t\treturn 1;\n+\t\t}\n+\n+\t\t/* Does the base match? */\n+\t\tif (strncmp(base, match, baselen))\n+\t\t\tcontinue;\n+\n+\t\tmatch += baselen;\n+\t\tmatchlen -= baselen;\n+\n+\t\tif (pathlen > matchlen)\n+\t\t\tcontinue;\n+\n+\t\tif (matchlen > pathlen) {\n+\t\t\tif (match[pathlen] != '/')\n+\t\t\t\tcontinue;\n+\t\t\tif (!S_ISDIR(mode))\n+\t\t\t\tcontinue;\n+\t\t}\n+\n+\t\tif (strncmp(path, match, pathlen))\n+\t\t\tcontinue;\n+\n+\t\treturn 1;\n+\t}\n+\treturn 0; /* No matches */\n+}\n+\n+/* A whole sub-tree went away or appeared */\n+static void show_tree(struct diff_options *opt, const char *prefix, struct tree_desc *desc, const char *base)\n+{\n+\twhile (desc->size) {\n+\t\tif (interesting(desc, base))\n+\t\t\tshow_entry(opt, prefix, desc, base);\n+\t\tupdate_tree_entry(desc);\n+\t}\n+}\n+\n+/* A file entry went away or appeared */\n+static int show_entry(struct diff_options *opt, const char *prefix, struct tree_desc *desc, const char *base)\n+{\n+\tunsigned mode;\n+\tconst char *path;\n+\tconst unsigned char *sha1 = extract(desc, &path, &mode);\n+\n+\tif (opt->recursive && S_ISDIR(mode)) {\n+\t\tchar type[20];\n+\t\tchar *newbase = malloc_base(base, path, strlen(path));\n+\t\tstruct tree_desc inner;\n+\t\tvoid *tree;\n+\n+\t\ttree = read_sha1_file(sha1, type, &inner.size);\n+\t\tif (!tree || strcmp(type, \"tree\"))\n+\t\t\tdie(\"corrupt tree sha %s\", sha1_to_hex(sha1));\n+\n+\t\tinner.buf = tree;\n+\t\tshow_tree(opt, prefix, &inner, newbase);\n+\n+\t\tfree(tree);\n+\t\tfree(newbase);\n+\t\treturn 0;\n+\t}\n+\n+\topt->add_remove(opt, prefix[0], mode, sha1, base, path);\n+\treturn 0;\n+}\n+\n+int diff_tree(struct tree_desc *t1, struct tree_desc *t2, const char *base, struct diff_options *opt)\n+{\n+\twhile (t1->size | t2->size) {\n+\t\tif (nr_paths && t1->size && !interesting(t1, base)) {\n+\t\t\tupdate_tree_entry(t1);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (nr_paths && t2->size && !interesting(t2, base)) {\n+\t\t\tupdate_tree_entry(t2);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!t1->size) {\n+\t\t\tshow_entry(opt, \"+\", t2, base);\n+\t\t\tupdate_tree_entry(t2);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!t2->size) {\n+\t\t\tshow_entry(opt, \"-\", t1, base);\n+\t\t\tupdate_tree_entry(t1);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tswitch (compare_tree_entry(t1, t2, base, opt)) {\n+\t\tcase -1:\n+\t\t\tupdate_tree_entry(t1);\n+\t\t\tcontinue;\n+\t\tcase 0:\n+\t\t\tupdate_tree_entry(t1);\n+\t\t\t/* Fallthrough */\n+\t\tcase 1:\n+\t\t\tupdate_tree_entry(t2);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tdie(\"git-diff-tree: internal error\");\n+\t}\n+\treturn 0;\n+}\n+\n+int diff_tree_sha1(const unsigned char *old, const unsigned char *new, const char *base, struct diff_options *opt)\n+{\n+\tvoid *tree1, *tree2;\n+\tstruct tree_desc t1, t2;\n+\tint retval;\n+\n+\ttree1 = read_object_with_reference(old, \"tree\", &t1.size, NULL);\n+\tif (!tree1)\n+\t\tdie(\"unable to read source tree (%s)\", sha1_to_hex(old));\n+\ttree2 = read_object_with_reference(new, \"tree\", &t2.size, NULL);\n+\tif (!tree2)\n+\t\tdie(\"unable to read destination tree (%s)\", sha1_to_hex(new));\n+\tt1.buf = tree1;\n+\tt2.buf = tree2;\n+\tretval = diff_tree(&t1, &t2, base, opt);\n+\tfree(tree1);\n+\tfree(tree2);\n+\treturn retval;\n+}\n+\n+static int count_paths(const char **paths)\n+{\n+\tint i = 0;\n+\twhile (*paths++)\n+\t\ti++;\n+\treturn i;\n+}\n+\n+void diff_tree_setup_paths(const char **p)\n+{\n+\tif (p) {\n+\t\tint i;\n+\n+\t\tpaths = p;\n+\t\tnr_paths = count_paths(paths);\n+\t\tpathlens = xmalloc(nr_paths * sizeof(int));\n+\t\tfor (i=0; i<nr_paths; i++)\n+\t\t\tpathlens[i] = strlen(paths[i]);\n+\t}\n+}\n"},{"id":"10416","messageId":"7v64rr2y4q.fsf@assigned-by-dhcp.cox.net","threadId":"2176","inReplyTo":"Pine.LNX.4.64.0510202058030.10477@g5.osdl.org","subject":"Re: Split up tree diff functions into tree-diff.c library","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-10-21T06:16:21Z","receivedAt":"2005-10-21T06:16:21Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"I have not closely studied the user of this change, rev-list,\nbut I have a hunch that you might be better off, if you define a\nnew diff \"format\", DIFF_FORMAT_CALLBACK, and hook into\ndiff_flush(), instead of hooking into diff_addremove() and\ndiff_change().  Instead of calling diff_flush_raw() or\ndiff_flush_name(), or diff_flush_patch(), your callback function\nwould be called with each filepair.\n\nRight now, you are only making the parallel tree traversing and\ncomparing part from diff-tree available in the library form\n(which is fine), but that way leaves the door open for it to\nalso use the rest of the diffcore machinery.\n\nBy doing things this way, later round of rev-list that culls\nuninteresting commits could be taught to also follow renames.\n\nAm I grossly off the mark?\n"},{"id":"10427","messageId":"Pine.LNX.4.64.0510210814310.10477@g5.osdl.org","threadId":"2176","inReplyTo":"7v64rr2y4q.fsf@assigned-by-dhcp.cox.net","subject":"Re: Split up tree diff functions into tree-diff.c library","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-10-21T15:19:30Z","receivedAt":"2005-10-21T15:19:30Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Thu, 20 Oct 2005, Junio C Hamano wrote:\n>\n> I have not closely studied the user of this change, rev-list,\n> but I have a hunch that you might be better off, if you define a\n> new diff \"format\", DIFF_FORMAT_CALLBACK, and hook into\n> diff_flush(), instead of hooking into diff_addremove() and\n> diff_change().\n\nNo, I did that on purpose.\n\nI did _not_ want the library interface users to have to use the flushing \nand all the associated memory management and complexity issues.\n\nThe thing is, the only thing rev-list wants to know is whether there was a \ndiff at all, and I want to eventually make \"opt->change()\" and \n\"opt->add_remove()\" return a possible error code so that the whole diff \nlibrary can just stop on the first change, since once we've seen _any_ \nchange, we don't care any more.\n\nAlso, by taking it over at this lieve, you don't need to do any of the \ndiff setup at all to use the core routines.\n\nNow, if somebody _wants_ to hook into flushing, you can still do so: you \njust do\n\n\topt->add_remove = diff_addremove;\n\topt->change = diff_change;\n\nand then you can hook into the higher-level functions if you want to. But \nfor a lot of uses, I bet we do _not_ want to. And those higher-level \nroutines have had some _serious_ memory use problems etc: by hooking into \nthe lower level, we can bypass all of that.\n\n(A memory leak in diff in git-rev-parse would be deadly on big archives).\n\n> Right now, you are only making the parallel tree traversing and\n> comparing part from diff-tree available in the library form\n> (which is fine), but that way leaves the door open for it to\n> also use the rest of the diffcore machinery.\n\nThe point is, we don't _want_ to use the rest of the diffcore machinery. \nIt's too expensive and fragile.\n\n\t\tLinus\n"}]}