git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH] Teach git to change to a given directory using -C option

From
Nazri Ramliy <ayiehere@gmail.com>
Date
Apr 21, 2013, 13:48 UTC
Message-ID
<1366552126-6090-1-git-send-email-ayiehere@gmail.com>
In-Reply-To
<20130419161250.GC14263@sigill.intra.peff.net>
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@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
Previous: Jeff King
Message 5 of 5 in “Teach git to change to a given directory using -C option”
  1. Teach git to change to a given directory using -C optionNazri Ramliy, Apr 19, 2013
  2. Jeff KingApr 19, 2013
  3. Jonathan NiederApr 20, 2013
  4. Jeff KingApr 21, 2013
  5. Teach git to change to a given directory using -C optionNazri Ramliy, Apr 21, 2013

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.