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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, Aug 30, 2013 at 9:35 AM, Nazri Ramliy <ayiehere@xxxxxxxxx> wrote:
> This is similar in spirit to to "make -C dir ..." and "tar -C dir ...".
>
> Currently it takes more effort (keypresses) to invoke git command in a
> different directory than the current one without leaving the current
> directory:
>
>     1. (cd ~/foo && git status)
>        git --git-dir=~/foo/.git --work-dir=~/foo status
>        GIT_DIR=~/foo/.git GIT_WORK_TREE=~/foo git status
>     2. (cd ../..; git grep foo)
>     3. for d in d1 d2 d3; do (cd $d && git svn rebase); done
>
> 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 with less keystrokes:

Grammar: s/less/fewer/

More below...

>     1. git -C ~/foo status
>     2. git -C ../.. grep foo
>     3. for d in d1 d2 d3; do git -C $d svn rebase; done
>
> A new test script is added to verify the behavior of this option with
> other path-related options like --git-dir and --work-tree.
>
> Signed-off-by: Nazri Ramliy <ayiehere@xxxxxxxxx>
> ---
> This is a reroll of [1]. The only difference is the rewording of the
> commit message.  I'm resending this as I've found it to be useful in my
> daily git usage in that it helps me stay focused on what I'm doing in
> the current directory while needing to run git on another directory.
>
> nazri.
>
> [1] http://permalink.gmane.org/gmane.comp.version-control.git/221954
>
>  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 dca11cc..0d44fa2 100644
> --- a/Documentation/git.txt
> +++ b/Documentation/git.txt
> @@ -395,6 +395,19 @@ displayed. See linkgit:git-help[1] for more information,
>  because `git --help ...` is converted internally into `git
>  help ...`.
>
> +-C <directory>::

The synopsis at the top of git.txt mentions --git-dir and --work-tree.
For consistency, -C probably ought to be mentioned there, as well.

Other options which accept a directory, such as --git-dir and
--work-tree, are documented as accepting <path>, but -C is
inconsistently documented as accepting <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

The fragment "interpreted relative" seems ambiguous when absolute
paths are involved. For instance, what happens when the user specifies
"-C /foo/ -C /bar/". From the implementation I can see that the
working directory becomes /bar, but without checking the
implementation, it's not clear what the result would be. For instance,
if the implementation merely did string concatenation of the -C
arguments, then input "-C /foo/ -C /bar/" might try to set the working
directory to "/foo//bar/" which would be interpreted as "/foo/bar/" on
Unix, but would probably fail on Windows. Perhaps rewriting might
remove the ambiguity?

    [...] When multiple -C options are given, each subsequent non-absolute
    -C <path> is interpreted relative to the preceding -C <path>. [...]

> +       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 2025f77..2207ee5 100644
> --- a/git.c
> +++ b/git.c
> @@ -7,7 +7,7 @@
>  #include "commit.h"
>
>  const char git_usage_string[] =
> -       "git [--version] [--help] [-c name=value]\n"
> +       "git [--version] [--help] [-C 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"

For existing options accepting an argument, the argument is formatted
as <argument>. The -C option does not follow suit.

As mentioned above, all other options accepting a directory are
documented as taking <path>, but -C is inconsistent and is documented
as taking 'directory' instead.

> @@ -54,7 +54,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'

s/it/its/
s/<directory>/<path>/

> +
> +. ./test-lib.sh
> +
> +test_expect_success '"git -C <dir>" runs git from the directory <dir>' '

s/<dir>/<path>/g

> +       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"
> +'

Modern git tests tend to place the expected and actual outputs in
files and then use test_cmp to verify that they are identical. For
instance:

    echo "initial in dir1" >expected &&
    git -C dir1 log --format="%s" >actual &&
    test_cmp expected actual

The benefit of doing so is that test_cmp will print the difference
between the expected and actual values when they differ, which
facilitates debugging a failed test.

> +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"
> +'

It would make sense also to test multiple -C options with combinations
of absolute and and relative paths.

> +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.4.1.g098df5a
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]