{"thread":{"id":"1546","subject":"Improve handling of \".\" and \"..\" in git-diff-*","startedAt":"2005-08-17T03:44:32Z","lastAt":"2005-08-23T19:54:55Z","messageCount":4,"participants":["Linus Torvalds","Alex Riesen"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"7415","messageId":"Pine.LNX.4.58.0508162037080.3553@g5.osdl.org","threadId":"1546","inReplyTo":null,"subject":"Improve handling of \".\" and \"..\" in git-diff-*","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-08-17T03:44:32Z","receivedAt":"2005-08-17T03:44:32Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\nThis fixes up usage of \"..\" (without an ending slash) and \".\" (with or \nwithout the ending slash) in the git diff family.\n\nIt also fixes pathspec matching for the case of an empty pathspec, since a \n\".\" in the top-level directory (or enough \"..\" under subdirectories) will \nresult in an empty pathspec. We used to not match it against anything, but \nit should in fact match everything.\n\nSigned-off-by: Linus Torvalds <torvalds@osdl.org>\n----\n\nThis is in addition to the previous fix for the t0000 testcase and applies \nindependently of that.\n\nNOTE! This does _not_ handle \"..\" or \".\" in the _middle_ of a pathspec. If \nyou have people who do\n\n\tgit diff net/../char/\n\nthis will not help them - it will _not_ simplify this to\n\n\tgit diff char/\n\nand only \"..\" and \".\" components at the head of the pathspec will be\nhonoured.\n\nFinally, if you try to give a pathspec that goes \"outside\" of the .git\ndirectory (eg, a \"..\" pattern at the top of the git archive), the code\nwill politely tell you to f*ck off and die (this is not a new feature, the\nprevious code drop did that too, but because we now handle it even for the\ntop-level case without any extra prefix, it's worth noting).\n\n---\ndiff --git a/diffcore-pathspec.c b/diffcore-pathspec.c\n--- a/diffcore-pathspec.c\n+++ b/diffcore-pathspec.c\n@@ -29,6 +29,8 @@ static int matches_pathspec(const char *\n \t\t    name[len] == 0 ||\n \t\t    name[len] == '/')\n \t\t\treturn 1;\n+\t\tif (!len)\n+\t\t\treturn 1;\n \t}\n \treturn 0;\n }\ndiff --git a/read-cache.c b/read-cache.c\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -191,6 +191,8 @@ int ce_path_match(const struct cache_ent\n \t\t\treturn 1;\n \t\tif (name[matchlen] == '/' || !name[matchlen])\n \t\t\treturn 1;\n+\t\tif (!matchlen)\n+\t\t\treturn 1;\n \t}\n \treturn 0;\n }\ndiff --git a/setup.c b/setup.c\n--- a/setup.c\n+++ b/setup.c\n@@ -1,23 +1,60 @@\n #include \"cache.h\"\n \n+static char *prefix_path(const char *prefix, int len, char *path)\n+{\n+\tchar *orig = path;\n+\tfor (;;) {\n+\t\tchar c;\n+\t\tif (*path != '.')\n+\t\t\tbreak;\n+\t\tc = path[1];\n+\t\t/* \".\" */\n+\t\tif (!c) {\n+\t\t\tpath++;\n+\t\t\tbreak;\n+\t\t}\n+\t\t/* \"./\" */\n+\t\tif (c == '/') {\n+\t\t\tpath += 2;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (c != '.')\n+\t\t\tbreak;\n+\t\tc = path[2];\n+\t\tif (!c)\n+\t\t\tpath += 2;\n+\t\telse if (c == '/')\n+\t\t\tpath += 3;\n+\t\telse\n+\t\t\tbreak;\n+\t\t/* \"..\" and \"../\" */\n+\t\t/* Remove last component of the prefix */\n+\t\tdo {\n+\t\t\tif (!len)\n+\t\t\t\tdie(\"'%s' is outside repository\", orig);\n+\t\t\tlen--;\n+\t\t} while (len && prefix[len-1] != '/');\n+\t\tcontinue;\n+\t}\n+\tif (len) {\n+\t\tint speclen = strlen(path);\n+\t\tchar *n = xmalloc(speclen + len + 1);\n+\t\n+\t\tmemcpy(n, prefix, len);\n+\t\tmemcpy(n + len, path, speclen+1);\n+\t\tpath = n;\n+\t}\n+\treturn path;\n+}\n+\n const char **get_pathspec(const char *prefix, char **pathspec)\n {\n \tchar *entry = *pathspec;\n \tchar **p;\n \tint prefixlen;\n \n-\tif (!prefix) {\n-\t\tchar **p;\n-\t\tif (!entry)\n-\t\t\treturn NULL;\n-\t\tp = pathspec;\n-\t\tdo {\n-\t\t\tif (*entry != '.')\n-\t\t\t\tcontinue;\n-\t\t\t/* fixup ? */\n-\t\t} while ((entry = *++p) != NULL);\n-\t\treturn (const char **) pathspec;\n-\t}\n+\tif (!prefix && !entry)\n+\t\treturn NULL;\n \n \tif (!entry) {\n \t\tstatic const char *spec[2];\n@@ -27,38 +64,10 @@ const char **get_pathspec(const char *pr\n \t}\n \n \t/* Otherwise we have to re-write the entries.. */\n-\tprefixlen = strlen(prefix);\n \tp = pathspec;\n+\tprefixlen = prefix ? strlen(prefix) : 0;\n \tdo {\n-\t\tint speclen, len = prefixlen;\n-\t\tchar *n;\n-\n-\t\tfor (;;) {\n-\t\t\tif (!strcmp(entry, \".\")) {\n-\t\t\t\tentry++;\n-\t\t\t\tbreak;\n-\t\t\t}\n-\t\t\tif (!strncmp(entry, \"./\", 2)) {\n-\t\t\t\tentry += 2;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strncmp(entry, \"../\", 3)) {\n-\t\t\t\tdo {\n-\t\t\t\t\tif (!len)\n-\t\t\t\t\t\tdie(\"'%s' is outside repository\", *p);\n-\t\t\t\t\tlen--;\n-\t\t\t\t} while (len && prefix[len-1] != '/');\n-\t\t\t\tentry += 3;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tbreak;\n-\t\t}\n-\t\tspeclen = strlen(entry);\n-\t\tn = xmalloc(speclen + len + 1);\n-\t\t\n-\t\tmemcpy(n, prefix, len);\n-\t\tmemcpy(n + len, entry, speclen+1);\n-\t\t*p = n;\n+\t\t*p = prefix_path(prefix, prefixlen, entry);\n \t} while ((entry = *++p) != NULL);\n \treturn (const char **) pathspec;\n }\n"},{"id":"7416","messageId":"Pine.LNX.4.58.0508162044390.3553@g5.osdl.org","threadId":"1546","inReplyTo":"Pine.LNX.4.58.0508162037080.3553@g5.osdl.org","subject":"Re: Improve handling of \".\" and \"..\" in git-diff-*","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-08-17T03:52:20Z","receivedAt":"2005-08-17T03:52:20Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\nOn Tue, 16 Aug 2005, Linus Torvalds wrote:\n> \n> This fixes up usage of \"..\" (without an ending slash) and \".\" (with or \n> without the ending slash) in the git diff family.\n\nBtw, if it wasn't clear, with this patch you can now do\n\n\tgit diff .\n\nand it will show the diffs for everything under the current working \ndirectory. Similarly, you can do\n\n\tgit diff ./drivers\n\nor something like\n\n\tcd arch/i386\n\tgit diff . ../x86-64 \n\nand it will do the obvious thing (the \"obvious thing\" in the latter case\nis to show the diffs for everything under _both_ arch/i386 and\narch/x86-64, but not anything outside of that - the path pattern gets\nrewritten to \"arch/i386\" and \"arch/x86-64\").\n\n\t\tLinus\n\nPS. A number of the other \"work tree\" ops can be similarly extended to\nDTRT in git project subdirectories, but I wanted to finish off the \"git\ndiff\" family first, and get that one all done. I think I'm done now, and I\nmight tackle the other things (git-update-cache, git-checkout-cache,\ngit-commit-script, etc) tomorrow.\n"},{"id":"7660","messageId":"81b0412b050823124938d735bf@mail.gmail.com","threadId":"1546","inReplyTo":"Pine.LNX.4.58.0508162037080.3553@g5.osdl.org","subject":"Re: Improve handling of \".\" and \"..\" in git-diff-*","fromName":"Alex Riesen","fromEmail":"raa.lkml@gmail.com","sentAt":"2005-08-23T19:49:07Z","receivedAt":"2005-08-23T19:49:07Z","isPatch":false,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"On 8/17/05, Linus Torvalds <torvalds@osdl.org> wrote:\n> NOTE! This does _not_ handle \"..\" or \".\" in the _middle_ of a pathspec. If\n> you have people who do\n\nBTW, could this (below) be useful for something?\n\n#include <string.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n// an analog of \"cd path\" from a directory \"cwd\".\nchar* pathexpand(const char* cwd, const char* path)\n{\n    static const char SEP[] = \"/\";\n    if ( !*path ) // empty path -> \".\" (don't move)\n\tpath = \".\";\n    if ( !*cwd || *SEP == *path ) // no cwd, or path begins with \"/\"\n\tcwd = SEP;\n\n    int len = strlen(cwd);\n    char* out = (char*)malloc(len + 1 + strlen(path) + 1);\n    char* p = strcpy(out, cwd) + len;\n\n    if ( *SEP != p[-1] )\n\t*p++ = *SEP;\n\n    for ( ; *path; ++path )\n    {\n\tchar * pl = p;\n\twhile ( *path && *SEP != *path )\n\t    *p++ = *path++;\n\t*p = '\\0';\n\n\tif ( p == pl ) /* ...\"//\"... */\n\t    ; // just ignore\n\n\t/* ...\"/./\"...  */\n\telse if ( p - pl == 1 && '.' == *pl )\n\t    --p; // just ignore\n\n\t/* ...\"/../\"...  */\n\telse if ( p - pl == 2 && '.' == pl[0] && '.' == pl[1] )\n\t{\n\t    // drop last element of the resulting path\n\t    if ( --pl > out )\n\t\tfor ( --pl; pl > out && *SEP != *pl; --pl );\n\t    p = ++pl;\n\t}\n\t/* ...\"/path/\"...  */\n\telse if ( *path )\n\t    *p++ = *path; // just add the separator\n\tif ( !*path )\n\t    break;\n    }\n    if ( p > out+1 && *SEP == p[-1] )\n\t--p;\n    *p = '\\0';\n    return out;\n}\n\n#ifdef CHECK_PATHEXPAND\nstatic void check(const char * cwd, const char * path, const char * good)\n{\n    static int n = 0;\n    printf(\"%-2d: %s$ cd %s\", ++n, cwd, path);\n    char* t = pathexpand(cwd, path);\n    if ( strcmp(t, good) )\n\tprintf(\" failed(%s)\\n\", t);\n    else\n\tprintf(\" \\t\\t%s\\n\", t);\n    free(t);\n}\n\nint main(int argc, char** argv)\n{\n    /* 1 */ check(\"/onelevel\", \"aa\", \"/onelevel/aa\");\n    /* 2 */ check(\"/\", \"..\", \"/\");\n    /* 3 */ check(\"/\", \"../..\", \"/\");\n    /* 4 */ check(\"/one\", \"aa/../bb\", \"/one/bb\");\n    /* 5 */ check(\"/one/two\", \"aa//bb\", \"/one/two/aa/bb\");\n    /* 6 */ check(\"\", \"/aa//bb\", \"/aa/bb\");\n    /* 7 */ check(\"/one/two\", \"\", \"/one/two\");\n    /* 8 */ check(\"/one/two\", \"aa/..bb/x/../cc/\", \"/one/two/aa/..bb/cc\");\n    /* 9 */ check(\"/one/two\", \"aa/x/././cc////\", \"/one/two/aa/x/cc\");\n    /* 10 */ check(\"/one/two\", \"../../../../aa\", \"/aa\");\n\n    return 0;\n}\n#endif\n"},{"id":"7661","messageId":"81b0412b05082312542bd1ecc@mail.gmail.com","threadId":"1546","inReplyTo":"81b0412b050823124938d735bf@mail.gmail.com","subject":"Re: Improve handling of \".\" and \"..\" in git-diff-*","fromName":"Alex Riesen","fromEmail":"raa.lkml@gmail.com","sentAt":"2005-08-23T19:54:55Z","receivedAt":"2005-08-23T19:54:55Z","isPatch":false,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"On 8/23/05, Alex Riesen <raa.lkml@gmail.com> wrote:\n> On 8/17/05, Linus Torvalds <torvalds@osdl.org> wrote:\n> > NOTE! This does _not_ handle \"..\" or \".\" in the _middle_ of a pathspec. If\n> > you have people who do\n> \n> BTW, could this (below) be useful for something?\n> \n\nWell, a bit of explanation is certainly missing, sorry. The code tries\nto emulate\n(on purely text level)  the behaviour of chdir(2), including stopping at root,\ngoing back and forth and removing useless (not changing directory)\nparts like \"/./\".\n\nThe file in the previous message can be tested with :\n\n  gcc -DCHECK_PATHEXPAND patchexpand.c && ./aout\n\nCheers,\nAlex Riesen.\n\nPS: Before anyone asked: the code is mine and free for any use.\n"}]}