Hi,
I want to know how I expect setup code to work and whether it matches
my expectation. So I list every possible cases and try to sum up what
git does and what I expect. Hopefully all corner cases are covered
(and fixed later). It's not an easy read, but any comments are
appreciated. If all go well, this document should be turned into test
cases.
As a side note, Debian bug #575917 is case #17. I did not take into
account what setup_git_directory() does (which causes #575917)
though. Once the cleanup is done, setup_git_directory() should become
a one-liner around setup_git_directory_gently().
Assumptions:
1. When .git is a file and contains a relative path, I assume it is
relative to .git file's parent directory. read_gitfile_gently()
function will make the path absolute, but it depends on (maybe
wrong) cwd.
2. Relative path is preferred over absolute path.
3. core.worktree is overidden by GIT_WORK_TREE
There are a few factors that affect gitdir/worktree/cwd/prefix setup.
Those are:
- GIT_DIR (or --git-dir)
- GIT_WORK_TREE (or --work-tree)
- core.worktree
- .git is a file pointing to real .git directory
- Bare repo
So there are 2^5 = 32 cases in total. Let's look at them one by
one. What describes here usually matches current code, except for
"should" phrases/sentences. Note that I did not look at the code
carefully so I might misunderstand things.
The following questions must be answered for each case:
0. What setup function handles that case?
1. Is worktree available?
2. Where is new cwd? Is it at worktree root?
3. Prefix?
4. Is (relative) $GIT_DIR accessible from current cwd?
5. What if original cwd is outside worktree, or new cwd if it's not at
worktree root?
Table of Contents
=================
1 Cases
1.1 Case 0, 8
1.2 Case 1, 4, 5, 9, 12, 13
1.2.1 cwd outside worktree
1.3 Case 2, 10
1.4 Case 3, 6, 7, 11, 14, 15
1.4.1 cwd outside worktree
1.5 Case 16, 24
1.6 Case 17, 20, 21, 25, 28, 29
1.6.1 cwd outside worktree
1.7 Case 18, 26
1.8 Case 19, 22, 23, 27, 30, 31
1 Cases
~~~~~~~~
Case# Bare .git-file core.worktree GIT_DIR GIT_WORK_TREE
0 0 0 0 0 0
1 0 0 0 0 1
2 0 0 0 1 0
3 0 0 0 1 1
4 0 0 1 0 0
5 0 0 1 0 1
6 0 0 1 1 0
7 0 0 1 1 1
8 0 1 0 0 0
9 0 1 0 0 1
10 0 1 0 1 0
11 0 1 0 1 1
12 0 1 1 0 0
13 0 1 1 0 1
14 0 1 1 1 0
15 0 1 1 1 1
16 1 0 0 0 0
17 1 0 0 0 1
18 1 0 0 1 0
19 1 0 0 1 1
20 1 0 1 0 0
21 1 0 1 0 1
22 1 0 1 1 0
23 1 0 1 1 1
24 1 1 0 0 0
25 1 1 0 0 1
26 1 1 0 1 0
27 1 1 0 1 1
28 1 1 1 0 0
29 1 1 1 0 1
30 1 1 1 1 0
31 1 1 1 1 1
1.1 Case 0, 8
==============
The most used case, nothing special is set.
0. setup_discovered_git_dir()
1. work tree is .git dir's parent directory
2. cwd is at worktree root, i.e. .git dir's parent dir
3. prefix is calculated from original cwd
4. git_dir is set to ".git" (#0) or according to .git file, made
absolute (#8)
5. N/A
TODO: turn git_dir to relative in #8
1.2 Case 1, 4, 5, 9, 12, 13
============================
0. setup_discovered_git_dir()
1. work tree is set according to GIT_WORK_TREE (#1, #5, #9, #13) or
core.worktree (#4, #12)
2. cwd is at worktree root, i.e. .git dir's parent dir
3. prefix is calculated from original cwd
4. git_dir is set to ".git" (#1, #4, #5) or according to .git file,
made absolute (#9, #12, #13)
TODO: turn git_dir to relative in #9, #12, #13
1.2.1 cwd outside worktree
---------------------------
FIXME
1.3 Case 2, 10
===============
0. setup_explicit_git_dir()
1. worktree is set at cwd for legacy reason
2. cwd is unchanged
3. prefix is NULL
4. git_dir is set according to GIT_DIR (#2) or according to .git file,
made absolute (#10)
TODO: turn git_dir to relative path
Note on #10: setup_git_env() is used to read .git file
1.4 Case 3, 6, 7, 11, 14, 15
=============================
Another normal case where worktree is at an unsual case.
0. setup_explicit_git_dir()
1. worktree is set according to GIT_WORK_TREE (#3, #7, #11, #15) or
core.worktree (#6, #14)
2. cwd is moved to worktree root dir if original cwd is inside
worktree
3. prefix is calculated if original cwd is inside worktree
4. git_dir is set to GIT_DIR (#3, #6, #7) or according to .git file,
made absolute (#11, #14, #15)
TODO: if GIT_DIR is relative, it is assumed relative to original cwd,
so it breaks because cwd now is changed. Right now this does not
happen because git_dir is turned absolute. Although it's better to be
relative.
TODO: turn git_dir to relative in #11, #14, #15
FIXME on #11, #14, #15: setup_git_env() is used to read .git file. cwd
by that time is worktree root, not .git's parent dir anymore.
1.4.1 cwd outside worktree
---------------------------
cwd is left unchanged, prefix is NULL, which is sensible for full tree
operations. is_inside_work_tree() returns 0, operations that require
worktree should check and error out.
TODO: File path output however may not be what user expected because
it will be relative to worktree root, not original cwd.
1.5 Case 16, 24
================
The most used case, nothing special is set.
0. setup_bare_git_dir()
1. no worktree
2. cwd is unchanged (actually it is moved back to original cwd)
3. prefix is NULL
4. git_dir is set in form "../../../" (or ".") (#16) or according to
.git file, made absolute (#24)
5. N/A
TODO on #24: turn git_dir to relative
FIXME on #24: cwd is not at .git's parent dir, so relative git_dir
fails assumption #1.
1.6 Case 17, 20, 21, 25, 28, 29
================================
0. setup_bare_git_dir()
1. work tree is set according to GIT_WORK_TREE (#17, #21, #25, #29) or
core.worktree (#20, #28)
2. cwd is unchanged (actually it is moved back to original
cwd).
3. prefix is calculated from original cwd
4. git_dir is set in form "../../../" (or ".") (#17, #20, #21) or according to
.git file, made absolute (#25 , #28, #29).
TODO: if core.bare = true, die()
FIXME: cwd should be moved to worktree's root instead. Similarly,
git_dir should be recalculated relative to worktree's root
TODO: turn git_dir to relative in #25, #28, #29 (or all if the above
FIXME turns everything to absolute)
Note on #25, #28, #9: pay attention to assumption #1 when the above
FIXME is done.
1.6.1 cwd outside worktree
---------------------------
FIXME
1.7 Case 18, 26
================
This is bare repo because of core.bare = true.
0. setup_explicit_git_dir()
1. no worktree
2. cwd is unchanged
3. prefix is NULL
4. git_dir is set according to GIT_DIR (#18) or according to .git file,
made absolute (#126)
FIXME: in current code, I believe this is actually #2/#10 (worktree is
set). Should that be fixed??? "Legacy reason" hanging around so I'm
not sure.
TODO: turn git_dir to relative path
Note on #26: setup_git_env() is used to read .git file
1.8 Case 19, 22, 23, 27, 30, 31
================================
This is bare repo because of core.bare = true.
0. setup_explicit_git_dir()
1. worktree is set according to GIT_WORK_TREE (#19, #23, #27, #31) or
core.worktree (#22, #30)
2. cwd is moved to worktree root dir if original cwd is inside
worktree
3. prefix is calculated if original cwd is inside worktree
4. git_dir is set to GIT_DIR (#19, #22, #23) or according to .git file,
made absolute (#27, #30, #31)
5. N/A
FIXME: just die() in check_repository_format_gently().
--
Duy
From: Jonathan Nieder <hidden> Date: 2016-06-15 22:49:49
Nguyen Thai Ngoc Duy wrote:
1. When .git is a file and contains a relative path, I assume it is
relative to .git file's parent directory. read_gitfile_gently()
function will make the path absolute, but it depends on (maybe
wrong) cwd.
Yep.
2. Relative path is preferred over absolute path.
I'm tempted to say: let's use absolute paths where it's more
convenient. They can be changed to relative paths as an afterthought
after the bugs are gone.
Relative paths have two big advantages over absolute paths,
which are avoiding path traversal overhead and allowing paths
longer than PATH_MAX. Supporting the latter consistently
would presumably require using openat() and co, though.
3. core.worktree is overidden by GIT_WORK_TREE
Yeah.
So there are 2^5 = 32 cases in total. Let's look at them one by
one.
Thanks! To summarize (and make sure I understand correctly):
anything not following the below rules is a bug, yes?
A. Weird cases.
- using a .git file is just like setting GIT_DIR;
- setting core.worktree is just like setting GIT_WORK_TREE.
B. Repository search.
- if GIT_DIR was set explicitly, GIT_WORK_TREE defaults to
"." (for legacy reasons).
- otherwise, if original cwd was under repository, it will not
prompt a search for work tree, even if the repo happens
to be named ".git" or core.bare is false. That is, in
this case, GIT_WORK_TREE defaults to unset.
- otherwise, if original cwd was under a directory containing
repository as ".git", GIT_WORK_TREE defaults to that
directory (i.e., parent to .git dir).
- otherwise, there is no repository. GIT_DIR is unset,
GIT_WORK_TREE defaults to unset.
C. Working directory and prefix
- if GIT_WORK_TREE is still unset after repository search,
stay in the original cwd, prefix = NULL.
- if original cwd is inside worktree, chdir to toplevel,
prefix = path to original cwd.
- otherwise, stay in the original cwd, prefix = NULL.
D. User-supplied relative paths.
- path in .git file is relative to containing directory
- path in GIT_DIR is relative to original cwd
- paths in GIT_WORK_TREE and core.worktree are relative to
$GIT_DIR
- paths passed to git commands are generally relative to
original cwd
E. Internally used relative paths.
- all paths are relative to current cwd.
On Thu, Oct 21, 2010 at 2:07 AM, Jonathan Nieder [off-list ref] wrote:
quoted
So there are 2^5 = 32 cases in total. Let's look at them one by
one.
Thanks! To summarize (and make sure I understand correctly):
anything not following the below rules is a bug, yes?
Let's see.
A. Weird cases.
- using a .git file is just like setting GIT_DIR;
Yes, except that GIT_DIR can be detected early when cwd has not been
moved. When .git is found a file, cwd could have been changed.
B. Repository search.
- if GIT_DIR was set explicitly, GIT_WORK_TREE defaults to
"." (for legacy reasons).
Correct.
- otherwise, if original cwd was under repository, it will not
prompt a search for work tree, even if the repo happens
to be named ".git" or core.bare is false. That is, in
this case, GIT_WORK_TREE defaults to unset.
What do you mean by "under repository"? If the repo is /tmp/git/.git,
then cwd is at /tmp/git/.git?
- otherwise, if original cwd was under a directory containing
repository as ".git", GIT_WORK_TREE defaults to that
directory (i.e., parent to .git dir).
Yes.
- otherwise, there is no repository. GIT_DIR is unset,
GIT_WORK_TREE defaults to unset.
- Otherwise, move up one dir and repeat?
C. Working directory and prefix
- if GIT_WORK_TREE is still unset after repository search,
stay in the original cwd, prefix = NULL.
if GIT_WORK_TREE and core.worktree are still unset, we get a bare repo
here (or force it to be a bare repo), so yes, cwd should stay in
original cwd and prefix = NULL.
- if original cwd is inside worktree, chdir to toplevel,
prefix = path to original cwd.
Yes.
- otherwise, stay in the original cwd, prefix = NULL.
I'm not really happy with this, which is why I wrote the
--cwd-to-worktree and --worktree-to-cwd patch. But this should be
enough for full-tree operations to work, so yes.
D. User-supplied relative paths.
- path in .git file is relative to containing directory
- path in GIT_DIR is relative to original cwd
- paths in GIT_WORK_TREE and core.worktree are relative to
$GIT_DIR
I think $GIT_WORK_TREE is relative to original cwd. Yes, core.worktree
should be relative to $GIT_DIR.
- paths passed to git commands are generally relative to
original cwd
And filename output should also be relative to original cwd (except a
few special, like diff output).
E. Internally used relative paths.
- all paths are relative to current cwd.
From: Jonathan Nieder <hidden> Date: 2016-06-15 22:49:50
Nguyen Thai Ngoc Duy wrote:
On Thu, Oct 21, 2010 at 2:07 AM, Jonathan Nieder [off-list ref] wrote:
quoted
- otherwise, if original cwd was under repository,
[...]
quoted
GIT_WORK_TREE defaults to unset.
What do you mean by "under repository"? If the repo is /tmp/git/.git,
then cwd is at /tmp/git/.git?
I meant if the original cwd lies within the repository.
Example:
repo /tmp/git/.git
starting cwd /tmp/git/.git/objects/pack
quoted
D. User-supplied relative paths.
- path in .git file is relative to containing directory
- path in GIT_DIR is relative to original cwd
- paths in GIT_WORK_TREE and core.worktree are relative to
$GIT_DIR
I think $GIT_WORK_TREE is relative to original cwd.
git.txt is confusing, then. Actually it has some other insights:
--work-tree=<path>
Set the path to the working tree. The value
will not be used in combination with
repositories found automatically in a .git
directory (i.e. $GIT_DIR is not set).
So GIT_WORK_TREE should be discarded or warned about when GIT_DIR is
not set. (?)
This can also be controlled by setting the
GIT_WORK_TREE environment variable and the
core.worktree configuration variable. It can be
an absolute path or relative path to the
directory specified by --git-dir or GIT_DIR.
This is where I got the impression about relative paths.
Note: If --git-dir or GIT_DIR are specified but
none of --work-tree, GIT_WORK_TREE and
core.worktree is specified, the current working
directory is regarded as the top directory of
your working tree.
Nice to see this case is documented.
Yes, core.worktree
should be relative to $GIT_DIR.
Speaking of which, it is not clear to me that core.worktree should
fall under the forbidden case discussed above. If it does, what is
the point of making it configurable?
On Thu, Oct 21, 2010 at 10:30 AM, Jonathan Nieder [off-list ref] wrote:
Nguyen Thai Ngoc Duy wrote:
quoted
On Thu, Oct 21, 2010 at 2:07 AM, Jonathan Nieder [off-list ref] wrote:
quoted
quoted
- otherwise, if original cwd was under repository,
[...]
quoted
quoted
GIT_WORK_TREE defaults to unset.
What do you mean by "under repository"? If the repo is /tmp/git/.git,
then cwd is at /tmp/git/.git?
I meant if the original cwd lies within the repository.
Example:
repo /tmp/git/.git
starting cwd /tmp/git/.git/objects/pack
OK it's considered a bare repo, so no worktree.
quoted
quoted
D. User-supplied relative paths.
- path in .git file is relative to containing directory
- path in GIT_DIR is relative to original cwd
- paths in GIT_WORK_TREE and core.worktree are relative to
$GIT_DIR
I think $GIT_WORK_TREE is relative to original cwd.
git.txt is confusing, then. Actually it has some other insights:
--work-tree=<path>
Set the path to the working tree. The value
will not be used in combination with
repositories found automatically in a .git
directory (i.e. $GIT_DIR is not set).
So GIT_WORK_TREE should be discarded or warned about when GIT_DIR is
not set. (?)
Yeah. Junio reminded me in another mail. It should warn (at least, me).
This can also be controlled by setting the
GIT_WORK_TREE environment variable and the
core.worktree configuration variable. It can be
an absolute path or relative path to the
directory specified by --git-dir or GIT_DIR.
This is where I got the impression about relative paths.
Hmm.. OK then. So worktree can be relative to gitdir, which in turn
can be relative to original cwd. Fun.
Note: If --git-dir or GIT_DIR are specified but
none of --work-tree, GIT_WORK_TREE and
core.worktree is specified, the current working
directory is regarded as the top directory of
your working tree.
Nice to see this case is documented.
quoted
Yes, core.worktree
should be relative to $GIT_DIR.
Speaking of which, it is not clear to me that core.worktree should
fall under the forbidden case discussed above. If it does, what is
the point of making it configurable?
I was not the one who introduced core.worktree, so I can't really
tell. Maybe less keystrokes? You know, "GIT_DIR=foo.git git foo" is
shorter than "GIT_DIR=foo.git GIT_WORK_TREE=/path/to/somewhere git
foo". I tend to think that core.worktree is always effective
regardless $GIT_DIR env setting. But I was wrong.
Also the "relative to $GIT_DIR" may be confusing. If $GIT_DIR points
to a file that points to a true repo, then to which one it is
relative?
--
Duy
From: Jonathan Nieder <hidden> Date: 2016-06-15 22:49:50
Nguyen Thai Ngoc Duy wrote:
On Thu, Oct 21, 2010 at 10:30 AM, Jonathan Nieder [off-list ref] wrote:
quoted
Speaking of which, it is not clear to me that core.worktree should
fall under the forbidden case discussed above. If it does, what is
the point of making it configurable?
I was not the one who introduced core.worktree, so I can't really
tell. Maybe less keystrokes?
Not necessarily: if config tells it's a non-bare repo, we could
assume, the worktree is where core.worktree tells (which per
default is "../" - relative to gitdir).
But currently it seems to be implemented as you said: running
git-status from within gitdir tells:
"fatal: This operation must be run in a work tree"
That's not really bad, but sometimes a bit inconvenient.
Also the "relative to $GIT_DIR" may be confusing. If $GIT_DIR points
to a file that points to a true repo, then to which one it is
relative?
Despite the fact that I doubt the usefulness of an .git file at
all, it IMHO should be interpreted as an kind of userland symlink.
cu
--
----------------------------------------------------------------------
Enrico Weigelt, metux IT service -- http://www.metux.de/
phone: +49 36207 519931 email: weigelt@metux.de
mobile: +49 151 27565287 icq: 210169427 skype: nekrad666
----------------------------------------------------------------------
Embedded-Linux / Portierung / Opensource-QM / Verteilte Systeme
----------------------------------------------------------------------
On Fri, Oct 22, 2010 at 1:51 AM, Enrico Weigelt [off-list ref] wrote:
quoted
Also the "relative to $GIT_DIR" may be confusing. If $GIT_DIR points
to a file that points to a true repo, then to which one it is
relative?
Despite the fact that I doubt the usefulness of an .git file at
all, it IMHO should be interpreted as an kind of userland symlink.
I do find .git file useful. I put my worktree in a server where it can
be wiped out completely. So I don't want to put my .git there. .git
file can be used this case so I don't have to set
GIT_DIR/GIT_WORK_TREE every time and my .git is safe elsewhere.
--
Duy
On Fri, Oct 22, 2010 at 1:51 AM, Enrico Weigelt [off-list ref] wrote:
quoted
quoted
Also the "relative to $GIT_DIR" may be confusing. If $GIT_DIR points
to a file that points to a true repo, then to which one it is
relative?
Despite the fact that I doubt the usefulness of an .git file at
all, it IMHO should be interpreted as an kind of userland symlink.
I do find .git file useful. I put my worktree in a server where it can
be wiped out completely. So I don't want to put my .git there. .git
file can be used this case so I don't have to set
GIT_DIR/GIT_WORK_TREE every time and my .git is safe elsewhere.
Why not using a symlink here ?
cu
--
----------------------------------------------------------------------
Enrico Weigelt, metux IT service -- http://www.metux.de/
phone: +49 36207 519931 email: weigelt@metux.de
mobile: +49 151 27565287 icq: 210169427 skype: nekrad666
----------------------------------------------------------------------
Embedded-Linux / Portierung / Opensource-QM / Verteilte Systeme
----------------------------------------------------------------------
From: Jonathan Nieder <hidden> Date: 2016-06-15 22:49:50
Enrico Weigelt wrote:
Why not using a symlink here ?
Suppose I wonder just that. grepping through setup.c for ".git file",
I find the function read_gitfile_gently() --- why was that added?
$ git log -Sread_gitfile_gently setup.c
commit b44ebb19e3234c5dffe9869ceac5408bb44c2e20
Author: Lars Hjemli [off-list ref]
Date: Wed Feb 20 23:13:13 2008 +0100
Add platform-independent .git "symlink"
This patch allows .git to be a regular textfile containing the path of
the real git directory (prefixed with "gitdir: "), which can be useful on
platforms lacking support for real symlinks.
Signed-off-by: Lars Hjemli [off-list ref]
Signed-off-by: Junio C Hamano [off-list ref]
Hope that helps.
On Fri, Oct 22, 2010 at 12:00 PM, Enrico Weigelt [off-list ref] wrote:
* Nguyen Thai Ngoc Duy [off-list ref] wrote:
quoted
On Fri, Oct 22, 2010 at 1:51 AM, Enrico Weigelt [off-list ref] wrote:
quoted
quoted
Also the "relative to $GIT_DIR" may be confusing. If $GIT_DIR points
to a file that points to a true repo, then to which one it is
relative?
Despite the fact that I doubt the usefulness of an .git file at
all, it IMHO should be interpreted as an kind of userland symlink.
I do find .git file useful. I put my worktree in a server where it can
be wiped out completely. So I don't want to put my .git there. .git
file can be used this case so I don't have to set
GIT_DIR/GIT_WORK_TREE every time and my .git is safe elsewhere.
Why not using a symlink here ?
Paranoia. A fault in Makefile's clean rule may make deletion follow
symlinks. Also symlinks are not available everywhere.
--
Duy
On Thu, Oct 21, 2010 at 11:01 PM, Jonathan Nieder [off-list ref] wrote:
Nguyen Thai Ngoc Duy wrote:
quoted
On Thu, Oct 21, 2010 at 10:30 AM, Jonathan Nieder [off-list ref] wrote:
quoted
quoted
Speaking of which, it is not clear to me that core.worktree should
fall under the forbidden case discussed above. If it does, what is
the point of making it configurable?
I was not the one who introduced core.worktree, so I can't really
tell. Maybe less keystrokes?
Yeah, it seems you're totally right. :(
Actually I have my part in this mess too: f5e025a (Documentation:
always respect core.worktree if set - 2009-12-29). I probably just
updated the document according to the code. It matches what Enrico
expects (i.e after .git discovery, worktree defaults is "../", if
core.worktree exists, follow it instead). Going back to the first
commit that introduced separate worktree, 892c41b (introduce
GIT_WORK_TREE to specify the work tree - 2007-06-06), core.worktree is
no different than --work-tree.
So what should it behave? Either revert f5e025a and update the code to
disregard/warn core.worktree if GIT_DIR/--work-tree is unset, or keep
the current behavior, i.e. core.worktree is different from
--work-tree/GIT_WORK_TREE.
The cleanup commit, e90fdc3 (Clean up work-tree handling -
2007-08-01), also gives something interesting regarding bare repo:
"--work-tree=bla overrides GIT_WORK_TREE, _which overrides core.bare =
true_, which overrides core.worktree, which overrides GIT_DIR/.. when
GIT_DIR ends in /.git, which overrides the directory in which .git/
was found.". I don't like this overriding chain, which is why I
proposed to die() if core.bare is set and worktree is explicitly
specified.
--
Duy