{"thread":{"id":"346","subject":"[PATCH] diff-files: in the spirit of diff-cache and diff-tree","startedAt":"2005-04-27T04:24:05Z","lastAt":"2005-04-27T04:24:05Z","messageCount":1,"participants":["Nicolas Pitre"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"1845","messageId":"Pine.LNX.4.62.0504270010210.14033@localhost.localdomain","threadId":"346","inReplyTo":null,"subject":"[PATCH] diff-files: in the spirit of diff-cache and diff-tree","fromName":"Nicolas Pitre","fromEmail":"nico@cam.org","sentAt":"2005-04-27T04:24:05Z","receivedAt":"2005-04-27T04:24:05Z","isPatch":true,"sender":{"key":"nico@fluxnic.net","avatar":"https://avatars.githubusercontent.com/u/702790?v=4"},"body":"\nHere's a diff-files implementation to go along with diff-cache and \ndiff-tree.  It is based on pieces taken from show-diff and show-files.  \nThe difference with show-diff is that it can handle files which are not \n(yet) in the cache.  And since the show-diff arguments are a bit awkward \nI decided it would be better to leave it alone and create a new tool.  \nIMHO show-diff could simply be removed once all its users have been \nswitched over to diff-files.\n\nSigned-off-by: Nicolas Pitre <nico@cam.org>\n\n--- k/Makefile\n+++ l/Makefile\n@@ -18,7 +18,7 @@ PROG=   update-cache show-diff init-db w\n \tcat-file fsck-cache checkout-cache diff-tree rev-tree show-files \\\n \tcheck-files ls-tree merge-base merge-cache unpack-file git-export \\\n \tdiff-cache convert-cache http-pull rpush rpull rev-list git-mktag \\\n-\tdiff-tree-helper\n+\tdiff-tree-helper diff-files\n \n all: $(PROG)\n \n--- k/diff-files.c\n+++ l/diff-files.c\n@@ -0,0 +1,246 @@\n+/*\n+ * GIT - The information manager from hell\n+ *\n+ * Copyright (C) Linus Torvalds, 2005\n+ */\n+\n+#include <dirent.h>\n+#include \"cache.h\"\n+\n+static const char *diff_files_usage = \"diff-files [-d] [-o] [-z] [paths...]\";\n+\n+/* What paths are we interested in? */\n+static int nr_paths = 0;\n+static char **paths = NULL;\n+static int *pathlens = NULL;\n+\n+/*\n+ * see if name matches our specified paths.\n+ * return value:\n+ *\t-1 if no match\n+ *\t0 if partial match (name is a directory component)\n+ *\t1 = exact match\n+ *\t2 = name is within a specified directory path\n+ */\n+static int path_match(const char *name, int namelen)\n+{\n+\tint i;\n+\tif (!nr_paths)\n+\t\treturn 2;\n+\tfor (i = 0; i < nr_paths; i++) {\n+\t\tint pathlen = pathlens[i];\n+\t\tif (pathlen == namelen &&\n+\t\t    strncmp(paths[i], name, pathlen) == 0) {\n+\t\t\treturn 1;\n+\t\t} else if (pathlen > namelen && \n+\t\t\t   strncmp(paths[i], name, namelen) == 0 &&\n+\t\t\t   paths[i][namelen] == '/') {\n+\t\t\t\t   return 0;\n+\t\t} else if (pathlen < namelen &&\n+\t\t\t   strncmp(paths[i], name, pathlen) == 0 &&\n+\t\t\t   name[pathlen] == '/') {\n+\t\t\treturn 2;\n+\t\t}\n+\t}\n+\treturn -1;\n+}\n+\n+static const char **dir;\n+static int nr_dir;\n+static int dir_alloc;\n+\n+static void add_name(const char *pathname, int len)\n+{\n+\tchar *name;\n+\n+\tif (cache_name_pos(pathname, len) >= 0)\n+\t\treturn;\n+\n+\tif (nr_dir == dir_alloc) {\n+\t\tdir_alloc = alloc_nr(dir_alloc);\n+\t\tdir = realloc(dir, dir_alloc*sizeof(char *));\n+\t}\n+\tname = malloc(len + 1);\n+\tmemcpy(name, pathname, len + 1);\n+\tdir[nr_dir++] = name;\n+}\n+\n+/*\n+ * Read a directory tree. We currently ignore anything but\n+ * directories and regular files. That's because git doesn't\n+ * handle them at all yet. Maybe that will change some day.\n+ *\n+ * Also, we currently ignore all names starting with a dot.\n+ * That likely will not change.\n+ */\n+static void read_directory(const char *path, const char *base, int baselen, int match)\n+{\n+\tDIR *dir = opendir(path);\n+\n+\tif (dir) {\n+\t\tstruct dirent *de;\n+\t\tchar fullname[MAXPATHLEN + 1];\n+\t\tmemcpy(fullname, base, baselen);\n+\n+\t\twhile ((de = readdir(dir)) != NULL) {\n+\t\t\tint len;\n+\n+\t\t\tif (de->d_name[0] == '.')\n+\t\t\t\tcontinue;\n+\t\t\tlen = strlen(de->d_name);\n+\t\t\tmemcpy(fullname + baselen, de->d_name, len+1);\n+\t\t\tif (match < 2)\n+\t\t\t\tmatch = path_match(fullname, baselen+len);\n+\t\t\tif (match < 0)\n+\t\t\t\tcontinue;\n+\n+\t\t\tswitch (de->d_type) {\n+\t\t\tstruct stat st;\n+\t\t\tdefault:\n+\t\t\t\tcontinue;\n+\t\t\tcase DT_UNKNOWN:\n+\t\t\t\tif (lstat(fullname, &st))\n+\t\t\t\t\tcontinue;\n+\t\t\t\tif (S_ISREG(st.st_mode))\n+\t\t\t\t\tbreak;\n+\t\t\t\tif (!S_ISDIR(st.st_mode))\n+\t\t\t\t\tcontinue;\n+\t\t\t\t/* fallthrough */\n+\t\t\tcase DT_DIR:\n+\t\t\t\tmemcpy(fullname + baselen + len, \"/\", 2);\n+\t\t\t\tread_directory(fullname, fullname,\n+\t\t\t\t\t       baselen + len + 1,\n+\t\t\t\t\t       match == 1 ? 2 : 0);\n+\t\t\t\tcontinue;\n+\t\t\tcase DT_REG:\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tif (match > 0)\n+\t\t\t\tadd_name(fullname, baselen + len);\n+\t\t}\n+\t\tclosedir(dir);\n+\t}\n+}\n+\n+static int cmp_name(const void *p1, const void *p2)\n+{\n+\tconst char *n1 = *(const char **)p1;\n+\tconst char *n2 = *(const char **)p2;\n+\tint l1 = strlen(n1), l2 = strlen(n2);\n+\n+\treturn cache_name_compare(n1, l1, n2, l2);\n+}\n+\n+static int show_deleted = 0;\n+static int show_others = 0;\n+static int line_terminator = '\\n';\n+\n+static const char null_sha1_hex[] = \"0000000000000000000000000000000000000000\";\n+\n+static void show_file(const char *prefix, unsigned int mode,\n+\t\t      const char *sha1, const char *name)\n+{\n+\tprintf(\"%s%o\\t%s\\t%s\\t%s%c\", prefix, mode, \"blob\",\n+\t       sha1, name, line_terminator);\n+}\n+\n+int main(int argc, char **argv)\n+{\n+\tint i, entries;\n+\n+\tfor (i = 1; i < argc; i++) {\n+\t\tchar *arg = argv[i];\n+\n+\t\tif (*arg != '-')\n+\t\t\tbreak;\n+\n+\t\tif (!strcmp(arg, \"-z\")) {\n+\t\t\tline_terminator = 0;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(arg, \"-d\")) {\n+\t\t\tshow_deleted = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(arg, \"-o\")) {\n+\t\t\tshow_others = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tusage(diff_files_usage);\n+\t}\n+\n+\tif (i < argc) {\n+\t\tpaths = &argv[i];\n+\t\tnr_paths = argc - i;\n+\t\tpathlens = malloc(nr_paths * sizeof(int));\n+\t\tfor (i=0; i<nr_paths; i++) {\n+\t\t\tpathlens[i] = strlen(paths[i]);\n+\t\t\tif (paths[i][pathlens[i] - 1] == '@')\n+\t\t\t\tpathlens[i]--;\n+\t\t}\n+\t}\n+\n+\tentries = read_cache();\n+\tif (entries < 0) {\n+\t\tperror(\"read_cache\");\n+\t\texit(1);\n+\t}\n+\n+\tif (show_others) {\n+\t\tread_directory(\".\", \"\", 0, 0);\n+\t\tqsort(dir, nr_dir, sizeof(char *), cmp_name);\n+\t\tfor (i = 0; i < nr_dir; i++) {\n+\t\t\tstruct stat st;\n+\t\t\tunsigned int mode;\n+\t\t\tif (stat(dir[i], &st) < 0) {\n+\t\t\t\tperror(dir[i]);\n+\t\t\t} else {\n+\t\t\t\tmode = S_IFREG | ce_permissions(st.st_mode);\n+\t\t\t\tshow_file(\"+\", mode, null_sha1_hex, dir[i]);\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tfor (i = 0; i < entries; i++) {\n+\t\tstruct stat st;\n+\t\tunsigned int ce_mode, mode;\n+\t\tstruct cache_entry *ce = active_cache[i];\n+\t\tint changed;\n+\n+\t\tif (path_match(ce->name, ce_namelen(ce)) < 1)\n+\t\t\tcontinue;\n+\n+\t\tif (ce_stage(ce)) {\n+\t\t\tprintf(\"U %s%c\", ce->name, line_terminator);\n+\t\t\tdo {\n+\t\t\t\ti++;\n+\t\t\t} while (i < entries &&\n+\t\t\t\t !strcmp(ce->name, active_cache[i]->name));\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tce_mode = ntohl(ce->ce_mode);\n+\t\tif (stat(ce->name, &st) < 0) {\n+\t\t\tif (errno != ENOENT) {\n+\t\t\t\tperror(ce->name);\n+\t\t\t} else if (show_deleted) {\n+\t\t\t\tshow_file(\"-\", ce_mode,\n+\t\t\t\t\t  sha1_to_hex(ce->sha1), ce->name);\n+\t\t\t}\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tchanged = cache_match_stat(ce, &st);\n+\t\tif (!changed)\n+\t\t\tcontinue;\n+\n+\t\tmode = S_IFREG | ce_permissions(st.st_mode);\n+\t\tprintf(\"*%o->%o\\t%s\\t%s->%s\\t%s%c\",\n+\t\t       ce_mode, mode, \"blob\",\n+\t\t       sha1_to_hex(ce->sha1), null_sha1_hex,\n+\t\t       ce->name, line_terminator);\n+\t}\n+\n+\treturn 0;\n+}\n\n"}]}