On Fri, Aug 30, 2013 at 9:35 AM, Nazri Ramliy <ayiehere@xxxxxxxxx> wrote: > This is similar in spirit to to "make -C dir ..." and "tar -C dir ...". > > Currently it takes more effort (keypresses) to invoke git command in a > different directory than the current one without leaving the current > directory: > > 1. (cd ~/foo && git status) > git --git-dir=~/foo/.git --work-dir=~/foo status > GIT_DIR=~/foo/.git GIT_WORK_TREE=~/foo git status > 2. (cd ../..; git grep foo) > 3. for d in d1 d2 d3; do (cd $d && git svn rebase); done > > While doable the methods shown above are arguably more suitable for > scripting than quick command line invocations. > > With this new option, the above can be done with less keystrokes: Grammar: s/less/fewer/ More below... > 1. git -C ~/foo status > 2. git -C ../.. grep foo > 3. for d in d1 d2 d3; do git -C $d svn rebase; done > > A new test script is added to verify the behavior of this option with > other path-related options like --git-dir and --work-tree. > > Signed-off-by: Nazri Ramliy <ayiehere@xxxxxxxxx> > --- > This is a reroll of [1]. The only difference is the rewording of the > commit message. I'm resending this as I've found it to be useful in my > daily git usage in that it helps me stay focused on what I'm doing in > the current directory while needing to run git on another directory. > > nazri. > > [1] http://permalink.gmane.org/gmane.comp.version-control.git/221954 > > Documentation/git.txt | 13 +++++++++ > git.c | 15 ++++++++-- > t/t0056-git-C.sh | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 102 insertions(+), 2 deletions(-) > create mode 100755 t/t0056-git-C.sh > > diff --git a/Documentation/git.txt b/Documentation/git.txt > index dca11cc..0d44fa2 100644 > --- a/Documentation/git.txt > +++ b/Documentation/git.txt > @@ -395,6 +395,19 @@ displayed. See linkgit:git-help[1] for more information, > because `git --help ...` is converted internally into `git > help ...`. > > +-C <directory>:: The synopsis at the top of git.txt mentions --git-dir and --work-tree. For consistency, -C probably ought to be mentioned there, as well. Other options which accept a directory, such as --git-dir and --work-tree, are documented as accepting <path>, but -C is inconsistently documented as accepting <directory>. > + Run as if git were started in <directory> instead of the current > + working directory. If multiple -C options are given, subsequent > + directory arguments are interpreted relative to the previous one: -C > + /usr -C src is equivalent to -C /usr/src. This option affects options The fragment "interpreted relative" seems ambiguous when absolute paths are involved. For instance, what happens when the user specifies "-C /foo/ -C /bar/". From the implementation I can see that the working directory becomes /bar, but without checking the implementation, it's not clear what the result would be. For instance, if the implementation merely did string concatenation of the -C arguments, then input "-C /foo/ -C /bar/" might try to set the working directory to "/foo//bar/" which would be interpreted as "/foo/bar/" on Unix, but would probably fail on Windows. Perhaps rewriting might remove the ambiguity? [...] When multiple -C options are given, each subsequent non-absolute -C <path> is interpreted relative to the preceding -C <path>. [...] > + that expect path name like --git-dir and --work-tree in that their > + interpretations of the path names would be made relative to the > + effective working directory caused by the -C option. For example the > + following invocations are equivalent: > + > + git --git-dir=a.git --work-tree=b -C c status > + git --git-dir=c/a.git --work-tree=c/b status > + > -c <name>=<value>:: > Pass a configuration parameter to the command. The value > given will override values from configuration files. > diff --git a/git.c b/git.c > index 2025f77..2207ee5 100644 > --- a/git.c > +++ b/git.c > @@ -7,7 +7,7 @@ > #include "commit.h" > > const char git_usage_string[] = > - "git [--version] [--help] [-c name=value]\n" > + "git [--version] [--help] [-C directory] [-c name=value]\n" > " [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\n" > " [-p|--paginate|--no-pager] [--no-replace-objects] [--bare]\n" > " [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n" For existing options accepting an argument, the argument is formatted as <argument>. The -C option does not follow suit. As mentioned above, all other options accepting a directory are documented as taking <path>, but -C is inconsistent and is documented as taking 'directory' instead. > @@ -54,7 +54,18 @@ static int handle_options(const char ***argv, int *argc, int *envchanged) > /* > * Check remaining flags. > */ > - if (!prefixcmp(cmd, "--exec-path")) { > + if (!strcmp(cmd, "-C")) { > + if (*argc < 2) { > + fprintf(stderr, "No directory given for -C.\n" ); > + usage(git_usage_string); > + } > + if (chdir((*argv)[1])) > + die_errno("Cannot change to '%s'", (*argv)[1]); > + if (envchanged) > + *envchanged = 1; > + (*argv)++; > + (*argc)--; > + } else if (!prefixcmp(cmd, "--exec-path")) { > cmd += 11; > if (*cmd == '=') > git_set_argv_exec_path(cmd + 1); > diff --git a/t/t0056-git-C.sh b/t/t0056-git-C.sh > new file mode 100755 > index 0000000..370eae6 > --- /dev/null > +++ b/t/t0056-git-C.sh > @@ -0,0 +1,76 @@ > +#!/bin/sh > + > +test_description='"-C <directory>" option and it effects on other path-related options' s/it/its/ s/<directory>/<path>/ > + > +. ./test-lib.sh > + > +test_expect_success '"git -C <dir>" runs git from the directory <dir>' ' s/<dir>/<path>/g > + test_create_repo dir1 && > + echo 1 >dir1/a.txt && > + (cd dir1 && git add a.txt && git commit -m "initial in dir1") && > + expected="initial in dir1" && > + actual=$(git -C dir1 log --format=%s) && > + test "$expected" = "$actual" > +' Modern git tests tend to place the expected and actual outputs in files and then use test_cmp to verify that they are identical. For instance: echo "initial in dir1" >expected && git -C dir1 log --format="%s" >actual && test_cmp expected actual The benefit of doing so is that test_cmp will print the difference between the expected and actual values when they differ, which facilitates debugging a failed test. > +test_expect_success 'Multiple -C options: "-C dir1 -C dir2" is equivalent to "-C dir1/dir2"' ' > + test_create_repo dir1/dir2 && > + echo 1 >dir1/dir2/a.txt && > + git -C dir1/dir2 add a.txt && > + expected="initial in dir1/dir2" && > + git -C dir1/dir2 commit -m "$expected" && > + actual=$(git -C dir1 -C dir2 log --format=%s) && > + test "$expected" = "$actual" > +' It would make sense also to test multiple -C options with combinations of absolute and and relative paths. > +test_expect_success 'Effect on --git-dir option: "-C c --git-dir=a.git" is equivalent to "--git-dir c/a.git"' ' > + mkdir c && > + mkdir c/a && > + mkdir c/a.git && > + (cd c/a.git && git init --bare) && > + echo 1 >c/a/a.txt && > + git --git-dir c/a.git --work-tree=c/a add a.txt && > + git --git-dir c/a.git --work-tree=c/a commit -m "initial" && > + expected="$(git --git-dir=c/a.git log -1 --format=%s)" && > + actual=$(git -C c --git-dir=a.git log -1 --format=%s) && > + test "$expected" = "$actual" > +' > + > +test_expect_success 'Order should not matter: "--git-dir=a.git -C c" is equivalent to "-C c --git-dir=a.git"' ' > + expected="$(git -C c --git-dir=a.git log -1 --format=%s)" && > + actual=$(git --git-dir=a.git -C c log -1 --format=%s) && > + test "$expected" = "$actual" > +' > + > +test_expect_success 'Effect on --work-tree option: "-C c/a.git --work-tree=../a" is equivalent to "--work-tree=c/a --git-dir=c/a.git"' ' > + rm c/a/a.txt && > + expected="$(git --git-dir=c/a.git --work-tree=c/a status)" && > + actual="$(git -C c/a.git --work-tree=../a status)" && > + test "$expected" = "$actual" > +' > + > +test_expect_success 'Order should not matter: "--work-tree=../a -C c/a.git" is equivalent to "-C c/a.git --work-tree=../a"' ' > + expected="$(git -C c/a.git --work-tree=../a status)" && > + actual="$(git --work-tree=../a -C c/a.git status)" && > + test "$expected" = "$actual" > +' > + > +test_expect_success 'Effect on --git-dir and --work-tree options - "-C c --git-dir=a.git --work-tree=a" is equivalent to "--git-dir=c/a.git --work-tree=c/a"' ' > + expected="$(git --git-dir=c/a.git --work-tree=c/a status)" && > + actual="$(git -C c --git-dir=a.git --work-tree=a status)" && > + test "$expected" = "$actual" > +' > + > +test_expect_success 'Order should not matter: "-C c --git-dir=a.git --work-tree=a" is equivalent to "--git-dir=a.git -C c --work-tree=a"' ' > + expected="$(git -C c --git-dir=a.git --work-tree=a status)" && > + actual="$(git --git-dir=a.git -C c --work-tree=a status)" && > + test "$expected" = "$actual" > +' > + > +test_expect_success 'Order should not matter: "-C c --git-dir=a.git --work-tree=a" is equivalent to "--git-dir=a.git --work-tree=a -C c"' ' > + expected="$(git -C c --git-dir=a.git --work-tree=a status)" && > + actual="$(git --git-dir=a.git --work-tree=a -C c status)" && > + test "$expected" = "$actual" > +' > + > +test_done > -- > 1.8.4.1.g098df5a -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html