{"thread":{"id":"554","subject":"\"git-checkout-cache -f -a\" failure","startedAt":"2005-05-09T18:25:29Z","lastAt":"2005-05-13T00:14:16Z","messageCount":19,"participants":["Morten Welinder","Junio C Hamano","Petr Baudis"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"2901","messageId":"118833cc05050911255e601fc@mail.gmail.com","threadId":"554","inReplyTo":null,"subject":"\"git-checkout-cache -f -a\" failure","fromName":"Morten Welinder","fromEmail":"mwelinder@gmail.com","sentAt":"2005-05-09T18:25:29Z","receivedAt":"2005-05-09T18:25:29Z","isPatch":false,"sender":{"key":"mwelinder@gmail.com","avatar":null},"body":"git-checkout-cache is having problems when files change from directories to\nplain files or vice versa.  cg-seek seems to be similarly affected.\n\nNote also, that  git-checkout-cache's error messages haven't caught up with\nthe command renaming.\n\nMorten\n\n\ncd /tmp\nmkdir xxx\ncd xxx\nrm -rf .git empty yyy \ntouch empty\ncg-init </dev/null\ncg-tag initial\nmkdir yyy\ntouch yyy/zzz\ncg-add yyy/zzz \ncg-commit </dev/null\ncg-tag dir\nrm -rf empty yyy\nrm -f .git/HEAD\ncat .git/refs/tags/initial > .git/HEAD \ngit-read-tree -m HEAD\ngit-checkout-cache -f -a\ntouch yyy\ncg-add yyy\ncg-commit </dev/null \ncg-tag file\n# Got that?\n\n# yyy is a file right now.\ngit-read-tree `cat .git/refs/tags/dir`\ngit-checkout-cache -f -a\n# error: checkout-cache: unable to create file yyy/zzz (Not a directory)\n# Not good!\n\nrm -rf empty yyy\ngit-read-tree `cat .git/refs/tags/dir`\ngit-checkout-cache -f -a\n\n# yyy is a directory right now\ngit-read-tree `cat .git/refs/tags/file`\ngit-checkout-cache -f -a\n# error: checkout-cache: unable to create file yyy (Is a directory)\n# Not good!\n"},{"id":"2943","messageId":"7v64xru83t.fsf@assigned-by-dhcp.cox.net","threadId":"554","inReplyTo":"118833cc05050911255e601fc@mail.gmail.com","subject":"Re: \"git-checkout-cache -f -a\" failure","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-10T02:29:10Z","receivedAt":"2005-05-10T02:29:10Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":">>>>> \"MW\" == Morten Welinder <mwelinder@gmail.com> writes:\n\nMW> git-checkout-cache is having problems when files change from directories to\nMW> plain files or vice versa.\n\nChanging files vs directories _is_ a big change and happens\nrarely in practice; I think the current behaviour is\njustified---it makes the user take notice and the user _should_\ntake notice.\n\nPorcelain layer, if it wanted to, should be able to hide this\nfrom the user, but it depends on which Porcelain layer command\nis involved.  My understanding of cg-seek is to switch the work\ntree to a completely different state, so in that case it\nprobably should hide this (still it makes me feel a bit nervous\nto realize that I am advocating for the tool to silently remove\na whole subtree to make room for a file, though).\n\n"},{"id":"2945","messageId":"118833cc05050920045204db03@mail.gmail.com","threadId":"554","inReplyTo":"7v64xru83t.fsf@assigned-by-dhcp.cox.net","subject":"Re: \"git-checkout-cache -f -a\" failure","fromName":"Morten Welinder","fromEmail":"mwelinder@gmail.com","sentAt":"2005-05-10T03:04:46Z","receivedAt":"2005-05-10T03:04:46Z","isPatch":false,"sender":{"key":"mwelinder@gmail.com","avatar":null},"body":"> Changing files vs directories _is_ a big change and happens\n> rarely in practice; I think the current behaviour is\n> justified---it makes the user take notice and the user _should_\n> take notice.\n\nFile vs. directory was just the easiest way to demonstrate.  In the presense\nof symlinks I am not sure you will always get a warning.  It'll be more of a\nsilent file-corrupting failure kind of thing.  (Somewhat worse if yyy points\nto /your/home/.ssh and zzz is \"authorized_keys\".)\n\nMorten\n"},{"id":"3006","messageId":"7vr7gewuxt.fsf@assigned-by-dhcp.cox.net","threadId":"554","inReplyTo":"118833cc05050911255e601fc@mail.gmail.com","subject":"Re: \"git-checkout-cache -f -a\" failure","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-10T22:57:34Z","receivedAt":"2005-05-10T22:57:34Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":">>>>> \"MW\" == Morten Welinder <mwelinder@gmail.com> writes:\n\nMW> git-checkout-cache is having problems when files change from directories to\nMW> plain files or vice versa.  cg-seek seems to be similarly affected.\n\nCould you give this patch a try?  It lets checkout-cache remove\na file where you want to have a directory (because you want to\ncreate something underneath) or remove a whole subdirectory\nwhere you want to have a non-directory, when '-f' parameter is\nspecified.\n\nIf things test well, I'll put this in the git-jc repository and\nask Linus to pull from it alongside with other accumulated\npatches when he returns.\n\nThanks.\n\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n------------\n# - HEAD: Adjust quoting styles for some environment variables in the documentation.\n# + 7: Let checkout-cache stomp on existing file/directory with -f.\n--- a/checkout-cache.c\n+++ b/checkout-cache.c\n@@ -32,6 +32,8 @@\n  * of \"-a\" causing problems (not possible in the above example,\n  * but get used to it in scripting!).\n  */\n+#include <sys/types.h>\n+#include <dirent.h>\n #include \"cache.h\"\n \n static int force = 0, quiet = 0, not_new = 0;\n@@ -46,11 +48,51 @@ static void create_directories(const cha\n \t\tlen = slash - path;\n \t\tmemcpy(buf, path, len);\n \t\tbuf[len] = 0;\n-\t\tmkdir(buf, 0755);\n+\t\tif (mkdir(buf, 0755)) {\n+\t\t\tif (errno == EEXIST) {\n+\t\t\t\tstruct stat st;\n+\t\t\t\tif (!lstat(buf, &st) && S_ISDIR(st.st_mode))\n+\t\t\t\t\tcontinue; /* ok */\n+\t\t\t\tif (force && !unlink(buf) && !mkdir(buf, 0755))\n+\t\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\tdie(\"cannot create directory at %s\", buf);\n+\t\t}\n \t}\n \tfree(buf);\n }\n \n+static void remove_subtree(const char *path)\n+{\n+\tDIR *dir = opendir(path);\n+\tstruct dirent *de;\n+\tchar pathbuf[PATH_MAX];\n+\tchar *name;\n+\t\n+\tif (!dir)\n+\t\tdie(\"cannot opendir %s\", path);\n+\tstrcpy(pathbuf, path);\n+\tname = pathbuf + strlen(path);\n+\t*name++ = '/';\n+\twhile ((de = readdir(dir)) != NULL) {\n+\t\tstruct stat st;\n+\t\tif (de->d_name[0] == '.')\n+\t\t\tcontinue; /* we mean . and .. but verify_path would\n+\t\t\t\t   * have rejected any dotfiles earlier.\n+\t\t\t\t   */\n+\t\tstrcpy(name, de->d_name);\n+\t\tif (lstat(pathbuf, &st))\n+\t\t\tdie(\"cannot lstat %s\", pathbuf);\n+\t\tif (S_ISDIR(st.st_mode))\n+\t\t\tremove_subtree(pathbuf);\n+\t\telse if (unlink(pathbuf))\n+\t\t\tdie(\"cannot unlink %s\", pathbuf);\n+\t}\n+\tclosedir(dir);\n+\tif (rmdir(path))\n+\t\tdie(\"cannot rmdir %s\", path);\n+}\n+\n static int create_file(const char *path, unsigned int mode)\n {\n \tint fd;\n@@ -62,6 +104,14 @@ static int create_file(const char *path,\n \t\t\tcreate_directories(path);\n \t\t\tfd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);\n \t\t}\n+\t\telse if (errno == EISDIR && force) {\n+\t\t\tremove_subtree(path);\n+\t\t\tfd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);\n+\t\t}\n+\t\telse if (errno == ENOTDIR && force) {\n+\t\t\tcreate_directories(path);\n+\t\t\tfd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);\n+\t\t}\n \t}\n \treturn fd;\n }\n\n\n\n"},{"id":"3009","messageId":"20050510230357.GF26384@pasky.ji.cz","threadId":"554","inReplyTo":"7vr7gewuxt.fsf@assigned-by-dhcp.cox.net","subject":"Re: \"git-checkout-cache -f -a\" failure","fromName":"Petr Baudis","fromEmail":"pasky@ucw.cz","sentAt":"2005-05-10T23:03:58Z","receivedAt":"2005-05-10T23:03:58Z","isPatch":false,"sender":{"key":"pasky@ucw.cz","avatar":"https://avatars.githubusercontent.com/u/18439?v=4"},"body":"Dear diary, on Wed, May 11, 2005 at 12:57:34AM CEST, I got a letter\nwhere Junio C Hamano <junkio@cox.net> told me that...\n> >>>>> \"MW\" == Morten Welinder <mwelinder@gmail.com> writes:\n> \n> MW> git-checkout-cache is having problems when files change from directories to\n> MW> plain files or vice versa.  cg-seek seems to be similarly affected.\n> \n> Could you give this patch a try?  It lets checkout-cache remove\n> a file where you want to have a directory (because you want to\n> create something underneath) or remove a whole subdirectory\n> where you want to have a non-directory, when '-f' parameter is\n> specified.\n> \n> If things test well, I'll put this in the git-jc repository and\n> ask Linus to pull from it alongside with other accumulated\n> patches when he returns.\n\nChilling.\n\nWhat if you have some files not tracked by git in the subdirectory?\nEither you need to check for this and deal with it (Cogito's approach\nwould be to remove the git-tracked files and rename the subdirectory),\nor not do it at all. Or at least please use -F or something if you\nreally want this and like to gamble.\n\n-- \n\t\t\t\tPetr \"Pasky\" Baudis\nStuff: http://pasky.or.cz/\nC++: an octopus made by nailing extra legs onto a dog. -- Steve Taylor\n"},{"id":"3026","messageId":"7vpsvyv5gp.fsf@assigned-by-dhcp.cox.net","threadId":"554","inReplyTo":"7vr7gewuxt.fsf@assigned-by-dhcp.cox.net","subject":"Re: \"git-checkout-cache -f -a\" failure","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-11T02:53:10Z","receivedAt":"2005-05-11T02:53:10Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Please forget about the previous one which was buggy when you\nhad \".something\" file under the directory being replaced.\n\nCould you give this patch a try?\n\n---\n\nRunning git-checkout-cache -f -a to check out a file in a\ndirectory fails when the work tree has a non-directory with the\nsame name as the directory being checked out.  Similarly it\nfails to check out a file when the work tree has a directory\nwith the same name as the file being checked out.\n\nWhen '-f' is specified, the user is telling us that \"I really\nwant to match the work tree with what is in the cache.\"  So\nremoving the offending file or directory from the work tree to\nmake room when necessary and possible in order to make the\ncheckout succeed is the right thing to do.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n\n--- a/checkout-cache.c\n+++ b/checkout-cache.c\n@@ -32,6 +32,8 @@\n  * of \"-a\" causing problems (not possible in the above example,\n  * but get used to it in scripting!).\n  */\n+#include <sys/types.h>\n+#include <dirent.h>\n #include \"cache.h\"\n \n static int force = 0, quiet = 0, not_new = 0;\n@@ -46,11 +48,51 @@ static void create_directories(const cha\n \t\tlen = slash - path;\n \t\tmemcpy(buf, path, len);\n \t\tbuf[len] = 0;\n-\t\tmkdir(buf, 0755);\n+\t\tif (mkdir(buf, 0755)) {\n+\t\t\tif (errno == EEXIST) {\n+\t\t\t\tstruct stat st;\n+\t\t\t\tif (!lstat(buf, &st) && S_ISDIR(st.st_mode))\n+\t\t\t\t\tcontinue; /* ok */\n+\t\t\t\tif (force && !unlink(buf) && !mkdir(buf, 0755))\n+\t\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\tdie(\"cannot create directory at %s\", buf);\n+\t\t}\n \t}\n \tfree(buf);\n }\n \n+static void remove_subtree(const char *path)\n+{\n+\tDIR *dir = opendir(path);\n+\tstruct dirent *de;\n+\tchar pathbuf[PATH_MAX];\n+\tchar *name;\n+\t\n+\tif (!dir)\n+\t\tdie(\"cannot opendir %s\", path);\n+\tstrcpy(pathbuf, path);\n+\tname = pathbuf + strlen(path);\n+\t*name++ = '/';\n+\twhile ((de = readdir(dir)) != NULL) {\n+\t\tstruct stat st;\n+\t\tif ((de->d_name[0] == '.') &&\n+\t\t    ((de->d_name[1] == 0) ||\n+\t\t     (de->d_name[1] == '.') && de->d_name[2] == 0))\n+\t\t\tcontinue;\n+\t\tstrcpy(name, de->d_name);\n+\t\tif (lstat(pathbuf, &st))\n+\t\t\tdie(\"cannot lstat %s\", pathbuf);\n+\t\tif (S_ISDIR(st.st_mode))\n+\t\t\tremove_subtree(pathbuf);\n+\t\telse if (unlink(pathbuf))\n+\t\t\tdie(\"cannot unlink %s\", pathbuf);\n+\t}\n+\tclosedir(dir);\n+\tif (rmdir(path))\n+\t\tdie(\"cannot rmdir %s\", path);\n+}\n+\n static int create_file(const char *path, unsigned int mode)\n {\n \tint fd;\n@@ -58,10 +100,14 @@ static int create_file(const char *path,\n \tmode = (mode & 0100) ? 0777 : 0666;\n \tfd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);\n \tif (fd < 0) {\n-\t\tif (errno == ENOENT) {\n+\t\tif ((errno == ENOENT) || (errno == ENOTDIR && force)) {\n \t\t\tcreate_directories(path);\n \t\t\tfd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);\n \t\t}\n+\t\telse if (errno == EISDIR && force) {\n+\t\t\tremove_subtree(path);\n+\t\t\tfd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);\n+\t\t}\n \t}\n \treturn fd;\n }\n------------------------------------------------\n\n"},{"id":"3030","messageId":"7vsm0us5p5.fsf@assigned-by-dhcp.cox.net","threadId":"554","inReplyTo":"20050510230357.GF26384@pasky.ji.cz","subject":"Re: \"git-checkout-cache -f -a\" failure","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-11T05:16:22Z","receivedAt":"2005-05-11T05:16:22Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":">>>>> \"PB\" == Petr Baudis <pasky@ucw.cz> writes:\n\nPB> What if you have some files not tracked by git in the subdirectory?\nPB> Either you need to check for this and deal with it (Cogito's approach\nPB> would be to remove the git-tracked files and rename the subdirectory),\nPB> or not do it at all.\n\nLet me understand you correctly.  You \"rename the subdirectory\"\nbecause the cache you want to check out records a non-directory\nthere.  Is this correct?  When do you \"remove the git-tracked\nfiles\"?  Do you mean \"if a file that is not tracked (in the\nuser's mind) is there in the filesystem, and the cache to be\nchecked out has a file there, the file on the filesystem is\nremoved\"?  What happens if an existing non-directory interferes\nwith a directory to be checked out from the cache?\n\nI think the right thing for the core GIT layer to do, when the\nend user is using the Plumbing tools directly and says \"really\nplease\" by specifying an '-f', is to make the checkout succeed\nby removing conflicting stuff, as my fixed patch does.\n\nIf the Porcelain runs checkout-cache on the user's behalf, the\nstory is a bit different.  Porcelain would have some idea of\nwhat GIT tree the files in the working directory originated from\n(\"the last commit\", or \"my head before the read-tree -m merge\")\nbefore the user started working in it.  When the user wants to\nupdate the work tree to match a cache (which may be quite\ndifferent from \"the last commit\", if it is coming from cg-seek\nor result of a merge), the Porcelain knows:\n\n - the set of non git-tracked (from the point of view from \"the\n   last commit\") files that are in the work tree;\n\n - among those, the set of files that would be overwritten by\n   this checkout.  This includes, but not limited to, the files\n   that would otherwise interfere because of file-directory\n   conflicts.\n\nAnd then you can set aside those \"to-be-overwritten\" files,\nremove them from the filesystem, and run your checkout.\n\nThe determination of which files to set aside should not be done\nbased on whether they cause file-directory conflicts.  If the\nuser has a non-tracked file which happens to be registered as a\ntracked file in the cache to be checked out (e.g. after a\nthree-way merge), that file is as valuable as a non-tracked file\nthat is in a subdirectory that the new cache happens to have as\na non-directory.  Losing the former is as harmful as losing the\nlatter.  From what you say above, it sounds like you are saving\nthe latter but not the former, but my readins of what you wrote\nabove may be mistaken.\n\n"},{"id":"3055","messageId":"118833cc050511113122e2d17d@mail.gmail.com","threadId":"554","inReplyTo":"7vsm0us5p5.fsf@assigned-by-dhcp.cox.net","subject":"Re: \"git-checkout-cache -f -a\" failure","fromName":"Morten Welinder","fromEmail":"mwelinder@gmail.com","sentAt":"2005-05-11T18:31:48Z","receivedAt":"2005-05-11T18:31:48Z","isPatch":false,"sender":{"key":"mwelinder@gmail.com","avatar":null},"body":"Here's the symlink version.  Note, that git does not complain but\nsimply creates (or\noverwrites) the wrong file.\n\nMorten\n\n\ncd /tmp\nmkdir xxx\ncd xxx\nrm -rf .git dir yyy\nmkdir dir\ntouch dir/empty\ncg-init </dev/null\ncg-tag initial\nmkdir yyy\ntouch yyy/zzz\ncg-add yyy/zzz\ncg-commit </dev/null\ncg-tag dir\nrm -rf yyy\nrm -f .git/HEAD\ncat .git/refs/tags/initial > .git/HEAD\ngit-read-tree -m HEAD\ngit-checkout-cache -f -a\nln -s dir yyy\ngit-update-cache --add -- yyy\ncg-commit </dev/null\ncg-tag symlink\n# Got that?\n\n# yyy is a symlink right now.\ngit-read-tree `cat .git/refs/tags/dir`\ngit-checkout-cache -f -a\nls -l dir\ntotal 0\n-rw-rw-r--    1 welinder research        0 May 11 14:20 empty\n-rw-rw-r--    1 welinder research        0 May 11 14:26 zzz\n# dir/zzz should not exist at ths point!\n"},{"id":"3065","messageId":"7vpsvxqwab.fsf@assigned-by-dhcp.cox.net","threadId":"554","inReplyTo":"118833cc050511113122e2d17d@mail.gmail.com","subject":"Re: \"git-checkout-cache -f -a\" failure","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-11T21:37:16Z","receivedAt":"2005-05-11T21:37:16Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":">>>>> \"MW\" == Morten Welinder <mwelinder@gmail.com> writes:\n\nMW> Here's the symlink version.  Note, that git does not complain but\nMW> simply creates (or\nMW> overwrites) the wrong file.\n\nMW> Morten\n\nHere is a proposed fix.\n\n------------\nCommit    685c391c9a755936175193f8b58973b74eaef930\nAuthor    Junio C Hamano <junkio@cox.net>, Wed May 11 14:36:05 2005 -0700\nCommitter Junio C Hamano <junkio@cox.net>, Wed May 11 14:36:05 2005 -0700\n\nFix checkout-cache when existing work tree interferes with the checkout.\n\nThe checkout-cache command gets confused when checking out a\nfile in a subdirectory and the work tree has a symlink to the\nsubdirectory.  Also it fails to check things out when there is a\nnon-directory in the work tree when cache expects a directory\nthere, and vice versa.  This patch fixes the first problem by\nmaking sure all the leading paths in the file being checked out\nare indeed directories, and also fixes directory vs\nnon-directory conflicts when '-f' is specified by removing the\noffending paths.\n\nI've added test subdirectory and two tests to check the above\nproblems are fixed.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n\n--- a/checkout-cache.c\n+++ b/checkout-cache.c\n@@ -32,6 +32,8 @@\n  * of \"-a\" causing problems (not possible in the above example,\n  * but get used to it in scripting!).\n  */\n+#include <sys/types.h>\n+#include <dirent.h>\n #include \"cache.h\"\n \n static int force = 0, quiet = 0, not_new = 0;\n@@ -46,22 +48,67 @@ static void create_directories(const cha\n \t\tlen = slash - path;\n \t\tmemcpy(buf, path, len);\n \t\tbuf[len] = 0;\n-\t\tmkdir(buf, 0755);\n+\t\tif (mkdir(buf, 0755)) {\n+\t\t\tif (errno == EEXIST) {\n+\t\t\t\tstruct stat st;\n+\t\t\t\tif (!lstat(buf, &st) && S_ISDIR(st.st_mode))\n+\t\t\t\t\tcontinue; /* ok */\n+\t\t\t\tif (force && !unlink(buf) && !mkdir(buf, 0755))\n+\t\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\tdie(\"cannot create directory at %s\", buf);\n+\t\t}\n \t}\n \tfree(buf);\n }\n \n+static void remove_subtree(const char *path)\n+{\n+\tDIR *dir = opendir(path);\n+\tstruct dirent *de;\n+\tchar pathbuf[PATH_MAX];\n+\tchar *name;\n+\t\n+\tif (!dir)\n+\t\tdie(\"cannot opendir %s\", path);\n+\tstrcpy(pathbuf, path);\n+\tname = pathbuf + strlen(path);\n+\t*name++ = '/';\n+\twhile ((de = readdir(dir)) != NULL) {\n+\t\tstruct stat st;\n+\t\tif ((de->d_name[0] == '.') &&\n+\t\t    ((de->d_name[1] == 0) ||\n+\t\t     ((de->d_name[1] == '.') && de->d_name[2] == 0)))\n+\t\t\tcontinue;\n+\t\tstrcpy(name, de->d_name);\n+\t\tif (lstat(pathbuf, &st))\n+\t\t\tdie(\"cannot lstat %s\", pathbuf);\n+\t\tif (S_ISDIR(st.st_mode))\n+\t\t\tremove_subtree(pathbuf);\n+\t\telse if (unlink(pathbuf))\n+\t\t\tdie(\"cannot unlink %s\", pathbuf);\n+\t}\n+\tclosedir(dir);\n+\tif (rmdir(path))\n+\t\tdie(\"cannot rmdir %s\", path);\n+}\n+\n static int create_file(const char *path, unsigned int mode)\n {\n \tint fd;\n \n \tmode = (mode & 0100) ? 0777 : 0666;\n+\tcreate_directories(path);\n \tfd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);\n \tif (fd < 0) {\n-\t\tif (errno == ENOENT) {\n+\t\tif ((errno == ENOENT) || (errno == ENOTDIR && force)) {\n \t\t\tcreate_directories(path);\n \t\t\tfd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);\n \t\t}\n+\t\telse if (errno == EISDIR && force) {\n+\t\t\tremove_subtree(path);\n+\t\t\tfd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);\n+\t\t}\n \t}\n \treturn fd;\n }\nCreated: t/t0000.sh (mode:100755)\n--- /dev/null\n+++ b/t/t0000.sh\n@@ -0,0 +1,35 @@\n+#!/bin/sh\n+\n+case \"${verbose+set}\" in\n+set)\tsay= ;;\n+*)\tsay=: ;;\n+esac\n+\n+export LANG C\n+unset AUTHOR_DATE\n+unset AUTHOR_EMAIL\n+unset AUTHOR_NAME\n+unset COMMIT_AUTHOR_EMAIL\n+unset COMMIT_AUTHOR_NAME\n+unset GIT_ALTERNATE_OBJECT_DIRECTORIES\n+unset GIT_AUTHOR_DATE\n+unset GIT_AUTHOR_EMAIL\n+unset GIT_AUTHOR_NAME\n+unset GIT_COMMITTER_EMAIL\n+unset GIT_COMMITTER_NAME\n+unset GIT_DIFF_OPTS\n+unset GIT_DIR\n+unset GIT_EXTERNAL_DIFF\n+unset GIT_INDEX_FILE\n+unset GIT_OBJECT_DIRECTORY\n+unset SHA1_FILE_DIRECTORIES\n+unset SHA1_FILE_DIRECTORY\n+\n+# Test the binaries we have just built.\n+PATH=$(pwd)/..:$PATH\n+\n+# Test repository\n+test=test-repo\n+rm -fr \"$test\"\n+mkdir \"$test\"\n+cd \"$test\"\nCreated: t/t1000-checkout-cache.sh (mode:100755)\n--- /dev/null\n+++ b/t/t1000-checkout-cache.sh\n@@ -0,0 +1,39 @@\n+#!/bin/sh\n+\n+. ./t0000.sh\n+git-init-db 2>/dev/null || exit\n+date >path0\n+mkdir path1\n+date >path1/file1\n+git-update-cache --add path0 path1/file1\n+$say git-ls-files --stage\n+\n+rm -fr path0 path1\n+mkdir path0\n+date >path0/file0\n+date >path1\n+$say git-ls-files --stage\n+$say find path*\n+\n+echo >&2 \"* checkout-cache sans -f\"\n+git-checkout-cache -a\n+case \"$?\" in\n+0)\techo >&2 \"*** bug: should not have succeeded.\" ;;\n+*)\techo >&2 \"*** ok: failed as expected.\" ;;\n+esac\n+$say find path*\n+\n+echo >&2 \"* checkout-cache with -f\"\n+git-checkout-cache -f -a\n+case \"$?\" in\n+0)\techo >&2 \"*** ok: succeeded as expected.\" ;;\n+*)\techo >&2 \"*** bug: should have succeeded.\" ;;\n+esac\n+$say find path*\n+if test -f path0 && test -d path1 && test -f path1/file1\n+then\n+\techo >&2 \"*** ok: checked out correctly.\"\n+else\n+\techo >&2 \"*** bug: checkout failed.\"\n+\texit 1\n+fi\nCreated: t/t1001-checkout-cache.sh (mode:100755)\n--- /dev/null\n+++ b/t/t1001-checkout-cache.sh\n@@ -0,0 +1,61 @@\n+#!/bin/sh\n+\n+. ./t0000.sh\n+git-init-db 2>/dev/null || exit\n+\n+show_files() {\n+\tfind path? -ls |\n+\tsed -e 's/^[0-9]* * [0-9]* * \\([-bcdl]\\)[^ ]* *[0-9]* *[^ ]* *[^ ]* *[0-9]* [A-Z][a-z][a-z] [0-9][0-9] [^ ]* /fs: \\1 /'\n+\tgit-ls-files --stage |\n+\tsed -e 's/^\\([0-9]*\\) [0-9a-f]* [0-3] /ca: \\1 /'\n+\tgit-ls-tree -r \"$1\" |\n+\tsed -e 's/^\\([0-9]*\\)\t[^ ]*\t[0-9a-f]*\t/tr: \\1 /'\n+}\n+\n+mkdir path0\n+date >path0/file0\n+git-update-cache --add path0/file0\n+echo >&2 \"* initial state: one file under one directory\"\n+tree1=$(git-write-tree)\n+$say show_files $tree1\n+\n+mkdir path1\n+date >path1/file1\n+git-update-cache --add path1/file1\n+echo >&2 \"* two directories with one file each\"\n+tree2=$(git-write-tree)\n+$say show_files $tree2\n+\n+rm -fr path1\n+git-read-tree -m $tree1\n+git-checkout-cache -f -a\n+echo >&2 \"* go back to initial state\"\n+$say show_files $tree1\n+\n+ln -s path0 path1\n+git-update-cache --add path1\n+echo >&2 \"* a symlink where the other side would create a directory.\"\n+tree3=$(git-write-tree)\n+$say show_files $tree3\n+\n+# Morten says \"Got that?\" here.\n+\n+git-read-tree $tree2\n+git-checkout-cache -f -a\n+case \"$?\" in\n+0)\techo >&2 \"*** ok: succeeded as expected.\" ;;\n+*)\techo >&2 \"*** bug: should have succeeded.\" ;;\n+esac\n+echo >&2 \"* read tree2 and checkout\"\n+$say show_files $tree2\n+\n+if test ! -h path0 && test -d path0 &&\n+   test ! -h path1 && test -d path1 &&\n+   test ! -h path0/file0 && test -f path0/file0 &&\n+   test ! -h path1/file1 && test -f path1/file1\n+then\n+    echo >&2 \"*** ok: checked out correctly.\"\n+else\n+    echo >&2 \"*** bug: did not check out correctly.\"\n+    exit 1\n+fi\n------------------------------------------------\n\n"},{"id":"3070","messageId":"7vd5rxquo5.fsf@assigned-by-dhcp.cox.net","threadId":"554","inReplyTo":"7vpsvxqwab.fsf@assigned-by-dhcp.cox.net","subject":"Re: \"git-checkout-cache -f -a\" failure","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-11T22:12:10Z","receivedAt":"2005-05-11T22:12:10Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Here is a re-diff against the current tip of the git-pb tree.\n\n------------\nFix checkout-cache when existing work tree interferes with the checkout.\n\nThis is essentially the same one as the last one I sent to the\nGIT list, except that the patch is rebased to the current tip of\nthe git-pb tree, and an unnecessary call to create_directories()\nremoved.\n\nThe checkout-cache command gets confused when checking out a\nfile in a subdirectory and the work tree has a symlink to the\nsubdirectory.  Also it fails to check things out when there is a\nnon-directory in the work tree when cache expects a directory\nthere, and vice versa.  This patch fixes the first problem by\nmaking sure all the leading paths in the file being checked out\nare indeed directories, and also fixes directory vs\nnon-directory conflicts when '-f' is specified by removing the\noffending paths.\n\nI've added test subdirectory and two tests to check the above\nproblems are fixed.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\ncheckout-cache.c          |   49 +++++++++++++++++++++++++++++++++--\nt/t0000.sh                |   38 +++++++++++++++++++++++++++\nt/t1000-checkout-cache.sh |   42 ++++++++++++++++++++++++++++++\nt/t1001-checkout-cache.sh |   64 ++++++++++++++++++++++++++++++++++++++++++++++\n4 files changed, 190 insertions(+), 3 deletions(-)\n\n--- a/checkout-cache.c\n+++ b/checkout-cache.c\n@@ -32,6 +32,8 @@\n  * of \"-a\" causing problems (not possible in the above example,\n  * but get used to it in scripting!).\n  */\n+#include <sys/types.h>\n+#include <dirent.h>\n #include \"cache.h\"\n \n static int force = 0, quiet = 0, not_new = 0;\n@@ -46,20 +48,61 @@ static void create_directories(const cha\n \t\tlen = slash - path;\n \t\tmemcpy(buf, path, len);\n \t\tbuf[len] = 0;\n-\t\tmkdir(buf, 0755);\n+\t\tif (mkdir(buf, 0755)) {\n+\t\t\tif (errno == EEXIST) {\n+\t\t\t\tstruct stat st;\n+\t\t\t\tif (!lstat(buf, &st) && S_ISDIR(st.st_mode))\n+\t\t\t\t\tcontinue; /* ok */\n+\t\t\t\tif (force && !unlink(buf) && !mkdir(buf, 0755))\n+\t\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\tdie(\"cannot create directory at %s\", buf);\n+\t\t}\n \t}\n \tfree(buf);\n }\n \n+static void remove_subtree(const char *path)\n+{\n+\tDIR *dir = opendir(path);\n+\tstruct dirent *de;\n+\tchar pathbuf[PATH_MAX];\n+\tchar *name;\n+\t\n+\tif (!dir)\n+\t\tdie(\"cannot opendir %s\", path);\n+\tstrcpy(pathbuf, path);\n+\tname = pathbuf + strlen(path);\n+\t*name++ = '/';\n+\twhile ((de = readdir(dir)) != NULL) {\n+\t\tstruct stat st;\n+\t\tif ((de->d_name[0] == '.') &&\n+\t\t    ((de->d_name[1] == 0) ||\n+\t\t     ((de->d_name[1] == '.') && de->d_name[2] == 0)))\n+\t\t\tcontinue;\n+\t\tstrcpy(name, de->d_name);\n+\t\tif (lstat(pathbuf, &st))\n+\t\t\tdie(\"cannot lstat %s\", pathbuf);\n+\t\tif (S_ISDIR(st.st_mode))\n+\t\t\tremove_subtree(pathbuf);\n+\t\telse if (unlink(pathbuf))\n+\t\t\tdie(\"cannot unlink %s\", pathbuf);\n+\t}\n+\tclosedir(dir);\n+\tif (rmdir(path))\n+\t\tdie(\"cannot rmdir %s\", path);\n+}\n+\n static int create_file(const char *path, unsigned int mode)\n {\n \tint fd;\n \n \tmode = (mode & 0100) ? 0777 : 0666;\n+\tcreate_directories(path);\n \tfd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);\n \tif (fd < 0) {\n-\t\tif (errno == ENOENT) {\n-\t\t\tcreate_directories(path);\n+\t\tif (errno == EISDIR && force) {\n+\t\t\tremove_subtree(path);\n \t\t\tfd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);\n \t\t}\n \t}\nCreated: t/t0000.sh (mode:100755)\n--- /dev/null\n+++ b/t/t0000.sh\n@@ -0,0 +1,38 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2005 Junio C Hamano\n+#\n+\n+case \"${verbose+set}\" in\n+set)\tsay= ;;\n+*)\tsay=: ;;\n+esac\n+\n+export LANG C\n+unset AUTHOR_DATE\n+unset AUTHOR_EMAIL\n+unset AUTHOR_NAME\n+unset COMMIT_AUTHOR_EMAIL\n+unset COMMIT_AUTHOR_NAME\n+unset GIT_ALTERNATE_OBJECT_DIRECTORIES\n+unset GIT_AUTHOR_DATE\n+unset GIT_AUTHOR_EMAIL\n+unset GIT_AUTHOR_NAME\n+unset GIT_COMMITTER_EMAIL\n+unset GIT_COMMITTER_NAME\n+unset GIT_DIFF_OPTS\n+unset GIT_DIR\n+unset GIT_EXTERNAL_DIFF\n+unset GIT_INDEX_FILE\n+unset GIT_OBJECT_DIRECTORY\n+unset SHA1_FILE_DIRECTORIES\n+unset SHA1_FILE_DIRECTORY\n+\n+# Test the binaries we have just built.\n+PATH=$(pwd)/..:$PATH\n+\n+# Test repository\n+test=test-repo\n+rm -fr \"$test\"\n+mkdir \"$test\"\n+cd \"$test\"\nCreated: t/t1000-checkout-cache.sh (mode:100755)\n--- /dev/null\n+++ b/t/t1000-checkout-cache.sh\n@@ -0,0 +1,42 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2005 Junio C Hamano\n+#\n+\n+. ./t0000.sh\n+git-init-db 2>/dev/null || exit\n+date >path0\n+mkdir path1\n+date >path1/file1\n+git-update-cache --add path0 path1/file1\n+$say git-ls-files --stage\n+\n+rm -fr path0 path1\n+mkdir path0\n+date >path0/file0\n+date >path1\n+$say git-ls-files --stage\n+$say find path*\n+\n+echo >&2 \"* checkout-cache sans -f\"\n+git-checkout-cache -a\n+case \"$?\" in\n+0)\techo >&2 \"*** bug: should not have succeeded.\" ;;\n+*)\techo >&2 \"*** ok: failed as expected.\" ;;\n+esac\n+$say find path*\n+\n+echo >&2 \"* checkout-cache with -f\"\n+git-checkout-cache -f -a\n+case \"$?\" in\n+0)\techo >&2 \"*** ok: succeeded as expected.\" ;;\n+*)\techo >&2 \"*** bug: should have succeeded.\" ;;\n+esac\n+$say find path*\n+if test -f path0 && test -d path1 && test -f path1/file1\n+then\n+\techo >&2 \"*** ok: checked out correctly.\"\n+else\n+\techo >&2 \"*** bug: checkout failed.\"\n+\texit 1\n+fi\nCreated: t/t1001-checkout-cache.sh (mode:100755)\n--- /dev/null\n+++ b/t/t1001-checkout-cache.sh\n@@ -0,0 +1,64 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2005 Junio C Hamano\n+#\n+\n+. ./t0000.sh\n+git-init-db 2>/dev/null || exit\n+\n+show_files() {\n+\tfind path? -ls |\n+\tsed -e 's/^[0-9]* * [0-9]* * \\([-bcdl]\\)[^ ]* *[0-9]* *[^ ]* *[^ ]* *[0-9]* [A-Z][a-z][a-z] [0-9][0-9] [^ ]* /fs: \\1 /'\n+\tgit-ls-files --stage |\n+\tsed -e 's/^\\([0-9]*\\) [0-9a-f]* [0-3] /ca: \\1 /'\n+\tgit-ls-tree -r \"$1\" |\n+\tsed -e 's/^\\([0-9]*\\)\t[^ ]*\t[0-9a-f]*\t/tr: \\1 /'\n+}\n+\n+mkdir path0\n+date >path0/file0\n+git-update-cache --add path0/file0\n+echo >&2 \"* initial state: one file under one directory\"\n+tree1=$(git-write-tree)\n+$say show_files $tree1\n+\n+mkdir path1\n+date >path1/file1\n+git-update-cache --add path1/file1\n+echo >&2 \"* two directories with one file each\"\n+tree2=$(git-write-tree)\n+$say show_files $tree2\n+\n+rm -fr path1\n+git-read-tree -m $tree1\n+git-checkout-cache -f -a\n+echo >&2 \"* go back to initial state\"\n+$say show_files $tree1\n+\n+ln -s path0 path1\n+git-update-cache --add path1\n+echo >&2 \"* a symlink where the other side would create a directory.\"\n+tree3=$(git-write-tree)\n+$say show_files $tree3\n+\n+# Morten says \"Got that?\" here.\n+\n+git-read-tree $tree2\n+git-checkout-cache -f -a\n+case \"$?\" in\n+0)\techo >&2 \"*** ok: succeeded as expected.\" ;;\n+*)\techo >&2 \"*** bug: should have succeeded.\" ;;\n+esac\n+echo >&2 \"* read tree2 and checkout\"\n+$say show_files $tree2\n+\n+if test ! -h path0 && test -d path0 &&\n+   test ! -h path1 && test -d path1 &&\n+   test ! -h path0/file0 && test -f path0/file0 &&\n+   test ! -h path1/file1 && test -f path1/file1\n+then\n+    echo >&2 \"*** ok: checked out correctly.\"\n+else\n+    echo >&2 \"*** bug: did not check out correctly.\"\n+    exit 1\n+fi\n------------------------------------------------\n\n"},{"id":"3076","messageId":"20050511224044.GI22686@pasky.ji.cz","threadId":"554","inReplyTo":"7vd5rxquo5.fsf@assigned-by-dhcp.cox.net","subject":"Test suite","fromName":"Petr Baudis","fromEmail":"pasky@ucw.cz","sentAt":"2005-05-11T22:40:44Z","receivedAt":"2005-05-11T22:40:44Z","isPatch":false,"sender":{"key":"pasky@ucw.cz","avatar":"https://avatars.githubusercontent.com/u/18439?v=4"},"body":"Dear diary, on Thu, May 12, 2005 at 12:12:10AM CEST, I got a letter\nwhere Junio C Hamano <junkio@cox.net> told me that...\n> I've added test subdirectory and two tests to check the above\n> problems are fixed.\n\nI'd prefer introduction of the infrastructure for this in a separate\ncommit. I'll focus on it here; I will have a look at the checkout-cache\nstuff tomorrow.\n\n> Created: t/t0000.sh (mode:100755)\n> --- /dev/null\n> +++ b/t/t0000.sh\n\nI'd prefer some better name for this. What about testlib.sh?\n\n> @@ -0,0 +1,38 @@\n> +#!/bin/sh\n> +#\n> +# Copyright (c) 2005 Junio C Hamano\n> +#\n\nA short description here would be nice.\n\n> +case \"${verbose+set}\" in\n> +set)\tsay= ;;\n> +*)\tsay=: ;;\n> +esac\n\nPerhaps set $verbose if -v was passed or something.\n\nLooking at how you use it, I wouldn't run the command at all if not\nverbose.\n\n> Created: t/t1000-checkout-cache.sh (mode:100755)\n> --- /dev/null\n> +++ b/t/t1000-checkout-cache.sh\n> @@ -0,0 +1,42 @@\n> +#!/bin/sh\n> +#\n> +# Copyright (c) 2005 Junio C Hamano\n> +#\n\nI think each test in the suite should have a short summary of its focus\nin the top comment.\n\n> +. ./t0000.sh\n> +git-init-db 2>/dev/null || exit\n\nI'd do this in testlib.sh (you might well blast it away and redo it when\ntesting init-db).\n\n> +echo >&2 \"* checkout-cache sans -f\"\n> +git-checkout-cache -a\n> +case \"$?\" in\n> +0)\techo >&2 \"*** bug: should not have succeeded.\" ;;\n> +*)\techo >&2 \"*** ok: failed as expected.\" ;;\n> +esac\n\nAnd this essentially repeats all over and over.\n\nWhat about something like\n\ncmdtest \"sans -f\" git-checkout-cache -a\n\n> +if test -f path0 && test -d path1 && test -f path1/file1\n> +then\n> +\techo >&2 \"*** ok: checked out correctly.\"\n> +else\n> +\techo >&2 \"*** bug: checkout failed.\"\n> +\texit 1\n> +fi\n\nWe might get a good function for this later too, for now I'd propose\n\nif test -f path0 && test -d path1 && test -f path1/file1; then\n\ttestok \"checkout\"\nelse\n\ttestfail \"checkout\" # non-fatal test failure\n\ttestfatal \"checkout\" # fatal test failure, die right away\nfi\n\n\nAnd at the end the script, you should do\n\nexit $teststatus\n\nwhich is set to non-zero anytime testfail was invoked.\n\n> Created: t/t1001-checkout-cache.sh (mode:100755)\n> --- /dev/null\n> +++ b/t/t1001-checkout-cache.sh\n> @@ -0,0 +1,64 @@\n> +show_files() {\n> +\tfind path? -ls |\n> +\tsed -e 's/^[0-9]* * [0-9]* * \\([-bcdl]\\)[^ ]* *[0-9]* *[^ ]* *[^ ]* *[0-9]* [A-Z][a-z][a-z] [0-9][0-9] [^ ]* /fs: \\1 /'\n> +\tgit-ls-files --stage |\n> +\tsed -e 's/^\\([0-9]*\\) [0-9a-f]* [0-3] /ca: \\1 /'\n> +\tgit-ls-tree -r \"$1\" |\n> +\tsed -e 's/^\\([0-9]*\\)\t[^ ]*\t[0-9a-f]*\t/tr: \\1 /'\n> +}\n\nUgh. I think a comment would be suitable here. ;-)\n\n> +mkdir path0\n> +date >path0/file0\n> +git-update-cache --add path0/file0\n> +echo >&2 \"* initial state: one file under one directory\"\n> +tree1=$(git-write-tree)\n\ncmdtest --capture-stdout \"one file under one directory\" git-write-tree\ntree1=$(cat $teststdout)\n\n-- \n\t\t\t\tPetr \"Pasky\" Baudis\nStuff: http://pasky.or.cz/\nC++: an octopus made by nailing extra legs onto a dog. -- Steve Taylor\n"},{"id":"3090","messageId":"7vu0l9nwgx.fsf_-_@assigned-by-dhcp.cox.net","threadId":"554","inReplyTo":"20050511224044.GI22686@pasky.ji.cz","subject":"[PATCH] Test suite","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-12T00:01:34Z","receivedAt":"2005-05-12T00:01:34Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Commit    1da683e1247046796a094c4917bc0c4591530272\nAuthor    Junio C Hamano <junkio@cox.net>, Wed May 11 16:59:35 2005 -0700\nCommitter Junio C Hamano <junkio@cox.net>, Wed May 11 16:59:35 2005 -0700\n\nTest suite: infrastructure and examples.\n\nThis adds the test suite infrastructure with two example tests.\nThe current git-checkout-cache the example tests would fail this\ntest and will be corrected in a separate patch.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\nCreated: t/t1000-checkout-cache.sh (mode:100755)\n--- /dev/null\n+++ b/t/t1000-checkout-cache.sh\n@@ -0,0 +1,54 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2005 Junio C Hamano\n+#\n+\n+. ./test-lib.sh\n+test_description \"$@\" 'git-checkout-cache test.\n+\n+This test registers the following filesystem structure in the\n+cache:\n+\n+    path0       - a file\n+    path1/file1 - a file in a directory\n+\n+And then tries to checkout in a work tree that has the following:\n+\n+    path0/file0 - a file in a directory\n+    path1       - a file\n+\n+The git-checkout-cache command should fail when attempting to checkout\n+path0, finding it is occupied by a directory, and path1/file1, finding\n+path1 is occupied by a non-directory.  With \"-f\" flag, it should remove\n+the conflicting paths and succeed.\n+'\n+\n+date >path0\n+mkdir path1\n+date >path1/file1\n+git-update-cache --add path0 path1/file1\n+test_debug 'git-ls-files --stage'\n+\n+rm -fr path0 path1\n+mkdir path0\n+date >path0/file0\n+date >path1\n+test_debug 'git-ls-files --stage'\n+test_debug 'find path*'\n+\n+test_expect_failure 'git-checkout-cache -a'\n+test_debug 'find path*'\n+\n+test_expect_success 'git-checkout-cache -f -a'\n+test_debug 'find path*'\n+\n+if test -f path0 && test -d path1 && test -f path1/file1\n+then\n+\ttest_ok \"checkout successful\"\n+else\n+\ttest_failure \"checkout failed\"\n+fi\n+\n+test_done\n+\n+\nCreated: t/t1001-checkout-cache.sh (mode:100755)\n--- /dev/null\n+++ b/t/t1001-checkout-cache.sh\n@@ -0,0 +1,76 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2005 Junio C Hamano\n+#\n+\n+. ./test-lib.sh\n+test_description \"$@\" 'git-checkout-cache test.\n+\n+This test registers the following filesystem structure in the cache:\n+\n+    path0/file0\t- a file in a directory\n+    path1/file1 - a file in a directory\n+\n+and attempts to check it out when the work tree has:\n+\n+    path0/file0 - a file in a directory\n+    path1       - a symlink pointing at \"path0\"\n+\n+Checkout cache should fail to extract path1/file1 because the leading\n+path path1 is occupied by a non-directory.  With \"-f\" it should remove\n+the symlink path1 and create directory path1 and file path1/file1.\n+'\n+\n+show_files() {\n+\t# show filesystem files, just [-dl] for type and name\n+\tfind path? -ls |\n+\tsed -e 's/^[0-9]* * [0-9]* * \\([-bcdl]\\)[^ ]* *[0-9]* *[^ ]* *[^ ]* *[0-9]* [A-Z][a-z][a-z] [0-9][0-9] [^ ]* /fs: \\1 /'\n+\t# what's in the cache, just mode and name\n+\tgit-ls-files --stage |\n+\tsed -e 's/^\\([0-9]*\\) [0-9a-f]* [0-3] /ca: \\1 /'\n+\t# what's in the tree, just mode and name.\n+\tgit-ls-tree -r \"$1\" |\n+\tsed -e 's/^\\([0-9]*\\)\t[^ ]*\t[0-9a-f]*\t/tr: \\1 /'\n+}\n+\n+mkdir path0\n+date >path0/file0\n+git-update-cache --add path0/file0\n+tree1=$(git-write-tree)\n+test_debug 'show_files $tree1'\n+\n+mkdir path1\n+date >path1/file1\n+git-update-cache --add path1/file1\n+tree2=$(git-write-tree)\n+test_debug 'show_files $tree2'\n+\n+rm -fr path1\n+git-read-tree -m $tree1\n+git-checkout-cache -f -a\n+test_debug 'show_files $tree1'\n+\n+ln -s path0 path1\n+git-update-cache --add path1\n+tree3=$(git-write-tree)\n+test_debug 'show_files $tree3'\n+\n+# Morten says \"Got that?\" here.\n+# Test begins.\n+\n+git-read-tree $tree2\n+test_expect_success 'git-checkout-cache -f -a'\n+test_debug show_files $tree2\n+\n+if test ! -h path0 && test -d path0 &&\n+   test ! -h path1 && test -d path1 &&\n+   test ! -h path0/file0 && test -f path0/file0 &&\n+   test ! -h path1/file1 && test -f path1/file1\n+then\n+    test_ok \"checked out correctly.\"\n+else\n+    test_failure \"did not check out correctly.\"\n+fi\n+\n+test_done\n+\nCreated: t/test-lib.sh (mode:100755)\n--- /dev/null\n+++ b/t/test-lib.sh\n@@ -0,0 +1,106 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2005 Junio C Hamano\n+#\n+\n+# For repeatability, reset the environment to known value.\n+export LANG C\n+export TZ UTC\n+unset AUTHOR_DATE\n+unset AUTHOR_EMAIL\n+unset AUTHOR_NAME\n+unset COMMIT_AUTHOR_EMAIL\n+unset COMMIT_AUTHOR_NAME\n+unset GIT_ALTERNATE_OBJECT_DIRECTORIES\n+unset GIT_AUTHOR_DATE\n+unset GIT_AUTHOR_EMAIL\n+unset GIT_AUTHOR_NAME\n+unset GIT_COMMITTER_EMAIL\n+unset GIT_COMMITTER_NAME\n+unset GIT_DIFF_OPTS\n+unset GIT_DIR\n+unset GIT_EXTERNAL_DIFF\n+unset GIT_INDEX_FILE\n+unset GIT_OBJECT_DIRECTORY\n+unset SHA1_FILE_DIRECTORIES\n+unset SHA1_FILE_DIRECTORY\n+\n+# Each test should start with something like this, after copyright notices:\n+#\n+# . ./testlib.sh\n+# test_description \"$@\" 'Description of this test...\n+# This test checks if command xyzzy does the right thing...\n+# '\n+#\n+\n+test_description () {\n+\twhile case \"$#\" in 0) break;; esac\n+\tdo\n+\t\tcase \"$1\" in\n+\t\t-d|--d|--de|--deb|--debu|--debug)\n+\t\t\tdebug=t; shift ;;\n+\t\t-h|--h|--he|--hel|--help)\n+\t\t\teval echo '\"$'$#'\"'\n+\t\t\texit 0\n+\t\t\t;;\n+\t\t*)\n+\t\t\tbreak ;;\n+\t\tesac\n+\tdone\n+\ttest_failure=0\n+}\n+\n+say () {\n+\techo \"* $*\"\n+}\n+\n+test_debug () {\n+\tcase \"$debug\" in '') ;; ?*) eval \"$*\" ;; esac\n+}\n+\n+test_ok () {\n+\techo \"* $*\";\n+}\n+\n+test_failure () {\n+\techo \"* $*\";\n+\ttest_failure=1;\n+}\n+\n+test_expect_failure () {\n+\tsay \"expecting failure: $1\"\n+\teval \"$1\"\n+\tcase $? in\n+\t0)\ttest_failure \"did not fail as expected\" ;;\n+\t*) \ttest_ok \"failed as expected\" ;;\n+\tesac\n+}\n+\n+test_expect_success () {\n+\tsay \"expecting success: $1\"\n+\teval \"$1\"\n+\tcase $? in\n+\t0) \ttest_ok \"succeeded as expected\" ;;\n+\t*)\ttest_failure \"did not succeed as expected\" ;;\n+\tesac\n+}\n+\n+test_done () {\n+\tcase \"$test_failure\" in\n+\t0)\texit 0 ;;\n+\t'')\techo \"*** test script did not start with test_description\";\n+\t\texit 2 ;;\n+\t*)\texit 1 ;;\n+\tesac\n+}\n+\n+# Test the binaries we have just built.  The tests are kept in\n+# t/ subdirectory and are run in test-repo subdirectory.\n+PATH=$(pwd)/..:$PATH\n+\n+# Test repository\n+test=test-repo\n+rm -fr \"$test\"\n+mkdir \"$test\"\n+cd \"$test\"\n+git-init-db 2>/dev/null || error \"cannot run git-init-db\"\n------------------------------------------------\n\n"},{"id":"3091","messageId":"7voebhnwey.fsf_-_@assigned-by-dhcp.cox.net","threadId":"554","inReplyTo":"20050511224044.GI22686@pasky.ji.cz","subject":"[PATCH] checkout-cache fix","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-12T00:02:45Z","receivedAt":"2005-05-12T00:02:45Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Commit    cc01b05f0a3dfdf5ed114e429a7bec1ad549ab1c\nAuthor    Junio C Hamano <junkio@cox.net>, Wed May 11 17:00:16 2005 -0700\nCommitter Junio C Hamano <junkio@cox.net>, Wed May 11 17:00:16 2005 -0700\n\nFix checkout-cache when existing work tree interferes with the checkout.\n\nThis is essentially the same one as the last one I sent to the\nGIT list, except that the patch is rebased to the current tip of\nthe git-pb tree, and an unnecessary call to create_directories()\nremoved.\n\nThe checkout-cache command gets confused when checking out a\nfile in a subdirectory and the work tree has a symlink to the\nsubdirectory.  Also it fails to check things out when there is a\nnon-directory in the work tree when cache expects a directory\nthere, and vice versa.  This patch fixes the first problem by\nmaking sure all the leading paths in the file being checked out\nare indeed directories, and also fixes directory vs\nnon-directory conflicts when '-f' is specified by removing the\noffending paths.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\n--- a/checkout-cache.c\n+++ b/checkout-cache.c\n@@ -32,6 +32,8 @@\n  * of \"-a\" causing problems (not possible in the above example,\n  * but get used to it in scripting!).\n  */\n+#include <sys/types.h>\n+#include <dirent.h>\n #include \"cache.h\"\n \n static int force = 0, quiet = 0, not_new = 0;\n@@ -46,20 +48,61 @@ static void create_directories(const cha\n \t\tlen = slash - path;\n \t\tmemcpy(buf, path, len);\n \t\tbuf[len] = 0;\n-\t\tmkdir(buf, 0755);\n+\t\tif (mkdir(buf, 0755)) {\n+\t\t\tif (errno == EEXIST) {\n+\t\t\t\tstruct stat st;\n+\t\t\t\tif (!lstat(buf, &st) && S_ISDIR(st.st_mode))\n+\t\t\t\t\tcontinue; /* ok */\n+\t\t\t\tif (force && !unlink(buf) && !mkdir(buf, 0755))\n+\t\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\tdie(\"cannot create directory at %s\", buf);\n+\t\t}\n \t}\n \tfree(buf);\n }\n \n+static void remove_subtree(const char *path)\n+{\n+\tDIR *dir = opendir(path);\n+\tstruct dirent *de;\n+\tchar pathbuf[PATH_MAX];\n+\tchar *name;\n+\t\n+\tif (!dir)\n+\t\tdie(\"cannot opendir %s\", path);\n+\tstrcpy(pathbuf, path);\n+\tname = pathbuf + strlen(path);\n+\t*name++ = '/';\n+\twhile ((de = readdir(dir)) != NULL) {\n+\t\tstruct stat st;\n+\t\tif ((de->d_name[0] == '.') &&\n+\t\t    ((de->d_name[1] == 0) ||\n+\t\t     ((de->d_name[1] == '.') && de->d_name[2] == 0)))\n+\t\t\tcontinue;\n+\t\tstrcpy(name, de->d_name);\n+\t\tif (lstat(pathbuf, &st))\n+\t\t\tdie(\"cannot lstat %s\", pathbuf);\n+\t\tif (S_ISDIR(st.st_mode))\n+\t\t\tremove_subtree(pathbuf);\n+\t\telse if (unlink(pathbuf))\n+\t\t\tdie(\"cannot unlink %s\", pathbuf);\n+\t}\n+\tclosedir(dir);\n+\tif (rmdir(path))\n+\t\tdie(\"cannot rmdir %s\", path);\n+}\n+\n static int create_file(const char *path, unsigned int mode)\n {\n \tint fd;\n \n \tmode = (mode & 0100) ? 0777 : 0666;\n+\tcreate_directories(path);\n \tfd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);\n \tif (fd < 0) {\n-\t\tif (errno == ENOENT) {\n-\t\t\tcreate_directories(path);\n+\t\tif (errno == EISDIR && force) {\n+\t\t\tremove_subtree(path);\n \t\t\tfd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);\n \t\t}\n \t}\n------------------------------------------------\n\n"},{"id":"3180","messageId":"20050512192941.GC324@pasky.ji.cz","threadId":"554","inReplyTo":"7vu0l9nwgx.fsf_-_@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] Test suite","fromName":"Petr Baudis","fromEmail":"pasky@ucw.cz","sentAt":"2005-05-12T19:29:41Z","receivedAt":"2005-05-12T19:29:41Z","isPatch":true,"sender":{"key":"pasky@ucw.cz","avatar":"https://avatars.githubusercontent.com/u/18439?v=4"},"body":"Dear diary, on Thu, May 12, 2005 at 02:01:34AM CEST, I got a letter\nwhere Junio C Hamano <junkio@cox.net> told me that...\n> Commit    1da683e1247046796a094c4917bc0c4591530272\n> Author    Junio C Hamano <junkio@cox.net>, Wed May 11 16:59:35 2005 -0700\n> Committer Junio C Hamano <junkio@cox.net>, Wed May 11 16:59:35 2005 -0700\n> \n> Test suite: infrastructure and examples.\n> \n> This adds the test suite infrastructure with two example tests.\n> The current git-checkout-cache the example tests would fail this\n> test and will be corrected in a separate patch.\n> \n> Signed-off-by: Junio C Hamano <junkio@cox.net>\n\nAdmittely, I'm not happy with this. From the design standpoint it looks\nmostly fine now, but the code is rather crude. I wanted to go over it at\nfirst and fix the obvious stuff, but it appeared to be overall quite\nbroken, so I decided to return it to you for another iteration. I don't\nmind if you just fix the broken code for now, we can fix the semantics\nand design stuff later - what is in the patch is already good enough for\nthe start.\n\nI'll drop the testcases from your other patches for now so that we don't\nget long stalls.\n\n> --- /dev/null\n> +++ b/t/test-lib.sh\n> +# For repeatability, reset the environment to known value.\n> +export LANG C\n> +export TZ UTC\n\nDunno about *your* shell but this just exports variables $LANG, $C, $TZ\nand $UTC here. You probably wanted assignments there?\n\n> +# Each test should start with something like this, after copyright notices:\n> +#\n> +# . ./testlib.sh\n\ntest-lib.sh\n\n> +# test_description \"$@\" 'Description of this test...\n> +# This test checks if command xyzzy does the right thing...\n> +# '\n\nI think this usage is pretty weird. Why not just\n\ntest_description='Description, blah blah.'\n. ./testlib.sh\n\nI think it would be quite less confusing than test_description, which\nactually does effectively something different anyway.\n\n> +\n> +test_description () {\n> +\twhile case \"$#\" in 0) break;; esac\n\nDuh. This looks mysterious - why not a simple test?\n\n> +\tdo\n> +\t\tcase \"$1\" in\n> +\t\t-d|--d|--de|--deb|--debu|--debug)\n> +\t\t\tdebug=t; shift ;;\n> +\t\t-h|--h|--he|--hel|--help)\n> +\t\t\teval echo '\"$'$#'\"'\n> +\t\t\texit 0\n> +\t\t\t;;\n> +\t\t*)\n> +\t\t\tbreak ;;\n\nThis branch makes no sense, I think.\n\n> +\t\tesac\n> +\tdone\n> +\ttest_failure=0\n> +}\n> +\n> +say () {\n> +\techo \"* $*\"\n> +}\n> +\n> +test_debug () {\n> +\tcase \"$debug\" in '') ;; ?*) eval \"$*\" ;; esac\n\nAgain, why not a simple test?\n\n[ \"$debug\" ] && eval \"$*\"\n\n(Actually, eval will do the wrong thing here - it just concatenates the\narguments. Just \"$@\" would do, I guess.)\n\n> +}\n> +\n> +test_ok () {\n> +\techo \"* $*\";\n> +}\n> +\n> +test_failure () {\n> +\techo \"* $*\";\n> +\ttest_failure=1;\n> +}\n> +\n> +test_expect_failure () {\n> +\tsay \"expecting failure: $1\"\n> +\teval \"$1\"\n> +\tcase $? in\n> +\t0)\ttest_failure \"did not fail as expected\" ;;\n> +\t*) \ttest_ok \"failed as expected\" ;;\n> +\tesac\n> +}\n> +\n> +test_expect_success () {\n> +\tsay \"expecting success: $1\"\n> +\teval \"$1\"\n> +\tcase $? in\n> +\t0) \ttest_ok \"succeeded as expected\" ;;\n> +\t*)\ttest_failure \"did not succeed as expected\" ;;\n> +\tesac\n> +}\n> +\n> +test_done () {\n> +\tcase \"$test_failure\" in\n> +\t0)\texit 0 ;;\n\nPlease clean up after yourself in this case.\n\n> +\t'')\techo \"*** test script did not start with test_description\";\n> +\t\texit 2 ;;\n> +\t*)\texit 1 ;;\n> +\tesac\n> +}\n> +\n> +# Test the binaries we have just built.  The tests are kept in\n> +# t/ subdirectory and are run in test-repo subdirectory.\n> +PATH=$(pwd)/..:$PATH\n> +\n> +# Test repository\n> +test=test-repo\n> +rm -fr \"$test\"\n> +mkdir \"$test\"\n> +cd \"$test\"\n> +git-init-db 2>/dev/null || error \"cannot run git-init-db\"\n\nBut there's no 'error' thing.\n\n> --- /dev/null\n> +++ b/t/t1000-checkout-cache.sh\n> +git-update-cache --add path0 path1/file1\n\nYou should make sure even those preparations calls actually succeed.\n\n> --- /dev/null\n> +++ b/t/t1001-checkout-cache.sh\n> +git-update-cache --add path0/file0\n> +tree1=$(git-write-tree)\n\nHere too.\n\n\nThe testcases currently utterly fail, which is not good sign - either\nthey are ahead of the current code, or they are broken. This is the main\nhurdle making me not accept it yet - it does not work for me. If you fix\nthis and the nits above, it can go in, I think.\n\n\n* expecting failure: git-checkout-cache -a\ncheckout-cache: path0 already exists\nerror: checkout-cache: unable to create file path1/file1 (Not a directory)\n* did not fail as expected\n* expecting success: git-checkout-cache -f -a\nerror: checkout-cache: unable to create file path0 (Is a directory)\n* succeeded as expected\n* checkout failed\n\nI consider this output... well, totally confusing. checkout-cache fails\nbut testcase thinks it does not, then it fails again but testcase thinks\nit succeeded as expected but dies anyway.\n\nI think it's too messy. I would much more appreciate output like this:\n\n* checkout-cache test [1/3]: git-checkout-cache -a... passed\n* checkout-cache test [2/3]: git-checkout-cache -f -a... NOT PASSED\nExpected success, but the command failed. Output:\nerror: checkout-cache: unable to create file path0 (Is a directory)\n* checkout-cache test [3/3]: git-checkout-cache foobar... NOT PASSED\nExpected failure, but the command succeeded.\n\nMuch less cluttered, it is clear what went wrong etc.\n\n-- \n\t\t\t\tPetr \"Pasky\" Baudis\nStuff: http://pasky.or.cz/\nC++: an octopus made by nailing extra legs onto a dog. -- Steve Taylor\n"},{"id":"3182","messageId":"20050512193838.GE324@pasky.ji.cz","threadId":"554","inReplyTo":"7voebhnwey.fsf_-_@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] checkout-cache fix","fromName":"Petr Baudis","fromEmail":"pasky@ucw.cz","sentAt":"2005-05-12T19:38:38Z","receivedAt":"2005-05-12T19:38:38Z","isPatch":true,"sender":{"key":"pasky@ucw.cz","avatar":"https://avatars.githubusercontent.com/u/18439?v=4"},"body":"Dear diary, on Thu, May 12, 2005 at 02:02:45AM CEST, I got a letter\nwhere Junio C Hamano <junkio@cox.net> told me that...\n> Commit    cc01b05f0a3dfdf5ed114e429a7bec1ad549ab1c\n> Author    Junio C Hamano <junkio@cox.net>, Wed May 11 17:00:16 2005 -0700\n> Committer Junio C Hamano <junkio@cox.net>, Wed May 11 17:00:16 2005 -0700\n> \n> Fix checkout-cache when existing work tree interferes with the checkout.\n\nThanks, applied. A nit about the commit message, though - I'd prefer you\nto put this metadata stuff belong the --- separator, since they really\ndo not belong to the log message. I've already seen something like this\nin one commit merged from git-jc (IIRC some of the Ingo Molnar's leak\nfixes), and it was a little PITA there since the first line was some\n Date: header but we tend to use the first line as the commit's caption\nat some places.\n\nThanks,\n\n-- \n\t\t\t\tPetr \"Pasky\" Baudis\nStuff: http://pasky.or.cz/\nC++: an octopus made by nailing extra legs onto a dog. -- Steve Taylor\n"},{"id":"3183","messageId":"7vll6kfcp8.fsf@assigned-by-dhcp.cox.net","threadId":"554","inReplyTo":"20050512192941.GC324@pasky.ji.cz","subject":"Re: [PATCH] Test suite","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-12T19:48:03Z","receivedAt":"2005-05-12T19:48:03Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":">>>>> \"PB\" == Petr Baudis <pasky@ucw.cz> writes:\n\n>> --- /dev/null\n>> +++ b/t/test-lib.sh\n>> +# For repeatability, reset the environment to known value.\n>> +export LANG C\n>> +export TZ UTC\n\nThese are just \"Duh\".  Sorry.\n\n>> +\n>> +test_description () {\n>> +\twhile case \"$#\" in 0) break;; esac\n\nPB> Duh. This looks mysterious - why not a simple test?\n\nBecause I am old fashioned and am used to this kind of Bourne\nshell idioms.\n\nPB> This branch makes no sense, I think.\n\nBecause...?\n\nPB> Again, why not a simple test?\n\nAgain using case when test is not needed is a good old Bourne\nshell idiom.\n\nPB> [ \"$debug\" ] && eval \"$*\"\n\nPB> (Actually, eval will do the wrong thing here - it just concatenates the\nPB> arguments. Just \"$@\" would do, I guess.)\n\nNo, I wrote \"$*\" because that was what I wanted and not \"$@\".  I\nwanted to give eval a _single_ string.\n\nPB> Please clean up after yourself in this case.\n\nI would not mind, but I'd mildly disagree.  The next test will\nstart by cleaning up test-repo/ anyway.  What we are lacking is\nt/Makefile that drives all the tests and clean-up can be done\nthere.\n\n>> +git-init-db 2>/dev/null || error \"cannot run git-init-db\"\n\nPB> But there's no 'error' thing.\n\nYes, and that way you can get an error message ;-).  Simple\noversight.\n\nPB> The testcases currently utterly fail,\n\nThat is something I told you in the cover letter (or commit log).\n\n"},{"id":"3184","messageId":"7vekccfceu.fsf@assigned-by-dhcp.cox.net","threadId":"554","inReplyTo":"20050512193838.GE324@pasky.ji.cz","subject":"Re: [PATCH] checkout-cache fix","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-12T19:54:17Z","receivedAt":"2005-05-12T19:54:17Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":">>>>> \"PB\" == Petr Baudis <pasky@ucw.cz> writes:\n\nPB> ... I've already seen something like this\nPB> in one commit merged from git-jc (IIRC some of the Ingo Molnar's leak\nPB> fixes),...\n\nYes, I am very unhappy about that commit, too.  I was mucking\nwith jit-commit command at the time, and apparently used a buggy\none to make that commit---I realized what happened much later\nwhen I reviewed the commit log and it was already too late (I\ntold everybody that the tree is published and pullable by then).\n\nSorry about that.\n\n"},{"id":"3213","messageId":"7vekccc8b7.fsf_-_@assigned-by-dhcp.cox.net","threadId":"554","inReplyTo":"20050512192941.GC324@pasky.ji.cz","subject":"[PATCH] Fix git-diff-files for symlinks.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-12T23:51:08Z","receivedAt":"2005-05-12T23:51:08Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Again I am not sure why this was missed during the last round,\nbut git-diff-files mishandles symlinks on the filesystem.  This\npatch fixes it.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\n*** Petr, this one falls into \"emergency obvious fix\" category.\n*** I found it during adding more \"basic\" test to the test suite,\n*** which turns out to be quite useful.\n\ndiff-files.c |    3 ++-\n1 files changed, 2 insertions(+), 1 deletion(-)\n\n--- a/diff-files.c\n+++ b/diff-files.c\n@@ -126,7 +126,8 @@\n \t\t\tcontinue;\n \n \t\toldmode = ntohl(ce->ce_mode);\n-\t\tmode = S_IFREG | ce_permissions(st.st_mode);\n+\t\tmode = (S_ISLNK(st.st_mode) ? S_IFLNK :\n+\t\t\tS_IFREG | ce_permissions(st.st_mode));\n \n \t\tshow_modified(oldmode, mode, ce->sha1, null_sha1,\n \t\t\t      ce->name);\n------------------------------------------------\n\n"},{"id":"3214","messageId":"7v7ji4c78n.fsf_-_@assigned-by-dhcp.cox.net","threadId":"554","inReplyTo":"20050512192941.GC324@pasky.ji.cz","subject":"[PATCH 0/3] Core GIT fixes and additions.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-13T00:14:16Z","receivedAt":"2005-05-13T00:14:16Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Pasky,\n        I am sending you three patches rediffed against the tip\nof git-pb tree for inclusion.\n\n  * [PATCH 1/3] Introduce \"rev-list --stop-at=<commit>\".\n  * [PATCH 2/3] Support symlinks in git-ls-files --others.\n  * [PATCH 3/3] Add git-ls-files -k.\n\nThe first one is independent from other two.  3/3 touches the\nsame file as 2/3.\n\n"}]}