From 44e1e4d67d5148c245db362cc48c3cc6c2ec82ca Mon Sep 17 00:00:00 2001 From: Nazri Ramliy Date: Mon, 9 Sep 2013 21:47:43 +0800 Subject: git: run in a directory given with -C option This is similar in spirit to "make -C dir ..." and "tar -C dir ...". It takes more keypresses to invoke git command in a different directory 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 The methods shown above are acceptable for scripting but are too cumbersome for quick command line invocations. With this new option, the above can be done with fewer keystrokes: 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 Signed-off-by: Junio C Hamano diff --git a/Documentation/git.txt b/Documentation/git.txt index dca11cc..d6baf98 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -9,7 +9,7 @@ git - the stupid content tracker SYNOPSIS -------- [verse] -'git' [--version] [--help] [-c =] +'git' [--version] [--help] [-C ] [-c =] [--exec-path[=]] [--html-path] [--man-path] [--info-path] [-p|--paginate|--no-pager] [--no-replace-objects] [--bare] [--git-dir=] [--work-tree=] [--namespace=] @@ -395,6 +395,20 @@ displayed. See linkgit:git-help[1] for more information, because `git --help ...` is converted internally into `git help ...`. +-C :: + Run as if git was started in '' instead of the current working + directory. When multiple `-C` options are given, each subsequent + non-absolute `-C ` is interpreted relative to the preceding `-C + `. ++ +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 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 =:: 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..a2d99a7 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 ] [-c name=value]\n" " [--exec-path[=]] [--html-path] [--man-path] [--info-path]\n" " [-p|--paginate|--no-pager] [--no-replace-objects] [--bare]\n" " [--git-dir=] [--work-tree=] [--namespace=]\n" @@ -153,6 +153,17 @@ static int handle_options(const char ***argv, int *argc, int *envchanged) set_alternate_shallow_file((*argv)[0]); if (envchanged) *envchanged = 1; + } else 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 { fprintf(stderr, "Unknown option: %s\n", cmd); usage(git_usage_string); diff --git a/t/t0056-git-C.sh b/t/t0056-git-C.sh new file mode 100755 index 0000000..c0006da --- /dev/null +++ b/t/t0056-git-C.sh @@ -0,0 +1,82 @@ +#!/bin/sh + +test_description='"-C " option and its effects on other path-related options' + +. ./test-lib.sh + +test_expect_success '"git -C " runs git from the directory ' ' + test_create_repo dir1 && + echo 1 >dir1/a.txt && + (cd dir1 && git add a.txt && git commit -m "initial in dir1") && + echo "initial in dir1" >expected && + git -C dir1 log --format=%s >actual && + test_cmp 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 && + echo "initial in dir1/dir2" >expected && + git -C dir1/dir2 commit -m "initial in dir1/dir2" && + git -C dir1 -C dir2 log --format=%s >actual && + test_cmp 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" && + git --git-dir=c/a.git log -1 --format=%s >expected && + git -C c --git-dir=a.git log -1 --format=%s >actual && + test_cmp expected actual +' + +test_expect_success 'Order should not matter: "--git-dir=a.git -C c" is equivalent to "-C c --git-dir=a.git"' ' + git -C c --git-dir=a.git log -1 --format=%s >expected && + git --git-dir=a.git -C c log -1 --format=%s >actual && + test_cmp 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 && + git --git-dir=c/a.git --work-tree=c/a status >expected && + git -C c/a.git --work-tree=../a status >actual && + test_cmp 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"' ' + git -C c/a.git --work-tree=../a status >expected && + git --work-tree=../a -C c/a.git status >actual && + test_cmp 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"' ' + git --git-dir=c/a.git --work-tree=c/a status >expected && + git -C c --git-dir=a.git --work-tree=a status >actual && + test_cmp 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"' ' + git -C c --git-dir=a.git --work-tree=a status >expected && + git --git-dir=a.git -C c --work-tree=a status >actual && + test_cmp 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"' ' + git -C c --git-dir=a.git --work-tree=a status >expected && + git --git-dir=a.git --work-tree=a -C c status >actual && + test_cmp expected actual +' + +test_expect_success 'Relative followed by fullpath: "-C ./here -C /there" is equivalent to "-C /there"' ' + echo "initial in dir1/dir2" >expected && + git -C dir1 -C "$(pwd)/dir1/dir2" log --format=%s >actual && + test_cmp expected actual +' + +test_done -- cgit v0.10.2-6-g49f6 From 28a81f8b93897402a8c8c15ee16e67f0c24ac11c Mon Sep 17 00:00:00 2001 From: Nazri Ramliy Date: Thu, 19 Sep 2013 21:18:54 +0800 Subject: t0056: "git -C" test updates Instead of repeating the text to record as the commit log message and string we expect to see in "log" output, use the same variable to avoid them going out of sync. Use different names for test files in different directories to improve our chance to catch future breakages that makes "-C " go to a place that is different from what was specified. Signed-off-by: Nazri Ramliy Signed-off-by: Junio C Hamano diff --git a/t/t0056-git-C.sh b/t/t0056-git-C.sh index c0006da..99c0377 100755 --- a/t/t0056-git-C.sh +++ b/t/t0056-git-C.sh @@ -7,18 +7,20 @@ test_description='"-C " option and its effects on other path-related optio test_expect_success '"git -C " runs git from the directory ' ' test_create_repo dir1 && echo 1 >dir1/a.txt && - (cd dir1 && git add a.txt && git commit -m "initial in dir1") && - echo "initial in dir1" >expected && + msg="initial in dir1" && + (cd dir1 && git add a.txt && git commit -m "$msg") && + echo "$msg" >expected && git -C dir1 log --format=%s >actual && test_cmp 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 && - echo "initial in dir1/dir2" >expected && - git -C dir1/dir2 commit -m "initial in dir1/dir2" && + echo 1 >dir1/dir2/b.txt && + git -C dir1/dir2 add b.txt && + msg="initial in dir1/dir2" && + echo "$msg" >expected && + git -C dir1/dir2 commit -m "$msg" && git -C dir1 -C dir2 log --format=%s >actual && test_cmp expected actual ' -- cgit v0.10.2-6-g49f6