2023-04-11 07:41:50 +00:00
|
|
|
#include "git-compat-util.h"
|
2023-03-21 06:25:58 +00:00
|
|
|
#include "abspath.h"
|
2023-03-21 06:26:03 +00:00
|
|
|
#include "environment.h"
|
2023-03-21 06:25:54 +00:00
|
|
|
#include "gettext.h"
|
2023-04-11 07:41:49 +00:00
|
|
|
#include "object-name.h"
|
2017-06-22 18:43:33 +00:00
|
|
|
#include "repository.h"
|
2017-06-14 18:07:36 +00:00
|
|
|
#include "config.h"
|
Clean up work-tree handling
The old version of work-tree support was an unholy mess, barely readable,
and not to the point.
For example, why do you have to provide a worktree, when it is not used?
As in "git status". Now it works.
Another riddle was: if you can have work trees inside the git dir, why
are some programs complaining that they need a work tree?
IOW it is allowed to call
$ git --git-dir=../ --work-tree=. bla
when you really want to. In this case, you are both in the git directory
and in the working tree. So, programs have to actually test for the right
thing, namely if they are inside a working tree, and not if they are
inside a git directory.
Also, GIT_DIR=../.git should behave the same as if no GIT_DIR was
specified, unless there is a repository in the current working directory.
It does now.
The logic to determine if a repository is bare, or has a work tree
(tertium non datur), is this:
--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.
In related news, a long standing bug was fixed: when in .git/bla/x.git/,
which is a bare repository, git formerly assumed ../.. to be the
appropriate git dir. This problem was reported by Shawn Pearce to have
caused much pain, where a colleague mistakenly ran "git init" in "/" a
long time ago, and bare repositories just would not work.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2007-08-01 00:30:14 +00:00
|
|
|
#include "dir.h"
|
2023-03-21 06:26:05 +00:00
|
|
|
#include "setup.h"
|
2012-10-28 16:16:24 +00:00
|
|
|
#include "string-list.h"
|
set_work_tree: use chdir_notify
When we change to the top of the working tree, we manually
re-adjust $GIT_DIR and call set_git_dir() again, in order to
update any relative git-dir we'd compute earlier.
Instead of the work-tree code having to know to call the
git-dir code, let's use the new chdir_notify interface.
There are two spots that need updating, with a few
subtleties in each:
1. the set_git_dir() code needs to chdir_notify_register()
so it can be told when to update its path.
Technically we could push this down into repo_set_gitdir(),
so that even repository structs besides the_repository
could benefit from this. But that opens up a lot of
complications:
- we'd still need to touch set_git_dir(), because it
does some other setup (like setting $GIT_DIR in the
environment)
- submodules using other repository structs get
cleaned up, which means we'd need to remove them
from the chdir_notify list
- it's unlikely to fix any bugs, since we shouldn't
generally chdir() in the middle of working on a
submodule
2. setup_work_tree now needs to call chdir_notify(), and
can lose its manual set_git_dir() call.
Note that at first glance it looks like this undoes the
absolute-to-relative optimization added by 044bbbcb63
(Make git_dir a path relative to work_tree in
setup_work_tree(), 2008-06-19). But for the most part
that optimization was just _undoing_ the
relative-to-absolute conversion which the function was
doing earlier (and which is now gone).
It is true that if you already have an absolute git_dir
that the setup_work_tree() function will no longer make
it relative as a side effect. But:
- we generally do have relative git-dir's due to the
way the discovery code works
- if we really care about making git-dir's relative
when possible, then we should be relativizing them
earlier (e.g., when we see an absolute $GIT_DIR we
could turn it relative, whether we are going to
chdir into a worktree or not). That would cover all
cases, including ones that 044bbbcb63 did not.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-03-30 18:35:08 +00:00
|
|
|
#include "chdir-notify.h"
|
2019-06-25 13:40:38 +00:00
|
|
|
#include "promisor-remote.h"
|
2022-03-02 11:23:04 +00:00
|
|
|
#include "quote.h"
|
2023-04-11 03:00:38 +00:00
|
|
|
#include "trace2.h"
|
2023-04-11 07:42:02 +00:00
|
|
|
#include "wrapper.h"
|
Clean up work-tree handling
The old version of work-tree support was an unholy mess, barely readable,
and not to the point.
For example, why do you have to provide a worktree, when it is not used?
As in "git status". Now it works.
Another riddle was: if you can have work trees inside the git dir, why
are some programs complaining that they need a work tree?
IOW it is allowed to call
$ git --git-dir=../ --work-tree=. bla
when you really want to. In this case, you are both in the git directory
and in the working tree. So, programs have to actually test for the right
thing, namely if they are inside a working tree, and not if they are
inside a git directory.
Also, GIT_DIR=../.git should behave the same as if no GIT_DIR was
specified, unless there is a repository in the current working directory.
It does now.
The logic to determine if a repository is bare, or has a work tree
(tertium non datur), is this:
--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.
In related news, a long standing bug was fixed: when in .git/bla/x.git/,
which is a bare repository, git formerly assumed ../.. to be the
appropriate git dir. This problem was reported by Shawn Pearce to have
caused much pain, where a colleague mistakenly ran "git init" in "/" a
long time ago, and bare repositories just would not work.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2007-08-01 00:30:14 +00:00
|
|
|
|
|
|
|
static int inside_git_dir = -1;
|
|
|
|
static int inside_work_tree = -1;
|
setup_git_directory: delay core.bare/core.worktree errors
If both core.bare and core.worktree are set, we complain
about the bogus config and die. Dying is good, because it
avoids commands running and doing damage in a potentially
incorrect setup. But dying _there_ is bad, because it means
that commands which do not even care about the work tree
cannot run. This can make repairing the situation harder:
[setup]
$ git config core.bare true
$ git config core.worktree /some/path
[OK, expected.]
$ git status
fatal: core.bare and core.worktree do not make sense
[Hrm...]
$ git config --unset core.worktree
fatal: core.bare and core.worktree do not make sense
[Nope...]
$ git config --edit
fatal: core.bare and core.worktree do not make sense
[Gaaah.]
$ git help config
fatal: core.bare and core.worktree do not make sense
Instead, let's issue a warning about the bogus config when
we notice it (i.e., for all commands), but only die when the
command tries to use the work tree (by calling setup_work_tree).
So we now get:
$ git status
warning: core.bare and core.worktree do not make sense
fatal: unable to set up work tree using invalid config
$ git config --unset core.worktree
warning: core.bare and core.worktree do not make sense
We have to update t1510 to accomodate this; it uses
symbolic-ref to check whether the configuration works or
not, but of course that command does not use the working
tree. Instead, we switch it to use `git status`, as it
requires a work-tree, does not need any special setup, and
is read-only (so a failure will not adversely affect further
tests).
In addition, we add a new test that checks the desired
behavior (i.e., that running "git config" with the bogus
config does in fact work).
Reported-by: SZEDER Gábor <szeder@ira.uka.de>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-05-29 06:49:10 +00:00
|
|
|
static int work_tree_config_is_bogus;
|
setup.c: create `safe.bareRepository`
There is a known social engineering attack that takes advantage of the
fact that a working tree can include an entire bare repository,
including a config file. A user could run a Git command inside the bare
repository thinking that the config file of the 'outer' repository would
be used, but in reality, the bare repository's config file (which is
attacker-controlled) is used, which may result in arbitrary code
execution. See [1] for a fuller description and deeper discussion.
A simple mitigation is to forbid bare repositories unless specified via
`--git-dir` or `GIT_DIR`. In environments that don't use bare
repositories, this would be minimally disruptive.
Create a config variable, `safe.bareRepository`, that tells Git whether
or not to die() when working with a bare repository. This config is an
enum of:
- "all": allow all bare repositories (this is the default)
- "explicit": only allow bare repositories specified via --git-dir
or GIT_DIR.
If we want to protect users from such attacks by default, neither value
will suffice - "all" provides no protection, but "explicit" is
impractical for bare repository users. A more usable default would be to
allow only non-embedded bare repositories ([2] contains one such
proposal), but detecting if a repository is embedded is potentially
non-trivial, so this work is not implemented in this series.
[1]: https://lore.kernel.org/git/kl6lsfqpygsj.fsf@chooglen-macbookpro.roam.corp.google.com
[2]: https://lore.kernel.org/git/5b969c5e-e802-c447-ad25-6acc0b784582@github.com
Signed-off-by: Glen Choo <chooglen@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-07-14 21:28:01 +00:00
|
|
|
enum allowed_bare_repo {
|
|
|
|
ALLOWED_BARE_REPO_EXPLICIT = 0,
|
|
|
|
ALLOWED_BARE_REPO_ALL,
|
|
|
|
};
|
2005-08-17 01:06:34 +00:00
|
|
|
|
setup: make startup_info available everywhere
Commit a60645f (setup: remember whether repository was
found, 2010-08-05) introduced the startup_info structure,
which records some parts of the setup_git_directory()
process (notably, whether we actually found a repository or
not).
One of the uses of this data is for functions to behave
appropriately based on whether we are in a repo. But the
startup_info struct is just a pointer to storage provided by
the main program, and the only program that sets it up is
the git.c wrapper. Thus builtins have access to
startup_info, but externally linked programs do not.
Worse, library code which is accessible from both has to be
careful about accessing startup_info. This can be used to
trigger a die("BUG") via get_sha1():
$ git fast-import <<-\EOF
tag foo
from HEAD:./whatever
EOF
fatal: BUG: startup_info struct is not initialized.
Obviously that's fairly nonsensical input to feed to
fast-import, but we should never hit a die("BUG"). And there
may be other ways to trigger it if other non-builtins
resolve sha1s.
So let's point the storage for startup_info to a static
variable in setup.c, making it available to all users of the
library code. We _could_ turn startup_info into a regular
extern struct, but doing so would mean tweaking all of the
existing use sites. So let's leave the pointer indirection
in place. We can, however, drop any checks for NULL, as
they will always be false (and likewise, we can drop the
test covering this case, which was a rather artificial
situation using one of the test-* programs).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-03-05 22:10:27 +00:00
|
|
|
static struct startup_info the_startup_info;
|
|
|
|
struct startup_info *startup_info = &the_startup_info;
|
2021-12-09 05:08:26 +00:00
|
|
|
const char *tmp_original_cwd;
|
setup: make startup_info available everywhere
Commit a60645f (setup: remember whether repository was
found, 2010-08-05) introduced the startup_info structure,
which records some parts of the setup_git_directory()
process (notably, whether we actually found a repository or
not).
One of the uses of this data is for functions to behave
appropriately based on whether we are in a repo. But the
startup_info struct is just a pointer to storage provided by
the main program, and the only program that sets it up is
the git.c wrapper. Thus builtins have access to
startup_info, but externally linked programs do not.
Worse, library code which is accessible from both has to be
careful about accessing startup_info. This can be used to
trigger a die("BUG") via get_sha1():
$ git fast-import <<-\EOF
tag foo
from HEAD:./whatever
EOF
fatal: BUG: startup_info struct is not initialized.
Obviously that's fairly nonsensical input to feed to
fast-import, but we should never hit a die("BUG"). And there
may be other ways to trigger it if other non-builtins
resolve sha1s.
So let's point the storage for startup_info to a static
variable in setup.c, making it available to all users of the
library code. We _could_ turn startup_info into a regular
extern struct, but doing so would mean tweaking all of the
existing use sites. So let's leave the pointer indirection
in place. We can, however, drop any checks for NULL, as
they will always be false (and likewise, we can drop the
test covering this case, which was a rather artificial
situation using one of the test-* programs).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-03-05 22:10:27 +00:00
|
|
|
|
2014-02-04 14:25:19 +00:00
|
|
|
/*
|
|
|
|
* The input parameter must contain an absolute path, and it must already be
|
|
|
|
* normalized.
|
|
|
|
*
|
|
|
|
* Find the part of an absolute path that lies inside the work tree by
|
|
|
|
* dereferencing symlinks outside the work tree, for example:
|
|
|
|
* /dir1/repo/dir2/file (work tree is /dir1/repo) -> dir2/file
|
|
|
|
* /dir/file (work tree is /) -> dir/file
|
|
|
|
* /dir/symlink1/symlink2 (symlink1 points to work tree) -> symlink2
|
|
|
|
* /dir/repolink/file (repolink points to /dir/repo) -> file
|
|
|
|
* /dir/repo (exactly equal to work tree) -> (empty string)
|
|
|
|
*/
|
|
|
|
static int abspath_part_inside_repo(char *path)
|
|
|
|
{
|
|
|
|
size_t len;
|
|
|
|
size_t wtlen;
|
|
|
|
char *path0;
|
|
|
|
int off;
|
|
|
|
const char *work_tree = get_git_work_tree();
|
2020-03-10 13:11:22 +00:00
|
|
|
struct strbuf realpath = STRBUF_INIT;
|
2014-02-04 14:25:19 +00:00
|
|
|
|
|
|
|
if (!work_tree)
|
|
|
|
return -1;
|
|
|
|
wtlen = strlen(work_tree);
|
|
|
|
len = strlen(path);
|
2014-04-24 13:06:09 +00:00
|
|
|
off = offset_1st_component(path);
|
2014-02-04 14:25:19 +00:00
|
|
|
|
|
|
|
/* check if work tree is already the prefix */
|
2019-01-18 13:24:54 +00:00
|
|
|
if (wtlen <= len && !fspathncmp(path, work_tree, wtlen)) {
|
2014-02-04 14:25:19 +00:00
|
|
|
if (path[wtlen] == '/') {
|
|
|
|
memmove(path, path + wtlen + 1, len - wtlen);
|
|
|
|
return 0;
|
|
|
|
} else if (path[wtlen - 1] == '/' || path[wtlen] == '\0') {
|
|
|
|
/* work tree is the root, or the whole path */
|
|
|
|
memmove(path, path + wtlen, len - wtlen + 1);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/* work tree might match beginning of a symlink to work tree */
|
|
|
|
off = wtlen;
|
|
|
|
}
|
|
|
|
path0 = path;
|
2014-04-24 13:06:09 +00:00
|
|
|
path += off;
|
2014-02-04 14:25:19 +00:00
|
|
|
|
|
|
|
/* check each '/'-terminated level */
|
|
|
|
while (*path) {
|
|
|
|
path++;
|
|
|
|
if (*path == '/') {
|
|
|
|
*path = '\0';
|
2020-03-10 13:11:22 +00:00
|
|
|
strbuf_realpath(&realpath, path0, 1);
|
|
|
|
if (fspathcmp(realpath.buf, work_tree) == 0) {
|
2014-02-04 14:25:19 +00:00
|
|
|
memmove(path0, path + 1, len - (path - path0));
|
2020-03-10 13:11:22 +00:00
|
|
|
strbuf_release(&realpath);
|
2014-02-04 14:25:19 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
*path = '/';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check whole path */
|
2020-03-10 13:11:22 +00:00
|
|
|
strbuf_realpath(&realpath, path0, 1);
|
|
|
|
if (fspathcmp(realpath.buf, work_tree) == 0) {
|
2014-02-04 14:25:19 +00:00
|
|
|
*path0 = '\0';
|
2020-03-10 13:11:22 +00:00
|
|
|
strbuf_release(&realpath);
|
2014-02-04 14:25:19 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-03-10 13:11:22 +00:00
|
|
|
strbuf_release(&realpath);
|
2014-02-04 14:25:19 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-07-14 08:36:03 +00:00
|
|
|
/*
|
|
|
|
* Normalize "path", prepending the "prefix" for relative paths. If
|
|
|
|
* remaining_prefix is not NULL, return the actual prefix still
|
|
|
|
* remains in the path. For example, prefix = sub1/sub2/ and path is
|
|
|
|
*
|
|
|
|
* foo -> sub1/sub2/foo (full prefix)
|
|
|
|
* ../foo -> sub1/foo (remaining prefix is sub1/)
|
|
|
|
* ../../bar -> bar (no remaining prefix)
|
|
|
|
* ../../sub1/sub2/foo -> sub1/sub2/foo (but no remaining prefix)
|
|
|
|
* `pwd`/../bar -> sub1/bar (no remaining prefix)
|
|
|
|
*/
|
|
|
|
char *prefix_path_gently(const char *prefix, int len,
|
|
|
|
int *remaining_prefix, const char *path)
|
setup: sanitize absolute and funny paths in get_pathspec()
The prefix_path() function called from get_pathspec() is
responsible for translating list of user-supplied pathspecs to
list of pathspecs that is relative to the root of the work
tree. When working inside a subdirectory, the user-supplied
pathspecs are taken to be relative to the current subdirectory.
Among special path components in pathspecs, we used to accept
and interpret only "." ("the directory", meaning a no-op) and
".." ("up one level") at the beginning. Everything else was
passed through as-is.
For example, if you are in Documentation/ directory of the
project, you can name Documentation/howto/maintain-git.txt as:
howto/maintain-git.txt
../Documentation/howto/maitain-git.txt
../././Documentation/howto/maitain-git.txt
but not as:
howto/./maintain-git.txt
$(pwd)/howto/maintain-git.txt
This patch updates prefix_path() in several ways:
- If the pathspec is not absolute, prefix (i.e. the current
subdirectory relative to the root of the work tree, with
terminating slash, if not empty) and the pathspec is
concatenated first and used in the next step. Otherwise,
that absolute pathspec is used in the next step.
- Then special path components "." (no-op) and ".." (up one
level) are interpreted to simplify the path. It is an error
to have too many ".." to cause the intermediate result to
step outside of the input to this step.
- If the original pathspec was not absolute, the result from
the previous step is the resulting "sanitized" pathspec.
Otherwise, the result from the previous step is still
absolute, and it is an error if it does not begin with the
directory that corresponds to the root of the work tree. The
directory is stripped away from the result and is returned.
- In any case, the resulting pathspec in the array
get_pathspec() returns omit the ones that caused errors.
With this patch, the last two examples also behave as expected.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-01-29 06:44:27 +00:00
|
|
|
{
|
|
|
|
const char *orig = path;
|
2010-12-27 10:54:37 +00:00
|
|
|
char *sanitized;
|
|
|
|
if (is_absolute_path(orig)) {
|
2016-02-22 22:44:28 +00:00
|
|
|
sanitized = xmallocz(strlen(path));
|
2013-07-14 08:36:03 +00:00
|
|
|
if (remaining_prefix)
|
|
|
|
*remaining_prefix = 0;
|
2014-02-04 14:25:20 +00:00
|
|
|
if (normalize_path_copy_len(sanitized, path, remaining_prefix)) {
|
|
|
|
free(sanitized);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (abspath_part_inside_repo(sanitized)) {
|
|
|
|
free(sanitized);
|
|
|
|
return NULL;
|
|
|
|
}
|
2010-12-27 10:54:37 +00:00
|
|
|
} else {
|
setup.c: do not feed NULL to "%.*s" even with precision 0
A recent update 75faa45a (replace trivial malloc + sprintf / strcpy
calls with xstrfmt, 2015-09-24) rewrote
prepare an empty buffer
if (len)
append the first len bytes of "prefix" to the buffer
append "path" to the buffer
that computed "path", optionally prefixed by "prefix", into
xstrfmt("%.*s%s", len, prefix, path);
However, passing a NULL pointer to the printf(3) family of functions
to format it with %s conversion, even with the precision set to 0,
i.e.
xstrfmt("%.*s", 0, NULL)
yields undefined results, at least on some platforms.
Avoid this problem by substituting prefix with "" when len==0, as
prefix can legally be NULL in that case. This would mimick the
intent of the original code better.
Reported-by: Tom G. Christensen <tgc@jupiterrise.com>
Helped-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-04-07 19:38:18 +00:00
|
|
|
sanitized = xstrfmt("%.*s%s", len, len ? prefix : "", path);
|
2013-07-14 08:36:03 +00:00
|
|
|
if (remaining_prefix)
|
|
|
|
*remaining_prefix = len;
|
2014-02-04 14:25:20 +00:00
|
|
|
if (normalize_path_copy_len(sanitized, sanitized, remaining_prefix)) {
|
2012-06-21 18:09:50 +00:00
|
|
|
free(sanitized);
|
|
|
|
return NULL;
|
setup: sanitize absolute and funny paths in get_pathspec()
The prefix_path() function called from get_pathspec() is
responsible for translating list of user-supplied pathspecs to
list of pathspecs that is relative to the root of the work
tree. When working inside a subdirectory, the user-supplied
pathspecs are taken to be relative to the current subdirectory.
Among special path components in pathspecs, we used to accept
and interpret only "." ("the directory", meaning a no-op) and
".." ("up one level") at the beginning. Everything else was
passed through as-is.
For example, if you are in Documentation/ directory of the
project, you can name Documentation/howto/maintain-git.txt as:
howto/maintain-git.txt
../Documentation/howto/maitain-git.txt
../././Documentation/howto/maitain-git.txt
but not as:
howto/./maintain-git.txt
$(pwd)/howto/maintain-git.txt
This patch updates prefix_path() in several ways:
- If the pathspec is not absolute, prefix (i.e. the current
subdirectory relative to the root of the work tree, with
terminating slash, if not empty) and the pathspec is
concatenated first and used in the next step. Otherwise,
that absolute pathspec is used in the next step.
- Then special path components "." (no-op) and ".." (up one
level) are interpreted to simplify the path. It is an error
to have too many ".." to cause the intermediate result to
step outside of the input to this step.
- If the original pathspec was not absolute, the result from
the previous step is the resulting "sanitized" pathspec.
Otherwise, the result from the previous step is still
absolute, and it is an error if it does not begin with the
directory that corresponds to the root of the work tree. The
directory is stripped away from the result and is returned.
- In any case, the resulting pathspec in the array
get_pathspec() returns omit the ones that caused errors.
With this patch, the last two examples also behave as expected.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-01-29 06:44:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return sanitized;
|
2005-08-17 03:44:32 +00:00
|
|
|
}
|
|
|
|
|
2012-06-21 18:09:50 +00:00
|
|
|
char *prefix_path(const char *prefix, int len, const char *path)
|
|
|
|
{
|
2013-07-14 08:36:03 +00:00
|
|
|
char *r = prefix_path_gently(prefix, len, NULL, path);
|
2020-03-03 04:05:06 +00:00
|
|
|
if (!r) {
|
|
|
|
const char *hint_path = get_git_work_tree();
|
|
|
|
if (!hint_path)
|
|
|
|
hint_path = get_git_dir();
|
2020-02-15 01:00:13 +00:00
|
|
|
die(_("'%s' is outside repository at '%s'"), path,
|
2020-03-03 04:05:06 +00:00
|
|
|
absolute_path(hint_path));
|
|
|
|
}
|
2012-06-21 18:09:50 +00:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
int path_inside_repo(const char *prefix, const char *path)
|
|
|
|
{
|
|
|
|
int len = prefix ? strlen(prefix) : 0;
|
2013-07-14 08:36:03 +00:00
|
|
|
char *r = prefix_path_gently(prefix, len, NULL, path);
|
2012-06-21 18:09:50 +00:00
|
|
|
if (r) {
|
|
|
|
free(r);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-10-18 07:27:24 +00:00
|
|
|
int check_filename(const char *prefix, const char *arg)
|
|
|
|
{
|
2017-03-21 01:28:49 +00:00
|
|
|
char *to_free = NULL;
|
2009-10-18 07:27:24 +00:00
|
|
|
struct stat st;
|
|
|
|
|
2017-05-26 19:07:42 +00:00
|
|
|
if (skip_prefix(arg, ":/", &arg)) {
|
|
|
|
if (!*arg) /* ":/" is root dir, always exists */
|
2013-01-21 13:00:48 +00:00
|
|
|
return 1;
|
2017-05-26 19:07:31 +00:00
|
|
|
prefix = NULL;
|
2017-05-26 19:08:39 +00:00
|
|
|
} else if (skip_prefix(arg, ":!", &arg) ||
|
|
|
|
skip_prefix(arg, ":^", &arg)) {
|
|
|
|
if (!*arg) /* excluding everything is silly, but allowed */
|
|
|
|
return 1;
|
2017-05-26 19:07:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (prefix)
|
|
|
|
arg = to_free = prefix_filename(prefix, arg);
|
|
|
|
|
|
|
|
if (!lstat(arg, &st)) {
|
2017-03-21 01:28:49 +00:00
|
|
|
free(to_free);
|
2009-10-18 07:27:24 +00:00
|
|
|
return 1; /* file exists */
|
2017-03-21 01:28:49 +00:00
|
|
|
}
|
2017-06-13 20:47:06 +00:00
|
|
|
if (is_missing_file_error(errno)) {
|
2017-03-21 01:28:49 +00:00
|
|
|
free(to_free);
|
2009-10-18 07:27:24 +00:00
|
|
|
return 0; /* file does not exist */
|
2017-03-21 01:28:49 +00:00
|
|
|
}
|
2018-02-13 13:19:15 +00:00
|
|
|
die_errno(_("failed to stat '%s'"), arg);
|
2009-10-18 07:27:24 +00:00
|
|
|
}
|
|
|
|
|
2019-04-16 09:33:39 +00:00
|
|
|
static void NORETURN die_verify_filename(struct repository *r,
|
|
|
|
const char *prefix,
|
2012-06-18 18:18:21 +00:00
|
|
|
const char *arg,
|
|
|
|
int diagnose_misspelt_rev)
|
2009-12-07 10:10:50 +00:00
|
|
|
{
|
2012-06-18 18:18:21 +00:00
|
|
|
if (!diagnose_misspelt_rev)
|
2016-06-17 20:21:06 +00:00
|
|
|
die(_("%s: no such path in the working tree.\n"
|
|
|
|
"Use 'git <command> -- <path>...' to specify paths that do not exist locally."),
|
2012-06-18 18:18:21 +00:00
|
|
|
arg);
|
2011-05-10 19:05:01 +00:00
|
|
|
/*
|
|
|
|
* Saying "'(icase)foo' does not exist in the index" when the
|
|
|
|
* user gave us ":(icase)foo" is just stupid. A magic pathspec
|
|
|
|
* begins with a colon and is followed by a non-alnum; do not
|
2012-07-02 18:01:25 +00:00
|
|
|
* let maybe_die_on_misspelt_object_name() even trigger.
|
2011-05-10 19:05:01 +00:00
|
|
|
*/
|
|
|
|
if (!(arg[0] == ':' && !isalnum(arg[1])))
|
2019-04-16 09:33:39 +00:00
|
|
|
maybe_die_on_misspelt_object_name(r, arg, prefix);
|
2011-05-10 19:05:01 +00:00
|
|
|
|
2009-12-07 10:10:50 +00:00
|
|
|
/* ... or fall back the most general message. */
|
2016-06-17 20:21:06 +00:00
|
|
|
die(_("ambiguous argument '%s': unknown revision or path not in the working tree.\n"
|
|
|
|
"Use '--' to separate paths from revisions, like this:\n"
|
|
|
|
"'git <command> [<revision>...] -- [<file>...]'"), arg);
|
2009-12-07 10:10:50 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
verify_filename(): treat ":(magic)" as a pathspec
For commands that take revisions and pathspecs, magic
pathspecs like ":(exclude)foo" require the user to specify
a disambiguating "--", since they do not match a file in the
filesystem, like:
git grep foo -- :(exclude)bar
This makes them more annoying to use than they need to be.
We loosened the rules for wildcards in 28fcc0b71 (pathspec:
avoid the need of "--" when wildcard is used, 2015-05-02).
Let's do the same for pathspecs with long-form magic.
We already handle the short-forms ":/" and ":^" specially in
check_filename(), so we don't need to handle them here. And
in fact, we could do the same with long-form magic, parsing
out the actual filename and making sure it exists. But there
are a few reasons not to do it that way:
- the parsing gets much more complicated, and we'd want to
hand it off to the pathspec code. But that code isn't
ready to do this kind of speculative parsing (it's happy
to die() when it sees a syntactically invalid pathspec).
- not all pathspec magic maps to a filesystem path. E.g.,
:(attr) should be treated as a pathspec regardless of
what is in the filesystem
- we can be a bit looser with ":(" than with the
short-form ":/", because it is much less likely to have
a false positive. Whereas ":/" also means "search for a
commit with this regex".
Note that because the change is in verify_filename() and not
in its helper check_filename(), this doesn't affect the
verify_non_filename() case. I.e., if an item that matches
our new rule doesn't resolve as an object, we may fallback
to treating it as a pathspec (rather than complaining it
doesn't exist). But if it does resolve (e.g., as a file in
the index that starts with an open-paren), we won't then
complain that it's also a valid pathspec. This matches the
wildcard-exception behavior.
And of course in either case, one can always insert the "--"
to get more precise results.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-05-26 19:10:31 +00:00
|
|
|
/*
|
|
|
|
* Check for arguments that don't resolve as actual files,
|
|
|
|
* but which look sufficiently like pathspecs that we'll consider
|
|
|
|
* them such for the purposes of rev/pathspec DWIM parsing.
|
|
|
|
*/
|
|
|
|
static int looks_like_pathspec(const char *arg)
|
|
|
|
{
|
verify_filename(): handle backslashes in "wildcards are pathspecs" rule
Commit 28fcc0b71a (pathspec: avoid the need of "--" when wildcard is
used, 2015-05-02) allowed:
git rev-parse '*.c'
without the double-dash. But the rule it uses to check for wildcards
actually looks for any glob special. This is overly liberal, as it means
that a pattern that doesn't actually do any wildcard matching, like
"a\b", will be considered a pathspec.
If you do have such a file on disk, that's presumably what you wanted.
But if you don't, the results are confusing: rather than say "there's no
such path a\b", we'll quietly accept it as a pathspec which very likely
matches nothing (or at least not what you intended). Likewise, looking
for path "a\*b" doesn't expand the search at all; it would only find a
single entry, "a*b".
This commit switches the rule to trigger only when glob metacharacters
would expand the search, meaning both of those cases will now report an
error (you can still disambiguate using "--", of course; we're just
tightening the DWIM heuristic).
Note that we didn't test the original feature in 28fcc0b71a at all. So
this patch not only tests for these corner cases, but also adds a
regression test for the existing behavior.
Reported-by: David Burström <davidburstrom@spotify.com>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-01-25 00:00:51 +00:00
|
|
|
const char *p;
|
|
|
|
int escaped = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Wildcard characters imply the user is looking to match pathspecs
|
|
|
|
* that aren't in the filesystem. Note that this doesn't include
|
|
|
|
* backslash even though it's a glob special; by itself it doesn't
|
|
|
|
* cause any increase in the match. Likewise ignore backslash-escaped
|
|
|
|
* wildcard characters.
|
|
|
|
*/
|
|
|
|
for (p = arg; *p; p++) {
|
|
|
|
if (escaped) {
|
|
|
|
escaped = 0;
|
|
|
|
} else if (is_glob_special(*p)) {
|
|
|
|
if (*p == '\\')
|
|
|
|
escaped = 1;
|
|
|
|
else
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
verify_filename(): treat ":(magic)" as a pathspec
For commands that take revisions and pathspecs, magic
pathspecs like ":(exclude)foo" require the user to specify
a disambiguating "--", since they do not match a file in the
filesystem, like:
git grep foo -- :(exclude)bar
This makes them more annoying to use than they need to be.
We loosened the rules for wildcards in 28fcc0b71 (pathspec:
avoid the need of "--" when wildcard is used, 2015-05-02).
Let's do the same for pathspecs with long-form magic.
We already handle the short-forms ":/" and ":^" specially in
check_filename(), so we don't need to handle them here. And
in fact, we could do the same with long-form magic, parsing
out the actual filename and making sure it exists. But there
are a few reasons not to do it that way:
- the parsing gets much more complicated, and we'd want to
hand it off to the pathspec code. But that code isn't
ready to do this kind of speculative parsing (it's happy
to die() when it sees a syntactically invalid pathspec).
- not all pathspec magic maps to a filesystem path. E.g.,
:(attr) should be treated as a pathspec regardless of
what is in the filesystem
- we can be a bit looser with ":(" than with the
short-form ":/", because it is much less likely to have
a false positive. Whereas ":/" also means "search for a
commit with this regex".
Note that because the change is in verify_filename() and not
in its helper check_filename(), this doesn't affect the
verify_non_filename() case. I.e., if an item that matches
our new rule doesn't resolve as an object, we may fallback
to treating it as a pathspec (rather than complaining it
doesn't exist). But if it does resolve (e.g., as a file in
the index that starts with an open-paren), we won't then
complain that it's also a valid pathspec. This matches the
wildcard-exception behavior.
And of course in either case, one can always insert the "--"
to get more precise results.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-05-26 19:10:31 +00:00
|
|
|
|
|
|
|
/* long-form pathspec magic */
|
|
|
|
if (starts_with(arg, ":("))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-04-26 17:15:54 +00:00
|
|
|
/*
|
|
|
|
* Verify a filename that we got as an argument for a pathspec
|
|
|
|
* entry. Note that a filename that begins with "-" never verifies
|
|
|
|
* as true, because even if such a filename were to exist, we want
|
|
|
|
* it to be preceded by the "--" marker (or we want the user to
|
|
|
|
* use a format like "./-filename")
|
2012-06-18 18:18:21 +00:00
|
|
|
*
|
|
|
|
* The "diagnose_misspelt_rev" is used to provide a user-friendly
|
|
|
|
* diagnosis when dying upon finding that "name" is not a pathname.
|
|
|
|
* If set to 1, the diagnosis will try to diagnose "name" as an
|
|
|
|
* invalid object name (e.g. HEAD:foo). If set to 0, the diagnosis
|
|
|
|
* will only complain about an inexisting file.
|
|
|
|
*
|
|
|
|
* This function is typically called to check that a "file or rev"
|
|
|
|
* argument is unambiguous. In this case, the caller will want
|
|
|
|
* diagnose_misspelt_rev == 1 when verifying the first non-rev
|
|
|
|
* argument (which could have been a revision), and
|
|
|
|
* diagnose_misspelt_rev == 0 for the next ones (because we already
|
|
|
|
* saw a filename, there's not ambiguity anymore).
|
2006-04-26 17:15:54 +00:00
|
|
|
*/
|
2012-06-18 18:18:21 +00:00
|
|
|
void verify_filename(const char *prefix,
|
|
|
|
const char *arg,
|
|
|
|
int diagnose_misspelt_rev)
|
2006-04-26 17:15:54 +00:00
|
|
|
{
|
|
|
|
if (*arg == '-')
|
2018-02-13 13:19:15 +00:00
|
|
|
die(_("option '%s' must come before non-option arguments"), arg);
|
2017-05-26 19:10:53 +00:00
|
|
|
if (looks_like_pathspec(arg) || check_filename(prefix, arg))
|
2006-04-26 17:15:54 +00:00
|
|
|
return;
|
2019-04-16 09:33:39 +00:00
|
|
|
die_verify_filename(the_repository, prefix, arg, diagnose_misspelt_rev);
|
2006-04-26 17:15:54 +00:00
|
|
|
}
|
|
|
|
|
2006-04-26 22:09:27 +00:00
|
|
|
/*
|
|
|
|
* Opposite of the above: the command line did not have -- marker
|
|
|
|
* and we parsed the arg as a refname. It should not be interpretable
|
|
|
|
* as a filename.
|
|
|
|
*/
|
|
|
|
void verify_non_filename(const char *prefix, const char *arg)
|
|
|
|
{
|
2007-06-03 14:48:16 +00:00
|
|
|
if (!is_inside_work_tree() || is_inside_git_dir())
|
2007-01-20 02:09:34 +00:00
|
|
|
return;
|
2006-04-26 22:09:27 +00:00
|
|
|
if (*arg == '-')
|
|
|
|
return; /* flag */
|
2009-10-18 07:27:24 +00:00
|
|
|
if (!check_filename(prefix, arg))
|
|
|
|
return;
|
2016-06-17 20:21:06 +00:00
|
|
|
die(_("ambiguous argument '%s': both revision and filename\n"
|
|
|
|
"Use '--' to separate paths from revisions, like this:\n"
|
|
|
|
"'git <command> [<revision>...] -- [<file>...]'"), arg);
|
2006-04-26 22:09:27 +00:00
|
|
|
}
|
|
|
|
|
2014-11-30 08:24:44 +00:00
|
|
|
int get_common_dir(struct strbuf *sb, const char *gitdir)
|
2015-09-13 22:17:42 +00:00
|
|
|
{
|
|
|
|
const char *git_env_common_dir = getenv(GIT_COMMON_DIR_ENVIRONMENT);
|
|
|
|
if (git_env_common_dir) {
|
|
|
|
strbuf_addstr(sb, git_env_common_dir);
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
return get_common_dir_noenv(sb, gitdir);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int get_common_dir_noenv(struct strbuf *sb, const char *gitdir)
|
2014-11-30 08:24:41 +00:00
|
|
|
{
|
|
|
|
struct strbuf data = STRBUF_INIT;
|
|
|
|
struct strbuf path = STRBUF_INIT;
|
2014-11-30 08:24:44 +00:00
|
|
|
int ret = 0;
|
2015-09-13 22:17:42 +00:00
|
|
|
|
2014-11-30 08:24:41 +00:00
|
|
|
strbuf_addf(&path, "%s/commondir", gitdir);
|
|
|
|
if (file_exists(path.buf)) {
|
|
|
|
if (strbuf_read_file(&data, path.buf, 0) <= 0)
|
|
|
|
die_errno(_("failed to read %s"), path.buf);
|
|
|
|
while (data.len && (data.buf[data.len - 1] == '\n' ||
|
|
|
|
data.buf[data.len - 1] == '\r'))
|
|
|
|
data.len--;
|
|
|
|
data.buf[data.len] = '\0';
|
|
|
|
strbuf_reset(&path);
|
|
|
|
if (!is_absolute_path(data.buf))
|
|
|
|
strbuf_addf(&path, "%s/", gitdir);
|
|
|
|
strbuf_addbuf(&path, &data);
|
2017-02-25 16:00:33 +00:00
|
|
|
strbuf_add_real_path(sb, path.buf);
|
2014-11-30 08:24:44 +00:00
|
|
|
ret = 1;
|
2016-12-12 18:16:55 +00:00
|
|
|
} else {
|
2014-11-30 08:24:41 +00:00
|
|
|
strbuf_addstr(sb, gitdir);
|
2016-12-12 18:16:55 +00:00
|
|
|
}
|
|
|
|
|
2014-11-30 08:24:41 +00:00
|
|
|
strbuf_release(&data);
|
|
|
|
strbuf_release(&path);
|
2014-11-30 08:24:44 +00:00
|
|
|
return ret;
|
2014-11-30 08:24:41 +00:00
|
|
|
}
|
2005-08-17 01:06:34 +00:00
|
|
|
|
[PATCH] Make .git directory validation code test HEAD
Inspired by a report by Kalle Valo, this changes git-sh-setup-script and
the "setup_git_directory()" function to test that $GIT_DIR/HEAD is a
symlink, since a number of core git features depend on that these days.
We used to allow a regular file there, but git-fsck-cache has been
complaining about that for a while, and anything that uses branches
depends on the HEAD file being a symlink, so let's just encode that as a
fundamental requirement.
Before, a non-symlink HEAD file would appear to work, but have subtle bugs
like not having the HEAD show up as a valid reference (because it wasn't
under "refs"). Now, we will complain loudly, and the user can fix it up
trivially instead of getting strange behaviour.
This also removes the tests for "$GIT_DIR" and "$GIT_OBJECT_DIRECTORY"
being directories, since the other tests will implicitly test for that
anyway (ie the tests for HEAD, refs and 00 would fail).
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2005-08-27 20:54:42 +00:00
|
|
|
/*
|
2006-12-31 04:30:19 +00:00
|
|
|
* Test if it looks like we're at a git directory.
|
2005-11-25 23:43:41 +00:00
|
|
|
* We want to see:
|
[PATCH] Make .git directory validation code test HEAD
Inspired by a report by Kalle Valo, this changes git-sh-setup-script and
the "setup_git_directory()" function to test that $GIT_DIR/HEAD is a
symlink, since a number of core git features depend on that these days.
We used to allow a regular file there, but git-fsck-cache has been
complaining about that for a while, and anything that uses branches
depends on the HEAD file being a symlink, so let's just encode that as a
fundamental requirement.
Before, a non-symlink HEAD file would appear to work, but have subtle bugs
like not having the HEAD show up as a valid reference (because it wasn't
under "refs"). Now, we will complain loudly, and the user can fix it up
trivially instead of getting strange behaviour.
This also removes the tests for "$GIT_DIR" and "$GIT_OBJECT_DIRECTORY"
being directories, since the other tests will implicitly test for that
anyway (ie the tests for HEAD, refs and 00 would fail).
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2005-08-27 20:54:42 +00:00
|
|
|
*
|
2008-01-03 14:18:07 +00:00
|
|
|
* - either an objects/ directory _or_ the proper
|
[PATCH] Make .git directory validation code test HEAD
Inspired by a report by Kalle Valo, this changes git-sh-setup-script and
the "setup_git_directory()" function to test that $GIT_DIR/HEAD is a
symlink, since a number of core git features depend on that these days.
We used to allow a regular file there, but git-fsck-cache has been
complaining about that for a while, and anything that uses branches
depends on the HEAD file being a symlink, so let's just encode that as a
fundamental requirement.
Before, a non-symlink HEAD file would appear to work, but have subtle bugs
like not having the HEAD show up as a valid reference (because it wasn't
under "refs"). Now, we will complain loudly, and the user can fix it up
trivially instead of getting strange behaviour.
This also removes the tests for "$GIT_DIR" and "$GIT_OBJECT_DIRECTORY"
being directories, since the other tests will implicitly test for that
anyway (ie the tests for HEAD, refs and 00 would fail).
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2005-08-27 20:54:42 +00:00
|
|
|
* GIT_OBJECT_DIRECTORY environment variable
|
2006-12-31 04:30:19 +00:00
|
|
|
* - a refs/ directory
|
2005-09-30 21:26:57 +00:00
|
|
|
* - either a HEAD symlink or a HEAD file that is formatted as
|
2007-01-02 07:31:08 +00:00
|
|
|
* a proper "ref:", or a regular file HEAD that has a properly
|
|
|
|
* formatted sha1 object name.
|
[PATCH] Make .git directory validation code test HEAD
Inspired by a report by Kalle Valo, this changes git-sh-setup-script and
the "setup_git_directory()" function to test that $GIT_DIR/HEAD is a
symlink, since a number of core git features depend on that these days.
We used to allow a regular file there, but git-fsck-cache has been
complaining about that for a while, and anything that uses branches
depends on the HEAD file being a symlink, so let's just encode that as a
fundamental requirement.
Before, a non-symlink HEAD file would appear to work, but have subtle bugs
like not having the HEAD show up as a valid reference (because it wasn't
under "refs"). Now, we will complain loudly, and the user can fix it up
trivially instead of getting strange behaviour.
This also removes the tests for "$GIT_DIR" and "$GIT_OBJECT_DIRECTORY"
being directories, since the other tests will implicitly test for that
anyway (ie the tests for HEAD, refs and 00 would fail).
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2005-08-27 20:54:42 +00:00
|
|
|
*/
|
standardize and improve lookup rules for external local repos
When you specify a local repository on the command line of
clone, ls-remote, upload-pack, receive-pack, or upload-archive,
or in a request to git-daemon, we perform a little bit of
lookup magic, doing things like looking in working trees for
.git directories and appending ".git" for bare repos.
For clone, this magic happens in get_repo_path. For
everything else, it happens in enter_repo. In both cases,
there are some ambiguous or confusing cases that aren't
handled well, and there is one case that is not handled the
same by both methods.
This patch tries to provide (and test!) standard, sensible
lookup rules for both code paths. The intended changes are:
1. When looking up "foo", we have always preferred
a working tree "foo" (containing "foo/.git" over the
bare "foo.git". But we did not prefer a bare "foo" over
"foo.git". With this patch, we do so.
2. We would select directories that existed but didn't
actually look like git repositories. With this patch,
we make sure a selected directory looks like a git
repo. Not only is this more sensible in general, but it
will help anybody who is negatively affected by change
(1) negatively (e.g., if they had "foo.git" next to its
separate work tree "foo", and expect to keep finding
"foo.git" when they reference "foo").
3. The enter_repo code path would, given "foo", look for
"foo.git/.git" (i.e., do the ".git" append magic even
for a repo with working tree). The clone code path did
not; with this patch, they now behave the same.
In the unlikely case of a working tree overlaying a bare
repo (i.e., a ".git" directory _inside_ a bare repo), we
continue to treat it as a working tree (prefering the
"inner" .git over the bare repo). This is mainly because the
combination seems nonsensical, and I'd rather stick with
existing behavior on the off chance that somebody is relying
on it.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2012-02-02 21:59:13 +00:00
|
|
|
int is_git_directory(const char *suspect)
|
[PATCH] Make .git directory validation code test HEAD
Inspired by a report by Kalle Valo, this changes git-sh-setup-script and
the "setup_git_directory()" function to test that $GIT_DIR/HEAD is a
symlink, since a number of core git features depend on that these days.
We used to allow a regular file there, but git-fsck-cache has been
complaining about that for a while, and anything that uses branches
depends on the HEAD file being a symlink, so let's just encode that as a
fundamental requirement.
Before, a non-symlink HEAD file would appear to work, but have subtle bugs
like not having the HEAD show up as a valid reference (because it wasn't
under "refs"). Now, we will complain loudly, and the user can fix it up
trivially instead of getting strange behaviour.
This also removes the tests for "$GIT_DIR" and "$GIT_OBJECT_DIRECTORY"
being directories, since the other tests will implicitly test for that
anyway (ie the tests for HEAD, refs and 00 would fail).
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2005-08-27 20:54:42 +00:00
|
|
|
{
|
2014-11-30 08:24:40 +00:00
|
|
|
struct strbuf path = STRBUF_INIT;
|
|
|
|
int ret = 0;
|
|
|
|
size_t len;
|
2006-12-31 04:30:19 +00:00
|
|
|
|
2014-11-30 08:24:41 +00:00
|
|
|
/* Check worktree-related signatures */
|
2017-11-03 12:58:02 +00:00
|
|
|
strbuf_addstr(&path, suspect);
|
|
|
|
strbuf_complete(&path, '/');
|
|
|
|
strbuf_addstr(&path, "HEAD");
|
2014-11-30 08:24:41 +00:00
|
|
|
if (validate_headref(path.buf))
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
strbuf_reset(&path);
|
|
|
|
get_common_dir(&path, suspect);
|
2014-11-30 08:24:40 +00:00
|
|
|
len = path.len;
|
2014-11-30 08:24:41 +00:00
|
|
|
|
|
|
|
/* Check non-worktree-related signatures */
|
2006-12-31 04:30:19 +00:00
|
|
|
if (getenv(DB_ENVIRONMENT)) {
|
|
|
|
if (access(getenv(DB_ENVIRONMENT), X_OK))
|
2014-11-30 08:24:40 +00:00
|
|
|
goto done;
|
2006-12-31 04:30:19 +00:00
|
|
|
}
|
|
|
|
else {
|
2014-11-30 08:24:41 +00:00
|
|
|
strbuf_setlen(&path, len);
|
2014-11-30 08:24:40 +00:00
|
|
|
strbuf_addstr(&path, "/objects");
|
|
|
|
if (access(path.buf, X_OK))
|
|
|
|
goto done;
|
2006-12-31 04:30:19 +00:00
|
|
|
}
|
|
|
|
|
2014-11-30 08:24:40 +00:00
|
|
|
strbuf_setlen(&path, len);
|
|
|
|
strbuf_addstr(&path, "/refs");
|
|
|
|
if (access(path.buf, X_OK))
|
|
|
|
goto done;
|
2006-12-31 04:30:19 +00:00
|
|
|
|
2014-11-30 08:24:40 +00:00
|
|
|
ret = 1;
|
|
|
|
done:
|
|
|
|
strbuf_release(&path);
|
|
|
|
return ret;
|
[PATCH] Make .git directory validation code test HEAD
Inspired by a report by Kalle Valo, this changes git-sh-setup-script and
the "setup_git_directory()" function to test that $GIT_DIR/HEAD is a
symlink, since a number of core git features depend on that these days.
We used to allow a regular file there, but git-fsck-cache has been
complaining about that for a while, and anything that uses branches
depends on the HEAD file being a symlink, so let's just encode that as a
fundamental requirement.
Before, a non-symlink HEAD file would appear to work, but have subtle bugs
like not having the HEAD show up as a valid reference (because it wasn't
under "refs"). Now, we will complain loudly, and the user can fix it up
trivially instead of getting strange behaviour.
This also removes the tests for "$GIT_DIR" and "$GIT_OBJECT_DIRECTORY"
being directories, since the other tests will implicitly test for that
anyway (ie the tests for HEAD, refs and 00 would fail).
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2005-08-27 20:54:42 +00:00
|
|
|
}
|
|
|
|
|
2016-01-22 22:27:33 +00:00
|
|
|
int is_nonbare_repository_dir(struct strbuf *path)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
int gitfile_error;
|
|
|
|
size_t orig_path_len = path->len;
|
|
|
|
assert(orig_path_len != 0);
|
|
|
|
strbuf_complete(path, '/');
|
|
|
|
strbuf_addstr(path, ".git");
|
|
|
|
if (read_gitfile_gently(path->buf, &gitfile_error) || is_git_directory(path->buf))
|
|
|
|
ret = 1;
|
|
|
|
if (gitfile_error == READ_GITFILE_ERR_OPEN_FAILED ||
|
|
|
|
gitfile_error == READ_GITFILE_ERR_READ_FAILED)
|
|
|
|
ret = 1;
|
|
|
|
strbuf_setlen(path, orig_path_len);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2007-01-20 02:09:34 +00:00
|
|
|
int is_inside_git_dir(void)
|
|
|
|
{
|
Clean up work-tree handling
The old version of work-tree support was an unholy mess, barely readable,
and not to the point.
For example, why do you have to provide a worktree, when it is not used?
As in "git status". Now it works.
Another riddle was: if you can have work trees inside the git dir, why
are some programs complaining that they need a work tree?
IOW it is allowed to call
$ git --git-dir=../ --work-tree=. bla
when you really want to. In this case, you are both in the git directory
and in the working tree. So, programs have to actually test for the right
thing, namely if they are inside a working tree, and not if they are
inside a git directory.
Also, GIT_DIR=../.git should behave the same as if no GIT_DIR was
specified, unless there is a repository in the current working directory.
It does now.
The logic to determine if a repository is bare, or has a work tree
(tertium non datur), is this:
--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.
In related news, a long standing bug was fixed: when in .git/bla/x.git/,
which is a bare repository, git formerly assumed ../.. to be the
appropriate git dir. This problem was reported by Shawn Pearce to have
caused much pain, where a colleague mistakenly ran "git init" in "/" a
long time ago, and bare repositories just would not work.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2007-08-01 00:30:14 +00:00
|
|
|
if (inside_git_dir < 0)
|
|
|
|
inside_git_dir = is_inside_dir(get_git_dir());
|
|
|
|
return inside_git_dir;
|
2007-06-06 07:10:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int is_inside_work_tree(void)
|
|
|
|
{
|
Clean up work-tree handling
The old version of work-tree support was an unholy mess, barely readable,
and not to the point.
For example, why do you have to provide a worktree, when it is not used?
As in "git status". Now it works.
Another riddle was: if you can have work trees inside the git dir, why
are some programs complaining that they need a work tree?
IOW it is allowed to call
$ git --git-dir=../ --work-tree=. bla
when you really want to. In this case, you are both in the git directory
and in the working tree. So, programs have to actually test for the right
thing, namely if they are inside a working tree, and not if they are
inside a git directory.
Also, GIT_DIR=../.git should behave the same as if no GIT_DIR was
specified, unless there is a repository in the current working directory.
It does now.
The logic to determine if a repository is bare, or has a work tree
(tertium non datur), is this:
--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.
In related news, a long standing bug was fixed: when in .git/bla/x.git/,
which is a bare repository, git formerly assumed ../.. to be the
appropriate git dir. This problem was reported by Shawn Pearce to have
caused much pain, where a colleague mistakenly ran "git init" in "/" a
long time ago, and bare repositories just would not work.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2007-08-01 00:30:14 +00:00
|
|
|
if (inside_work_tree < 0)
|
|
|
|
inside_work_tree = is_inside_dir(get_git_work_tree());
|
|
|
|
return inside_work_tree;
|
2007-06-06 07:10:42 +00:00
|
|
|
}
|
|
|
|
|
2007-11-08 23:35:32 +00:00
|
|
|
void setup_work_tree(void)
|
|
|
|
{
|
set_work_tree: use chdir_notify
When we change to the top of the working tree, we manually
re-adjust $GIT_DIR and call set_git_dir() again, in order to
update any relative git-dir we'd compute earlier.
Instead of the work-tree code having to know to call the
git-dir code, let's use the new chdir_notify interface.
There are two spots that need updating, with a few
subtleties in each:
1. the set_git_dir() code needs to chdir_notify_register()
so it can be told when to update its path.
Technically we could push this down into repo_set_gitdir(),
so that even repository structs besides the_repository
could benefit from this. But that opens up a lot of
complications:
- we'd still need to touch set_git_dir(), because it
does some other setup (like setting $GIT_DIR in the
environment)
- submodules using other repository structs get
cleaned up, which means we'd need to remove them
from the chdir_notify list
- it's unlikely to fix any bugs, since we shouldn't
generally chdir() in the middle of working on a
submodule
2. setup_work_tree now needs to call chdir_notify(), and
can lose its manual set_git_dir() call.
Note that at first glance it looks like this undoes the
absolute-to-relative optimization added by 044bbbcb63
(Make git_dir a path relative to work_tree in
setup_work_tree(), 2008-06-19). But for the most part
that optimization was just _undoing_ the
relative-to-absolute conversion which the function was
doing earlier (and which is now gone).
It is true that if you already have an absolute git_dir
that the setup_work_tree() function will no longer make
it relative as a side effect. But:
- we generally do have relative git-dir's due to the
way the discovery code works
- if we really care about making git-dir's relative
when possible, then we should be relativizing them
earlier (e.g., when we see an absolute $GIT_DIR we
could turn it relative, whether we are going to
chdir into a worktree or not). That would cover all
cases, including ones that 044bbbcb63 did not.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-03-30 18:35:08 +00:00
|
|
|
const char *work_tree;
|
2007-11-09 11:34:07 +00:00
|
|
|
static int initialized = 0;
|
|
|
|
|
|
|
|
if (initialized)
|
|
|
|
return;
|
setup_git_directory: delay core.bare/core.worktree errors
If both core.bare and core.worktree are set, we complain
about the bogus config and die. Dying is good, because it
avoids commands running and doing damage in a potentially
incorrect setup. But dying _there_ is bad, because it means
that commands which do not even care about the work tree
cannot run. This can make repairing the situation harder:
[setup]
$ git config core.bare true
$ git config core.worktree /some/path
[OK, expected.]
$ git status
fatal: core.bare and core.worktree do not make sense
[Hrm...]
$ git config --unset core.worktree
fatal: core.bare and core.worktree do not make sense
[Nope...]
$ git config --edit
fatal: core.bare and core.worktree do not make sense
[Gaaah.]
$ git help config
fatal: core.bare and core.worktree do not make sense
Instead, let's issue a warning about the bogus config when
we notice it (i.e., for all commands), but only die when the
command tries to use the work tree (by calling setup_work_tree).
So we now get:
$ git status
warning: core.bare and core.worktree do not make sense
fatal: unable to set up work tree using invalid config
$ git config --unset core.worktree
warning: core.bare and core.worktree do not make sense
We have to update t1510 to accomodate this; it uses
symbolic-ref to check whether the configuration works or
not, but of course that command does not use the working
tree. Instead, we switch it to use `git status`, as it
requires a work-tree, does not need any special setup, and
is read-only (so a failure will not adversely affect further
tests).
In addition, we add a new test that checks the desired
behavior (i.e., that running "git config" with the bogus
config does in fact work).
Reported-by: SZEDER Gábor <szeder@ira.uka.de>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-05-29 06:49:10 +00:00
|
|
|
|
|
|
|
if (work_tree_config_is_bogus)
|
2018-02-13 13:19:15 +00:00
|
|
|
die(_("unable to set up work tree using invalid config"));
|
setup_git_directory: delay core.bare/core.worktree errors
If both core.bare and core.worktree are set, we complain
about the bogus config and die. Dying is good, because it
avoids commands running and doing damage in a potentially
incorrect setup. But dying _there_ is bad, because it means
that commands which do not even care about the work tree
cannot run. This can make repairing the situation harder:
[setup]
$ git config core.bare true
$ git config core.worktree /some/path
[OK, expected.]
$ git status
fatal: core.bare and core.worktree do not make sense
[Hrm...]
$ git config --unset core.worktree
fatal: core.bare and core.worktree do not make sense
[Nope...]
$ git config --edit
fatal: core.bare and core.worktree do not make sense
[Gaaah.]
$ git help config
fatal: core.bare and core.worktree do not make sense
Instead, let's issue a warning about the bogus config when
we notice it (i.e., for all commands), but only die when the
command tries to use the work tree (by calling setup_work_tree).
So we now get:
$ git status
warning: core.bare and core.worktree do not make sense
fatal: unable to set up work tree using invalid config
$ git config --unset core.worktree
warning: core.bare and core.worktree do not make sense
We have to update t1510 to accomodate this; it uses
symbolic-ref to check whether the configuration works or
not, but of course that command does not use the working
tree. Instead, we switch it to use `git status`, as it
requires a work-tree, does not need any special setup, and
is read-only (so a failure will not adversely affect further
tests).
In addition, we add a new test that checks the desired
behavior (i.e., that running "git config" with the bogus
config does in fact work).
Reported-by: SZEDER Gábor <szeder@ira.uka.de>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-05-29 06:49:10 +00:00
|
|
|
|
2007-11-09 11:34:07 +00:00
|
|
|
work_tree = get_git_work_tree();
|
set_work_tree: use chdir_notify
When we change to the top of the working tree, we manually
re-adjust $GIT_DIR and call set_git_dir() again, in order to
update any relative git-dir we'd compute earlier.
Instead of the work-tree code having to know to call the
git-dir code, let's use the new chdir_notify interface.
There are two spots that need updating, with a few
subtleties in each:
1. the set_git_dir() code needs to chdir_notify_register()
so it can be told when to update its path.
Technically we could push this down into repo_set_gitdir(),
so that even repository structs besides the_repository
could benefit from this. But that opens up a lot of
complications:
- we'd still need to touch set_git_dir(), because it
does some other setup (like setting $GIT_DIR in the
environment)
- submodules using other repository structs get
cleaned up, which means we'd need to remove them
from the chdir_notify list
- it's unlikely to fix any bugs, since we shouldn't
generally chdir() in the middle of working on a
submodule
2. setup_work_tree now needs to call chdir_notify(), and
can lose its manual set_git_dir() call.
Note that at first glance it looks like this undoes the
absolute-to-relative optimization added by 044bbbcb63
(Make git_dir a path relative to work_tree in
setup_work_tree(), 2008-06-19). But for the most part
that optimization was just _undoing_ the
relative-to-absolute conversion which the function was
doing earlier (and which is now gone).
It is true that if you already have an absolute git_dir
that the setup_work_tree() function will no longer make
it relative as a side effect. But:
- we generally do have relative git-dir's due to the
way the discovery code works
- if we really care about making git-dir's relative
when possible, then we should be relativizing them
earlier (e.g., when we see an absolute $GIT_DIR we
could turn it relative, whether we are going to
chdir into a worktree or not). That would cover all
cases, including ones that 044bbbcb63 did not.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-03-30 18:35:08 +00:00
|
|
|
if (!work_tree || chdir_notify(work_tree))
|
2018-02-13 13:19:15 +00:00
|
|
|
die(_("this operation must be run in a work tree"));
|
2010-12-27 01:26:04 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure subsequent git processes find correct worktree
|
|
|
|
* if $GIT_WORK_TREE is set relative
|
|
|
|
*/
|
|
|
|
if (getenv(GIT_WORK_TREE_ENVIRONMENT))
|
|
|
|
setenv(GIT_WORK_TREE_ENVIRONMENT, ".", 1);
|
|
|
|
|
2007-11-09 11:34:07 +00:00
|
|
|
initialized = 1;
|
2007-11-03 11:23:11 +00:00
|
|
|
}
|
|
|
|
|
2021-12-09 05:08:26 +00:00
|
|
|
static void setup_original_cwd(void)
|
|
|
|
{
|
|
|
|
struct strbuf tmp = STRBUF_INIT;
|
|
|
|
const char *worktree = NULL;
|
|
|
|
int offset = -1;
|
|
|
|
|
|
|
|
if (!tmp_original_cwd)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* startup_info->original_cwd points to the current working
|
|
|
|
* directory we inherited from our parent process, which is a
|
|
|
|
* directory we want to avoid removing.
|
|
|
|
*
|
|
|
|
* For convience, we would like to have the path relative to the
|
|
|
|
* worktree instead of an absolute path.
|
|
|
|
*
|
|
|
|
* Yes, startup_info->original_cwd is usually the same as 'prefix',
|
|
|
|
* but differs in two ways:
|
|
|
|
* - prefix has a trailing '/'
|
|
|
|
* - if the user passes '-C' to git, that modifies the prefix but
|
|
|
|
* not startup_info->original_cwd.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Normalize the directory */
|
setup: don't die if realpath(3) fails on getcwd(3)
Prior to Git 2.35.0, git could be run from an inaccessible working
directory so long as the git repository specified by options and/or
environment variables was accessible. For example:
git init repo
mkdir -p a/b
cd a/b
chmod u-x ..
git -C "${PWD%/a/b}/repo" status
If this example seems a bit contrived, consider running with the
repository owner as a substitute UID (e.g. with runuser(1) or sudo(8))
without ensuring the working directory is accessible by that user.
The code added by e6f8861bd4 ("setup: introduce
startup_info->original_cwd") to preserve the working directory attempts
to normalize the path using strbuf_realpath(). If that fails, as in the
case above, it is treated as a fatal error.
This commit treats strbuf_realpath() errors as non-fatal. If an error
occurs, setup_original_cwd() will continue without applying removal
prevention for cwd, resulting in the pre-2.35.0 behavior. The risk
should be minimal, since git will not operate on a repository with
inaccessible ancestors, this behavior is only known to occur when cwd is
a descendant of the repository, an ancestor of cwd is inaccessible, and
no ancestors of the repository are inaccessible.
Signed-off-by: Kevin Locke <kevin@kevinlocke.name>
Reviewed-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-05-24 19:20:12 +00:00
|
|
|
if (!strbuf_realpath(&tmp, tmp_original_cwd, 0)) {
|
|
|
|
trace2_data_string("setup", the_repository,
|
|
|
|
"realpath-path", tmp_original_cwd);
|
|
|
|
trace2_data_string("setup", the_repository,
|
|
|
|
"realpath-failure", strerror(errno));
|
|
|
|
free((char*)tmp_original_cwd);
|
|
|
|
tmp_original_cwd = NULL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-12-09 05:08:26 +00:00
|
|
|
free((char*)tmp_original_cwd);
|
|
|
|
tmp_original_cwd = NULL;
|
|
|
|
startup_info->original_cwd = strbuf_detach(&tmp, NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get our worktree; we only protect the current working directory
|
|
|
|
* if it's in the worktree.
|
|
|
|
*/
|
|
|
|
worktree = get_git_work_tree();
|
|
|
|
if (!worktree)
|
|
|
|
goto no_prevention_needed;
|
|
|
|
|
|
|
|
offset = dir_inside_of(startup_info->original_cwd, worktree);
|
|
|
|
if (offset >= 0) {
|
|
|
|
/*
|
|
|
|
* If startup_info->original_cwd == worktree, that is already
|
|
|
|
* protected and we don't need original_cwd as a secondary
|
|
|
|
* protection measure.
|
|
|
|
*/
|
|
|
|
if (!*(startup_info->original_cwd + offset))
|
|
|
|
goto no_prevention_needed;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* original_cwd was inside worktree; precompose it just as
|
|
|
|
* we do prefix so that built up paths will match
|
|
|
|
*/
|
|
|
|
startup_info->original_cwd = \
|
|
|
|
precompose_string_if_needed(startup_info->original_cwd
|
|
|
|
+ offset);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
no_prevention_needed:
|
|
|
|
free((char*)startup_info->original_cwd);
|
|
|
|
startup_info->original_cwd = NULL;
|
|
|
|
}
|
|
|
|
|
2018-10-21 14:02:28 +00:00
|
|
|
static int read_worktree_config(const char *var, const char *value, void *vdata)
|
|
|
|
{
|
|
|
|
struct repository_format *data = vdata;
|
|
|
|
|
|
|
|
if (strcmp(var, "core.bare") == 0) {
|
|
|
|
data->is_bare = git_config_bool(var, value);
|
|
|
|
} else if (strcmp(var, "core.worktree") == 0) {
|
|
|
|
if (!value)
|
|
|
|
return config_error_nonbool(var);
|
2019-01-22 21:45:47 +00:00
|
|
|
free(data->work_tree);
|
2018-10-21 14:02:28 +00:00
|
|
|
data->work_tree = xstrdup(value);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-07-16 12:25:13 +00:00
|
|
|
enum extension_result {
|
|
|
|
EXTENSION_ERROR = -1, /* compatible with error(), etc */
|
|
|
|
EXTENSION_UNKNOWN = 0,
|
|
|
|
EXTENSION_OK = 1
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Do not add new extensions to this function. It handles extensions which are
|
|
|
|
* respected even in v0-format repositories for historical compatibility.
|
|
|
|
*/
|
|
|
|
static enum extension_result handle_extension_v0(const char *var,
|
|
|
|
const char *value,
|
|
|
|
const char *ext,
|
|
|
|
struct repository_format *data)
|
|
|
|
{
|
|
|
|
if (!strcmp(ext, "noop")) {
|
|
|
|
return EXTENSION_OK;
|
|
|
|
} else if (!strcmp(ext, "preciousobjects")) {
|
|
|
|
data->precious_objects = git_config_bool(var, value);
|
|
|
|
return EXTENSION_OK;
|
|
|
|
} else if (!strcmp(ext, "partialclone")) {
|
|
|
|
data->partial_clone = xstrdup(value);
|
|
|
|
return EXTENSION_OK;
|
|
|
|
} else if (!strcmp(ext, "worktreeconfig")) {
|
|
|
|
data->worktree_config = git_config_bool(var, value);
|
|
|
|
return EXTENSION_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
return EXTENSION_UNKNOWN;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Record any new extensions in this function.
|
|
|
|
*/
|
|
|
|
static enum extension_result handle_extension(const char *var,
|
|
|
|
const char *value,
|
|
|
|
const char *ext,
|
|
|
|
struct repository_format *data)
|
|
|
|
{
|
|
|
|
if (!strcmp(ext, "noop-v1")) {
|
|
|
|
return EXTENSION_OK;
|
2020-08-12 01:04:11 +00:00
|
|
|
} else if (!strcmp(ext, "objectformat")) {
|
|
|
|
int format;
|
2020-07-16 12:25:13 +00:00
|
|
|
|
2020-08-12 01:04:11 +00:00
|
|
|
if (!value)
|
|
|
|
return config_error_nonbool(var);
|
|
|
|
format = hash_algo_by_name(value);
|
|
|
|
if (format == GIT_HASH_UNKNOWN)
|
2022-01-31 22:07:47 +00:00
|
|
|
return error(_("invalid value for '%s': '%s'"),
|
|
|
|
"extensions.objectformat", value);
|
2020-08-12 01:04:11 +00:00
|
|
|
data->hash_algo = format;
|
|
|
|
return EXTENSION_OK;
|
|
|
|
}
|
2020-07-16 12:25:13 +00:00
|
|
|
return EXTENSION_UNKNOWN;
|
|
|
|
}
|
|
|
|
|
2016-03-11 22:37:07 +00:00
|
|
|
static int check_repo_format(const char *var, const char *value, void *vdata)
|
2014-11-30 08:24:44 +00:00
|
|
|
{
|
2016-03-11 22:37:07 +00:00
|
|
|
struct repository_format *data = vdata;
|
introduce "extensions" form of core.repositoryformatversion
Normally we try to avoid bumps of the whole-repository
core.repositoryformatversion field. However, it is
unavoidable if we want to safely change certain aspects of
git in a backwards-incompatible way (e.g., modifying the set
of ref tips that we must traverse to generate a list of
unreachable, safe-to-prune objects).
If we were to bump the repository version for every such
change, then any implementation understanding version `X`
would also have to understand `X-1`, `X-2`, and so forth,
even though the incompatibilities may be in orthogonal parts
of the system, and there is otherwise no reason we cannot
implement one without the other (or more importantly, that
the user cannot choose to use one feature without the other,
weighing the tradeoff in compatibility only for that
particular feature).
This patch documents the existing repositoryformatversion
strategy and introduces a new format, "1", which lets a
repository specify that it must run with an arbitrary set of
extensions. This can be used, for example:
- to inform git that the objects should not be pruned based
only on the reachability of the ref tips (e.g, because it
has "clone --shared" children)
- that the refs are stored in a format besides the usual
"refs" and "packed-refs" directories
Because we bump to format "1", and because format "1"
requires that a running git knows about any extensions
mentioned, we know that older versions of the code will not
do something dangerous when confronted with these new
formats.
For example, if the user chooses to use database storage for
refs, they may set the "extensions.refbackend" config to
"db". Older versions of git will not understand format "1"
and bail. Versions of git which understand "1" but do not
know about "refbackend", or which know about "refbackend"
but not about the "db" backend, will refuse to run. This is
annoying, of course, but much better than the alternative of
claiming that there are no refs in the repository, or
writing to a location that other implementations will not
read.
Note that we are only defining the rules for format 1 here.
We do not ever write format 1 ourselves; it is a tool that
is meant to be used by users and future extensions to
provide safety with older implementations.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-06-23 10:53:58 +00:00
|
|
|
const char *ext;
|
|
|
|
|
2014-11-30 08:24:44 +00:00
|
|
|
if (strcmp(var, "core.repositoryformatversion") == 0)
|
2016-03-11 22:37:07 +00:00
|
|
|
data->version = git_config_int(var, value);
|
introduce "extensions" form of core.repositoryformatversion
Normally we try to avoid bumps of the whole-repository
core.repositoryformatversion field. However, it is
unavoidable if we want to safely change certain aspects of
git in a backwards-incompatible way (e.g., modifying the set
of ref tips that we must traverse to generate a list of
unreachable, safe-to-prune objects).
If we were to bump the repository version for every such
change, then any implementation understanding version `X`
would also have to understand `X-1`, `X-2`, and so forth,
even though the incompatibilities may be in orthogonal parts
of the system, and there is otherwise no reason we cannot
implement one without the other (or more importantly, that
the user cannot choose to use one feature without the other,
weighing the tradeoff in compatibility only for that
particular feature).
This patch documents the existing repositoryformatversion
strategy and introduces a new format, "1", which lets a
repository specify that it must run with an arbitrary set of
extensions. This can be used, for example:
- to inform git that the objects should not be pruned based
only on the reachability of the ref tips (e.g, because it
has "clone --shared" children)
- that the refs are stored in a format besides the usual
"refs" and "packed-refs" directories
Because we bump to format "1", and because format "1"
requires that a running git knows about any extensions
mentioned, we know that older versions of the code will not
do something dangerous when confronted with these new
formats.
For example, if the user chooses to use database storage for
refs, they may set the "extensions.refbackend" config to
"db". Older versions of git will not understand format "1"
and bail. Versions of git which understand "1" but do not
know about "refbackend", or which know about "refbackend"
but not about the "db" backend, will refuse to run. This is
annoying, of course, but much better than the alternative of
claiming that there are no refs in the repository, or
writing to a location that other implementations will not
read.
Note that we are only defining the rules for format 1 here.
We do not ever write format 1 ourselves; it is a tool that
is meant to be used by users and future extensions to
provide safety with older implementations.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-06-23 10:53:58 +00:00
|
|
|
else if (skip_prefix(var, "extensions.", &ext)) {
|
2020-07-16 12:25:13 +00:00
|
|
|
switch (handle_extension_v0(var, value, ext, data)) {
|
|
|
|
case EXTENSION_ERROR:
|
|
|
|
return -1;
|
|
|
|
case EXTENSION_OK:
|
|
|
|
return 0;
|
|
|
|
case EXTENSION_UNKNOWN:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (handle_extension(var, value, ext, data)) {
|
|
|
|
case EXTENSION_ERROR:
|
|
|
|
return -1;
|
|
|
|
case EXTENSION_OK:
|
|
|
|
string_list_append(&data->v1_only_extensions, ext);
|
|
|
|
return 0;
|
|
|
|
case EXTENSION_UNKNOWN:
|
2016-03-11 22:37:07 +00:00
|
|
|
string_list_append(&data->unknown_extensions, ext);
|
2020-07-16 12:25:13 +00:00
|
|
|
return 0;
|
|
|
|
}
|
introduce "extensions" form of core.repositoryformatversion
Normally we try to avoid bumps of the whole-repository
core.repositoryformatversion field. However, it is
unavoidable if we want to safely change certain aspects of
git in a backwards-incompatible way (e.g., modifying the set
of ref tips that we must traverse to generate a list of
unreachable, safe-to-prune objects).
If we were to bump the repository version for every such
change, then any implementation understanding version `X`
would also have to understand `X-1`, `X-2`, and so forth,
even though the incompatibilities may be in orthogonal parts
of the system, and there is otherwise no reason we cannot
implement one without the other (or more importantly, that
the user cannot choose to use one feature without the other,
weighing the tradeoff in compatibility only for that
particular feature).
This patch documents the existing repositoryformatversion
strategy and introduces a new format, "1", which lets a
repository specify that it must run with an arbitrary set of
extensions. This can be used, for example:
- to inform git that the objects should not be pruned based
only on the reachability of the ref tips (e.g, because it
has "clone --shared" children)
- that the refs are stored in a format besides the usual
"refs" and "packed-refs" directories
Because we bump to format "1", and because format "1"
requires that a running git knows about any extensions
mentioned, we know that older versions of the code will not
do something dangerous when confronted with these new
formats.
For example, if the user chooses to use database storage for
refs, they may set the "extensions.refbackend" config to
"db". Older versions of git will not understand format "1"
and bail. Versions of git which understand "1" but do not
know about "refbackend", or which know about "refbackend"
but not about the "db" backend, will refuse to run. This is
annoying, of course, but much better than the alternative of
claiming that there are no refs in the repository, or
writing to a location that other implementations will not
read.
Note that we are only defining the rules for format 1 here.
We do not ever write format 1 ourselves; it is a tool that
is meant to be used by users and future extensions to
provide safety with older implementations.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-06-23 10:53:58 +00:00
|
|
|
}
|
2018-10-21 14:02:28 +00:00
|
|
|
|
|
|
|
return read_worktree_config(var, value, vdata);
|
2014-11-30 08:24:44 +00:00
|
|
|
}
|
|
|
|
|
2017-11-12 21:28:51 +00:00
|
|
|
static int check_repository_format_gently(const char *gitdir, struct repository_format *candidate, int *nongit_ok)
|
2007-12-05 13:33:32 +00:00
|
|
|
{
|
2014-11-30 08:24:42 +00:00
|
|
|
struct strbuf sb = STRBUF_INIT;
|
2016-03-11 22:37:07 +00:00
|
|
|
struct strbuf err = STRBUF_INIT;
|
2016-03-11 22:37:14 +00:00
|
|
|
int has_common;
|
introduce "extensions" form of core.repositoryformatversion
Normally we try to avoid bumps of the whole-repository
core.repositoryformatversion field. However, it is
unavoidable if we want to safely change certain aspects of
git in a backwards-incompatible way (e.g., modifying the set
of ref tips that we must traverse to generate a list of
unreachable, safe-to-prune objects).
If we were to bump the repository version for every such
change, then any implementation understanding version `X`
would also have to understand `X-1`, `X-2`, and so forth,
even though the incompatibilities may be in orthogonal parts
of the system, and there is otherwise no reason we cannot
implement one without the other (or more importantly, that
the user cannot choose to use one feature without the other,
weighing the tradeoff in compatibility only for that
particular feature).
This patch documents the existing repositoryformatversion
strategy and introduces a new format, "1", which lets a
repository specify that it must run with an arbitrary set of
extensions. This can be used, for example:
- to inform git that the objects should not be pruned based
only on the reachability of the ref tips (e.g, because it
has "clone --shared" children)
- that the refs are stored in a format besides the usual
"refs" and "packed-refs" directories
Because we bump to format "1", and because format "1"
requires that a running git knows about any extensions
mentioned, we know that older versions of the code will not
do something dangerous when confronted with these new
formats.
For example, if the user chooses to use database storage for
refs, they may set the "extensions.refbackend" config to
"db". Older versions of git will not understand format "1"
and bail. Versions of git which understand "1" but do not
know about "refbackend", or which know about "refbackend"
but not about the "db" backend, will refuse to run. This is
annoying, of course, but much better than the alternative of
claiming that there are no refs in the repository, or
writing to a location that other implementations will not
read.
Note that we are only defining the rules for format 1 here.
We do not ever write format 1 ourselves; it is a tool that
is meant to be used by users and future extensions to
provide safety with older implementations.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-06-23 10:53:58 +00:00
|
|
|
|
2016-03-11 22:37:14 +00:00
|
|
|
has_common = get_common_dir(&sb, gitdir);
|
2014-11-30 08:24:43 +00:00
|
|
|
strbuf_addstr(&sb, "/config");
|
2017-11-12 21:28:51 +00:00
|
|
|
read_repository_format(candidate, sb.buf);
|
2016-03-11 22:37:07 +00:00
|
|
|
strbuf_release(&sb);
|
2014-11-30 08:24:43 +00:00
|
|
|
|
2010-11-26 15:32:34 +00:00
|
|
|
/*
|
2016-03-11 22:37:07 +00:00
|
|
|
* For historical use of check_repository_format() in git-init,
|
|
|
|
* we treat a missing config as a silent "ok", even when nongit_ok
|
|
|
|
* is unset.
|
2010-11-26 15:32:34 +00:00
|
|
|
*/
|
2017-11-12 21:28:51 +00:00
|
|
|
if (candidate->version < 0)
|
2016-03-11 22:37:07 +00:00
|
|
|
return 0;
|
|
|
|
|
2017-11-12 21:28:51 +00:00
|
|
|
if (verify_repository_format(candidate, &err) < 0) {
|
2016-03-11 22:37:07 +00:00
|
|
|
if (nongit_ok) {
|
|
|
|
warning("%s", err.buf);
|
|
|
|
strbuf_release(&err);
|
|
|
|
*nongit_ok = -1;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
die("%s", err.buf);
|
|
|
|
}
|
|
|
|
|
Revert "check_repository_format_gently(): refuse extensions for old repositories"
This reverts commit 14c7fa269e42df4133edd9ae7763b678ed6594cd.
The core.repositoryFormatVersion field was introduced in ab9cb76f661
(Repository format version check., 2005-11-25), providing a welcome
bit of forward compatibility, thanks to some welcome analysis by
Martin Atukunda. The semantics are simple: a repository with
core.repositoryFormatVersion set to 0 should be comprehensible by all
Git implementations in active use; and Git implementations should
error out early instead of trying to act on Git repositories with
higher core.repositoryFormatVersion values representing new formats
that they do not understand.
A new repository format did not need to be defined until 00a09d57eb8
(introduce "extensions" form of core.repositoryformatversion,
2015-06-23). This provided a finer-grained extension mechanism for
Git repositories. In a repository with core.repositoryFormatVersion
set to 1, Git implementations can act on "extensions.*" settings that
modify how a repository is interpreted. In repository format version
1, unrecognized extensions settings cause Git to error out.
What happens if a user sets an extension setting but forgets to
increase the repository format version to 1? The extension settings
were still recognized in that case; worse, unrecognized extensions
settings do *not* cause Git to error out. So combining repository
format version 0 with extensions settings produces in some sense the
worst of both worlds.
To improve that situation, since 14c7fa269e4
(check_repository_format_gently(): refuse extensions for old
repositories, 2020-06-05) Git instead ignores extensions in v0 mode.
This way, v0 repositories get the historical (pre-2015) behavior and
maintain compatibility with Git implementations that do not know about
the v1 format. Unfortunately, users had been using this sort of
configuration and this behavior change came to many as a surprise:
- users of "git config --worktree" that had followed its advice
to enable extensions.worktreeConfig (without also increasing the
repository format version) would find their worktree configuration
no longer taking effect
- tools such as copybara[*] that had set extensions.partialClone in
existing repositories (without also increasing the repository format
version) would find that setting no longer taking effect
The behavior introduced in 14c7fa269e4 might be a good behavior if we
were traveling back in time to 2015, but we're far too late. For some
reason I thought that it was what had been originally implemented and
that it had regressed. Apologies for not doing my research when
14c7fa269e4 was under development.
Let's return to the behavior we've had since 2015: always act on
extensions.* settings, regardless of repository format version. While
we're here, include some tests to describe the effect on the "upgrade
repository version" code path.
[*] https://github.com/google/copybara/commit/ca76c0b1e13c4e36448d12c2aba4a5d9d98fb6e7
Reported-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-07-16 06:24:29 +00:00
|
|
|
repository_format_precious_objects = candidate->precious_objects;
|
|
|
|
repository_format_worktree_config = candidate->worktree_config;
|
2017-11-12 21:28:51 +00:00
|
|
|
string_list_clear(&candidate->unknown_extensions, 0);
|
2020-07-16 12:25:13 +00:00
|
|
|
string_list_clear(&candidate->v1_only_extensions, 0);
|
2018-10-21 14:02:28 +00:00
|
|
|
|
|
|
|
if (repository_format_worktree_config) {
|
|
|
|
/*
|
|
|
|
* pick up core.bare and core.worktree from per-worktree
|
|
|
|
* config if present
|
|
|
|
*/
|
|
|
|
strbuf_addf(&sb, "%s/config.worktree", gitdir);
|
|
|
|
git_config_from_file(read_worktree_config, sb.buf, candidate);
|
|
|
|
strbuf_release(&sb);
|
|
|
|
has_common = 0;
|
|
|
|
}
|
|
|
|
|
2016-03-11 22:37:14 +00:00
|
|
|
if (!has_common) {
|
2017-11-12 21:28:51 +00:00
|
|
|
if (candidate->is_bare != -1) {
|
|
|
|
is_bare_repository_cfg = candidate->is_bare;
|
2016-03-11 22:37:14 +00:00
|
|
|
if (is_bare_repository_cfg == 1)
|
|
|
|
inside_work_tree = -1;
|
|
|
|
}
|
2017-11-12 21:28:51 +00:00
|
|
|
if (candidate->work_tree) {
|
2016-03-11 22:37:14 +00:00
|
|
|
free(git_work_tree_cfg);
|
setup: fix memory leaks with `struct repository_format`
After we set up a `struct repository_format`, it owns various pieces of
allocated memory. We then either use those members, because we decide we
want to use the "candidate" repository format, or we discard the
candidate / scratch space. In the first case, we transfer ownership of
the memory to a few global variables. In the latter case, we just
silently drop the struct and end up leaking memory.
Introduce an initialization macro `REPOSITORY_FORMAT_INIT` and a
function `clear_repository_format()`, to be used on each side of
`read_repository_format()`. To have a clear and simple memory ownership,
let all users of `struct repository_format` duplicate the strings that
they take from it, rather than stealing the pointers.
Call `clear_...()` at the start of `read_...()` instead of just zeroing
the struct, since we sometimes enter the function multiple times. Thus,
it is important to initialize the struct before calling `read_...()`, so
document that. It's also important because we might not even call
`read_...()` before we call `clear_...()`, see, e.g., builtin/init-db.c.
Teach `read_...()` to clear the struct on error, so that it is reset to
a safe state, and document this. (In `setup_git_directory_gently()`, we
look at `repo_fmt.hash_algo` even if `repo_fmt.version` is -1, which we
weren't actually supposed to do per the API. After this commit, that's
ok.)
We inherit the existing code's combining "error" and "no version found".
Both are signalled through `version == -1` and now both cause us to
clear any partial configuration we have picked up. For "extensions.*",
that's fine, since they require a positive version number. For
"core.bare" and "core.worktree", we're already verifying that we have a
non-negative version number before using them.
Signed-off-by: Martin Ågren <martin.agren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-02-28 20:36:28 +00:00
|
|
|
git_work_tree_cfg = xstrdup(candidate->work_tree);
|
2016-03-11 22:37:07 +00:00
|
|
|
inside_work_tree = -1;
|
2016-03-11 22:37:14 +00:00
|
|
|
}
|
2016-03-11 22:37:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-06-05 09:10:01 +00:00
|
|
|
int upgrade_repository_format(int target_version)
|
|
|
|
{
|
|
|
|
struct strbuf sb = STRBUF_INIT;
|
|
|
|
struct strbuf err = STRBUF_INIT;
|
|
|
|
struct strbuf repo_version = STRBUF_INIT;
|
|
|
|
struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
|
|
|
|
|
|
|
|
strbuf_git_common_path(&sb, the_repository, "config");
|
|
|
|
read_repository_format(&repo_fmt, sb.buf);
|
|
|
|
strbuf_release(&sb);
|
|
|
|
|
|
|
|
if (repo_fmt.version >= target_version)
|
|
|
|
return 0;
|
|
|
|
|
2020-07-16 06:28:18 +00:00
|
|
|
if (verify_repository_format(&repo_fmt, &err) < 0) {
|
|
|
|
error("cannot upgrade repository format from %d to %d: %s",
|
|
|
|
repo_fmt.version, target_version, err.buf);
|
2020-06-05 09:10:01 +00:00
|
|
|
strbuf_release(&err);
|
|
|
|
return -1;
|
|
|
|
}
|
2020-07-16 06:28:18 +00:00
|
|
|
if (!repo_fmt.version && repo_fmt.unknown_extensions.nr)
|
|
|
|
return error("cannot upgrade repository format: "
|
|
|
|
"unknown extension %s",
|
|
|
|
repo_fmt.unknown_extensions.items[0].string);
|
2020-06-05 09:10:01 +00:00
|
|
|
|
|
|
|
strbuf_addf(&repo_version, "%d", target_version);
|
|
|
|
git_config_set("core.repositoryformatversion", repo_version.buf);
|
|
|
|
strbuf_release(&repo_version);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
setup: fix memory leaks with `struct repository_format`
After we set up a `struct repository_format`, it owns various pieces of
allocated memory. We then either use those members, because we decide we
want to use the "candidate" repository format, or we discard the
candidate / scratch space. In the first case, we transfer ownership of
the memory to a few global variables. In the latter case, we just
silently drop the struct and end up leaking memory.
Introduce an initialization macro `REPOSITORY_FORMAT_INIT` and a
function `clear_repository_format()`, to be used on each side of
`read_repository_format()`. To have a clear and simple memory ownership,
let all users of `struct repository_format` duplicate the strings that
they take from it, rather than stealing the pointers.
Call `clear_...()` at the start of `read_...()` instead of just zeroing
the struct, since we sometimes enter the function multiple times. Thus,
it is important to initialize the struct before calling `read_...()`, so
document that. It's also important because we might not even call
`read_...()` before we call `clear_...()`, see, e.g., builtin/init-db.c.
Teach `read_...()` to clear the struct on error, so that it is reset to
a safe state, and document this. (In `setup_git_directory_gently()`, we
look at `repo_fmt.hash_algo` even if `repo_fmt.version` is -1, which we
weren't actually supposed to do per the API. After this commit, that's
ok.)
We inherit the existing code's combining "error" and "no version found".
Both are signalled through `version == -1` and now both cause us to
clear any partial configuration we have picked up. For "extensions.*",
that's fine, since they require a positive version number. For
"core.bare" and "core.worktree", we're already verifying that we have a
non-negative version number before using them.
Signed-off-by: Martin Ågren <martin.agren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-02-28 20:36:28 +00:00
|
|
|
static void init_repository_format(struct repository_format *format)
|
|
|
|
{
|
|
|
|
const struct repository_format fresh = REPOSITORY_FORMAT_INIT;
|
|
|
|
|
|
|
|
memcpy(format, &fresh, sizeof(fresh));
|
|
|
|
}
|
|
|
|
|
2016-03-11 22:37:14 +00:00
|
|
|
int read_repository_format(struct repository_format *format, const char *path)
|
2016-03-11 22:37:07 +00:00
|
|
|
{
|
setup: fix memory leaks with `struct repository_format`
After we set up a `struct repository_format`, it owns various pieces of
allocated memory. We then either use those members, because we decide we
want to use the "candidate" repository format, or we discard the
candidate / scratch space. In the first case, we transfer ownership of
the memory to a few global variables. In the latter case, we just
silently drop the struct and end up leaking memory.
Introduce an initialization macro `REPOSITORY_FORMAT_INIT` and a
function `clear_repository_format()`, to be used on each side of
`read_repository_format()`. To have a clear and simple memory ownership,
let all users of `struct repository_format` duplicate the strings that
they take from it, rather than stealing the pointers.
Call `clear_...()` at the start of `read_...()` instead of just zeroing
the struct, since we sometimes enter the function multiple times. Thus,
it is important to initialize the struct before calling `read_...()`, so
document that. It's also important because we might not even call
`read_...()` before we call `clear_...()`, see, e.g., builtin/init-db.c.
Teach `read_...()` to clear the struct on error, so that it is reset to
a safe state, and document this. (In `setup_git_directory_gently()`, we
look at `repo_fmt.hash_algo` even if `repo_fmt.version` is -1, which we
weren't actually supposed to do per the API. After this commit, that's
ok.)
We inherit the existing code's combining "error" and "no version found".
Both are signalled through `version == -1` and now both cause us to
clear any partial configuration we have picked up. For "extensions.*",
that's fine, since they require a positive version number. For
"core.bare" and "core.worktree", we're already verifying that we have a
non-negative version number before using them.
Signed-off-by: Martin Ågren <martin.agren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-02-28 20:36:28 +00:00
|
|
|
clear_repository_format(format);
|
2016-03-11 22:37:14 +00:00
|
|
|
git_config_from_file(check_repo_format, path, format);
|
setup: fix memory leaks with `struct repository_format`
After we set up a `struct repository_format`, it owns various pieces of
allocated memory. We then either use those members, because we decide we
want to use the "candidate" repository format, or we discard the
candidate / scratch space. In the first case, we transfer ownership of
the memory to a few global variables. In the latter case, we just
silently drop the struct and end up leaking memory.
Introduce an initialization macro `REPOSITORY_FORMAT_INIT` and a
function `clear_repository_format()`, to be used on each side of
`read_repository_format()`. To have a clear and simple memory ownership,
let all users of `struct repository_format` duplicate the strings that
they take from it, rather than stealing the pointers.
Call `clear_...()` at the start of `read_...()` instead of just zeroing
the struct, since we sometimes enter the function multiple times. Thus,
it is important to initialize the struct before calling `read_...()`, so
document that. It's also important because we might not even call
`read_...()` before we call `clear_...()`, see, e.g., builtin/init-db.c.
Teach `read_...()` to clear the struct on error, so that it is reset to
a safe state, and document this. (In `setup_git_directory_gently()`, we
look at `repo_fmt.hash_algo` even if `repo_fmt.version` is -1, which we
weren't actually supposed to do per the API. After this commit, that's
ok.)
We inherit the existing code's combining "error" and "no version found".
Both are signalled through `version == -1` and now both cause us to
clear any partial configuration we have picked up. For "extensions.*",
that's fine, since they require a positive version number. For
"core.bare" and "core.worktree", we're already verifying that we have a
non-negative version number before using them.
Signed-off-by: Martin Ågren <martin.agren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-02-28 20:36:28 +00:00
|
|
|
if (format->version == -1)
|
|
|
|
clear_repository_format(format);
|
2016-03-11 22:37:07 +00:00
|
|
|
return format->version;
|
|
|
|
}
|
|
|
|
|
setup: fix memory leaks with `struct repository_format`
After we set up a `struct repository_format`, it owns various pieces of
allocated memory. We then either use those members, because we decide we
want to use the "candidate" repository format, or we discard the
candidate / scratch space. In the first case, we transfer ownership of
the memory to a few global variables. In the latter case, we just
silently drop the struct and end up leaking memory.
Introduce an initialization macro `REPOSITORY_FORMAT_INIT` and a
function `clear_repository_format()`, to be used on each side of
`read_repository_format()`. To have a clear and simple memory ownership,
let all users of `struct repository_format` duplicate the strings that
they take from it, rather than stealing the pointers.
Call `clear_...()` at the start of `read_...()` instead of just zeroing
the struct, since we sometimes enter the function multiple times. Thus,
it is important to initialize the struct before calling `read_...()`, so
document that. It's also important because we might not even call
`read_...()` before we call `clear_...()`, see, e.g., builtin/init-db.c.
Teach `read_...()` to clear the struct on error, so that it is reset to
a safe state, and document this. (In `setup_git_directory_gently()`, we
look at `repo_fmt.hash_algo` even if `repo_fmt.version` is -1, which we
weren't actually supposed to do per the API. After this commit, that's
ok.)
We inherit the existing code's combining "error" and "no version found".
Both are signalled through `version == -1` and now both cause us to
clear any partial configuration we have picked up. For "extensions.*",
that's fine, since they require a positive version number. For
"core.bare" and "core.worktree", we're already verifying that we have a
non-negative version number before using them.
Signed-off-by: Martin Ågren <martin.agren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-02-28 20:36:28 +00:00
|
|
|
void clear_repository_format(struct repository_format *format)
|
|
|
|
{
|
|
|
|
string_list_clear(&format->unknown_extensions, 0);
|
2020-07-16 12:25:13 +00:00
|
|
|
string_list_clear(&format->v1_only_extensions, 0);
|
setup: fix memory leaks with `struct repository_format`
After we set up a `struct repository_format`, it owns various pieces of
allocated memory. We then either use those members, because we decide we
want to use the "candidate" repository format, or we discard the
candidate / scratch space. In the first case, we transfer ownership of
the memory to a few global variables. In the latter case, we just
silently drop the struct and end up leaking memory.
Introduce an initialization macro `REPOSITORY_FORMAT_INIT` and a
function `clear_repository_format()`, to be used on each side of
`read_repository_format()`. To have a clear and simple memory ownership,
let all users of `struct repository_format` duplicate the strings that
they take from it, rather than stealing the pointers.
Call `clear_...()` at the start of `read_...()` instead of just zeroing
the struct, since we sometimes enter the function multiple times. Thus,
it is important to initialize the struct before calling `read_...()`, so
document that. It's also important because we might not even call
`read_...()` before we call `clear_...()`, see, e.g., builtin/init-db.c.
Teach `read_...()` to clear the struct on error, so that it is reset to
a safe state, and document this. (In `setup_git_directory_gently()`, we
look at `repo_fmt.hash_algo` even if `repo_fmt.version` is -1, which we
weren't actually supposed to do per the API. After this commit, that's
ok.)
We inherit the existing code's combining "error" and "no version found".
Both are signalled through `version == -1` and now both cause us to
clear any partial configuration we have picked up. For "extensions.*",
that's fine, since they require a positive version number. For
"core.bare" and "core.worktree", we're already verifying that we have a
non-negative version number before using them.
Signed-off-by: Martin Ågren <martin.agren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-02-28 20:36:28 +00:00
|
|
|
free(format->work_tree);
|
|
|
|
free(format->partial_clone);
|
|
|
|
init_repository_format(format);
|
|
|
|
}
|
|
|
|
|
2016-03-11 22:37:07 +00:00
|
|
|
int verify_repository_format(const struct repository_format *format,
|
|
|
|
struct strbuf *err)
|
|
|
|
{
|
|
|
|
if (GIT_REPO_VERSION_READ < format->version) {
|
2016-03-11 22:37:22 +00:00
|
|
|
strbuf_addf(err, _("Expected git repo version <= %d, found %d"),
|
2016-03-11 22:37:07 +00:00
|
|
|
GIT_REPO_VERSION_READ, format->version);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (format->version >= 1 && format->unknown_extensions.nr) {
|
introduce "extensions" form of core.repositoryformatversion
Normally we try to avoid bumps of the whole-repository
core.repositoryformatversion field. However, it is
unavoidable if we want to safely change certain aspects of
git in a backwards-incompatible way (e.g., modifying the set
of ref tips that we must traverse to generate a list of
unreachable, safe-to-prune objects).
If we were to bump the repository version for every such
change, then any implementation understanding version `X`
would also have to understand `X-1`, `X-2`, and so forth,
even though the incompatibilities may be in orthogonal parts
of the system, and there is otherwise no reason we cannot
implement one without the other (or more importantly, that
the user cannot choose to use one feature without the other,
weighing the tradeoff in compatibility only for that
particular feature).
This patch documents the existing repositoryformatversion
strategy and introduces a new format, "1", which lets a
repository specify that it must run with an arbitrary set of
extensions. This can be used, for example:
- to inform git that the objects should not be pruned based
only on the reachability of the ref tips (e.g, because it
has "clone --shared" children)
- that the refs are stored in a format besides the usual
"refs" and "packed-refs" directories
Because we bump to format "1", and because format "1"
requires that a running git knows about any extensions
mentioned, we know that older versions of the code will not
do something dangerous when confronted with these new
formats.
For example, if the user chooses to use database storage for
refs, they may set the "extensions.refbackend" config to
"db". Older versions of git will not understand format "1"
and bail. Versions of git which understand "1" but do not
know about "refbackend", or which know about "refbackend"
but not about the "db" backend, will refuse to run. This is
annoying, of course, but much better than the alternative of
claiming that there are no refs in the repository, or
writing to a location that other implementations will not
read.
Note that we are only defining the rules for format 1 here.
We do not ever write format 1 ourselves; it is a tool that
is meant to be used by users and future extensions to
provide safety with older implementations.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-06-23 10:53:58 +00:00
|
|
|
int i;
|
|
|
|
|
2021-05-18 06:19:17 +00:00
|
|
|
strbuf_addstr(err, Q_("unknown repository extension found:",
|
|
|
|
"unknown repository extensions found:",
|
|
|
|
format->unknown_extensions.nr));
|
introduce "extensions" form of core.repositoryformatversion
Normally we try to avoid bumps of the whole-repository
core.repositoryformatversion field. However, it is
unavoidable if we want to safely change certain aspects of
git in a backwards-incompatible way (e.g., modifying the set
of ref tips that we must traverse to generate a list of
unreachable, safe-to-prune objects).
If we were to bump the repository version for every such
change, then any implementation understanding version `X`
would also have to understand `X-1`, `X-2`, and so forth,
even though the incompatibilities may be in orthogonal parts
of the system, and there is otherwise no reason we cannot
implement one without the other (or more importantly, that
the user cannot choose to use one feature without the other,
weighing the tradeoff in compatibility only for that
particular feature).
This patch documents the existing repositoryformatversion
strategy and introduces a new format, "1", which lets a
repository specify that it must run with an arbitrary set of
extensions. This can be used, for example:
- to inform git that the objects should not be pruned based
only on the reachability of the ref tips (e.g, because it
has "clone --shared" children)
- that the refs are stored in a format besides the usual
"refs" and "packed-refs" directories
Because we bump to format "1", and because format "1"
requires that a running git knows about any extensions
mentioned, we know that older versions of the code will not
do something dangerous when confronted with these new
formats.
For example, if the user chooses to use database storage for
refs, they may set the "extensions.refbackend" config to
"db". Older versions of git will not understand format "1"
and bail. Versions of git which understand "1" but do not
know about "refbackend", or which know about "refbackend"
but not about the "db" backend, will refuse to run. This is
annoying, of course, but much better than the alternative of
claiming that there are no refs in the repository, or
writing to a location that other implementations will not
read.
Note that we are only defining the rules for format 1 here.
We do not ever write format 1 ourselves; it is a tool that
is meant to be used by users and future extensions to
provide safety with older implementations.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-06-23 10:53:58 +00:00
|
|
|
|
2016-03-11 22:37:07 +00:00
|
|
|
for (i = 0; i < format->unknown_extensions.nr; i++)
|
|
|
|
strbuf_addf(err, "\n\t%s",
|
|
|
|
format->unknown_extensions.items[i].string);
|
|
|
|
return -1;
|
introduce "extensions" form of core.repositoryformatversion
Normally we try to avoid bumps of the whole-repository
core.repositoryformatversion field. However, it is
unavoidable if we want to safely change certain aspects of
git in a backwards-incompatible way (e.g., modifying the set
of ref tips that we must traverse to generate a list of
unreachable, safe-to-prune objects).
If we were to bump the repository version for every such
change, then any implementation understanding version `X`
would also have to understand `X-1`, `X-2`, and so forth,
even though the incompatibilities may be in orthogonal parts
of the system, and there is otherwise no reason we cannot
implement one without the other (or more importantly, that
the user cannot choose to use one feature without the other,
weighing the tradeoff in compatibility only for that
particular feature).
This patch documents the existing repositoryformatversion
strategy and introduces a new format, "1", which lets a
repository specify that it must run with an arbitrary set of
extensions. This can be used, for example:
- to inform git that the objects should not be pruned based
only on the reachability of the ref tips (e.g, because it
has "clone --shared" children)
- that the refs are stored in a format besides the usual
"refs" and "packed-refs" directories
Because we bump to format "1", and because format "1"
requires that a running git knows about any extensions
mentioned, we know that older versions of the code will not
do something dangerous when confronted with these new
formats.
For example, if the user chooses to use database storage for
refs, they may set the "extensions.refbackend" config to
"db". Older versions of git will not understand format "1"
and bail. Versions of git which understand "1" but do not
know about "refbackend", or which know about "refbackend"
but not about the "db" backend, will refuse to run. This is
annoying, of course, but much better than the alternative of
claiming that there are no refs in the repository, or
writing to a location that other implementations will not
read.
Note that we are only defining the rules for format 1 here.
We do not ever write format 1 ourselves; it is a tool that
is meant to be used by users and future extensions to
provide safety with older implementations.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-06-23 10:53:58 +00:00
|
|
|
}
|
|
|
|
|
2020-07-16 12:25:13 +00:00
|
|
|
if (format->version == 0 && format->v1_only_extensions.nr) {
|
|
|
|
int i;
|
|
|
|
|
|
|
|
strbuf_addstr(err,
|
2021-05-18 06:19:17 +00:00
|
|
|
Q_("repo version is 0, but v1-only extension found:",
|
|
|
|
"repo version is 0, but v1-only extensions found:",
|
|
|
|
format->v1_only_extensions.nr));
|
2020-07-16 12:25:13 +00:00
|
|
|
|
|
|
|
for (i = 0; i < format->v1_only_extensions.nr; i++)
|
|
|
|
strbuf_addf(err, "\n\t%s",
|
|
|
|
format->v1_only_extensions.items[i].string);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2016-03-11 22:37:07 +00:00
|
|
|
return 0;
|
2007-12-05 13:33:32 +00:00
|
|
|
}
|
|
|
|
|
2017-01-24 23:56:50 +00:00
|
|
|
void read_gitfile_error_die(int error_code, const char *path, const char *dir)
|
|
|
|
{
|
|
|
|
switch (error_code) {
|
|
|
|
case READ_GITFILE_ERR_STAT_FAILED:
|
|
|
|
case READ_GITFILE_ERR_NOT_A_FILE:
|
|
|
|
/* non-fatal; follow return path */
|
|
|
|
break;
|
|
|
|
case READ_GITFILE_ERR_OPEN_FAILED:
|
2018-02-13 13:19:15 +00:00
|
|
|
die_errno(_("error opening '%s'"), path);
|
2017-01-24 23:56:50 +00:00
|
|
|
case READ_GITFILE_ERR_TOO_LARGE:
|
2018-02-13 13:19:15 +00:00
|
|
|
die(_("too large to be a .git file: '%s'"), path);
|
2017-01-24 23:56:50 +00:00
|
|
|
case READ_GITFILE_ERR_READ_FAILED:
|
2018-02-13 13:19:15 +00:00
|
|
|
die(_("error reading %s"), path);
|
2017-01-24 23:56:50 +00:00
|
|
|
case READ_GITFILE_ERR_INVALID_FORMAT:
|
2018-02-13 13:19:15 +00:00
|
|
|
die(_("invalid gitfile format: %s"), path);
|
2017-01-24 23:56:50 +00:00
|
|
|
case READ_GITFILE_ERR_NO_PATH:
|
2018-02-13 13:19:15 +00:00
|
|
|
die(_("no path in gitfile: %s"), path);
|
2017-01-24 23:56:50 +00:00
|
|
|
case READ_GITFILE_ERR_NOT_A_REPO:
|
2018-02-13 13:19:15 +00:00
|
|
|
die(_("not a git repository: %s"), dir);
|
2017-01-24 23:56:50 +00:00
|
|
|
default:
|
2018-05-02 09:38:39 +00:00
|
|
|
BUG("unknown error code");
|
2017-01-24 23:56:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-02-20 22:13:13 +00:00
|
|
|
/*
|
|
|
|
* Try to read the location of the git directory from the .git file,
|
2017-09-26 11:21:49 +00:00
|
|
|
* return path to git directory if found. The return value comes from
|
|
|
|
* a shared buffer.
|
2015-06-09 18:24:35 +00:00
|
|
|
*
|
|
|
|
* On failure, if return_error_code is not NULL, return_error_code
|
|
|
|
* will be set to an error code and NULL will be returned. If
|
|
|
|
* return_error_code is NULL the function will die instead (for most
|
|
|
|
* cases).
|
2008-02-20 22:13:13 +00:00
|
|
|
*/
|
2015-06-09 18:24:35 +00:00
|
|
|
const char *read_gitfile_gently(const char *path, int *return_error_code)
|
2008-02-20 22:13:13 +00:00
|
|
|
{
|
2015-06-15 19:39:52 +00:00
|
|
|
const int max_file_size = 1 << 20; /* 1MB */
|
2015-06-09 18:24:35 +00:00
|
|
|
int error_code = 0;
|
|
|
|
char *buf = NULL;
|
|
|
|
char *dir = NULL;
|
2010-01-09 03:36:41 +00:00
|
|
|
const char *slash;
|
2008-02-20 22:13:13 +00:00
|
|
|
struct stat st;
|
|
|
|
int fd;
|
2011-05-26 16:28:44 +00:00
|
|
|
ssize_t len;
|
2020-03-10 13:11:22 +00:00
|
|
|
static struct strbuf realpath = STRBUF_INIT;
|
2008-02-20 22:13:13 +00:00
|
|
|
|
2015-06-09 18:24:35 +00:00
|
|
|
if (stat(path, &st)) {
|
2017-03-13 20:12:18 +00:00
|
|
|
/* NEEDSWORK: discern between ENOENT vs other errors */
|
2015-06-09 18:24:35 +00:00
|
|
|
error_code = READ_GITFILE_ERR_STAT_FAILED;
|
|
|
|
goto cleanup_return;
|
|
|
|
}
|
|
|
|
if (!S_ISREG(st.st_mode)) {
|
|
|
|
error_code = READ_GITFILE_ERR_NOT_A_FILE;
|
|
|
|
goto cleanup_return;
|
|
|
|
}
|
2015-06-15 19:39:52 +00:00
|
|
|
if (st.st_size > max_file_size) {
|
|
|
|
error_code = READ_GITFILE_ERR_TOO_LARGE;
|
|
|
|
goto cleanup_return;
|
|
|
|
}
|
2008-02-20 22:13:13 +00:00
|
|
|
fd = open(path, O_RDONLY);
|
2015-06-09 18:24:35 +00:00
|
|
|
if (fd < 0) {
|
|
|
|
error_code = READ_GITFILE_ERR_OPEN_FAILED;
|
|
|
|
goto cleanup_return;
|
|
|
|
}
|
2016-02-22 22:44:28 +00:00
|
|
|
buf = xmallocz(st.st_size);
|
2008-02-20 22:13:13 +00:00
|
|
|
len = read_in_full(fd, buf, st.st_size);
|
|
|
|
close(fd);
|
2015-06-09 18:24:35 +00:00
|
|
|
if (len != st.st_size) {
|
|
|
|
error_code = READ_GITFILE_ERR_READ_FAILED;
|
|
|
|
goto cleanup_return;
|
|
|
|
}
|
|
|
|
if (!starts_with(buf, "gitdir: ")) {
|
|
|
|
error_code = READ_GITFILE_ERR_INVALID_FORMAT;
|
|
|
|
goto cleanup_return;
|
|
|
|
}
|
2008-02-20 22:13:13 +00:00
|
|
|
while (buf[len - 1] == '\n' || buf[len - 1] == '\r')
|
|
|
|
len--;
|
2015-06-09 18:24:35 +00:00
|
|
|
if (len < 9) {
|
|
|
|
error_code = READ_GITFILE_ERR_NO_PATH;
|
|
|
|
goto cleanup_return;
|
|
|
|
}
|
2008-02-20 22:13:13 +00:00
|
|
|
buf[len] = '\0';
|
2010-01-09 03:36:41 +00:00
|
|
|
dir = buf + 8;
|
|
|
|
|
|
|
|
if (!is_absolute_path(dir) && (slash = strrchr(path, '/'))) {
|
|
|
|
size_t pathlen = slash+1 - path;
|
2015-09-24 21:07:03 +00:00
|
|
|
dir = xstrfmt("%.*s%.*s", (int)pathlen, path,
|
|
|
|
(int)(len - 8), buf + 8);
|
2010-01-09 03:36:41 +00:00
|
|
|
free(buf);
|
|
|
|
buf = dir;
|
|
|
|
}
|
2015-06-09 18:24:35 +00:00
|
|
|
if (!is_git_directory(dir)) {
|
|
|
|
error_code = READ_GITFILE_ERR_NOT_A_REPO;
|
|
|
|
goto cleanup_return;
|
|
|
|
}
|
2020-03-10 13:11:22 +00:00
|
|
|
|
|
|
|
strbuf_realpath(&realpath, dir, 1);
|
|
|
|
path = realpath.buf;
|
2010-01-09 03:36:41 +00:00
|
|
|
|
2015-06-09 18:24:35 +00:00
|
|
|
cleanup_return:
|
|
|
|
if (return_error_code)
|
|
|
|
*return_error_code = error_code;
|
2017-01-24 23:56:50 +00:00
|
|
|
else if (error_code)
|
|
|
|
read_gitfile_error_die(error_code, path, dir);
|
2015-06-09 18:24:35 +00:00
|
|
|
|
2008-02-20 22:13:13 +00:00
|
|
|
free(buf);
|
2015-06-26 09:03:31 +00:00
|
|
|
return error_code ? NULL : path;
|
2008-02-20 22:13:13 +00:00
|
|
|
}
|
|
|
|
|
2010-07-24 11:19:44 +00:00
|
|
|
static const char *setup_explicit_git_dir(const char *gitdirenv,
|
2014-07-28 18:26:40 +00:00
|
|
|
struct strbuf *cwd,
|
2017-11-12 21:28:51 +00:00
|
|
|
struct repository_format *repo_fmt,
|
2010-11-26 15:32:39 +00:00
|
|
|
int *nongit_ok)
|
2010-07-24 11:19:44 +00:00
|
|
|
{
|
2010-11-26 15:32:39 +00:00
|
|
|
const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);
|
|
|
|
const char *worktree;
|
|
|
|
char *gitfile;
|
2011-03-26 09:04:24 +00:00
|
|
|
int offset;
|
2010-07-24 11:19:44 +00:00
|
|
|
|
|
|
|
if (PATH_MAX - 40 < strlen(gitdirenv))
|
2018-02-13 13:19:15 +00:00
|
|
|
die(_("'$%s' too big"), GIT_DIR_ENVIRONMENT);
|
2010-11-26 15:32:39 +00:00
|
|
|
|
2011-08-22 21:04:56 +00:00
|
|
|
gitfile = (char*)read_gitfile(gitdirenv);
|
2010-11-26 15:32:39 +00:00
|
|
|
if (gitfile) {
|
|
|
|
gitfile = xstrdup(gitfile);
|
|
|
|
gitdirenv = gitfile;
|
|
|
|
}
|
|
|
|
|
2010-07-24 11:19:44 +00:00
|
|
|
if (!is_git_directory(gitdirenv)) {
|
|
|
|
if (nongit_ok) {
|
|
|
|
*nongit_ok = 1;
|
2010-11-26 15:32:39 +00:00
|
|
|
free(gitfile);
|
2010-07-24 11:19:44 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2018-02-13 13:19:15 +00:00
|
|
|
die(_("not a git repository: '%s'"), gitdirenv);
|
2010-07-24 11:19:44 +00:00
|
|
|
}
|
2010-11-26 15:32:39 +00:00
|
|
|
|
2017-11-12 21:28:51 +00:00
|
|
|
if (check_repository_format_gently(gitdirenv, repo_fmt, nongit_ok)) {
|
2010-11-26 15:32:39 +00:00
|
|
|
free(gitfile);
|
|
|
|
return NULL;
|
2010-07-24 11:19:44 +00:00
|
|
|
}
|
2010-11-26 15:32:39 +00:00
|
|
|
|
|
|
|
/* #3, #7, #11, #15, #19, #23, #27, #31 (see t1510) */
|
|
|
|
if (work_tree_env)
|
|
|
|
set_git_work_tree(work_tree_env);
|
|
|
|
else if (is_bare_repository_cfg > 0) {
|
setup_git_directory: delay core.bare/core.worktree errors
If both core.bare and core.worktree are set, we complain
about the bogus config and die. Dying is good, because it
avoids commands running and doing damage in a potentially
incorrect setup. But dying _there_ is bad, because it means
that commands which do not even care about the work tree
cannot run. This can make repairing the situation harder:
[setup]
$ git config core.bare true
$ git config core.worktree /some/path
[OK, expected.]
$ git status
fatal: core.bare and core.worktree do not make sense
[Hrm...]
$ git config --unset core.worktree
fatal: core.bare and core.worktree do not make sense
[Nope...]
$ git config --edit
fatal: core.bare and core.worktree do not make sense
[Gaaah.]
$ git help config
fatal: core.bare and core.worktree do not make sense
Instead, let's issue a warning about the bogus config when
we notice it (i.e., for all commands), but only die when the
command tries to use the work tree (by calling setup_work_tree).
So we now get:
$ git status
warning: core.bare and core.worktree do not make sense
fatal: unable to set up work tree using invalid config
$ git config --unset core.worktree
warning: core.bare and core.worktree do not make sense
We have to update t1510 to accomodate this; it uses
symbolic-ref to check whether the configuration works or
not, but of course that command does not use the working
tree. Instead, we switch it to use `git status`, as it
requires a work-tree, does not need any special setup, and
is read-only (so a failure will not adversely affect further
tests).
In addition, we add a new test that checks the desired
behavior (i.e., that running "git config" with the bogus
config does in fact work).
Reported-by: SZEDER Gábor <szeder@ira.uka.de>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-05-29 06:49:10 +00:00
|
|
|
if (git_work_tree_cfg) {
|
|
|
|
/* #22.2, #30 */
|
|
|
|
warning("core.bare and core.worktree do not make sense");
|
|
|
|
work_tree_config_is_bogus = 1;
|
|
|
|
}
|
2010-11-26 15:32:39 +00:00
|
|
|
|
|
|
|
/* #18, #26 */
|
2020-03-06 19:03:13 +00:00
|
|
|
set_git_dir(gitdirenv, 0);
|
2010-11-26 15:32:39 +00:00
|
|
|
free(gitfile);
|
2010-07-24 11:19:44 +00:00
|
|
|
return NULL;
|
2010-11-26 15:32:39 +00:00
|
|
|
}
|
|
|
|
else if (git_work_tree_cfg) { /* #6, #14 */
|
|
|
|
if (is_absolute_path(git_work_tree_cfg))
|
|
|
|
set_git_work_tree(git_work_tree_cfg);
|
|
|
|
else {
|
2014-07-28 18:30:39 +00:00
|
|
|
char *core_worktree;
|
2010-11-26 15:32:39 +00:00
|
|
|
if (chdir(gitdirenv))
|
2018-02-13 13:19:15 +00:00
|
|
|
die_errno(_("cannot chdir to '%s'"), gitdirenv);
|
2010-11-26 15:32:39 +00:00
|
|
|
if (chdir(git_work_tree_cfg))
|
2018-02-13 13:19:15 +00:00
|
|
|
die_errno(_("cannot chdir to '%s'"), git_work_tree_cfg);
|
2014-07-28 18:30:39 +00:00
|
|
|
core_worktree = xgetcwd();
|
2014-07-28 18:26:40 +00:00
|
|
|
if (chdir(cwd->buf))
|
2018-02-13 13:19:15 +00:00
|
|
|
die_errno(_("cannot come back to cwd"));
|
2010-11-26 15:32:39 +00:00
|
|
|
set_git_work_tree(core_worktree);
|
2014-07-28 18:30:39 +00:00
|
|
|
free(core_worktree);
|
2010-11-26 15:32:39 +00:00
|
|
|
}
|
|
|
|
}
|
setup: suppress implicit "." work-tree for bare repos
If an explicit GIT_DIR is given without a working tree, we
implicitly assume that the current working directory should
be used as the working tree. E.g.,:
GIT_DIR=/some/repo.git git status
would compare against the cwd.
Unfortunately, we fool this rule for sub-invocations of git
by setting GIT_DIR internally ourselves. For example:
git init foo
cd foo/.git
git status ;# fails, as we expect
git config alias.st status
git status ;# does not fail, but should
What happens is that we run setup_git_directory when doing
alias lookup (since we need to see the config), set GIT_DIR
as a result, and then leave GIT_WORK_TREE blank (because we
do not have one). Then when we actually run the status
command, we do setup_git_directory again, which sees our
explicit GIT_DIR and uses the cwd as an implicit worktree.
It's tempting to argue that we should be suppressing that
second invocation of setup_git_directory, as it could use
the values we already found in memory. However, the problem
still exists for sub-processes (e.g., if "git status" were
an external command).
You can see another example with the "--bare" option, which
sets GIT_DIR explicitly. For example:
git init foo
cd foo/.git
git status ;# fails
git --bare status ;# does NOT fail
We need some way of telling sub-processes "even though
GIT_DIR is set, do not use cwd as an implicit working tree".
We could do it by putting a special token into
GIT_WORK_TREE, but the obvious choice (an empty string) has
some portability problems.
Instead, we add a new boolean variable, GIT_IMPLICIT_WORK_TREE,
which suppresses the use of cwd as a working tree when
GIT_DIR is set. We trigger the new variable when we know we
are in a bare setting.
The variable is left intentionally undocumented, as this is
an internal detail (for now, anyway). If somebody comes up
with a good alternate use for it, and once we are confident
we have shaken any bugs out of it, we can consider promoting
it further.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-03-08 09:32:22 +00:00
|
|
|
else if (!git_env_bool(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, 1)) {
|
|
|
|
/* #16d */
|
2020-03-06 19:03:13 +00:00
|
|
|
set_git_dir(gitdirenv, 0);
|
setup: suppress implicit "." work-tree for bare repos
If an explicit GIT_DIR is given without a working tree, we
implicitly assume that the current working directory should
be used as the working tree. E.g.,:
GIT_DIR=/some/repo.git git status
would compare against the cwd.
Unfortunately, we fool this rule for sub-invocations of git
by setting GIT_DIR internally ourselves. For example:
git init foo
cd foo/.git
git status ;# fails, as we expect
git config alias.st status
git status ;# does not fail, but should
What happens is that we run setup_git_directory when doing
alias lookup (since we need to see the config), set GIT_DIR
as a result, and then leave GIT_WORK_TREE blank (because we
do not have one). Then when we actually run the status
command, we do setup_git_directory again, which sees our
explicit GIT_DIR and uses the cwd as an implicit worktree.
It's tempting to argue that we should be suppressing that
second invocation of setup_git_directory, as it could use
the values we already found in memory. However, the problem
still exists for sub-processes (e.g., if "git status" were
an external command).
You can see another example with the "--bare" option, which
sets GIT_DIR explicitly. For example:
git init foo
cd foo/.git
git status ;# fails
git --bare status ;# does NOT fail
We need some way of telling sub-processes "even though
GIT_DIR is set, do not use cwd as an implicit working tree".
We could do it by putting a special token into
GIT_WORK_TREE, but the obvious choice (an empty string) has
some portability problems.
Instead, we add a new boolean variable, GIT_IMPLICIT_WORK_TREE,
which suppresses the use of cwd as a working tree when
GIT_DIR is set. We trigger the new variable when we know we
are in a bare setting.
The variable is left intentionally undocumented, as this is
an internal detail (for now, anyway). If somebody comes up
with a good alternate use for it, and once we are confident
we have shaken any bugs out of it, we can consider promoting
it further.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-03-08 09:32:22 +00:00
|
|
|
free(gitfile);
|
|
|
|
return NULL;
|
|
|
|
}
|
2010-11-26 15:32:39 +00:00
|
|
|
else /* #2, #10 */
|
|
|
|
set_git_work_tree(".");
|
|
|
|
|
|
|
|
/* set_git_work_tree() must have been called by now */
|
|
|
|
worktree = get_git_work_tree();
|
|
|
|
|
|
|
|
/* both get_git_work_tree() and cwd are already normalized */
|
2014-07-28 18:26:40 +00:00
|
|
|
if (!strcmp(cwd->buf, worktree)) { /* cwd == worktree */
|
2020-03-06 19:03:13 +00:00
|
|
|
set_git_dir(gitdirenv, 0);
|
2010-11-26 15:32:39 +00:00
|
|
|
free(gitfile);
|
2010-07-24 11:19:44 +00:00
|
|
|
return NULL;
|
2010-11-26 15:32:39 +00:00
|
|
|
}
|
2010-07-24 11:19:44 +00:00
|
|
|
|
2014-07-28 18:26:40 +00:00
|
|
|
offset = dir_inside_of(cwd->buf, worktree);
|
2011-03-26 09:04:24 +00:00
|
|
|
if (offset >= 0) { /* cwd inside worktree? */
|
2020-03-06 19:03:13 +00:00
|
|
|
set_git_dir(gitdirenv, 1);
|
2010-11-26 15:32:39 +00:00
|
|
|
if (chdir(worktree))
|
2018-02-13 13:19:15 +00:00
|
|
|
die_errno(_("cannot chdir to '%s'"), worktree);
|
2014-07-28 18:26:40 +00:00
|
|
|
strbuf_addch(cwd, '/');
|
2010-11-26 15:32:39 +00:00
|
|
|
free(gitfile);
|
2014-07-28 18:26:40 +00:00
|
|
|
return cwd->buf + offset;
|
2010-07-24 11:20:15 +00:00
|
|
|
}
|
2010-11-26 15:32:39 +00:00
|
|
|
|
|
|
|
/* cwd outside worktree */
|
2020-03-06 19:03:13 +00:00
|
|
|
set_git_dir(gitdirenv, 0);
|
2010-11-26 15:32:39 +00:00
|
|
|
free(gitfile);
|
|
|
|
return NULL;
|
2010-07-24 11:20:15 +00:00
|
|
|
}
|
|
|
|
|
2010-11-26 15:32:38 +00:00
|
|
|
static const char *setup_discovered_git_dir(const char *gitdir,
|
2014-07-28 18:26:40 +00:00
|
|
|
struct strbuf *cwd, int offset,
|
2017-11-12 21:28:51 +00:00
|
|
|
struct repository_format *repo_fmt,
|
2010-11-26 15:32:38 +00:00
|
|
|
int *nongit_ok)
|
2010-07-24 12:11:58 +00:00
|
|
|
{
|
2017-11-12 21:28:51 +00:00
|
|
|
if (check_repository_format_gently(gitdir, repo_fmt, nongit_ok))
|
2010-11-26 15:32:38 +00:00
|
|
|
return NULL;
|
2010-07-24 12:11:58 +00:00
|
|
|
|
2011-01-19 12:42:30 +00:00
|
|
|
/* --work-tree is set without --git-dir; use discovered one */
|
|
|
|
if (getenv(GIT_WORK_TREE_ENVIRONMENT) || git_work_tree_cfg) {
|
2017-05-04 13:56:51 +00:00
|
|
|
char *to_free = NULL;
|
|
|
|
const char *ret;
|
|
|
|
|
2014-07-28 18:26:40 +00:00
|
|
|
if (offset != cwd->len && !is_absolute_path(gitdir))
|
2017-05-04 13:56:51 +00:00
|
|
|
gitdir = to_free = real_pathdup(gitdir, 1);
|
2014-07-28 18:26:40 +00:00
|
|
|
if (chdir(cwd->buf))
|
2018-02-13 13:19:15 +00:00
|
|
|
die_errno(_("cannot come back to cwd"));
|
2017-11-12 21:28:51 +00:00
|
|
|
ret = setup_explicit_git_dir(gitdir, cwd, repo_fmt, nongit_ok);
|
2017-05-04 13:56:51 +00:00
|
|
|
free(to_free);
|
|
|
|
return ret;
|
2011-01-19 12:42:30 +00:00
|
|
|
}
|
|
|
|
|
2010-11-26 15:32:38 +00:00
|
|
|
/* #16.2, #17.2, #20.2, #21.2, #24, #25, #28, #29 (see t1510) */
|
|
|
|
if (is_bare_repository_cfg > 0) {
|
2020-03-06 19:03:13 +00:00
|
|
|
set_git_dir(gitdir, (offset != cwd->len));
|
2014-07-28 18:26:40 +00:00
|
|
|
if (chdir(cwd->buf))
|
2018-02-13 13:19:15 +00:00
|
|
|
die_errno(_("cannot come back to cwd"));
|
2010-07-24 12:11:58 +00:00
|
|
|
return NULL;
|
2010-11-26 15:32:38 +00:00
|
|
|
}
|
2010-07-24 12:11:58 +00:00
|
|
|
|
2010-11-26 15:32:38 +00:00
|
|
|
/* #0, #1, #5, #8, #9, #12, #13 */
|
|
|
|
set_git_work_tree(".");
|
|
|
|
if (strcmp(gitdir, DEFAULT_GIT_DIR_ENVIRONMENT))
|
2020-03-06 19:03:13 +00:00
|
|
|
set_git_dir(gitdir, 0);
|
2010-07-24 12:11:58 +00:00
|
|
|
inside_git_dir = 0;
|
2010-11-26 15:32:38 +00:00
|
|
|
inside_work_tree = 1;
|
setup_git_directory(): handle UNC root paths correctly
When working in the root directory of a file share (this is only
possible in Git Bash and Powershell, but not in CMD), the current
directory is reported without a trailing slash.
This is different from Unix and standard Windows directories: both / and
C:\ are reported with a trailing slash as current directories.
If a Git worktree is located there, Git is not quite prepared for that:
while it does manage to find the .git directory/file, it returns as
length of the top-level directory's path *one more* than the length of
the current directory, and setup_git_directory_gently() would then
return an undefined string as prefix.
In practice, this undefined string usually points to NUL bytes, and does
not cause much harm. Under rare circumstances that are really involved
to reproduce (and not reliably so), the reported prefix could be a
suffix string of Git's exec path, though.
A careful analysis determined that this bug is unlikely to be
exploitable, therefore we mark this as a regular bug fix.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-08-24 22:10:46 +00:00
|
|
|
if (offset >= cwd->len)
|
2010-07-24 12:11:58 +00:00
|
|
|
return NULL;
|
|
|
|
|
2017-03-13 20:09:44 +00:00
|
|
|
/* Make "offset" point past the '/' (already the case for root dirs) */
|
|
|
|
if (offset != offset_1st_component(cwd->buf))
|
|
|
|
offset++;
|
|
|
|
/* Add a '/' at the end */
|
2014-07-28 18:26:40 +00:00
|
|
|
strbuf_addch(cwd, '/');
|
|
|
|
return cwd->buf + offset;
|
2010-07-24 12:11:58 +00:00
|
|
|
}
|
|
|
|
|
2010-11-26 15:32:36 +00:00
|
|
|
/* #16.1, #17.1, #20.1, #21.1, #22.1 (see t1510) */
|
2014-07-28 18:26:40 +00:00
|
|
|
static const char *setup_bare_git_dir(struct strbuf *cwd, int offset,
|
2017-11-12 21:28:51 +00:00
|
|
|
struct repository_format *repo_fmt,
|
2014-07-28 18:26:40 +00:00
|
|
|
int *nongit_ok)
|
2010-07-24 11:25:32 +00:00
|
|
|
{
|
|
|
|
int root_len;
|
|
|
|
|
2017-11-12 21:28:51 +00:00
|
|
|
if (check_repository_format_gently(".", repo_fmt, nongit_ok))
|
2010-11-26 15:32:36 +00:00
|
|
|
return NULL;
|
|
|
|
|
setup: suppress implicit "." work-tree for bare repos
If an explicit GIT_DIR is given without a working tree, we
implicitly assume that the current working directory should
be used as the working tree. E.g.,:
GIT_DIR=/some/repo.git git status
would compare against the cwd.
Unfortunately, we fool this rule for sub-invocations of git
by setting GIT_DIR internally ourselves. For example:
git init foo
cd foo/.git
git status ;# fails, as we expect
git config alias.st status
git status ;# does not fail, but should
What happens is that we run setup_git_directory when doing
alias lookup (since we need to see the config), set GIT_DIR
as a result, and then leave GIT_WORK_TREE blank (because we
do not have one). Then when we actually run the status
command, we do setup_git_directory again, which sees our
explicit GIT_DIR and uses the cwd as an implicit worktree.
It's tempting to argue that we should be suppressing that
second invocation of setup_git_directory, as it could use
the values we already found in memory. However, the problem
still exists for sub-processes (e.g., if "git status" were
an external command).
You can see another example with the "--bare" option, which
sets GIT_DIR explicitly. For example:
git init foo
cd foo/.git
git status ;# fails
git --bare status ;# does NOT fail
We need some way of telling sub-processes "even though
GIT_DIR is set, do not use cwd as an implicit working tree".
We could do it by putting a special token into
GIT_WORK_TREE, but the obvious choice (an empty string) has
some portability problems.
Instead, we add a new boolean variable, GIT_IMPLICIT_WORK_TREE,
which suppresses the use of cwd as a working tree when
GIT_DIR is set. We trigger the new variable when we know we
are in a bare setting.
The variable is left intentionally undocumented, as this is
an internal detail (for now, anyway). If somebody comes up
with a good alternate use for it, and once we are confident
we have shaken any bugs out of it, we can consider promoting
it further.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-03-08 09:32:22 +00:00
|
|
|
setenv(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, "0", 1);
|
|
|
|
|
2011-01-19 12:42:30 +00:00
|
|
|
/* --work-tree is set without --git-dir; use discovered one */
|
|
|
|
if (getenv(GIT_WORK_TREE_ENVIRONMENT) || git_work_tree_cfg) {
|
2017-05-04 13:56:47 +00:00
|
|
|
static const char *gitdir;
|
2011-01-19 12:42:30 +00:00
|
|
|
|
2014-07-28 18:26:40 +00:00
|
|
|
gitdir = offset == cwd->len ? "." : xmemdupz(cwd->buf, offset);
|
|
|
|
if (chdir(cwd->buf))
|
2018-02-13 13:19:15 +00:00
|
|
|
die_errno(_("cannot come back to cwd"));
|
2017-11-12 21:28:51 +00:00
|
|
|
return setup_explicit_git_dir(gitdir, cwd, repo_fmt, nongit_ok);
|
2011-01-19 12:42:30 +00:00
|
|
|
}
|
|
|
|
|
2010-07-24 11:25:32 +00:00
|
|
|
inside_git_dir = 1;
|
2010-11-26 15:32:36 +00:00
|
|
|
inside_work_tree = 0;
|
2014-07-28 18:26:40 +00:00
|
|
|
if (offset != cwd->len) {
|
|
|
|
if (chdir(cwd->buf))
|
2018-02-13 13:19:15 +00:00
|
|
|
die_errno(_("cannot come back to cwd"));
|
2014-07-28 18:26:40 +00:00
|
|
|
root_len = offset_1st_component(cwd->buf);
|
|
|
|
strbuf_setlen(cwd, offset > root_len ? offset : root_len);
|
2020-03-06 19:03:13 +00:00
|
|
|
set_git_dir(cwd->buf, 0);
|
2010-11-26 15:32:34 +00:00
|
|
|
}
|
2010-11-26 15:32:36 +00:00
|
|
|
else
|
2020-03-06 19:03:13 +00:00
|
|
|
set_git_dir(".", 0);
|
2010-07-24 11:25:32 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-04-12 23:11:36 +00:00
|
|
|
static dev_t get_device_or_die(const char *path, const char *prefix, int prefix_len)
|
2010-07-24 11:27:58 +00:00
|
|
|
{
|
|
|
|
struct stat buf;
|
2012-04-12 23:11:36 +00:00
|
|
|
if (stat(path, &buf)) {
|
2018-02-13 13:19:15 +00:00
|
|
|
die_errno(_("failed to stat '%*s%s%s'"),
|
2012-04-12 23:11:36 +00:00
|
|
|
prefix_len,
|
2010-07-24 11:27:58 +00:00
|
|
|
prefix ? prefix : "",
|
|
|
|
prefix ? "/" : "", path);
|
2012-04-12 23:11:36 +00:00
|
|
|
}
|
2010-07-24 11:27:58 +00:00
|
|
|
return buf.st_dev;
|
|
|
|
}
|
|
|
|
|
2012-10-28 16:16:25 +00:00
|
|
|
/*
|
2012-10-28 16:16:26 +00:00
|
|
|
* A "string_list_each_func_t" function that canonicalizes an entry
|
2020-03-10 13:11:23 +00:00
|
|
|
* from GIT_CEILING_DIRECTORIES using real_pathdup(), or
|
Provide a mechanism to turn off symlink resolution in ceiling paths
Commit 1b77d83cab 'setup_git_directory_gently_1(): resolve symlinks
in ceiling paths' changed the setup code to resolve symlinks in the
entries in GIT_CEILING_DIRECTORIES. Because those entries are
compared textually to the symlink-resolved current directory, an
entry in GIT_CEILING_DIRECTORIES that contained a symlink would have
no effect. It was known that this could cause performance problems
if the symlink resolution *itself* touched slow filesystems, but it
was thought that such use cases would be unlikely. The intention of
the earlier change was to deal with a case when the user has this:
GIT_CEILING_DIRECTORIES=/home/gitster
but in reality, /home/gitster is a symbolic link to somewhere else,
e.g. /net/machine/home4/gitster. A textual comparison between the
specified value /home/gitster and the location getcwd(3) returns
would not help us, but readlink("/home/gitster") would still be
fast.
After this change was released, Anders Kaseorg <andersk@mit.edu>
reported:
> [...] my computer has been acting so slow when I’m not connected to
> the network. I put various network filesystem paths in
> $GIT_CEILING_DIRECTORIES, such as
> /afs/athena.mit.edu/user/a/n/andersk (to avoid hitting its parents
> /afs/athena.mit.edu, /afs/athena.mit.edu/user/a, and
> /afs/athena.mit.edu/user/a/n which all live in different AFS
> volumes). Now when I’m not connected to the network, every
> invocation of Git, including the __git_ps1 in my shell prompt, waits
> for AFS to timeout.
To allow users to work around this problem, give them a mechanism to
turn off symlink resolution in GIT_CEILING_DIRECTORIES entries. All
the entries that follow an empty entry will not be checked for symbolic
links and used literally in comparison. E.g. with these:
GIT_CEILING_DIRECTORIES=:/foo/bar:/xyzzy or
GIT_CEILING_DIRECTORIES=/foo/bar::/xyzzy
we will not readlink("/xyzzy") because it comes after an empty entry.
With the former (but not with the latter), "/foo/bar" comes after an
empty entry, and we will not readlink it, either.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-02-20 09:09:24 +00:00
|
|
|
* discards it if unusable. The presence of an empty entry in
|
|
|
|
* GIT_CEILING_DIRECTORIES turns off canonicalization for all
|
|
|
|
* subsequent entries.
|
2012-10-28 16:16:25 +00:00
|
|
|
*/
|
2012-10-28 16:16:26 +00:00
|
|
|
static int canonicalize_ceiling_entry(struct string_list_item *item,
|
Provide a mechanism to turn off symlink resolution in ceiling paths
Commit 1b77d83cab 'setup_git_directory_gently_1(): resolve symlinks
in ceiling paths' changed the setup code to resolve symlinks in the
entries in GIT_CEILING_DIRECTORIES. Because those entries are
compared textually to the symlink-resolved current directory, an
entry in GIT_CEILING_DIRECTORIES that contained a symlink would have
no effect. It was known that this could cause performance problems
if the symlink resolution *itself* touched slow filesystems, but it
was thought that such use cases would be unlikely. The intention of
the earlier change was to deal with a case when the user has this:
GIT_CEILING_DIRECTORIES=/home/gitster
but in reality, /home/gitster is a symbolic link to somewhere else,
e.g. /net/machine/home4/gitster. A textual comparison between the
specified value /home/gitster and the location getcwd(3) returns
would not help us, but readlink("/home/gitster") would still be
fast.
After this change was released, Anders Kaseorg <andersk@mit.edu>
reported:
> [...] my computer has been acting so slow when I’m not connected to
> the network. I put various network filesystem paths in
> $GIT_CEILING_DIRECTORIES, such as
> /afs/athena.mit.edu/user/a/n/andersk (to avoid hitting its parents
> /afs/athena.mit.edu, /afs/athena.mit.edu/user/a, and
> /afs/athena.mit.edu/user/a/n which all live in different AFS
> volumes). Now when I’m not connected to the network, every
> invocation of Git, including the __git_ps1 in my shell prompt, waits
> for AFS to timeout.
To allow users to work around this problem, give them a mechanism to
turn off symlink resolution in GIT_CEILING_DIRECTORIES entries. All
the entries that follow an empty entry will not be checked for symbolic
links and used literally in comparison. E.g. with these:
GIT_CEILING_DIRECTORIES=:/foo/bar:/xyzzy or
GIT_CEILING_DIRECTORIES=/foo/bar::/xyzzy
we will not readlink("/xyzzy") because it comes after an empty entry.
With the former (but not with the latter), "/foo/bar" comes after an
empty entry, and we will not readlink it, either.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-02-20 09:09:24 +00:00
|
|
|
void *cb_data)
|
2012-10-28 16:16:25 +00:00
|
|
|
{
|
Provide a mechanism to turn off symlink resolution in ceiling paths
Commit 1b77d83cab 'setup_git_directory_gently_1(): resolve symlinks
in ceiling paths' changed the setup code to resolve symlinks in the
entries in GIT_CEILING_DIRECTORIES. Because those entries are
compared textually to the symlink-resolved current directory, an
entry in GIT_CEILING_DIRECTORIES that contained a symlink would have
no effect. It was known that this could cause performance problems
if the symlink resolution *itself* touched slow filesystems, but it
was thought that such use cases would be unlikely. The intention of
the earlier change was to deal with a case when the user has this:
GIT_CEILING_DIRECTORIES=/home/gitster
but in reality, /home/gitster is a symbolic link to somewhere else,
e.g. /net/machine/home4/gitster. A textual comparison between the
specified value /home/gitster and the location getcwd(3) returns
would not help us, but readlink("/home/gitster") would still be
fast.
After this change was released, Anders Kaseorg <andersk@mit.edu>
reported:
> [...] my computer has been acting so slow when I’m not connected to
> the network. I put various network filesystem paths in
> $GIT_CEILING_DIRECTORIES, such as
> /afs/athena.mit.edu/user/a/n/andersk (to avoid hitting its parents
> /afs/athena.mit.edu, /afs/athena.mit.edu/user/a, and
> /afs/athena.mit.edu/user/a/n which all live in different AFS
> volumes). Now when I’m not connected to the network, every
> invocation of Git, including the __git_ps1 in my shell prompt, waits
> for AFS to timeout.
To allow users to work around this problem, give them a mechanism to
turn off symlink resolution in GIT_CEILING_DIRECTORIES entries. All
the entries that follow an empty entry will not be checked for symbolic
links and used literally in comparison. E.g. with these:
GIT_CEILING_DIRECTORIES=:/foo/bar:/xyzzy or
GIT_CEILING_DIRECTORIES=/foo/bar::/xyzzy
we will not readlink("/xyzzy") because it comes after an empty entry.
With the former (but not with the latter), "/foo/bar" comes after an
empty entry, and we will not readlink it, either.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-02-20 09:09:24 +00:00
|
|
|
int *empty_entry_found = cb_data;
|
2012-10-28 16:16:26 +00:00
|
|
|
char *ceil = item->string;
|
2012-10-28 16:16:25 +00:00
|
|
|
|
Provide a mechanism to turn off symlink resolution in ceiling paths
Commit 1b77d83cab 'setup_git_directory_gently_1(): resolve symlinks
in ceiling paths' changed the setup code to resolve symlinks in the
entries in GIT_CEILING_DIRECTORIES. Because those entries are
compared textually to the symlink-resolved current directory, an
entry in GIT_CEILING_DIRECTORIES that contained a symlink would have
no effect. It was known that this could cause performance problems
if the symlink resolution *itself* touched slow filesystems, but it
was thought that such use cases would be unlikely. The intention of
the earlier change was to deal with a case when the user has this:
GIT_CEILING_DIRECTORIES=/home/gitster
but in reality, /home/gitster is a symbolic link to somewhere else,
e.g. /net/machine/home4/gitster. A textual comparison between the
specified value /home/gitster and the location getcwd(3) returns
would not help us, but readlink("/home/gitster") would still be
fast.
After this change was released, Anders Kaseorg <andersk@mit.edu>
reported:
> [...] my computer has been acting so slow when I’m not connected to
> the network. I put various network filesystem paths in
> $GIT_CEILING_DIRECTORIES, such as
> /afs/athena.mit.edu/user/a/n/andersk (to avoid hitting its parents
> /afs/athena.mit.edu, /afs/athena.mit.edu/user/a, and
> /afs/athena.mit.edu/user/a/n which all live in different AFS
> volumes). Now when I’m not connected to the network, every
> invocation of Git, including the __git_ps1 in my shell prompt, waits
> for AFS to timeout.
To allow users to work around this problem, give them a mechanism to
turn off symlink resolution in GIT_CEILING_DIRECTORIES entries. All
the entries that follow an empty entry will not be checked for symbolic
links and used literally in comparison. E.g. with these:
GIT_CEILING_DIRECTORIES=:/foo/bar:/xyzzy or
GIT_CEILING_DIRECTORIES=/foo/bar::/xyzzy
we will not readlink("/xyzzy") because it comes after an empty entry.
With the former (but not with the latter), "/foo/bar" comes after an
empty entry, and we will not readlink it, either.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-02-20 09:09:24 +00:00
|
|
|
if (!*ceil) {
|
|
|
|
*empty_entry_found = 1;
|
2012-10-28 16:16:25 +00:00
|
|
|
return 0;
|
Provide a mechanism to turn off symlink resolution in ceiling paths
Commit 1b77d83cab 'setup_git_directory_gently_1(): resolve symlinks
in ceiling paths' changed the setup code to resolve symlinks in the
entries in GIT_CEILING_DIRECTORIES. Because those entries are
compared textually to the symlink-resolved current directory, an
entry in GIT_CEILING_DIRECTORIES that contained a symlink would have
no effect. It was known that this could cause performance problems
if the symlink resolution *itself* touched slow filesystems, but it
was thought that such use cases would be unlikely. The intention of
the earlier change was to deal with a case when the user has this:
GIT_CEILING_DIRECTORIES=/home/gitster
but in reality, /home/gitster is a symbolic link to somewhere else,
e.g. /net/machine/home4/gitster. A textual comparison between the
specified value /home/gitster and the location getcwd(3) returns
would not help us, but readlink("/home/gitster") would still be
fast.
After this change was released, Anders Kaseorg <andersk@mit.edu>
reported:
> [...] my computer has been acting so slow when I’m not connected to
> the network. I put various network filesystem paths in
> $GIT_CEILING_DIRECTORIES, such as
> /afs/athena.mit.edu/user/a/n/andersk (to avoid hitting its parents
> /afs/athena.mit.edu, /afs/athena.mit.edu/user/a, and
> /afs/athena.mit.edu/user/a/n which all live in different AFS
> volumes). Now when I’m not connected to the network, every
> invocation of Git, including the __git_ps1 in my shell prompt, waits
> for AFS to timeout.
To allow users to work around this problem, give them a mechanism to
turn off symlink resolution in GIT_CEILING_DIRECTORIES entries. All
the entries that follow an empty entry will not be checked for symbolic
links and used literally in comparison. E.g. with these:
GIT_CEILING_DIRECTORIES=:/foo/bar:/xyzzy or
GIT_CEILING_DIRECTORIES=/foo/bar::/xyzzy
we will not readlink("/xyzzy") because it comes after an empty entry.
With the former (but not with the latter), "/foo/bar" comes after an
empty entry, and we will not readlink it, either.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-02-20 09:09:24 +00:00
|
|
|
} else if (!is_absolute_path(ceil)) {
|
2012-10-28 16:16:25 +00:00
|
|
|
return 0;
|
Provide a mechanism to turn off symlink resolution in ceiling paths
Commit 1b77d83cab 'setup_git_directory_gently_1(): resolve symlinks
in ceiling paths' changed the setup code to resolve symlinks in the
entries in GIT_CEILING_DIRECTORIES. Because those entries are
compared textually to the symlink-resolved current directory, an
entry in GIT_CEILING_DIRECTORIES that contained a symlink would have
no effect. It was known that this could cause performance problems
if the symlink resolution *itself* touched slow filesystems, but it
was thought that such use cases would be unlikely. The intention of
the earlier change was to deal with a case when the user has this:
GIT_CEILING_DIRECTORIES=/home/gitster
but in reality, /home/gitster is a symbolic link to somewhere else,
e.g. /net/machine/home4/gitster. A textual comparison between the
specified value /home/gitster and the location getcwd(3) returns
would not help us, but readlink("/home/gitster") would still be
fast.
After this change was released, Anders Kaseorg <andersk@mit.edu>
reported:
> [...] my computer has been acting so slow when I’m not connected to
> the network. I put various network filesystem paths in
> $GIT_CEILING_DIRECTORIES, such as
> /afs/athena.mit.edu/user/a/n/andersk (to avoid hitting its parents
> /afs/athena.mit.edu, /afs/athena.mit.edu/user/a, and
> /afs/athena.mit.edu/user/a/n which all live in different AFS
> volumes). Now when I’m not connected to the network, every
> invocation of Git, including the __git_ps1 in my shell prompt, waits
> for AFS to timeout.
To allow users to work around this problem, give them a mechanism to
turn off symlink resolution in GIT_CEILING_DIRECTORIES entries. All
the entries that follow an empty entry will not be checked for symbolic
links and used literally in comparison. E.g. with these:
GIT_CEILING_DIRECTORIES=:/foo/bar:/xyzzy or
GIT_CEILING_DIRECTORIES=/foo/bar::/xyzzy
we will not readlink("/xyzzy") because it comes after an empty entry.
With the former (but not with the latter), "/foo/bar" comes after an
empty entry, and we will not readlink it, either.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-02-20 09:09:24 +00:00
|
|
|
} else if (*empty_entry_found) {
|
|
|
|
/* Keep entry but do not canonicalize it */
|
|
|
|
return 1;
|
|
|
|
} else {
|
2017-03-08 15:43:40 +00:00
|
|
|
char *real_path = real_pathdup(ceil, 0);
|
2016-12-12 18:16:55 +00:00
|
|
|
if (!real_path) {
|
Provide a mechanism to turn off symlink resolution in ceiling paths
Commit 1b77d83cab 'setup_git_directory_gently_1(): resolve symlinks
in ceiling paths' changed the setup code to resolve symlinks in the
entries in GIT_CEILING_DIRECTORIES. Because those entries are
compared textually to the symlink-resolved current directory, an
entry in GIT_CEILING_DIRECTORIES that contained a symlink would have
no effect. It was known that this could cause performance problems
if the symlink resolution *itself* touched slow filesystems, but it
was thought that such use cases would be unlikely. The intention of
the earlier change was to deal with a case when the user has this:
GIT_CEILING_DIRECTORIES=/home/gitster
but in reality, /home/gitster is a symbolic link to somewhere else,
e.g. /net/machine/home4/gitster. A textual comparison between the
specified value /home/gitster and the location getcwd(3) returns
would not help us, but readlink("/home/gitster") would still be
fast.
After this change was released, Anders Kaseorg <andersk@mit.edu>
reported:
> [...] my computer has been acting so slow when I’m not connected to
> the network. I put various network filesystem paths in
> $GIT_CEILING_DIRECTORIES, such as
> /afs/athena.mit.edu/user/a/n/andersk (to avoid hitting its parents
> /afs/athena.mit.edu, /afs/athena.mit.edu/user/a, and
> /afs/athena.mit.edu/user/a/n which all live in different AFS
> volumes). Now when I’m not connected to the network, every
> invocation of Git, including the __git_ps1 in my shell prompt, waits
> for AFS to timeout.
To allow users to work around this problem, give them a mechanism to
turn off symlink resolution in GIT_CEILING_DIRECTORIES entries. All
the entries that follow an empty entry will not be checked for symbolic
links and used literally in comparison. E.g. with these:
GIT_CEILING_DIRECTORIES=:/foo/bar:/xyzzy or
GIT_CEILING_DIRECTORIES=/foo/bar::/xyzzy
we will not readlink("/xyzzy") because it comes after an empty entry.
With the former (but not with the latter), "/foo/bar" comes after an
empty entry, and we will not readlink it, either.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-02-20 09:09:24 +00:00
|
|
|
return 0;
|
2016-12-12 18:16:55 +00:00
|
|
|
}
|
Provide a mechanism to turn off symlink resolution in ceiling paths
Commit 1b77d83cab 'setup_git_directory_gently_1(): resolve symlinks
in ceiling paths' changed the setup code to resolve symlinks in the
entries in GIT_CEILING_DIRECTORIES. Because those entries are
compared textually to the symlink-resolved current directory, an
entry in GIT_CEILING_DIRECTORIES that contained a symlink would have
no effect. It was known that this could cause performance problems
if the symlink resolution *itself* touched slow filesystems, but it
was thought that such use cases would be unlikely. The intention of
the earlier change was to deal with a case when the user has this:
GIT_CEILING_DIRECTORIES=/home/gitster
but in reality, /home/gitster is a symbolic link to somewhere else,
e.g. /net/machine/home4/gitster. A textual comparison between the
specified value /home/gitster and the location getcwd(3) returns
would not help us, but readlink("/home/gitster") would still be
fast.
After this change was released, Anders Kaseorg <andersk@mit.edu>
reported:
> [...] my computer has been acting so slow when I’m not connected to
> the network. I put various network filesystem paths in
> $GIT_CEILING_DIRECTORIES, such as
> /afs/athena.mit.edu/user/a/n/andersk (to avoid hitting its parents
> /afs/athena.mit.edu, /afs/athena.mit.edu/user/a, and
> /afs/athena.mit.edu/user/a/n which all live in different AFS
> volumes). Now when I’m not connected to the network, every
> invocation of Git, including the __git_ps1 in my shell prompt, waits
> for AFS to timeout.
To allow users to work around this problem, give them a mechanism to
turn off symlink resolution in GIT_CEILING_DIRECTORIES entries. All
the entries that follow an empty entry will not be checked for symbolic
links and used literally in comparison. E.g. with these:
GIT_CEILING_DIRECTORIES=:/foo/bar:/xyzzy or
GIT_CEILING_DIRECTORIES=/foo/bar::/xyzzy
we will not readlink("/xyzzy") because it comes after an empty entry.
With the former (but not with the latter), "/foo/bar" comes after an
empty entry, and we will not readlink it, either.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-02-20 09:09:24 +00:00
|
|
|
free(item->string);
|
2016-12-12 18:16:55 +00:00
|
|
|
item->string = real_path;
|
Provide a mechanism to turn off symlink resolution in ceiling paths
Commit 1b77d83cab 'setup_git_directory_gently_1(): resolve symlinks
in ceiling paths' changed the setup code to resolve symlinks in the
entries in GIT_CEILING_DIRECTORIES. Because those entries are
compared textually to the symlink-resolved current directory, an
entry in GIT_CEILING_DIRECTORIES that contained a symlink would have
no effect. It was known that this could cause performance problems
if the symlink resolution *itself* touched slow filesystems, but it
was thought that such use cases would be unlikely. The intention of
the earlier change was to deal with a case when the user has this:
GIT_CEILING_DIRECTORIES=/home/gitster
but in reality, /home/gitster is a symbolic link to somewhere else,
e.g. /net/machine/home4/gitster. A textual comparison between the
specified value /home/gitster and the location getcwd(3) returns
would not help us, but readlink("/home/gitster") would still be
fast.
After this change was released, Anders Kaseorg <andersk@mit.edu>
reported:
> [...] my computer has been acting so slow when I’m not connected to
> the network. I put various network filesystem paths in
> $GIT_CEILING_DIRECTORIES, such as
> /afs/athena.mit.edu/user/a/n/andersk (to avoid hitting its parents
> /afs/athena.mit.edu, /afs/athena.mit.edu/user/a, and
> /afs/athena.mit.edu/user/a/n which all live in different AFS
> volumes). Now when I’m not connected to the network, every
> invocation of Git, including the __git_ps1 in my shell prompt, waits
> for AFS to timeout.
To allow users to work around this problem, give them a mechanism to
turn off symlink resolution in GIT_CEILING_DIRECTORIES entries. All
the entries that follow an empty entry will not be checked for symbolic
links and used literally in comparison. E.g. with these:
GIT_CEILING_DIRECTORIES=:/foo/bar:/xyzzy or
GIT_CEILING_DIRECTORIES=/foo/bar::/xyzzy
we will not readlink("/xyzzy") because it comes after an empty entry.
With the former (but not with the latter), "/foo/bar" comes after an
empty entry, and we will not readlink it, either.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-02-20 09:09:24 +00:00
|
|
|
return 1;
|
|
|
|
}
|
2012-10-28 16:16:25 +00:00
|
|
|
}
|
|
|
|
|
2022-03-02 11:23:04 +00:00
|
|
|
struct safe_directory_data {
|
|
|
|
const char *path;
|
|
|
|
int is_safe;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int safe_directory_cb(const char *key, const char *value, void *d)
|
|
|
|
{
|
|
|
|
struct safe_directory_data *data = d;
|
|
|
|
|
2022-04-13 15:32:30 +00:00
|
|
|
if (strcmp(key, "safe.directory"))
|
|
|
|
return 0;
|
|
|
|
|
2022-04-13 15:32:31 +00:00
|
|
|
if (!value || !*value) {
|
2022-03-02 11:23:04 +00:00
|
|
|
data->is_safe = 0;
|
2022-04-13 15:32:31 +00:00
|
|
|
} else if (!strcmp(value, "*")) {
|
|
|
|
data->is_safe = 1;
|
|
|
|
} else {
|
2022-03-02 11:23:04 +00:00
|
|
|
const char *interpolated = NULL;
|
|
|
|
|
|
|
|
if (!git_config_pathname(&interpolated, key, value) &&
|
|
|
|
!fspathcmp(data->path, interpolated ? interpolated : value))
|
|
|
|
data->is_safe = 1;
|
|
|
|
|
|
|
|
free((char *)interpolated);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-05-10 19:35:29 +00:00
|
|
|
/*
|
|
|
|
* Check if a repository is safe, by verifying the ownership of the
|
|
|
|
* worktree (if any), the git directory, and the gitfile (if any).
|
|
|
|
*
|
|
|
|
* Exemptions for known-safe repositories can be added via `safe.directory`
|
|
|
|
* config settings; for non-bare repositories, their worktree needs to be
|
|
|
|
* added, for bare ones their git directory.
|
|
|
|
*/
|
|
|
|
static int ensure_valid_ownership(const char *gitfile,
|
2022-08-08 13:27:47 +00:00
|
|
|
const char *worktree, const char *gitdir,
|
|
|
|
struct strbuf *report)
|
2022-03-02 11:23:04 +00:00
|
|
|
{
|
2022-05-10 19:35:29 +00:00
|
|
|
struct safe_directory_data data = {
|
|
|
|
.path = worktree ? worktree : gitdir
|
|
|
|
};
|
2022-03-02 11:23:04 +00:00
|
|
|
|
2022-04-13 15:32:29 +00:00
|
|
|
if (!git_env_bool("GIT_TEST_ASSUME_DIFFERENT_OWNER", 0) &&
|
2022-08-08 13:27:47 +00:00
|
|
|
(!gitfile || is_path_owned_by_current_user(gitfile, report)) &&
|
|
|
|
(!worktree || is_path_owned_by_current_user(worktree, report)) &&
|
|
|
|
(!gitdir || is_path_owned_by_current_user(gitdir, report)))
|
2022-03-02 11:23:04 +00:00
|
|
|
return 1;
|
|
|
|
|
2022-05-10 19:35:29 +00:00
|
|
|
/*
|
|
|
|
* data.path is the "path" that identifies the repository and it is
|
|
|
|
* constant regardless of what failed above. data.is_safe should be
|
|
|
|
* initialized to false, and might be changed by the callback.
|
|
|
|
*/
|
2022-07-14 21:28:00 +00:00
|
|
|
git_protected_config(safe_directory_cb, &data);
|
2022-03-02 11:23:04 +00:00
|
|
|
|
|
|
|
return data.is_safe;
|
|
|
|
}
|
|
|
|
|
setup.c: create `safe.bareRepository`
There is a known social engineering attack that takes advantage of the
fact that a working tree can include an entire bare repository,
including a config file. A user could run a Git command inside the bare
repository thinking that the config file of the 'outer' repository would
be used, but in reality, the bare repository's config file (which is
attacker-controlled) is used, which may result in arbitrary code
execution. See [1] for a fuller description and deeper discussion.
A simple mitigation is to forbid bare repositories unless specified via
`--git-dir` or `GIT_DIR`. In environments that don't use bare
repositories, this would be minimally disruptive.
Create a config variable, `safe.bareRepository`, that tells Git whether
or not to die() when working with a bare repository. This config is an
enum of:
- "all": allow all bare repositories (this is the default)
- "explicit": only allow bare repositories specified via --git-dir
or GIT_DIR.
If we want to protect users from such attacks by default, neither value
will suffice - "all" provides no protection, but "explicit" is
impractical for bare repository users. A more usable default would be to
allow only non-embedded bare repositories ([2] contains one such
proposal), but detecting if a repository is embedded is potentially
non-trivial, so this work is not implemented in this series.
[1]: https://lore.kernel.org/git/kl6lsfqpygsj.fsf@chooglen-macbookpro.roam.corp.google.com
[2]: https://lore.kernel.org/git/5b969c5e-e802-c447-ad25-6acc0b784582@github.com
Signed-off-by: Glen Choo <chooglen@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-07-14 21:28:01 +00:00
|
|
|
static int allowed_bare_repo_cb(const char *key, const char *value, void *d)
|
|
|
|
{
|
|
|
|
enum allowed_bare_repo *allowed_bare_repo = d;
|
|
|
|
|
|
|
|
if (strcasecmp(key, "safe.bareRepository"))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!strcmp(value, "explicit")) {
|
|
|
|
*allowed_bare_repo = ALLOWED_BARE_REPO_EXPLICIT;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (!strcmp(value, "all")) {
|
|
|
|
*allowed_bare_repo = ALLOWED_BARE_REPO_ALL;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static enum allowed_bare_repo get_allowed_bare_repo(void)
|
|
|
|
{
|
|
|
|
enum allowed_bare_repo result = ALLOWED_BARE_REPO_ALL;
|
|
|
|
git_protected_config(allowed_bare_repo_cb, &result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char *allowed_bare_repo_to_string(
|
|
|
|
enum allowed_bare_repo allowed_bare_repo)
|
|
|
|
{
|
|
|
|
switch (allowed_bare_repo) {
|
|
|
|
case ALLOWED_BARE_REPO_EXPLICIT:
|
|
|
|
return "explicit";
|
|
|
|
case ALLOWED_BARE_REPO_ALL:
|
|
|
|
return "all";
|
|
|
|
default:
|
|
|
|
BUG("invalid allowed_bare_repo %d",
|
|
|
|
allowed_bare_repo);
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
setup_git_directory_1(): avoid changing global state
For historical reasons, Git searches for the .git/ directory (or the
.git file) by changing the working directory successively to the parent
directory of the current directory, until either anything was found or
until a ceiling or a mount point is hit.
Further global state may be changed in case a .git/ directory was found.
We do have a use case, though, where we would like to find the .git/
directory without having any global state touched, though: when we read
the early config e.g. for the pager or for alias expansion.
Let's just move all of code that changes any global state out of the
function `setup_git_directory_gently_1()` into
`setup_git_directory_gently()`.
In subsequent patches, we will use the _1() function in a new
`discover_git_directory()` function that we will then use for the early
config code.
Note: the new loop is a *little* tricky, as we have to handle the root
directory specially: we cannot simply strip away the last component
including the slash, as the root directory only has that slash. To remedy
that, we introduce the `min_offset` variable that holds the minimal length
of an absolute path, and using that to special-case the root directory,
including an early exit before trying to find the parent of the root
directory.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-03-13 20:10:42 +00:00
|
|
|
enum discovery_result {
|
|
|
|
GIT_DIR_NONE = 0,
|
|
|
|
GIT_DIR_EXPLICIT,
|
|
|
|
GIT_DIR_DISCOVERED,
|
|
|
|
GIT_DIR_BARE,
|
|
|
|
/* these are errors */
|
|
|
|
GIT_DIR_HIT_CEILING = -1,
|
2017-03-13 20:11:22 +00:00
|
|
|
GIT_DIR_HIT_MOUNT_POINT = -2,
|
2022-03-02 11:23:04 +00:00
|
|
|
GIT_DIR_INVALID_GITFILE = -3,
|
setup.c: create `safe.bareRepository`
There is a known social engineering attack that takes advantage of the
fact that a working tree can include an entire bare repository,
including a config file. A user could run a Git command inside the bare
repository thinking that the config file of the 'outer' repository would
be used, but in reality, the bare repository's config file (which is
attacker-controlled) is used, which may result in arbitrary code
execution. See [1] for a fuller description and deeper discussion.
A simple mitigation is to forbid bare repositories unless specified via
`--git-dir` or `GIT_DIR`. In environments that don't use bare
repositories, this would be minimally disruptive.
Create a config variable, `safe.bareRepository`, that tells Git whether
or not to die() when working with a bare repository. This config is an
enum of:
- "all": allow all bare repositories (this is the default)
- "explicit": only allow bare repositories specified via --git-dir
or GIT_DIR.
If we want to protect users from such attacks by default, neither value
will suffice - "all" provides no protection, but "explicit" is
impractical for bare repository users. A more usable default would be to
allow only non-embedded bare repositories ([2] contains one such
proposal), but detecting if a repository is embedded is potentially
non-trivial, so this work is not implemented in this series.
[1]: https://lore.kernel.org/git/kl6lsfqpygsj.fsf@chooglen-macbookpro.roam.corp.google.com
[2]: https://lore.kernel.org/git/5b969c5e-e802-c447-ad25-6acc0b784582@github.com
Signed-off-by: Glen Choo <chooglen@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-07-14 21:28:01 +00:00
|
|
|
GIT_DIR_INVALID_OWNERSHIP = -4,
|
|
|
|
GIT_DIR_DISALLOWED_BARE = -5,
|
setup_git_directory_1(): avoid changing global state
For historical reasons, Git searches for the .git/ directory (or the
.git file) by changing the working directory successively to the parent
directory of the current directory, until either anything was found or
until a ceiling or a mount point is hit.
Further global state may be changed in case a .git/ directory was found.
We do have a use case, though, where we would like to find the .git/
directory without having any global state touched, though: when we read
the early config e.g. for the pager or for alias expansion.
Let's just move all of code that changes any global state out of the
function `setup_git_directory_gently_1()` into
`setup_git_directory_gently()`.
In subsequent patches, we will use the _1() function in a new
`discover_git_directory()` function that we will then use for the early
config code.
Note: the new loop is a *little* tricky, as we have to handle the root
directory specially: we cannot simply strip away the last component
including the slash, as the root directory only has that slash. To remedy
that, we introduce the `min_offset` variable that holds the minimal length
of an absolute path, and using that to special-case the root directory,
including an early exit before trying to find the parent of the root
directory.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-03-13 20:10:42 +00:00
|
|
|
};
|
|
|
|
|
Clean up work-tree handling
The old version of work-tree support was an unholy mess, barely readable,
and not to the point.
For example, why do you have to provide a worktree, when it is not used?
As in "git status". Now it works.
Another riddle was: if you can have work trees inside the git dir, why
are some programs complaining that they need a work tree?
IOW it is allowed to call
$ git --git-dir=../ --work-tree=. bla
when you really want to. In this case, you are both in the git directory
and in the working tree. So, programs have to actually test for the right
thing, namely if they are inside a working tree, and not if they are
inside a git directory.
Also, GIT_DIR=../.git should behave the same as if no GIT_DIR was
specified, unless there is a repository in the current working directory.
It does now.
The logic to determine if a repository is bare, or has a work tree
(tertium non datur), is this:
--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.
In related news, a long standing bug was fixed: when in .git/bla/x.git/,
which is a bare repository, git formerly assumed ../.. to be the
appropriate git dir. This problem was reported by Shawn Pearce to have
caused much pain, where a colleague mistakenly ran "git init" in "/" a
long time ago, and bare repositories just would not work.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2007-08-01 00:30:14 +00:00
|
|
|
/*
|
|
|
|
* We cannot decide in this function whether we are in the work tree or
|
|
|
|
* not, since the config can only be read _after_ this function was called.
|
setup_git_directory_1(): avoid changing global state
For historical reasons, Git searches for the .git/ directory (or the
.git file) by changing the working directory successively to the parent
directory of the current directory, until either anything was found or
until a ceiling or a mount point is hit.
Further global state may be changed in case a .git/ directory was found.
We do have a use case, though, where we would like to find the .git/
directory without having any global state touched, though: when we read
the early config e.g. for the pager or for alias expansion.
Let's just move all of code that changes any global state out of the
function `setup_git_directory_gently_1()` into
`setup_git_directory_gently()`.
In subsequent patches, we will use the _1() function in a new
`discover_git_directory()` function that we will then use for the early
config code.
Note: the new loop is a *little* tricky, as we have to handle the root
directory specially: we cannot simply strip away the last component
including the slash, as the root directory only has that slash. To remedy
that, we introduce the `min_offset` variable that holds the minimal length
of an absolute path, and using that to special-case the root directory,
including an early exit before trying to find the parent of the root
directory.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-03-13 20:10:42 +00:00
|
|
|
*
|
|
|
|
* Also, we avoid changing any global state (such as the current working
|
|
|
|
* directory) to allow early callers.
|
|
|
|
*
|
|
|
|
* The directory where the search should start needs to be passed in via the
|
|
|
|
* `dir` parameter; upon return, the `dir` buffer will contain the path of
|
|
|
|
* the directory where the search ended, and `gitdir` will contain the path of
|
|
|
|
* the discovered .git/ directory, if any. If `gitdir` is not absolute, it
|
|
|
|
* is relative to `dir` (i.e. *not* necessarily the cwd).
|
Clean up work-tree handling
The old version of work-tree support was an unholy mess, barely readable,
and not to the point.
For example, why do you have to provide a worktree, when it is not used?
As in "git status". Now it works.
Another riddle was: if you can have work trees inside the git dir, why
are some programs complaining that they need a work tree?
IOW it is allowed to call
$ git --git-dir=../ --work-tree=. bla
when you really want to. In this case, you are both in the git directory
and in the working tree. So, programs have to actually test for the right
thing, namely if they are inside a working tree, and not if they are
inside a git directory.
Also, GIT_DIR=../.git should behave the same as if no GIT_DIR was
specified, unless there is a repository in the current working directory.
It does now.
The logic to determine if a repository is bare, or has a work tree
(tertium non datur), is this:
--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.
In related news, a long standing bug was fixed: when in .git/bla/x.git/,
which is a bare repository, git formerly assumed ../.. to be the
appropriate git dir. This problem was reported by Shawn Pearce to have
caused much pain, where a colleague mistakenly ran "git init" in "/" a
long time ago, and bare repositories just would not work.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2007-08-01 00:30:14 +00:00
|
|
|
*/
|
setup_git_directory_1(): avoid changing global state
For historical reasons, Git searches for the .git/ directory (or the
.git file) by changing the working directory successively to the parent
directory of the current directory, until either anything was found or
until a ceiling or a mount point is hit.
Further global state may be changed in case a .git/ directory was found.
We do have a use case, though, where we would like to find the .git/
directory without having any global state touched, though: when we read
the early config e.g. for the pager or for alias expansion.
Let's just move all of code that changes any global state out of the
function `setup_git_directory_gently_1()` into
`setup_git_directory_gently()`.
In subsequent patches, we will use the _1() function in a new
`discover_git_directory()` function that we will then use for the early
config code.
Note: the new loop is a *little* tricky, as we have to handle the root
directory specially: we cannot simply strip away the last component
including the slash, as the root directory only has that slash. To remedy
that, we introduce the `min_offset` variable that holds the minimal length
of an absolute path, and using that to special-case the root directory,
including an early exit before trying to find the parent of the root
directory.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-03-13 20:10:42 +00:00
|
|
|
static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,
|
2017-03-13 20:11:22 +00:00
|
|
|
struct strbuf *gitdir,
|
2022-08-08 13:27:47 +00:00
|
|
|
struct strbuf *report,
|
2017-03-13 20:11:22 +00:00
|
|
|
int die_on_error)
|
2005-08-17 01:06:34 +00:00
|
|
|
{
|
2008-05-20 06:49:26 +00:00
|
|
|
const char *env_ceiling_dirs = getenv(CEILING_DIRECTORIES_ENVIRONMENT);
|
2012-10-28 16:16:24 +00:00
|
|
|
struct string_list ceiling_dirs = STRING_LIST_INIT_DUP;
|
setup_git_directory_1(): avoid changing global state
For historical reasons, Git searches for the .git/ directory (or the
.git file) by changing the working directory successively to the parent
directory of the current directory, until either anything was found or
until a ceiling or a mount point is hit.
Further global state may be changed in case a .git/ directory was found.
We do have a use case, though, where we would like to find the .git/
directory without having any global state touched, though: when we read
the early config e.g. for the pager or for alias expansion.
Let's just move all of code that changes any global state out of the
function `setup_git_directory_gently_1()` into
`setup_git_directory_gently()`.
In subsequent patches, we will use the _1() function in a new
`discover_git_directory()` function that we will then use for the early
config code.
Note: the new loop is a *little* tricky, as we have to handle the root
directory specially: we cannot simply strip away the last component
including the slash, as the root directory only has that slash. To remedy
that, we introduce the `min_offset` variable that holds the minimal length
of an absolute path, and using that to special-case the root directory,
including an early exit before trying to find the parent of the root
directory.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-03-13 20:10:42 +00:00
|
|
|
const char *gitdirenv;
|
2019-08-24 22:10:44 +00:00
|
|
|
int ceil_offset = -1, min_offset = offset_1st_component(dir->buf);
|
2010-07-13 09:02:00 +00:00
|
|
|
dev_t current_device = 0;
|
|
|
|
int one_filesystem = 1;
|
2005-08-17 01:06:34 +00:00
|
|
|
|
Clean up work-tree handling
The old version of work-tree support was an unholy mess, barely readable,
and not to the point.
For example, why do you have to provide a worktree, when it is not used?
As in "git status". Now it works.
Another riddle was: if you can have work trees inside the git dir, why
are some programs complaining that they need a work tree?
IOW it is allowed to call
$ git --git-dir=../ --work-tree=. bla
when you really want to. In this case, you are both in the git directory
and in the working tree. So, programs have to actually test for the right
thing, namely if they are inside a working tree, and not if they are
inside a git directory.
Also, GIT_DIR=../.git should behave the same as if no GIT_DIR was
specified, unless there is a repository in the current working directory.
It does now.
The logic to determine if a repository is bare, or has a work tree
(tertium non datur), is this:
--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.
In related news, a long standing bug was fixed: when in .git/bla/x.git/,
which is a bare repository, git formerly assumed ../.. to be the
appropriate git dir. This problem was reported by Shawn Pearce to have
caused much pain, where a colleague mistakenly ran "git init" in "/" a
long time ago, and bare repositories just would not work.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2007-08-01 00:30:14 +00:00
|
|
|
/*
|
|
|
|
* If GIT_DIR is set explicitly, we're not going
|
|
|
|
* to do any discovery, but we still do repository
|
|
|
|
* validation.
|
|
|
|
*/
|
2006-12-31 04:30:19 +00:00
|
|
|
gitdirenv = getenv(GIT_DIR_ENVIRONMENT);
|
setup_git_directory_1(): avoid changing global state
For historical reasons, Git searches for the .git/ directory (or the
.git file) by changing the working directory successively to the parent
directory of the current directory, until either anything was found or
until a ceiling or a mount point is hit.
Further global state may be changed in case a .git/ directory was found.
We do have a use case, though, where we would like to find the .git/
directory without having any global state touched, though: when we read
the early config e.g. for the pager or for alias expansion.
Let's just move all of code that changes any global state out of the
function `setup_git_directory_gently_1()` into
`setup_git_directory_gently()`.
In subsequent patches, we will use the _1() function in a new
`discover_git_directory()` function that we will then use for the early
config code.
Note: the new loop is a *little* tricky, as we have to handle the root
directory specially: we cannot simply strip away the last component
including the slash, as the root directory only has that slash. To remedy
that, we introduce the `min_offset` variable that holds the minimal length
of an absolute path, and using that to special-case the root directory,
including an early exit before trying to find the parent of the root
directory.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-03-13 20:10:42 +00:00
|
|
|
if (gitdirenv) {
|
|
|
|
strbuf_addstr(gitdir, gitdirenv);
|
|
|
|
return GIT_DIR_EXPLICIT;
|
|
|
|
}
|
2005-08-17 01:06:34 +00:00
|
|
|
|
2012-10-28 16:16:24 +00:00
|
|
|
if (env_ceiling_dirs) {
|
Provide a mechanism to turn off symlink resolution in ceiling paths
Commit 1b77d83cab 'setup_git_directory_gently_1(): resolve symlinks
in ceiling paths' changed the setup code to resolve symlinks in the
entries in GIT_CEILING_DIRECTORIES. Because those entries are
compared textually to the symlink-resolved current directory, an
entry in GIT_CEILING_DIRECTORIES that contained a symlink would have
no effect. It was known that this could cause performance problems
if the symlink resolution *itself* touched slow filesystems, but it
was thought that such use cases would be unlikely. The intention of
the earlier change was to deal with a case when the user has this:
GIT_CEILING_DIRECTORIES=/home/gitster
but in reality, /home/gitster is a symbolic link to somewhere else,
e.g. /net/machine/home4/gitster. A textual comparison between the
specified value /home/gitster and the location getcwd(3) returns
would not help us, but readlink("/home/gitster") would still be
fast.
After this change was released, Anders Kaseorg <andersk@mit.edu>
reported:
> [...] my computer has been acting so slow when I’m not connected to
> the network. I put various network filesystem paths in
> $GIT_CEILING_DIRECTORIES, such as
> /afs/athena.mit.edu/user/a/n/andersk (to avoid hitting its parents
> /afs/athena.mit.edu, /afs/athena.mit.edu/user/a, and
> /afs/athena.mit.edu/user/a/n which all live in different AFS
> volumes). Now when I’m not connected to the network, every
> invocation of Git, including the __git_ps1 in my shell prompt, waits
> for AFS to timeout.
To allow users to work around this problem, give them a mechanism to
turn off symlink resolution in GIT_CEILING_DIRECTORIES entries. All
the entries that follow an empty entry will not be checked for symbolic
links and used literally in comparison. E.g. with these:
GIT_CEILING_DIRECTORIES=:/foo/bar:/xyzzy or
GIT_CEILING_DIRECTORIES=/foo/bar::/xyzzy
we will not readlink("/xyzzy") because it comes after an empty entry.
With the former (but not with the latter), "/foo/bar" comes after an
empty entry, and we will not readlink it, either.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-02-20 09:09:24 +00:00
|
|
|
int empty_entry_found = 0;
|
|
|
|
|
2012-10-28 16:16:24 +00:00
|
|
|
string_list_split(&ceiling_dirs, env_ceiling_dirs, PATH_SEP, -1);
|
2012-10-28 16:16:26 +00:00
|
|
|
filter_string_list(&ceiling_dirs, 0,
|
Provide a mechanism to turn off symlink resolution in ceiling paths
Commit 1b77d83cab 'setup_git_directory_gently_1(): resolve symlinks
in ceiling paths' changed the setup code to resolve symlinks in the
entries in GIT_CEILING_DIRECTORIES. Because those entries are
compared textually to the symlink-resolved current directory, an
entry in GIT_CEILING_DIRECTORIES that contained a symlink would have
no effect. It was known that this could cause performance problems
if the symlink resolution *itself* touched slow filesystems, but it
was thought that such use cases would be unlikely. The intention of
the earlier change was to deal with a case when the user has this:
GIT_CEILING_DIRECTORIES=/home/gitster
but in reality, /home/gitster is a symbolic link to somewhere else,
e.g. /net/machine/home4/gitster. A textual comparison between the
specified value /home/gitster and the location getcwd(3) returns
would not help us, but readlink("/home/gitster") would still be
fast.
After this change was released, Anders Kaseorg <andersk@mit.edu>
reported:
> [...] my computer has been acting so slow when I’m not connected to
> the network. I put various network filesystem paths in
> $GIT_CEILING_DIRECTORIES, such as
> /afs/athena.mit.edu/user/a/n/andersk (to avoid hitting its parents
> /afs/athena.mit.edu, /afs/athena.mit.edu/user/a, and
> /afs/athena.mit.edu/user/a/n which all live in different AFS
> volumes). Now when I’m not connected to the network, every
> invocation of Git, including the __git_ps1 in my shell prompt, waits
> for AFS to timeout.
To allow users to work around this problem, give them a mechanism to
turn off symlink resolution in GIT_CEILING_DIRECTORIES entries. All
the entries that follow an empty entry will not be checked for symbolic
links and used literally in comparison. E.g. with these:
GIT_CEILING_DIRECTORIES=:/foo/bar:/xyzzy or
GIT_CEILING_DIRECTORIES=/foo/bar::/xyzzy
we will not readlink("/xyzzy") because it comes after an empty entry.
With the former (but not with the latter), "/foo/bar" comes after an
empty entry, and we will not readlink it, either.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-02-20 09:09:24 +00:00
|
|
|
canonicalize_ceiling_entry, &empty_entry_found);
|
setup_git_directory_1(): avoid changing global state
For historical reasons, Git searches for the .git/ directory (or the
.git file) by changing the working directory successively to the parent
directory of the current directory, until either anything was found or
until a ceiling or a mount point is hit.
Further global state may be changed in case a .git/ directory was found.
We do have a use case, though, where we would like to find the .git/
directory without having any global state touched, though: when we read
the early config e.g. for the pager or for alias expansion.
Let's just move all of code that changes any global state out of the
function `setup_git_directory_gently_1()` into
`setup_git_directory_gently()`.
In subsequent patches, we will use the _1() function in a new
`discover_git_directory()` function that we will then use for the early
config code.
Note: the new loop is a *little* tricky, as we have to handle the root
directory specially: we cannot simply strip away the last component
including the slash, as the root directory only has that slash. To remedy
that, we introduce the `min_offset` variable that holds the minimal length
of an absolute path, and using that to special-case the root directory,
including an early exit before trying to find the parent of the root
directory.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-03-13 20:10:42 +00:00
|
|
|
ceil_offset = longest_ancestor_length(dir->buf, &ceiling_dirs);
|
2012-10-28 16:16:24 +00:00
|
|
|
string_list_clear(&ceiling_dirs, 0);
|
|
|
|
}
|
|
|
|
|
setup_git_directory_1(): avoid changing global state
For historical reasons, Git searches for the .git/ directory (or the
.git file) by changing the working directory successively to the parent
directory of the current directory, until either anything was found or
until a ceiling or a mount point is hit.
Further global state may be changed in case a .git/ directory was found.
We do have a use case, though, where we would like to find the .git/
directory without having any global state touched, though: when we read
the early config e.g. for the pager or for alias expansion.
Let's just move all of code that changes any global state out of the
function `setup_git_directory_gently_1()` into
`setup_git_directory_gently()`.
In subsequent patches, we will use the _1() function in a new
`discover_git_directory()` function that we will then use for the early
config code.
Note: the new loop is a *little* tricky, as we have to handle the root
directory specially: we cannot simply strip away the last component
including the slash, as the root directory only has that slash. To remedy
that, we introduce the `min_offset` variable that holds the minimal length
of an absolute path, and using that to special-case the root directory,
including an early exit before trying to find the parent of the root
directory.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-03-13 20:10:42 +00:00
|
|
|
if (ceil_offset < 0)
|
|
|
|
ceil_offset = min_offset - 2;
|
2005-08-17 01:06:34 +00:00
|
|
|
|
2019-08-24 22:10:45 +00:00
|
|
|
if (min_offset && min_offset == dir->len &&
|
|
|
|
!is_dir_sep(dir->buf[min_offset - 1])) {
|
|
|
|
strbuf_addch(dir, '/');
|
|
|
|
min_offset++;
|
|
|
|
}
|
|
|
|
|
2007-06-06 07:10:42 +00:00
|
|
|
/*
|
setup_git_directory_1(): avoid changing global state
For historical reasons, Git searches for the .git/ directory (or the
.git file) by changing the working directory successively to the parent
directory of the current directory, until either anything was found or
until a ceiling or a mount point is hit.
Further global state may be changed in case a .git/ directory was found.
We do have a use case, though, where we would like to find the .git/
directory without having any global state touched, though: when we read
the early config e.g. for the pager or for alias expansion.
Let's just move all of code that changes any global state out of the
function `setup_git_directory_gently_1()` into
`setup_git_directory_gently()`.
In subsequent patches, we will use the _1() function in a new
`discover_git_directory()` function that we will then use for the early
config code.
Note: the new loop is a *little* tricky, as we have to handle the root
directory specially: we cannot simply strip away the last component
including the slash, as the root directory only has that slash. To remedy
that, we introduce the `min_offset` variable that holds the minimal length
of an absolute path, and using that to special-case the root directory,
including an early exit before trying to find the parent of the root
directory.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-03-13 20:10:42 +00:00
|
|
|
* Test in the following order (relative to the dir):
|
2008-02-20 22:13:13 +00:00
|
|
|
* - .git (file containing "gitdir: <path>")
|
Clean up work-tree handling
The old version of work-tree support was an unholy mess, barely readable,
and not to the point.
For example, why do you have to provide a worktree, when it is not used?
As in "git status". Now it works.
Another riddle was: if you can have work trees inside the git dir, why
are some programs complaining that they need a work tree?
IOW it is allowed to call
$ git --git-dir=../ --work-tree=. bla
when you really want to. In this case, you are both in the git directory
and in the working tree. So, programs have to actually test for the right
thing, namely if they are inside a working tree, and not if they are
inside a git directory.
Also, GIT_DIR=../.git should behave the same as if no GIT_DIR was
specified, unless there is a repository in the current working directory.
It does now.
The logic to determine if a repository is bare, or has a work tree
(tertium non datur), is this:
--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.
In related news, a long standing bug was fixed: when in .git/bla/x.git/,
which is a bare repository, git formerly assumed ../.. to be the
appropriate git dir. This problem was reported by Shawn Pearce to have
caused much pain, where a colleague mistakenly ran "git init" in "/" a
long time ago, and bare repositories just would not work.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2007-08-01 00:30:14 +00:00
|
|
|
* - .git/
|
|
|
|
* - ./ (bare)
|
2008-02-20 22:13:13 +00:00
|
|
|
* - ../.git
|
Clean up work-tree handling
The old version of work-tree support was an unholy mess, barely readable,
and not to the point.
For example, why do you have to provide a worktree, when it is not used?
As in "git status". Now it works.
Another riddle was: if you can have work trees inside the git dir, why
are some programs complaining that they need a work tree?
IOW it is allowed to call
$ git --git-dir=../ --work-tree=. bla
when you really want to. In this case, you are both in the git directory
and in the working tree. So, programs have to actually test for the right
thing, namely if they are inside a working tree, and not if they are
inside a git directory.
Also, GIT_DIR=../.git should behave the same as if no GIT_DIR was
specified, unless there is a repository in the current working directory.
It does now.
The logic to determine if a repository is bare, or has a work tree
(tertium non datur), is this:
--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.
In related news, a long standing bug was fixed: when in .git/bla/x.git/,
which is a bare repository, git formerly assumed ../.. to be the
appropriate git dir. This problem was reported by Shawn Pearce to have
caused much pain, where a colleague mistakenly ran "git init" in "/" a
long time ago, and bare repositories just would not work.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2007-08-01 00:30:14 +00:00
|
|
|
* - ../.git/
|
|
|
|
* - ../ (bare)
|
2017-12-07 08:59:49 +00:00
|
|
|
* - ../../.git
|
Clean up work-tree handling
The old version of work-tree support was an unholy mess, barely readable,
and not to the point.
For example, why do you have to provide a worktree, when it is not used?
As in "git status". Now it works.
Another riddle was: if you can have work trees inside the git dir, why
are some programs complaining that they need a work tree?
IOW it is allowed to call
$ git --git-dir=../ --work-tree=. bla
when you really want to. In this case, you are both in the git directory
and in the working tree. So, programs have to actually test for the right
thing, namely if they are inside a working tree, and not if they are
inside a git directory.
Also, GIT_DIR=../.git should behave the same as if no GIT_DIR was
specified, unless there is a repository in the current working directory.
It does now.
The logic to determine if a repository is bare, or has a work tree
(tertium non datur), is this:
--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.
In related news, a long standing bug was fixed: when in .git/bla/x.git/,
which is a bare repository, git formerly assumed ../.. to be the
appropriate git dir. This problem was reported by Shawn Pearce to have
caused much pain, where a colleague mistakenly ran "git init" in "/" a
long time ago, and bare repositories just would not work.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2007-08-01 00:30:14 +00:00
|
|
|
* etc.
|
2007-06-06 07:10:42 +00:00
|
|
|
*/
|
2010-04-04 21:49:31 +00:00
|
|
|
one_filesystem = !git_env_bool("GIT_DISCOVERY_ACROSS_FILESYSTEM", 0);
|
2010-07-24 11:27:58 +00:00
|
|
|
if (one_filesystem)
|
setup_git_directory_1(): avoid changing global state
For historical reasons, Git searches for the .git/ directory (or the
.git file) by changing the working directory successively to the parent
directory of the current directory, until either anything was found or
until a ceiling or a mount point is hit.
Further global state may be changed in case a .git/ directory was found.
We do have a use case, though, where we would like to find the .git/
directory without having any global state touched, though: when we read
the early config e.g. for the pager or for alias expansion.
Let's just move all of code that changes any global state out of the
function `setup_git_directory_gently_1()` into
`setup_git_directory_gently()`.
In subsequent patches, we will use the _1() function in a new
`discover_git_directory()` function that we will then use for the early
config code.
Note: the new loop is a *little* tricky, as we have to handle the root
directory specially: we cannot simply strip away the last component
including the slash, as the root directory only has that slash. To remedy
that, we introduce the `min_offset` variable that holds the minimal length
of an absolute path, and using that to special-case the root directory,
including an early exit before trying to find the parent of the root
directory.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-03-13 20:10:42 +00:00
|
|
|
current_device = get_device_or_die(dir->buf, NULL, 0);
|
Clean up work-tree handling
The old version of work-tree support was an unholy mess, barely readable,
and not to the point.
For example, why do you have to provide a worktree, when it is not used?
As in "git status". Now it works.
Another riddle was: if you can have work trees inside the git dir, why
are some programs complaining that they need a work tree?
IOW it is allowed to call
$ git --git-dir=../ --work-tree=. bla
when you really want to. In this case, you are both in the git directory
and in the working tree. So, programs have to actually test for the right
thing, namely if they are inside a working tree, and not if they are
inside a git directory.
Also, GIT_DIR=../.git should behave the same as if no GIT_DIR was
specified, unless there is a repository in the current working directory.
It does now.
The logic to determine if a repository is bare, or has a work tree
(tertium non datur), is this:
--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.
In related news, a long standing bug was fixed: when in .git/bla/x.git/,
which is a bare repository, git formerly assumed ../.. to be the
appropriate git dir. This problem was reported by Shawn Pearce to have
caused much pain, where a colleague mistakenly ran "git init" in "/" a
long time ago, and bare repositories just would not work.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2007-08-01 00:30:14 +00:00
|
|
|
for (;;) {
|
2017-03-13 20:11:22 +00:00
|
|
|
int offset = dir->len, error_code = 0;
|
2022-05-10 19:35:29 +00:00
|
|
|
char *gitdir_path = NULL;
|
|
|
|
char *gitfile = NULL;
|
setup_git_directory_1(): avoid changing global state
For historical reasons, Git searches for the .git/ directory (or the
.git file) by changing the working directory successively to the parent
directory of the current directory, until either anything was found or
until a ceiling or a mount point is hit.
Further global state may be changed in case a .git/ directory was found.
We do have a use case, though, where we would like to find the .git/
directory without having any global state touched, though: when we read
the early config e.g. for the pager or for alias expansion.
Let's just move all of code that changes any global state out of the
function `setup_git_directory_gently_1()` into
`setup_git_directory_gently()`.
In subsequent patches, we will use the _1() function in a new
`discover_git_directory()` function that we will then use for the early
config code.
Note: the new loop is a *little* tricky, as we have to handle the root
directory specially: we cannot simply strip away the last component
including the slash, as the root directory only has that slash. To remedy
that, we introduce the `min_offset` variable that holds the minimal length
of an absolute path, and using that to special-case the root directory,
including an early exit before trying to find the parent of the root
directory.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-03-13 20:10:42 +00:00
|
|
|
|
|
|
|
if (offset > min_offset)
|
|
|
|
strbuf_addch(dir, '/');
|
|
|
|
strbuf_addstr(dir, DEFAULT_GIT_DIR_ENVIRONMENT);
|
2017-03-13 20:11:22 +00:00
|
|
|
gitdirenv = read_gitfile_gently(dir->buf, die_on_error ?
|
|
|
|
NULL : &error_code);
|
|
|
|
if (!gitdirenv) {
|
|
|
|
if (die_on_error ||
|
|
|
|
error_code == READ_GITFILE_ERR_NOT_A_FILE) {
|
2017-03-13 20:12:18 +00:00
|
|
|
/* NEEDSWORK: fail if .git is not file nor dir */
|
2022-05-10 19:35:29 +00:00
|
|
|
if (is_git_directory(dir->buf)) {
|
2017-03-13 20:11:22 +00:00
|
|
|
gitdirenv = DEFAULT_GIT_DIR_ENVIRONMENT;
|
2022-05-10 19:35:29 +00:00
|
|
|
gitdir_path = xstrdup(dir->buf);
|
|
|
|
}
|
2017-03-13 20:11:22 +00:00
|
|
|
} else if (error_code != READ_GITFILE_ERR_STAT_FAILED)
|
|
|
|
return GIT_DIR_INVALID_GITFILE;
|
2022-05-10 19:35:29 +00:00
|
|
|
} else
|
|
|
|
gitfile = xstrdup(dir->buf);
|
|
|
|
/*
|
|
|
|
* Earlier, we tentatively added DEFAULT_GIT_DIR_ENVIRONMENT
|
|
|
|
* to check that directory for a repository.
|
|
|
|
* Now trim that tentative addition away, because we want to
|
|
|
|
* focus on the real directory we are in.
|
|
|
|
*/
|
setup_git_directory_1(): avoid changing global state
For historical reasons, Git searches for the .git/ directory (or the
.git file) by changing the working directory successively to the parent
directory of the current directory, until either anything was found or
until a ceiling or a mount point is hit.
Further global state may be changed in case a .git/ directory was found.
We do have a use case, though, where we would like to find the .git/
directory without having any global state touched, though: when we read
the early config e.g. for the pager or for alias expansion.
Let's just move all of code that changes any global state out of the
function `setup_git_directory_gently_1()` into
`setup_git_directory_gently()`.
In subsequent patches, we will use the _1() function in a new
`discover_git_directory()` function that we will then use for the early
config code.
Note: the new loop is a *little* tricky, as we have to handle the root
directory specially: we cannot simply strip away the last component
including the slash, as the root directory only has that slash. To remedy
that, we introduce the `min_offset` variable that holds the minimal length
of an absolute path, and using that to special-case the root directory,
including an early exit before trying to find the parent of the root
directory.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-03-13 20:10:42 +00:00
|
|
|
strbuf_setlen(dir, offset);
|
2010-11-26 15:32:38 +00:00
|
|
|
if (gitdirenv) {
|
2022-05-10 19:35:29 +00:00
|
|
|
enum discovery_result ret;
|
2022-08-08 13:27:46 +00:00
|
|
|
const char *gitdir_candidate =
|
|
|
|
gitdir_path ? gitdir_path : gitdirenv;
|
2022-05-10 19:35:29 +00:00
|
|
|
|
2022-08-08 13:27:46 +00:00
|
|
|
if (ensure_valid_ownership(gitfile, dir->buf,
|
2022-08-08 13:27:47 +00:00
|
|
|
gitdir_candidate, report)) {
|
2022-05-10 19:35:29 +00:00
|
|
|
strbuf_addstr(gitdir, gitdirenv);
|
|
|
|
ret = GIT_DIR_DISCOVERED;
|
|
|
|
} else
|
|
|
|
ret = GIT_DIR_INVALID_OWNERSHIP;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Earlier, during discovery, we might have allocated
|
|
|
|
* string copies for gitdir_path or gitfile so make
|
|
|
|
* sure we don't leak by freeing them now, before
|
|
|
|
* leaving the loop and function.
|
|
|
|
*
|
|
|
|
* Note: gitdirenv will be non-NULL whenever these are
|
|
|
|
* allocated, therefore we need not take care of releasing
|
|
|
|
* them outside of this conditional block.
|
|
|
|
*/
|
|
|
|
free(gitdir_path);
|
|
|
|
free(gitfile);
|
|
|
|
|
|
|
|
return ret;
|
2010-11-26 15:32:38 +00:00
|
|
|
}
|
|
|
|
|
setup_git_directory_1(): avoid changing global state
For historical reasons, Git searches for the .git/ directory (or the
.git file) by changing the working directory successively to the parent
directory of the current directory, until either anything was found or
until a ceiling or a mount point is hit.
Further global state may be changed in case a .git/ directory was found.
We do have a use case, though, where we would like to find the .git/
directory without having any global state touched, though: when we read
the early config e.g. for the pager or for alias expansion.
Let's just move all of code that changes any global state out of the
function `setup_git_directory_gently_1()` into
`setup_git_directory_gently()`.
In subsequent patches, we will use the _1() function in a new
`discover_git_directory()` function that we will then use for the early
config code.
Note: the new loop is a *little* tricky, as we have to handle the root
directory specially: we cannot simply strip away the last component
including the slash, as the root directory only has that slash. To remedy
that, we introduce the `min_offset` variable that holds the minimal length
of an absolute path, and using that to special-case the root directory,
including an early exit before trying to find the parent of the root
directory.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-03-13 20:10:42 +00:00
|
|
|
if (is_git_directory(dir->buf)) {
|
setup: trace bare repository setups
safe.bareRepository=explicit is a safer default mode of operation, since
it guards against the embedded bare repository attack [1]. Most end
users don't use bare repositories directly, so they should be able to
set safe.bareRepository=explicit, with the expectation that they can
reenable bare repositories by specifying GIT_DIR or --git-dir.
However, the user might use a tool that invokes Git on bare repositories
without setting GIT_DIR (e.g. "go mod" will clone bare repositories
[2]), so even if a user wanted to use safe.bareRepository=explicit, it
wouldn't be feasible until their tools learned to set GIT_DIR.
To make this transition easier, add a trace message to note when we
attempt to set up a bare repository without setting GIT_DIR. This allows
users and tool developers to audit which of their tools are problematic
and report/fix the issue. When they are sufficiently confident, they
would switch over to "safe.bareRepository=explicit".
Note that this uses trace2_data_string(), which isn't supported by the
"normal" GIT_TRACE2 target, only _EVENT or _PERF.
[1] https://lore.kernel.org/git/kl6lsfqpygsj.fsf@chooglen-macbookpro.roam.corp.google.com/
[2] https://go.dev/ref/mod
Signed-off-by: Glen Choo <chooglen@google.com>
Signed-off-by: Josh Steadmon <steadmon@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-05-01 17:30:37 +00:00
|
|
|
trace2_data_string("setup", NULL, "implicit-bare-repository", dir->buf);
|
setup.c: create `safe.bareRepository`
There is a known social engineering attack that takes advantage of the
fact that a working tree can include an entire bare repository,
including a config file. A user could run a Git command inside the bare
repository thinking that the config file of the 'outer' repository would
be used, but in reality, the bare repository's config file (which is
attacker-controlled) is used, which may result in arbitrary code
execution. See [1] for a fuller description and deeper discussion.
A simple mitigation is to forbid bare repositories unless specified via
`--git-dir` or `GIT_DIR`. In environments that don't use bare
repositories, this would be minimally disruptive.
Create a config variable, `safe.bareRepository`, that tells Git whether
or not to die() when working with a bare repository. This config is an
enum of:
- "all": allow all bare repositories (this is the default)
- "explicit": only allow bare repositories specified via --git-dir
or GIT_DIR.
If we want to protect users from such attacks by default, neither value
will suffice - "all" provides no protection, but "explicit" is
impractical for bare repository users. A more usable default would be to
allow only non-embedded bare repositories ([2] contains one such
proposal), but detecting if a repository is embedded is potentially
non-trivial, so this work is not implemented in this series.
[1]: https://lore.kernel.org/git/kl6lsfqpygsj.fsf@chooglen-macbookpro.roam.corp.google.com
[2]: https://lore.kernel.org/git/5b969c5e-e802-c447-ad25-6acc0b784582@github.com
Signed-off-by: Glen Choo <chooglen@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-07-14 21:28:01 +00:00
|
|
|
if (get_allowed_bare_repo() == ALLOWED_BARE_REPO_EXPLICIT)
|
|
|
|
return GIT_DIR_DISALLOWED_BARE;
|
2022-08-08 13:27:47 +00:00
|
|
|
if (!ensure_valid_ownership(NULL, NULL, dir->buf, report))
|
2022-03-02 11:23:04 +00:00
|
|
|
return GIT_DIR_INVALID_OWNERSHIP;
|
setup_git_directory_1(): avoid changing global state
For historical reasons, Git searches for the .git/ directory (or the
.git file) by changing the working directory successively to the parent
directory of the current directory, until either anything was found or
until a ceiling or a mount point is hit.
Further global state may be changed in case a .git/ directory was found.
We do have a use case, though, where we would like to find the .git/
directory without having any global state touched, though: when we read
the early config e.g. for the pager or for alias expansion.
Let's just move all of code that changes any global state out of the
function `setup_git_directory_gently_1()` into
`setup_git_directory_gently()`.
In subsequent patches, we will use the _1() function in a new
`discover_git_directory()` function that we will then use for the early
config code.
Note: the new loop is a *little* tricky, as we have to handle the root
directory specially: we cannot simply strip away the last component
including the slash, as the root directory only has that slash. To remedy
that, we introduce the `min_offset` variable that holds the minimal length
of an absolute path, and using that to special-case the root directory,
including an early exit before trying to find the parent of the root
directory.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-03-13 20:10:42 +00:00
|
|
|
strbuf_addstr(gitdir, ".");
|
|
|
|
return GIT_DIR_BARE;
|
2010-03-17 19:55:52 +00:00
|
|
|
}
|
2010-11-26 15:32:38 +00:00
|
|
|
|
setup_git_directory_1(): avoid changing global state
For historical reasons, Git searches for the .git/ directory (or the
.git file) by changing the working directory successively to the parent
directory of the current directory, until either anything was found or
until a ceiling or a mount point is hit.
Further global state may be changed in case a .git/ directory was found.
We do have a use case, though, where we would like to find the .git/
directory without having any global state touched, though: when we read
the early config e.g. for the pager or for alias expansion.
Let's just move all of code that changes any global state out of the
function `setup_git_directory_gently_1()` into
`setup_git_directory_gently()`.
In subsequent patches, we will use the _1() function in a new
`discover_git_directory()` function that we will then use for the early
config code.
Note: the new loop is a *little* tricky, as we have to handle the root
directory specially: we cannot simply strip away the last component
including the slash, as the root directory only has that slash. To remedy
that, we introduce the `min_offset` variable that holds the minimal length
of an absolute path, and using that to special-case the root directory,
including an early exit before trying to find the parent of the root
directory.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-03-13 20:10:42 +00:00
|
|
|
if (offset <= min_offset)
|
|
|
|
return GIT_DIR_HIT_CEILING;
|
2010-11-26 15:32:36 +00:00
|
|
|
|
setup_git_directory_1(): avoid changing global state
For historical reasons, Git searches for the .git/ directory (or the
.git file) by changing the working directory successively to the parent
directory of the current directory, until either anything was found or
until a ceiling or a mount point is hit.
Further global state may be changed in case a .git/ directory was found.
We do have a use case, though, where we would like to find the .git/
directory without having any global state touched, though: when we read
the early config e.g. for the pager or for alias expansion.
Let's just move all of code that changes any global state out of the
function `setup_git_directory_gently_1()` into
`setup_git_directory_gently()`.
In subsequent patches, we will use the _1() function in a new
`discover_git_directory()` function that we will then use for the early
config code.
Note: the new loop is a *little* tricky, as we have to handle the root
directory specially: we cannot simply strip away the last component
including the slash, as the root directory only has that slash. To remedy
that, we introduce the `min_offset` variable that holds the minimal length
of an absolute path, and using that to special-case the root directory,
including an early exit before trying to find the parent of the root
directory.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-03-13 20:10:42 +00:00
|
|
|
while (--offset > ceil_offset && !is_dir_sep(dir->buf[offset]))
|
2017-03-07 14:32:32 +00:00
|
|
|
; /* continue */
|
setup_git_directory_1(): avoid changing global state
For historical reasons, Git searches for the .git/ directory (or the
.git file) by changing the working directory successively to the parent
directory of the current directory, until either anything was found or
until a ceiling or a mount point is hit.
Further global state may be changed in case a .git/ directory was found.
We do have a use case, though, where we would like to find the .git/
directory without having any global state touched, though: when we read
the early config e.g. for the pager or for alias expansion.
Let's just move all of code that changes any global state out of the
function `setup_git_directory_gently_1()` into
`setup_git_directory_gently()`.
In subsequent patches, we will use the _1() function in a new
`discover_git_directory()` function that we will then use for the early
config code.
Note: the new loop is a *little* tricky, as we have to handle the root
directory specially: we cannot simply strip away the last component
including the slash, as the root directory only has that slash. To remedy
that, we introduce the `min_offset` variable that holds the minimal length
of an absolute path, and using that to special-case the root directory,
including an early exit before trying to find the parent of the root
directory.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-03-13 20:10:42 +00:00
|
|
|
if (offset <= ceil_offset)
|
|
|
|
return GIT_DIR_HIT_CEILING;
|
|
|
|
|
|
|
|
strbuf_setlen(dir, offset > min_offset ? offset : min_offset);
|
|
|
|
if (one_filesystem &&
|
|
|
|
current_device != get_device_or_die(dir->buf, NULL, offset))
|
|
|
|
return GIT_DIR_HIT_MOUNT_POINT;
|
2007-06-06 07:10:42 +00:00
|
|
|
}
|
2005-08-17 01:06:34 +00:00
|
|
|
}
|
2005-11-25 23:43:41 +00:00
|
|
|
|
2017-06-14 18:07:37 +00:00
|
|
|
int discover_git_directory(struct strbuf *commondir,
|
|
|
|
struct strbuf *gitdir)
|
2017-03-13 20:10:45 +00:00
|
|
|
{
|
|
|
|
struct strbuf dir = STRBUF_INIT, err = STRBUF_INIT;
|
|
|
|
size_t gitdir_offset = gitdir->len, cwd_len;
|
2017-06-14 18:07:37 +00:00
|
|
|
size_t commondir_offset = commondir->len;
|
setup: fix memory leaks with `struct repository_format`
After we set up a `struct repository_format`, it owns various pieces of
allocated memory. We then either use those members, because we decide we
want to use the "candidate" repository format, or we discard the
candidate / scratch space. In the first case, we transfer ownership of
the memory to a few global variables. In the latter case, we just
silently drop the struct and end up leaking memory.
Introduce an initialization macro `REPOSITORY_FORMAT_INIT` and a
function `clear_repository_format()`, to be used on each side of
`read_repository_format()`. To have a clear and simple memory ownership,
let all users of `struct repository_format` duplicate the strings that
they take from it, rather than stealing the pointers.
Call `clear_...()` at the start of `read_...()` instead of just zeroing
the struct, since we sometimes enter the function multiple times. Thus,
it is important to initialize the struct before calling `read_...()`, so
document that. It's also important because we might not even call
`read_...()` before we call `clear_...()`, see, e.g., builtin/init-db.c.
Teach `read_...()` to clear the struct on error, so that it is reset to
a safe state, and document this. (In `setup_git_directory_gently()`, we
look at `repo_fmt.hash_algo` even if `repo_fmt.version` is -1, which we
weren't actually supposed to do per the API. After this commit, that's
ok.)
We inherit the existing code's combining "error" and "no version found".
Both are signalled through `version == -1` and now both cause us to
clear any partial configuration we have picked up. For "extensions.*",
that's fine, since they require a positive version number. For
"core.bare" and "core.worktree", we're already verifying that we have a
non-negative version number before using them.
Signed-off-by: Martin Ågren <martin.agren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-02-28 20:36:28 +00:00
|
|
|
struct repository_format candidate = REPOSITORY_FORMAT_INIT;
|
2017-03-13 20:10:45 +00:00
|
|
|
|
|
|
|
if (strbuf_getcwd(&dir))
|
2017-06-14 18:07:37 +00:00
|
|
|
return -1;
|
2017-03-13 20:10:45 +00:00
|
|
|
|
|
|
|
cwd_len = dir.len;
|
2022-08-08 13:27:47 +00:00
|
|
|
if (setup_git_directory_gently_1(&dir, gitdir, NULL, 0) <= 0) {
|
2017-03-13 20:10:45 +00:00
|
|
|
strbuf_release(&dir);
|
2017-06-14 18:07:37 +00:00
|
|
|
return -1;
|
2017-03-13 20:10:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The returned gitdir is relative to dir, and if dir does not reflect
|
|
|
|
* the current working directory, we simply make the gitdir absolute.
|
|
|
|
*/
|
|
|
|
if (dir.len < cwd_len && !is_absolute_path(gitdir->buf + gitdir_offset)) {
|
|
|
|
/* Avoid a trailing "/." */
|
|
|
|
if (!strcmp(".", gitdir->buf + gitdir_offset))
|
|
|
|
strbuf_setlen(gitdir, gitdir_offset);
|
|
|
|
else
|
|
|
|
strbuf_addch(&dir, '/');
|
|
|
|
strbuf_insert(gitdir, gitdir_offset, dir.buf, dir.len);
|
|
|
|
}
|
|
|
|
|
2017-06-14 18:07:37 +00:00
|
|
|
get_common_dir(commondir, gitdir->buf + gitdir_offset);
|
|
|
|
|
2017-03-13 20:10:45 +00:00
|
|
|
strbuf_reset(&dir);
|
2017-06-14 18:07:37 +00:00
|
|
|
strbuf_addf(&dir, "%s/config", commondir->buf + commondir_offset);
|
2017-03-13 20:10:45 +00:00
|
|
|
read_repository_format(&candidate, dir.buf);
|
|
|
|
strbuf_release(&dir);
|
|
|
|
|
|
|
|
if (verify_repository_format(&candidate, &err) < 0) {
|
|
|
|
warning("ignoring git dir '%s': %s",
|
|
|
|
gitdir->buf + gitdir_offset, err.buf);
|
|
|
|
strbuf_release(&err);
|
2017-06-14 18:07:37 +00:00
|
|
|
strbuf_setlen(commondir, commondir_offset);
|
2017-06-14 11:35:26 +00:00
|
|
|
strbuf_setlen(gitdir, gitdir_offset);
|
setup: fix memory leaks with `struct repository_format`
After we set up a `struct repository_format`, it owns various pieces of
allocated memory. We then either use those members, because we decide we
want to use the "candidate" repository format, or we discard the
candidate / scratch space. In the first case, we transfer ownership of
the memory to a few global variables. In the latter case, we just
silently drop the struct and end up leaking memory.
Introduce an initialization macro `REPOSITORY_FORMAT_INIT` and a
function `clear_repository_format()`, to be used on each side of
`read_repository_format()`. To have a clear and simple memory ownership,
let all users of `struct repository_format` duplicate the strings that
they take from it, rather than stealing the pointers.
Call `clear_...()` at the start of `read_...()` instead of just zeroing
the struct, since we sometimes enter the function multiple times. Thus,
it is important to initialize the struct before calling `read_...()`, so
document that. It's also important because we might not even call
`read_...()` before we call `clear_...()`, see, e.g., builtin/init-db.c.
Teach `read_...()` to clear the struct on error, so that it is reset to
a safe state, and document this. (In `setup_git_directory_gently()`, we
look at `repo_fmt.hash_algo` even if `repo_fmt.version` is -1, which we
weren't actually supposed to do per the API. After this commit, that's
ok.)
We inherit the existing code's combining "error" and "no version found".
Both are signalled through `version == -1` and now both cause us to
clear any partial configuration we have picked up. For "extensions.*",
that's fine, since they require a positive version number. For
"core.bare" and "core.worktree", we're already verifying that we have a
non-negative version number before using them.
Signed-off-by: Martin Ågren <martin.agren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-02-28 20:36:28 +00:00
|
|
|
clear_repository_format(&candidate);
|
2017-06-14 18:07:37 +00:00
|
|
|
return -1;
|
2017-03-13 20:10:45 +00:00
|
|
|
}
|
|
|
|
|
2021-06-17 17:13:22 +00:00
|
|
|
/* take ownership of candidate.partial_clone */
|
|
|
|
the_repository->repository_format_partial_clone =
|
|
|
|
candidate.partial_clone;
|
|
|
|
candidate.partial_clone = NULL;
|
|
|
|
|
setup: fix memory leaks with `struct repository_format`
After we set up a `struct repository_format`, it owns various pieces of
allocated memory. We then either use those members, because we decide we
want to use the "candidate" repository format, or we discard the
candidate / scratch space. In the first case, we transfer ownership of
the memory to a few global variables. In the latter case, we just
silently drop the struct and end up leaking memory.
Introduce an initialization macro `REPOSITORY_FORMAT_INIT` and a
function `clear_repository_format()`, to be used on each side of
`read_repository_format()`. To have a clear and simple memory ownership,
let all users of `struct repository_format` duplicate the strings that
they take from it, rather than stealing the pointers.
Call `clear_...()` at the start of `read_...()` instead of just zeroing
the struct, since we sometimes enter the function multiple times. Thus,
it is important to initialize the struct before calling `read_...()`, so
document that. It's also important because we might not even call
`read_...()` before we call `clear_...()`, see, e.g., builtin/init-db.c.
Teach `read_...()` to clear the struct on error, so that it is reset to
a safe state, and document this. (In `setup_git_directory_gently()`, we
look at `repo_fmt.hash_algo` even if `repo_fmt.version` is -1, which we
weren't actually supposed to do per the API. After this commit, that's
ok.)
We inherit the existing code's combining "error" and "no version found".
Both are signalled through `version == -1` and now both cause us to
clear any partial configuration we have picked up. For "extensions.*",
that's fine, since they require a positive version number. For
"core.bare" and "core.worktree", we're already verifying that we have a
non-negative version number before using them.
Signed-off-by: Martin Ågren <martin.agren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-02-28 20:36:28 +00:00
|
|
|
clear_repository_format(&candidate);
|
2017-06-14 18:07:37 +00:00
|
|
|
return 0;
|
2017-03-13 20:10:45 +00:00
|
|
|
}
|
|
|
|
|
2010-08-06 02:46:33 +00:00
|
|
|
const char *setup_git_directory_gently(int *nongit_ok)
|
|
|
|
{
|
setup_git_directory_1(): avoid changing global state
For historical reasons, Git searches for the .git/ directory (or the
.git file) by changing the working directory successively to the parent
directory of the current directory, until either anything was found or
until a ceiling or a mount point is hit.
Further global state may be changed in case a .git/ directory was found.
We do have a use case, though, where we would like to find the .git/
directory without having any global state touched, though: when we read
the early config e.g. for the pager or for alias expansion.
Let's just move all of code that changes any global state out of the
function `setup_git_directory_gently_1()` into
`setup_git_directory_gently()`.
In subsequent patches, we will use the _1() function in a new
`discover_git_directory()` function that we will then use for the early
config code.
Note: the new loop is a *little* tricky, as we have to handle the root
directory specially: we cannot simply strip away the last component
including the slash, as the root directory only has that slash. To remedy
that, we introduce the `min_offset` variable that holds the minimal length
of an absolute path, and using that to special-case the root directory,
including an early exit before trying to find the parent of the root
directory.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-03-13 20:10:42 +00:00
|
|
|
static struct strbuf cwd = STRBUF_INIT;
|
2022-08-08 13:27:47 +00:00
|
|
|
struct strbuf dir = STRBUF_INIT, gitdir = STRBUF_INIT, report = STRBUF_INIT;
|
2018-12-27 23:36:29 +00:00
|
|
|
const char *prefix = NULL;
|
setup: fix memory leaks with `struct repository_format`
After we set up a `struct repository_format`, it owns various pieces of
allocated memory. We then either use those members, because we decide we
want to use the "candidate" repository format, or we discard the
candidate / scratch space. In the first case, we transfer ownership of
the memory to a few global variables. In the latter case, we just
silently drop the struct and end up leaking memory.
Introduce an initialization macro `REPOSITORY_FORMAT_INIT` and a
function `clear_repository_format()`, to be used on each side of
`read_repository_format()`. To have a clear and simple memory ownership,
let all users of `struct repository_format` duplicate the strings that
they take from it, rather than stealing the pointers.
Call `clear_...()` at the start of `read_...()` instead of just zeroing
the struct, since we sometimes enter the function multiple times. Thus,
it is important to initialize the struct before calling `read_...()`, so
document that. It's also important because we might not even call
`read_...()` before we call `clear_...()`, see, e.g., builtin/init-db.c.
Teach `read_...()` to clear the struct on error, so that it is reset to
a safe state, and document this. (In `setup_git_directory_gently()`, we
look at `repo_fmt.hash_algo` even if `repo_fmt.version` is -1, which we
weren't actually supposed to do per the API. After this commit, that's
ok.)
We inherit the existing code's combining "error" and "no version found".
Both are signalled through `version == -1` and now both cause us to
clear any partial configuration we have picked up. For "extensions.*",
that's fine, since they require a positive version number. For
"core.bare" and "core.worktree", we're already verifying that we have a
non-negative version number before using them.
Signed-off-by: Martin Ågren <martin.agren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-02-28 20:36:28 +00:00
|
|
|
struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
|
2010-08-06 02:46:33 +00:00
|
|
|
|
setup_git_directory_1(): avoid changing global state
For historical reasons, Git searches for the .git/ directory (or the
.git file) by changing the working directory successively to the parent
directory of the current directory, until either anything was found or
until a ceiling or a mount point is hit.
Further global state may be changed in case a .git/ directory was found.
We do have a use case, though, where we would like to find the .git/
directory without having any global state touched, though: when we read
the early config e.g. for the pager or for alias expansion.
Let's just move all of code that changes any global state out of the
function `setup_git_directory_gently_1()` into
`setup_git_directory_gently()`.
In subsequent patches, we will use the _1() function in a new
`discover_git_directory()` function that we will then use for the early
config code.
Note: the new loop is a *little* tricky, as we have to handle the root
directory specially: we cannot simply strip away the last component
including the slash, as the root directory only has that slash. To remedy
that, we introduce the `min_offset` variable that holds the minimal length
of an absolute path, and using that to special-case the root directory,
including an early exit before trying to find the parent of the root
directory.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-03-13 20:10:42 +00:00
|
|
|
/*
|
|
|
|
* We may have read an incomplete configuration before
|
|
|
|
* setting-up the git directory. If so, clear the cache so
|
|
|
|
* that the next queries to the configuration reload complete
|
|
|
|
* configuration (including the per-repo config file that we
|
|
|
|
* ignored previously).
|
|
|
|
*/
|
|
|
|
git_config_clear();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Let's assume that we are in a git repository.
|
|
|
|
* If it turns out later that we are somewhere else, the value will be
|
|
|
|
* updated accordingly.
|
|
|
|
*/
|
|
|
|
if (nongit_ok)
|
|
|
|
*nongit_ok = 0;
|
|
|
|
|
|
|
|
if (strbuf_getcwd(&cwd))
|
|
|
|
die_errno(_("Unable to read current working directory"));
|
|
|
|
strbuf_addbuf(&dir, &cwd);
|
|
|
|
|
2022-08-08 13:27:47 +00:00
|
|
|
switch (setup_git_directory_gently_1(&dir, &gitdir, &report, 1)) {
|
setup_git_directory_1(): avoid changing global state
For historical reasons, Git searches for the .git/ directory (or the
.git file) by changing the working directory successively to the parent
directory of the current directory, until either anything was found or
until a ceiling or a mount point is hit.
Further global state may be changed in case a .git/ directory was found.
We do have a use case, though, where we would like to find the .git/
directory without having any global state touched, though: when we read
the early config e.g. for the pager or for alias expansion.
Let's just move all of code that changes any global state out of the
function `setup_git_directory_gently_1()` into
`setup_git_directory_gently()`.
In subsequent patches, we will use the _1() function in a new
`discover_git_directory()` function that we will then use for the early
config code.
Note: the new loop is a *little* tricky, as we have to handle the root
directory specially: we cannot simply strip away the last component
including the slash, as the root directory only has that slash. To remedy
that, we introduce the `min_offset` variable that holds the minimal length
of an absolute path, and using that to special-case the root directory,
including an early exit before trying to find the parent of the root
directory.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-03-13 20:10:42 +00:00
|
|
|
case GIT_DIR_EXPLICIT:
|
2017-11-12 21:28:51 +00:00
|
|
|
prefix = setup_explicit_git_dir(gitdir.buf, &cwd, &repo_fmt, nongit_ok);
|
setup_git_directory_1(): avoid changing global state
For historical reasons, Git searches for the .git/ directory (or the
.git file) by changing the working directory successively to the parent
directory of the current directory, until either anything was found or
until a ceiling or a mount point is hit.
Further global state may be changed in case a .git/ directory was found.
We do have a use case, though, where we would like to find the .git/
directory without having any global state touched, though: when we read
the early config e.g. for the pager or for alias expansion.
Let's just move all of code that changes any global state out of the
function `setup_git_directory_gently_1()` into
`setup_git_directory_gently()`.
In subsequent patches, we will use the _1() function in a new
`discover_git_directory()` function that we will then use for the early
config code.
Note: the new loop is a *little* tricky, as we have to handle the root
directory specially: we cannot simply strip away the last component
including the slash, as the root directory only has that slash. To remedy
that, we introduce the `min_offset` variable that holds the minimal length
of an absolute path, and using that to special-case the root directory,
including an early exit before trying to find the parent of the root
directory.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-03-13 20:10:42 +00:00
|
|
|
break;
|
|
|
|
case GIT_DIR_DISCOVERED:
|
|
|
|
if (dir.len < cwd.len && chdir(dir.buf))
|
2018-02-13 13:19:15 +00:00
|
|
|
die(_("cannot change to '%s'"), dir.buf);
|
setup_git_directory_1(): avoid changing global state
For historical reasons, Git searches for the .git/ directory (or the
.git file) by changing the working directory successively to the parent
directory of the current directory, until either anything was found or
until a ceiling or a mount point is hit.
Further global state may be changed in case a .git/ directory was found.
We do have a use case, though, where we would like to find the .git/
directory without having any global state touched, though: when we read
the early config e.g. for the pager or for alias expansion.
Let's just move all of code that changes any global state out of the
function `setup_git_directory_gently_1()` into
`setup_git_directory_gently()`.
In subsequent patches, we will use the _1() function in a new
`discover_git_directory()` function that we will then use for the early
config code.
Note: the new loop is a *little* tricky, as we have to handle the root
directory specially: we cannot simply strip away the last component
including the slash, as the root directory only has that slash. To remedy
that, we introduce the `min_offset` variable that holds the minimal length
of an absolute path, and using that to special-case the root directory,
including an early exit before trying to find the parent of the root
directory.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-03-13 20:10:42 +00:00
|
|
|
prefix = setup_discovered_git_dir(gitdir.buf, &cwd, dir.len,
|
2017-11-12 21:28:51 +00:00
|
|
|
&repo_fmt, nongit_ok);
|
setup_git_directory_1(): avoid changing global state
For historical reasons, Git searches for the .git/ directory (or the
.git file) by changing the working directory successively to the parent
directory of the current directory, until either anything was found or
until a ceiling or a mount point is hit.
Further global state may be changed in case a .git/ directory was found.
We do have a use case, though, where we would like to find the .git/
directory without having any global state touched, though: when we read
the early config e.g. for the pager or for alias expansion.
Let's just move all of code that changes any global state out of the
function `setup_git_directory_gently_1()` into
`setup_git_directory_gently()`.
In subsequent patches, we will use the _1() function in a new
`discover_git_directory()` function that we will then use for the early
config code.
Note: the new loop is a *little* tricky, as we have to handle the root
directory specially: we cannot simply strip away the last component
including the slash, as the root directory only has that slash. To remedy
that, we introduce the `min_offset` variable that holds the minimal length
of an absolute path, and using that to special-case the root directory,
including an early exit before trying to find the parent of the root
directory.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-03-13 20:10:42 +00:00
|
|
|
break;
|
|
|
|
case GIT_DIR_BARE:
|
|
|
|
if (dir.len < cwd.len && chdir(dir.buf))
|
2018-02-13 13:19:15 +00:00
|
|
|
die(_("cannot change to '%s'"), dir.buf);
|
2017-11-12 21:28:51 +00:00
|
|
|
prefix = setup_bare_git_dir(&cwd, dir.len, &repo_fmt, nongit_ok);
|
setup_git_directory_1(): avoid changing global state
For historical reasons, Git searches for the .git/ directory (or the
.git file) by changing the working directory successively to the parent
directory of the current directory, until either anything was found or
until a ceiling or a mount point is hit.
Further global state may be changed in case a .git/ directory was found.
We do have a use case, though, where we would like to find the .git/
directory without having any global state touched, though: when we read
the early config e.g. for the pager or for alias expansion.
Let's just move all of code that changes any global state out of the
function `setup_git_directory_gently_1()` into
`setup_git_directory_gently()`.
In subsequent patches, we will use the _1() function in a new
`discover_git_directory()` function that we will then use for the early
config code.
Note: the new loop is a *little* tricky, as we have to handle the root
directory specially: we cannot simply strip away the last component
including the slash, as the root directory only has that slash. To remedy
that, we introduce the `min_offset` variable that holds the minimal length
of an absolute path, and using that to special-case the root directory,
including an early exit before trying to find the parent of the root
directory.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-03-13 20:10:42 +00:00
|
|
|
break;
|
|
|
|
case GIT_DIR_HIT_CEILING:
|
2018-12-27 23:36:29 +00:00
|
|
|
if (!nongit_ok)
|
|
|
|
die(_("not a git repository (or any of the parent directories): %s"),
|
|
|
|
DEFAULT_GIT_DIR_ENVIRONMENT);
|
|
|
|
*nongit_ok = 1;
|
setup_git_directory_1(): avoid changing global state
For historical reasons, Git searches for the .git/ directory (or the
.git file) by changing the working directory successively to the parent
directory of the current directory, until either anything was found or
until a ceiling or a mount point is hit.
Further global state may be changed in case a .git/ directory was found.
We do have a use case, though, where we would like to find the .git/
directory without having any global state touched, though: when we read
the early config e.g. for the pager or for alias expansion.
Let's just move all of code that changes any global state out of the
function `setup_git_directory_gently_1()` into
`setup_git_directory_gently()`.
In subsequent patches, we will use the _1() function in a new
`discover_git_directory()` function that we will then use for the early
config code.
Note: the new loop is a *little* tricky, as we have to handle the root
directory specially: we cannot simply strip away the last component
including the slash, as the root directory only has that slash. To remedy
that, we introduce the `min_offset` variable that holds the minimal length
of an absolute path, and using that to special-case the root directory,
including an early exit before trying to find the parent of the root
directory.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-03-13 20:10:42 +00:00
|
|
|
break;
|
|
|
|
case GIT_DIR_HIT_MOUNT_POINT:
|
2018-12-27 23:36:29 +00:00
|
|
|
if (!nongit_ok)
|
|
|
|
die(_("not a git repository (or any parent up to mount point %s)\n"
|
|
|
|
"Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."),
|
|
|
|
dir.buf);
|
|
|
|
*nongit_ok = 1;
|
|
|
|
break;
|
2022-03-02 11:23:04 +00:00
|
|
|
case GIT_DIR_INVALID_OWNERSHIP:
|
|
|
|
if (!nongit_ok) {
|
|
|
|
struct strbuf quoted = STRBUF_INIT;
|
|
|
|
|
2022-08-08 13:27:47 +00:00
|
|
|
strbuf_complete(&report, '\n');
|
2022-03-02 11:23:04 +00:00
|
|
|
sq_quote_buf_pretty("ed, dir.buf);
|
2022-05-10 19:35:29 +00:00
|
|
|
die(_("detected dubious ownership in repository at '%s'\n"
|
2022-08-08 13:27:47 +00:00
|
|
|
"%s"
|
2022-03-02 11:23:04 +00:00
|
|
|
"To add an exception for this directory, call:\n"
|
|
|
|
"\n"
|
|
|
|
"\tgit config --global --add safe.directory %s"),
|
2022-08-08 13:27:47 +00:00
|
|
|
dir.buf, report.buf, quoted.buf);
|
2022-03-02 11:23:04 +00:00
|
|
|
}
|
|
|
|
*nongit_ok = 1;
|
|
|
|
break;
|
setup.c: create `safe.bareRepository`
There is a known social engineering attack that takes advantage of the
fact that a working tree can include an entire bare repository,
including a config file. A user could run a Git command inside the bare
repository thinking that the config file of the 'outer' repository would
be used, but in reality, the bare repository's config file (which is
attacker-controlled) is used, which may result in arbitrary code
execution. See [1] for a fuller description and deeper discussion.
A simple mitigation is to forbid bare repositories unless specified via
`--git-dir` or `GIT_DIR`. In environments that don't use bare
repositories, this would be minimally disruptive.
Create a config variable, `safe.bareRepository`, that tells Git whether
or not to die() when working with a bare repository. This config is an
enum of:
- "all": allow all bare repositories (this is the default)
- "explicit": only allow bare repositories specified via --git-dir
or GIT_DIR.
If we want to protect users from such attacks by default, neither value
will suffice - "all" provides no protection, but "explicit" is
impractical for bare repository users. A more usable default would be to
allow only non-embedded bare repositories ([2] contains one such
proposal), but detecting if a repository is embedded is potentially
non-trivial, so this work is not implemented in this series.
[1]: https://lore.kernel.org/git/kl6lsfqpygsj.fsf@chooglen-macbookpro.roam.corp.google.com
[2]: https://lore.kernel.org/git/5b969c5e-e802-c447-ad25-6acc0b784582@github.com
Signed-off-by: Glen Choo <chooglen@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-07-14 21:28:01 +00:00
|
|
|
case GIT_DIR_DISALLOWED_BARE:
|
|
|
|
if (!nongit_ok) {
|
|
|
|
die(_("cannot use bare repository '%s' (safe.bareRepository is '%s')"),
|
|
|
|
dir.buf,
|
|
|
|
allowed_bare_repo_to_string(get_allowed_bare_repo()));
|
|
|
|
}
|
|
|
|
*nongit_ok = 1;
|
|
|
|
break;
|
2018-12-27 23:36:29 +00:00
|
|
|
case GIT_DIR_NONE:
|
|
|
|
/*
|
|
|
|
* As a safeguard against setup_git_directory_gently_1 returning
|
|
|
|
* this value, fallthrough to BUG. Otherwise it is possible to
|
|
|
|
* set startup_info->have_repository to 1 when we did nothing to
|
|
|
|
* find a repository.
|
|
|
|
*/
|
setup_git_directory_1(): avoid changing global state
For historical reasons, Git searches for the .git/ directory (or the
.git file) by changing the working directory successively to the parent
directory of the current directory, until either anything was found or
until a ceiling or a mount point is hit.
Further global state may be changed in case a .git/ directory was found.
We do have a use case, though, where we would like to find the .git/
directory without having any global state touched, though: when we read
the early config e.g. for the pager or for alias expansion.
Let's just move all of code that changes any global state out of the
function `setup_git_directory_gently_1()` into
`setup_git_directory_gently()`.
In subsequent patches, we will use the _1() function in a new
`discover_git_directory()` function that we will then use for the early
config code.
Note: the new loop is a *little* tricky, as we have to handle the root
directory specially: we cannot simply strip away the last component
including the slash, as the root directory only has that slash. To remedy
that, we introduce the `min_offset` variable that holds the minimal length
of an absolute path, and using that to special-case the root directory,
including an early exit before trying to find the parent of the root
directory.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-03-13 20:10:42 +00:00
|
|
|
default:
|
2022-06-09 13:55:20 +00:00
|
|
|
BUG("unhandled setup_git_directory_gently_1() result");
|
setup_git_directory_1(): avoid changing global state
For historical reasons, Git searches for the .git/ directory (or the
.git file) by changing the working directory successively to the parent
directory of the current directory, until either anything was found or
until a ceiling or a mount point is hit.
Further global state may be changed in case a .git/ directory was found.
We do have a use case, though, where we would like to find the .git/
directory without having any global state touched, though: when we read
the early config e.g. for the pager or for alias expansion.
Let's just move all of code that changes any global state out of the
function `setup_git_directory_gently_1()` into
`setup_git_directory_gently()`.
In subsequent patches, we will use the _1() function in a new
`discover_git_directory()` function that we will then use for the early
config code.
Note: the new loop is a *little* tricky, as we have to handle the root
directory specially: we cannot simply strip away the last component
including the slash, as the root directory only has that slash. To remedy
that, we introduce the `min_offset` variable that holds the minimal length
of an absolute path, and using that to special-case the root directory,
including an early exit before trying to find the parent of the root
directory.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-03-13 20:10:42 +00:00
|
|
|
}
|
|
|
|
|
2018-12-27 23:36:29 +00:00
|
|
|
/*
|
|
|
|
* At this point, nongit_ok is stable. If it is non-NULL and points
|
|
|
|
* to a non-zero value, then this means that we haven't found a
|
|
|
|
* repository and that the caller expects startup_info to reflect
|
|
|
|
* this.
|
|
|
|
*
|
|
|
|
* Regardless of the state of nongit_ok, startup_info->prefix and
|
|
|
|
* the GIT_PREFIX environment variable must always match. For details
|
|
|
|
* see Documentation/config/alias.txt.
|
|
|
|
*/
|
2021-04-04 17:14:14 +00:00
|
|
|
if (nongit_ok && *nongit_ok)
|
2018-12-27 23:36:29 +00:00
|
|
|
startup_info->have_repository = 0;
|
2021-04-04 17:14:14 +00:00
|
|
|
else
|
2018-12-27 23:36:29 +00:00
|
|
|
startup_info->have_repository = 1;
|
setup: make startup_info available everywhere
Commit a60645f (setup: remember whether repository was
found, 2010-08-05) introduced the startup_info structure,
which records some parts of the setup_git_directory()
process (notably, whether we actually found a repository or
not).
One of the uses of this data is for functions to behave
appropriately based on whether we are in a repo. But the
startup_info struct is just a pointer to storage provided by
the main program, and the only program that sets it up is
the git.c wrapper. Thus builtins have access to
startup_info, but externally linked programs do not.
Worse, library code which is accessible from both has to be
careful about accessing startup_info. This can be used to
trigger a die("BUG") via get_sha1():
$ git fast-import <<-\EOF
tag foo
from HEAD:./whatever
EOF
fatal: BUG: startup_info struct is not initialized.
Obviously that's fairly nonsensical input to feed to
fast-import, but we should never hit a die("BUG"). And there
may be other ways to trigger it if other non-builtins
resolve sha1s.
So let's point the storage for startup_info to a static
variable in setup.c, making it available to all users of the
library code. We _could_ turn startup_info into a regular
extern struct, but doing so would mean tweaking all of the
existing use sites. So let's leave the pointer indirection
in place. We can, however, drop any checks for NULL, as
they will always be false (and likewise, we can drop the
test covering this case, which was a rather artificial
situation using one of the test-* programs).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-03-05 22:10:27 +00:00
|
|
|
|
2017-06-20 19:19:32 +00:00
|
|
|
/*
|
|
|
|
* Not all paths through the setup code will call 'set_git_dir()' (which
|
|
|
|
* directly sets up the environment) so in order to guarantee that the
|
|
|
|
* environment is in a consistent state after setup, explicitly setup
|
|
|
|
* the environment if we have a repository.
|
|
|
|
*
|
|
|
|
* NEEDSWORK: currently we allow bogus GIT_DIR values to be set in some
|
|
|
|
* code paths so we also need to explicitly setup the environment if
|
|
|
|
* the user has set GIT_DIR. It may be beneficial to disallow bogus
|
|
|
|
* GIT_DIR values at some point in the future.
|
|
|
|
*/
|
2018-12-27 23:36:29 +00:00
|
|
|
if (/* GIT_DIR_EXPLICIT, GIT_DIR_DISCOVERED, GIT_DIR_BARE */
|
|
|
|
startup_info->have_repository ||
|
|
|
|
/* GIT_DIR_EXPLICIT */
|
|
|
|
getenv(GIT_DIR_ENVIRONMENT)) {
|
2017-06-22 18:43:33 +00:00
|
|
|
if (!the_repository->gitdir) {
|
|
|
|
const char *gitdir = getenv(GIT_DIR_ENVIRONMENT);
|
|
|
|
if (!gitdir)
|
|
|
|
gitdir = DEFAULT_GIT_DIR_ENVIRONMENT;
|
2018-03-03 11:35:55 +00:00
|
|
|
setup_git_env(gitdir);
|
2017-06-22 18:43:33 +00:00
|
|
|
}
|
2021-06-17 17:13:22 +00:00
|
|
|
if (startup_info->have_repository) {
|
2017-11-12 21:28:53 +00:00
|
|
|
repo_set_hash_algo(the_repository, repo_fmt.hash_algo);
|
2021-06-17 17:13:22 +00:00
|
|
|
/* take ownership of repo_fmt.partial_clone */
|
|
|
|
the_repository->repository_format_partial_clone =
|
|
|
|
repo_fmt.partial_clone;
|
|
|
|
repo_fmt.partial_clone = NULL;
|
|
|
|
}
|
2017-06-22 18:43:33 +00:00
|
|
|
}
|
2021-04-04 17:14:14 +00:00
|
|
|
/*
|
|
|
|
* Since precompose_string_if_needed() needs to look at
|
|
|
|
* the core.precomposeunicode configuration, this
|
|
|
|
* has to happen after the above block that finds
|
|
|
|
* out where the repository is, i.e. a preparation
|
|
|
|
* for calling git_config_get_bool().
|
|
|
|
*/
|
|
|
|
if (prefix) {
|
|
|
|
prefix = precompose_string_if_needed(prefix);
|
|
|
|
startup_info->prefix = prefix;
|
|
|
|
setenv(GIT_PREFIX_ENVIRONMENT, prefix, 1);
|
|
|
|
} else {
|
|
|
|
startup_info->prefix = NULL;
|
|
|
|
setenv(GIT_PREFIX_ENVIRONMENT, "", 1);
|
|
|
|
}
|
|
|
|
|
2021-12-09 05:08:26 +00:00
|
|
|
setup_original_cwd();
|
2017-06-20 19:19:32 +00:00
|
|
|
|
setup_git_directory_1(): avoid changing global state
For historical reasons, Git searches for the .git/ directory (or the
.git file) by changing the working directory successively to the parent
directory of the current directory, until either anything was found or
until a ceiling or a mount point is hit.
Further global state may be changed in case a .git/ directory was found.
We do have a use case, though, where we would like to find the .git/
directory without having any global state touched, though: when we read
the early config e.g. for the pager or for alias expansion.
Let's just move all of code that changes any global state out of the
function `setup_git_directory_gently_1()` into
`setup_git_directory_gently()`.
In subsequent patches, we will use the _1() function in a new
`discover_git_directory()` function that we will then use for the early
config code.
Note: the new loop is a *little* tricky, as we have to handle the root
directory specially: we cannot simply strip away the last component
including the slash, as the root directory only has that slash. To remedy
that, we introduce the `min_offset` variable that holds the minimal length
of an absolute path, and using that to special-case the root directory,
including an early exit before trying to find the parent of the root
directory.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-03-13 20:10:42 +00:00
|
|
|
strbuf_release(&dir);
|
|
|
|
strbuf_release(&gitdir);
|
2022-08-08 13:27:47 +00:00
|
|
|
strbuf_release(&report);
|
setup: fix memory leaks with `struct repository_format`
After we set up a `struct repository_format`, it owns various pieces of
allocated memory. We then either use those members, because we decide we
want to use the "candidate" repository format, or we discard the
candidate / scratch space. In the first case, we transfer ownership of
the memory to a few global variables. In the latter case, we just
silently drop the struct and end up leaking memory.
Introduce an initialization macro `REPOSITORY_FORMAT_INIT` and a
function `clear_repository_format()`, to be used on each side of
`read_repository_format()`. To have a clear and simple memory ownership,
let all users of `struct repository_format` duplicate the strings that
they take from it, rather than stealing the pointers.
Call `clear_...()` at the start of `read_...()` instead of just zeroing
the struct, since we sometimes enter the function multiple times. Thus,
it is important to initialize the struct before calling `read_...()`, so
document that. It's also important because we might not even call
`read_...()` before we call `clear_...()`, see, e.g., builtin/init-db.c.
Teach `read_...()` to clear the struct on error, so that it is reset to
a safe state, and document this. (In `setup_git_directory_gently()`, we
look at `repo_fmt.hash_algo` even if `repo_fmt.version` is -1, which we
weren't actually supposed to do per the API. After this commit, that's
ok.)
We inherit the existing code's combining "error" and "no version found".
Both are signalled through `version == -1` and now both cause us to
clear any partial configuration we have picked up. For "extensions.*",
that's fine, since they require a positive version number. For
"core.bare" and "core.worktree", we're already verifying that we have a
non-negative version number before using them.
Signed-off-by: Martin Ågren <martin.agren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-02-28 20:36:28 +00:00
|
|
|
clear_repository_format(&repo_fmt);
|
setup_git_directory_1(): avoid changing global state
For historical reasons, Git searches for the .git/ directory (or the
.git file) by changing the working directory successively to the parent
directory of the current directory, until either anything was found or
until a ceiling or a mount point is hit.
Further global state may be changed in case a .git/ directory was found.
We do have a use case, though, where we would like to find the .git/
directory without having any global state touched, though: when we read
the early config e.g. for the pager or for alias expansion.
Let's just move all of code that changes any global state out of the
function `setup_git_directory_gently_1()` into
`setup_git_directory_gently()`.
In subsequent patches, we will use the _1() function in a new
`discover_git_directory()` function that we will then use for the early
config code.
Note: the new loop is a *little* tricky, as we have to handle the root
directory specially: we cannot simply strip away the last component
including the slash, as the root directory only has that slash. To remedy
that, we introduce the `min_offset` variable that holds the minimal length
of an absolute path, and using that to special-case the root directory,
including an early exit before trying to find the parent of the root
directory.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-03-13 20:10:42 +00:00
|
|
|
|
2010-08-06 02:46:33 +00:00
|
|
|
return prefix;
|
|
|
|
}
|
|
|
|
|
2006-06-10 06:09:49 +00:00
|
|
|
int git_config_perm(const char *var, const char *value)
|
|
|
|
{
|
2008-04-16 08:34:24 +00:00
|
|
|
int i;
|
|
|
|
char *endptr;
|
|
|
|
|
2022-05-02 16:50:37 +00:00
|
|
|
if (!value)
|
2008-04-16 08:34:24 +00:00
|
|
|
return PERM_GROUP;
|
|
|
|
|
|
|
|
if (!strcmp(value, "umask"))
|
|
|
|
return PERM_UMASK;
|
|
|
|
if (!strcmp(value, "group"))
|
|
|
|
return PERM_GROUP;
|
|
|
|
if (!strcmp(value, "all") ||
|
|
|
|
!strcmp(value, "world") ||
|
|
|
|
!strcmp(value, "everybody"))
|
|
|
|
return PERM_EVERYBODY;
|
|
|
|
|
|
|
|
/* Parse octal numbers */
|
|
|
|
i = strtol(value, &endptr, 8);
|
|
|
|
|
|
|
|
/* If not an octal number, maybe true/false? */
|
|
|
|
if (*endptr != 0)
|
|
|
|
return git_config_bool(var, value) ? PERM_GROUP : PERM_UMASK;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Treat values 0, 1 and 2 as compatibility cases, otherwise it is
|
2009-03-25 23:19:36 +00:00
|
|
|
* a chmod value to restrict to.
|
2008-04-16 08:34:24 +00:00
|
|
|
*/
|
|
|
|
switch (i) {
|
|
|
|
case PERM_UMASK: /* 0 */
|
|
|
|
return PERM_UMASK;
|
|
|
|
case OLD_PERM_GROUP: /* 1 */
|
|
|
|
return PERM_GROUP;
|
|
|
|
case OLD_PERM_EVERYBODY: /* 2 */
|
|
|
|
return PERM_EVERYBODY;
|
2006-06-10 06:09:49 +00:00
|
|
|
}
|
2008-04-16 08:34:24 +00:00
|
|
|
|
|
|
|
/* A filemode value was given: 0xxx */
|
|
|
|
|
|
|
|
if ((i & 0600) != 0600)
|
2018-02-13 13:19:15 +00:00
|
|
|
die(_("problem with core.sharedRepository filemode value "
|
2008-04-16 08:34:24 +00:00
|
|
|
"(0%.3o).\nThe owner of files must always have "
|
2016-08-08 11:15:59 +00:00
|
|
|
"read and write permissions."), i);
|
2008-04-16 08:34:24 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Mask filemode value. Others can not get write permission.
|
|
|
|
* x flags for directories are handled separately.
|
|
|
|
*/
|
2009-03-25 23:19:36 +00:00
|
|
|
return -(i & 0666);
|
2006-06-10 06:09:49 +00:00
|
|
|
}
|
|
|
|
|
2020-02-22 20:17:37 +00:00
|
|
|
void check_repository_format(struct repository_format *fmt)
|
2005-11-25 23:59:09 +00:00
|
|
|
{
|
setup: fix memory leaks with `struct repository_format`
After we set up a `struct repository_format`, it owns various pieces of
allocated memory. We then either use those members, because we decide we
want to use the "candidate" repository format, or we discard the
candidate / scratch space. In the first case, we transfer ownership of
the memory to a few global variables. In the latter case, we just
silently drop the struct and end up leaking memory.
Introduce an initialization macro `REPOSITORY_FORMAT_INIT` and a
function `clear_repository_format()`, to be used on each side of
`read_repository_format()`. To have a clear and simple memory ownership,
let all users of `struct repository_format` duplicate the strings that
they take from it, rather than stealing the pointers.
Call `clear_...()` at the start of `read_...()` instead of just zeroing
the struct, since we sometimes enter the function multiple times. Thus,
it is important to initialize the struct before calling `read_...()`, so
document that. It's also important because we might not even call
`read_...()` before we call `clear_...()`, see, e.g., builtin/init-db.c.
Teach `read_...()` to clear the struct on error, so that it is reset to
a safe state, and document this. (In `setup_git_directory_gently()`, we
look at `repo_fmt.hash_algo` even if `repo_fmt.version` is -1, which we
weren't actually supposed to do per the API. After this commit, that's
ok.)
We inherit the existing code's combining "error" and "no version found".
Both are signalled through `version == -1` and now both cause us to
clear any partial configuration we have picked up. For "extensions.*",
that's fine, since they require a positive version number. For
"core.bare" and "core.worktree", we're already verifying that we have a
non-negative version number before using them.
Signed-off-by: Martin Ågren <martin.agren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-02-28 20:36:28 +00:00
|
|
|
struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
|
2020-02-22 20:17:37 +00:00
|
|
|
if (!fmt)
|
|
|
|
fmt = &repo_fmt;
|
|
|
|
check_repository_format_gently(get_git_dir(), fmt, NULL);
|
2016-03-05 22:11:34 +00:00
|
|
|
startup_info->have_repository = 1;
|
2020-05-25 19:59:08 +00:00
|
|
|
repo_set_hash_algo(the_repository, fmt->hash_algo);
|
2021-06-17 17:13:22 +00:00
|
|
|
the_repository->repository_format_partial_clone =
|
|
|
|
xstrdup_or_null(fmt->partial_clone);
|
setup: fix memory leaks with `struct repository_format`
After we set up a `struct repository_format`, it owns various pieces of
allocated memory. We then either use those members, because we decide we
want to use the "candidate" repository format, or we discard the
candidate / scratch space. In the first case, we transfer ownership of
the memory to a few global variables. In the latter case, we just
silently drop the struct and end up leaking memory.
Introduce an initialization macro `REPOSITORY_FORMAT_INIT` and a
function `clear_repository_format()`, to be used on each side of
`read_repository_format()`. To have a clear and simple memory ownership,
let all users of `struct repository_format` duplicate the strings that
they take from it, rather than stealing the pointers.
Call `clear_...()` at the start of `read_...()` instead of just zeroing
the struct, since we sometimes enter the function multiple times. Thus,
it is important to initialize the struct before calling `read_...()`, so
document that. It's also important because we might not even call
`read_...()` before we call `clear_...()`, see, e.g., builtin/init-db.c.
Teach `read_...()` to clear the struct on error, so that it is reset to
a safe state, and document this. (In `setup_git_directory_gently()`, we
look at `repo_fmt.hash_algo` even if `repo_fmt.version` is -1, which we
weren't actually supposed to do per the API. After this commit, that's
ok.)
We inherit the existing code's combining "error" and "no version found".
Both are signalled through `version == -1` and now both cause us to
clear any partial configuration we have picked up. For "extensions.*",
that's fine, since they require a positive version number. For
"core.bare" and "core.worktree", we're already verifying that we have a
non-negative version number before using them.
Signed-off-by: Martin Ågren <martin.agren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-02-28 20:36:28 +00:00
|
|
|
clear_repository_format(&repo_fmt);
|
2005-11-25 23:59:09 +00:00
|
|
|
}
|
|
|
|
|
2010-06-05 08:04:20 +00:00
|
|
|
/*
|
|
|
|
* Returns the "prefix", a path to the current working directory
|
|
|
|
* relative to the work tree root, or NULL, if the current working
|
|
|
|
* directory is not a strict subdirectory of the work tree root. The
|
|
|
|
* prefix always ends with a '/' character.
|
|
|
|
*/
|
2005-11-25 23:43:41 +00:00
|
|
|
const char *setup_git_directory(void)
|
|
|
|
{
|
2010-11-26 15:32:39 +00:00
|
|
|
return setup_git_directory_gently(NULL);
|
2005-11-25 23:43:41 +00:00
|
|
|
}
|
2011-08-15 21:17:46 +00:00
|
|
|
|
2017-01-24 23:56:49 +00:00
|
|
|
const char *resolve_gitdir_gently(const char *suspect, int *return_error_code)
|
2011-08-15 21:17:46 +00:00
|
|
|
{
|
|
|
|
if (is_git_directory(suspect))
|
|
|
|
return suspect;
|
2017-01-24 23:56:49 +00:00
|
|
|
return read_gitfile_gently(suspect, return_error_code);
|
2011-08-15 21:17:46 +00:00
|
|
|
}
|
2013-07-16 09:27:36 +00:00
|
|
|
|
|
|
|
/* if any standard file descriptor is missing open it to /dev/null */
|
|
|
|
void sanitize_stdfds(void)
|
|
|
|
{
|
2021-09-09 21:45:29 +00:00
|
|
|
int fd = xopen("/dev/null", O_RDWR);
|
|
|
|
while (fd < 2)
|
|
|
|
fd = xdup(fd);
|
2013-07-16 09:27:36 +00:00
|
|
|
if (fd > 2)
|
|
|
|
close(fd);
|
|
|
|
}
|
2014-02-08 07:08:51 +00:00
|
|
|
|
|
|
|
int daemonize(void)
|
|
|
|
{
|
|
|
|
#ifdef NO_POSIX_GOODIES
|
|
|
|
errno = ENOSYS;
|
|
|
|
return -1;
|
|
|
|
#else
|
|
|
|
switch (fork()) {
|
|
|
|
case 0:
|
|
|
|
break;
|
|
|
|
case -1:
|
2018-02-13 13:19:15 +00:00
|
|
|
die_errno(_("fork failed"));
|
2014-02-08 07:08:51 +00:00
|
|
|
default:
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
if (setsid() == -1)
|
2018-02-13 13:19:15 +00:00
|
|
|
die_errno(_("setsid failed"));
|
2014-02-08 07:08:51 +00:00
|
|
|
close(0);
|
|
|
|
close(1);
|
|
|
|
close(2);
|
|
|
|
sanitize_stdfds();
|
|
|
|
return 0;
|
|
|
|
#endif
|
|
|
|
}
|