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 <ayieh...@gmail.com>
---
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 majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to