{"thread":{"id":"34840","subject":"[PATCH v3] Teach git to change to a given directory using -C option","startedAt":"2013-09-03T11:59:47Z","lastAt":"2013-09-09T18:43:48Z","messageCount":14,"participants":["Nazri Ramliy","Junio C Hamano","Eric Sunshine"],"isPatch":true,"patchVersion":3,"patchTotal":null},"messages":[{"id":"226614","messageId":"20130903115944.GA29542@gmail.com","threadId":"34840","inReplyTo":null,"subject":"[PATCH v3] Teach git to change to a given directory using -C option","fromName":"Nazri Ramliy","fromEmail":"ayiehere@gmail.com","sentAt":"2013-09-03T11:59:47Z","receivedAt":"2013-09-03T11:59:47Z","isPatch":true,"sender":{"key":"ayiehere@gmail.com","avatar":"https://avatars.githubusercontent.com/u/164756?v=4"},"body":"On Tue, Sep 3, 2013 at 3:42 PM, Eric Sunshine <sunshine@sunshineco.com> wrote:\n> No wish to bike-shed, however, I find \"effective directory\" somewhat\n> difficult to digest due to its jargony feel. It also seems ambiguous\n> (to me) since \"previous effective directory\" may mean \"directory when\n> git was run\" or \"directory set by most recent -C\". My earlier\n> suggestion\n>\n>     When multiple -C options are given, each subsequent non-absolute\n>     -C <path> is interpreted relative to the preceding -C <path>.\n>\n> avoided jargon and left no room for ambiguity.\n>\n> However, perhaps the examples are clear enough to make excessive prose\n> explanation 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\nI did feel that the explanation is becoming wordy.  I'll take the former\nas it is terser. I've split the part that explain the effects on other\noptions that take path argument into a separate paragraph.\n\n\n> It's curious that this test uses a variable ($expected) to avoid\n> repeating literal \"initial in dir1/dir2\", however, the previous test\n> repeats its literal \"initial in dir1\". (IMHO, the repeated literal\n> actually makes the test a bit easier to read, and it's not likely to\n> be a maintenance burden.)\n\nFixed.\n\n\n> It is suggested in t/README that, for Windows (MSYS bash)\n> compatibility, you should use $(pwd) rather than $PWD.\n\nFixed.\n\nThanks for another round of review!\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      | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 110 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..6105cb0 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 was started in <path> instead of the current working\n+\tdirectory.  When multiple -C options are given, each subsequent\n+\tnon-absolute \"-C <path>\" is interpreted relative to the preceding \"-C\n+\t<path>\".\n+\n+\tThis option affects options that expect path name like --git-dir and\n+\t--work-tree in that their interpretations of the path names would be\n+\tmade relative to the working directory caused by the -C option. For\n+\texample the following invocations 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..c0006da\n--- /dev/null\n+++ b/t/t0056-git-C.sh\n@@ -0,0 +1,82 @@\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+\techo \"initial in dir1/dir2\" >expected &&\n+\tgit -C dir1/dir2 commit -m \"initial in dir1/dir2\" &&\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.22.g38647a4\n"},{"id":"226687","messageId":"xmqq8uzdplqv.fsf@gitster.dls.corp.google.com","threadId":"34840","inReplyTo":"20130903115944.GA29542@gmail.com","subject":"Re: [PATCH v3] Teach git to change to a given directory using -C option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-09-03T22:46:00Z","receivedAt":"2013-09-03T22:46:00Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nazri Ramliy <ayiehere@gmail.com> writes:\n\n> -- >8 --\n> Subject: [PATCH] Teach git to change to a given directory using -C option\n>\n> This is similar in spirit to to \"make -C dir ...\" and \"tar -C dir ...\".\n>\n> Currently it takes more effort (keypresses) to invoke git command in a\n> different directory than the current one without leaving the current\n> directory:\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>\n> While doable the methods shown above are arguably more suitable for\n> scripting than quick command line invocations.\n>\n> With 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>\n> A new test script is added to verify the behavior of this option with\n> other path-related options like --git-dir and --work-tree.\n>\n> Signed-off-by: Nazri Ramliy <ayiehere@gmail.com>\n> ---\n\nThanks; will tentatively queue on 'pu' with some rephrasing of the\nlog message, but I have a few comments.\n\n>  Documentation/git.txt | 16 +++++++++-\n>  git.c                 | 15 ++++++++--\n>  t/t0056-git-C.sh      | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++\n>  3 files changed, 110 insertions(+), 3 deletions(-)\n>  create mode 100755 t/t0056-git-C.sh\n>\n> diff --git a/Documentation/git.txt b/Documentation/git.txt\n> index 83edf30..6105cb0 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\nI do not care too deeply either way, but I am curious if there was a\nreason why you changed the earlier <directory> to <path>?  Somehow,\nwhen we _know_ a path has to be a directory, I find it easier on the\nreaders to spell that out, instead of saying \"this is a path\",\nimplying that it could be a directory, a regular file, or even\nnon-existent.\n\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 was started in <path> instead of the current working\n> +\tdirectory.  When multiple -C options are given, each subsequent\n> +\tnon-absolute \"-C <path>\" is interpreted relative to the preceding \"-C\n> +\t<path>\".\n> +\n> +\tThis option affects options that expect path name like --git-dir and\n> +\t--work-tree in that their interpretations of the path names would be\n> +\tmade relative to the working directory caused by the -C option. For\n> +\texample the following invocations 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\nDoes the above format correctly without the usual \"second and\nsubsequent paragraphs are not indented, but has '+' in place of\na blank line\"?\n\n> diff --git a/git.c b/git.c\n> index 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\nWe usually do not prepend to an existing if/else if/ chain unless\nthere is a very good reason (e.g. the new \"if\" condition is very\noften triggered and we are better off checking it early) exactly\nbecause doing so would make a patch that is ugly like the above.\nYou are not touching the codepath that deal with --exec-path, but\nthe resulting patch makes it appear as if you are doing something to\nit.\n"},{"id":"226693","messageId":"CAPig+cR6gsv+D4gu+hWStqp1t2C6ZkeSkqbg9H8UuQtHQhSmNg@mail.gmail.com","threadId":"34840","inReplyTo":"xmqq8uzdplqv.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v3] Teach git to change to a given directory using -C option","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2013-09-03T23:37:05Z","receivedAt":"2013-09-03T23:37:05Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Tue, Sep 3, 2013 at 6:46 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Nazri Ramliy <ayiehere@gmail.com> writes:\n>\n>> -- >8 --\n>> diff --git a/Documentation/git.txt b/Documentation/git.txt\n>> index 83edf30..6105cb0 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>\n> I do not care too deeply either way, but I am curious if there was a\n> reason why you changed the earlier <directory> to <path>?  Somehow,\n> when we _know_ a path has to be a directory, I find it easier on the\n> readers to spell that out, instead of saying \"this is a path\",\n> implying that it could be a directory, a regular file, or even\n> non-existent.\n\nThat change was in response to my review [1] in which I mentioned:\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\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\nThus <directory> was inconsistent with existing text in git.txt, such\nas what is visible here for --git-dir and --work-tree, as well as\nlater in git.txt where --git-dir and --work-tree are described in more\ndetail (also using <path>).\n\n[1]: http://thread.gmane.org/gmane.comp.version-control.git/233441/focus=233564\n"},{"id":"226702","messageId":"CAEY4ZpN4xgt+gJSVeTfDNC--xt3N+M7vVLFBC7FTWBQtjvN8tw@mail.gmail.com","threadId":"34840","inReplyTo":"xmqq8uzdplqv.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v3] Teach git to change to a given directory using -C option","fromName":"Nazri Ramliy","fromEmail":"ayiehere@gmail.com","sentAt":"2013-09-04T06:36:47Z","receivedAt":"2013-09-04T06:36:47Z","isPatch":true,"sender":{"key":"ayiehere@gmail.com","avatar":"https://avatars.githubusercontent.com/u/164756?v=4"},"body":"On Wed, Sep 4, 2013 at 6:46 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> I do not care too deeply either way, but I am curious if there was a\n> reason why you changed the earlier <directory> to <path>?  Somehow,\n> when we _know_ a path has to be a directory, I find it easier on the\n> readers to spell that out, instead of saying \"this is a path\",\n> implying that it could be a directory, a regular file, or even\n> non-existent.\n\nEric made me do it :). Personally I prefer the non-ambiguous \"directory\"\nover \"path\". In fact, \"directory\" is used in the error message spat out\nby git:\n\n    $ git --work-tree\n    No directory given for --work-tree.\n    ...\n\n    $ git --git-dir\n    No directory given for --git-dir.\n    ...\n    $\n\nI think changing \"<path>\" to \"<directory>\" wherever appropriate in\ngit.txt would be an improvement. Tangent: <dir> is shorter but that\nmight not be easy on the translators.\n\n>> +-C <path>::\n>> +     Run as if git was started in <path> instead of the current working\n>> +     directory.  When multiple -C options are given, each subsequent\n>> +     non-absolute \"-C <path>\" is interpreted relative to the preceding \"-C\n>> +     <path>\".\n>> +\n>> +     This option affects options that expect path name like --git-dir and\n>> +     --work-tree in that their interpretations of the path names would be\n>> +     made relative to the working directory caused by the -C option. For\n>> +     example the following invocations are equivalent:\n>> +\n>> +         git --git-dir=a.git --work-tree=b -C c status\n>> +         git --git-dir=c/a.git --work-tree=c/b status\n>> +\n>\n> Does the above format correctly without the usual \"second and\n> subsequent paragraphs are not indented, but has '+' in place of\n> a blank line\"?\n\nNo it doesn't. I'll fix it in the next reroll.\n\n>> -             if (!prefixcmp(cmd, \"--exec-path\")) {\n>> +             if (!strcmp(cmd, \"-C\")) {\n>> +                     if (*argc < 2) {\n>> +                             fprintf(stderr, \"No directory given for -C.\\n\" );\n>> +                             usage(git_usage_string);\n>> +                     }\n>> +                     if (chdir((*argv)[1]))\n>> +                             die_errno(\"Cannot change to '%s'\", (*argv)[1]);\n>> +                     if (envchanged)\n>> +                             *envchanged = 1;\n>> +                     (*argv)++;\n>> +                     (*argc)--;\n>> +             } else if (!prefixcmp(cmd, \"--exec-path\")) {\n>\n> We usually do not prepend to an existing if/else if/ chain unless\n> there is a very good reason (e.g. the new \"if\" condition is very\n> often triggered and we are better off checking it early) exactly\n> because doing so would make a patch that is ugly like the above.\n> You are not touching the codepath that deal with --exec-path, but\n> the resulting patch makes it appear as if you are doing something to\n> it.\n\nI agree with you here. I'll send the reroll later.\n\nnazri\n"},{"id":"226703","messageId":"CAPig+cTmVfrRWNfoqUGbZnf=GhozSnBB+rm1uuKAZRiRYHr7Xg@mail.gmail.com","threadId":"34840","inReplyTo":"CAEY4ZpN4xgt+gJSVeTfDNC--xt3N+M7vVLFBC7FTWBQtjvN8tw@mail.gmail.com","subject":"Re: [PATCH v3] Teach git to change to a given directory using -C option","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2013-09-04T06:45:07Z","receivedAt":"2013-09-04T06:45:07Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Sep 4, 2013 at 2:36 AM, Nazri Ramliy <ayiehere@gmail.com> wrote:\n> On Wed, Sep 4, 2013 at 6:46 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>> I do not care too deeply either way, but I am curious if there was a\n>> reason why you changed the earlier <directory> to <path>?  Somehow,\n>> when we _know_ a path has to be a directory, I find it easier on the\n>> readers to spell that out, instead of saying \"this is a path\",\n>> implying that it could be a directory, a regular file, or even\n>> non-existent.\n>\n> Eric made me do it :). Personally I prefer the non-ambiguous \"directory\"\n> over \"path\". In fact, \"directory\" is used in the error message spat out\n> by git:\n>\n>     $ git --work-tree\n>     No directory given for --work-tree.\n>     ...\n>\n>     $ git --git-dir\n>     No directory given for --git-dir.\n>     ...\n>     $\n>\n> I think changing \"<path>\" to \"<directory>\" wherever appropriate in\n> git.txt would be an improvement. Tangent: <dir> is shorter but that\n> might not be easy on the translators.\n\nSuch a terminology change is orthogonal to the patch adding -C\nsupport, so if you do pursue it, the terminology change should be a\nseparate patch (either preparatory or follow-up to the -C patch).\n"},{"id":"226726","messageId":"20130904122020.GA25538@gmail.com","threadId":"34840","inReplyTo":"CAEY4ZpN4xgt+gJSVeTfDNC--xt3N+M7vVLFBC7FTWBQtjvN8tw@mail.gmail.com","subject":"Re: [PATCH v3] Teach git to change to a given directory using -C option","fromName":"Nazri Ramliy","fromEmail":"ayiehere@gmail.com","sentAt":"2013-09-04T12:20:24Z","receivedAt":"2013-09-04T12:20:24Z","isPatch":true,"sender":{"key":"ayiehere@gmail.com","avatar":"https://avatars.githubusercontent.com/u/164756?v=4"},"body":"On Wed, Sep 04, 2013 at 02:36:47PM +0800, Nazri Ramliy wrote:\n> On Wed, Sep 4, 2013 at 6:46 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> > Does the above format correctly without the usual \"second and\n> > subsequent paragraphs are not indented, but has '+' in place of\n> > a blank line\"?\n> \n> No it doesn't. I'll fix it in the next reroll.\n> \n> >> -             if (!prefixcmp(cmd, \"--exec-path\")) {\n> >> +             if (!strcmp(cmd, \"-C\")) {\n> >> +                     if (*argc < 2) {\n> >> +                             fprintf(stderr, \"No directory given for -C.\\n\" );\n> >> +                             usage(git_usage_string);\n> >> +                     }\n> >> +                     if (chdir((*argv)[1]))\n> >> +                             die_errno(\"Cannot change to '%s'\", (*argv)[1]);\n> >> +                     if (envchanged)\n> >> +                             *envchanged = 1;\n> >> +                     (*argv)++;\n> >> +                     (*argc)--;\n> >> +             } else if (!prefixcmp(cmd, \"--exec-path\")) {\n> >\n> > We usually do not prepend to an existing if/else if/ chain unless\n> > there is a very good reason (e.g. the new \"if\" condition is very\n> > often triggered and we are better off checking it early) exactly\n> > because doing so would make a patch that is ugly like the above.\n> > You are not touching the codepath that deal with --exec-path, but\n> > the resulting patch makes it appear as if you are doing something to\n> > it.\n> \n> I agree with you here. I'll send the reroll later.\n\nHere it is.\n\nEric: Care to give your \"Reviewed-by:\" stamp?\n\nnazri\n\n-- >8 --\nSubject: git: run in a directory given with -C option\n\nThis is similar in spirit to to \"make -C dir ...\" and \"tar -C dir ...\".\n\nIt takes more keypresses to invoke git command in a different\ndirectory 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>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/git.txt | 16 +++++++++-\n git.c                 | 13 +++++++-\n t/t0056-git-C.sh      | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 109 insertions(+), 2 deletions(-)\n create mode 100755 t/t0056-git-C.sh\n\ndiff --git a/Documentation/git.txt b/Documentation/git.txt\nindex 83edf30..ae049da 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 was started in <path> instead of the current working\n+\tdirectory.  When multiple -C options are given, each subsequent\n+\tnon-absolute \"-C <path>\" is interpreted relative to the preceding \"-C\n+\t<path>\".\n++\n+This option affects options that expect path name like --git-dir and\n+--work-tree in that their interpretations of the path names would be\n+made relative to the working directory caused by the -C option. For\n+example the following invocations are equivalent:\n+\n+    git --git-dir=a.git --work-tree=b -C c status\n+    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..a2d99a7 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@@ -153,6 +153,17 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)\n \t\t\tset_alternate_shallow_file((*argv)[0]);\n \t\t\tif (envchanged)\n \t\t\t\t*envchanged = 1;\n+\t\t} else if (!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 {\n \t\t\tfprintf(stderr, \"Unknown option: %s\\n\", cmd);\n \t\t\tusage(git_usage_string);\ndiff --git a/t/t0056-git-C.sh b/t/t0056-git-C.sh\nnew file mode 100755\nindex 0000000..c0006da\n--- /dev/null\n+++ b/t/t0056-git-C.sh\n@@ -0,0 +1,82 @@\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+\techo \"initial in dir1/dir2\" >expected &&\n+\tgit -C dir1/dir2 commit -m \"initial in dir1/dir2\" &&\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.22.g38647a4\n"},{"id":"227121","messageId":"CAPig+cRt9o=6Amhx6qTkzfk5R9aQfRZ=357BOVELm_hPsWE3WQ@mail.gmail.com","threadId":"34840","inReplyTo":"20130904122020.GA25538@gmail.com","subject":"Re: [PATCH v3] Teach git to change to a given directory using -C option","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2013-09-08T10:32:07Z","receivedAt":"2013-09-08T10:32:07Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Sep 4, 2013 at 8:20 AM, Nazri Ramliy <ayiehere@gmail.com> wrote:\n> Subject: git: run in a directory given with -C option\n>\n> This is similar in spirit to to \"make -C dir ...\" and \"tar -C dir ...\".\n> ---\n> diff --git a/Documentation/git.txt b/Documentation/git.txt\n> index 83edf30..ae049da 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 was started in <path> instead of the current working\n> +       directory.  When multiple -C options are given, each subsequent\n> +       non-absolute \"-C <path>\" is interpreted relative to the preceding \"-C\n> +       <path>\".\n\nFor consistency with existing formatting in git.txt, you may want to\nsquash in the following fixes (sans gmail whitespace damage):\n\n--- >8 ---\ndiff --git a/Documentation/git.txt b/Documentation/git.txt\nindex ae049da..6622037 100644\n--- a/Documentation/git.txt\n+++ b/Documentation/git.txt\n@@ -396,14 +396,14 @@ because `git --help ...` is converted internally into `git\n help ...`.\n\n -C <path>::\n- Run as if git was started in <path> instead of the current working\n- directory.  When multiple -C options are given, each subsequent\n- non-absolute \"-C <path>\" is interpreted relative to the preceding \"-C\n- <path>\".\n+ Run as if git was started in '<path>' instead of the current working\n+ directory.  When multiple '-C' options are given, each subsequent\n+ non-absolute `-C <path>` is interpreted relative to the preceding `-C\n+ <path>`.\n +\n-This option affects options that expect path name like --git-dir and\n---work-tree in that their interpretations of the path names would be\n-made relative to the working directory caused by the -C option. For\n+This option affects options that expect path name like '--git-dir' and\n+'--work-tree' in that their interpretations of the path names would be\n+made relative to the working directory caused by the '-C' option. For\n example the following invocations are equivalent:\n\n     git --git-dir=a.git --work-tree=b -C c status\n--- >8 ---\n\n> +This option affects options that expect path name like --git-dir and\n> +--work-tree in that their interpretations of the path names would be\n> +made relative to the working directory caused by the -C option. For\n> +example the following invocations are equivalent:\n> +\n> +    git --git-dir=a.git --work-tree=b -C c status\n> +    git --git-dir=c/a.git --work-tree=c/b status\n\nIs the interaction of -C with --work-tree and --git-dir desirable or\nuseful? (I'm genuinely curious.) Do you have use-cases in mind? Would\nmentioning them in the commit message help to justify the interaction?\n"},{"id":"227189","messageId":"CAEY4ZpNEae1UprRcpC8XUPP4XBQ89bDXP1A7yVcFdW405HZr0w@mail.gmail.com","threadId":"34840","inReplyTo":"CAPig+cRt9o=6Amhx6qTkzfk5R9aQfRZ=357BOVELm_hPsWE3WQ@mail.gmail.com","subject":"Re: [PATCH v3] Teach git to change to a given directory using -C option","fromName":"Nazri Ramliy","fromEmail":"ayiehere@gmail.com","sentAt":"2013-09-09T01:49:41Z","receivedAt":"2013-09-09T01:49:41Z","isPatch":true,"sender":{"key":"ayiehere@gmail.com","avatar":"https://avatars.githubusercontent.com/u/164756?v=4"},"body":"On Sun, Sep 8, 2013 at 6:32 PM, Eric Sunshine <sunshine@sunshineco.com> wrote:\n> For consistency with existing formatting in git.txt, you may want to\n> squash in the following fixes (sans gmail whitespace damage):\n>\n> --- >8 ---\n[ diff snipped ]\n> --- >8 ---\n\nThanks. I'll submit a reroll later.\n\n>> +This option affects options that expect path name like --git-dir and\n>> +--work-tree in that their interpretations of the path names would be\n>> +made relative to the working directory caused by the -C option. For\n>> +example the following invocations are equivalent:\n>> +\n>> +    git --git-dir=a.git --work-tree=b -C c status\n>> +    git --git-dir=c/a.git --work-tree=c/b status\n>\n> Is the interaction of -C with --work-tree and --git-dir desirable or\n> useful? (I'm genuinely curious.) Do you have use-cases in mind? Would\n> mentioning them in the commit message help to justify the interaction?\n\nThe example is meant to clarify the effect of the -C option, rather than a\nproposed usage with the --work-tree and --git-dir options. The example came out\ndue to the following discussions from an earlier version of this patch [1]:\n\nOn Sat, Apr 20, 2013 at 12:12 AM, Jeff King <peff@peff.net> wrote:\n> On Fri, Apr 19, 2013 at 08:21:48PM +0800, Nazri Ramliy wrote:\n>> diff --git a/Documentation/git.txt b/Documentation/git.txt\n>> index 6a875f2..20bba86 100644\n>> --- a/Documentation/git.txt\n>> +++ b/Documentation/git.txt\n>> @@ -379,6 +379,9 @@ displayed. See linkgit:git-help[1] for more information,\n>>  because `git --help ...` is converted internally into `git\n>>  help ...`.\n>>\n>> +-C <directory>::\n>> +     Change to given directory before doing anything else.\n>> +\n>\n> It might make sense to clarify this as \"...anything else, including\n> determining the location of the git repository directory\". If you think\n> hard about it, doing anything else would not really make much sense, but\n> spelling it out makes it clear what the option can be used for.\n\nand [2]:\n\nOn Sun, Apr 21, 2013 at 6:18 AM, Jonathan Nieder <jrnieder@gmail.com> wrote:\n> All that said, I don't mind -C terribly as long as it can maintain\n> itself, which means including thorough documentation that covers the\n> purpose and how pathname parameters and envvars interact with the new\n> option and including tests under t/ to ensure it continues to work\n> correctly in the future.\n\nnazri\n\n[1] http://article.gmane.org/gmane.comp.version-control.git/221766\n[2] http://article.gmane.org/gmane.comp.version-control.git/221878\n"},{"id":"227195","messageId":"CAPig+cTNeqNhGwD-EZ3uszh5vJ4JeJ6L0RXdTsveb1FgXE5t3Q@mail.gmail.com","threadId":"34840","inReplyTo":"CAEY4ZpNEae1UprRcpC8XUPP4XBQ89bDXP1A7yVcFdW405HZr0w@mail.gmail.com","subject":"Re: [PATCH v3] Teach git to change to a given directory using -C option","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2013-09-09T05:01:33Z","receivedAt":"2013-09-09T05:01:33Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sun, Sep 8, 2013 at 9:49 PM, Nazri Ramliy <ayiehere@gmail.com> wrote:\n> On Sun, Sep 8, 2013 at 6:32 PM, Eric Sunshine <sunshine@sunshineco.com> wrote:\n>>> +This option affects options that expect path name like --git-dir and\n>>> +--work-tree in that their interpretations of the path names would be\n>>> +made relative to the working directory caused by the -C option. For\n>>> +example the following invocations are equivalent:\n>>> +\n>>> +    git --git-dir=a.git --work-tree=b -C c status\n>>> +    git --git-dir=c/a.git --work-tree=c/b status\n>>\n>> Is the interaction of -C with --work-tree and --git-dir desirable or\n>> useful? (I'm genuinely curious.) Do you have use-cases in mind? Would\n>> mentioning them in the commit message help to justify the interaction?\n>\n> The example is meant to clarify the effect of the -C option, rather than a\n> proposed usage with the --work-tree and --git-dir options. The example came out\n> due to the following discussions from an earlier version of this patch [1]:\n> [1] http://article.gmane.org/gmane.comp.version-control.git/221766\n\nThanks for the reference. I did read that thread earlier. It doesn't\nreally answer my question, but perhaps it's not terribly important\nsince the interaction is documented. I was mainly asking if the choice\nof locking in one particular interpretation was deliberate even though\nother potentially valid (and perhaps more intuitive) interpretations\nexists. More below TL;DR if you care to read on.\n\nTL;DR\n\nI was interested in knowing whether the exact interaction between -C\nand --work-tree and --git-dir was intentional (and desirable) or an\n\"accident of implementation\". I can see it going either way.\n\nAs implemented by the patch, -C is acted upon immediately (via\nchdir()), whereas --work-tree and --git-dir have a delayed effect, so:\n\n  git -C foo --work-tree=bar -C baz --git-dir=moo\n\nmeans:\n\n  work-tree = foo/baz/bar\n  git-dir = foo/baz/moo\n\nHowever, it would be equally valid for a user to expect the options to\nbe evaluated sequentially such that the above command line would mean:\n\n  work-tree = foo/bar\n  git-dir = foo/baz/moo\n\nIs the former interpretation better than the latter possibly more\nintuitive interpretation? This is a genuine question. I'm not\nsuggesting that one interpretation is better than the other, and it's\npossible that it won't matter in practice [1], but it might be good to\nknow that alternate interpretations have been taken into consideration\nbefore locking in a particular behavior.\n\nThis is why I was asking if you had particular use-cases in mind where\nthe former made more sense than the latter (or some other [2])\ninterpretation. Since there are multiple potential interpretations, it\nmight make sense to explain in the commit message why the one was\nchosen over the other(s), and such use-cases could help solidify that\nexplanation.\n\n[1]: Mixing of -C, --work-tree, and --git-dir may be sufficiently\nunlikely that the reason the patch's behavior was chosen becomes\nimmaterial. Since the behavior is documented, a person can choose to\navoid -C if it doesn't work in a way suitable to his situation.\n\n[2]: For example, a user might reasonably expect -C to be relative to\n--work-tree or GIT_WORK_TREE rather than the other way around. So,\n\"git --work-tree=foo -C bar\" or \"git -C bar --work-tree=foo\" would\nchdir(\"foo/bar\") before performing the git operation, and --git-dir\nwould be unaffected. Yet another possibility is that -C would impact\nneither --work-tree nor --git-dir.\n"},{"id":"227213","messageId":"20130909134743.GA11335@gmail.com","threadId":"34840","inReplyTo":"CAPig+cTNeqNhGwD-EZ3uszh5vJ4JeJ6L0RXdTsveb1FgXE5t3Q@mail.gmail.com","subject":"Re: [PATCH v3] Teach git to change to a given directory using -C option","fromName":"Nazri Ramliy","fromEmail":"ayiehere@gmail.com","sentAt":"2013-09-09T13:47:43Z","receivedAt":"2013-09-09T13:47:43Z","isPatch":true,"sender":{"key":"ayiehere@gmail.com","avatar":"https://avatars.githubusercontent.com/u/164756?v=4"},"body":"On Mon, Sep 09, 2013 at 01:01:33AM -0400, Eric Sunshine wrote:\n> On Sun, Sep 8, 2013 at 9:49 PM, Nazri Ramliy <ayiehere@gmail.com> wrote:\n> Thanks for the reference. I did read that thread earlier. It doesn't\n> really answer my question, but perhaps it's not terribly important\n> since the interaction is documented. I was mainly asking if the choice\n> of locking in one particular interpretation was deliberate even though\n> other potentially valid (and perhaps more intuitive) interpretations\n> exists. More below TL;DR if you care to read on.\n\nThanks for the clarification. Reroll of the patch to fix the\ndocumentation is at the end of this mail.\n\n> TL;DR\n> \n> I was interested in knowing whether the exact interaction between -C\n> and --work-tree and --git-dir was intentional (and desirable) or an\n> \"accident of implementation\". I can see it going either way.\n\nThe implementation was the simplest one that I could think of for\nachieving what I wanted -C to do. I can agree that the side effects with\nany other options that handles path argument are \"accident of\nimplementation\" and there might be room for improvements, at the expense\nof complexity.\n\n> However, it would be equally valid for a user to expect the options to\n> be evaluated sequentially such that the above command line would mean:\n> \n>   work-tree = foo/bar\n>   git-dir = foo/baz/moo\n> \n> Is the former interpretation better than the latter possibly more\n> intuitive interpretation? This is a genuine question. I'm not\n> suggesting that one interpretation is better than the other, and it's\n> possible that it won't matter in practice [1], but it might be good to\n> know that alternate interpretations have been taken into consideration\n> before locking in a particular behavior.\n> \n> This is why I was asking if you had particular use-cases in mind where\n> the former made more sense than the latter (or some other [2])\n> interpretation. Since there are multiple potential interpretations, it\n> might make sense to explain in the commit message why the one was\n> chosen over the other(s), and such use-cases could help solidify that\n> explanation.\n> \n\nWhen I first experimented the interaction of -C with --git-dir and\n--work-tree, I did feel its counter-intuitiveness side effect due to the\nimplementation. Consider -C's interaction with format-patch:\n\n    $ git -C foo format-patch HEAD~3..\n\nBased on the current implementation, the patch files generated by the\nabove would in the directory \"foo\" instead of where git was started\nfrom.\n\nIs it worth the complexity to ensure that subsequent git options,\nbuiltin options, or builtins be aware of where git was started from vs.\nthe effective \"new\" directory that it chdir'd into due to the -C option?\nI'm not sure about that at the moment. I foresee that it might not be\nthat simple to get that to work.\n\nI took a look at GNU tar's -C implementation and it seems that for the\nequivalent case,:\n\n    $ tar Ccf foo/ foo.tar bar.txt baz.txt\n\nGNU tar is aware of where it was called from and created the file\nfoo.tar there (instead of in foo/).\n\nTangent: This now reminds me of a feature that I wished git-grep had -\nwhen run on a non-bare repository, I'd like git-grep to show me the\n\"usable\" path from the point of view of where I started it from,\nassuming $PWD is $HOME and my non-bare clone of git.git is in $HOME/src:\n\n    $ git -C src/git grep main git.c\n    src/git/git.c:           * Check remaining flags.\n    src/git/git.c:int main(int argc, char **av)\n\ninstead of the what we have in the current behavior:\n\n    $ git -C src/git grep main git.c\n    git.c:           * Check remaining flags.\n    git.c:int main(int argc, char **av)\n\nNazri\n-- >8 --\nSubject: git: run in a directory given with -C option\n\nThis is similar in spirit to to \"make -C dir ...\" and \"tar -C dir ...\".\n\nIt takes more keypresses to invoke git command in a different\ndirectory 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>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/git.txt | 16 +++++++++-\n git.c                 | 13 +++++++-\n t/t0056-git-C.sh      | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 109 insertions(+), 2 deletions(-)\n create mode 100755 t/t0056-git-C.sh\n\ndiff --git a/Documentation/git.txt b/Documentation/git.txt\nindex 83edf30..6622037 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 was started in '<path>' instead of the current working\n+\tdirectory.  When multiple '-C' options are given, each subsequent\n+\tnon-absolute `-C <path>` is interpreted relative to the preceding `-C\n+\t<path>`.\n++\n+This option affects options that expect path name like '--git-dir' and\n+'--work-tree' in that their interpretations of the path names would be\n+made relative to the working directory caused by the '-C' option. For\n+example the following invocations are equivalent:\n+\n+    git --git-dir=a.git --work-tree=b -C c status\n+    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..a2d99a7 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@@ -153,6 +153,17 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)\n \t\t\tset_alternate_shallow_file((*argv)[0]);\n \t\t\tif (envchanged)\n \t\t\t\t*envchanged = 1;\n+\t\t} else if (!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 {\n \t\t\tfprintf(stderr, \"Unknown option: %s\\n\", cmd);\n \t\t\tusage(git_usage_string);\ndiff --git a/t/t0056-git-C.sh b/t/t0056-git-C.sh\nnew file mode 100755\nindex 0000000..c0006da\n--- /dev/null\n+++ b/t/t0056-git-C.sh\n@@ -0,0 +1,82 @@\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+\techo \"initial in dir1/dir2\" >expected &&\n+\tgit -C dir1/dir2 commit -m \"initial in dir1/dir2\" &&\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.100.gd06f3e9\n"},{"id":"227236","messageId":"xmqqtxhu3qil.fsf@gitster.dls.corp.google.com","threadId":"34840","inReplyTo":"20130909134743.GA11335@gmail.com","subject":"Re: [PATCH v3] Teach git to change to a given directory using -C option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-09-09T16:32:02Z","receivedAt":"2013-09-09T16:32:02Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nazri Ramliy <ayiehere@gmail.com> writes:\n\n> Subject: git: run in a directory given with -C option\n>\n> This is similar in spirit to to \"make -C dir ...\" and \"tar -C dir ...\".\n\nThe doubled-\"to to\" which I locally fixed when I queued the last one\n(together with other rewording to make it more agreeable and easier\nto read) somehow came back ;-) Will fix locally again.\n\n> +-C <path>::\n> +\tRun as if git was started in '<path>' instead of the current working\n> +\tdirectory.  When multiple '-C' options are given, each subsequent\n\nI think this should be `-C` to typeset it as \"typed literally\".\n\n> +\tnon-absolute `-C <path>` is interpreted relative to the preceding `-C\n> +\t<path>`.\n> ++\n> +This option affects options that expect path name like '--git-dir' and\n> +'--work-tree' in that their interpretations of the path names would be\n\nLikewise for `--git-dir` and `--work-tree`.\n\n> +made relative to the working directory caused by the '-C' option. For\n\nand here.\n\n> diff --git a/t/t0056-git-C.sh b/t/t0056-git-C.sh\n> new file mode 100755\n> index 0000000..c0006da\n> --- /dev/null\n> +++ b/t/t0056-git-C.sh\n> @@ -0,0 +1,82 @@\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\nCurious why this does not use -C here.\n\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\nBecause \"a.txt\" exists in both dir1 and dir1/dir2, this has less\nchance of catching a bug (if somebody breaks the feature to run it\nin dir1 not dir1/dir2, \"add\" will happily say \"Oh, I found something\nto add\", instead of saying \"Huh? there is no such path\".\n\nIf you used b.txt instead, you would catch such a breakage.\n\nRemember, tests are not about demonstrating how cool the new feature\nis and/or how well it works in an expected setting.  Imagine ways\nother people can break your spiffy new feature in later patches, and\ndesign tests that are more likely to catch them.\n\nThe same comment applies throughout the remainder of this script.\n\n> +\techo \"initial in dir1/dir2\" >expected &&\n> +\tgit -C dir1/dir2 commit -m \"initial in dir1/dir2\" &&\n\nto reduce possibilities of breaking this test in the future due to\ntypos (e.g. somebody may want to say \"initial commit in dir1/dir2\"),\ndoing this may be a better idea:\n\n\tmsg=\"initial in dir1/dir2\" &&\n        echo \"$msg\" >expected &&\n        git -C dir1/dir2 commit -m \"$msg\" &&\n\nThe same comment applies to the previous one.\n"},{"id":"227238","messageId":"CAPig+cQEor+dBU6Xww25A4Sc-mgEOq1SDmBX-9xM6pKuMqu-NQ@mail.gmail.com","threadId":"34840","inReplyTo":"xmqqtxhu3qil.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v3] Teach git to change to a given directory using -C option","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2013-09-09T17:42:50Z","receivedAt":"2013-09-09T17:42:50Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Sep 9, 2013 at 12:32 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Nazri Ramliy <ayiehere@gmail.com> writes:\n>\n>> Subject: git: run in a directory given with -C option\n>>\n>> +-C <path>::\n>> +     Run as if git was started in '<path>' instead of the current working\n>> +     directory.  When multiple '-C' options are given, each subsequent\n>\n> I think this should be `-C` to typeset it as \"typed literally\".\n>\n>> +     non-absolute `-C <path>` is interpreted relative to the preceding `-C\n>> +     <path>`.\n>> ++\n>> +This option affects options that expect path name like '--git-dir' and\n>> +'--work-tree' in that their interpretations of the path names would be\n>\n> Likewise for `--git-dir` and `--work-tree`.\n>\n>> +made relative to the working directory caused by the '-C' option. For\n>\n> and here.\n\nI agree with all of the above, however, the unfortunate typesetting in\nthese cases was chosen deliberately to be consistent with the\nimmediately surrounding text. A separate typesetting normalization\npatch, either before or after this patch, would not be unwelcome. (I\nforgot to make such a suggestion in [1].)\n\n[1]: http://thread.gmane.org/gmane.comp.version-control.git/233719/focus=234234\n"},{"id":"227239","messageId":"xmqqa9jl5112.fsf@gitster.dls.corp.google.com","threadId":"34840","inReplyTo":"CAPig+cQEor+dBU6Xww25A4Sc-mgEOq1SDmBX-9xM6pKuMqu-NQ@mail.gmail.com","subject":"Re: [PATCH v3] Teach git to change to a given directory using -C option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-09-09T17:59:37Z","receivedAt":"2013-09-09T17:59:37Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Eric Sunshine <sunshine@sunshineco.com> writes:\n\n> On Mon, Sep 9, 2013 at 12:32 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>> Nazri Ramliy <ayiehere@gmail.com> writes:\n>>\n>>> Subject: git: run in a directory given with -C option\n>>>\n>>> +-C <path>::\n>>> +     Run as if git was started in '<path>' instead of the current working\n>>> +     directory.  When multiple '-C' options are given, each subsequent\n>>\n>> I think this should be `-C` to typeset it as \"typed literally\".\n>>\n>>> +     non-absolute `-C <path>` is interpreted relative to the preceding `-C\n>>> +     <path>`.\n>>> ++\n>>> +This option affects options that expect path name like '--git-dir' and\n>>> +'--work-tree' in that their interpretations of the path names would be\n>>\n>> Likewise for `--git-dir` and `--work-tree`.\n>>\n>>> +made relative to the working directory caused by the '-C' option. For\n>>\n>> and here.\n>\n> I agree with all of the above, however, the unfortunate typesetting in\n> these cases was chosen deliberately to be consistent with the\n> immediately surrounding text. A separate typesetting normalization\n> patch, either before or after this patch, would not be unwelcome. (I\n> forgot to make such a suggestion in [1].)\n>\n> [1]: http://thread.gmane.org/gmane.comp.version-control.git/233719/focus=234234\n\nOK, thanks for clarification.\n"},{"id":"227241","messageId":"CAPig+cTLnMqcemMRn+fDZJDP6KH6Qn5HwKUbRViRhc60fMqfHw@mail.gmail.com","threadId":"34840","inReplyTo":"xmqqtxhu3qil.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v3] Teach git to change to a given directory using -C option","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2013-09-09T18:43:48Z","receivedAt":"2013-09-09T18:43:48Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Sep 9, 2013 at 12:32 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>> +     echo \"initial in dir1/dir2\" >expected &&\n>> +     git -C dir1/dir2 commit -m \"initial in dir1/dir2\" &&\n>\n> to reduce possibilities of breaking this test in the future due to\n> typos (e.g. somebody may want to say \"initial commit in dir1/dir2\"),\n> doing this may be a better idea:\n>\n>         msg=\"initial in dir1/dir2\" &&\n>         echo \"$msg\" >expected &&\n>         git -C dir1/dir2 commit -m \"$msg\" &&\n>\n> The same comment applies to the previous one.\n\nIn his original submission, Nazri actually had it the way you suggest\nfor this test (though not the preceding test), but changed it in\nresponse to a silly parenthetical \"IMHO\" in my review [1].\n\n[1]: http://thread.gmane.org/gmane.comp.version-control.git/233636/focus=233704\n"}]}