All of lore.kernel.org
 help / color / mirror / Atom feed
* Linked workdirs break typo-correction.
@ 2015-06-25 14:15 Bjørnar Snoksrud
  2015-06-26 10:37 ` [PATCH] setup: set env $GIT_WORK_TREE when work tree is set, like $GIT_DIR Nguyễn Thái Ngọc Duy
  0 siblings, 1 reply; 7+ messages in thread
From: Bjørnar Snoksrud @ 2015-06-25 14:15 UTC (permalink / raw)
  To: Git Mailing List

This is a weird one..

When standing in a folder inside a linked working copy, the
typo-correction breaks all commands.

Repro:
~/git $ git --version
git version 2.4.4.600.g6397abd
~/git $ git init bar
Initialized empty Git repository in ~/git/bar/.git/
~/git $ cd bar
~/git/bar (master #) $ git commit -m init --allow-empty
[master (root-commit) 554ea84] init
~/git/bar (master) $ mkdir folder
~/git/bar (master) $ touch folder/file
~/git/bar (master) $ git add folder
~/git/bar (master +) $ git commit -m folder
[master 8c00ba8] folder
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 folder/file
~/git/bar (master) $ cd folder/
~/git/bar/folder (master) $ git shw
WARNING: You called a Git command named 'shw', which does not exist.
Continuing under the assumption that you meant 'show'
in 0.1 seconds automatically...
commit 8c00ba8d30cff0e0d1b9cf110a65ea8f33edf8b2
Author: Bjørnar Snoksrud <bsnoksru@cisco.com>
Date:   Thu Jun 25 16:08:01 2015 +0200

    folder

diff --git a/folder/file b/folder/file
new file mode 100644
index 0000000..e69de29
bsnoksru@calculon ~/git/bar/folder (master) $ git branch foo
bsnoksru@calculon ~/git/bar/folder (master) $ git checkout foo --to
~/git/foo
Enter /home/bsnoksru/git/foo (identifier foo)
Switched to branch 'foo'
bsnoksru@calculon ~/git/bar/folder (master) $ cd ../../foo/folder/
~/git/foo/folder (foo) $ git shw
WARNING: You called a Git command named 'shw', which does not exist.
Continuing under the assumption that you meant 'show'
in 0.1 seconds automatically...
fatal: internal error: work tree has already been set
Current worktree: ~/git/foo
New worktree: ~/git/foo/folder
~/git/foo/folder (foo) $ git brnch baz
WARNING: You called a Git command named 'brnch', which does not exist.
Continuing under the assumption that you meant 'branch'
in 0.1 seconds automatically...
fatal: internal error: work tree has already been set
Current worktree: /home/bsnoksru/git/foo
New worktree: /home/bsnoksru/git/foo/folder


-- 
bjornar@snoksrud.no

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

* [PATCH] setup: set env $GIT_WORK_TREE when work tree is set, like $GIT_DIR
  2015-06-25 14:15 Linked workdirs break typo-correction Bjørnar Snoksrud
@ 2015-06-26 10:37 ` Nguyễn Thái Ngọc Duy
  2015-06-26 11:56   ` Jeff King
                     ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2015-06-26 10:37 UTC (permalink / raw)
  To: git; +Cc: snoksrud, Nguyễn Thái Ngọc Duy

In the test case, we run setup_git_dir_gently() the first time to read
$GIT_DIR/config so that we can resolve aliases. We'll enter
setup_discovered_git_dir() and may or may not call set_git_dir() near
the end of the function, depending on whether the detected git dir is
".git" or not. This set_git_dir() will set env var $GIT_DIR.

For normal repo, git dir detected via setup_discovered_git_dir() will be
".git", and set_git_dir() is not called. If .git file is used however,
the git dir can't be ".git" and set_git_dir() is called and $GIT_DIR
set. This is the key of this problem.

If we expand an alias (or autocorrect command names), then
setup_git_dir_gently() is run the second time. If $GIT_DIR is not set in
the first run, we run the same setup_discovered_git_dir() as before.
Nothing to see. If it is, however, we'll enter setup_explicit_git_dir()
this time.

This is where the "fun" is. The legacy behavior is, if $GIT_WORK_TREE is
not set but $GIT_DIR is, cwd is chosen as worktree's top. If you happen
to stand at worktree's top when you do this, all is well. If you are in
a subdir "foo/bar" (real worktree's top is "foo"), this behavior bites
you: your detected worktree is now "foo/bar", but the first run
correctly detected worktree as "foo". You get "internal error: work tree
has already been set" as a result.

Bottom line is, when $GIT_DIR is set, $GIT_WORK_TREE should be set too
unless there's no work tree. But setting $GIT_WORK_TREE inside
set_git_dir() may backfire. We don't know at that point if work tree is
already configured by the caller. So set it when work tree is
detected. It does not harm if $GIT_WORK_TREE is set while $GIT_DIR is
not.

Reported-by: Bjørnar Snoksrud <snoksrud@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 environment.c      |  2 ++
 t/t0002-gitfile.sh | 17 +++++++++++++++++
 2 files changed, 19 insertions(+)

diff --git a/environment.c b/environment.c
index 61c685b..8f1b249 100644
--- a/environment.c
+++ b/environment.c
@@ -231,6 +231,8 @@ void set_git_work_tree(const char *new_work_tree)
 	}
 	git_work_tree_initialized = 1;
 	work_tree = xstrdup(real_path(new_work_tree));
+	if (setenv(GIT_WORK_TREE_ENVIRONMENT, work_tree, 1))
+		error("Could not set GIT_WORK_TREE to '%s'", work_tree);
 }
 
 const char *get_git_work_tree(void)
diff --git a/t/t0002-gitfile.sh b/t/t0002-gitfile.sh
index 37e9396..9393322 100755
--- a/t/t0002-gitfile.sh
+++ b/t/t0002-gitfile.sh
@@ -99,4 +99,21 @@ test_expect_success 'check rev-list' '
 	test "$SHA" = "$(git rev-list HEAD)"
 '
 
+test_expect_success 'setup_git_dir twice in subdir' '
+	git init sgd &&
+	(
+		cd sgd &&
+		git config alias.lsfi ls-files &&
+		mv .git .realgit &&
+		echo "gitdir: .realgit" >.git &&
+		mkdir subdir &&
+		cd subdir &&
+		>foo &&
+		git add foo &&
+		git lsfi >actual &&
+		echo foo >expected &&
+		test_cmp expected actual
+	)
+'
+
 test_done
-- 
2.3.0.rc1.137.g477eb31

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

* Re: [PATCH] setup: set env $GIT_WORK_TREE when work tree is set, like $GIT_DIR
  2015-06-26 10:37 ` [PATCH] setup: set env $GIT_WORK_TREE when work tree is set, like $GIT_DIR Nguyễn Thái Ngọc Duy
@ 2015-06-26 11:56   ` Jeff King
  2015-06-27  5:57     ` Duy Nguyen
  2015-06-26 17:15   ` Junio C Hamano
  2015-06-26 17:30   ` Junio C Hamano
  2 siblings, 1 reply; 7+ messages in thread
From: Jeff King @ 2015-06-26 11:56 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, snoksrud

On Fri, Jun 26, 2015 at 05:37:35PM +0700, Nguyễn Thái Ngọc Duy wrote:

> This is where the "fun" is. The legacy behavior is, if $GIT_WORK_TREE is
> not set but $GIT_DIR is, cwd is chosen as worktree's top. If you happen
> to stand at worktree's top when you do this, all is well. If you are in
> a subdir "foo/bar" (real worktree's top is "foo"), this behavior bites
> you: your detected worktree is now "foo/bar", but the first run
> correctly detected worktree as "foo". You get "internal error: work tree
> has already been set" as a result.

I think this makes sense. I feel like we've dealt with this before, but
the two previous rounds I found were basically:

  - we have GIT_IMPLICIT_WORK_TREE, but that is for the _opposite_ case.
    I.e., when we do not have a work tree and must communicate so to
    later code (including sub-processes).

  - a discussion about switching the "work tree defaults to '.' when
    $GIT_DIR is set" behavior yielded almost the identical patch:

      http://article.gmane.org/gmane.comp.version-control.git/219196

    but we were so wrapped up in the greater discussion we did not apply
    that simple fix. :)

> Bottom line is, when $GIT_DIR is set, $GIT_WORK_TREE should be set too
> unless there's no work tree. But setting $GIT_WORK_TREE inside
> set_git_dir() may backfire. We don't know at that point if work tree is
> already configured by the caller. So set it when work tree is
> detected. It does not harm if $GIT_WORK_TREE is set while $GIT_DIR is
> not.

Yeah, it would be nicer if we could make sure we always set
GIT_WORK_TREE along with GIT_DIR (since anything else is potentially
dangerous), but I don't think it's feasible to do it in set_git_dir for
the reasons you state.

I gave a quick peek through setup_explicit_git_dir to see if we missed
other cases (spoiler: no):

  - if we have core.bare set, we set $GIT_DIR without setting the
    working tree. But that's OK, because we'll never look at
    $GIT_WORK_TREE in that case. Good.

  - if we have core.worktree set, then we call set_git_work_tree to
    match. Good.

  - If GIT_IMPLICIT_WORK_TREE is turned off, we set_git_dir but do not
    set GIT_WORK_TREE. Good, because otherwise it would take precedence.

  - there are some more calls to set_git_dir at the end, but I think in
    those cases we will already have set up the working tree (or decided
    we do not have one, by the above logic).

So that all seems fine. We also call set_git_dir from enter_repo, but in
that case we have already moved into the .git directory itself, so that
is OK. setup_discovered_git_dir also makes some calls, but either we are
explicitly bare there, or we call set_git_work_tree(".").

So I do not see any cases that will regress, or that are uncovered. Of
course, that is just from reading the code. Getting one million monkeys
to bang on the keyboard may turn up any real problems. :)

> +	if (setenv(GIT_WORK_TREE_ENVIRONMENT, work_tree, 1))
> +		error("Could not set GIT_WORK_TREE to '%s'", work_tree);

Should this be die()? setenv() should basically never fail, but if it
does, it could be confusing and/or dangerous to run without the variable
set.

-Peff

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

* Re: [PATCH] setup: set env $GIT_WORK_TREE when work tree is set, like $GIT_DIR
  2015-06-26 10:37 ` [PATCH] setup: set env $GIT_WORK_TREE when work tree is set, like $GIT_DIR Nguyễn Thái Ngọc Duy
  2015-06-26 11:56   ` Jeff King
@ 2015-06-26 17:15   ` Junio C Hamano
  2015-06-26 19:02     ` Junio C Hamano
  2015-06-26 17:30   ` Junio C Hamano
  2 siblings, 1 reply; 7+ messages in thread
From: Junio C Hamano @ 2015-06-26 17:15 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, snoksrud

Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:

> Bottom line is, when $GIT_DIR is set, $GIT_WORK_TREE should be set too
> unless there's no work tree. But setting $GIT_WORK_TREE inside
> set_git_dir() may backfire. We don't know at that point if work tree is
> already configured by the caller. So set it when work tree is
> detected. It does not harm if $GIT_WORK_TREE is set while $GIT_DIR is
> not.

Hmmm, setting GIT_WORK_TREE locally without exporting it to our
subprocesses would not hurt even when we do not see GIT_DIR, so I
agree "It does not harm" is true for _us_, the process that runs
this code itself.

But I am not sure if it is true for our children (e.g. hooks,
filters etc. that is spawned by us).  With this change, they inherit
GIT_WORK_TREE and no GIT_DIR, in such a case.  If they set GIT_DIR
themselves for their own use, perhaps arranging to work in somewhere
else they know by chdir'ing there, they did not have to set
GIT_WORK_TREE=. before runing git in there, but now they do, because
we start exporting GIT_WORK_TREE to interfere what they have been
doing.

"It does not harm" is probably false for our children, I would
think.  The children can do new things to avoid the harm, though.


> Reported-by: Bjørnar Snoksrud <snoksrud@gmail.com>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
>  environment.c      |  2 ++
>  t/t0002-gitfile.sh | 17 +++++++++++++++++
>  2 files changed, 19 insertions(+)
>
> diff --git a/environment.c b/environment.c
> index 61c685b..8f1b249 100644
> --- a/environment.c
> +++ b/environment.c
> @@ -231,6 +231,8 @@ void set_git_work_tree(const char *new_work_tree)
>  	}
>  	git_work_tree_initialized = 1;
>  	work_tree = xstrdup(real_path(new_work_tree));
> +	if (setenv(GIT_WORK_TREE_ENVIRONMENT, work_tree, 1))
> +		error("Could not set GIT_WORK_TREE to '%s'", work_tree);
>  }
>  
>  const char *get_git_work_tree(void)
> diff --git a/t/t0002-gitfile.sh b/t/t0002-gitfile.sh
> index 37e9396..9393322 100755
> --- a/t/t0002-gitfile.sh
> +++ b/t/t0002-gitfile.sh
> @@ -99,4 +99,21 @@ test_expect_success 'check rev-list' '
>  	test "$SHA" = "$(git rev-list HEAD)"
>  '
>  
> +test_expect_success 'setup_git_dir twice in subdir' '
> +	git init sgd &&
> +	(
> +		cd sgd &&
> +		git config alias.lsfi ls-files &&
> +		mv .git .realgit &&
> +		echo "gitdir: .realgit" >.git &&
> +		mkdir subdir &&
> +		cd subdir &&
> +		>foo &&
> +		git add foo &&
> +		git lsfi >actual &&
> +		echo foo >expected &&
> +		test_cmp expected actual
> +	)
> +'
> +
>  test_done

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

* Re: [PATCH] setup: set env $GIT_WORK_TREE when work tree is set, like $GIT_DIR
  2015-06-26 10:37 ` [PATCH] setup: set env $GIT_WORK_TREE when work tree is set, like $GIT_DIR Nguyễn Thái Ngọc Duy
  2015-06-26 11:56   ` Jeff King
  2015-06-26 17:15   ` Junio C Hamano
@ 2015-06-26 17:30   ` Junio C Hamano
  2 siblings, 0 replies; 7+ messages in thread
From: Junio C Hamano @ 2015-06-26 17:30 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, snoksrud

Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:

> This is where the "fun" is. The legacy behavior is, if $GIT_WORK_TREE is
> not set but $GIT_DIR is, cwd is chosen as worktree's top. If you happen
> to stand at worktree's top when you do this, all is well.

It is not legacy, though.  It is how things were designed to be
used, and how things are supposed to work in the future.

Until we do the deprecation dance to force people to update existing
scripts to explicitly export GIT_WORK_TREE=$(pwd) or something like
that, that is.

And I wouldn't be opposed to such a transition plan; something along
the lines of what Peff outlined in that old thread.

http://thread.gmane.org/gmane.comp.version-control.git/219096/focus=219197

> Bottom line is, when $GIT_DIR is set, $GIT_WORK_TREE should be set too
> unless there's no work tree. But setting $GIT_WORK_TREE inside
> set_git_dir() may backfire. We don't know at that point if work tree is
> already configured by the caller. So set it when work tree is
> detected. It does not harm if $GIT_WORK_TREE is set while $GIT_DIR is
> not.

I am inclined to queue this on 'next' per "experimental" basis so
that other people with different workflows (especially those who
use existing scripts around Git plumbing heavily) can see if this
does not have any unintended fallouts.  I cannot convince myself
that it is generally a safe thing to do to muck with environment
partially, hoping nobody would care.

Thanks for looking into it.

>  environment.c      |  2 ++
>  t/t0002-gitfile.sh | 17 +++++++++++++++++
>  2 files changed, 19 insertions(+)
>
> diff --git a/environment.c b/environment.c
> index 61c685b..8f1b249 100644
> --- a/environment.c
> +++ b/environment.c
> @@ -231,6 +231,8 @@ void set_git_work_tree(const char *new_work_tree)
>  	}
>  	git_work_tree_initialized = 1;
>  	work_tree = xstrdup(real_path(new_work_tree));
> +	if (setenv(GIT_WORK_TREE_ENVIRONMENT, work_tree, 1))
> +		error("Could not set GIT_WORK_TREE to '%s'", work_tree);
>  }
>  
>  const char *get_git_work_tree(void)
> diff --git a/t/t0002-gitfile.sh b/t/t0002-gitfile.sh
> index 37e9396..9393322 100755
> --- a/t/t0002-gitfile.sh
> +++ b/t/t0002-gitfile.sh
> @@ -99,4 +99,21 @@ test_expect_success 'check rev-list' '
>  	test "$SHA" = "$(git rev-list HEAD)"
>  '
>  
> +test_expect_success 'setup_git_dir twice in subdir' '
> +	git init sgd &&
> +	(
> +		cd sgd &&
> +		git config alias.lsfi ls-files &&
> +		mv .git .realgit &&
> +		echo "gitdir: .realgit" >.git &&
> +		mkdir subdir &&
> +		cd subdir &&
> +		>foo &&
> +		git add foo &&
> +		git lsfi >actual &&
> +		echo foo >expected &&
> +		test_cmp expected actual
> +	)
> +'
> +
>  test_done

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

* Re: [PATCH] setup: set env $GIT_WORK_TREE when work tree is set, like $GIT_DIR
  2015-06-26 17:15   ` Junio C Hamano
@ 2015-06-26 19:02     ` Junio C Hamano
  0 siblings, 0 replies; 7+ messages in thread
From: Junio C Hamano @ 2015-06-26 19:02 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, snoksrud

Junio C Hamano <gitster@pobox.com> writes:

> But I am not sure if it is true for our children (e.g. hooks,
> filters etc. that is spawned by us).  With this change, they inherit
> GIT_WORK_TREE and no GIT_DIR, in such a case.  If they set GIT_DIR
> themselves for their own use, perhaps arranging to work in somewhere
> else they know by chdir'ing there, they did not have to set
> GIT_WORK_TREE=. before runing git in there, but now they do, because
> we start exporting GIT_WORK_TREE to interfere what they have been
> doing.
>
> "It does not harm" is probably false for our children, I would
> think.  The children can do new things to avoid the harm, though.

Actually, I think I am overly and unnecessarily worried in the
above.

We do not do anything special wrt existing GIT_ environment
variables the user exported into us when spawning hooks or filters;
if existing scripts wanted to visit a different repository and
interact with it while they do their work, they would have already
been seeing these variables in their environment inherited from us,
and they should have been protecting themselves from these settings
anyway.  So I do not think this patch breaks anything (other than
such scripts that are already broken).

So, sorry for a false alarm.  Queued.

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

* Re: [PATCH] setup: set env $GIT_WORK_TREE when work tree is set, like $GIT_DIR
  2015-06-26 11:56   ` Jeff King
@ 2015-06-27  5:57     ` Duy Nguyen
  0 siblings, 0 replies; 7+ messages in thread
From: Duy Nguyen @ 2015-06-27  5:57 UTC (permalink / raw)
  To: Jeff King, Junio C Hamano; +Cc: Git Mailing List, Bjørnar Snoksrud

On Fri, Jun 26, 2015 at 6:56 PM, Jeff King <peff@peff.net> wrote:
> On Fri, Jun 26, 2015 at 05:37:35PM +0700, Nguyễn Thái Ngọc Duy wrote:
>
>> This is where the "fun" is. The legacy behavior is, if $GIT_WORK_TREE is
>> not set but $GIT_DIR is, cwd is chosen as worktree's top. If you happen
>> to stand at worktree's top when you do this, all is well. If you are in
>> a subdir "foo/bar" (real worktree's top is "foo"), this behavior bites
>> you: your detected worktree is now "foo/bar", but the first run
>> correctly detected worktree as "foo". You get "internal error: work tree
>> has already been set" as a result.
>
> I think this makes sense. I feel like we've dealt with this before, but
> the two previous rounds I found were basically:
>
>   - we have GIT_IMPLICIT_WORK_TREE, but that is for the _opposite_ case.
>     I.e., when we do not have a work tree and must communicate so to
>     later code (including sub-processes).
>
>   - a discussion about switching the "work tree defaults to '.' when
>     $GIT_DIR is set" behavior yielded almost the identical patch:
>
>       http://article.gmane.org/gmane.comp.version-control.git/219196
>
>     but we were so wrapped up in the greater discussion we did not apply
>     that simple fix. :)

There's also the patch "[PATCH v2] setup.c: set workdir when gitdir is
not default" from Max Kirillov last year (gmane down, no link), so we
have one patch about this every year since 2013 :)

Junio if you are worried about unnecessary setenv, I think Max's
approach is safer as he solves a specific use case. If this problem
pops up again in another use case, we can deal with that again.

>> +     if (setenv(GIT_WORK_TREE_ENVIRONMENT, work_tree, 1))
>> +             error("Could not set GIT_WORK_TREE to '%s'", work_tree);
>
> Should this be die()? setenv() should basically never fail, but if it
> does, it could be confusing and/or dangerous to run without the variable
> set.

It's a straight copy from set_git_dir() but I guess the situation is a
bit different. If setenv fails in gitdir, no repo is found but if it
fails here we may select a wrong worktree and could wipe it out by
mistake. Will fix if Junio chooses this patch instead of Max's.
-- 
Duy

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

end of thread, other threads:[~2015-06-27  5:58 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-06-25 14:15 Linked workdirs break typo-correction Bjørnar Snoksrud
2015-06-26 10:37 ` [PATCH] setup: set env $GIT_WORK_TREE when work tree is set, like $GIT_DIR Nguyễn Thái Ngọc Duy
2015-06-26 11:56   ` Jeff King
2015-06-27  5:57     ` Duy Nguyen
2015-06-26 17:15   ` Junio C Hamano
2015-06-26 19:02     ` Junio C Hamano
2015-06-26 17:30   ` Junio C Hamano

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.