{"thread":{"id":"34832","subject":"[PATCH v2] Teach git to change to a given directory using -C option","startedAt":"2013-09-02T13:39:58Z","lastAt":"2013-09-03T07:42:45Z","messageCount":2,"participants":["Nazri Ramliy","Eric Sunshine"],"isPatch":true,"patchVersion":2,"patchTotal":null},"messages":[{"id":"226532","messageId":"20130902133911.GA23924@gmail.com","threadId":"34832","inReplyTo":null,"subject":"[PATCH v2] Teach git to change to a given directory using -C option","fromName":"Nazri Ramliy","fromEmail":"ayiehere@gmail.com","sentAt":"2013-09-02T13:39:58Z","receivedAt":"2013-09-02T13:39:58Z","isPatch":true,"sender":{"key":"ayiehere@gmail.com","avatar":"https://avatars.githubusercontent.com/u/164756?v=4"},"body":"On Sun, Sep 01, 2013 at 12:48:23AM -0400, Eric Sunshine wrote:\n> On Fri, Aug 30, 2013 at 9:35 AM, Nazri Ramliy <ayiehere@gmail.com> wrote:\n>> With this new option, the above can be done with less keystrokes:\n>\n> Grammar: s/less/fewer/\n>\n> More below...\n\nThanks for taking the time to review this patch! The fix for the\nabove, and the following issues are in the re-roll below.\n\n> The synopsis at the top of git.txt mentions --git-dir and --work-tree.\n> For consistency, -C probably ought to be mentioned there, as well.\n\nFixed.\n\n\n> Other options which accept a directory, such as --git-dir and\n> --work-tree, are documented as accepting <path>, but -C is\n> inconsistently documented as accepting <directory>.\n\nFixed.\n\n\n>> +       Run as if git were started in <directory> instead of the current\n>> +       working directory. If multiple -C options are given, subsequent\n>> +       directory arguments are interpreted relative to the previous one: -C\n>> +       /usr -C src is equivalent to -C /usr/src. This option affects options\n>\n> The fragment \"interpreted relative\" seems ambiguous when absolute\n> paths are involved.\n\nIn this re-roll the above text is now:\n\n -C <path>::\n \tRun as if git were started in <path> instead of the current working\n \tdirectory. If multiple -C options are given, subsequent relative <path>\n \targuments is interpreted relative to the previous effective directory:\n \t\"-C /usr -C src\" is equivalent to \"-C /usr/src\", while \"-C src -C /usr\"\n \tis equivalent to \"C /usr\". This option ...\n\n\n> For existing options accepting an argument, the argument is formatted\n> as <argument>. The -C option does not follow suit.\n>\n> As mentioned above, all other options accepting a directory are\n> documented as taking <path>, but -C is inconsistent and is documented\n> as taking 'directory' instead.\n\nFixed as \"[-C <path>]\"\n\n\n>> +test_description='\"-C <directory>\" option and it effects on other path-related options'\n>\n> s/it/its/\n> s/<directory>/<path>/\n\nFixed.\n\n\n>> +test_expect_success '\"git -C <dir>\" runs git from the directory <dir>' '\n>\n> s/<dir>/<path>/g\n\nFixed.\n\n\n> Modern git tests tend to place the expected and actual outputs in\n> files and then use test_cmp to verify that they are identical. For\n> instance:\n>\n>     echo \"initial in dir1\" >expected &&\n>     git -C dir1 log --format=\"%s\" >actual &&\n>     test_cmp expected actual\n\nFixed.\n\n\n> It would make sense also to test multiple -C options with combinations\n> of absolute and and relative paths.\n\nFixed - I've added one more test for testing that \"-C ./here -C /there\"\nis equivalent to \"-C /there\" at the end of t0056-git-C.sh.\n\nnazri\n-- >8 --\nSubject: [PATCH] Teach git to change to a given directory using -C option\n\nThis is similar in spirit to to \"make -C dir ...\" and \"tar -C dir ...\".\n\nCurrently it takes more effort (keypresses) to invoke git command in a\ndifferent directory than the current one without leaving the current\ndirectory:\n\n    1. (cd ~/foo && git status)\n       git --git-dir=~/foo/.git --work-dir=~/foo status\n       GIT_DIR=~/foo/.git GIT_WORK_TREE=~/foo git status\n    2. (cd ../..; git grep foo)\n    3. for d in d1 d2 d3; do (cd $d && git svn rebase); done\n\nWhile doable the methods shown above are arguably more suitable for\nscripting than quick command line invocations.\n\nWith this new option, the above can be done with fewer keystrokes:\n\n    1. git -C ~/foo status\n    2. git -C ../.. grep foo\n    3. for d in d1 d2 d3; do git -C $d svn rebase; done\n\nA new test script is added to verify the behavior of this option with\nother path-related options like --git-dir and --work-tree.\n\nSigned-off-by: Nazri Ramliy <ayiehere@gmail.com>\n---\n Documentation/git.txt | 16 +++++++++-\n git.c                 | 15 ++++++++--\n t/t0056-git-C.sh      | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 111 insertions(+), 3 deletions(-)\n create mode 100755 t/t0056-git-C.sh\n\ndiff --git a/Documentation/git.txt b/Documentation/git.txt\nindex 83edf30..7a1369a 100644\n--- a/Documentation/git.txt\n+++ b/Documentation/git.txt\n@@ -9,7 +9,7 @@ git - the stupid content tracker\n SYNOPSIS\n --------\n [verse]\n-'git' [--version] [--help] [-c <name>=<value>]\n+'git' [--version] [--help] [-C <path>] [-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@@ -395,6 +395,20 @@ displayed. See linkgit:git-help[1] for more information,\n because `git --help ...` is converted internally into `git\n help ...`.\n \n+-C <path>::\n+\tRun as if git were started in <path> instead of the current working\n+\tdirectory. If multiple -C options are given, subsequent relative <path>\n+\targuments are interpreted relative to the previous effective directory:\n+\t\"-C /usr -C src\" is equivalent to \"-C /usr/src\", while \"-C src -C /usr\"\n+\tis equivalent to \"C /usr\". This option affects options that expect path\n+\tname like --git-dir and --work-tree in that their interpretations of\n+\tthe path names would be made relative to the effective working\n+\tdirectory caused by the -C option. For example the following\n+\tinvocations are equivalent:\n+\n+\t    git --git-dir=a.git --work-tree=b -C c status\n+\t    git --git-dir=c/a.git --work-tree=c/b status\n+\n -c <name>=<value>::\n \tPass a configuration parameter to the command. The value\n \tgiven will override values from configuration files.\ndiff --git a/git.c b/git.c\nindex 2025f77..52bce74 100644\n--- a/git.c\n+++ b/git.c\n@@ -7,7 +7,7 @@\n #include \"commit.h\"\n \n const char git_usage_string[] =\n-\t\"git [--version] [--help] [-c name=value]\\n\"\n+\t\"git [--version] [--help] [-C <path>] [-c name=value]\\n\"\n \t\"           [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\\n\"\n \t\"           [-p|--paginate|--no-pager] [--no-replace-objects] [--bare]\\n\"\n \t\"           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\\n\"\n@@ -54,7 +54,18 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)\n \t\t/*\n \t\t * Check remaining flags.\n \t\t */\n-\t\tif (!prefixcmp(cmd, \"--exec-path\")) {\n+\t\tif (!strcmp(cmd, \"-C\")) {\n+\t\t\tif (*argc < 2) {\n+\t\t\t\tfprintf(stderr, \"No directory given for -C.\\n\" );\n+\t\t\t\tusage(git_usage_string);\n+\t\t\t}\n+\t\t\tif (chdir((*argv)[1]))\n+\t\t\t\tdie_errno(\"Cannot change to '%s'\", (*argv)[1]);\n+\t\t\tif (envchanged)\n+\t\t\t\t*envchanged = 1;\n+\t\t\t(*argv)++;\n+\t\t\t(*argc)--;\n+\t\t} else if (!prefixcmp(cmd, \"--exec-path\")) {\n \t\t\tcmd += 11;\n \t\t\tif (*cmd == '=')\n \t\t\t\tgit_set_argv_exec_path(cmd + 1);\ndiff --git a/t/t0056-git-C.sh b/t/t0056-git-C.sh\nnew file mode 100755\nindex 0000000..7dc1e48\n--- /dev/null\n+++ b/t/t0056-git-C.sh\n@@ -0,0 +1,83 @@\n+#!/bin/sh\n+\n+test_description='\"-C <path>\" option and its effects on other path-related options'\n+\n+. ./test-lib.sh\n+\n+test_expect_success '\"git -C <path>\" runs git from the directory <path>' '\n+\ttest_create_repo dir1 &&\n+\techo 1 >dir1/a.txt &&\n+\t(cd dir1 && git add a.txt && git commit -m \"initial in dir1\") &&\n+\techo \"initial in dir1\" >expected &&\n+\tgit -C dir1 log --format=%s >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'Multiple -C options: \"-C dir1 -C dir2\" is equivalent to \"-C dir1/dir2\"' '\n+\ttest_create_repo dir1/dir2 &&\n+\techo 1 >dir1/dir2/a.txt &&\n+\tgit -C dir1/dir2 add a.txt &&\n+\texpected=\"initial in dir1/dir2\"\n+\techo $expected >expected &&\n+\tgit -C dir1/dir2 commit -m \"$expected\" &&\n+\tgit -C dir1 -C dir2 log --format=%s >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'Effect on --git-dir option: \"-C c --git-dir=a.git\" is equivalent to \"--git-dir c/a.git\"' '\n+\tmkdir c &&\n+\tmkdir c/a &&\n+\tmkdir c/a.git &&\n+\t(cd c/a.git && git init --bare) &&\n+\techo 1 >c/a/a.txt &&\n+\tgit --git-dir c/a.git --work-tree=c/a add a.txt &&\n+\tgit --git-dir c/a.git --work-tree=c/a commit -m \"initial\" &&\n+\tgit --git-dir=c/a.git log -1 --format=%s >expected &&\n+\tgit -C c --git-dir=a.git log -1 --format=%s >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'Order should not matter: \"--git-dir=a.git -C c\" is equivalent to \"-C c --git-dir=a.git\"' '\n+\tgit -C c --git-dir=a.git log -1 --format=%s >expected &&\n+\tgit --git-dir=a.git -C c log -1 --format=%s >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+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\"' '\n+\trm c/a/a.txt &&\n+\tgit --git-dir=c/a.git --work-tree=c/a status >expected &&\n+\tgit -C c/a.git --work-tree=../a status >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'Order should not matter: \"--work-tree=../a -C c/a.git\" is equivalent to \"-C c/a.git --work-tree=../a\"' '\n+\tgit -C c/a.git --work-tree=../a status >expected &&\n+\tgit --work-tree=../a -C c/a.git status >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+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\"' '\n+\tgit --git-dir=c/a.git --work-tree=c/a status >expected &&\n+\tgit -C c --git-dir=a.git --work-tree=a status >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+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\"' '\n+\tgit -C c --git-dir=a.git --work-tree=a status >expected &&\n+\tgit --git-dir=a.git -C c --work-tree=a status >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+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\"' '\n+\tgit -C c --git-dir=a.git --work-tree=a status >expected &&\n+\tgit --git-dir=a.git --work-tree=a -C c status >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'Relative followed by fullpath: \"-C ./here -C /there\" is equivalent to \"-C /there\"' '\n+\techo \"initial in dir1/dir2\" >expected &&\n+\tgit -C dir1 -C \"$PWD/dir1/dir2\" log --format=%s >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_done\n-- \n1.8.4.24.g5fcd118\n"},{"id":"226600","messageId":"CAPig+cTQrjmiKYWoo57BTBNS0nuR+NMDf8uCk5EqAvcMzr0iVA@mail.gmail.com","threadId":"34832","inReplyTo":"20130902133911.GA23924@gmail.com","subject":"Re: [PATCH v2] Teach git to change to a given directory using -C option","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2013-09-03T07:42:45Z","receivedAt":"2013-09-03T07:42:45Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Sep 2, 2013 at 9:39 AM, Nazri Ramliy <ayiehere@gmail.com> wrote:\n> diff --git a/Documentation/git.txt b/Documentation/git.txt\n> index 83edf30..7a1369a 100644\n> --- a/Documentation/git.txt\n> +++ b/Documentation/git.txt\n> @@ -395,6 +395,20 @@ displayed. See linkgit:git-help[1] for more information,\n>  because `git --help ...` is converted internally into `git\n>  help ...`.\n>\n> +-C <path>::\n> +       Run as if git were started in <path> instead of the current working\n> +       directory. If multiple -C options are given, subsequent relative <path>\n> +       arguments are interpreted relative to the previous effective directory:\n> +       \"-C /usr -C src\" is equivalent to \"-C /usr/src\", while \"-C src -C /usr\"\n> +       is equivalent to \"C /usr\". This option affects options that expect path\n\nNo wish to bike-shed, however, I find \"effective directory\" somewhat\ndifficult to digest due to its jargony feel. It also seems ambiguous\n(to me) since \"previous effective directory\" may mean \"directory when\ngit was run\" or \"directory set by most recent -C\". My earlier\nsuggestion\n\n    When multiple -C options are given, each subsequent non-absolute\n    -C <path> is interpreted relative to the preceding -C <path>.\n\navoided jargon and left no room for ambiguity.\n\nHowever, perhaps the examples are clear enough to make excessive prose\nexplanation unnecessary, thus:\n\n    Run as if git was started in <path> instead of the current\n    working directory. Multiple -C options are allowed and acted upon\n    in the order given, thus \"-C /usr -C src\" is equivalent to \"-C\n    /usr/src\", and \"-C src -C /usr\" is equivalent to \"C /usr\". This\n    option affects ...\n\n> diff --git a/t/t0056-git-C.sh b/t/t0056-git-C.sh\n> new file mode 100755\n> index 0000000..7dc1e48\n> --- /dev/null\n> +++ b/t/t0056-git-C.sh\n> @@ -0,0 +1,83 @@\n> +#!/bin/sh\n> +\n> +test_description='\"-C <path>\" option and its effects on other path-related options'\n> +\n> +. ./test-lib.sh\n> +\n> +test_expect_success '\"git -C <path>\" runs git from the directory <path>' '\n> +       test_create_repo dir1 &&\n> +       echo 1 >dir1/a.txt &&\n> +       (cd dir1 && git add a.txt && git commit -m \"initial in dir1\") &&\n> +       echo \"initial in dir1\" >expected &&\n> +       git -C dir1 log --format=%s >actual &&\n> +       test_cmp expected actual\n> +'\n> +\n> +test_expect_success 'Multiple -C options: \"-C dir1 -C dir2\" is equivalent to \"-C dir1/dir2\"' '\n> +       test_create_repo dir1/dir2 &&\n> +       echo 1 >dir1/dir2/a.txt &&\n> +       git -C dir1/dir2 add a.txt &&\n> +       expected=\"initial in dir1/dir2\"\n> +       echo $expected >expected &&\n> +       git -C dir1/dir2 commit -m \"$expected\" &&\n\nIt's curious that this test uses a variable ($expected) to avoid\nrepeating literal \"initial in dir1/dir2\", however, the previous test\nrepeats its literal \"initial in dir1\". (IMHO, the repeated literal\nactually makes the test a bit easier to read, and it's not likely to\nbe a maintenance burden.)\n\n> +       git -C dir1 -C dir2 log --format=%s >actual &&\n> +       test_cmp expected actual\n> +'\n> +\n> +test_expect_success 'Relative followed by fullpath: \"-C ./here -C /there\" is equivalent to \"-C /there\"' '\n> +       echo \"initial in dir1/dir2\" >expected &&\n> +       git -C dir1 -C \"$PWD/dir1/dir2\" log --format=%s >actual &&\n\nIt is suggested in t/README that, for Windows (MSYS bash)\ncompatibility, you should use $(pwd) rather than $PWD.\n\n> +       test_cmp expected actual\n> +'\n> +\n> +test_done\n> --\n> 1.8.4.24.g5fcd118\n"}]}