{"thread":{"id":"596","subject":"[PATCH 3/3] Add git-ls-files -k.","startedAt":"2005-05-13T00:17:54Z","lastAt":"2005-05-13T00:17:54Z","messageCount":1,"participants":["Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":3},"messages":[{"id":"3217","messageId":"7vr7gcasi5.fsf@assigned-by-dhcp.cox.net","threadId":"596","inReplyTo":null,"subject":"[PATCH 3/3] Add git-ls-files -k.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-13T00:17:54Z","receivedAt":"2005-05-13T00:17:54Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"When checkout-cache attempts to check out a non-directory where\na directory exists on the work tree, or to check out a file\nunder directory D when path D is a non-directory on the work\ntree, the attempt fails.  Before running checkout-cache, the\nuser can run git-ls-files with the -k (killed) option to get a\nlist of such paths.  The tagged output format uses \"K\" to denote\nthem.  This is useful for Porcelain layer to be careful when\ndealing with the recently corrected behaviour of checkout-cache.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\n*** This should be considered a companion patch to the\n*** checkout-cache fix you merged recently.  An extra safety\n*** net just like git-check-files.\n\nDocumentation/git-ls-files.txt |   10 +++-\nls-files.c                     |  101 ++++++++++++++++++++++++++++++++++-------\n2 files changed, 92 insertions(+), 19 deletions(-)\n\n--- a/Documentation/git-ls-files.txt\n+++ b/Documentation/git-ls-files.txt\n@@ -10,8 +10,8 @@\n SYNOPSIS\n --------\n 'git-ls-files' [-z] [-t]\n-\t\t(--[cached|deleted|others|ignored|stage|unmerged])\\*\n-\t\t(-[c|d|o|i|s|u])\\*\n+\t\t(--[cached|deleted|others|ignored|stage|unmerged|killed])\\*\n+\t\t(-[c|d|o|i|s|u|k])\\*\n \t\t[-x <pattern>|--exclude=<pattern>]\n \t\t[-X <file>|--exclude-from=<file>]\n \n@@ -45,6 +45,11 @@\n -u|--unmerged::\n \tShow unmerged files in the output (forces --stage)\n \n+-k|--killed::\n+\tShow files on the filesystem that need to be removed due\n+\tto file/directory conflicts for checkout-cache to\n+\tsucceed.\n+\n -z::\n \t\\0 line termination on output\n \n@@ -65,6 +70,7 @@\n \tH\tcached\n \tM\tunmerged\n \tR\tremoved/deleted\n+\tK\tto be killed\n \t?\tother\n \n Output\n--- a/ls-files.c\n+++ b/ls-files.c\n@@ -16,12 +16,14 @@\n static int show_ignored = 0;\n static int show_stage = 0;\n static int show_unmerged = 0;\n+static int show_killed = 0;\n static int line_terminator = '\\n';\n \n static const char *tag_cached = \"\";\n static const char *tag_unmerged = \"\";\n static const char *tag_removed = \"\";\n static const char *tag_other = \"\";\n+static const char *tag_killed = \"\";\n \n static int nr_excludes;\n static const char **excludes;\n@@ -87,24 +89,30 @@\n \treturn 0;\n }\n \n-static const char **dir;\n+struct nond_on_fs {\n+\tint len;\n+\tchar name[0];\n+};\n+\n+static struct nond_on_fs **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+\tstruct nond_on_fs *ent;\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 = xrealloc(dir, dir_alloc*sizeof(char *));\n+\t\tdir = xrealloc(dir, dir_alloc*sizeof(ent));\n \t}\n-\tname = xmalloc(len + 1);\n-\tmemcpy(name, pathname, len + 1);\n-\tdir[nr_dir++] = name;\n+\tent = xmalloc(sizeof(*ent) + len + 1);\n+\tent->len = len;\n+\tmemcpy(ent->name, pathname, len);\n+\tdir[nr_dir++] = ent;\n }\n \n /*\n@@ -164,11 +172,62 @@\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+\tconst struct nond_on_fs *e1 = *(const struct nond_on_fs **)p1;\n+\tconst struct nond_on_fs *e2 = *(const struct nond_on_fs **)p2;\n+\n+\treturn cache_name_compare(e1->name, e1->len,\n+\t\t\t\t  e2->name, e2->len);\n+}\n \n-\treturn cache_name_compare(n1, l1, n2, l2);\n+static void show_killed_files()\n+{\n+\tint i;\n+\tfor (i = 0; i < nr_dir; i++) {\n+\t\tstruct nond_on_fs *ent = dir[i];\n+\t\tchar *cp, *sp;\n+\t\tint pos, len, killed = 0;\n+\n+\t\tfor (cp = ent->name; cp - ent->name < ent->len; cp = sp + 1) {\n+\t\t\tsp = strchr(cp, '/');\n+\t\t\tif (!sp) {\n+\t\t\t\t/* If ent->name is prefix of an entry in the\n+\t\t\t\t * cache, it will be killed.\n+\t\t\t\t */\n+\t\t\t\tpos = cache_name_pos(ent->name, ent->len);\n+\t\t\t\tif (0 <= pos)\n+\t\t\t\t\tdie(\"bug in show-killed-files\");\n+\t\t\t\tpos = -pos - 1;\n+\t\t\t\twhile (pos < active_nr &&\n+\t\t\t\t       ce_stage(active_cache[pos]))\n+\t\t\t\t\tpos++; /* skip unmerged */\n+\t\t\t\tif (active_nr <= pos)\n+\t\t\t\t\tbreak;\n+\t\t\t\t/* pos points at a name immediately after\n+\t\t\t\t * ent->name in the cache.  Does it expect\n+\t\t\t\t * ent->name to be a directory?\n+\t\t\t\t */\n+\t\t\t\tlen = ce_namelen(active_cache[pos]);\n+\t\t\t\tif ((ent->len < len) &&\n+\t\t\t\t    !strncmp(active_cache[pos]->name,\n+\t\t\t\t\t     ent->name, ent->len) &&\n+\t\t\t\t    active_cache[pos]->name[ent->len] == '/')\n+\t\t\t\t\tkilled = 1;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tif (0 <= cache_name_pos(ent->name, sp - ent->name)) {\n+\t\t\t\t/* If any of the leading directories in\n+\t\t\t\t * ent->name is registered in the cache,\n+\t\t\t\t * ent->name will be killed.\n+\t\t\t\t */\n+\t\t\t\tkilled = 1;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t\tif (killed)\n+\t\t\tprintf(\"%s%.*s%c\", tag_killed,\n+\t\t\t       dir[i]->len, dir[i]->name,\n+\t\t\t       line_terminator);\n+\t}\n }\n \n static void show_files(void)\n@@ -176,11 +235,16 @@\n \tint i;\n \n \t/* For cached/deleted files we don't need to even do the readdir */\n-\tif (show_others) {\n+\tif (show_others || show_killed) {\n \t\tread_directory(\".\", \"\", 0);\n-\t\tqsort(dir, nr_dir, sizeof(char *), cmp_name);\n-\t\tfor (i = 0; i < nr_dir; i++)\n-\t\t\tprintf(\"%s%s%c\", tag_other, dir[i], line_terminator);\n+\t\tqsort(dir, nr_dir, sizeof(struct nond_on_fs *), cmp_name);\n+\t\tif (show_others)\n+\t\t\tfor (i = 0; i < nr_dir; i++)\n+\t\t\t\tprintf(\"%s%.*s%c\", tag_other,\n+\t\t\t\t       dir[i]->len, dir[i]->name,\n+\t\t\t\t       line_terminator);\n+\t\tif (show_killed)\n+\t\t\tshow_killed_files();\n \t}\n \tif (show_cached | show_stage) {\n \t\tfor (i = 0; i < active_nr; i++) {\n@@ -219,8 +283,8 @@\n }\n \n static const char *ls_files_usage =\n-\t\"ls-files [-z] [-t] (--[cached|deleted|others|stage|unmerged])* \"\n-\t\"[ --ignored [--exclude=<pattern>] [--exclude-from=<file>) ]\";\n+\"ls-files [-z] [-t] (--[cached|deleted|others|stage|unmerged|killed])* \"\n+\"[ --ignored [--exclude=<pattern>] [--exclude-from=<file>) ]\";\n \n int main(int argc, char **argv)\n {\n@@ -236,6 +300,7 @@\n \t\t\ttag_unmerged = \"M \";\n \t\t\ttag_removed = \"R \";\n \t\t\ttag_other = \"? \";\n+\t\t\ttag_killed = \"K \";\n \t\t} else if (!strcmp(arg, \"-c\") || !strcmp(arg, \"--cached\")) {\n \t\t\tshow_cached = 1;\n \t\t} else if (!strcmp(arg, \"-d\") || !strcmp(arg, \"--deleted\")) {\n@@ -246,6 +311,8 @@\n \t\t\tshow_ignored = 1;\n \t\t} else if (!strcmp(arg, \"-s\") || !strcmp(arg, \"--stage\")) {\n \t\t\tshow_stage = 1;\n+\t\t} else if (!strcmp(arg, \"-k\") || !strcmp(arg, \"--killed\")) {\n+\t\t\tshow_killed = 1;\n \t\t} else if (!strcmp(arg, \"-u\") || !strcmp(arg, \"--unmerged\")) {\n \t\t\t/* There's no point in showing unmerged unless\n \t\t\t * you also show the stage information.\n@@ -271,7 +338,7 @@\n \t}\n \n \t/* With no flags, we default to showing the cached files */\n-\tif (!(show_stage | show_deleted | show_others | show_unmerged))\n+\tif (!(show_stage | show_deleted | show_others | show_unmerged | show_killed))\n \t\tshow_cached = 1;\n \n \tread_cache();\n------------------------------------------------\n\n"}]}