This is similar in spirit to to "make -C dir ..." and "tar -C dir ...". Currently finding out the status of a git repository that is located away from the current working directory without going to that directory can be done in the following ways: 1. (cd ~/foo && git status) 2. git --git-dir=~/foo/.git --work-dir=~/foo status 3. GIT_DIR=~/foo/.git GIT_WORK_TREE=~/foo git status 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 a bit more tersely: $ git -C ~/foo status 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> --- Jeff: Thanks for pointing out the mistakes. But I did not address your concern: > I know you are copying this from the other options in the same function, > but I wonder if they should all be calling "error()" (and dropping the > terminating ".") to better match our usual error messages. because I'd rather have that fix be done in a separate topic. The other points raised are all valid and fixed in this new patch. 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 6a875f2..6064b3d 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -379,6 +379,19 @@ displayed. See linkgit:git-help[1] for more information, because `git --help ...` is converted internally into `git help ...`. +-C <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 + 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 1ada169..a8731e9 100644 --- a/git.c +++ b/git.c @@ -6,7 +6,7 @@ #include "run-command.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" @@ -53,7 +53,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' + +. ./test-lib.sh + +test_expect_success '"git -C <dir>" runs git from the directory <dir>' ' + 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" +' + +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" +' + +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.2.1.339.g52a3e01 -- 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