* "git-checkout-cache -f -a" failure @ 2005-05-09 18:25 Morten Welinder 2005-05-10 2:29 ` Junio C Hamano 2005-05-10 22:57 ` Junio C Hamano 0 siblings, 2 replies; 19+ messages in thread From: Morten Welinder @ 2005-05-09 18:25 UTC (permalink / raw) To: GIT Mailing List, Linus Torvalds git-checkout-cache is having problems when files change from directories to plain files or vice versa. cg-seek seems to be similarly affected. Note also, that git-checkout-cache's error messages haven't caught up with the command renaming. Morten cd /tmp mkdir xxx cd xxx rm -rf .git empty yyy touch empty cg-init </dev/null cg-tag initial mkdir yyy touch yyy/zzz cg-add yyy/zzz cg-commit </dev/null cg-tag dir rm -rf empty yyy rm -f .git/HEAD cat .git/refs/tags/initial > .git/HEAD git-read-tree -m HEAD git-checkout-cache -f -a touch yyy cg-add yyy cg-commit </dev/null cg-tag file # Got that? # yyy is a file right now. git-read-tree `cat .git/refs/tags/dir` git-checkout-cache -f -a # error: checkout-cache: unable to create file yyy/zzz (Not a directory) # Not good! rm -rf empty yyy git-read-tree `cat .git/refs/tags/dir` git-checkout-cache -f -a # yyy is a directory right now git-read-tree `cat .git/refs/tags/file` git-checkout-cache -f -a # error: checkout-cache: unable to create file yyy (Is a directory) # Not good! ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: "git-checkout-cache -f -a" failure 2005-05-09 18:25 "git-checkout-cache -f -a" failure Morten Welinder @ 2005-05-10 2:29 ` Junio C Hamano 2005-05-10 3:04 ` Morten Welinder 2005-05-10 22:57 ` Junio C Hamano 1 sibling, 1 reply; 19+ messages in thread From: Junio C Hamano @ 2005-05-10 2:29 UTC (permalink / raw) To: Morten Welinder; +Cc: GIT Mailing List, Linus Torvalds >>>>> "MW" == Morten Welinder <mwelinder@gmail.com> writes: MW> git-checkout-cache is having problems when files change from directories to MW> plain files or vice versa. Changing files vs directories _is_ a big change and happens rarely in practice; I think the current behaviour is justified---it makes the user take notice and the user _should_ take notice. Porcelain layer, if it wanted to, should be able to hide this from the user, but it depends on which Porcelain layer command is involved. My understanding of cg-seek is to switch the work tree to a completely different state, so in that case it probably should hide this (still it makes me feel a bit nervous to realize that I am advocating for the tool to silently remove a whole subtree to make room for a file, though). ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: "git-checkout-cache -f -a" failure 2005-05-10 2:29 ` Junio C Hamano @ 2005-05-10 3:04 ` Morten Welinder 0 siblings, 0 replies; 19+ messages in thread From: Morten Welinder @ 2005-05-10 3:04 UTC (permalink / raw) To: Junio C Hamano; +Cc: GIT Mailing List, Linus Torvalds > Changing files vs directories _is_ a big change and happens > rarely in practice; I think the current behaviour is > justified---it makes the user take notice and the user _should_ > take notice. File vs. directory was just the easiest way to demonstrate. In the presense of symlinks I am not sure you will always get a warning. It'll be more of a silent file-corrupting failure kind of thing. (Somewhat worse if yyy points to /your/home/.ssh and zzz is "authorized_keys".) Morten ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: "git-checkout-cache -f -a" failure 2005-05-09 18:25 "git-checkout-cache -f -a" failure Morten Welinder 2005-05-10 2:29 ` Junio C Hamano @ 2005-05-10 22:57 ` Junio C Hamano 2005-05-10 23:03 ` Petr Baudis 2005-05-11 2:53 ` "git-checkout-cache -f -a" failure Junio C Hamano 1 sibling, 2 replies; 19+ messages in thread From: Junio C Hamano @ 2005-05-10 22:57 UTC (permalink / raw) To: Morten Welinder; +Cc: GIT Mailing List, Linus Torvalds >>>>> "MW" == Morten Welinder <mwelinder@gmail.com> writes: MW> git-checkout-cache is having problems when files change from directories to MW> plain files or vice versa. cg-seek seems to be similarly affected. Could you give this patch a try? It lets checkout-cache remove a file where you want to have a directory (because you want to create something underneath) or remove a whole subdirectory where you want to have a non-directory, when '-f' parameter is specified. If things test well, I'll put this in the git-jc repository and ask Linus to pull from it alongside with other accumulated patches when he returns. Thanks. Signed-off-by: Junio C Hamano <junkio@cox.net> ------------ # - HEAD: Adjust quoting styles for some environment variables in the documentation. # + 7: Let checkout-cache stomp on existing file/directory with -f. --- a/checkout-cache.c +++ b/checkout-cache.c @@ -32,6 +32,8 @@ * of "-a" causing problems (not possible in the above example, * but get used to it in scripting!). */ +#include <sys/types.h> +#include <dirent.h> #include "cache.h" static int force = 0, quiet = 0, not_new = 0; @@ -46,11 +48,51 @@ static void create_directories(const cha len = slash - path; memcpy(buf, path, len); buf[len] = 0; - mkdir(buf, 0755); + if (mkdir(buf, 0755)) { + if (errno == EEXIST) { + struct stat st; + if (!lstat(buf, &st) && S_ISDIR(st.st_mode)) + continue; /* ok */ + if (force && !unlink(buf) && !mkdir(buf, 0755)) + continue; + } + die("cannot create directory at %s", buf); + } } free(buf); } +static void remove_subtree(const char *path) +{ + DIR *dir = opendir(path); + struct dirent *de; + char pathbuf[PATH_MAX]; + char *name; + + if (!dir) + die("cannot opendir %s", path); + strcpy(pathbuf, path); + name = pathbuf + strlen(path); + *name++ = '/'; + while ((de = readdir(dir)) != NULL) { + struct stat st; + if (de->d_name[0] == '.') + continue; /* we mean . and .. but verify_path would + * have rejected any dotfiles earlier. + */ + strcpy(name, de->d_name); + if (lstat(pathbuf, &st)) + die("cannot lstat %s", pathbuf); + if (S_ISDIR(st.st_mode)) + remove_subtree(pathbuf); + else if (unlink(pathbuf)) + die("cannot unlink %s", pathbuf); + } + closedir(dir); + if (rmdir(path)) + die("cannot rmdir %s", path); +} + static int create_file(const char *path, unsigned int mode) { int fd; @@ -62,6 +104,14 @@ static int create_file(const char *path, create_directories(path); fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode); } + else if (errno == EISDIR && force) { + remove_subtree(path); + fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode); + } + else if (errno == ENOTDIR && force) { + create_directories(path); + fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode); + } } return fd; } ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: "git-checkout-cache -f -a" failure 2005-05-10 22:57 ` Junio C Hamano @ 2005-05-10 23:03 ` Petr Baudis 2005-05-11 5:16 ` Junio C Hamano 2005-05-11 2:53 ` "git-checkout-cache -f -a" failure Junio C Hamano 1 sibling, 1 reply; 19+ messages in thread From: Petr Baudis @ 2005-05-10 23:03 UTC (permalink / raw) To: Junio C Hamano; +Cc: Morten Welinder, GIT Mailing List, Linus Torvalds Dear diary, on Wed, May 11, 2005 at 12:57:34AM CEST, I got a letter where Junio C Hamano <junkio@cox.net> told me that... > >>>>> "MW" == Morten Welinder <mwelinder@gmail.com> writes: > > MW> git-checkout-cache is having problems when files change from directories to > MW> plain files or vice versa. cg-seek seems to be similarly affected. > > Could you give this patch a try? It lets checkout-cache remove > a file where you want to have a directory (because you want to > create something underneath) or remove a whole subdirectory > where you want to have a non-directory, when '-f' parameter is > specified. > > If things test well, I'll put this in the git-jc repository and > ask Linus to pull from it alongside with other accumulated > patches when he returns. Chilling. What if you have some files not tracked by git in the subdirectory? Either you need to check for this and deal with it (Cogito's approach would be to remove the git-tracked files and rename the subdirectory), or not do it at all. Or at least please use -F or something if you really want this and like to gamble. -- Petr "Pasky" Baudis Stuff: http://pasky.or.cz/ C++: an octopus made by nailing extra legs onto a dog. -- Steve Taylor ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: "git-checkout-cache -f -a" failure 2005-05-10 23:03 ` Petr Baudis @ 2005-05-11 5:16 ` Junio C Hamano 2005-05-11 18:31 ` Morten Welinder 0 siblings, 1 reply; 19+ messages in thread From: Junio C Hamano @ 2005-05-11 5:16 UTC (permalink / raw) To: Petr Baudis; +Cc: Morten Welinder, git, Linus Torvalds >>>>> "PB" == Petr Baudis <pasky@ucw.cz> writes: PB> What if you have some files not tracked by git in the subdirectory? PB> Either you need to check for this and deal with it (Cogito's approach PB> would be to remove the git-tracked files and rename the subdirectory), PB> or not do it at all. Let me understand you correctly. You "rename the subdirectory" because the cache you want to check out records a non-directory there. Is this correct? When do you "remove the git-tracked files"? Do you mean "if a file that is not tracked (in the user's mind) is there in the filesystem, and the cache to be checked out has a file there, the file on the filesystem is removed"? What happens if an existing non-directory interferes with a directory to be checked out from the cache? I think the right thing for the core GIT layer to do, when the end user is using the Plumbing tools directly and says "really please" by specifying an '-f', is to make the checkout succeed by removing conflicting stuff, as my fixed patch does. If the Porcelain runs checkout-cache on the user's behalf, the story is a bit different. Porcelain would have some idea of what GIT tree the files in the working directory originated from ("the last commit", or "my head before the read-tree -m merge") before the user started working in it. When the user wants to update the work tree to match a cache (which may be quite different from "the last commit", if it is coming from cg-seek or result of a merge), the Porcelain knows: - the set of non git-tracked (from the point of view from "the last commit") files that are in the work tree; - among those, the set of files that would be overwritten by this checkout. This includes, but not limited to, the files that would otherwise interfere because of file-directory conflicts. And then you can set aside those "to-be-overwritten" files, remove them from the filesystem, and run your checkout. The determination of which files to set aside should not be done based on whether they cause file-directory conflicts. If the user has a non-tracked file which happens to be registered as a tracked file in the cache to be checked out (e.g. after a three-way merge), that file is as valuable as a non-tracked file that is in a subdirectory that the new cache happens to have as a non-directory. Losing the former is as harmful as losing the latter. From what you say above, it sounds like you are saving the latter but not the former, but my readins of what you wrote above may be mistaken. ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: "git-checkout-cache -f -a" failure 2005-05-11 5:16 ` Junio C Hamano @ 2005-05-11 18:31 ` Morten Welinder 2005-05-11 21:37 ` Junio C Hamano 0 siblings, 1 reply; 19+ messages in thread From: Morten Welinder @ 2005-05-11 18:31 UTC (permalink / raw) To: Junio C Hamano; +Cc: Petr Baudis, git, Linus Torvalds Here's the symlink version. Note, that git does not complain but simply creates (or overwrites) the wrong file. Morten cd /tmp mkdir xxx cd xxx rm -rf .git dir yyy mkdir dir touch dir/empty cg-init </dev/null cg-tag initial mkdir yyy touch yyy/zzz cg-add yyy/zzz cg-commit </dev/null cg-tag dir rm -rf yyy rm -f .git/HEAD cat .git/refs/tags/initial > .git/HEAD git-read-tree -m HEAD git-checkout-cache -f -a ln -s dir yyy git-update-cache --add -- yyy cg-commit </dev/null cg-tag symlink # Got that? # yyy is a symlink right now. git-read-tree `cat .git/refs/tags/dir` git-checkout-cache -f -a ls -l dir total 0 -rw-rw-r-- 1 welinder research 0 May 11 14:20 empty -rw-rw-r-- 1 welinder research 0 May 11 14:26 zzz # dir/zzz should not exist at ths point! ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: "git-checkout-cache -f -a" failure 2005-05-11 18:31 ` Morten Welinder @ 2005-05-11 21:37 ` Junio C Hamano 2005-05-11 22:12 ` Junio C Hamano 0 siblings, 1 reply; 19+ messages in thread From: Junio C Hamano @ 2005-05-11 21:37 UTC (permalink / raw) To: Morten Welinder; +Cc: Petr Baudis, git, Linus Torvalds >>>>> "MW" == Morten Welinder <mwelinder@gmail.com> writes: MW> Here's the symlink version. Note, that git does not complain but MW> simply creates (or MW> overwrites) the wrong file. MW> Morten Here is a proposed fix. ------------ Commit 685c391c9a755936175193f8b58973b74eaef930 Author Junio C Hamano <junkio@cox.net>, Wed May 11 14:36:05 2005 -0700 Committer Junio C Hamano <junkio@cox.net>, Wed May 11 14:36:05 2005 -0700 Fix checkout-cache when existing work tree interferes with the checkout. The checkout-cache command gets confused when checking out a file in a subdirectory and the work tree has a symlink to the subdirectory. Also it fails to check things out when there is a non-directory in the work tree when cache expects a directory there, and vice versa. This patch fixes the first problem by making sure all the leading paths in the file being checked out are indeed directories, and also fixes directory vs non-directory conflicts when '-f' is specified by removing the offending paths. I've added test subdirectory and two tests to check the above problems are fixed. Signed-off-by: Junio C Hamano <junkio@cox.net> --- a/checkout-cache.c +++ b/checkout-cache.c @@ -32,6 +32,8 @@ * of "-a" causing problems (not possible in the above example, * but get used to it in scripting!). */ +#include <sys/types.h> +#include <dirent.h> #include "cache.h" static int force = 0, quiet = 0, not_new = 0; @@ -46,22 +48,67 @@ static void create_directories(const cha len = slash - path; memcpy(buf, path, len); buf[len] = 0; - mkdir(buf, 0755); + if (mkdir(buf, 0755)) { + if (errno == EEXIST) { + struct stat st; + if (!lstat(buf, &st) && S_ISDIR(st.st_mode)) + continue; /* ok */ + if (force && !unlink(buf) && !mkdir(buf, 0755)) + continue; + } + die("cannot create directory at %s", buf); + } } free(buf); } +static void remove_subtree(const char *path) +{ + DIR *dir = opendir(path); + struct dirent *de; + char pathbuf[PATH_MAX]; + char *name; + + if (!dir) + die("cannot opendir %s", path); + strcpy(pathbuf, path); + name = pathbuf + strlen(path); + *name++ = '/'; + while ((de = readdir(dir)) != NULL) { + struct stat st; + if ((de->d_name[0] == '.') && + ((de->d_name[1] == 0) || + ((de->d_name[1] == '.') && de->d_name[2] == 0))) + continue; + strcpy(name, de->d_name); + if (lstat(pathbuf, &st)) + die("cannot lstat %s", pathbuf); + if (S_ISDIR(st.st_mode)) + remove_subtree(pathbuf); + else if (unlink(pathbuf)) + die("cannot unlink %s", pathbuf); + } + closedir(dir); + if (rmdir(path)) + die("cannot rmdir %s", path); +} + static int create_file(const char *path, unsigned int mode) { int fd; mode = (mode & 0100) ? 0777 : 0666; + create_directories(path); fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode); if (fd < 0) { - if (errno == ENOENT) { + if ((errno == ENOENT) || (errno == ENOTDIR && force)) { create_directories(path); fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode); } + else if (errno == EISDIR && force) { + remove_subtree(path); + fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode); + } } return fd; } Created: t/t0000.sh (mode:100755) --- /dev/null +++ b/t/t0000.sh @@ -0,0 +1,35 @@ +#!/bin/sh + +case "${verbose+set}" in +set) say= ;; +*) say=: ;; +esac + +export LANG C +unset AUTHOR_DATE +unset AUTHOR_EMAIL +unset AUTHOR_NAME +unset COMMIT_AUTHOR_EMAIL +unset COMMIT_AUTHOR_NAME +unset GIT_ALTERNATE_OBJECT_DIRECTORIES +unset GIT_AUTHOR_DATE +unset GIT_AUTHOR_EMAIL +unset GIT_AUTHOR_NAME +unset GIT_COMMITTER_EMAIL +unset GIT_COMMITTER_NAME +unset GIT_DIFF_OPTS +unset GIT_DIR +unset GIT_EXTERNAL_DIFF +unset GIT_INDEX_FILE +unset GIT_OBJECT_DIRECTORY +unset SHA1_FILE_DIRECTORIES +unset SHA1_FILE_DIRECTORY + +# Test the binaries we have just built. +PATH=$(pwd)/..:$PATH + +# Test repository +test=test-repo +rm -fr "$test" +mkdir "$test" +cd "$test" Created: t/t1000-checkout-cache.sh (mode:100755) --- /dev/null +++ b/t/t1000-checkout-cache.sh @@ -0,0 +1,39 @@ +#!/bin/sh + +. ./t0000.sh +git-init-db 2>/dev/null || exit +date >path0 +mkdir path1 +date >path1/file1 +git-update-cache --add path0 path1/file1 +$say git-ls-files --stage + +rm -fr path0 path1 +mkdir path0 +date >path0/file0 +date >path1 +$say git-ls-files --stage +$say find path* + +echo >&2 "* checkout-cache sans -f" +git-checkout-cache -a +case "$?" in +0) echo >&2 "*** bug: should not have succeeded." ;; +*) echo >&2 "*** ok: failed as expected." ;; +esac +$say find path* + +echo >&2 "* checkout-cache with -f" +git-checkout-cache -f -a +case "$?" in +0) echo >&2 "*** ok: succeeded as expected." ;; +*) echo >&2 "*** bug: should have succeeded." ;; +esac +$say find path* +if test -f path0 && test -d path1 && test -f path1/file1 +then + echo >&2 "*** ok: checked out correctly." +else + echo >&2 "*** bug: checkout failed." + exit 1 +fi Created: t/t1001-checkout-cache.sh (mode:100755) --- /dev/null +++ b/t/t1001-checkout-cache.sh @@ -0,0 +1,61 @@ +#!/bin/sh + +. ./t0000.sh +git-init-db 2>/dev/null || exit + +show_files() { + find path? -ls | + sed -e 's/^[0-9]* * [0-9]* * \([-bcdl]\)[^ ]* *[0-9]* *[^ ]* *[^ ]* *[0-9]* [A-Z][a-z][a-z] [0-9][0-9] [^ ]* /fs: \1 /' + git-ls-files --stage | + sed -e 's/^\([0-9]*\) [0-9a-f]* [0-3] /ca: \1 /' + git-ls-tree -r "$1" | + sed -e 's/^\([0-9]*\) [^ ]* [0-9a-f]* /tr: \1 /' +} + +mkdir path0 +date >path0/file0 +git-update-cache --add path0/file0 +echo >&2 "* initial state: one file under one directory" +tree1=$(git-write-tree) +$say show_files $tree1 + +mkdir path1 +date >path1/file1 +git-update-cache --add path1/file1 +echo >&2 "* two directories with one file each" +tree2=$(git-write-tree) +$say show_files $tree2 + +rm -fr path1 +git-read-tree -m $tree1 +git-checkout-cache -f -a +echo >&2 "* go back to initial state" +$say show_files $tree1 + +ln -s path0 path1 +git-update-cache --add path1 +echo >&2 "* a symlink where the other side would create a directory." +tree3=$(git-write-tree) +$say show_files $tree3 + +# Morten says "Got that?" here. + +git-read-tree $tree2 +git-checkout-cache -f -a +case "$?" in +0) echo >&2 "*** ok: succeeded as expected." ;; +*) echo >&2 "*** bug: should have succeeded." ;; +esac +echo >&2 "* read tree2 and checkout" +$say show_files $tree2 + +if test ! -h path0 && test -d path0 && + test ! -h path1 && test -d path1 && + test ! -h path0/file0 && test -f path0/file0 && + test ! -h path1/file1 && test -f path1/file1 +then + echo >&2 "*** ok: checked out correctly." +else + echo >&2 "*** bug: did not check out correctly." + exit 1 +fi ------------------------------------------------ ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: "git-checkout-cache -f -a" failure 2005-05-11 21:37 ` Junio C Hamano @ 2005-05-11 22:12 ` Junio C Hamano 2005-05-11 22:40 ` Test suite Petr Baudis 0 siblings, 1 reply; 19+ messages in thread From: Junio C Hamano @ 2005-05-11 22:12 UTC (permalink / raw) To: Petr Baudis; +Cc: Morten Welinder, git, Linus Torvalds Here is a re-diff against the current tip of the git-pb tree. ------------ Fix checkout-cache when existing work tree interferes with the checkout. This is essentially the same one as the last one I sent to the GIT list, except that the patch is rebased to the current tip of the git-pb tree, and an unnecessary call to create_directories() removed. The checkout-cache command gets confused when checking out a file in a subdirectory and the work tree has a symlink to the subdirectory. Also it fails to check things out when there is a non-directory in the work tree when cache expects a directory there, and vice versa. This patch fixes the first problem by making sure all the leading paths in the file being checked out are indeed directories, and also fixes directory vs non-directory conflicts when '-f' is specified by removing the offending paths. I've added test subdirectory and two tests to check the above problems are fixed. Signed-off-by: Junio C Hamano <junkio@cox.net> --- checkout-cache.c | 49 +++++++++++++++++++++++++++++++++-- t/t0000.sh | 38 +++++++++++++++++++++++++++ t/t1000-checkout-cache.sh | 42 ++++++++++++++++++++++++++++++ t/t1001-checkout-cache.sh | 64 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 190 insertions(+), 3 deletions(-) --- a/checkout-cache.c +++ b/checkout-cache.c @@ -32,6 +32,8 @@ * of "-a" causing problems (not possible in the above example, * but get used to it in scripting!). */ +#include <sys/types.h> +#include <dirent.h> #include "cache.h" static int force = 0, quiet = 0, not_new = 0; @@ -46,20 +48,61 @@ static void create_directories(const cha len = slash - path; memcpy(buf, path, len); buf[len] = 0; - mkdir(buf, 0755); + if (mkdir(buf, 0755)) { + if (errno == EEXIST) { + struct stat st; + if (!lstat(buf, &st) && S_ISDIR(st.st_mode)) + continue; /* ok */ + if (force && !unlink(buf) && !mkdir(buf, 0755)) + continue; + } + die("cannot create directory at %s", buf); + } } free(buf); } +static void remove_subtree(const char *path) +{ + DIR *dir = opendir(path); + struct dirent *de; + char pathbuf[PATH_MAX]; + char *name; + + if (!dir) + die("cannot opendir %s", path); + strcpy(pathbuf, path); + name = pathbuf + strlen(path); + *name++ = '/'; + while ((de = readdir(dir)) != NULL) { + struct stat st; + if ((de->d_name[0] == '.') && + ((de->d_name[1] == 0) || + ((de->d_name[1] == '.') && de->d_name[2] == 0))) + continue; + strcpy(name, de->d_name); + if (lstat(pathbuf, &st)) + die("cannot lstat %s", pathbuf); + if (S_ISDIR(st.st_mode)) + remove_subtree(pathbuf); + else if (unlink(pathbuf)) + die("cannot unlink %s", pathbuf); + } + closedir(dir); + if (rmdir(path)) + die("cannot rmdir %s", path); +} + static int create_file(const char *path, unsigned int mode) { int fd; mode = (mode & 0100) ? 0777 : 0666; + create_directories(path); fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode); if (fd < 0) { - if (errno == ENOENT) { - create_directories(path); + if (errno == EISDIR && force) { + remove_subtree(path); fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode); } } Created: t/t0000.sh (mode:100755) --- /dev/null +++ b/t/t0000.sh @@ -0,0 +1,38 @@ +#!/bin/sh +# +# Copyright (c) 2005 Junio C Hamano +# + +case "${verbose+set}" in +set) say= ;; +*) say=: ;; +esac + +export LANG C +unset AUTHOR_DATE +unset AUTHOR_EMAIL +unset AUTHOR_NAME +unset COMMIT_AUTHOR_EMAIL +unset COMMIT_AUTHOR_NAME +unset GIT_ALTERNATE_OBJECT_DIRECTORIES +unset GIT_AUTHOR_DATE +unset GIT_AUTHOR_EMAIL +unset GIT_AUTHOR_NAME +unset GIT_COMMITTER_EMAIL +unset GIT_COMMITTER_NAME +unset GIT_DIFF_OPTS +unset GIT_DIR +unset GIT_EXTERNAL_DIFF +unset GIT_INDEX_FILE +unset GIT_OBJECT_DIRECTORY +unset SHA1_FILE_DIRECTORIES +unset SHA1_FILE_DIRECTORY + +# Test the binaries we have just built. +PATH=$(pwd)/..:$PATH + +# Test repository +test=test-repo +rm -fr "$test" +mkdir "$test" +cd "$test" Created: t/t1000-checkout-cache.sh (mode:100755) --- /dev/null +++ b/t/t1000-checkout-cache.sh @@ -0,0 +1,42 @@ +#!/bin/sh +# +# Copyright (c) 2005 Junio C Hamano +# + +. ./t0000.sh +git-init-db 2>/dev/null || exit +date >path0 +mkdir path1 +date >path1/file1 +git-update-cache --add path0 path1/file1 +$say git-ls-files --stage + +rm -fr path0 path1 +mkdir path0 +date >path0/file0 +date >path1 +$say git-ls-files --stage +$say find path* + +echo >&2 "* checkout-cache sans -f" +git-checkout-cache -a +case "$?" in +0) echo >&2 "*** bug: should not have succeeded." ;; +*) echo >&2 "*** ok: failed as expected." ;; +esac +$say find path* + +echo >&2 "* checkout-cache with -f" +git-checkout-cache -f -a +case "$?" in +0) echo >&2 "*** ok: succeeded as expected." ;; +*) echo >&2 "*** bug: should have succeeded." ;; +esac +$say find path* +if test -f path0 && test -d path1 && test -f path1/file1 +then + echo >&2 "*** ok: checked out correctly." +else + echo >&2 "*** bug: checkout failed." + exit 1 +fi Created: t/t1001-checkout-cache.sh (mode:100755) --- /dev/null +++ b/t/t1001-checkout-cache.sh @@ -0,0 +1,64 @@ +#!/bin/sh +# +# Copyright (c) 2005 Junio C Hamano +# + +. ./t0000.sh +git-init-db 2>/dev/null || exit + +show_files() { + find path? -ls | + sed -e 's/^[0-9]* * [0-9]* * \([-bcdl]\)[^ ]* *[0-9]* *[^ ]* *[^ ]* *[0-9]* [A-Z][a-z][a-z] [0-9][0-9] [^ ]* /fs: \1 /' + git-ls-files --stage | + sed -e 's/^\([0-9]*\) [0-9a-f]* [0-3] /ca: \1 /' + git-ls-tree -r "$1" | + sed -e 's/^\([0-9]*\) [^ ]* [0-9a-f]* /tr: \1 /' +} + +mkdir path0 +date >path0/file0 +git-update-cache --add path0/file0 +echo >&2 "* initial state: one file under one directory" +tree1=$(git-write-tree) +$say show_files $tree1 + +mkdir path1 +date >path1/file1 +git-update-cache --add path1/file1 +echo >&2 "* two directories with one file each" +tree2=$(git-write-tree) +$say show_files $tree2 + +rm -fr path1 +git-read-tree -m $tree1 +git-checkout-cache -f -a +echo >&2 "* go back to initial state" +$say show_files $tree1 + +ln -s path0 path1 +git-update-cache --add path1 +echo >&2 "* a symlink where the other side would create a directory." +tree3=$(git-write-tree) +$say show_files $tree3 + +# Morten says "Got that?" here. + +git-read-tree $tree2 +git-checkout-cache -f -a +case "$?" in +0) echo >&2 "*** ok: succeeded as expected." ;; +*) echo >&2 "*** bug: should have succeeded." ;; +esac +echo >&2 "* read tree2 and checkout" +$say show_files $tree2 + +if test ! -h path0 && test -d path0 && + test ! -h path1 && test -d path1 && + test ! -h path0/file0 && test -f path0/file0 && + test ! -h path1/file1 && test -f path1/file1 +then + echo >&2 "*** ok: checked out correctly." +else + echo >&2 "*** bug: did not check out correctly." + exit 1 +fi ------------------------------------------------ ^ permalink raw reply [flat|nested] 19+ messages in thread
* Test suite 2005-05-11 22:12 ` Junio C Hamano @ 2005-05-11 22:40 ` Petr Baudis 2005-05-12 0:01 ` [PATCH] " Junio C Hamano 2005-05-12 0:02 ` [PATCH] checkout-cache fix Junio C Hamano 0 siblings, 2 replies; 19+ messages in thread From: Petr Baudis @ 2005-05-11 22:40 UTC (permalink / raw) To: Junio C Hamano; +Cc: git Dear diary, on Thu, May 12, 2005 at 12:12:10AM CEST, I got a letter where Junio C Hamano <junkio@cox.net> told me that... > I've added test subdirectory and two tests to check the above > problems are fixed. I'd prefer introduction of the infrastructure for this in a separate commit. I'll focus on it here; I will have a look at the checkout-cache stuff tomorrow. > Created: t/t0000.sh (mode:100755) > --- /dev/null > +++ b/t/t0000.sh I'd prefer some better name for this. What about testlib.sh? > @@ -0,0 +1,38 @@ > +#!/bin/sh > +# > +# Copyright (c) 2005 Junio C Hamano > +# A short description here would be nice. > +case "${verbose+set}" in > +set) say= ;; > +*) say=: ;; > +esac Perhaps set $verbose if -v was passed or something. Looking at how you use it, I wouldn't run the command at all if not verbose. > Created: t/t1000-checkout-cache.sh (mode:100755) > --- /dev/null > +++ b/t/t1000-checkout-cache.sh > @@ -0,0 +1,42 @@ > +#!/bin/sh > +# > +# Copyright (c) 2005 Junio C Hamano > +# I think each test in the suite should have a short summary of its focus in the top comment. > +. ./t0000.sh > +git-init-db 2>/dev/null || exit I'd do this in testlib.sh (you might well blast it away and redo it when testing init-db). > +echo >&2 "* checkout-cache sans -f" > +git-checkout-cache -a > +case "$?" in > +0) echo >&2 "*** bug: should not have succeeded." ;; > +*) echo >&2 "*** ok: failed as expected." ;; > +esac And this essentially repeats all over and over. What about something like cmdtest "sans -f" git-checkout-cache -a > +if test -f path0 && test -d path1 && test -f path1/file1 > +then > + echo >&2 "*** ok: checked out correctly." > +else > + echo >&2 "*** bug: checkout failed." > + exit 1 > +fi We might get a good function for this later too, for now I'd propose if test -f path0 && test -d path1 && test -f path1/file1; then testok "checkout" else testfail "checkout" # non-fatal test failure testfatal "checkout" # fatal test failure, die right away fi And at the end the script, you should do exit $teststatus which is set to non-zero anytime testfail was invoked. > Created: t/t1001-checkout-cache.sh (mode:100755) > --- /dev/null > +++ b/t/t1001-checkout-cache.sh > @@ -0,0 +1,64 @@ > +show_files() { > + find path? -ls | > + sed -e 's/^[0-9]* * [0-9]* * \([-bcdl]\)[^ ]* *[0-9]* *[^ ]* *[^ ]* *[0-9]* [A-Z][a-z][a-z] [0-9][0-9] [^ ]* /fs: \1 /' > + git-ls-files --stage | > + sed -e 's/^\([0-9]*\) [0-9a-f]* [0-3] /ca: \1 /' > + git-ls-tree -r "$1" | > + sed -e 's/^\([0-9]*\) [^ ]* [0-9a-f]* /tr: \1 /' > +} Ugh. I think a comment would be suitable here. ;-) > +mkdir path0 > +date >path0/file0 > +git-update-cache --add path0/file0 > +echo >&2 "* initial state: one file under one directory" > +tree1=$(git-write-tree) cmdtest --capture-stdout "one file under one directory" git-write-tree tree1=$(cat $teststdout) -- Petr "Pasky" Baudis Stuff: http://pasky.or.cz/ C++: an octopus made by nailing extra legs onto a dog. -- Steve Taylor ^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH] Test suite 2005-05-11 22:40 ` Test suite Petr Baudis @ 2005-05-12 0:01 ` Junio C Hamano 2005-05-12 19:29 ` Petr Baudis 2005-05-12 0:02 ` [PATCH] checkout-cache fix Junio C Hamano 1 sibling, 1 reply; 19+ messages in thread From: Junio C Hamano @ 2005-05-12 0:01 UTC (permalink / raw) To: Petr Baudis; +Cc: git Commit 1da683e1247046796a094c4917bc0c4591530272 Author Junio C Hamano <junkio@cox.net>, Wed May 11 16:59:35 2005 -0700 Committer Junio C Hamano <junkio@cox.net>, Wed May 11 16:59:35 2005 -0700 Test suite: infrastructure and examples. This adds the test suite infrastructure with two example tests. The current git-checkout-cache the example tests would fail this test and will be corrected in a separate patch. Signed-off-by: Junio C Hamano <junkio@cox.net> --- Created: t/t1000-checkout-cache.sh (mode:100755) --- /dev/null +++ b/t/t1000-checkout-cache.sh @@ -0,0 +1,54 @@ +#!/bin/sh +# +# Copyright (c) 2005 Junio C Hamano +# + +. ./test-lib.sh +test_description "$@" 'git-checkout-cache test. + +This test registers the following filesystem structure in the +cache: + + path0 - a file + path1/file1 - a file in a directory + +And then tries to checkout in a work tree that has the following: + + path0/file0 - a file in a directory + path1 - a file + +The git-checkout-cache command should fail when attempting to checkout +path0, finding it is occupied by a directory, and path1/file1, finding +path1 is occupied by a non-directory. With "-f" flag, it should remove +the conflicting paths and succeed. +' + +date >path0 +mkdir path1 +date >path1/file1 +git-update-cache --add path0 path1/file1 +test_debug 'git-ls-files --stage' + +rm -fr path0 path1 +mkdir path0 +date >path0/file0 +date >path1 +test_debug 'git-ls-files --stage' +test_debug 'find path*' + +test_expect_failure 'git-checkout-cache -a' +test_debug 'find path*' + +test_expect_success 'git-checkout-cache -f -a' +test_debug 'find path*' + +if test -f path0 && test -d path1 && test -f path1/file1 +then + test_ok "checkout successful" +else + test_failure "checkout failed" +fi + +test_done + + Created: t/t1001-checkout-cache.sh (mode:100755) --- /dev/null +++ b/t/t1001-checkout-cache.sh @@ -0,0 +1,76 @@ +#!/bin/sh +# +# Copyright (c) 2005 Junio C Hamano +# + +. ./test-lib.sh +test_description "$@" 'git-checkout-cache test. + +This test registers the following filesystem structure in the cache: + + path0/file0 - a file in a directory + path1/file1 - a file in a directory + +and attempts to check it out when the work tree has: + + path0/file0 - a file in a directory + path1 - a symlink pointing at "path0" + +Checkout cache should fail to extract path1/file1 because the leading +path path1 is occupied by a non-directory. With "-f" it should remove +the symlink path1 and create directory path1 and file path1/file1. +' + +show_files() { + # show filesystem files, just [-dl] for type and name + find path? -ls | + sed -e 's/^[0-9]* * [0-9]* * \([-bcdl]\)[^ ]* *[0-9]* *[^ ]* *[^ ]* *[0-9]* [A-Z][a-z][a-z] [0-9][0-9] [^ ]* /fs: \1 /' + # what's in the cache, just mode and name + git-ls-files --stage | + sed -e 's/^\([0-9]*\) [0-9a-f]* [0-3] /ca: \1 /' + # what's in the tree, just mode and name. + git-ls-tree -r "$1" | + sed -e 's/^\([0-9]*\) [^ ]* [0-9a-f]* /tr: \1 /' +} + +mkdir path0 +date >path0/file0 +git-update-cache --add path0/file0 +tree1=$(git-write-tree) +test_debug 'show_files $tree1' + +mkdir path1 +date >path1/file1 +git-update-cache --add path1/file1 +tree2=$(git-write-tree) +test_debug 'show_files $tree2' + +rm -fr path1 +git-read-tree -m $tree1 +git-checkout-cache -f -a +test_debug 'show_files $tree1' + +ln -s path0 path1 +git-update-cache --add path1 +tree3=$(git-write-tree) +test_debug 'show_files $tree3' + +# Morten says "Got that?" here. +# Test begins. + +git-read-tree $tree2 +test_expect_success 'git-checkout-cache -f -a' +test_debug show_files $tree2 + +if test ! -h path0 && test -d path0 && + test ! -h path1 && test -d path1 && + test ! -h path0/file0 && test -f path0/file0 && + test ! -h path1/file1 && test -f path1/file1 +then + test_ok "checked out correctly." +else + test_failure "did not check out correctly." +fi + +test_done + Created: t/test-lib.sh (mode:100755) --- /dev/null +++ b/t/test-lib.sh @@ -0,0 +1,106 @@ +#!/bin/sh +# +# Copyright (c) 2005 Junio C Hamano +# + +# For repeatability, reset the environment to known value. +export LANG C +export TZ UTC +unset AUTHOR_DATE +unset AUTHOR_EMAIL +unset AUTHOR_NAME +unset COMMIT_AUTHOR_EMAIL +unset COMMIT_AUTHOR_NAME +unset GIT_ALTERNATE_OBJECT_DIRECTORIES +unset GIT_AUTHOR_DATE +unset GIT_AUTHOR_EMAIL +unset GIT_AUTHOR_NAME +unset GIT_COMMITTER_EMAIL +unset GIT_COMMITTER_NAME +unset GIT_DIFF_OPTS +unset GIT_DIR +unset GIT_EXTERNAL_DIFF +unset GIT_INDEX_FILE +unset GIT_OBJECT_DIRECTORY +unset SHA1_FILE_DIRECTORIES +unset SHA1_FILE_DIRECTORY + +# Each test should start with something like this, after copyright notices: +# +# . ./testlib.sh +# test_description "$@" 'Description of this test... +# This test checks if command xyzzy does the right thing... +# ' +# + +test_description () { + while case "$#" in 0) break;; esac + do + case "$1" in + -d|--d|--de|--deb|--debu|--debug) + debug=t; shift ;; + -h|--h|--he|--hel|--help) + eval echo '"$'$#'"' + exit 0 + ;; + *) + break ;; + esac + done + test_failure=0 +} + +say () { + echo "* $*" +} + +test_debug () { + case "$debug" in '') ;; ?*) eval "$*" ;; esac +} + +test_ok () { + echo "* $*"; +} + +test_failure () { + echo "* $*"; + test_failure=1; +} + +test_expect_failure () { + say "expecting failure: $1" + eval "$1" + case $? in + 0) test_failure "did not fail as expected" ;; + *) test_ok "failed as expected" ;; + esac +} + +test_expect_success () { + say "expecting success: $1" + eval "$1" + case $? in + 0) test_ok "succeeded as expected" ;; + *) test_failure "did not succeed as expected" ;; + esac +} + +test_done () { + case "$test_failure" in + 0) exit 0 ;; + '') echo "*** test script did not start with test_description"; + exit 2 ;; + *) exit 1 ;; + esac +} + +# Test the binaries we have just built. The tests are kept in +# t/ subdirectory and are run in test-repo subdirectory. +PATH=$(pwd)/..:$PATH + +# Test repository +test=test-repo +rm -fr "$test" +mkdir "$test" +cd "$test" +git-init-db 2>/dev/null || error "cannot run git-init-db" ------------------------------------------------ ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] Test suite 2005-05-12 0:01 ` [PATCH] " Junio C Hamano @ 2005-05-12 19:29 ` Petr Baudis 2005-05-12 19:48 ` Junio C Hamano ` (2 more replies) 0 siblings, 3 replies; 19+ messages in thread From: Petr Baudis @ 2005-05-12 19:29 UTC (permalink / raw) To: Junio C Hamano; +Cc: git Dear diary, on Thu, May 12, 2005 at 02:01:34AM CEST, I got a letter where Junio C Hamano <junkio@cox.net> told me that... > Commit 1da683e1247046796a094c4917bc0c4591530272 > Author Junio C Hamano <junkio@cox.net>, Wed May 11 16:59:35 2005 -0700 > Committer Junio C Hamano <junkio@cox.net>, Wed May 11 16:59:35 2005 -0700 > > Test suite: infrastructure and examples. > > This adds the test suite infrastructure with two example tests. > The current git-checkout-cache the example tests would fail this > test and will be corrected in a separate patch. > > Signed-off-by: Junio C Hamano <junkio@cox.net> Admittely, I'm not happy with this. From the design standpoint it looks mostly fine now, but the code is rather crude. I wanted to go over it at first and fix the obvious stuff, but it appeared to be overall quite broken, so I decided to return it to you for another iteration. I don't mind if you just fix the broken code for now, we can fix the semantics and design stuff later - what is in the patch is already good enough for the start. I'll drop the testcases from your other patches for now so that we don't get long stalls. > --- /dev/null > +++ b/t/test-lib.sh > +# For repeatability, reset the environment to known value. > +export LANG C > +export TZ UTC Dunno about *your* shell but this just exports variables $LANG, $C, $TZ and $UTC here. You probably wanted assignments there? > +# Each test should start with something like this, after copyright notices: > +# > +# . ./testlib.sh test-lib.sh > +# test_description "$@" 'Description of this test... > +# This test checks if command xyzzy does the right thing... > +# ' I think this usage is pretty weird. Why not just test_description='Description, blah blah.' . ./testlib.sh I think it would be quite less confusing than test_description, which actually does effectively something different anyway. > + > +test_description () { > + while case "$#" in 0) break;; esac Duh. This looks mysterious - why not a simple test? > + do > + case "$1" in > + -d|--d|--de|--deb|--debu|--debug) > + debug=t; shift ;; > + -h|--h|--he|--hel|--help) > + eval echo '"$'$#'"' > + exit 0 > + ;; > + *) > + break ;; This branch makes no sense, I think. > + esac > + done > + test_failure=0 > +} > + > +say () { > + echo "* $*" > +} > + > +test_debug () { > + case "$debug" in '') ;; ?*) eval "$*" ;; esac Again, why not a simple test? [ "$debug" ] && eval "$*" (Actually, eval will do the wrong thing here - it just concatenates the arguments. Just "$@" would do, I guess.) > +} > + > +test_ok () { > + echo "* $*"; > +} > + > +test_failure () { > + echo "* $*"; > + test_failure=1; > +} > + > +test_expect_failure () { > + say "expecting failure: $1" > + eval "$1" > + case $? in > + 0) test_failure "did not fail as expected" ;; > + *) test_ok "failed as expected" ;; > + esac > +} > + > +test_expect_success () { > + say "expecting success: $1" > + eval "$1" > + case $? in > + 0) test_ok "succeeded as expected" ;; > + *) test_failure "did not succeed as expected" ;; > + esac > +} > + > +test_done () { > + case "$test_failure" in > + 0) exit 0 ;; Please clean up after yourself in this case. > + '') echo "*** test script did not start with test_description"; > + exit 2 ;; > + *) exit 1 ;; > + esac > +} > + > +# Test the binaries we have just built. The tests are kept in > +# t/ subdirectory and are run in test-repo subdirectory. > +PATH=$(pwd)/..:$PATH > + > +# Test repository > +test=test-repo > +rm -fr "$test" > +mkdir "$test" > +cd "$test" > +git-init-db 2>/dev/null || error "cannot run git-init-db" But there's no 'error' thing. > --- /dev/null > +++ b/t/t1000-checkout-cache.sh > +git-update-cache --add path0 path1/file1 You should make sure even those preparations calls actually succeed. > --- /dev/null > +++ b/t/t1001-checkout-cache.sh > +git-update-cache --add path0/file0 > +tree1=$(git-write-tree) Here too. The testcases currently utterly fail, which is not good sign - either they are ahead of the current code, or they are broken. This is the main hurdle making me not accept it yet - it does not work for me. If you fix this and the nits above, it can go in, I think. * expecting failure: git-checkout-cache -a checkout-cache: path0 already exists error: checkout-cache: unable to create file path1/file1 (Not a directory) * did not fail as expected * expecting success: git-checkout-cache -f -a error: checkout-cache: unable to create file path0 (Is a directory) * succeeded as expected * checkout failed I consider this output... well, totally confusing. checkout-cache fails but testcase thinks it does not, then it fails again but testcase thinks it succeeded as expected but dies anyway. I think it's too messy. I would much more appreciate output like this: * checkout-cache test [1/3]: git-checkout-cache -a... passed * checkout-cache test [2/3]: git-checkout-cache -f -a... NOT PASSED Expected success, but the command failed. Output: error: checkout-cache: unable to create file path0 (Is a directory) * checkout-cache test [3/3]: git-checkout-cache foobar... NOT PASSED Expected failure, but the command succeeded. Much less cluttered, it is clear what went wrong etc. -- Petr "Pasky" Baudis Stuff: http://pasky.or.cz/ C++: an octopus made by nailing extra legs onto a dog. -- Steve Taylor ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] Test suite 2005-05-12 19:29 ` Petr Baudis @ 2005-05-12 19:48 ` Junio C Hamano 2005-05-12 23:51 ` [PATCH] Fix git-diff-files for symlinks Junio C Hamano 2005-05-13 0:14 ` [PATCH 0/3] Core GIT fixes and additions Junio C Hamano 2 siblings, 0 replies; 19+ messages in thread From: Junio C Hamano @ 2005-05-12 19:48 UTC (permalink / raw) To: Petr Baudis; +Cc: git >>>>> "PB" == Petr Baudis <pasky@ucw.cz> writes: >> --- /dev/null >> +++ b/t/test-lib.sh >> +# For repeatability, reset the environment to known value. >> +export LANG C >> +export TZ UTC These are just "Duh". Sorry. >> + >> +test_description () { >> + while case "$#" in 0) break;; esac PB> Duh. This looks mysterious - why not a simple test? Because I am old fashioned and am used to this kind of Bourne shell idioms. PB> This branch makes no sense, I think. Because...? PB> Again, why not a simple test? Again using case when test is not needed is a good old Bourne shell idiom. PB> [ "$debug" ] && eval "$*" PB> (Actually, eval will do the wrong thing here - it just concatenates the PB> arguments. Just "$@" would do, I guess.) No, I wrote "$*" because that was what I wanted and not "$@". I wanted to give eval a _single_ string. PB> Please clean up after yourself in this case. I would not mind, but I'd mildly disagree. The next test will start by cleaning up test-repo/ anyway. What we are lacking is t/Makefile that drives all the tests and clean-up can be done there. >> +git-init-db 2>/dev/null || error "cannot run git-init-db" PB> But there's no 'error' thing. Yes, and that way you can get an error message ;-). Simple oversight. PB> The testcases currently utterly fail, That is something I told you in the cover letter (or commit log). ^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH] Fix git-diff-files for symlinks. 2005-05-12 19:29 ` Petr Baudis 2005-05-12 19:48 ` Junio C Hamano @ 2005-05-12 23:51 ` Junio C Hamano 2005-05-13 0:14 ` [PATCH 0/3] Core GIT fixes and additions Junio C Hamano 2 siblings, 0 replies; 19+ messages in thread From: Junio C Hamano @ 2005-05-12 23:51 UTC (permalink / raw) To: Petr Baudis; +Cc: git Again I am not sure why this was missed during the last round, but git-diff-files mishandles symlinks on the filesystem. This patch fixes it. Signed-off-by: Junio C Hamano <junkio@cox.net> --- *** Petr, this one falls into "emergency obvious fix" category. *** I found it during adding more "basic" test to the test suite, *** which turns out to be quite useful. diff-files.c | 3 ++- 1 files changed, 2 insertions(+), 1 deletion(-) --- a/diff-files.c +++ b/diff-files.c @@ -126,7 +126,8 @@ continue; oldmode = ntohl(ce->ce_mode); - mode = S_IFREG | ce_permissions(st.st_mode); + mode = (S_ISLNK(st.st_mode) ? S_IFLNK : + S_IFREG | ce_permissions(st.st_mode)); show_modified(oldmode, mode, ce->sha1, null_sha1, ce->name); ------------------------------------------------ ^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH 0/3] Core GIT fixes and additions. 2005-05-12 19:29 ` Petr Baudis 2005-05-12 19:48 ` Junio C Hamano 2005-05-12 23:51 ` [PATCH] Fix git-diff-files for symlinks Junio C Hamano @ 2005-05-13 0:14 ` Junio C Hamano 2 siblings, 0 replies; 19+ messages in thread From: Junio C Hamano @ 2005-05-13 0:14 UTC (permalink / raw) To: Petr Baudis; +Cc: git Pasky, I am sending you three patches rediffed against the tip of git-pb tree for inclusion. * [PATCH 1/3] Introduce "rev-list --stop-at=<commit>". * [PATCH 2/3] Support symlinks in git-ls-files --others. * [PATCH 3/3] Add git-ls-files -k. The first one is independent from other two. 3/3 touches the same file as 2/3. ^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH] checkout-cache fix 2005-05-11 22:40 ` Test suite Petr Baudis 2005-05-12 0:01 ` [PATCH] " Junio C Hamano @ 2005-05-12 0:02 ` Junio C Hamano 2005-05-12 19:38 ` Petr Baudis 1 sibling, 1 reply; 19+ messages in thread From: Junio C Hamano @ 2005-05-12 0:02 UTC (permalink / raw) To: Petr Baudis; +Cc: git Commit cc01b05f0a3dfdf5ed114e429a7bec1ad549ab1c Author Junio C Hamano <junkio@cox.net>, Wed May 11 17:00:16 2005 -0700 Committer Junio C Hamano <junkio@cox.net>, Wed May 11 17:00:16 2005 -0700 Fix checkout-cache when existing work tree interferes with the checkout. This is essentially the same one as the last one I sent to the GIT list, except that the patch is rebased to the current tip of the git-pb tree, and an unnecessary call to create_directories() removed. The checkout-cache command gets confused when checking out a file in a subdirectory and the work tree has a symlink to the subdirectory. Also it fails to check things out when there is a non-directory in the work tree when cache expects a directory there, and vice versa. This patch fixes the first problem by making sure all the leading paths in the file being checked out are indeed directories, and also fixes directory vs non-directory conflicts when '-f' is specified by removing the offending paths. Signed-off-by: Junio C Hamano <junkio@cox.net> --- --- a/checkout-cache.c +++ b/checkout-cache.c @@ -32,6 +32,8 @@ * of "-a" causing problems (not possible in the above example, * but get used to it in scripting!). */ +#include <sys/types.h> +#include <dirent.h> #include "cache.h" static int force = 0, quiet = 0, not_new = 0; @@ -46,20 +48,61 @@ static void create_directories(const cha len = slash - path; memcpy(buf, path, len); buf[len] = 0; - mkdir(buf, 0755); + if (mkdir(buf, 0755)) { + if (errno == EEXIST) { + struct stat st; + if (!lstat(buf, &st) && S_ISDIR(st.st_mode)) + continue; /* ok */ + if (force && !unlink(buf) && !mkdir(buf, 0755)) + continue; + } + die("cannot create directory at %s", buf); + } } free(buf); } +static void remove_subtree(const char *path) +{ + DIR *dir = opendir(path); + struct dirent *de; + char pathbuf[PATH_MAX]; + char *name; + + if (!dir) + die("cannot opendir %s", path); + strcpy(pathbuf, path); + name = pathbuf + strlen(path); + *name++ = '/'; + while ((de = readdir(dir)) != NULL) { + struct stat st; + if ((de->d_name[0] == '.') && + ((de->d_name[1] == 0) || + ((de->d_name[1] == '.') && de->d_name[2] == 0))) + continue; + strcpy(name, de->d_name); + if (lstat(pathbuf, &st)) + die("cannot lstat %s", pathbuf); + if (S_ISDIR(st.st_mode)) + remove_subtree(pathbuf); + else if (unlink(pathbuf)) + die("cannot unlink %s", pathbuf); + } + closedir(dir); + if (rmdir(path)) + die("cannot rmdir %s", path); +} + static int create_file(const char *path, unsigned int mode) { int fd; mode = (mode & 0100) ? 0777 : 0666; + create_directories(path); fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode); if (fd < 0) { - if (errno == ENOENT) { - create_directories(path); + if (errno == EISDIR && force) { + remove_subtree(path); fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode); } } ------------------------------------------------ ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] checkout-cache fix 2005-05-12 0:02 ` [PATCH] checkout-cache fix Junio C Hamano @ 2005-05-12 19:38 ` Petr Baudis 2005-05-12 19:54 ` Junio C Hamano 0 siblings, 1 reply; 19+ messages in thread From: Petr Baudis @ 2005-05-12 19:38 UTC (permalink / raw) To: Junio C Hamano; +Cc: git Dear diary, on Thu, May 12, 2005 at 02:02:45AM CEST, I got a letter where Junio C Hamano <junkio@cox.net> told me that... > Commit cc01b05f0a3dfdf5ed114e429a7bec1ad549ab1c > Author Junio C Hamano <junkio@cox.net>, Wed May 11 17:00:16 2005 -0700 > Committer Junio C Hamano <junkio@cox.net>, Wed May 11 17:00:16 2005 -0700 > > Fix checkout-cache when existing work tree interferes with the checkout. Thanks, applied. A nit about the commit message, though - I'd prefer you to put this metadata stuff belong the --- separator, since they really do not belong to the log message. I've already seen something like this in one commit merged from git-jc (IIRC some of the Ingo Molnar's leak fixes), and it was a little PITA there since the first line was some Date: header but we tend to use the first line as the commit's caption at some places. Thanks, -- Petr "Pasky" Baudis Stuff: http://pasky.or.cz/ C++: an octopus made by nailing extra legs onto a dog. -- Steve Taylor ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] checkout-cache fix 2005-05-12 19:38 ` Petr Baudis @ 2005-05-12 19:54 ` Junio C Hamano 0 siblings, 0 replies; 19+ messages in thread From: Junio C Hamano @ 2005-05-12 19:54 UTC (permalink / raw) To: Petr Baudis; +Cc: git >>>>> "PB" == Petr Baudis <pasky@ucw.cz> writes: PB> ... I've already seen something like this PB> in one commit merged from git-jc (IIRC some of the Ingo Molnar's leak PB> fixes),... Yes, I am very unhappy about that commit, too. I was mucking with jit-commit command at the time, and apparently used a buggy one to make that commit---I realized what happened much later when I reviewed the commit log and it was already too late (I told everybody that the tree is published and pullable by then). Sorry about that. ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: "git-checkout-cache -f -a" failure 2005-05-10 22:57 ` Junio C Hamano 2005-05-10 23:03 ` Petr Baudis @ 2005-05-11 2:53 ` Junio C Hamano 1 sibling, 0 replies; 19+ messages in thread From: Junio C Hamano @ 2005-05-11 2:53 UTC (permalink / raw) To: Morten Welinder; +Cc: GIT Mailing List, Linus Torvalds Please forget about the previous one which was buggy when you had ".something" file under the directory being replaced. Could you give this patch a try? --- Running git-checkout-cache -f -a to check out a file in a directory fails when the work tree has a non-directory with the same name as the directory being checked out. Similarly it fails to check out a file when the work tree has a directory with the same name as the file being checked out. When '-f' is specified, the user is telling us that "I really want to match the work tree with what is in the cache." So removing the offending file or directory from the work tree to make room when necessary and possible in order to make the checkout succeed is the right thing to do. Signed-off-by: Junio C Hamano <junkio@cox.net> --- a/checkout-cache.c +++ b/checkout-cache.c @@ -32,6 +32,8 @@ * of "-a" causing problems (not possible in the above example, * but get used to it in scripting!). */ +#include <sys/types.h> +#include <dirent.h> #include "cache.h" static int force = 0, quiet = 0, not_new = 0; @@ -46,11 +48,51 @@ static void create_directories(const cha len = slash - path; memcpy(buf, path, len); buf[len] = 0; - mkdir(buf, 0755); + if (mkdir(buf, 0755)) { + if (errno == EEXIST) { + struct stat st; + if (!lstat(buf, &st) && S_ISDIR(st.st_mode)) + continue; /* ok */ + if (force && !unlink(buf) && !mkdir(buf, 0755)) + continue; + } + die("cannot create directory at %s", buf); + } } free(buf); } +static void remove_subtree(const char *path) +{ + DIR *dir = opendir(path); + struct dirent *de; + char pathbuf[PATH_MAX]; + char *name; + + if (!dir) + die("cannot opendir %s", path); + strcpy(pathbuf, path); + name = pathbuf + strlen(path); + *name++ = '/'; + while ((de = readdir(dir)) != NULL) { + struct stat st; + if ((de->d_name[0] == '.') && + ((de->d_name[1] == 0) || + (de->d_name[1] == '.') && de->d_name[2] == 0)) + continue; + strcpy(name, de->d_name); + if (lstat(pathbuf, &st)) + die("cannot lstat %s", pathbuf); + if (S_ISDIR(st.st_mode)) + remove_subtree(pathbuf); + else if (unlink(pathbuf)) + die("cannot unlink %s", pathbuf); + } + closedir(dir); + if (rmdir(path)) + die("cannot rmdir %s", path); +} + static int create_file(const char *path, unsigned int mode) { int fd; @@ -58,10 +100,14 @@ static int create_file(const char *path, mode = (mode & 0100) ? 0777 : 0666; fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode); if (fd < 0) { - if (errno == ENOENT) { + if ((errno == ENOENT) || (errno == ENOTDIR && force)) { create_directories(path); fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode); } + else if (errno == EISDIR && force) { + remove_subtree(path); + fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode); + } } return fd; } ------------------------------------------------ ^ permalink raw reply [flat|nested] 19+ messages in thread
end of thread, other threads:[~2005-05-13 0:07 UTC | newest] Thread overview: 19+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2005-05-09 18:25 "git-checkout-cache -f -a" failure Morten Welinder 2005-05-10 2:29 ` Junio C Hamano 2005-05-10 3:04 ` Morten Welinder 2005-05-10 22:57 ` Junio C Hamano 2005-05-10 23:03 ` Petr Baudis 2005-05-11 5:16 ` Junio C Hamano 2005-05-11 18:31 ` Morten Welinder 2005-05-11 21:37 ` Junio C Hamano 2005-05-11 22:12 ` Junio C Hamano 2005-05-11 22:40 ` Test suite Petr Baudis 2005-05-12 0:01 ` [PATCH] " Junio C Hamano 2005-05-12 19:29 ` Petr Baudis 2005-05-12 19:48 ` Junio C Hamano 2005-05-12 23:51 ` [PATCH] Fix git-diff-files for symlinks Junio C Hamano 2005-05-13 0:14 ` [PATCH 0/3] Core GIT fixes and additions Junio C Hamano 2005-05-12 0:02 ` [PATCH] checkout-cache fix Junio C Hamano 2005-05-12 19:38 ` Petr Baudis 2005-05-12 19:54 ` Junio C Hamano 2005-05-11 2:53 ` "git-checkout-cache -f -a" failure Junio C Hamano
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).