git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
* [PATCH/RFC 0/2] fix some rev-parse options in non-repos
@ 2016-02-26 23:25 Jeff King
  2016-02-26 23:26 ` [PATCH 1/2] t1515: add tests for rev-parse out-of-repo helpers Jeff King
  2016-02-26 23:29 ` [PATCH 2/2] Revert "rev-parse: remove restrictions on some options" Jeff King
  0 siblings, 2 replies; 13+ messages in thread
From: Jeff King @ 2016-02-26 23:25 UTC (permalink / raw
  To: git; +Cc: Michael Haggerty, John Keeping, Junio C Hamano

Michael Haggerty noticed recently (off-list) that "git rev-parse
--local-env-vars" doesn't work outside of a git repository. This turns
out to be a regression in v1.8.5, due to a patch by John Keeping that
lifted some other restrictions on how the option could be used.

This fixes it by reverting John's patch, which puts the original
restrictions back in place. I won't repeat the lengthy discussion from
patch 2's commit message here, but the gist of it is that probably
nobody cares about those restrictions, it's more important to fix the
original regression, and it's probably too hard to make both work.

The only thing that gives me pause (and hence the RFC) is that it has
been over 2 years since the original regression. So it's entirely
possible somebody will consider _this_ fix a regression.

  [1/2]: t1515: add tests for rev-parse out-of-repo helpers
  [2/2]: Revert "rev-parse: remove restrictions on some options"

-Peff

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 1/2] t1515: add tests for rev-parse out-of-repo helpers
  2016-02-26 23:25 [PATCH/RFC 0/2] fix some rev-parse options in non-repos Jeff King
@ 2016-02-26 23:26 ` Jeff King
  2016-02-26 23:29 ` [PATCH 2/2] Revert "rev-parse: remove restrictions on some options" Jeff King
  1 sibling, 0 replies; 13+ messages in thread
From: Jeff King @ 2016-02-26 23:26 UTC (permalink / raw
  To: git; +Cc: Michael Haggerty, John Keeping, Junio C Hamano

The git-rev-parse command is a dumping ground for helpers
that let scripts make various queries of git. Many of these
are conceptually independent of being inside a git
repository.

With the exception of --parseopt, we do not directly test
most of these features in our test suite. Let's give them
some basic sanity checks, which reveals that some of them
have been broken for some time when run from outside a
repository.

Signed-off-by: Jeff King <peff@peff.net>
---
 t/t1515-rev-parse-outside-repo.sh | 45 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 45 insertions(+)
 create mode 100755 t/t1515-rev-parse-outside-repo.sh

diff --git a/t/t1515-rev-parse-outside-repo.sh b/t/t1515-rev-parse-outside-repo.sh
new file mode 100755
index 0000000..ae33093
--- /dev/null
+++ b/t/t1515-rev-parse-outside-repo.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+test_description='check that certain rev-parse options work outside repo'
+. ./test-lib.sh
+
+test_expect_success 'set up non-repo directory' '
+	GIT_CEILING_DIRECTORIES=$(pwd) &&
+	export GIT_CEILING_DIRECTORIES &&
+	mkdir non-repo &&
+	cd non-repo &&
+	# confirm that git does not find a repo
+	test_must_fail git rev-parse --git-dir
+'
+
+# Rather than directly test the output of sq-quote directly,
+# make sure the shell can read back a tricky case, since
+# that's what we really care about anyway.
+tricky="really tricky with \\ and \" and '"
+dump_args () {
+	for i in "$@"; do
+		echo "arg: $i"
+	done
+}
+test_expect_success 'rev-parse --sq-quote' '
+	dump_args "$tricky" easy >expect &&
+	eval "dump_args $(git rev-parse --sq-quote "$tricky" easy)" >actual &&
+	test_cmp expect actual
+'
+
+test_expect_failure 'rev-parse --local-env-vars' '
+	git rev-parse --local-env-vars >actual &&
+	# we do not want to depend on the complete list here,
+	# so just look for something plausible
+	grep ^GIT_DIR actual
+'
+
+test_expect_failure 'rev-parse --resolve-git-dir' '
+	git init --separate-git-dir repo dir &&
+	test_must_fail git rev-parse --resolve-git-dir . &&
+	echo "$(pwd)/repo" >expect &&
+	git rev-parse --resolve-git-dir dir/.git >actual &&
+	test_cmp expect actual
+'
+
+test_done
-- 
2.7.2.767.g705917e

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 2/2] Revert "rev-parse: remove restrictions on some options"
  2016-02-26 23:25 [PATCH/RFC 0/2] fix some rev-parse options in non-repos Jeff King
  2016-02-26 23:26 ` [PATCH 1/2] t1515: add tests for rev-parse out-of-repo helpers Jeff King
@ 2016-02-26 23:29 ` Jeff King
  2016-02-26 23:34   ` Jeff King
                     ` (2 more replies)
  1 sibling, 3 replies; 13+ messages in thread
From: Jeff King @ 2016-02-26 23:29 UTC (permalink / raw
  To: git; +Cc: Michael Haggerty, John Keeping, Junio C Hamano

This reverts commit 68889b416d5b6a5cf7d280a428281d635fe9b292.

That commit bumped some rev-parse options into the main
option-parsing loop, which meant that they no longer had to
come at the very beginning of the option list. However, that
also means that they now came after our call to
setup_git_directory(), and will no longer run outside of a
git repository.

For --local-env-vars, this is semi-questionable. The main
use of that option is to get a list of environment variables
to clear, and if you are not in a repository, there
_probably_ isn't anything to clear. But it places an
unnecessary restriction on callers who may be using it
preemptively.

For --resolve-git-dir, it is almost certainly a regression.
That option is about finding a git dir in the first place,
so it is reasonably likely to be called from outside an
existing repository.

The best solution here would be to have a full parsing loop
that handles all options, but only calls setup_git_directory
as appropriate. Unfortunately, that's a bit complicated to
implement. We _have_ to handle each option in the order it
appears on the command line. If the caller asked for:

  git rev-parse --resolve-git-dir foo/.git --show-toplevel

then it must receive the two lines of output in the correct
to know which is which. But asking for:

  git rev-parse --show-toplevel --resolve-git-dir foo/.git

is weird; we have to setup_git_directory() for the first
option.

So any implementation would probably have to either:

  - make two passes over the options, first figuring out
    whether we need a git-dir, and then actually handling
    the options. That's possible, but it's probably not
    worth the trouble.

  - call setup_git_directory() on the fly when an option
    needs it; that requires annotating all of the options,
    and there are a considerable number of them.

The original patch was not spurred by an actual bug report,
but by an observation[1] that was essentially "eh, this
looks unnecessarily restrictive". It _is_ restrictive, but
it turns out to be necessarily so. :)

And in practice, it is unlikely anybody was bothered by the
restriction. It's not really sane to use --local-env-vars
with other options, anyway, as it produces unbounded output
(so the caller only know it ends at EOF). It's more
plausible for --resolve-git-dir to be used with other
options, but still unlikely. It's main use is accessing
_other_ repositories (e.g., submodules), so making a query
on the main repository at the same time isn't very useful.

This patch therefore just reverts 68889b416, with a few
caveats:

  1. Since the original change, --resolve-git-dir learned to
     avoid segfaulting on a bogus. We don't know need to
     backport that, because the "restricted" form checks
     argc.

  2. The original patch mentioned that we didn't need to
     clean up any documentation, because the restriction
     wasn't documented. We can at least fix that by
     mentioning the restriction in the manpage.

  3. We can now mark our newly-added tests as passing. :)

[1] http://article.gmane.org/gmane.comp.version-control.git/230849

Signed-off-by: Jeff King <peff@peff.net>
---
 Documentation/git-rev-parse.txt   | 27 ++++++++++++++-------------
 builtin/rev-parse.c               | 31 +++++++++++++++----------------
 t/t1515-rev-parse-outside-repo.sh |  4 ++--
 3 files changed, 31 insertions(+), 31 deletions(-)

diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index b6c6326..0f2bb9b 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -28,7 +28,8 @@ OPTIONS
 Operation Modes
 ~~~~~~~~~~~~~~~
 
-Each of these options must appear first on the command line.
+Each of these options must appear first on the command line; they do not
+need to be run in a git repository.
 
 --parseopt::
 	Use 'git rev-parse' in option parsing mode (see PARSEOPT section below).
@@ -38,6 +39,18 @@ Each of these options must appear first on the command line.
 	section below). In contrast to the `--sq` option below, this
 	mode does only quoting. Nothing else is done to command input.
 
+--local-env-vars::
+	List the GIT_* environment variables that are local to the
+	repository (e.g. GIT_DIR or GIT_WORK_TREE, but not GIT_EDITOR).
+	Only the names of the variables are listed, not their value,
+	even if they are set.
+
+--resolve-git-dir <path>::
+	Check if <path> is a valid repository or a gitfile that
+	points at a valid repository, and print the location of the
+	repository.  If <path> is a gitfile then the resolved path
+	to the real repository is printed.
+
 Options for --parseopt
 ~~~~~~~~~~~~~~~~~~~~~~
 
@@ -201,12 +214,6 @@ explicitly.
 Options for Files
 ~~~~~~~~~~~~~~~~~
 
---local-env-vars::
-	List the GIT_* environment variables that are local to the
-	repository (e.g. GIT_DIR or GIT_WORK_TREE, but not GIT_EDITOR).
-	Only the names of the variables are listed, not their value,
-	even if they are set.
-
 --git-dir::
 	Show `$GIT_DIR` if defined. Otherwise show the path to
 	the .git directory. The path shown, when relative, is
@@ -230,12 +237,6 @@ print a message to stderr and exit with nonzero status.
 --is-bare-repository::
 	When the repository is bare print "true", otherwise "false".
 
---resolve-git-dir <path>::
-	Check if <path> is a valid repository or a gitfile that
-	points at a valid repository, and print the location of the
-	repository.  If <path> is a gitfile then the resolved path
-	to the real repository is printed.
-
 --git-path <path>::
 	Resolve "$GIT_DIR/<path>" and takes other path relocation
 	variables such as $GIT_OBJECT_DIRECTORY,
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index cf8487b..ccc0328 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -518,6 +518,21 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 	if (argc > 1 && !strcmp("--sq-quote", argv[1]))
 		return cmd_sq_quote(argc - 2, argv + 2);
 
+	if (argc == 2 && !strcmp("--local-env-vars", argv[1])) {
+		int i;
+		for (i = 0; local_repo_env[i]; i++)
+			printf("%s\n", local_repo_env[i]);
+		return 0;
+	}
+
+	if (argc > 2 && !strcmp(argv[1], "--resolve-git-dir")) {
+		const char *gitdir = resolve_gitdir(argv[2]);
+		if (!gitdir)
+			die("not a gitdir '%s'", argv[2]);
+		puts(gitdir);
+		return 0;
+	}
+
 	if (argc > 1 && !strcmp("-h", argv[1]))
 		usage(builtin_rev_parse_usage);
 
@@ -706,12 +721,6 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 				add_ref_exclusion(&ref_excludes, arg + 10);
 				continue;
 			}
-			if (!strcmp(arg, "--local-env-vars")) {
-				int i;
-				for (i = 0; local_repo_env[i]; i++)
-					printf("%s\n", local_repo_env[i]);
-				continue;
-			}
 			if (!strcmp(arg, "--show-toplevel")) {
 				const char *work_tree = get_git_work_tree();
 				if (work_tree)
@@ -767,16 +776,6 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 				puts(prefix_filename(pfx, strlen(pfx), get_git_common_dir()));
 				continue;
 			}
-			if (!strcmp(arg, "--resolve-git-dir")) {
-				const char *gitdir = argv[++i];
-				if (!gitdir)
-					die("--resolve-git-dir requires an argument");
-				gitdir = resolve_gitdir(gitdir);
-				if (!gitdir)
-					die("not a gitdir '%s'", argv[i]);
-				puts(gitdir);
-				continue;
-			}
 			if (!strcmp(arg, "--is-inside-git-dir")) {
 				printf("%s\n", is_inside_git_dir() ? "true"
 						: "false");
diff --git a/t/t1515-rev-parse-outside-repo.sh b/t/t1515-rev-parse-outside-repo.sh
index ae33093..3ec2971 100755
--- a/t/t1515-rev-parse-outside-repo.sh
+++ b/t/t1515-rev-parse-outside-repo.sh
@@ -27,14 +27,14 @@ test_expect_success 'rev-parse --sq-quote' '
 	test_cmp expect actual
 '
 
-test_expect_failure 'rev-parse --local-env-vars' '
+test_expect_success 'rev-parse --local-env-vars' '
 	git rev-parse --local-env-vars >actual &&
 	# we do not want to depend on the complete list here,
 	# so just look for something plausible
 	grep ^GIT_DIR actual
 '
 
-test_expect_failure 'rev-parse --resolve-git-dir' '
+test_expect_success 'rev-parse --resolve-git-dir' '
 	git init --separate-git-dir repo dir &&
 	test_must_fail git rev-parse --resolve-git-dir . &&
 	echo "$(pwd)/repo" >expect &&
-- 
2.7.2.767.g705917e

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH 2/2] Revert "rev-parse: remove restrictions on some options"
  2016-02-26 23:29 ` [PATCH 2/2] Revert "rev-parse: remove restrictions on some options" Jeff King
@ 2016-02-26 23:34   ` Jeff King
  2016-02-26 23:44     ` Junio C Hamano
  2016-02-29 11:01     ` Jeff King
  2016-02-27 12:25   ` John Keeping
  2016-02-28  0:53   ` Eric Sunshine
  2 siblings, 2 replies; 13+ messages in thread
From: Jeff King @ 2016-02-26 23:34 UTC (permalink / raw
  To: git; +Cc: Michael Haggerty, John Keeping, Junio C Hamano

On Fri, Feb 26, 2016 at 06:29:57PM -0500, Jeff King wrote:

> The best solution here would be to have a full parsing loop
> that handles all options, but only calls setup_git_directory
> as appropriate. Unfortunately, that's a bit complicated to
> implement. We _have_ to handle each option in the order it
> appears on the command line. If the caller asked for:
> 
>   git rev-parse --resolve-git-dir foo/.git --show-toplevel
> 
> then it must receive the two lines of output in the correct
> to know which is which. But asking for:
> 
>   git rev-parse --show-toplevel --resolve-git-dir foo/.git
> 
> is weird; we have to setup_git_directory() for the first
> option.
> 
> So any implementation would probably have to either:
> 
>   - make two passes over the options, first figuring out
>     whether we need a git-dir, and then actually handling
>     the options. That's possible, but it's probably not
>     worth the trouble.
> 
>   - call setup_git_directory() on the fly when an option
>     needs it; that requires annotating all of the options,
>     and there are a considerable number of them.

Having just sent this, of course, it occurs to me that a loop like:

   setup_git_directory_gently(&nongit);
   for (i = 0; i < argc; i++) {
  	if (!strcmp(argv[i], "--local-env-vars"))
  	... and other nongit-ok options ...
  
  	if (nongit)
  		die("need a repo");
  
  	if (!strcmp(argv[i], "--git-dir"))
  	... and other options ...
   }

would probably work. It does still leave things like --parseopt and
--sq-quote as one-offs, though, because they consume the rest of the
command line. And the fact remains that --local-repo-env isn't really
suitable for use with other options.

So I'm still tempted by this "lazy" approach.

-Peff

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 2/2] Revert "rev-parse: remove restrictions on some options"
  2016-02-26 23:34   ` Jeff King
@ 2016-02-26 23:44     ` Junio C Hamano
  2016-02-27  3:22       ` Jeff King
  2016-02-29 11:01     ` Jeff King
  1 sibling, 1 reply; 13+ messages in thread
From: Junio C Hamano @ 2016-02-26 23:44 UTC (permalink / raw
  To: Jeff King; +Cc: git, Michael Haggerty, John Keeping

Jeff King <peff@peff.net> writes:

> On Fri, Feb 26, 2016 at 06:29:57PM -0500, Jeff King wrote:
>
>> The best solution here would be to have a full parsing loop
>> that handles all options, but only calls setup_git_directory
>> as appropriate. Unfortunately, that's a bit complicated to
>> implement. We _have_ to handle each option in the order it
>> appears on the command line. If the caller asked for:
>> 
>>   git rev-parse --resolve-git-dir foo/.git --show-toplevel
>> 
>> then it must receive the two lines of output in the correct
>> to know which is which. But asking for:
>> 
>>   git rev-parse --show-toplevel --resolve-git-dir foo/.git
>> 
>> is weird; we have to setup_git_directory() for the first
>> option.
>> 
>> So any implementation would probably have to either:
>> 
>>   - make two passes over the options, first figuring out
>>     whether we need a git-dir, and then actually handling
>>     the options. That's possible, but it's probably not
>>     worth the trouble.
>> 
>>   - call setup_git_directory() on the fly when an option
>>     needs it; that requires annotating all of the options,
>>     and there are a considerable number of them.
>
> Having just sent this, of course, it occurs to me that a loop like:
>
>    setup_git_directory_gently(&nongit);
>    for (i = 0; i < argc; i++) {
>   	if (!strcmp(argv[i], "--local-env-vars"))
>   	... and other nongit-ok options ...
>   
>   	if (nongit)
>   		die("need a repo");
>   
>   	if (!strcmp(argv[i], "--git-dir"))
>   	... and other options ...
>    }
>
> would probably work. It does still leave things like --parseopt and
> --sq-quote as one-offs, though, because they consume the rest of the
> command line. And the fact remains that --local-repo-env isn't really
> suitable for use with other options.
>
> So I'm still tempted by this "lazy" approach.
>
> -Peff

Yup.

But why do you even need to run local-env-vars from outside a
repository in the first place?

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 2/2] Revert "rev-parse: remove restrictions on some options"
  2016-02-26 23:44     ` Junio C Hamano
@ 2016-02-27  3:22       ` Jeff King
  0 siblings, 0 replies; 13+ messages in thread
From: Jeff King @ 2016-02-27  3:22 UTC (permalink / raw
  To: Junio C Hamano; +Cc: git, Michael Haggerty, John Keeping

On Fri, Feb 26, 2016 at 03:44:01PM -0800, Junio C Hamano wrote:

> But why do you even need to run local-env-vars from outside a
> repository in the first place?

The short answer is: because it is about clearing the state to move into
a new repository, and we do not necessarily know what the old state was.

Here's a longer answer.

We (GitHub) have some scripts that preemptively clear the git env when
moving into another repository directory, so that the environment
doesn't lead to us operating in the wrong repository.

For example, we use alternates to share object storage between a series
of forks. So frequently in such scripts we may need to switch between
repositories (e.g., to sync a fork to the shared storage, and then go
back to the shared storage and run repack). To do so safely, we have to
clear the git env for each "cd" (otherwise things work fine when
$GIT_DIR is not set and we rely on auto-finding the repo, and break
horribly if the script is run with $GIT_DIR set).

There are a few corner cases where library code wants to "cd", but
doesn't know if it's coming from another repo or not.  So it clears the
git env to be on the safe side. We could fix it by always going to the
new repo and running "unset $(git rev-parse --local-env-vars)" there,
but I think that just has the opposite problem (you may be _leaving_ a
repository, and want to make sure you are no longer in one).

For us, it's mostly just an annoyance. rev-parse produces no output so
we don't clear any variables, and its stderr gets logged somewhere. We
really only care about $GIT_DIR, and if that is set to something valid,
then you are in a repo, rev-parse works, and we clear it. But you can
come up with cases where it does the wrong thing, like:

  # Work in some repo; set some git vars in the environment, but
  # rely on auto-discovery to find the actual repo.
  cd some-git-repo
  export GIT_WORK_TREE=/whatever
  git ...

  # Now go back to our root and do some work elsewhere.
  # We're no longer in a git repo.
  cd ..
  ... run some non-git commands ...

  # Now we want to go into a new repo. Clear the environment.
  unset $(git rev-parse --local-env-vars)
  cd ../another-git-repo
  git ...

In the third directory, you'd still have GIT_WORK_TREE set, even though
you asked to clear existing git state.

I don't think we have any scripts that do that, but it doesn't seem that
implausible to me.

-Peff

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 2/2] Revert "rev-parse: remove restrictions on some options"
  2016-02-26 23:29 ` [PATCH 2/2] Revert "rev-parse: remove restrictions on some options" Jeff King
  2016-02-26 23:34   ` Jeff King
@ 2016-02-27 12:25   ` John Keeping
  2016-02-29 11:11     ` Jeff King
  2016-02-28  0:53   ` Eric Sunshine
  2 siblings, 1 reply; 13+ messages in thread
From: John Keeping @ 2016-02-27 12:25 UTC (permalink / raw
  To: Jeff King; +Cc: git, Michael Haggerty, Junio C Hamano

On Fri, Feb 26, 2016 at 06:29:57PM -0500, Jeff King wrote:
> This reverts commit 68889b416d5b6a5cf7d280a428281d635fe9b292.
[snip]
> The original patch was not spurred by an actual bug report,
> but by an observation[1] that was essentially "eh, this
> looks unnecessarily restrictive". It _is_ restrictive, but
> it turns out to be necessarily so. :)

The aim of the original series was to improve the documentation, so I
don't think it's unreasonable to consider this a regression and revert
the functional change.  Although I think we can improve the behaviour
slightly (see below).

> diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
> index b6c6326..0f2bb9b 100644
> --- a/Documentation/git-rev-parse.txt
> +++ b/Documentation/git-rev-parse.txt
> @@ -28,7 +28,8 @@ OPTIONS
>  Operation Modes
>  ~~~~~~~~~~~~~~~
>  
> -Each of these options must appear first on the command line.
> +Each of these options must appear first on the command line; they do not
> +need to be run in a git repository.
>  
>  --parseopt::
>  	Use 'git rev-parse' in option parsing mode (see PARSEOPT section below).
> @@ -38,6 +39,18 @@ Each of these options must appear first on the command line.
>  	section below). In contrast to the `--sq` option below, this
>  	mode does only quoting. Nothing else is done to command input.
>  
> +--local-env-vars::
> +	List the GIT_* environment variables that are local to the
> +	repository (e.g. GIT_DIR or GIT_WORK_TREE, but not GIT_EDITOR).
> +	Only the names of the variables are listed, not their value,
> +	even if they are set.

I think we should add:

	No other arguments may be supplied.

> +--resolve-git-dir <path>::
> +	Check if <path> is a valid repository or a gitfile that
> +	points at a valid repository, and print the location of the
> +	repository.  If <path> is a gitfile then the resolved path
> +	to the real repository is printed.

Again I think this should say that only the `path` argument may be
supplied.

>  --git-path <path>::
>  	Resolve "$GIT_DIR/<path>" and takes other path relocation
>  	variables such as $GIT_OBJECT_DIRECTORY,
> diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
> index cf8487b..ccc0328 100644
> --- a/builtin/rev-parse.c
> +++ b/builtin/rev-parse.c
> @@ -518,6 +518,21 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
>  	if (argc > 1 && !strcmp("--sq-quote", argv[1]))
>  		return cmd_sq_quote(argc - 2, argv + 2);
>  
> +	if (argc == 2 && !strcmp("--local-env-vars", argv[1])) {

Maybe:

	if (argc > 1 && !strcmp("--local-env-vars", argv[1])) {
		if (argc != 2)
			die("--local-env-vars must be the only argument");

since the behaviour of:

	$ git rev-parse --local-env-vars --
	--local-env-vars
	--

is quite surprising.

> +		int i;
> +		for (i = 0; local_repo_env[i]; i++)
> +			printf("%s\n", local_repo_env[i]);
> +		return 0;
> +	}
> +
> +	if (argc > 2 && !strcmp(argv[1], "--resolve-git-dir")) {

This is less bad, but again it might be nice to provide a better error
if the path argument isn't supplied.

> +		const char *gitdir = resolve_gitdir(argv[2]);
> +		if (!gitdir)
> +			die("not a gitdir '%s'", argv[2]);
> +		puts(gitdir);
> +		return 0;
> +	}
> +
>  	if (argc > 1 && !strcmp("-h", argv[1]))
>  		usage(builtin_rev_parse_usage);
>  
> @@ -706,12 +721,6 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
>  				add_ref_exclusion(&ref_excludes, arg + 10);
>  				continue;
>  			}
> -			if (!strcmp(arg, "--local-env-vars")) {

What about leaving this in and replacing the body of the if statement
with:

	die("--local-env-vars must be the first argument");

?  I expect this will significantly reduce debugging time if anyone is
relying on the current behaviour.

> -				int i;
> -				for (i = 0; local_repo_env[i]; i++)
> -					printf("%s\n", local_repo_env[i]);
> -				continue;
> -			}
>  			if (!strcmp(arg, "--show-toplevel")) {
>  				const char *work_tree = get_git_work_tree();
>  				if (work_tree)
> @@ -767,16 +776,6 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
>  				puts(prefix_filename(pfx, strlen(pfx), get_git_common_dir()));
>  				continue;
>  			}
> -			if (!strcmp(arg, "--resolve-git-dir")) {
> -				const char *gitdir = argv[++i];
> -				if (!gitdir)
> -					die("--resolve-git-dir requires an argument");
> -				gitdir = resolve_gitdir(gitdir);
> -				if (!gitdir)
> -					die("not a gitdir '%s'", argv[i]);
> -				puts(gitdir);
> -				continue;
> -			}
>  			if (!strcmp(arg, "--is-inside-git-dir")) {
>  				printf("%s\n", is_inside_git_dir() ? "true"
>  						: "false");
> diff --git a/t/t1515-rev-parse-outside-repo.sh b/t/t1515-rev-parse-outside-repo.sh
> index ae33093..3ec2971 100755
> --- a/t/t1515-rev-parse-outside-repo.sh
> +++ b/t/t1515-rev-parse-outside-repo.sh
> @@ -27,14 +27,14 @@ test_expect_success 'rev-parse --sq-quote' '
>  	test_cmp expect actual
>  '
>  
> -test_expect_failure 'rev-parse --local-env-vars' '
> +test_expect_success 'rev-parse --local-env-vars' '
>  	git rev-parse --local-env-vars >actual &&
>  	# we do not want to depend on the complete list here,
>  	# so just look for something plausible
>  	grep ^GIT_DIR actual
>  '
>  
> -test_expect_failure 'rev-parse --resolve-git-dir' '
> +test_expect_success 'rev-parse --resolve-git-dir' '
>  	git init --separate-git-dir repo dir &&
>  	test_must_fail git rev-parse --resolve-git-dir . &&
>  	echo "$(pwd)/repo" >expect &&
> -- 
> 2.7.2.767.g705917e
> --
> To unsubscribe from this list: send the line "unsubscribe git" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 2/2] Revert "rev-parse: remove restrictions on some options"
  2016-02-26 23:29 ` [PATCH 2/2] Revert "rev-parse: remove restrictions on some options" Jeff King
  2016-02-26 23:34   ` Jeff King
  2016-02-27 12:25   ` John Keeping
@ 2016-02-28  0:53   ` Eric Sunshine
  2016-02-29 11:12     ` Jeff King
  2 siblings, 1 reply; 13+ messages in thread
From: Eric Sunshine @ 2016-02-28  0:53 UTC (permalink / raw
  To: Jeff King; +Cc: Git List, Michael Haggerty, John Keeping, Junio C Hamano

On Fri, Feb 26, 2016 at 6:29 PM, Jeff King <peff@peff.net> wrote:
> This reverts commit 68889b416d5b6a5cf7d280a428281d635fe9b292.
>
> That commit bumped some rev-parse options into the main
> option-parsing loop, which meant that they no longer had to
> come at the very beginning of the option list. However, that
> also means that they now came after our call to
> setup_git_directory(), and will no longer run outside of a
> git repository.
>
> For --local-env-vars, this is semi-questionable. The main
> use of that option is to get a list of environment variables
> to clear, and if you are not in a repository, there
> _probably_ isn't anything to clear. But it places an
> unnecessary restriction on callers who may be using it
> preemptively.
>
> For --resolve-git-dir, it is almost certainly a regression.
> That option is about finding a git dir in the first place,
> so it is reasonably likely to be called from outside an
> existing repository.
>
> The best solution here would be to have a full parsing loop
> that handles all options, but only calls setup_git_directory
> as appropriate. Unfortunately, that's a bit complicated to
> implement. We _have_ to handle each option in the order it
> appears on the command line. If the caller asked for:
>
>   git rev-parse --resolve-git-dir foo/.git --show-toplevel
>
> then it must receive the two lines of output in the correct

s/correct/& order/

> to know which is which. But asking for:
>
>   git rev-parse --show-toplevel --resolve-git-dir foo/.git
>
> is weird; we have to setup_git_directory() for the first
> option.
>
> So any implementation would probably have to either:
>
>   - make two passes over the options, first figuring out
>     whether we need a git-dir, and then actually handling
>     the options. That's possible, but it's probably not
>     worth the trouble.
>
>   - call setup_git_directory() on the fly when an option
>     needs it; that requires annotating all of the options,
>     and there are a considerable number of them.
>
> The original patch was not spurred by an actual bug report,
> but by an observation[1] that was essentially "eh, this
> looks unnecessarily restrictive". It _is_ restrictive, but
> it turns out to be necessarily so. :)
>
> And in practice, it is unlikely anybody was bothered by the
> restriction. It's not really sane to use --local-env-vars
> with other options, anyway, as it produces unbounded output
> (so the caller only know it ends at EOF). It's more
> plausible for --resolve-git-dir to be used with other
> options, but still unlikely. It's main use is accessing

s/It's/Its/

> _other_ repositories (e.g., submodules), so making a query
> on the main repository at the same time isn't very useful.
>
> This patch therefore just reverts 68889b416, with a few
> caveats:
>
>   1. Since the original change, --resolve-git-dir learned to
>      avoid segfaulting on a bogus. We don't know need to

s/bogus/& argument/

>      backport that, because the "restricted" form checks
>      argc.
>
>   2. The original patch mentioned that we didn't need to
>      clean up any documentation, because the restriction
>      wasn't documented. We can at least fix that by
>      mentioning the restriction in the manpage.
>
>   3. We can now mark our newly-added tests as passing. :)
>
> [1] http://article.gmane.org/gmane.comp.version-control.git/230849
>
> Signed-off-by: Jeff King <peff@peff.net>

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 2/2] Revert "rev-parse: remove restrictions on some options"
  2016-02-26 23:34   ` Jeff King
  2016-02-26 23:44     ` Junio C Hamano
@ 2016-02-29 11:01     ` Jeff King
  2016-02-29 17:32       ` Junio C Hamano
  1 sibling, 1 reply; 13+ messages in thread
From: Jeff King @ 2016-02-29 11:01 UTC (permalink / raw
  To: git; +Cc: Michael Haggerty, John Keeping, Junio C Hamano

On Fri, Feb 26, 2016 at 06:34:49PM -0500, Jeff King wrote:

> > So any implementation would probably have to either:
> > 
> >   - make two passes over the options, first figuring out
> >     whether we need a git-dir, and then actually handling
> >     the options. That's possible, but it's probably not
> >     worth the trouble.
> > 
> >   - call setup_git_directory() on the fly when an option
> >     needs it; that requires annotating all of the options,
> >     and there are a considerable number of them.
> 
> Having just sent this, of course, it occurs to me that a loop like:
> 
>    setup_git_directory_gently(&nongit);
>    for (i = 0; i < argc; i++) {
>   	if (!strcmp(argv[i], "--local-env-vars"))
>   	... and other nongit-ok options ...
>   
>   	if (nongit)
>   		die("need a repo");
>   
>   	if (!strcmp(argv[i], "--git-dir"))
>   	... and other options ...
>    }

Actually, it is even easier than that. Those options don't care whether
they are in a repo or not, so we can just wait to call
setup_git_directory() in the loop.

IOW, I think my 2/2 should be replaced with this:

-- >8 --
Subject: [PATCH] rev-parse: let some options run outside repository

Once upon a time, you could use "--local-env-vars" and
"--resolve-git-dir" outside of any git repository, but they
had to come first on the command line. Commit 68889b4
(rev-parse: remove restrictions on some options, 2013-07-21)
put them into the normal option-parsing loop, fixing the
latter. But it inadvertently broke the former, as we call
setup_git_directory() before starting that loop.

We can note that those options don't care even conditionally
about whether we are in a git repo. So it's fine if we
simply wait to setup the repo until we see an option that
needs it.

However, there is one special exception we should make:
historically, rev-parse will set up the repository and read
config even if there are _no_ options. Some of the
tests in t1300 rely on this to check "git -c $config"
parsing. That's not mirroring real-world use, and we could
tweak the test.  But t0002 uses a bare "git rev-parse" to
check "are we in a git repository?". It's plausible that
real-world scripts are relying on this.

So let's cover this case specially, and treat an option-less
"rev-parse" as "see if we're in a repo".

Signed-off-by: Jeff King <peff@peff.net>
---
 builtin/rev-parse.c               | 50 +++++++++++++++++++++++++--------------
 t/t1515-rev-parse-outside-repo.sh |  4 ++--
 2 files changed, 34 insertions(+), 20 deletions(-)

diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index cf8487b..c961b74 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -505,6 +505,7 @@ N_("git rev-parse --parseopt [<options>] -- [<args>...]\n"
 int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 {
 	int i, as_is = 0, verify = 0, quiet = 0, revs_count = 0, type = 0;
+	int did_repo_setup = 0;
 	int has_dashdash = 0;
 	int output_prefix = 0;
 	unsigned char sha1[20];
@@ -528,11 +529,40 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 		}
 	}
 
-	prefix = setup_git_directory();
-	git_config(git_default_config, NULL);
+	/* No options; just report on whether we're in a git repo or not. */
+	if (argc == 1) {
+		setup_git_directory();
+		git_config(git_default_config, NULL);
+		return 0;
+	}
+
 	for (i = 1; i < argc; i++) {
 		const char *arg = argv[i];
 
+		if (!strcmp(arg, "--local-env-vars")) {
+			int i;
+			for (i = 0; local_repo_env[i]; i++)
+				printf("%s\n", local_repo_env[i]);
+			continue;
+		}
+		if (!strcmp(arg, "--resolve-git-dir")) {
+			const char *gitdir = argv[++i];
+			if (!gitdir)
+				die("--resolve-git-dir requires an argument");
+			gitdir = resolve_gitdir(gitdir);
+			if (!gitdir)
+				die("not a gitdir '%s'", argv[i]);
+			puts(gitdir);
+			continue;
+		}
+
+		/* The rest of the options require a git repository. */
+		if (!did_repo_setup) {
+			prefix = setup_git_directory();
+			git_config(git_default_config, NULL);
+			did_repo_setup = 1;
+		}
+
 		if (!strcmp(arg, "--git-path")) {
 			if (!argv[i + 1])
 				die("--git-path requires an argument");
@@ -706,12 +736,6 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 				add_ref_exclusion(&ref_excludes, arg + 10);
 				continue;
 			}
-			if (!strcmp(arg, "--local-env-vars")) {
-				int i;
-				for (i = 0; local_repo_env[i]; i++)
-					printf("%s\n", local_repo_env[i]);
-				continue;
-			}
 			if (!strcmp(arg, "--show-toplevel")) {
 				const char *work_tree = get_git_work_tree();
 				if (work_tree)
@@ -767,16 +791,6 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 				puts(prefix_filename(pfx, strlen(pfx), get_git_common_dir()));
 				continue;
 			}
-			if (!strcmp(arg, "--resolve-git-dir")) {
-				const char *gitdir = argv[++i];
-				if (!gitdir)
-					die("--resolve-git-dir requires an argument");
-				gitdir = resolve_gitdir(gitdir);
-				if (!gitdir)
-					die("not a gitdir '%s'", argv[i]);
-				puts(gitdir);
-				continue;
-			}
 			if (!strcmp(arg, "--is-inside-git-dir")) {
 				printf("%s\n", is_inside_git_dir() ? "true"
 						: "false");
diff --git a/t/t1515-rev-parse-outside-repo.sh b/t/t1515-rev-parse-outside-repo.sh
index ae33093..3ec2971 100755
--- a/t/t1515-rev-parse-outside-repo.sh
+++ b/t/t1515-rev-parse-outside-repo.sh
@@ -27,14 +27,14 @@ test_expect_success 'rev-parse --sq-quote' '
 	test_cmp expect actual
 '
 
-test_expect_failure 'rev-parse --local-env-vars' '
+test_expect_success 'rev-parse --local-env-vars' '
 	git rev-parse --local-env-vars >actual &&
 	# we do not want to depend on the complete list here,
 	# so just look for something plausible
 	grep ^GIT_DIR actual
 '
 
-test_expect_failure 'rev-parse --resolve-git-dir' '
+test_expect_success 'rev-parse --resolve-git-dir' '
 	git init --separate-git-dir repo dir &&
 	test_must_fail git rev-parse --resolve-git-dir . &&
 	echo "$(pwd)/repo" >expect &&
-- 
2.8.0.rc0.276.gddf4100

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH 2/2] Revert "rev-parse: remove restrictions on some options"
  2016-02-27 12:25   ` John Keeping
@ 2016-02-29 11:11     ` Jeff King
  0 siblings, 0 replies; 13+ messages in thread
From: Jeff King @ 2016-02-29 11:11 UTC (permalink / raw
  To: John Keeping; +Cc: git, Michael Haggerty, Junio C Hamano

On Sat, Feb 27, 2016 at 12:25:11PM +0000, John Keeping wrote:

> On Fri, Feb 26, 2016 at 06:29:57PM -0500, Jeff King wrote:
> > This reverts commit 68889b416d5b6a5cf7d280a428281d635fe9b292.
> [snip]
> > The original patch was not spurred by an actual bug report,
> > but by an observation[1] that was essentially "eh, this
> > looks unnecessarily restrictive". It _is_ restrictive, but
> > it turns out to be necessarily so. :)
> 
> The aim of the original series was to improve the documentation, so I
> don't think it's unreasonable to consider this a regression and revert
> the functional change.  Although I think we can improve the behaviour
> slightly (see below).

Thanks for looking this over. I agree that the changes you suggested
would be an improvement over what I posted. But I tried out the
alternate plan to handle the repo-setup more gracefully inside the loop.
I think that looks much simpler (at the very least, I had to spend a lot
fewer words trying to justify it in the commit message!).

And that makes most of your suggestions obsolete. I'll go through
them...

> > +--local-env-vars::
> > +	List the GIT_* environment variables that are local to the
> > +	repository (e.g. GIT_DIR or GIT_WORK_TREE, but not GIT_EDITOR).
> > +	Only the names of the variables are listed, not their value,
> > +	even if they are set.
> 
> I think we should add:
> 
> 	No other arguments may be supplied.

So now you can give other arguments. I doubt it's a _good idea_ to do
so, but you could certainly do:

  git rev-parse --git-dir --local-env-vars

if you wanted to. You can even do the opposite, and I guess it would be
correct as long as you popped the final line off the end as --git-dir.

So I guess we could restrict it, but I don't think it's an issue in
practice, and it does technically work.

> > +--resolve-git-dir <path>::
> > +	Check if <path> is a valid repository or a gitfile that
> > +	points at a valid repository, and print the location of the
> > +	repository.  If <path> is a gitfile then the resolved path
> > +	to the real repository is printed.
> 
> Again I think this should say that only the `path` argument may be
> supplied.

This one I think is more reasonable to use along with other options. Or
you could even pass a sequence of:

  git rev-parse --resolve-git-dir foo --resolve-git-dir bar

Again, I doubt it's very useful in practice, but it does the obvious
thing (whereas with my original patch, it silently ignored subsequent
options).

> > +	if (argc == 2 && !strcmp("--local-env-vars", argv[1])) {
> 
> Maybe:
> 
> 	if (argc > 1 && !strcmp("--local-env-vars", argv[1])) {
> 		if (argc != 2)
> 			die("--local-env-vars must be the only argument");
> 
> since the behaviour of:
> 
> 	$ git rev-parse --local-env-vars --
> 	--local-env-vars
> 	--
> 
> is quite surprising.

This now does what you'd expect (it's probably not _useful_, but at
least it isn't horrifying and surprising, like the v1 behavior).

> > +	if (argc > 2 && !strcmp(argv[1], "--resolve-git-dir")) {
> 
> This is less bad, but again it might be nice to provide a better error
> if the path argument isn't supplied.

This one is OK now, too.

> > @@ -706,12 +721,6 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
> >  				add_ref_exclusion(&ref_excludes, arg + 10);
> >  				continue;
> >  			}
> > -			if (!strcmp(arg, "--local-env-vars")) {
> 
> What about leaving this in and replacing the body of the if statement
> with:
> 
> 	die("--local-env-vars must be the first argument");
> 
> ?  I expect this will significantly reduce debugging time if anyone is
> relying on the current behaviour.

The new version I sent covers this, too (and it does the right thing).


Thanks for a careful review of the corner cases. Even though my response
to all of them is "yeah, but your suggestion is now obsolete", it makes
me feel better about the v2 patch to see that it magically clears up all
of these issues.

-Peff

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 2/2] Revert "rev-parse: remove restrictions on some options"
  2016-02-28  0:53   ` Eric Sunshine
@ 2016-02-29 11:12     ` Jeff King
  0 siblings, 0 replies; 13+ messages in thread
From: Jeff King @ 2016-02-29 11:12 UTC (permalink / raw
  To: Eric Sunshine; +Cc: Git List, Michael Haggerty, John Keeping, Junio C Hamano

On Sat, Feb 27, 2016 at 07:53:02PM -0500, Eric Sunshine wrote:

> > then it must receive the two lines of output in the correct
> 
> s/correct/& order/

I fixed this and all of the other typos by switching to a patch that
needs about one tenth as much explanation. :)

I'm sure it's not possible that I introduced any new ones...

-Peff

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 2/2] Revert "rev-parse: remove restrictions on some options"
  2016-02-29 11:01     ` Jeff King
@ 2016-02-29 17:32       ` Junio C Hamano
  2016-02-29 21:29         ` Jeff King
  0 siblings, 1 reply; 13+ messages in thread
From: Junio C Hamano @ 2016-02-29 17:32 UTC (permalink / raw
  To: Jeff King; +Cc: git, Michael Haggerty, John Keeping

Jeff King <peff@peff.net> writes:

> IOW, I think my 2/2 should be replaced with this:

This looks sensible.

Don't we still want the documentation updates from the previous 2/2?

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 2/2] Revert "rev-parse: remove restrictions on some options"
  2016-02-29 17:32       ` Junio C Hamano
@ 2016-02-29 21:29         ` Jeff King
  0 siblings, 0 replies; 13+ messages in thread
From: Jeff King @ 2016-02-29 21:29 UTC (permalink / raw
  To: Junio C Hamano; +Cc: git, Michael Haggerty, John Keeping

On Mon, Feb 29, 2016 at 09:32:22AM -0800, Junio C Hamano wrote:

> Jeff King <peff@peff.net> writes:
> 
> > IOW, I think my 2/2 should be replaced with this:
> 
> This looks sensible.
> 
> Don't we still want the documentation updates from the previous 2/2?

I don't think so. They were primarily about moving those option blocks
to the "these options must come first..." section, which is no longer
true.

It also added "you don't have to be in a git repository for these" to
that section, but I think that is less important. We could add that
individually to each option, I guess.

-Peff

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2016-02-29 21:29 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-02-26 23:25 [PATCH/RFC 0/2] fix some rev-parse options in non-repos Jeff King
2016-02-26 23:26 ` [PATCH 1/2] t1515: add tests for rev-parse out-of-repo helpers Jeff King
2016-02-26 23:29 ` [PATCH 2/2] Revert "rev-parse: remove restrictions on some options" Jeff King
2016-02-26 23:34   ` Jeff King
2016-02-26 23:44     ` Junio C Hamano
2016-02-27  3:22       ` Jeff King
2016-02-29 11:01     ` Jeff King
2016-02-29 17:32       ` Junio C Hamano
2016-02-29 21:29         ` Jeff King
2016-02-27 12:25   ` John Keeping
2016-02-29 11:11     ` Jeff King
2016-02-28  0:53   ` Eric Sunshine
2016-02-29 11:12     ` Jeff King

Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).