global: introduce `USE_THE_REPOSITORY_VARIABLE` macro
Use of the `the_repository` variable is deprecated nowadays, and we
slowly but steadily convert the codebase to not use it anymore. Instead,
callers should be passing down the repository to work on via parameters.
It is hard though to prove that a given code unit does not use this
variable anymore. The most trivial case, merely demonstrating that there
is no direct use of `the_repository`, is already a bit of a pain during
code reviews as the reviewer needs to manually verify claims made by the
patch author. The bigger problem though is that we have many interfaces
that implicitly rely on `the_repository`.
Introduce a new `USE_THE_REPOSITORY_VARIABLE` macro that allows code
units to opt into usage of `the_repository`. The intent of this macro is
to demonstrate that a certain code unit does not use this variable
anymore, and to keep it from new dependencies on it in future changes,
be it explicit or implicit
For now, the macro only guards `the_repository` itself as well as
`the_hash_algo`. There are many more known interfaces where we have an
implicit dependency on `the_repository`, but those are not guarded at
the current point in time. Over time though, we should start to add
guards as required (or even better, just remove them).
Define the macro as required in our code units. As expected, most of our
code still relies on the global variable. Nearly all of our builtins
rely on the variable as there is no way yet to pass `the_repository` to
their entry point. For now, declare the macro in "biultin.h" to keep the
required changes at least a little bit more contained.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-06-14 14:50:23 +08:00
|
|
|
#define USE_THE_REPOSITORY_VARIABLE
|
|
|
|
|
2023-04-11 15:41:50 +08:00
|
|
|
#include "git-compat-util.h"
|
2023-03-21 14:25:58 +08:00
|
|
|
#include "abspath.h"
|
2023-05-16 14:33:44 +08:00
|
|
|
#include "copy.h"
|
2023-03-21 14:26:03 +08:00
|
|
|
#include "environment.h"
|
2023-05-16 14:33:44 +08:00
|
|
|
#include "exec-cmd.h"
|
2023-03-21 14:25:54 +08:00
|
|
|
#include "gettext.h"
|
2024-05-07 12:52:51 +08:00
|
|
|
#include "hex.h"
|
2023-04-11 15:41:49 +08:00
|
|
|
#include "object-name.h"
|
2023-05-16 14:33:44 +08:00
|
|
|
#include "refs.h"
|
2017-06-23 02:43:33 +08:00
|
|
|
#include "repository.h"
|
2017-06-15 02:07:36 +08: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 08:30:14 +08:00
|
|
|
#include "dir.h"
|
2023-03-21 14:26:05 +08:00
|
|
|
#include "setup.h"
|
2012-10-29 00:16:24 +08: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-31 02:35:08 +08:00
|
|
|
#include "chdir-notify.h"
|
2023-05-16 14:33:59 +08:00
|
|
|
#include "path.h"
|
2022-03-02 19:23:04 +08:00
|
|
|
#include "quote.h"
|
2023-04-11 11:00:38 +08:00
|
|
|
#include "trace2.h"
|
2023-05-16 14:33:44 +08:00
|
|
|
#include "worktree.h"
|
2024-03-29 18:45:01 +08:00
|
|
|
#include "exec-cmd.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 08:30:14 +08: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 14:49:10 +08: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-15 05:28:01 +08:00
|
|
|
enum allowed_bare_repo {
|
|
|
|
ALLOWED_BARE_REPO_EXPLICIT = 0,
|
|
|
|
ALLOWED_BARE_REPO_ALL,
|
|
|
|
};
|
2005-08-17 09:06:34 +08: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-06 06:10:27 +08:00
|
|
|
static struct startup_info the_startup_info;
|
|
|
|
struct startup_info *startup_info = &the_startup_info;
|
2021-12-09 13:08:26 +08: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-06 06:10:27 +08:00
|
|
|
|
2014-02-04 22:25:19 +08: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;
|
macOS: ls-files path fails if path of workdir is NFD
Under macOS, `git ls-files path` does not work (gives an error)
if the absolute 'path' contains characters in NFD (decomposed).
This happens when core.precomposeunicode is true, which is the
most common case. The bug report says:
$ cd somewhere # some safe place, /tmp or ~/tmp etc.
$ mkdir $'u\xcc\x88' # ü in NFD
$ cd ü # or cd $'u\xcc\x88' or cd $'\xc3\xbc'
$ git init
$ git ls-files $'/somewhere/u\xcc\x88' # NFD
fatal: /somewhere/ü: '/somewhere/ü' is outside repository at '/somewhere/ü'
$ git ls-files $'/somewhere/\xc3\xbc' # NFC
(the same error as above)
In the 'fatal:' error message, there are three ü;
the 1st and 2nd are in NFC, the 3rd is in NFD.
Add test cases that follows the bug report, with the simplification
that the 'ü' is replaced by an 'ä', which is already used as NFD and
NFC in t3910.
The solution is to add a call to precompose_string_if_needed()
to this code in setup.c :
`work_tree = precompose_string_if_needed(get_git_work_tree());`
There is, however, a limitation with this very usage of Git:
The (repo) local .gitconfig file is not used, only the global
"core.precomposeunicode" is taken into account, if it is set (or not).
To set it to true is a good recommendation anyway, and here is the
analyzes from Jun T :
The problem is the_repository->config->hash_initialized
is set to 1 before the_repository->commondir is set to ".git".
Due to this, .git/config is never read, and precomposed_unicode
is never set to 1 (remains -1).
run_builtin() {
setup_git_directory() {
strbuf_getcwd() { # setup.c:1542
precompose_{strbuf,string}_if_needed() {
# precomposed_unicode is still -1
git_congig_get_bool("core.precomposeunicode") {
git_config_check_init() {
repo_read_config() {
git_config_init() {
# !!!
the_repository->config->hash_initialized=1
# !!!
}
# does not read .git/config since
# the_repository->commondir is still NULL
}
}
}
returns without converting to NFC
}
returns cwd in NFD
}
setup_discovered_git_dir() {
set_git_work_tree(".") {
repo_set_worktree() {
# this function indirectly calls strbuf_getcwd()
# --> precompose_{strbuf,string}_if_needed() -->
# {git,repo}_config_get_bool("core.precomposeunicode"),
# but does not try to read .git/config since
# the_repository->config->hash_initialized
# is already set to 1 above. And it will not read
# .git/config even if hash_initialized is 0
# since the_repository->commondir is still NULL.
the_repository->worktree = NFD
}
}
}
setup_git_env() {
repo_setup_gitdir() {
repo_set_commondir() {
# finally commondir is set here
the_repository->commondir = ".git"
}
}
}
} // END setup_git_directory
Reported-by: Jun T <takimoto-j@kba.biglobe.ne.jp>
Signed-off-by: Torsten Bögershausen <tboegi@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-06-01 03:31:56 +08:00
|
|
|
const char *work_tree = precompose_string_if_needed(get_git_work_tree());
|
2020-03-10 21:11:22 +08:00
|
|
|
struct strbuf realpath = STRBUF_INIT;
|
2014-02-04 22:25:19 +08:00
|
|
|
|
|
|
|
if (!work_tree)
|
|
|
|
return -1;
|
|
|
|
wtlen = strlen(work_tree);
|
|
|
|
len = strlen(path);
|
2014-04-24 21:06:09 +08:00
|
|
|
off = offset_1st_component(path);
|
2014-02-04 22:25:19 +08:00
|
|
|
|
|
|
|
/* check if work tree is already the prefix */
|
2019-01-18 21:24:54 +08:00
|
|
|
if (wtlen <= len && !fspathncmp(path, work_tree, wtlen)) {
|
2014-02-04 22:25:19 +08: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 21:06:09 +08:00
|
|
|
path += off;
|
2014-02-04 22:25:19 +08:00
|
|
|
|
|
|
|
/* check each '/'-terminated level */
|
|
|
|
while (*path) {
|
|
|
|
path++;
|
|
|
|
if (*path == '/') {
|
|
|
|
*path = '\0';
|
2020-03-10 21:11:22 +08:00
|
|
|
strbuf_realpath(&realpath, path0, 1);
|
|
|
|
if (fspathcmp(realpath.buf, work_tree) == 0) {
|
2014-02-04 22:25:19 +08:00
|
|
|
memmove(path0, path + 1, len - (path - path0));
|
2020-03-10 21:11:22 +08:00
|
|
|
strbuf_release(&realpath);
|
2014-02-04 22:25:19 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
*path = '/';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check whole path */
|
2020-03-10 21:11:22 +08:00
|
|
|
strbuf_realpath(&realpath, path0, 1);
|
|
|
|
if (fspathcmp(realpath.buf, work_tree) == 0) {
|
2014-02-04 22:25:19 +08:00
|
|
|
*path0 = '\0';
|
2020-03-10 21:11:22 +08:00
|
|
|
strbuf_release(&realpath);
|
2014-02-04 22:25:19 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-03-10 21:11:22 +08:00
|
|
|
strbuf_release(&realpath);
|
2014-02-04 22:25:19 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-07-14 16:36:03 +08: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 14:44:27 +08:00
|
|
|
{
|
|
|
|
const char *orig = path;
|
2010-12-27 18:54:37 +08:00
|
|
|
char *sanitized;
|
|
|
|
if (is_absolute_path(orig)) {
|
2016-02-23 06:44:28 +08:00
|
|
|
sanitized = xmallocz(strlen(path));
|
2013-07-14 16:36:03 +08:00
|
|
|
if (remaining_prefix)
|
|
|
|
*remaining_prefix = 0;
|
2014-02-04 22:25:20 +08: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 18:54:37 +08: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-08 03:38:18 +08:00
|
|
|
sanitized = xstrfmt("%.*s%s", len, len ? prefix : "", path);
|
2013-07-14 16:36:03 +08:00
|
|
|
if (remaining_prefix)
|
|
|
|
*remaining_prefix = len;
|
2014-02-04 22:25:20 +08:00
|
|
|
if (normalize_path_copy_len(sanitized, sanitized, remaining_prefix)) {
|
2012-06-22 02:09:50 +08: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 14:44:27 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return sanitized;
|
2005-08-17 11:44:32 +08:00
|
|
|
}
|
|
|
|
|
2012-06-22 02:09:50 +08:00
|
|
|
char *prefix_path(const char *prefix, int len, const char *path)
|
|
|
|
{
|
2013-07-14 16:36:03 +08:00
|
|
|
char *r = prefix_path_gently(prefix, len, NULL, path);
|
2020-03-03 12:05:06 +08:00
|
|
|
if (!r) {
|
|
|
|
const char *hint_path = get_git_work_tree();
|
|
|
|
if (!hint_path)
|
2024-09-12 19:29:24 +08:00
|
|
|
hint_path = repo_get_git_dir(the_repository);
|
2020-02-15 09:00:13 +08:00
|
|
|
die(_("'%s' is outside repository at '%s'"), path,
|
2020-03-03 12:05:06 +08:00
|
|
|
absolute_path(hint_path));
|
|
|
|
}
|
2012-06-22 02:09:50 +08:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
int path_inside_repo(const char *prefix, const char *path)
|
|
|
|
{
|
|
|
|
int len = prefix ? strlen(prefix) : 0;
|
2013-07-14 16:36:03 +08:00
|
|
|
char *r = prefix_path_gently(prefix, len, NULL, path);
|
2012-06-22 02:09:50 +08:00
|
|
|
if (r) {
|
|
|
|
free(r);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-10-18 15:27:24 +08:00
|
|
|
int check_filename(const char *prefix, const char *arg)
|
|
|
|
{
|
2017-03-21 09:28:49 +08:00
|
|
|
char *to_free = NULL;
|
2009-10-18 15:27:24 +08:00
|
|
|
struct stat st;
|
|
|
|
|
2017-05-27 03:07:42 +08:00
|
|
|
if (skip_prefix(arg, ":/", &arg)) {
|
|
|
|
if (!*arg) /* ":/" is root dir, always exists */
|
2013-01-21 21:00:48 +08:00
|
|
|
return 1;
|
2017-05-27 03:07:31 +08:00
|
|
|
prefix = NULL;
|
2017-05-27 03:08:39 +08:00
|
|
|
} else if (skip_prefix(arg, ":!", &arg) ||
|
|
|
|
skip_prefix(arg, ":^", &arg)) {
|
|
|
|
if (!*arg) /* excluding everything is silly, but allowed */
|
|
|
|
return 1;
|
2017-05-27 03:07:31 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (prefix)
|
|
|
|
arg = to_free = prefix_filename(prefix, arg);
|
|
|
|
|
|
|
|
if (!lstat(arg, &st)) {
|
2017-03-21 09:28:49 +08:00
|
|
|
free(to_free);
|
2009-10-18 15:27:24 +08:00
|
|
|
return 1; /* file exists */
|
2017-03-21 09:28:49 +08:00
|
|
|
}
|
2017-06-14 04:47:06 +08:00
|
|
|
if (is_missing_file_error(errno)) {
|
2017-03-21 09:28:49 +08:00
|
|
|
free(to_free);
|
2009-10-18 15:27:24 +08:00
|
|
|
return 0; /* file does not exist */
|
2017-03-21 09:28:49 +08:00
|
|
|
}
|
2018-02-13 21:19:15 +08:00
|
|
|
die_errno(_("failed to stat '%s'"), arg);
|
2009-10-18 15:27:24 +08:00
|
|
|
}
|
|
|
|
|
2019-04-16 17:33:39 +08:00
|
|
|
static void NORETURN die_verify_filename(struct repository *r,
|
|
|
|
const char *prefix,
|
2012-06-19 02:18:21 +08:00
|
|
|
const char *arg,
|
|
|
|
int diagnose_misspelt_rev)
|
2009-12-07 18:10:50 +08:00
|
|
|
{
|
2012-06-19 02:18:21 +08:00
|
|
|
if (!diagnose_misspelt_rev)
|
2016-06-18 04:21:06 +08: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-19 02:18:21 +08:00
|
|
|
arg);
|
2011-05-11 03:05:01 +08: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-03 02:01:25 +08:00
|
|
|
* let maybe_die_on_misspelt_object_name() even trigger.
|
2011-05-11 03:05:01 +08:00
|
|
|
*/
|
|
|
|
if (!(arg[0] == ':' && !isalnum(arg[1])))
|
2019-04-16 17:33:39 +08:00
|
|
|
maybe_die_on_misspelt_object_name(r, arg, prefix);
|
2011-05-11 03:05:01 +08:00
|
|
|
|
2009-12-07 18:10:50 +08:00
|
|
|
/* ... or fall back the most general message. */
|
2016-06-18 04:21:06 +08: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 18:10:50 +08: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-27 03:10:31 +08: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 08:00:51 +08: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-27 03:10:31 +08:00
|
|
|
|
|
|
|
/* long-form pathspec magic */
|
|
|
|
if (starts_with(arg, ":("))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-04-27 01:15:54 +08: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-19 02:18:21 +08: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-27 01:15:54 +08:00
|
|
|
*/
|
2012-06-19 02:18:21 +08:00
|
|
|
void verify_filename(const char *prefix,
|
|
|
|
const char *arg,
|
|
|
|
int diagnose_misspelt_rev)
|
2006-04-27 01:15:54 +08:00
|
|
|
{
|
|
|
|
if (*arg == '-')
|
2018-02-13 21:19:15 +08:00
|
|
|
die(_("option '%s' must come before non-option arguments"), arg);
|
2017-05-27 03:10:53 +08:00
|
|
|
if (looks_like_pathspec(arg) || check_filename(prefix, arg))
|
2006-04-27 01:15:54 +08:00
|
|
|
return;
|
2019-04-16 17:33:39 +08:00
|
|
|
die_verify_filename(the_repository, prefix, arg, diagnose_misspelt_rev);
|
2006-04-27 01:15:54 +08:00
|
|
|
}
|
|
|
|
|
2006-04-27 06:09:27 +08: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 22:48:16 +08:00
|
|
|
if (!is_inside_work_tree() || is_inside_git_dir())
|
2007-01-20 10:09:34 +08:00
|
|
|
return;
|
2006-04-27 06:09:27 +08:00
|
|
|
if (*arg == '-')
|
|
|
|
return; /* flag */
|
2009-10-18 15:27:24 +08:00
|
|
|
if (!check_filename(prefix, arg))
|
|
|
|
return;
|
2016-06-18 04:21:06 +08: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-27 06:09:27 +08:00
|
|
|
}
|
|
|
|
|
2014-11-30 16:24:44 +08:00
|
|
|
int get_common_dir(struct strbuf *sb, const char *gitdir)
|
2015-09-14 06:17:42 +08: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 16:24:41 +08:00
|
|
|
{
|
|
|
|
struct strbuf data = STRBUF_INIT;
|
|
|
|
struct strbuf path = STRBUF_INIT;
|
2014-11-30 16:24:44 +08:00
|
|
|
int ret = 0;
|
2015-09-14 06:17:42 +08:00
|
|
|
|
2014-11-30 16:24:41 +08: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-26 00:00:33 +08:00
|
|
|
strbuf_add_real_path(sb, path.buf);
|
2014-11-30 16:24:44 +08:00
|
|
|
ret = 1;
|
2016-12-13 02:16:55 +08:00
|
|
|
} else {
|
2014-11-30 16:24:41 +08:00
|
|
|
strbuf_addstr(sb, gitdir);
|
2016-12-13 02:16:55 +08:00
|
|
|
}
|
|
|
|
|
2014-11-30 16:24:41 +08:00
|
|
|
strbuf_release(&data);
|
|
|
|
strbuf_release(&path);
|
2014-11-30 16:24:44 +08:00
|
|
|
return ret;
|
2014-11-30 16:24:41 +08:00
|
|
|
}
|
2005-08-17 09:06:34 +08:00
|
|
|
|
2024-05-07 12:52:51 +08:00
|
|
|
static int validate_headref(const char *path)
|
|
|
|
{
|
|
|
|
struct stat st;
|
|
|
|
char buffer[256];
|
|
|
|
const char *refname;
|
|
|
|
struct object_id oid;
|
|
|
|
int fd;
|
|
|
|
ssize_t len;
|
|
|
|
|
|
|
|
if (lstat(path, &st) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* Make sure it is a "refs/.." symlink */
|
|
|
|
if (S_ISLNK(st.st_mode)) {
|
|
|
|
len = readlink(path, buffer, sizeof(buffer)-1);
|
|
|
|
if (len >= 5 && !memcmp("refs/", buffer, 5))
|
|
|
|
return 0;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Anything else, just open it and try to see if it is a symbolic ref.
|
|
|
|
*/
|
|
|
|
fd = open(path, O_RDONLY);
|
|
|
|
if (fd < 0)
|
|
|
|
return -1;
|
|
|
|
len = read_in_full(fd, buffer, sizeof(buffer)-1);
|
|
|
|
close(fd);
|
|
|
|
|
|
|
|
if (len < 0)
|
|
|
|
return -1;
|
|
|
|
buffer[len] = '\0';
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Is it a symbolic ref?
|
|
|
|
*/
|
|
|
|
if (skip_prefix(buffer, "ref:", &refname)) {
|
|
|
|
while (isspace(*refname))
|
|
|
|
refname++;
|
|
|
|
if (starts_with(refname, "refs/"))
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Is this a detached HEAD?
|
|
|
|
*/
|
|
|
|
if (get_oid_hex_any(buffer, &oid) != GIT_HASH_UNKNOWN)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
[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-28 04:54:42 +08:00
|
|
|
/*
|
2006-12-31 12:30:19 +08:00
|
|
|
* Test if it looks like we're at a git directory.
|
2005-11-26 07:43:41 +08: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-28 04:54:42 +08:00
|
|
|
*
|
2008-01-03 22:18:07 +08: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-28 04:54:42 +08:00
|
|
|
* GIT_OBJECT_DIRECTORY environment variable
|
2006-12-31 12:30:19 +08:00
|
|
|
* - a refs/ directory
|
2005-10-01 05:26:57 +08:00
|
|
|
* - either a HEAD symlink or a HEAD file that is formatted as
|
2007-01-02 15:31:08 +08: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-28 04:54:42 +08: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-03 05:59:13 +08: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-28 04:54:42 +08:00
|
|
|
{
|
2014-11-30 16:24:40 +08:00
|
|
|
struct strbuf path = STRBUF_INIT;
|
|
|
|
int ret = 0;
|
|
|
|
size_t len;
|
2006-12-31 12:30:19 +08:00
|
|
|
|
2014-11-30 16:24:41 +08:00
|
|
|
/* Check worktree-related signatures */
|
2017-11-03 20:58:02 +08:00
|
|
|
strbuf_addstr(&path, suspect);
|
|
|
|
strbuf_complete(&path, '/');
|
|
|
|
strbuf_addstr(&path, "HEAD");
|
2014-11-30 16:24:41 +08:00
|
|
|
if (validate_headref(path.buf))
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
strbuf_reset(&path);
|
|
|
|
get_common_dir(&path, suspect);
|
2014-11-30 16:24:40 +08:00
|
|
|
len = path.len;
|
2014-11-30 16:24:41 +08:00
|
|
|
|
|
|
|
/* Check non-worktree-related signatures */
|
2006-12-31 12:30:19 +08:00
|
|
|
if (getenv(DB_ENVIRONMENT)) {
|
|
|
|
if (access(getenv(DB_ENVIRONMENT), X_OK))
|
2014-11-30 16:24:40 +08:00
|
|
|
goto done;
|
2006-12-31 12:30:19 +08:00
|
|
|
}
|
|
|
|
else {
|
2014-11-30 16:24:41 +08:00
|
|
|
strbuf_setlen(&path, len);
|
2014-11-30 16:24:40 +08:00
|
|
|
strbuf_addstr(&path, "/objects");
|
|
|
|
if (access(path.buf, X_OK))
|
|
|
|
goto done;
|
2006-12-31 12:30:19 +08:00
|
|
|
}
|
|
|
|
|
2014-11-30 16:24:40 +08:00
|
|
|
strbuf_setlen(&path, len);
|
|
|
|
strbuf_addstr(&path, "/refs");
|
|
|
|
if (access(path.buf, X_OK))
|
|
|
|
goto done;
|
2006-12-31 12:30:19 +08:00
|
|
|
|
2014-11-30 16:24:40 +08: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-28 04:54:42 +08:00
|
|
|
}
|
|
|
|
|
2016-01-23 06:27:33 +08: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 10:09:34 +08: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 08:30:14 +08:00
|
|
|
if (inside_git_dir < 0)
|
2024-09-12 19:29:24 +08:00
|
|
|
inside_git_dir = is_inside_dir(repo_get_git_dir(the_repository));
|
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 08:30:14 +08:00
|
|
|
return inside_git_dir;
|
2007-06-06 15:10:42 +08: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 08:30:14 +08:00
|
|
|
if (inside_work_tree < 0)
|
|
|
|
inside_work_tree = is_inside_dir(get_git_work_tree());
|
|
|
|
return inside_work_tree;
|
2007-06-06 15:10:42 +08:00
|
|
|
}
|
|
|
|
|
2007-11-09 07:35:32 +08: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-31 02:35:08 +08:00
|
|
|
const char *work_tree;
|
2007-11-09 19:34:07 +08: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 14:49:10 +08:00
|
|
|
|
|
|
|
if (work_tree_config_is_bogus)
|
2018-02-13 21:19:15 +08: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 14:49:10 +08:00
|
|
|
|
2007-11-09 19:34:07 +08: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-31 02:35:08 +08:00
|
|
|
if (!work_tree || chdir_notify(work_tree))
|
2018-02-13 21:19:15 +08:00
|
|
|
die(_("this operation must be run in a work tree"));
|
2010-12-27 09:26:04 +08: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 19:34:07 +08:00
|
|
|
initialized = 1;
|
2007-11-03 19:23:11 +08:00
|
|
|
}
|
|
|
|
|
2021-12-09 13:08:26 +08: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-25 03:20:12 +08: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 13:08:26 +08: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;
|
|
|
|
}
|
|
|
|
|
config: add ctx arg to config_fn_t
Add a new "const struct config_context *ctx" arg to config_fn_t to hold
additional information about the config iteration operation.
config_context has a "struct key_value_info kvi" member that holds
metadata about the config source being read (e.g. what kind of config
source it is, the filename, etc). In this series, we're only interested
in .kvi, so we could have just used "struct key_value_info" as an arg,
but config_context makes it possible to add/adjust members in the future
without changing the config_fn_t signature. We could also consider other
ways of organizing the args (e.g. moving the config name and value into
config_context or key_value_info), but in my experiments, the
incremental benefit doesn't justify the added complexity (e.g. a
config_fn_t will sometimes invoke another config_fn_t but with a
different config value).
In subsequent commits, the .kvi member will replace the global "struct
config_reader" in config.c, making config iteration a global-free
operation. It requires much more work for the machinery to provide
meaningful values of .kvi, so for now, merely change the signature and
call sites, pass NULL as a placeholder value, and don't rely on the arg
in any meaningful way.
Most of the changes are performed by
contrib/coccinelle/config_fn_ctx.pending.cocci, which, for every
config_fn_t:
- Modifies the signature to accept "const struct config_context *ctx"
- Passes "ctx" to any inner config_fn_t, if needed
- Adds UNUSED attributes to "ctx", if needed
Most config_fn_t instances are easily identified by seeing if they are
called by the various config functions. Most of the remaining ones are
manually named in the .cocci patch. Manual cleanups are still needed,
but the majority of it is trivial; it's either adjusting config_fn_t
that the .cocci patch didn't catch, or adding forward declarations of
"struct config_context ctx" to make the signatures make sense.
The non-trivial changes are in cases where we are invoking a config_fn_t
outside of config machinery, and we now need to decide what value of
"ctx" to pass. These cases are:
- trace2/tr2_cfg.c:tr2_cfg_set_fl()
This is indirectly called by git_config_set() so that the trace2
machinery can notice the new config values and update its settings
using the tr2 config parsing function, i.e. tr2_cfg_cb().
- builtin/checkout.c:checkout_main()
This calls git_xmerge_config() as a shorthand for parsing a CLI arg.
This might be worth refactoring away in the future, since
git_xmerge_config() can call git_default_config(), which can do much
more than just parsing.
Handle them by creating a KVI_INIT macro that initializes "struct
key_value_info" to a reasonable default, and use that to construct the
"ctx" arg.
Signed-off-by: Glen Choo <chooglen@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-06-29 03:26:22 +08:00
|
|
|
static int read_worktree_config(const char *var, const char *value,
|
|
|
|
const struct config_context *ctx UNUSED,
|
|
|
|
void *vdata)
|
2018-10-21 22:02:28 +08:00
|
|
|
{
|
|
|
|
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-23 05:45:47 +08:00
|
|
|
free(data->work_tree);
|
2018-10-21 22:02:28 +08:00
|
|
|
data->work_tree = xstrdup(value);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-07-16 20:25:13 +08: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")) {
|
2023-12-07 15:11:21 +08:00
|
|
|
if (!value)
|
|
|
|
return config_error_nonbool(var);
|
2020-07-16 20:25:13 +08:00
|
|
|
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 09:04:11 +08:00
|
|
|
} else if (!strcmp(ext, "objectformat")) {
|
|
|
|
int format;
|
2020-07-16 20:25:13 +08:00
|
|
|
|
2020-08-12 09:04:11 +08:00
|
|
|
if (!value)
|
|
|
|
return config_error_nonbool(var);
|
|
|
|
format = hash_algo_by_name(value);
|
|
|
|
if (format == GIT_HASH_UNKNOWN)
|
2022-02-01 06:07:47 +08:00
|
|
|
return error(_("invalid value for '%s': '%s'"),
|
|
|
|
"extensions.objectformat", value);
|
2020-08-12 09:04:11 +08:00
|
|
|
data->hash_algo = format;
|
|
|
|
return EXTENSION_OK;
|
2023-10-02 10:40:25 +08:00
|
|
|
} else if (!strcmp(ext, "compatobjectformat")) {
|
|
|
|
struct string_list_item *item;
|
|
|
|
int format;
|
|
|
|
|
|
|
|
if (!value)
|
|
|
|
return config_error_nonbool(var);
|
|
|
|
format = hash_algo_by_name(value);
|
|
|
|
if (format == GIT_HASH_UNKNOWN)
|
|
|
|
return error(_("invalid value for '%s': '%s'"),
|
|
|
|
"extensions.compatobjectformat", value);
|
|
|
|
/* For now only support compatObjectFormat being specified once. */
|
|
|
|
for_each_string_list_item(item, &data->v1_only_extensions) {
|
|
|
|
if (!strcmp(item->string, "compatobjectformat"))
|
|
|
|
return error(_("'%s' already specified as '%s'"),
|
|
|
|
"extensions.compatobjectformat",
|
|
|
|
hash_algos[data->compat_hash_algo].name);
|
|
|
|
}
|
|
|
|
data->compat_hash_algo = format;
|
|
|
|
return EXTENSION_OK;
|
setup: introduce "extensions.refStorage" extension
Introduce a new "extensions.refStorage" extension that allows us to
specify the ref storage format used by a repository. For now, the only
supported format is the "files" format, but this list will likely soon
be extended to also support the upcoming "reftable" format.
There have been discussions on the Git mailing list in the past around
how exactly this extension should look like. One alternative [1] that
was discussed was whether it would make sense to model the extension in
such a way that backends are arbitrarily stackable. This would allow for
a combined value of e.g. "loose,packed-refs" or "loose,reftable", which
indicates that new refs would be written via "loose" files backend and
compressed into "packed-refs" or "reftable" backends, respectively.
It is arguable though whether this flexibility and the complexity that
it brings with it is really required for now. It is not foreseeable that
there will be a proliferation of backends in the near-term future, and
the current set of existing formats and formats which are on the horizon
can easily be configured with the much simpler proposal where we have a
single value, only.
Furthermore, if we ever see that we indeed want to gain the ability to
arbitrarily stack the ref formats, then we can adapt the current
extension rather easily. Given that Git clients will refuse any unknown
value for the "extensions.refStorage" extension they would also know to
ignore a stacked "loose,packed-refs" in the future.
So let's stick with the easy proposal for the time being and wire up the
extension.
[1]: <pull.1408.git.1667846164.gitgitgadget@gmail.com>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-12-29 15:26:47 +08:00
|
|
|
} else if (!strcmp(ext, "refstorage")) {
|
|
|
|
unsigned int format;
|
|
|
|
|
|
|
|
if (!value)
|
|
|
|
return config_error_nonbool(var);
|
|
|
|
format = ref_storage_format_by_name(value);
|
|
|
|
if (format == REF_STORAGE_FORMAT_UNKNOWN)
|
|
|
|
return error(_("invalid value for '%s': '%s'"),
|
|
|
|
"extensions.refstorage", value);
|
|
|
|
data->ref_storage_format = format;
|
|
|
|
return EXTENSION_OK;
|
2020-08-12 09:04:11 +08:00
|
|
|
}
|
2020-07-16 20:25:13 +08:00
|
|
|
return EXTENSION_UNKNOWN;
|
|
|
|
}
|
|
|
|
|
config: add ctx arg to config_fn_t
Add a new "const struct config_context *ctx" arg to config_fn_t to hold
additional information about the config iteration operation.
config_context has a "struct key_value_info kvi" member that holds
metadata about the config source being read (e.g. what kind of config
source it is, the filename, etc). In this series, we're only interested
in .kvi, so we could have just used "struct key_value_info" as an arg,
but config_context makes it possible to add/adjust members in the future
without changing the config_fn_t signature. We could also consider other
ways of organizing the args (e.g. moving the config name and value into
config_context or key_value_info), but in my experiments, the
incremental benefit doesn't justify the added complexity (e.g. a
config_fn_t will sometimes invoke another config_fn_t but with a
different config value).
In subsequent commits, the .kvi member will replace the global "struct
config_reader" in config.c, making config iteration a global-free
operation. It requires much more work for the machinery to provide
meaningful values of .kvi, so for now, merely change the signature and
call sites, pass NULL as a placeholder value, and don't rely on the arg
in any meaningful way.
Most of the changes are performed by
contrib/coccinelle/config_fn_ctx.pending.cocci, which, for every
config_fn_t:
- Modifies the signature to accept "const struct config_context *ctx"
- Passes "ctx" to any inner config_fn_t, if needed
- Adds UNUSED attributes to "ctx", if needed
Most config_fn_t instances are easily identified by seeing if they are
called by the various config functions. Most of the remaining ones are
manually named in the .cocci patch. Manual cleanups are still needed,
but the majority of it is trivial; it's either adjusting config_fn_t
that the .cocci patch didn't catch, or adding forward declarations of
"struct config_context ctx" to make the signatures make sense.
The non-trivial changes are in cases where we are invoking a config_fn_t
outside of config machinery, and we now need to decide what value of
"ctx" to pass. These cases are:
- trace2/tr2_cfg.c:tr2_cfg_set_fl()
This is indirectly called by git_config_set() so that the trace2
machinery can notice the new config values and update its settings
using the tr2 config parsing function, i.e. tr2_cfg_cb().
- builtin/checkout.c:checkout_main()
This calls git_xmerge_config() as a shorthand for parsing a CLI arg.
This might be worth refactoring away in the future, since
git_xmerge_config() can call git_default_config(), which can do much
more than just parsing.
Handle them by creating a KVI_INIT macro that initializes "struct
key_value_info" to a reasonable default, and use that to construct the
"ctx" arg.
Signed-off-by: Glen Choo <chooglen@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-06-29 03:26:22 +08:00
|
|
|
static int check_repo_format(const char *var, const char *value,
|
|
|
|
const struct config_context *ctx, void *vdata)
|
2014-11-30 16:24:44 +08:00
|
|
|
{
|
2016-03-12 06:37:07 +08: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 18:53:58 +08:00
|
|
|
const char *ext;
|
|
|
|
|
2014-11-30 16:24:44 +08:00
|
|
|
if (strcmp(var, "core.repositoryformatversion") == 0)
|
config: pass kvi to die_bad_number()
Plumb "struct key_value_info" through all code paths that end in
die_bad_number(), which lets us remove the helper functions that read
analogous values from "struct config_reader". As a result, nothing reads
config_reader.config_kvi any more, so remove that too.
In config.c, this requires changing the signature of
git_configset_get_value() to 'return' "kvi" in an out parameter so that
git_configset_get_<type>() can pass it to git_config_<type>(). Only
numeric types will use "kvi", so for non-numeric types (e.g.
git_configset_get_string()), pass NULL to indicate that the out
parameter isn't needed.
Outside of config.c, config callbacks now need to pass "ctx->kvi" to any
of the git_config_<type>() functions that parse a config string into a
number type. Included is a .cocci patch to make that refactor.
The only exceptional case is builtin/config.c, where git_config_<type>()
is called outside of a config callback (namely, on user-provided input),
so config source information has never been available. In this case,
die_bad_number() defaults to a generic, but perfectly descriptive
message. Let's provide a safe, non-NULL for "kvi" anyway, but make sure
not to change the message.
Signed-off-by: Glen Choo <chooglen@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-06-29 03:26:27 +08:00
|
|
|
data->version = git_config_int(var, value, ctx->kvi);
|
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 18:53:58 +08:00
|
|
|
else if (skip_prefix(var, "extensions.", &ext)) {
|
2020-07-16 20:25:13 +08: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-12 06:37:07 +08:00
|
|
|
string_list_append(&data->unknown_extensions, ext);
|
2020-07-16 20:25:13 +08: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 18:53:58 +08:00
|
|
|
}
|
2018-10-21 22:02:28 +08:00
|
|
|
|
config: add ctx arg to config_fn_t
Add a new "const struct config_context *ctx" arg to config_fn_t to hold
additional information about the config iteration operation.
config_context has a "struct key_value_info kvi" member that holds
metadata about the config source being read (e.g. what kind of config
source it is, the filename, etc). In this series, we're only interested
in .kvi, so we could have just used "struct key_value_info" as an arg,
but config_context makes it possible to add/adjust members in the future
without changing the config_fn_t signature. We could also consider other
ways of organizing the args (e.g. moving the config name and value into
config_context or key_value_info), but in my experiments, the
incremental benefit doesn't justify the added complexity (e.g. a
config_fn_t will sometimes invoke another config_fn_t but with a
different config value).
In subsequent commits, the .kvi member will replace the global "struct
config_reader" in config.c, making config iteration a global-free
operation. It requires much more work for the machinery to provide
meaningful values of .kvi, so for now, merely change the signature and
call sites, pass NULL as a placeholder value, and don't rely on the arg
in any meaningful way.
Most of the changes are performed by
contrib/coccinelle/config_fn_ctx.pending.cocci, which, for every
config_fn_t:
- Modifies the signature to accept "const struct config_context *ctx"
- Passes "ctx" to any inner config_fn_t, if needed
- Adds UNUSED attributes to "ctx", if needed
Most config_fn_t instances are easily identified by seeing if they are
called by the various config functions. Most of the remaining ones are
manually named in the .cocci patch. Manual cleanups are still needed,
but the majority of it is trivial; it's either adjusting config_fn_t
that the .cocci patch didn't catch, or adding forward declarations of
"struct config_context ctx" to make the signatures make sense.
The non-trivial changes are in cases where we are invoking a config_fn_t
outside of config machinery, and we now need to decide what value of
"ctx" to pass. These cases are:
- trace2/tr2_cfg.c:tr2_cfg_set_fl()
This is indirectly called by git_config_set() so that the trace2
machinery can notice the new config values and update its settings
using the tr2 config parsing function, i.e. tr2_cfg_cb().
- builtin/checkout.c:checkout_main()
This calls git_xmerge_config() as a shorthand for parsing a CLI arg.
This might be worth refactoring away in the future, since
git_xmerge_config() can call git_default_config(), which can do much
more than just parsing.
Handle them by creating a KVI_INIT macro that initializes "struct
key_value_info" to a reasonable default, and use that to construct the
"ctx" arg.
Signed-off-by: Glen Choo <chooglen@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-06-29 03:26:22 +08:00
|
|
|
return read_worktree_config(var, value, ctx, vdata);
|
2014-11-30 16:24:44 +08:00
|
|
|
}
|
|
|
|
|
2017-11-13 05:28:51 +08:00
|
|
|
static int check_repository_format_gently(const char *gitdir, struct repository_format *candidate, int *nongit_ok)
|
2007-12-05 21:33:32 +08:00
|
|
|
{
|
2014-11-30 16:24:42 +08:00
|
|
|
struct strbuf sb = STRBUF_INIT;
|
2016-03-12 06:37:07 +08:00
|
|
|
struct strbuf err = STRBUF_INIT;
|
2016-03-12 06:37:14 +08: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 18:53:58 +08:00
|
|
|
|
2016-03-12 06:37:14 +08:00
|
|
|
has_common = get_common_dir(&sb, gitdir);
|
2014-11-30 16:24:43 +08:00
|
|
|
strbuf_addstr(&sb, "/config");
|
2017-11-13 05:28:51 +08:00
|
|
|
read_repository_format(candidate, sb.buf);
|
2016-03-12 06:37:07 +08:00
|
|
|
strbuf_release(&sb);
|
2014-11-30 16:24:43 +08:00
|
|
|
|
2010-11-26 23:32:34 +08:00
|
|
|
/*
|
2016-03-12 06:37:07 +08: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 23:32:34 +08:00
|
|
|
*/
|
2017-11-13 05:28:51 +08:00
|
|
|
if (candidate->version < 0)
|
2016-03-12 06:37:07 +08:00
|
|
|
return 0;
|
|
|
|
|
2017-11-13 05:28:51 +08:00
|
|
|
if (verify_repository_format(candidate, &err) < 0) {
|
2016-03-12 06:37:07 +08: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 14:24:29 +08:00
|
|
|
repository_format_precious_objects = candidate->precious_objects;
|
2017-11-13 05:28:51 +08:00
|
|
|
string_list_clear(&candidate->unknown_extensions, 0);
|
2020-07-16 20:25:13 +08:00
|
|
|
string_list_clear(&candidate->v1_only_extensions, 0);
|
2018-10-21 22:02:28 +08:00
|
|
|
|
2023-05-26 09:33:00 +08:00
|
|
|
if (candidate->worktree_config) {
|
2018-10-21 22:02:28 +08:00
|
|
|
/*
|
|
|
|
* 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-12 06:37:14 +08:00
|
|
|
if (!has_common) {
|
2017-11-13 05:28:51 +08:00
|
|
|
if (candidate->is_bare != -1) {
|
|
|
|
is_bare_repository_cfg = candidate->is_bare;
|
2016-03-12 06:37:14 +08:00
|
|
|
if (is_bare_repository_cfg == 1)
|
|
|
|
inside_work_tree = -1;
|
|
|
|
}
|
2017-11-13 05:28:51 +08:00
|
|
|
if (candidate->work_tree) {
|
2016-03-12 06:37:14 +08: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-03-01 04:36:28 +08:00
|
|
|
git_work_tree_cfg = xstrdup(candidate->work_tree);
|
2016-03-12 06:37:07 +08:00
|
|
|
inside_work_tree = -1;
|
2016-03-12 06:37:14 +08:00
|
|
|
}
|
2016-03-12 06:37:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-06-05 17:10:01 +08: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;
|
2023-11-06 18:46:01 +08:00
|
|
|
int ret;
|
2020-06-05 17:10:01 +08:00
|
|
|
|
|
|
|
strbuf_git_common_path(&sb, the_repository, "config");
|
|
|
|
read_repository_format(&repo_fmt, sb.buf);
|
|
|
|
strbuf_release(&sb);
|
|
|
|
|
2023-11-06 18:46:01 +08:00
|
|
|
if (repo_fmt.version >= target_version) {
|
|
|
|
ret = 0;
|
|
|
|
goto out;
|
|
|
|
}
|
2020-06-05 17:10:01 +08:00
|
|
|
|
2020-07-16 14:28:18 +08:00
|
|
|
if (verify_repository_format(&repo_fmt, &err) < 0) {
|
2023-11-06 18:46:01 +08:00
|
|
|
ret = error("cannot upgrade repository format from %d to %d: %s",
|
|
|
|
repo_fmt.version, target_version, err.buf);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (!repo_fmt.version && repo_fmt.unknown_extensions.nr) {
|
|
|
|
ret = error("cannot upgrade repository format: "
|
|
|
|
"unknown extension %s",
|
|
|
|
repo_fmt.unknown_extensions.items[0].string);
|
|
|
|
goto out;
|
2020-06-05 17:10:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
strbuf_addf(&repo_version, "%d", target_version);
|
|
|
|
git_config_set("core.repositoryformatversion", repo_version.buf);
|
2023-11-06 18:46:01 +08:00
|
|
|
|
|
|
|
ret = 1;
|
|
|
|
|
|
|
|
out:
|
2023-11-06 18:46:05 +08:00
|
|
|
clear_repository_format(&repo_fmt);
|
2020-06-05 17:10:01 +08:00
|
|
|
strbuf_release(&repo_version);
|
2023-11-06 18:46:01 +08:00
|
|
|
strbuf_release(&err);
|
|
|
|
return ret;
|
2020-06-05 17:10:01 +08: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-03-01 04:36:28 +08: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-12 06:37:14 +08:00
|
|
|
int read_repository_format(struct repository_format *format, const char *path)
|
2016-03-12 06:37:07 +08: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-03-01 04:36:28 +08:00
|
|
|
clear_repository_format(format);
|
2016-03-12 06:37:14 +08: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-03-01 04:36:28 +08:00
|
|
|
if (format->version == -1)
|
|
|
|
clear_repository_format(format);
|
2016-03-12 06:37:07 +08: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-03-01 04:36:28 +08:00
|
|
|
void clear_repository_format(struct repository_format *format)
|
|
|
|
{
|
|
|
|
string_list_clear(&format->unknown_extensions, 0);
|
2020-07-16 20:25:13 +08: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-03-01 04:36:28 +08:00
|
|
|
free(format->work_tree);
|
|
|
|
free(format->partial_clone);
|
|
|
|
init_repository_format(format);
|
|
|
|
}
|
|
|
|
|
2016-03-12 06:37:07 +08:00
|
|
|
int verify_repository_format(const struct repository_format *format,
|
|
|
|
struct strbuf *err)
|
|
|
|
{
|
|
|
|
if (GIT_REPO_VERSION_READ < format->version) {
|
2016-03-12 06:37:22 +08:00
|
|
|
strbuf_addf(err, _("Expected git repo version <= %d, found %d"),
|
2016-03-12 06:37:07 +08: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 18:53:58 +08:00
|
|
|
int i;
|
|
|
|
|
2021-05-18 14:19:17 +08: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 18:53:58 +08:00
|
|
|
|
2016-03-12 06:37:07 +08: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 18:53:58 +08:00
|
|
|
}
|
|
|
|
|
2020-07-16 20:25:13 +08:00
|
|
|
if (format->version == 0 && format->v1_only_extensions.nr) {
|
|
|
|
int i;
|
|
|
|
|
|
|
|
strbuf_addstr(err,
|
2021-05-18 14:19:17 +08: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 20:25:13 +08: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-12 06:37:07 +08:00
|
|
|
return 0;
|
2007-12-05 21:33:32 +08:00
|
|
|
}
|
|
|
|
|
2017-01-25 07:56:50 +08: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 21:19:15 +08:00
|
|
|
die_errno(_("error opening '%s'"), path);
|
2017-01-25 07:56:50 +08:00
|
|
|
case READ_GITFILE_ERR_TOO_LARGE:
|
2018-02-13 21:19:15 +08:00
|
|
|
die(_("too large to be a .git file: '%s'"), path);
|
2017-01-25 07:56:50 +08:00
|
|
|
case READ_GITFILE_ERR_READ_FAILED:
|
2018-02-13 21:19:15 +08:00
|
|
|
die(_("error reading %s"), path);
|
2017-01-25 07:56:50 +08:00
|
|
|
case READ_GITFILE_ERR_INVALID_FORMAT:
|
2018-02-13 21:19:15 +08:00
|
|
|
die(_("invalid gitfile format: %s"), path);
|
2017-01-25 07:56:50 +08:00
|
|
|
case READ_GITFILE_ERR_NO_PATH:
|
2018-02-13 21:19:15 +08:00
|
|
|
die(_("no path in gitfile: %s"), path);
|
2017-01-25 07:56:50 +08:00
|
|
|
case READ_GITFILE_ERR_NOT_A_REPO:
|
2018-02-13 21:19:15 +08:00
|
|
|
die(_("not a git repository: %s"), dir);
|
2017-01-25 07:56:50 +08:00
|
|
|
default:
|
2018-05-02 17:38:39 +08:00
|
|
|
BUG("unknown error code");
|
2017-01-25 07:56:50 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-02-21 06:13:13 +08:00
|
|
|
/*
|
|
|
|
* Try to read the location of the git directory from the .git file,
|
2017-09-26 19:21:49 +08:00
|
|
|
* return path to git directory if found. The return value comes from
|
|
|
|
* a shared buffer.
|
2015-06-10 02:24:35 +08: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-21 06:13:13 +08:00
|
|
|
*/
|
2015-06-10 02:24:35 +08:00
|
|
|
const char *read_gitfile_gently(const char *path, int *return_error_code)
|
2008-02-21 06:13:13 +08:00
|
|
|
{
|
2015-06-16 03:39:52 +08:00
|
|
|
const int max_file_size = 1 << 20; /* 1MB */
|
2015-06-10 02:24:35 +08:00
|
|
|
int error_code = 0;
|
|
|
|
char *buf = NULL;
|
|
|
|
char *dir = NULL;
|
2010-01-09 11:36:41 +08:00
|
|
|
const char *slash;
|
2008-02-21 06:13:13 +08:00
|
|
|
struct stat st;
|
|
|
|
int fd;
|
2011-05-27 00:28:44 +08:00
|
|
|
ssize_t len;
|
2020-03-10 21:11:22 +08:00
|
|
|
static struct strbuf realpath = STRBUF_INIT;
|
2008-02-21 06:13:13 +08:00
|
|
|
|
2015-06-10 02:24:35 +08:00
|
|
|
if (stat(path, &st)) {
|
2017-03-14 04:12:18 +08:00
|
|
|
/* NEEDSWORK: discern between ENOENT vs other errors */
|
2015-06-10 02:24:35 +08: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-16 03:39:52 +08:00
|
|
|
if (st.st_size > max_file_size) {
|
|
|
|
error_code = READ_GITFILE_ERR_TOO_LARGE;
|
|
|
|
goto cleanup_return;
|
|
|
|
}
|
2008-02-21 06:13:13 +08:00
|
|
|
fd = open(path, O_RDONLY);
|
2015-06-10 02:24:35 +08:00
|
|
|
if (fd < 0) {
|
|
|
|
error_code = READ_GITFILE_ERR_OPEN_FAILED;
|
|
|
|
goto cleanup_return;
|
|
|
|
}
|
2016-02-23 06:44:28 +08:00
|
|
|
buf = xmallocz(st.st_size);
|
2008-02-21 06:13:13 +08:00
|
|
|
len = read_in_full(fd, buf, st.st_size);
|
|
|
|
close(fd);
|
2015-06-10 02:24:35 +08: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-21 06:13:13 +08:00
|
|
|
while (buf[len - 1] == '\n' || buf[len - 1] == '\r')
|
|
|
|
len--;
|
2015-06-10 02:24:35 +08:00
|
|
|
if (len < 9) {
|
|
|
|
error_code = READ_GITFILE_ERR_NO_PATH;
|
|
|
|
goto cleanup_return;
|
|
|
|
}
|
2008-02-21 06:13:13 +08:00
|
|
|
buf[len] = '\0';
|
2010-01-09 11:36:41 +08:00
|
|
|
dir = buf + 8;
|
|
|
|
|
|
|
|
if (!is_absolute_path(dir) && (slash = strrchr(path, '/'))) {
|
|
|
|
size_t pathlen = slash+1 - path;
|
2015-09-25 05:07:03 +08:00
|
|
|
dir = xstrfmt("%.*s%.*s", (int)pathlen, path,
|
|
|
|
(int)(len - 8), buf + 8);
|
2010-01-09 11:36:41 +08:00
|
|
|
free(buf);
|
|
|
|
buf = dir;
|
|
|
|
}
|
2015-06-10 02:24:35 +08:00
|
|
|
if (!is_git_directory(dir)) {
|
|
|
|
error_code = READ_GITFILE_ERR_NOT_A_REPO;
|
|
|
|
goto cleanup_return;
|
|
|
|
}
|
2020-03-10 21:11:22 +08:00
|
|
|
|
|
|
|
strbuf_realpath(&realpath, dir, 1);
|
|
|
|
path = realpath.buf;
|
2010-01-09 11:36:41 +08:00
|
|
|
|
2015-06-10 02:24:35 +08:00
|
|
|
cleanup_return:
|
|
|
|
if (return_error_code)
|
|
|
|
*return_error_code = error_code;
|
2017-01-25 07:56:50 +08:00
|
|
|
else if (error_code)
|
|
|
|
read_gitfile_error_die(error_code, path, dir);
|
2015-06-10 02:24:35 +08:00
|
|
|
|
2008-02-21 06:13:13 +08:00
|
|
|
free(buf);
|
2015-06-26 17:03:31 +08:00
|
|
|
return error_code ? NULL : path;
|
2008-02-21 06:13:13 +08:00
|
|
|
}
|
|
|
|
|
2010-07-24 19:19:44 +08:00
|
|
|
static const char *setup_explicit_git_dir(const char *gitdirenv,
|
2014-07-29 02:26:40 +08:00
|
|
|
struct strbuf *cwd,
|
2017-11-13 05:28:51 +08:00
|
|
|
struct repository_format *repo_fmt,
|
2010-11-26 23:32:39 +08:00
|
|
|
int *nongit_ok)
|
2010-07-24 19:19:44 +08:00
|
|
|
{
|
2010-11-26 23:32:39 +08:00
|
|
|
const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);
|
|
|
|
const char *worktree;
|
|
|
|
char *gitfile;
|
2011-03-26 17:04:24 +08:00
|
|
|
int offset;
|
2010-07-24 19:19:44 +08:00
|
|
|
|
|
|
|
if (PATH_MAX - 40 < strlen(gitdirenv))
|
2018-02-13 21:19:15 +08:00
|
|
|
die(_("'$%s' too big"), GIT_DIR_ENVIRONMENT);
|
2010-11-26 23:32:39 +08:00
|
|
|
|
2011-08-23 05:04:56 +08:00
|
|
|
gitfile = (char*)read_gitfile(gitdirenv);
|
2010-11-26 23:32:39 +08:00
|
|
|
if (gitfile) {
|
|
|
|
gitfile = xstrdup(gitfile);
|
|
|
|
gitdirenv = gitfile;
|
|
|
|
}
|
|
|
|
|
2010-07-24 19:19:44 +08:00
|
|
|
if (!is_git_directory(gitdirenv)) {
|
|
|
|
if (nongit_ok) {
|
|
|
|
*nongit_ok = 1;
|
2010-11-26 23:32:39 +08:00
|
|
|
free(gitfile);
|
2010-07-24 19:19:44 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
2018-02-13 21:19:15 +08:00
|
|
|
die(_("not a git repository: '%s'"), gitdirenv);
|
2010-07-24 19:19:44 +08:00
|
|
|
}
|
2010-11-26 23:32:39 +08:00
|
|
|
|
2017-11-13 05:28:51 +08:00
|
|
|
if (check_repository_format_gently(gitdirenv, repo_fmt, nongit_ok)) {
|
2010-11-26 23:32:39 +08:00
|
|
|
free(gitfile);
|
|
|
|
return NULL;
|
2010-07-24 19:19:44 +08:00
|
|
|
}
|
2010-11-26 23:32:39 +08: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 14:49:10 +08: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 23:32:39 +08:00
|
|
|
|
|
|
|
/* #18, #26 */
|
2020-03-07 03:03:13 +08:00
|
|
|
set_git_dir(gitdirenv, 0);
|
2010-11-26 23:32:39 +08:00
|
|
|
free(gitfile);
|
2010-07-24 19:19:44 +08:00
|
|
|
return NULL;
|
2010-11-26 23:32:39 +08: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-29 02:30:39 +08:00
|
|
|
char *core_worktree;
|
2010-11-26 23:32:39 +08:00
|
|
|
if (chdir(gitdirenv))
|
2018-02-13 21:19:15 +08:00
|
|
|
die_errno(_("cannot chdir to '%s'"), gitdirenv);
|
2010-11-26 23:32:39 +08:00
|
|
|
if (chdir(git_work_tree_cfg))
|
2018-02-13 21:19:15 +08:00
|
|
|
die_errno(_("cannot chdir to '%s'"), git_work_tree_cfg);
|
2014-07-29 02:30:39 +08:00
|
|
|
core_worktree = xgetcwd();
|
2014-07-29 02:26:40 +08:00
|
|
|
if (chdir(cwd->buf))
|
2018-02-13 21:19:15 +08:00
|
|
|
die_errno(_("cannot come back to cwd"));
|
2010-11-26 23:32:39 +08:00
|
|
|
set_git_work_tree(core_worktree);
|
2014-07-29 02:30:39 +08:00
|
|
|
free(core_worktree);
|
2010-11-26 23:32:39 +08: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 17:32:22 +08:00
|
|
|
else if (!git_env_bool(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, 1)) {
|
|
|
|
/* #16d */
|
2020-03-07 03:03:13 +08: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 17:32:22 +08:00
|
|
|
free(gitfile);
|
|
|
|
return NULL;
|
|
|
|
}
|
2010-11-26 23:32:39 +08: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-29 02:26:40 +08:00
|
|
|
if (!strcmp(cwd->buf, worktree)) { /* cwd == worktree */
|
2020-03-07 03:03:13 +08:00
|
|
|
set_git_dir(gitdirenv, 0);
|
2010-11-26 23:32:39 +08:00
|
|
|
free(gitfile);
|
2010-07-24 19:19:44 +08:00
|
|
|
return NULL;
|
2010-11-26 23:32:39 +08:00
|
|
|
}
|
2010-07-24 19:19:44 +08:00
|
|
|
|
2014-07-29 02:26:40 +08:00
|
|
|
offset = dir_inside_of(cwd->buf, worktree);
|
2011-03-26 17:04:24 +08:00
|
|
|
if (offset >= 0) { /* cwd inside worktree? */
|
2020-03-07 03:03:13 +08:00
|
|
|
set_git_dir(gitdirenv, 1);
|
2010-11-26 23:32:39 +08:00
|
|
|
if (chdir(worktree))
|
2018-02-13 21:19:15 +08:00
|
|
|
die_errno(_("cannot chdir to '%s'"), worktree);
|
2014-07-29 02:26:40 +08:00
|
|
|
strbuf_addch(cwd, '/');
|
2010-11-26 23:32:39 +08:00
|
|
|
free(gitfile);
|
2014-07-29 02:26:40 +08:00
|
|
|
return cwd->buf + offset;
|
2010-07-24 19:20:15 +08:00
|
|
|
}
|
2010-11-26 23:32:39 +08:00
|
|
|
|
|
|
|
/* cwd outside worktree */
|
2020-03-07 03:03:13 +08:00
|
|
|
set_git_dir(gitdirenv, 0);
|
2010-11-26 23:32:39 +08:00
|
|
|
free(gitfile);
|
|
|
|
return NULL;
|
2010-07-24 19:20:15 +08:00
|
|
|
}
|
|
|
|
|
2010-11-26 23:32:38 +08:00
|
|
|
static const char *setup_discovered_git_dir(const char *gitdir,
|
2014-07-29 02:26:40 +08:00
|
|
|
struct strbuf *cwd, int offset,
|
2017-11-13 05:28:51 +08:00
|
|
|
struct repository_format *repo_fmt,
|
2010-11-26 23:32:38 +08:00
|
|
|
int *nongit_ok)
|
2010-07-24 20:11:58 +08:00
|
|
|
{
|
2017-11-13 05:28:51 +08:00
|
|
|
if (check_repository_format_gently(gitdir, repo_fmt, nongit_ok))
|
2010-11-26 23:32:38 +08:00
|
|
|
return NULL;
|
2010-07-24 20:11:58 +08:00
|
|
|
|
2011-01-19 20:42:30 +08:00
|
|
|
/* --work-tree is set without --git-dir; use discovered one */
|
|
|
|
if (getenv(GIT_WORK_TREE_ENVIRONMENT) || git_work_tree_cfg) {
|
2017-05-04 21:56:51 +08:00
|
|
|
char *to_free = NULL;
|
|
|
|
const char *ret;
|
|
|
|
|
2014-07-29 02:26:40 +08:00
|
|
|
if (offset != cwd->len && !is_absolute_path(gitdir))
|
2017-05-04 21:56:51 +08:00
|
|
|
gitdir = to_free = real_pathdup(gitdir, 1);
|
2014-07-29 02:26:40 +08:00
|
|
|
if (chdir(cwd->buf))
|
2018-02-13 21:19:15 +08:00
|
|
|
die_errno(_("cannot come back to cwd"));
|
2017-11-13 05:28:51 +08:00
|
|
|
ret = setup_explicit_git_dir(gitdir, cwd, repo_fmt, nongit_ok);
|
2017-05-04 21:56:51 +08:00
|
|
|
free(to_free);
|
|
|
|
return ret;
|
2011-01-19 20:42:30 +08:00
|
|
|
}
|
|
|
|
|
2010-11-26 23:32:38 +08:00
|
|
|
/* #16.2, #17.2, #20.2, #21.2, #24, #25, #28, #29 (see t1510) */
|
|
|
|
if (is_bare_repository_cfg > 0) {
|
2020-03-07 03:03:13 +08:00
|
|
|
set_git_dir(gitdir, (offset != cwd->len));
|
2014-07-29 02:26:40 +08:00
|
|
|
if (chdir(cwd->buf))
|
2018-02-13 21:19:15 +08:00
|
|
|
die_errno(_("cannot come back to cwd"));
|
2010-07-24 20:11:58 +08:00
|
|
|
return NULL;
|
2010-11-26 23:32:38 +08:00
|
|
|
}
|
2010-07-24 20:11:58 +08:00
|
|
|
|
2010-11-26 23:32:38 +08:00
|
|
|
/* #0, #1, #5, #8, #9, #12, #13 */
|
|
|
|
set_git_work_tree(".");
|
|
|
|
if (strcmp(gitdir, DEFAULT_GIT_DIR_ENVIRONMENT))
|
2020-03-07 03:03:13 +08:00
|
|
|
set_git_dir(gitdir, 0);
|
2010-07-24 20:11:58 +08:00
|
|
|
inside_git_dir = 0;
|
2010-11-26 23:32:38 +08: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-25 06:10:46 +08:00
|
|
|
if (offset >= cwd->len)
|
2010-07-24 20:11:58 +08:00
|
|
|
return NULL;
|
|
|
|
|
2017-03-14 04:09:44 +08: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-29 02:26:40 +08:00
|
|
|
strbuf_addch(cwd, '/');
|
|
|
|
return cwd->buf + offset;
|
2010-07-24 20:11:58 +08:00
|
|
|
}
|
|
|
|
|
2010-11-26 23:32:36 +08:00
|
|
|
/* #16.1, #17.1, #20.1, #21.1, #22.1 (see t1510) */
|
2014-07-29 02:26:40 +08:00
|
|
|
static const char *setup_bare_git_dir(struct strbuf *cwd, int offset,
|
2017-11-13 05:28:51 +08:00
|
|
|
struct repository_format *repo_fmt,
|
2014-07-29 02:26:40 +08:00
|
|
|
int *nongit_ok)
|
2010-07-24 19:25:32 +08:00
|
|
|
{
|
|
|
|
int root_len;
|
|
|
|
|
2017-11-13 05:28:51 +08:00
|
|
|
if (check_repository_format_gently(".", repo_fmt, nongit_ok))
|
2010-11-26 23:32:36 +08: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 17:32:22 +08:00
|
|
|
setenv(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, "0", 1);
|
|
|
|
|
2011-01-19 20:42:30 +08:00
|
|
|
/* --work-tree is set without --git-dir; use discovered one */
|
|
|
|
if (getenv(GIT_WORK_TREE_ENVIRONMENT) || git_work_tree_cfg) {
|
2017-05-04 21:56:47 +08:00
|
|
|
static const char *gitdir;
|
2011-01-19 20:42:30 +08:00
|
|
|
|
2014-07-29 02:26:40 +08:00
|
|
|
gitdir = offset == cwd->len ? "." : xmemdupz(cwd->buf, offset);
|
|
|
|
if (chdir(cwd->buf))
|
2018-02-13 21:19:15 +08:00
|
|
|
die_errno(_("cannot come back to cwd"));
|
2017-11-13 05:28:51 +08:00
|
|
|
return setup_explicit_git_dir(gitdir, cwd, repo_fmt, nongit_ok);
|
2011-01-19 20:42:30 +08:00
|
|
|
}
|
|
|
|
|
2010-07-24 19:25:32 +08:00
|
|
|
inside_git_dir = 1;
|
2010-11-26 23:32:36 +08:00
|
|
|
inside_work_tree = 0;
|
2014-07-29 02:26:40 +08:00
|
|
|
if (offset != cwd->len) {
|
|
|
|
if (chdir(cwd->buf))
|
2018-02-13 21:19:15 +08:00
|
|
|
die_errno(_("cannot come back to cwd"));
|
2014-07-29 02:26:40 +08:00
|
|
|
root_len = offset_1st_component(cwd->buf);
|
|
|
|
strbuf_setlen(cwd, offset > root_len ? offset : root_len);
|
2020-03-07 03:03:13 +08:00
|
|
|
set_git_dir(cwd->buf, 0);
|
2010-11-26 23:32:34 +08:00
|
|
|
}
|
2010-11-26 23:32:36 +08:00
|
|
|
else
|
2020-03-07 03:03:13 +08:00
|
|
|
set_git_dir(".", 0);
|
2010-07-24 19:25:32 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-04-13 07:11:36 +08:00
|
|
|
static dev_t get_device_or_die(const char *path, const char *prefix, int prefix_len)
|
2010-07-24 19:27:58 +08:00
|
|
|
{
|
|
|
|
struct stat buf;
|
2012-04-13 07:11:36 +08:00
|
|
|
if (stat(path, &buf)) {
|
2018-02-13 21:19:15 +08:00
|
|
|
die_errno(_("failed to stat '%*s%s%s'"),
|
2012-04-13 07:11:36 +08:00
|
|
|
prefix_len,
|
2010-07-24 19:27:58 +08:00
|
|
|
prefix ? prefix : "",
|
|
|
|
prefix ? "/" : "", path);
|
2012-04-13 07:11:36 +08:00
|
|
|
}
|
2010-07-24 19:27:58 +08:00
|
|
|
return buf.st_dev;
|
|
|
|
}
|
|
|
|
|
2012-10-29 00:16:25 +08:00
|
|
|
/*
|
2012-10-29 00:16:26 +08:00
|
|
|
* A "string_list_each_func_t" function that canonicalizes an entry
|
2020-03-10 21:11:23 +08: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 17:09:24 +08:00
|
|
|
* discards it if unusable. The presence of an empty entry in
|
|
|
|
* GIT_CEILING_DIRECTORIES turns off canonicalization for all
|
|
|
|
* subsequent entries.
|
2012-10-29 00:16:25 +08:00
|
|
|
*/
|
2012-10-29 00:16:26 +08: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 17:09:24 +08:00
|
|
|
void *cb_data)
|
2012-10-29 00:16:25 +08: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 17:09:24 +08:00
|
|
|
int *empty_entry_found = cb_data;
|
2012-10-29 00:16:26 +08:00
|
|
|
char *ceil = item->string;
|
2012-10-29 00:16:25 +08: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 17:09:24 +08:00
|
|
|
if (!*ceil) {
|
|
|
|
*empty_entry_found = 1;
|
2012-10-29 00:16:25 +08: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 17:09:24 +08:00
|
|
|
} else if (!is_absolute_path(ceil)) {
|
2012-10-29 00:16:25 +08: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 17:09:24 +08:00
|
|
|
} else if (*empty_entry_found) {
|
|
|
|
/* Keep entry but do not canonicalize it */
|
|
|
|
return 1;
|
|
|
|
} else {
|
2017-03-08 23:43:40 +08:00
|
|
|
char *real_path = real_pathdup(ceil, 0);
|
2016-12-13 02:16:55 +08: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 17:09:24 +08:00
|
|
|
return 0;
|
2016-12-13 02:16:55 +08: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 17:09:24 +08:00
|
|
|
free(item->string);
|
2016-12-13 02:16:55 +08: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 17:09:24 +08:00
|
|
|
return 1;
|
|
|
|
}
|
2012-10-29 00:16:25 +08:00
|
|
|
}
|
|
|
|
|
2022-03-02 19:23:04 +08:00
|
|
|
struct safe_directory_data {
|
2024-07-31 02:43:50 +08:00
|
|
|
char *path;
|
2022-03-02 19:23:04 +08:00
|
|
|
int is_safe;
|
|
|
|
};
|
|
|
|
|
config: add ctx arg to config_fn_t
Add a new "const struct config_context *ctx" arg to config_fn_t to hold
additional information about the config iteration operation.
config_context has a "struct key_value_info kvi" member that holds
metadata about the config source being read (e.g. what kind of config
source it is, the filename, etc). In this series, we're only interested
in .kvi, so we could have just used "struct key_value_info" as an arg,
but config_context makes it possible to add/adjust members in the future
without changing the config_fn_t signature. We could also consider other
ways of organizing the args (e.g. moving the config name and value into
config_context or key_value_info), but in my experiments, the
incremental benefit doesn't justify the added complexity (e.g. a
config_fn_t will sometimes invoke another config_fn_t but with a
different config value).
In subsequent commits, the .kvi member will replace the global "struct
config_reader" in config.c, making config iteration a global-free
operation. It requires much more work for the machinery to provide
meaningful values of .kvi, so for now, merely change the signature and
call sites, pass NULL as a placeholder value, and don't rely on the arg
in any meaningful way.
Most of the changes are performed by
contrib/coccinelle/config_fn_ctx.pending.cocci, which, for every
config_fn_t:
- Modifies the signature to accept "const struct config_context *ctx"
- Passes "ctx" to any inner config_fn_t, if needed
- Adds UNUSED attributes to "ctx", if needed
Most config_fn_t instances are easily identified by seeing if they are
called by the various config functions. Most of the remaining ones are
manually named in the .cocci patch. Manual cleanups are still needed,
but the majority of it is trivial; it's either adjusting config_fn_t
that the .cocci patch didn't catch, or adding forward declarations of
"struct config_context ctx" to make the signatures make sense.
The non-trivial changes are in cases where we are invoking a config_fn_t
outside of config machinery, and we now need to decide what value of
"ctx" to pass. These cases are:
- trace2/tr2_cfg.c:tr2_cfg_set_fl()
This is indirectly called by git_config_set() so that the trace2
machinery can notice the new config values and update its settings
using the tr2 config parsing function, i.e. tr2_cfg_cb().
- builtin/checkout.c:checkout_main()
This calls git_xmerge_config() as a shorthand for parsing a CLI arg.
This might be worth refactoring away in the future, since
git_xmerge_config() can call git_default_config(), which can do much
more than just parsing.
Handle them by creating a KVI_INIT macro that initializes "struct
key_value_info" to a reasonable default, and use that to construct the
"ctx" arg.
Signed-off-by: Glen Choo <chooglen@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-06-29 03:26:22 +08:00
|
|
|
static int safe_directory_cb(const char *key, const char *value,
|
|
|
|
const struct config_context *ctx UNUSED, void *d)
|
2022-03-02 19:23:04 +08:00
|
|
|
{
|
|
|
|
struct safe_directory_data *data = d;
|
|
|
|
|
2022-04-13 23:32:30 +08:00
|
|
|
if (strcmp(key, "safe.directory"))
|
|
|
|
return 0;
|
|
|
|
|
2022-04-13 23:32:31 +08:00
|
|
|
if (!value || !*value) {
|
2022-03-02 19:23:04 +08:00
|
|
|
data->is_safe = 0;
|
2022-04-13 23:32:31 +08:00
|
|
|
} else if (!strcmp(value, "*")) {
|
|
|
|
data->is_safe = 1;
|
|
|
|
} else {
|
2024-06-13 04:37:16 +08:00
|
|
|
char *allowed = NULL;
|
2024-05-30 00:02:16 +08:00
|
|
|
|
|
|
|
if (!git_config_pathname(&allowed, key, value)) {
|
safe.directory: normalize the configured path
The pathname of a repository comes from getcwd() and it could be a
path aliased via symbolic links, e.g., the real directory may be
/home/u/repository but a symbolic link /home/u/repo may point at it,
and the clone request may come as "git clone file:///home/u/repo/"
A request to check if /home/u/repository is safe would be rejected
if the safe.directory configuration allows /home/u/repo/ but not its
alias /home/u/repository/. Normalize the paths configured for the
safe.directory configuration variable before comparing them with the
path being checked.
Two and a half things to note, compared to the previous step to
normalize the actual path of the suspected repository, are:
- A configured safe.directory may be coming from .gitignore in the
home directory that may be shared across machines. The path
meant to match with an entry may not necessarily exist on all of
such machines, so not being able to convert them to real path on
this machine is *not* a condition that is worthy of warning.
Hence, we ignore a path that cannot be converted to a real path.
- A configured safe.directory is essentially a random string that
user throws at us, written completely unrelated to the directory
the current process happens to be in. Hence it makes little
sense to give a non-absolute path. Hence we ignore any
non-absolute paths, except for ".".
- The safe.directory set to "." was once advertised on the list as
a valid workaround for the regression caused by the overly tight
safe.directory check introduced in 2.45.1; we treat it to mean
"if we are at the top level of a repository, it is OK".
(cf. <834862fd-b579-438a-b9b3-5246bf27ce8a@gmail.com>).
Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
Helped-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-07-31 02:43:51 +08:00
|
|
|
char *normalized = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Setting safe.directory to a non-absolute path
|
|
|
|
* makes little sense---it won't be relative to
|
|
|
|
* the configuration file the item is defined in.
|
|
|
|
* Except for ".", which means "if we are at the top
|
|
|
|
* level of a repository, then it is OK", which is
|
|
|
|
* slightly tighter than "*" that allows discovery.
|
|
|
|
*/
|
|
|
|
if (!is_absolute_path(allowed) && strcmp(allowed, ".")) {
|
|
|
|
warning(_("safe.directory '%s' not absolute"),
|
|
|
|
allowed);
|
|
|
|
goto next;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* A .gitconfig in $HOME may be shared across
|
|
|
|
* different machines and safe.directory entries
|
|
|
|
* may or may not exist as paths on all of these
|
|
|
|
* machines. In other words, it is not a warning
|
|
|
|
* worthy event when there is no such path on this
|
|
|
|
* machine---the entry may be useful elsewhere.
|
|
|
|
*/
|
|
|
|
normalized = real_pathdup(allowed, 0);
|
|
|
|
if (!normalized)
|
|
|
|
goto next;
|
|
|
|
|
|
|
|
if (ends_with(normalized, "/*")) {
|
|
|
|
size_t len = strlen(normalized);
|
|
|
|
if (!fspathncmp(normalized, data->path, len - 1))
|
2024-05-30 00:02:16 +08:00
|
|
|
data->is_safe = 1;
|
safe.directory: normalize the configured path
The pathname of a repository comes from getcwd() and it could be a
path aliased via symbolic links, e.g., the real directory may be
/home/u/repository but a symbolic link /home/u/repo may point at it,
and the clone request may come as "git clone file:///home/u/repo/"
A request to check if /home/u/repository is safe would be rejected
if the safe.directory configuration allows /home/u/repo/ but not its
alias /home/u/repository/. Normalize the paths configured for the
safe.directory configuration variable before comparing them with the
path being checked.
Two and a half things to note, compared to the previous step to
normalize the actual path of the suspected repository, are:
- A configured safe.directory may be coming from .gitignore in the
home directory that may be shared across machines. The path
meant to match with an entry may not necessarily exist on all of
such machines, so not being able to convert them to real path on
this machine is *not* a condition that is worthy of warning.
Hence, we ignore a path that cannot be converted to a real path.
- A configured safe.directory is essentially a random string that
user throws at us, written completely unrelated to the directory
the current process happens to be in. Hence it makes little
sense to give a non-absolute path. Hence we ignore any
non-absolute paths, except for ".".
- The safe.directory set to "." was once advertised on the list as
a valid workaround for the regression caused by the overly tight
safe.directory check introduced in 2.45.1; we treat it to mean
"if we are at the top level of a repository, it is OK".
(cf. <834862fd-b579-438a-b9b3-5246bf27ce8a@gmail.com>).
Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
Helped-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-07-31 02:43:51 +08:00
|
|
|
} else if (!fspathcmp(data->path, normalized)) {
|
2024-05-30 00:02:16 +08:00
|
|
|
data->is_safe = 1;
|
|
|
|
}
|
safe.directory: normalize the configured path
The pathname of a repository comes from getcwd() and it could be a
path aliased via symbolic links, e.g., the real directory may be
/home/u/repository but a symbolic link /home/u/repo may point at it,
and the clone request may come as "git clone file:///home/u/repo/"
A request to check if /home/u/repository is safe would be rejected
if the safe.directory configuration allows /home/u/repo/ but not its
alias /home/u/repository/. Normalize the paths configured for the
safe.directory configuration variable before comparing them with the
path being checked.
Two and a half things to note, compared to the previous step to
normalize the actual path of the suspected repository, are:
- A configured safe.directory may be coming from .gitignore in the
home directory that may be shared across machines. The path
meant to match with an entry may not necessarily exist on all of
such machines, so not being able to convert them to real path on
this machine is *not* a condition that is worthy of warning.
Hence, we ignore a path that cannot be converted to a real path.
- A configured safe.directory is essentially a random string that
user throws at us, written completely unrelated to the directory
the current process happens to be in. Hence it makes little
sense to give a non-absolute path. Hence we ignore any
non-absolute paths, except for ".".
- The safe.directory set to "." was once advertised on the list as
a valid workaround for the regression caused by the overly tight
safe.directory check introduced in 2.45.1; we treat it to mean
"if we are at the top level of a repository, it is OK".
(cf. <834862fd-b579-438a-b9b3-5246bf27ce8a@gmail.com>).
Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
Helped-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-07-31 02:43:51 +08:00
|
|
|
next:
|
|
|
|
free(normalized);
|
2024-06-13 04:37:16 +08:00
|
|
|
free(allowed);
|
2024-07-31 02:43:49 +08:00
|
|
|
}
|
2022-03-02 19:23:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-05-11 03:35:29 +08: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 21:27:47 +08:00
|
|
|
const char *worktree, const char *gitdir,
|
|
|
|
struct strbuf *report)
|
2022-03-02 19:23:04 +08:00
|
|
|
{
|
2024-07-31 02:43:50 +08:00
|
|
|
struct safe_directory_data data = { 0 };
|
2022-03-02 19:23:04 +08:00
|
|
|
|
2022-04-13 23:32:29 +08:00
|
|
|
if (!git_env_bool("GIT_TEST_ASSUME_DIFFERENT_OWNER", 0) &&
|
2022-08-08 21:27:47 +08: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 19:23:04 +08:00
|
|
|
return 1;
|
|
|
|
|
2024-07-31 02:43:50 +08:00
|
|
|
/*
|
|
|
|
* normalize the data.path for comparison with normalized paths
|
|
|
|
* that come from the configuration file. The path is unsafe
|
|
|
|
* if it cannot be normalized.
|
|
|
|
*/
|
|
|
|
data.path = real_pathdup(worktree ? worktree : gitdir, 0);
|
|
|
|
if (!data.path)
|
|
|
|
return 0;
|
|
|
|
|
2022-05-11 03:35:29 +08: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-15 05:28:00 +08:00
|
|
|
git_protected_config(safe_directory_cb, &data);
|
2022-03-02 19:23:04 +08:00
|
|
|
|
2024-07-31 02:43:50 +08:00
|
|
|
free(data.path);
|
2022-03-02 19:23:04 +08:00
|
|
|
return data.is_safe;
|
|
|
|
}
|
|
|
|
|
fetch/clone: detect dubious ownership of local repositories
When cloning from somebody else's repositories, it is possible that,
say, the `upload-pack` command is overridden in the repository that is
about to be cloned, which would then be run in the user's context who
started the clone.
To remind the user that this is a potentially unsafe operation, let's
extend the ownership checks we have already established for regular
gitdir discovery to extend also to local repositories that are about to
be cloned.
This protection extends also to file:// URLs.
The fixes in this commit address CVE-2024-32004.
Note: This commit does not touch the `fetch`/`clone` code directly, but
instead the function used implicitly by both: `enter_repo()`. This
function is also used by `git receive-pack` (i.e. pushes), by `git
upload-archive`, by `git daemon` and by `git http-backend`. In setups
that want to serve repositories owned by different users than the
account running the service, this will require `safe.*` settings to be
configured accordingly.
Also note: there are tiny time windows where a time-of-check-time-of-use
("TOCTOU") race is possible. The real solution to those would be to work
with `fstat()` and `openat()`. However, the latter function is not
available on Windows (and would have to be emulated with rather
expensive low-level `NtCreateFile()` calls), and the changes would be
quite extensive, for my taste too extensive for the little gain given
that embargoed releases need to pay extra attention to avoid introducing
inadvertent bugs.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2024-04-10 20:39:37 +08:00
|
|
|
void die_upon_dubious_ownership(const char *gitfile, const char *worktree,
|
|
|
|
const char *gitdir)
|
|
|
|
{
|
|
|
|
struct strbuf report = STRBUF_INIT, quoted = STRBUF_INIT;
|
|
|
|
const char *path;
|
|
|
|
|
|
|
|
if (ensure_valid_ownership(gitfile, worktree, gitdir, &report))
|
|
|
|
return;
|
|
|
|
|
|
|
|
strbuf_complete(&report, '\n');
|
|
|
|
path = gitfile ? gitfile : gitdir;
|
|
|
|
sq_quote_buf_pretty("ed, path);
|
|
|
|
|
|
|
|
die(_("detected dubious ownership in repository at '%s'\n"
|
|
|
|
"%s"
|
|
|
|
"To add an exception for this directory, call:\n"
|
|
|
|
"\n"
|
|
|
|
"\tgit config --global --add safe.directory %s"),
|
|
|
|
path, report.buf, quoted.buf);
|
|
|
|
}
|
2022-03-02 19:23:04 +08:00
|
|
|
|
config: add ctx arg to config_fn_t
Add a new "const struct config_context *ctx" arg to config_fn_t to hold
additional information about the config iteration operation.
config_context has a "struct key_value_info kvi" member that holds
metadata about the config source being read (e.g. what kind of config
source it is, the filename, etc). In this series, we're only interested
in .kvi, so we could have just used "struct key_value_info" as an arg,
but config_context makes it possible to add/adjust members in the future
without changing the config_fn_t signature. We could also consider other
ways of organizing the args (e.g. moving the config name and value into
config_context or key_value_info), but in my experiments, the
incremental benefit doesn't justify the added complexity (e.g. a
config_fn_t will sometimes invoke another config_fn_t but with a
different config value).
In subsequent commits, the .kvi member will replace the global "struct
config_reader" in config.c, making config iteration a global-free
operation. It requires much more work for the machinery to provide
meaningful values of .kvi, so for now, merely change the signature and
call sites, pass NULL as a placeholder value, and don't rely on the arg
in any meaningful way.
Most of the changes are performed by
contrib/coccinelle/config_fn_ctx.pending.cocci, which, for every
config_fn_t:
- Modifies the signature to accept "const struct config_context *ctx"
- Passes "ctx" to any inner config_fn_t, if needed
- Adds UNUSED attributes to "ctx", if needed
Most config_fn_t instances are easily identified by seeing if they are
called by the various config functions. Most of the remaining ones are
manually named in the .cocci patch. Manual cleanups are still needed,
but the majority of it is trivial; it's either adjusting config_fn_t
that the .cocci patch didn't catch, or adding forward declarations of
"struct config_context ctx" to make the signatures make sense.
The non-trivial changes are in cases where we are invoking a config_fn_t
outside of config machinery, and we now need to decide what value of
"ctx" to pass. These cases are:
- trace2/tr2_cfg.c:tr2_cfg_set_fl()
This is indirectly called by git_config_set() so that the trace2
machinery can notice the new config values and update its settings
using the tr2 config parsing function, i.e. tr2_cfg_cb().
- builtin/checkout.c:checkout_main()
This calls git_xmerge_config() as a shorthand for parsing a CLI arg.
This might be worth refactoring away in the future, since
git_xmerge_config() can call git_default_config(), which can do much
more than just parsing.
Handle them by creating a KVI_INIT macro that initializes "struct
key_value_info" to a reasonable default, and use that to construct the
"ctx" arg.
Signed-off-by: Glen Choo <chooglen@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-06-29 03:26:22 +08:00
|
|
|
static int allowed_bare_repo_cb(const char *key, const char *value,
|
|
|
|
const struct config_context *ctx UNUSED,
|
|
|
|
void *d)
|
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-15 05:28:01 +08:00
|
|
|
{
|
|
|
|
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: notice more types of implicit bare repositories
Setting the safe.bareRepository configuration variable to explicit
stops git from using a bare repository, unless the repository is
explicitly specified, either by the "--git-dir=<path>" command line
option, or by exporting $GIT_DIR environment variable. This may be
a reasonable measure to safeguard users from accidentally straying
into a bare repository in unexpected places, but often gets in the
way of users who need valid accesses to the repository.
Earlier, 45bb9162 (setup: allow cwd=.git w/ bareRepository=explicit,
2024-01-20) loosened the rule such that being inside the ".git"
directory of a non-bare repository does not really count as
accessing a "bare" repository. The reason why such a loosening is
needed is because often hooks and third-party tools run from within
$GIT_DIR while working with a non-bare repository.
More importantly, the reason why this is safe is because a directory
whose contents look like that of a "bare" repository cannot be a
bare repository that came embedded within a checkout of a malicious
project, as long as its directory name is ".git", because ".git" is
not a name allowed for a directory in payload.
There are at least two other cases where tools have to work in a
bare-repository looking directory that is not an embedded bare
repository, and accesses to them are still not allowed by the recent
change.
- A secondary worktree (whose name is $name) has its $GIT_DIR
inside "worktrees/$name/" subdirectory of the $GIT_DIR of the
primary worktree of the same repository.
- A submodule worktree (whose name is $name) has its $GIT_DIR
inside "modules/$name/" subdirectory of the $GIT_DIR of its
superproject.
As long as the primary worktree or the superproject in these cases
are not bare, the pathname of these "looks like bare but not really"
directories will have "/.git/worktrees/" and "/.git/modules/" as a
substring in its leading part, and we can take advantage of the same
security guarantee allow git to work from these places.
Extend the earlier "in a directory called '.git' we are OK" logic
used for the primary worktree to also cover the secondary worktree's
and non-embedded submodule's $GIT_DIR, by moving the logic to a
helper function "is_implicit_bare_repo()". We deliberately exclude
secondary worktrees and submodules of a bare repository, as these
are exactly what safe.bareRepository=explicit setting is designed to
forbid accesses to without an explicit GIT_DIR/--git-dir=<path>
Helped-by: Kyle Lippincott <spectral@google.com>
Helped-by: Kyle Meyer <kyle@kyleam.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-03-10 07:27:09 +08:00
|
|
|
static int is_implicit_bare_repo(const char *path)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* what we found is a ".git" directory at the root of
|
|
|
|
* the working tree.
|
|
|
|
*/
|
|
|
|
if (ends_with_path_components(path, ".git"))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* we are inside $GIT_DIR of a secondary worktree of a
|
|
|
|
* non-bare repository.
|
|
|
|
*/
|
|
|
|
if (strstr(path, "/.git/worktrees/"))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* we are inside $GIT_DIR of a worktree of a non-embedded
|
|
|
|
* submodule, whose superproject is not a bare repository.
|
|
|
|
*/
|
|
|
|
if (strstr(path, "/.git/modules/"))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return 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 08:30:14 +08: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-14 04:10:42 +08: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 08:30:14 +08: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-14 04:10:42 +08:00
|
|
|
static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,
|
2017-03-14 04:11:22 +08:00
|
|
|
struct strbuf *gitdir,
|
2022-08-08 21:27:47 +08:00
|
|
|
struct strbuf *report,
|
2017-03-14 04:11:22 +08:00
|
|
|
int die_on_error)
|
2005-08-17 09:06:34 +08:00
|
|
|
{
|
2008-05-20 14:49:26 +08:00
|
|
|
const char *env_ceiling_dirs = getenv(CEILING_DIRECTORIES_ENVIRONMENT);
|
2012-10-29 00:16:24 +08: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-14 04:10:42 +08:00
|
|
|
const char *gitdirenv;
|
2019-08-25 06:10:44 +08:00
|
|
|
int ceil_offset = -1, min_offset = offset_1st_component(dir->buf);
|
2010-07-13 17:02:00 +08:00
|
|
|
dev_t current_device = 0;
|
|
|
|
int one_filesystem = 1;
|
2005-08-17 09:06:34 +08: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 08:30:14 +08:00
|
|
|
/*
|
|
|
|
* If GIT_DIR is set explicitly, we're not going
|
|
|
|
* to do any discovery, but we still do repository
|
|
|
|
* validation.
|
|
|
|
*/
|
2006-12-31 12:30:19 +08: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-14 04:10:42 +08:00
|
|
|
if (gitdirenv) {
|
|
|
|
strbuf_addstr(gitdir, gitdirenv);
|
|
|
|
return GIT_DIR_EXPLICIT;
|
|
|
|
}
|
2005-08-17 09:06:34 +08:00
|
|
|
|
2012-10-29 00:16:24 +08: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 17:09:24 +08:00
|
|
|
int empty_entry_found = 0;
|
|
|
|
|
2012-10-29 00:16:24 +08:00
|
|
|
string_list_split(&ceiling_dirs, env_ceiling_dirs, PATH_SEP, -1);
|
2012-10-29 00:16:26 +08: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 17:09:24 +08: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-14 04:10:42 +08:00
|
|
|
ceil_offset = longest_ancestor_length(dir->buf, &ceiling_dirs);
|
2012-10-29 00:16:24 +08: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-14 04:10:42 +08:00
|
|
|
if (ceil_offset < 0)
|
|
|
|
ceil_offset = min_offset - 2;
|
2005-08-17 09:06:34 +08:00
|
|
|
|
2019-08-25 06:10:45 +08:00
|
|
|
if (min_offset && min_offset == dir->len &&
|
|
|
|
!is_dir_sep(dir->buf[min_offset - 1])) {
|
|
|
|
strbuf_addch(dir, '/');
|
|
|
|
min_offset++;
|
|
|
|
}
|
|
|
|
|
2007-06-06 15:10:42 +08: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-14 04:10:42 +08:00
|
|
|
* Test in the following order (relative to the dir):
|
2008-02-21 06:13:13 +08: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 08:30:14 +08:00
|
|
|
* - .git/
|
|
|
|
* - ./ (bare)
|
2008-02-21 06:13:13 +08: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 08:30:14 +08:00
|
|
|
* - ../.git/
|
|
|
|
* - ../ (bare)
|
2017-12-07 16:59:49 +08: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 08:30:14 +08:00
|
|
|
* etc.
|
2007-06-06 15:10:42 +08:00
|
|
|
*/
|
2010-04-05 05:49:31 +08:00
|
|
|
one_filesystem = !git_env_bool("GIT_DISCOVERY_ACROSS_FILESYSTEM", 0);
|
2010-07-24 19:27:58 +08: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-14 04:10:42 +08: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 08:30:14 +08:00
|
|
|
for (;;) {
|
2017-03-14 04:11:22 +08:00
|
|
|
int offset = dir->len, error_code = 0;
|
2022-05-11 03:35:29 +08: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-14 04:10:42 +08:00
|
|
|
|
|
|
|
if (offset > min_offset)
|
|
|
|
strbuf_addch(dir, '/');
|
|
|
|
strbuf_addstr(dir, DEFAULT_GIT_DIR_ENVIRONMENT);
|
2017-03-14 04:11:22 +08: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-14 04:12:18 +08:00
|
|
|
/* NEEDSWORK: fail if .git is not file nor dir */
|
2022-05-11 03:35:29 +08:00
|
|
|
if (is_git_directory(dir->buf)) {
|
2017-03-14 04:11:22 +08:00
|
|
|
gitdirenv = DEFAULT_GIT_DIR_ENVIRONMENT;
|
2022-05-11 03:35:29 +08:00
|
|
|
gitdir_path = xstrdup(dir->buf);
|
|
|
|
}
|
2017-03-14 04:11:22 +08:00
|
|
|
} else if (error_code != READ_GITFILE_ERR_STAT_FAILED)
|
|
|
|
return GIT_DIR_INVALID_GITFILE;
|
2022-05-11 03:35:29 +08: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-14 04:10:42 +08:00
|
|
|
strbuf_setlen(dir, offset);
|
2010-11-26 23:32:38 +08:00
|
|
|
if (gitdirenv) {
|
2022-05-11 03:35:29 +08:00
|
|
|
enum discovery_result ret;
|
2022-08-08 21:27:46 +08:00
|
|
|
const char *gitdir_candidate =
|
|
|
|
gitdir_path ? gitdir_path : gitdirenv;
|
2022-05-11 03:35:29 +08:00
|
|
|
|
2022-08-08 21:27:46 +08:00
|
|
|
if (ensure_valid_ownership(gitfile, dir->buf,
|
2022-08-08 21:27:47 +08:00
|
|
|
gitdir_candidate, report)) {
|
2022-05-11 03:35:29 +08: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 23:32:38 +08: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-14 04:10:42 +08: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-02 01:30:37 +08:00
|
|
|
trace2_data_string("setup", NULL, "implicit-bare-repository", dir->buf);
|
2024-01-20 08:08:22 +08:00
|
|
|
if (get_allowed_bare_repo() == ALLOWED_BARE_REPO_EXPLICIT &&
|
setup: notice more types of implicit bare repositories
Setting the safe.bareRepository configuration variable to explicit
stops git from using a bare repository, unless the repository is
explicitly specified, either by the "--git-dir=<path>" command line
option, or by exporting $GIT_DIR environment variable. This may be
a reasonable measure to safeguard users from accidentally straying
into a bare repository in unexpected places, but often gets in the
way of users who need valid accesses to the repository.
Earlier, 45bb9162 (setup: allow cwd=.git w/ bareRepository=explicit,
2024-01-20) loosened the rule such that being inside the ".git"
directory of a non-bare repository does not really count as
accessing a "bare" repository. The reason why such a loosening is
needed is because often hooks and third-party tools run from within
$GIT_DIR while working with a non-bare repository.
More importantly, the reason why this is safe is because a directory
whose contents look like that of a "bare" repository cannot be a
bare repository that came embedded within a checkout of a malicious
project, as long as its directory name is ".git", because ".git" is
not a name allowed for a directory in payload.
There are at least two other cases where tools have to work in a
bare-repository looking directory that is not an embedded bare
repository, and accesses to them are still not allowed by the recent
change.
- A secondary worktree (whose name is $name) has its $GIT_DIR
inside "worktrees/$name/" subdirectory of the $GIT_DIR of the
primary worktree of the same repository.
- A submodule worktree (whose name is $name) has its $GIT_DIR
inside "modules/$name/" subdirectory of the $GIT_DIR of its
superproject.
As long as the primary worktree or the superproject in these cases
are not bare, the pathname of these "looks like bare but not really"
directories will have "/.git/worktrees/" and "/.git/modules/" as a
substring in its leading part, and we can take advantage of the same
security guarantee allow git to work from these places.
Extend the earlier "in a directory called '.git' we are OK" logic
used for the primary worktree to also cover the secondary worktree's
and non-embedded submodule's $GIT_DIR, by moving the logic to a
helper function "is_implicit_bare_repo()". We deliberately exclude
secondary worktrees and submodules of a bare repository, as these
are exactly what safe.bareRepository=explicit setting is designed to
forbid accesses to without an explicit GIT_DIR/--git-dir=<path>
Helped-by: Kyle Lippincott <spectral@google.com>
Helped-by: Kyle Meyer <kyle@kyleam.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-03-10 07:27:09 +08:00
|
|
|
!is_implicit_bare_repo(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-15 05:28:01 +08:00
|
|
|
return GIT_DIR_DISALLOWED_BARE;
|
2022-08-08 21:27:47 +08:00
|
|
|
if (!ensure_valid_ownership(NULL, NULL, dir->buf, report))
|
2022-03-02 19:23:04 +08: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-14 04:10:42 +08:00
|
|
|
strbuf_addstr(gitdir, ".");
|
|
|
|
return GIT_DIR_BARE;
|
2010-03-18 03:55:52 +08:00
|
|
|
}
|
2010-11-26 23:32:38 +08: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-14 04:10:42 +08:00
|
|
|
if (offset <= min_offset)
|
|
|
|
return GIT_DIR_HIT_CEILING;
|
2010-11-26 23:32:36 +08: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-14 04:10:42 +08:00
|
|
|
while (--offset > ceil_offset && !is_dir_sep(dir->buf[offset]))
|
2017-03-07 22:32:32 +08: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-14 04:10:42 +08: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 15:10:42 +08:00
|
|
|
}
|
2005-08-17 09:06:34 +08:00
|
|
|
}
|
2005-11-26 07:43:41 +08:00
|
|
|
|
setup: add discover_git_directory_reason()
There are many reasons why discovering a Git directory may fail. In
particular, 8959555cee7 (setup_git_directory(): add an owner check for
the top-level directory, 2022-03-02) added ownership checks as a
security precaution.
Callers attempting to set up a Git directory may want to inform the user
about the reason for the failure. For that, expose the enum
discovery_result from within setup.c and move it into cache.h where
discover_git_directory() is defined.
I initially wanted to change the return type of discover_git_directory()
to be this enum, but several callers rely upon the "zero means success".
The two problems with this are:
1. The zero value of the enum is actually GIT_DIR_NONE, so nonpositive
results are errors.
2. There are multiple successful states; positive results are
successful.
It is worth noting that GIT_DIR_NONE is not returned, so we remove this
option from the enum. We must be careful to keep the successful reasons
as positive values, so they are given explicit positive values.
Instead of updating all callers immediately, add a new method,
discover_git_directory_reason(), and convert discover_git_directory() to
be a thin shim on top of it.
One thing that is important to note is that discover_git_directory()
previously returned -1 on error, so let's continue that into the future.
There is only one caller (in scalar.c) that depends on that signedness
instead of a non-zero check, so clean that up, too.
Because there are extra checks that discover_git_directory_reason() does
after setup_git_directory_gently_1(), there are other modes that can be
returned for failure states. Add these modes to the enum, but be sure to
explicitly add them as BUG() states in the switch of
setup_git_directory_gently().
Signed-off-by: Derrick Stolee <derrickstolee@github.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-08-28 21:52:25 +08:00
|
|
|
enum discovery_result discover_git_directory_reason(struct strbuf *commondir,
|
|
|
|
struct strbuf *gitdir)
|
2017-03-14 04:10:45 +08:00
|
|
|
{
|
|
|
|
struct strbuf dir = STRBUF_INIT, err = STRBUF_INIT;
|
|
|
|
size_t gitdir_offset = gitdir->len, cwd_len;
|
2017-06-15 02:07:37 +08: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-03-01 04:36:28 +08:00
|
|
|
struct repository_format candidate = REPOSITORY_FORMAT_INIT;
|
setup: add discover_git_directory_reason()
There are many reasons why discovering a Git directory may fail. In
particular, 8959555cee7 (setup_git_directory(): add an owner check for
the top-level directory, 2022-03-02) added ownership checks as a
security precaution.
Callers attempting to set up a Git directory may want to inform the user
about the reason for the failure. For that, expose the enum
discovery_result from within setup.c and move it into cache.h where
discover_git_directory() is defined.
I initially wanted to change the return type of discover_git_directory()
to be this enum, but several callers rely upon the "zero means success".
The two problems with this are:
1. The zero value of the enum is actually GIT_DIR_NONE, so nonpositive
results are errors.
2. There are multiple successful states; positive results are
successful.
It is worth noting that GIT_DIR_NONE is not returned, so we remove this
option from the enum. We must be careful to keep the successful reasons
as positive values, so they are given explicit positive values.
Instead of updating all callers immediately, add a new method,
discover_git_directory_reason(), and convert discover_git_directory() to
be a thin shim on top of it.
One thing that is important to note is that discover_git_directory()
previously returned -1 on error, so let's continue that into the future.
There is only one caller (in scalar.c) that depends on that signedness
instead of a non-zero check, so clean that up, too.
Because there are extra checks that discover_git_directory_reason() does
after setup_git_directory_gently_1(), there are other modes that can be
returned for failure states. Add these modes to the enum, but be sure to
explicitly add them as BUG() states in the switch of
setup_git_directory_gently().
Signed-off-by: Derrick Stolee <derrickstolee@github.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-08-28 21:52:25 +08:00
|
|
|
enum discovery_result result;
|
2017-03-14 04:10:45 +08:00
|
|
|
|
|
|
|
if (strbuf_getcwd(&dir))
|
setup: add discover_git_directory_reason()
There are many reasons why discovering a Git directory may fail. In
particular, 8959555cee7 (setup_git_directory(): add an owner check for
the top-level directory, 2022-03-02) added ownership checks as a
security precaution.
Callers attempting to set up a Git directory may want to inform the user
about the reason for the failure. For that, expose the enum
discovery_result from within setup.c and move it into cache.h where
discover_git_directory() is defined.
I initially wanted to change the return type of discover_git_directory()
to be this enum, but several callers rely upon the "zero means success".
The two problems with this are:
1. The zero value of the enum is actually GIT_DIR_NONE, so nonpositive
results are errors.
2. There are multiple successful states; positive results are
successful.
It is worth noting that GIT_DIR_NONE is not returned, so we remove this
option from the enum. We must be careful to keep the successful reasons
as positive values, so they are given explicit positive values.
Instead of updating all callers immediately, add a new method,
discover_git_directory_reason(), and convert discover_git_directory() to
be a thin shim on top of it.
One thing that is important to note is that discover_git_directory()
previously returned -1 on error, so let's continue that into the future.
There is only one caller (in scalar.c) that depends on that signedness
instead of a non-zero check, so clean that up, too.
Because there are extra checks that discover_git_directory_reason() does
after setup_git_directory_gently_1(), there are other modes that can be
returned for failure states. Add these modes to the enum, but be sure to
explicitly add them as BUG() states in the switch of
setup_git_directory_gently().
Signed-off-by: Derrick Stolee <derrickstolee@github.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-08-28 21:52:25 +08:00
|
|
|
return GIT_DIR_CWD_FAILURE;
|
2017-03-14 04:10:45 +08:00
|
|
|
|
|
|
|
cwd_len = dir.len;
|
setup: add discover_git_directory_reason()
There are many reasons why discovering a Git directory may fail. In
particular, 8959555cee7 (setup_git_directory(): add an owner check for
the top-level directory, 2022-03-02) added ownership checks as a
security precaution.
Callers attempting to set up a Git directory may want to inform the user
about the reason for the failure. For that, expose the enum
discovery_result from within setup.c and move it into cache.h where
discover_git_directory() is defined.
I initially wanted to change the return type of discover_git_directory()
to be this enum, but several callers rely upon the "zero means success".
The two problems with this are:
1. The zero value of the enum is actually GIT_DIR_NONE, so nonpositive
results are errors.
2. There are multiple successful states; positive results are
successful.
It is worth noting that GIT_DIR_NONE is not returned, so we remove this
option from the enum. We must be careful to keep the successful reasons
as positive values, so they are given explicit positive values.
Instead of updating all callers immediately, add a new method,
discover_git_directory_reason(), and convert discover_git_directory() to
be a thin shim on top of it.
One thing that is important to note is that discover_git_directory()
previously returned -1 on error, so let's continue that into the future.
There is only one caller (in scalar.c) that depends on that signedness
instead of a non-zero check, so clean that up, too.
Because there are extra checks that discover_git_directory_reason() does
after setup_git_directory_gently_1(), there are other modes that can be
returned for failure states. Add these modes to the enum, but be sure to
explicitly add them as BUG() states in the switch of
setup_git_directory_gently().
Signed-off-by: Derrick Stolee <derrickstolee@github.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-08-28 21:52:25 +08:00
|
|
|
result = setup_git_directory_gently_1(&dir, gitdir, NULL, 0);
|
|
|
|
if (result <= 0) {
|
2017-03-14 04:10:45 +08:00
|
|
|
strbuf_release(&dir);
|
setup: add discover_git_directory_reason()
There are many reasons why discovering a Git directory may fail. In
particular, 8959555cee7 (setup_git_directory(): add an owner check for
the top-level directory, 2022-03-02) added ownership checks as a
security precaution.
Callers attempting to set up a Git directory may want to inform the user
about the reason for the failure. For that, expose the enum
discovery_result from within setup.c and move it into cache.h where
discover_git_directory() is defined.
I initially wanted to change the return type of discover_git_directory()
to be this enum, but several callers rely upon the "zero means success".
The two problems with this are:
1. The zero value of the enum is actually GIT_DIR_NONE, so nonpositive
results are errors.
2. There are multiple successful states; positive results are
successful.
It is worth noting that GIT_DIR_NONE is not returned, so we remove this
option from the enum. We must be careful to keep the successful reasons
as positive values, so they are given explicit positive values.
Instead of updating all callers immediately, add a new method,
discover_git_directory_reason(), and convert discover_git_directory() to
be a thin shim on top of it.
One thing that is important to note is that discover_git_directory()
previously returned -1 on error, so let's continue that into the future.
There is only one caller (in scalar.c) that depends on that signedness
instead of a non-zero check, so clean that up, too.
Because there are extra checks that discover_git_directory_reason() does
after setup_git_directory_gently_1(), there are other modes that can be
returned for failure states. Add these modes to the enum, but be sure to
explicitly add them as BUG() states in the switch of
setup_git_directory_gently().
Signed-off-by: Derrick Stolee <derrickstolee@github.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-08-28 21:52:25 +08:00
|
|
|
return result;
|
2017-03-14 04:10:45 +08: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-15 02:07:37 +08:00
|
|
|
get_common_dir(commondir, gitdir->buf + gitdir_offset);
|
|
|
|
|
2017-03-14 04:10:45 +08:00
|
|
|
strbuf_reset(&dir);
|
2017-06-15 02:07:37 +08:00
|
|
|
strbuf_addf(&dir, "%s/config", commondir->buf + commondir_offset);
|
2017-03-14 04:10:45 +08: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-15 02:07:37 +08:00
|
|
|
strbuf_setlen(commondir, commondir_offset);
|
2017-06-14 19:35:26 +08: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-03-01 04:36:28 +08:00
|
|
|
clear_repository_format(&candidate);
|
setup: add discover_git_directory_reason()
There are many reasons why discovering a Git directory may fail. In
particular, 8959555cee7 (setup_git_directory(): add an owner check for
the top-level directory, 2022-03-02) added ownership checks as a
security precaution.
Callers attempting to set up a Git directory may want to inform the user
about the reason for the failure. For that, expose the enum
discovery_result from within setup.c and move it into cache.h where
discover_git_directory() is defined.
I initially wanted to change the return type of discover_git_directory()
to be this enum, but several callers rely upon the "zero means success".
The two problems with this are:
1. The zero value of the enum is actually GIT_DIR_NONE, so nonpositive
results are errors.
2. There are multiple successful states; positive results are
successful.
It is worth noting that GIT_DIR_NONE is not returned, so we remove this
option from the enum. We must be careful to keep the successful reasons
as positive values, so they are given explicit positive values.
Instead of updating all callers immediately, add a new method,
discover_git_directory_reason(), and convert discover_git_directory() to
be a thin shim on top of it.
One thing that is important to note is that discover_git_directory()
previously returned -1 on error, so let's continue that into the future.
There is only one caller (in scalar.c) that depends on that signedness
instead of a non-zero check, so clean that up, too.
Because there are extra checks that discover_git_directory_reason() does
after setup_git_directory_gently_1(), there are other modes that can be
returned for failure states. Add these modes to the enum, but be sure to
explicitly add them as BUG() states in the switch of
setup_git_directory_gently().
Signed-off-by: Derrick Stolee <derrickstolee@github.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-08-28 21:52:25 +08:00
|
|
|
return GIT_DIR_INVALID_FORMAT;
|
2017-03-14 04:10:45 +08: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-03-01 04:36:28 +08:00
|
|
|
clear_repository_format(&candidate);
|
setup: add discover_git_directory_reason()
There are many reasons why discovering a Git directory may fail. In
particular, 8959555cee7 (setup_git_directory(): add an owner check for
the top-level directory, 2022-03-02) added ownership checks as a
security precaution.
Callers attempting to set up a Git directory may want to inform the user
about the reason for the failure. For that, expose the enum
discovery_result from within setup.c and move it into cache.h where
discover_git_directory() is defined.
I initially wanted to change the return type of discover_git_directory()
to be this enum, but several callers rely upon the "zero means success".
The two problems with this are:
1. The zero value of the enum is actually GIT_DIR_NONE, so nonpositive
results are errors.
2. There are multiple successful states; positive results are
successful.
It is worth noting that GIT_DIR_NONE is not returned, so we remove this
option from the enum. We must be careful to keep the successful reasons
as positive values, so they are given explicit positive values.
Instead of updating all callers immediately, add a new method,
discover_git_directory_reason(), and convert discover_git_directory() to
be a thin shim on top of it.
One thing that is important to note is that discover_git_directory()
previously returned -1 on error, so let's continue that into the future.
There is only one caller (in scalar.c) that depends on that signedness
instead of a non-zero check, so clean that up, too.
Because there are extra checks that discover_git_directory_reason() does
after setup_git_directory_gently_1(), there are other modes that can be
returned for failure states. Add these modes to the enum, but be sure to
explicitly add them as BUG() states in the switch of
setup_git_directory_gently().
Signed-off-by: Derrick Stolee <derrickstolee@github.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-08-28 21:52:25 +08:00
|
|
|
return result;
|
2017-03-14 04:10:45 +08:00
|
|
|
}
|
|
|
|
|
2010-08-06 10:46:33 +08: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-14 04:10:42 +08:00
|
|
|
static struct strbuf cwd = STRBUF_INIT;
|
2022-08-08 21:27:47 +08:00
|
|
|
struct strbuf dir = STRBUF_INIT, gitdir = STRBUF_INIT, report = STRBUF_INIT;
|
2018-12-28 07:36:29 +08: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-03-01 04:36:28 +08:00
|
|
|
struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
|
2010-08-06 10:46:33 +08: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-14 04:10:42 +08: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 21:27:47 +08: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-14 04:10:42 +08:00
|
|
|
case GIT_DIR_EXPLICIT:
|
2017-11-13 05:28:51 +08: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-14 04:10:42 +08:00
|
|
|
break;
|
|
|
|
case GIT_DIR_DISCOVERED:
|
|
|
|
if (dir.len < cwd.len && chdir(dir.buf))
|
2018-02-13 21:19:15 +08: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-14 04:10:42 +08:00
|
|
|
prefix = setup_discovered_git_dir(gitdir.buf, &cwd, dir.len,
|
2017-11-13 05:28:51 +08: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-14 04:10:42 +08:00
|
|
|
break;
|
|
|
|
case GIT_DIR_BARE:
|
|
|
|
if (dir.len < cwd.len && chdir(dir.buf))
|
2018-02-13 21:19:15 +08:00
|
|
|
die(_("cannot change to '%s'"), dir.buf);
|
2017-11-13 05:28:51 +08: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-14 04:10:42 +08:00
|
|
|
break;
|
|
|
|
case GIT_DIR_HIT_CEILING:
|
2018-12-28 07:36:29 +08: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-14 04:10:42 +08:00
|
|
|
break;
|
|
|
|
case GIT_DIR_HIT_MOUNT_POINT:
|
2018-12-28 07:36:29 +08: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 19:23:04 +08:00
|
|
|
case GIT_DIR_INVALID_OWNERSHIP:
|
|
|
|
if (!nongit_ok) {
|
|
|
|
struct strbuf quoted = STRBUF_INIT;
|
|
|
|
|
2022-08-08 21:27:47 +08:00
|
|
|
strbuf_complete(&report, '\n');
|
2022-03-02 19:23:04 +08:00
|
|
|
sq_quote_buf_pretty("ed, dir.buf);
|
2022-05-11 03:35:29 +08:00
|
|
|
die(_("detected dubious ownership in repository at '%s'\n"
|
2022-08-08 21:27:47 +08:00
|
|
|
"%s"
|
2022-03-02 19:23:04 +08:00
|
|
|
"To add an exception for this directory, call:\n"
|
|
|
|
"\n"
|
|
|
|
"\tgit config --global --add safe.directory %s"),
|
2022-08-08 21:27:47 +08:00
|
|
|
dir.buf, report.buf, quoted.buf);
|
2022-03-02 19:23:04 +08: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-15 05:28:01 +08: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;
|
setup: add discover_git_directory_reason()
There are many reasons why discovering a Git directory may fail. In
particular, 8959555cee7 (setup_git_directory(): add an owner check for
the top-level directory, 2022-03-02) added ownership checks as a
security precaution.
Callers attempting to set up a Git directory may want to inform the user
about the reason for the failure. For that, expose the enum
discovery_result from within setup.c and move it into cache.h where
discover_git_directory() is defined.
I initially wanted to change the return type of discover_git_directory()
to be this enum, but several callers rely upon the "zero means success".
The two problems with this are:
1. The zero value of the enum is actually GIT_DIR_NONE, so nonpositive
results are errors.
2. There are multiple successful states; positive results are
successful.
It is worth noting that GIT_DIR_NONE is not returned, so we remove this
option from the enum. We must be careful to keep the successful reasons
as positive values, so they are given explicit positive values.
Instead of updating all callers immediately, add a new method,
discover_git_directory_reason(), and convert discover_git_directory() to
be a thin shim on top of it.
One thing that is important to note is that discover_git_directory()
previously returned -1 on error, so let's continue that into the future.
There is only one caller (in scalar.c) that depends on that signedness
instead of a non-zero check, so clean that up, too.
Because there are extra checks that discover_git_directory_reason() does
after setup_git_directory_gently_1(), there are other modes that can be
returned for failure states. Add these modes to the enum, but be sure to
explicitly add them as BUG() states in the switch of
setup_git_directory_gently().
Signed-off-by: Derrick Stolee <derrickstolee@github.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-08-28 21:52:25 +08:00
|
|
|
case GIT_DIR_CWD_FAILURE:
|
|
|
|
case GIT_DIR_INVALID_FORMAT:
|
2018-12-28 07:36:29 +08:00
|
|
|
/*
|
|
|
|
* As a safeguard against setup_git_directory_gently_1 returning
|
setup: add discover_git_directory_reason()
There are many reasons why discovering a Git directory may fail. In
particular, 8959555cee7 (setup_git_directory(): add an owner check for
the top-level directory, 2022-03-02) added ownership checks as a
security precaution.
Callers attempting to set up a Git directory may want to inform the user
about the reason for the failure. For that, expose the enum
discovery_result from within setup.c and move it into cache.h where
discover_git_directory() is defined.
I initially wanted to change the return type of discover_git_directory()
to be this enum, but several callers rely upon the "zero means success".
The two problems with this are:
1. The zero value of the enum is actually GIT_DIR_NONE, so nonpositive
results are errors.
2. There are multiple successful states; positive results are
successful.
It is worth noting that GIT_DIR_NONE is not returned, so we remove this
option from the enum. We must be careful to keep the successful reasons
as positive values, so they are given explicit positive values.
Instead of updating all callers immediately, add a new method,
discover_git_directory_reason(), and convert discover_git_directory() to
be a thin shim on top of it.
One thing that is important to note is that discover_git_directory()
previously returned -1 on error, so let's continue that into the future.
There is only one caller (in scalar.c) that depends on that signedness
instead of a non-zero check, so clean that up, too.
Because there are extra checks that discover_git_directory_reason() does
after setup_git_directory_gently_1(), there are other modes that can be
returned for failure states. Add these modes to the enum, but be sure to
explicitly add them as BUG() states in the switch of
setup_git_directory_gently().
Signed-off-by: Derrick Stolee <derrickstolee@github.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-08-28 21:52:25 +08:00
|
|
|
* these values, fallthrough to BUG. Otherwise it is possible to
|
2018-12-28 07:36:29 +08:00
|
|
|
* 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-14 04:10:42 +08:00
|
|
|
default:
|
2022-06-09 21:55:20 +08: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-14 04:10:42 +08:00
|
|
|
}
|
|
|
|
|
2018-12-28 07:36:29 +08: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-05 01:14:14 +08:00
|
|
|
if (nongit_ok && *nongit_ok)
|
2018-12-28 07:36:29 +08:00
|
|
|
startup_info->have_repository = 0;
|
2021-04-05 01:14:14 +08:00
|
|
|
else
|
2018-12-28 07:36:29 +08: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-06 06:10:27 +08:00
|
|
|
|
2017-06-21 03:19:32 +08: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-28 07:36:29 +08:00
|
|
|
if (/* GIT_DIR_EXPLICIT, GIT_DIR_DISCOVERED, GIT_DIR_BARE */
|
|
|
|
startup_info->have_repository ||
|
|
|
|
/* GIT_DIR_EXPLICIT */
|
|
|
|
getenv(GIT_DIR_ENVIRONMENT)) {
|
2017-06-23 02:43:33 +08:00
|
|
|
if (!the_repository->gitdir) {
|
|
|
|
const char *gitdir = getenv(GIT_DIR_ENVIRONMENT);
|
|
|
|
if (!gitdir)
|
|
|
|
gitdir = DEFAULT_GIT_DIR_ENVIRONMENT;
|
2018-03-03 19:35:55 +08:00
|
|
|
setup_git_env(gitdir);
|
2017-06-23 02:43:33 +08:00
|
|
|
}
|
2021-06-18 01:13:22 +08:00
|
|
|
if (startup_info->have_repository) {
|
2017-11-13 05:28:53 +08:00
|
|
|
repo_set_hash_algo(the_repository, repo_fmt.hash_algo);
|
2023-10-02 10:40:08 +08:00
|
|
|
repo_set_compat_hash_algo(the_repository,
|
2023-10-02 10:40:25 +08:00
|
|
|
repo_fmt.compat_hash_algo);
|
2023-12-29 15:26:39 +08:00
|
|
|
repo_set_ref_storage_format(the_repository,
|
|
|
|
repo_fmt.ref_storage_format);
|
2023-05-26 09:33:00 +08:00
|
|
|
the_repository->repository_format_worktree_config =
|
|
|
|
repo_fmt.worktree_config;
|
2021-06-18 01:13:22 +08: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-23 02:43:33 +08:00
|
|
|
}
|
2021-04-05 01:14:14 +08: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 13:08:26 +08:00
|
|
|
setup_original_cwd();
|
2017-06-21 03:19:32 +08: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-14 04:10:42 +08:00
|
|
|
strbuf_release(&dir);
|
|
|
|
strbuf_release(&gitdir);
|
2022-08-08 21:27:47 +08: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-03-01 04:36:28 +08: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-14 04:10:42 +08:00
|
|
|
|
2010-08-06 10:46:33 +08:00
|
|
|
return prefix;
|
|
|
|
}
|
|
|
|
|
2006-06-10 14:09:49 +08:00
|
|
|
int git_config_perm(const char *var, const char *value)
|
|
|
|
{
|
2008-04-16 16:34:24 +08:00
|
|
|
int i;
|
|
|
|
char *endptr;
|
|
|
|
|
2022-05-03 00:50:37 +08:00
|
|
|
if (!value)
|
2008-04-16 16:34:24 +08: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-26 07:19:36 +08:00
|
|
|
* a chmod value to restrict to.
|
2008-04-16 16:34:24 +08: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 14:09:49 +08:00
|
|
|
}
|
2008-04-16 16:34:24 +08:00
|
|
|
|
|
|
|
/* A filemode value was given: 0xxx */
|
|
|
|
|
|
|
|
if ((i & 0600) != 0600)
|
2018-02-13 21:19:15 +08:00
|
|
|
die(_("problem with core.sharedRepository filemode value "
|
2008-04-16 16:34:24 +08:00
|
|
|
"(0%.3o).\nThe owner of files must always have "
|
2016-08-08 19:15:59 +08:00
|
|
|
"read and write permissions."), i);
|
2008-04-16 16:34:24 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Mask filemode value. Others can not get write permission.
|
|
|
|
* x flags for directories are handled separately.
|
|
|
|
*/
|
2009-03-26 07:19:36 +08:00
|
|
|
return -(i & 0666);
|
2006-06-10 14:09:49 +08:00
|
|
|
}
|
|
|
|
|
2020-02-23 04:17:37 +08:00
|
|
|
void check_repository_format(struct repository_format *fmt)
|
2005-11-26 07:59:09 +08: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-03-01 04:36:28 +08:00
|
|
|
struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
|
2020-02-23 04:17:37 +08:00
|
|
|
if (!fmt)
|
|
|
|
fmt = &repo_fmt;
|
2024-09-12 19:29:24 +08:00
|
|
|
check_repository_format_gently(repo_get_git_dir(the_repository), fmt, NULL);
|
2016-03-06 06:11:34 +08:00
|
|
|
startup_info->have_repository = 1;
|
2020-05-26 03:59:08 +08:00
|
|
|
repo_set_hash_algo(the_repository, fmt->hash_algo);
|
2023-10-02 10:40:25 +08:00
|
|
|
repo_set_compat_hash_algo(the_repository, fmt->compat_hash_algo);
|
2023-12-29 15:26:39 +08:00
|
|
|
repo_set_ref_storage_format(the_repository,
|
|
|
|
fmt->ref_storage_format);
|
2023-05-26 09:33:00 +08:00
|
|
|
the_repository->repository_format_worktree_config =
|
|
|
|
fmt->worktree_config;
|
2021-06-18 01:13:22 +08: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-03-01 04:36:28 +08:00
|
|
|
clear_repository_format(&repo_fmt);
|
2005-11-26 07:59:09 +08:00
|
|
|
}
|
|
|
|
|
2010-06-05 16:04:20 +08: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-26 07:43:41 +08:00
|
|
|
const char *setup_git_directory(void)
|
|
|
|
{
|
2010-11-26 23:32:39 +08:00
|
|
|
return setup_git_directory_gently(NULL);
|
2005-11-26 07:43:41 +08:00
|
|
|
}
|
2011-08-16 05:17:46 +08:00
|
|
|
|
2017-01-25 07:56:49 +08:00
|
|
|
const char *resolve_gitdir_gently(const char *suspect, int *return_error_code)
|
2011-08-16 05:17:46 +08:00
|
|
|
{
|
|
|
|
if (is_git_directory(suspect))
|
|
|
|
return suspect;
|
2017-01-25 07:56:49 +08:00
|
|
|
return read_gitfile_gently(suspect, return_error_code);
|
2011-08-16 05:17:46 +08:00
|
|
|
}
|
2013-07-16 17:27:36 +08:00
|
|
|
|
|
|
|
/* if any standard file descriptor is missing open it to /dev/null */
|
|
|
|
void sanitize_stdfds(void)
|
|
|
|
{
|
2021-09-10 05:45:29 +08:00
|
|
|
int fd = xopen("/dev/null", O_RDWR);
|
|
|
|
while (fd < 2)
|
|
|
|
fd = xdup(fd);
|
2013-07-16 17:27:36 +08:00
|
|
|
if (fd > 2)
|
|
|
|
close(fd);
|
|
|
|
}
|
2014-02-08 15:08:51 +08:00
|
|
|
|
|
|
|
int daemonize(void)
|
|
|
|
{
|
|
|
|
#ifdef NO_POSIX_GOODIES
|
|
|
|
errno = ENOSYS;
|
|
|
|
return -1;
|
|
|
|
#else
|
|
|
|
switch (fork()) {
|
|
|
|
case 0:
|
|
|
|
break;
|
|
|
|
case -1:
|
2018-02-13 21:19:15 +08:00
|
|
|
die_errno(_("fork failed"));
|
2014-02-08 15:08:51 +08:00
|
|
|
default:
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
if (setsid() == -1)
|
2018-02-13 21:19:15 +08:00
|
|
|
die_errno(_("setsid failed"));
|
2014-02-08 15:08:51 +08:00
|
|
|
close(0);
|
|
|
|
close(1);
|
|
|
|
close(2);
|
|
|
|
sanitize_stdfds();
|
|
|
|
return 0;
|
|
|
|
#endif
|
|
|
|
}
|
2023-05-16 14:33:44 +08:00
|
|
|
|
init.templateDir: consider this config setting protected
The ability to configuring the template directory is a delicate feature:
It allows defining hooks that will be run e.g. during a `git clone`
operation, such as the `post-checkout` hook.
As such, it is of utmost importance that Git would not allow that config
setting to be changed during a `git clone` by mistake, allowing an
attacker a chance for a Remote Code Execution, allowing attackers to run
arbitrary code on unsuspecting users' machines.
As a defense-in-depth measure, to prevent minor vulnerabilities in the
`git clone` code from ballooning into higher-serverity attack vectors,
let's make this a protected setting just like `safe.directory` and
friends, i.e. ignore any `init.templateDir` entries from any local
config.
Note: This does not change the behavior of any recursive clone (modulo
bugs), as the local repository config is not even supposed to be written
while cloning the superproject, except in one scenario: If a config
template is configured that sets the template directory. This might be
done because `git clone --recurse-submodules --template=<directory>`
does not pass that template directory on to the submodules'
initialization.
Another scenario where this commit changes behavior is where
repositories are _not_ cloned recursively, and then some (intentional,
benign) automation configures the template directory to be used before
initializing the submodules.
So the caveat is that this could theoretically break existing processes.
In both scenarios, there is a way out, though: configuring the template
directory via the environment variable `GIT_TEMPLATE_DIR`.
This change in behavior is a trade-off between security and
backwards-compatibility that is struck in favor of security.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2024-03-29 20:15:32 +08:00
|
|
|
struct template_dir_cb_data {
|
|
|
|
char *path;
|
|
|
|
int initialized;
|
|
|
|
};
|
|
|
|
|
2024-04-17 17:39:09 +08:00
|
|
|
static int template_dir_cb(const char *key, const char *value,
|
2024-08-17 16:25:16 +08:00
|
|
|
const struct config_context *ctx UNUSED, void *d)
|
init.templateDir: consider this config setting protected
The ability to configuring the template directory is a delicate feature:
It allows defining hooks that will be run e.g. during a `git clone`
operation, such as the `post-checkout` hook.
As such, it is of utmost importance that Git would not allow that config
setting to be changed during a `git clone` by mistake, allowing an
attacker a chance for a Remote Code Execution, allowing attackers to run
arbitrary code on unsuspecting users' machines.
As a defense-in-depth measure, to prevent minor vulnerabilities in the
`git clone` code from ballooning into higher-serverity attack vectors,
let's make this a protected setting just like `safe.directory` and
friends, i.e. ignore any `init.templateDir` entries from any local
config.
Note: This does not change the behavior of any recursive clone (modulo
bugs), as the local repository config is not even supposed to be written
while cloning the superproject, except in one scenario: If a config
template is configured that sets the template directory. This might be
done because `git clone --recurse-submodules --template=<directory>`
does not pass that template directory on to the submodules'
initialization.
Another scenario where this commit changes behavior is where
repositories are _not_ cloned recursively, and then some (intentional,
benign) automation configures the template directory to be used before
initializing the submodules.
So the caveat is that this could theoretically break existing processes.
In both scenarios, there is a way out, though: configuring the template
directory via the environment variable `GIT_TEMPLATE_DIR`.
This change in behavior is a trade-off between security and
backwards-compatibility that is struck in favor of security.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2024-03-29 20:15:32 +08:00
|
|
|
{
|
|
|
|
struct template_dir_cb_data *data = d;
|
|
|
|
|
|
|
|
if (strcmp(key, "init.templatedir"))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!value) {
|
|
|
|
data->path = NULL;
|
|
|
|
} else {
|
|
|
|
char *path = NULL;
|
|
|
|
|
|
|
|
FREE_AND_NULL(data->path);
|
2024-05-27 19:46:15 +08:00
|
|
|
if (!git_config_pathname(&path, key, value))
|
init.templateDir: consider this config setting protected
The ability to configuring the template directory is a delicate feature:
It allows defining hooks that will be run e.g. during a `git clone`
operation, such as the `post-checkout` hook.
As such, it is of utmost importance that Git would not allow that config
setting to be changed during a `git clone` by mistake, allowing an
attacker a chance for a Remote Code Execution, allowing attackers to run
arbitrary code on unsuspecting users' machines.
As a defense-in-depth measure, to prevent minor vulnerabilities in the
`git clone` code from ballooning into higher-serverity attack vectors,
let's make this a protected setting just like `safe.directory` and
friends, i.e. ignore any `init.templateDir` entries from any local
config.
Note: This does not change the behavior of any recursive clone (modulo
bugs), as the local repository config is not even supposed to be written
while cloning the superproject, except in one scenario: If a config
template is configured that sets the template directory. This might be
done because `git clone --recurse-submodules --template=<directory>`
does not pass that template directory on to the submodules'
initialization.
Another scenario where this commit changes behavior is where
repositories are _not_ cloned recursively, and then some (intentional,
benign) automation configures the template directory to be used before
initializing the submodules.
So the caveat is that this could theoretically break existing processes.
In both scenarios, there is a way out, though: configuring the template
directory via the environment variable `GIT_TEMPLATE_DIR`.
This change in behavior is a trade-off between security and
backwards-compatibility that is struck in favor of security.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2024-03-29 20:15:32 +08:00
|
|
|
data->path = path ? path : xstrdup(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2024-03-29 18:45:01 +08:00
|
|
|
const char *get_template_dir(const char *option_template)
|
|
|
|
{
|
|
|
|
const char *template_dir = option_template;
|
|
|
|
|
|
|
|
if (!template_dir)
|
|
|
|
template_dir = getenv(TEMPLATE_DIR_ENVIRONMENT);
|
|
|
|
if (!template_dir) {
|
init.templateDir: consider this config setting protected
The ability to configuring the template directory is a delicate feature:
It allows defining hooks that will be run e.g. during a `git clone`
operation, such as the `post-checkout` hook.
As such, it is of utmost importance that Git would not allow that config
setting to be changed during a `git clone` by mistake, allowing an
attacker a chance for a Remote Code Execution, allowing attackers to run
arbitrary code on unsuspecting users' machines.
As a defense-in-depth measure, to prevent minor vulnerabilities in the
`git clone` code from ballooning into higher-serverity attack vectors,
let's make this a protected setting just like `safe.directory` and
friends, i.e. ignore any `init.templateDir` entries from any local
config.
Note: This does not change the behavior of any recursive clone (modulo
bugs), as the local repository config is not even supposed to be written
while cloning the superproject, except in one scenario: If a config
template is configured that sets the template directory. This might be
done because `git clone --recurse-submodules --template=<directory>`
does not pass that template directory on to the submodules'
initialization.
Another scenario where this commit changes behavior is where
repositories are _not_ cloned recursively, and then some (intentional,
benign) automation configures the template directory to be used before
initializing the submodules.
So the caveat is that this could theoretically break existing processes.
In both scenarios, there is a way out, though: configuring the template
directory via the environment variable `GIT_TEMPLATE_DIR`.
This change in behavior is a trade-off between security and
backwards-compatibility that is struck in favor of security.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2024-03-29 20:15:32 +08:00
|
|
|
static struct template_dir_cb_data data;
|
2024-03-29 18:45:01 +08:00
|
|
|
|
init.templateDir: consider this config setting protected
The ability to configuring the template directory is a delicate feature:
It allows defining hooks that will be run e.g. during a `git clone`
operation, such as the `post-checkout` hook.
As such, it is of utmost importance that Git would not allow that config
setting to be changed during a `git clone` by mistake, allowing an
attacker a chance for a Remote Code Execution, allowing attackers to run
arbitrary code on unsuspecting users' machines.
As a defense-in-depth measure, to prevent minor vulnerabilities in the
`git clone` code from ballooning into higher-serverity attack vectors,
let's make this a protected setting just like `safe.directory` and
friends, i.e. ignore any `init.templateDir` entries from any local
config.
Note: This does not change the behavior of any recursive clone (modulo
bugs), as the local repository config is not even supposed to be written
while cloning the superproject, except in one scenario: If a config
template is configured that sets the template directory. This might be
done because `git clone --recurse-submodules --template=<directory>`
does not pass that template directory on to the submodules'
initialization.
Another scenario where this commit changes behavior is where
repositories are _not_ cloned recursively, and then some (intentional,
benign) automation configures the template directory to be used before
initializing the submodules.
So the caveat is that this could theoretically break existing processes.
In both scenarios, there is a way out, though: configuring the template
directory via the environment variable `GIT_TEMPLATE_DIR`.
This change in behavior is a trade-off between security and
backwards-compatibility that is struck in favor of security.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2024-03-29 20:15:32 +08:00
|
|
|
if (!data.initialized) {
|
|
|
|
git_protected_config(template_dir_cb, &data);
|
|
|
|
data.initialized = 1;
|
2024-03-29 18:45:01 +08:00
|
|
|
}
|
init.templateDir: consider this config setting protected
The ability to configuring the template directory is a delicate feature:
It allows defining hooks that will be run e.g. during a `git clone`
operation, such as the `post-checkout` hook.
As such, it is of utmost importance that Git would not allow that config
setting to be changed during a `git clone` by mistake, allowing an
attacker a chance for a Remote Code Execution, allowing attackers to run
arbitrary code on unsuspecting users' machines.
As a defense-in-depth measure, to prevent minor vulnerabilities in the
`git clone` code from ballooning into higher-serverity attack vectors,
let's make this a protected setting just like `safe.directory` and
friends, i.e. ignore any `init.templateDir` entries from any local
config.
Note: This does not change the behavior of any recursive clone (modulo
bugs), as the local repository config is not even supposed to be written
while cloning the superproject, except in one scenario: If a config
template is configured that sets the template directory. This might be
done because `git clone --recurse-submodules --template=<directory>`
does not pass that template directory on to the submodules'
initialization.
Another scenario where this commit changes behavior is where
repositories are _not_ cloned recursively, and then some (intentional,
benign) automation configures the template directory to be used before
initializing the submodules.
So the caveat is that this could theoretically break existing processes.
In both scenarios, there is a way out, though: configuring the template
directory via the environment variable `GIT_TEMPLATE_DIR`.
This change in behavior is a trade-off between security and
backwards-compatibility that is struck in favor of security.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2024-03-29 20:15:32 +08:00
|
|
|
template_dir = data.path;
|
2024-03-29 18:45:01 +08:00
|
|
|
}
|
|
|
|
if (!template_dir) {
|
|
|
|
static char *dir;
|
|
|
|
|
|
|
|
if (!dir)
|
|
|
|
dir = system_path(DEFAULT_GIT_TEMPLATE_DIR);
|
|
|
|
template_dir = dir;
|
|
|
|
}
|
|
|
|
return template_dir;
|
|
|
|
}
|
2024-04-17 17:39:09 +08:00
|
|
|
|
2023-05-16 14:33:44 +08:00
|
|
|
#ifdef NO_TRUSTABLE_FILEMODE
|
|
|
|
#define TEST_FILEMODE 0
|
|
|
|
#else
|
|
|
|
#define TEST_FILEMODE 1
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define GIT_DEFAULT_HASH_ENVIRONMENT "GIT_DEFAULT_HASH"
|
|
|
|
|
|
|
|
static void copy_templates_1(struct strbuf *path, struct strbuf *template_path,
|
|
|
|
DIR *dir)
|
|
|
|
{
|
|
|
|
size_t path_baselen = path->len;
|
|
|
|
size_t template_baselen = template_path->len;
|
|
|
|
struct dirent *de;
|
|
|
|
|
|
|
|
/* Note: if ".git/hooks" file exists in the repository being
|
|
|
|
* re-initialized, /etc/core-git/templates/hooks/update would
|
|
|
|
* cause "git init" to fail here. I think this is sane but
|
|
|
|
* it means that the set of templates we ship by default, along
|
|
|
|
* with the way the namespace under .git/ is organized, should
|
|
|
|
* be really carefully chosen.
|
|
|
|
*/
|
|
|
|
safe_create_dir(path->buf, 1);
|
|
|
|
while ((de = readdir(dir)) != NULL) {
|
|
|
|
struct stat st_git, st_template;
|
|
|
|
int exists = 0;
|
|
|
|
|
|
|
|
strbuf_setlen(path, path_baselen);
|
|
|
|
strbuf_setlen(template_path, template_baselen);
|
|
|
|
|
|
|
|
if (de->d_name[0] == '.')
|
|
|
|
continue;
|
|
|
|
strbuf_addstr(path, de->d_name);
|
|
|
|
strbuf_addstr(template_path, de->d_name);
|
|
|
|
if (lstat(path->buf, &st_git)) {
|
|
|
|
if (errno != ENOENT)
|
|
|
|
die_errno(_("cannot stat '%s'"), path->buf);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
exists = 1;
|
|
|
|
|
|
|
|
if (lstat(template_path->buf, &st_template))
|
|
|
|
die_errno(_("cannot stat template '%s'"), template_path->buf);
|
|
|
|
|
|
|
|
if (S_ISDIR(st_template.st_mode)) {
|
|
|
|
DIR *subdir = opendir(template_path->buf);
|
|
|
|
if (!subdir)
|
|
|
|
die_errno(_("cannot opendir '%s'"), template_path->buf);
|
|
|
|
strbuf_addch(path, '/');
|
|
|
|
strbuf_addch(template_path, '/');
|
|
|
|
copy_templates_1(path, template_path, subdir);
|
|
|
|
closedir(subdir);
|
|
|
|
}
|
|
|
|
else if (exists)
|
|
|
|
continue;
|
|
|
|
else if (S_ISLNK(st_template.st_mode)) {
|
|
|
|
struct strbuf lnk = STRBUF_INIT;
|
|
|
|
if (strbuf_readlink(&lnk, template_path->buf,
|
|
|
|
st_template.st_size) < 0)
|
|
|
|
die_errno(_("cannot readlink '%s'"), template_path->buf);
|
|
|
|
if (symlink(lnk.buf, path->buf))
|
|
|
|
die_errno(_("cannot symlink '%s' '%s'"),
|
|
|
|
lnk.buf, path->buf);
|
|
|
|
strbuf_release(&lnk);
|
|
|
|
}
|
|
|
|
else if (S_ISREG(st_template.st_mode)) {
|
|
|
|
if (copy_file(path->buf, template_path->buf, st_template.st_mode))
|
|
|
|
die_errno(_("cannot copy '%s' to '%s'"),
|
|
|
|
template_path->buf, path->buf);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
error(_("ignoring template %s"), template_path->buf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-17 17:39:09 +08:00
|
|
|
static void copy_templates(const char *option_template)
|
2023-05-16 14:33:44 +08:00
|
|
|
{
|
2024-04-17 17:39:09 +08:00
|
|
|
const char *template_dir = get_template_dir(option_template);
|
2023-05-16 14:33:44 +08:00
|
|
|
struct strbuf path = STRBUF_INIT;
|
|
|
|
struct strbuf template_path = STRBUF_INIT;
|
|
|
|
size_t template_len;
|
|
|
|
struct repository_format template_format = REPOSITORY_FORMAT_INIT;
|
|
|
|
struct strbuf err = STRBUF_INIT;
|
|
|
|
DIR *dir;
|
|
|
|
char *to_free = NULL;
|
|
|
|
|
2024-04-17 17:39:09 +08:00
|
|
|
if (!template_dir || !*template_dir)
|
2023-05-16 14:33:44 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
strbuf_addstr(&template_path, template_dir);
|
|
|
|
strbuf_complete(&template_path, '/');
|
|
|
|
template_len = template_path.len;
|
|
|
|
|
|
|
|
dir = opendir(template_path.buf);
|
|
|
|
if (!dir) {
|
|
|
|
warning(_("templates not found in %s"), template_dir);
|
|
|
|
goto free_return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Make sure that template is from the correct vintage */
|
|
|
|
strbuf_addstr(&template_path, "config");
|
|
|
|
read_repository_format(&template_format, template_path.buf);
|
|
|
|
strbuf_setlen(&template_path, template_len);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* No mention of version at all is OK, but anything else should be
|
|
|
|
* verified.
|
|
|
|
*/
|
|
|
|
if (template_format.version >= 0 &&
|
|
|
|
verify_repository_format(&template_format, &err) < 0) {
|
|
|
|
warning(_("not copying templates from '%s': %s"),
|
|
|
|
template_dir, err.buf);
|
|
|
|
strbuf_release(&err);
|
|
|
|
goto close_free_return;
|
|
|
|
}
|
|
|
|
|
2024-09-12 19:29:27 +08:00
|
|
|
strbuf_addstr(&path, repo_get_common_dir(the_repository));
|
2023-05-16 14:33:44 +08:00
|
|
|
strbuf_complete(&path, '/');
|
|
|
|
copy_templates_1(&path, &template_path, dir);
|
|
|
|
close_free_return:
|
|
|
|
closedir(dir);
|
|
|
|
free_return:
|
|
|
|
free(to_free);
|
|
|
|
strbuf_release(&path);
|
|
|
|
strbuf_release(&template_path);
|
|
|
|
clear_repository_format(&template_format);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the git_dir is not directly inside the working tree, then git will not
|
|
|
|
* find it by default, and we need to set the worktree explicitly.
|
|
|
|
*/
|
|
|
|
static int needs_work_tree_config(const char *git_dir, const char *work_tree)
|
|
|
|
{
|
|
|
|
if (!strcmp(work_tree, "/") && !strcmp(git_dir, "/.git"))
|
|
|
|
return 0;
|
|
|
|
if (skip_prefix(git_dir, work_tree, &git_dir) &&
|
|
|
|
!strcmp(git_dir, "/.git"))
|
|
|
|
return 0;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
setup: introduce "extensions.refStorage" extension
Introduce a new "extensions.refStorage" extension that allows us to
specify the ref storage format used by a repository. For now, the only
supported format is the "files" format, but this list will likely soon
be extended to also support the upcoming "reftable" format.
There have been discussions on the Git mailing list in the past around
how exactly this extension should look like. One alternative [1] that
was discussed was whether it would make sense to model the extension in
such a way that backends are arbitrarily stackable. This would allow for
a combined value of e.g. "loose,packed-refs" or "loose,reftable", which
indicates that new refs would be written via "loose" files backend and
compressed into "packed-refs" or "reftable" backends, respectively.
It is arguable though whether this flexibility and the complexity that
it brings with it is really required for now. It is not foreseeable that
there will be a proliferation of backends in the near-term future, and
the current set of existing formats and formats which are on the horizon
can easily be configured with the much simpler proposal where we have a
single value, only.
Furthermore, if we ever see that we indeed want to gain the ability to
arbitrarily stack the ref formats, then we can adapt the current
extension rather easily. Given that Git clients will refuse any unknown
value for the "extensions.refStorage" extension they would also know to
ignore a stacked "loose,packed-refs" in the future.
So let's stick with the easy proposal for the time being and wire up the
extension.
[1]: <pull.1408.git.1667846164.gitgitgadget@gmail.com>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-12-29 15:26:47 +08:00
|
|
|
void initialize_repository_version(int hash_algo,
|
2024-06-06 13:29:01 +08:00
|
|
|
enum ref_storage_format ref_storage_format,
|
setup: introduce "extensions.refStorage" extension
Introduce a new "extensions.refStorage" extension that allows us to
specify the ref storage format used by a repository. For now, the only
supported format is the "files" format, but this list will likely soon
be extended to also support the upcoming "reftable" format.
There have been discussions on the Git mailing list in the past around
how exactly this extension should look like. One alternative [1] that
was discussed was whether it would make sense to model the extension in
such a way that backends are arbitrarily stackable. This would allow for
a combined value of e.g. "loose,packed-refs" or "loose,reftable", which
indicates that new refs would be written via "loose" files backend and
compressed into "packed-refs" or "reftable" backends, respectively.
It is arguable though whether this flexibility and the complexity that
it brings with it is really required for now. It is not foreseeable that
there will be a proliferation of backends in the near-term future, and
the current set of existing formats and formats which are on the horizon
can easily be configured with the much simpler proposal where we have a
single value, only.
Furthermore, if we ever see that we indeed want to gain the ability to
arbitrarily stack the ref formats, then we can adapt the current
extension rather easily. Given that Git clients will refuse any unknown
value for the "extensions.refStorage" extension they would also know to
ignore a stacked "loose,packed-refs" in the future.
So let's stick with the easy proposal for the time being and wire up the
extension.
[1]: <pull.1408.git.1667846164.gitgitgadget@gmail.com>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-12-29 15:26:47 +08:00
|
|
|
int reinit)
|
2023-05-16 14:33:44 +08:00
|
|
|
{
|
|
|
|
char repo_version_string[10];
|
|
|
|
int repo_version = GIT_REPO_VERSION;
|
|
|
|
|
2024-02-27 22:27:44 +08:00
|
|
|
/*
|
|
|
|
* Note that we initialize the repository version to 1 when the ref
|
|
|
|
* storage format is unknown. This is on purpose so that we can add the
|
|
|
|
* correct object format to the config during git-clone(1). The format
|
|
|
|
* version will get adjusted by git-clone(1) once it has learned about
|
|
|
|
* the remote repository's format.
|
|
|
|
*/
|
setup: introduce "extensions.refStorage" extension
Introduce a new "extensions.refStorage" extension that allows us to
specify the ref storage format used by a repository. For now, the only
supported format is the "files" format, but this list will likely soon
be extended to also support the upcoming "reftable" format.
There have been discussions on the Git mailing list in the past around
how exactly this extension should look like. One alternative [1] that
was discussed was whether it would make sense to model the extension in
such a way that backends are arbitrarily stackable. This would allow for
a combined value of e.g. "loose,packed-refs" or "loose,reftable", which
indicates that new refs would be written via "loose" files backend and
compressed into "packed-refs" or "reftable" backends, respectively.
It is arguable though whether this flexibility and the complexity that
it brings with it is really required for now. It is not foreseeable that
there will be a proliferation of backends in the near-term future, and
the current set of existing formats and formats which are on the horizon
can easily be configured with the much simpler proposal where we have a
single value, only.
Furthermore, if we ever see that we indeed want to gain the ability to
arbitrarily stack the ref formats, then we can adapt the current
extension rather easily. Given that Git clients will refuse any unknown
value for the "extensions.refStorage" extension they would also know to
ignore a stacked "loose,packed-refs" in the future.
So let's stick with the easy proposal for the time being and wire up the
extension.
[1]: <pull.1408.git.1667846164.gitgitgadget@gmail.com>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-12-29 15:26:47 +08:00
|
|
|
if (hash_algo != GIT_HASH_SHA1 ||
|
|
|
|
ref_storage_format != REF_STORAGE_FORMAT_FILES)
|
2023-05-16 14:33:44 +08:00
|
|
|
repo_version = GIT_REPO_VERSION_READ;
|
|
|
|
|
|
|
|
/* This forces creation of new config file */
|
|
|
|
xsnprintf(repo_version_string, sizeof(repo_version_string),
|
|
|
|
"%d", repo_version);
|
|
|
|
git_config_set("core.repositoryformatversion", repo_version_string);
|
|
|
|
|
2024-02-27 22:27:44 +08:00
|
|
|
if (hash_algo != GIT_HASH_SHA1 && hash_algo != GIT_HASH_UNKNOWN)
|
2023-05-16 14:33:44 +08:00
|
|
|
git_config_set("extensions.objectformat",
|
|
|
|
hash_algos[hash_algo].name);
|
|
|
|
else if (reinit)
|
|
|
|
git_config_set_gently("extensions.objectformat", NULL);
|
setup: introduce "extensions.refStorage" extension
Introduce a new "extensions.refStorage" extension that allows us to
specify the ref storage format used by a repository. For now, the only
supported format is the "files" format, but this list will likely soon
be extended to also support the upcoming "reftable" format.
There have been discussions on the Git mailing list in the past around
how exactly this extension should look like. One alternative [1] that
was discussed was whether it would make sense to model the extension in
such a way that backends are arbitrarily stackable. This would allow for
a combined value of e.g. "loose,packed-refs" or "loose,reftable", which
indicates that new refs would be written via "loose" files backend and
compressed into "packed-refs" or "reftable" backends, respectively.
It is arguable though whether this flexibility and the complexity that
it brings with it is really required for now. It is not foreseeable that
there will be a proliferation of backends in the near-term future, and
the current set of existing formats and formats which are on the horizon
can easily be configured with the much simpler proposal where we have a
single value, only.
Furthermore, if we ever see that we indeed want to gain the ability to
arbitrarily stack the ref formats, then we can adapt the current
extension rather easily. Given that Git clients will refuse any unknown
value for the "extensions.refStorage" extension they would also know to
ignore a stacked "loose,packed-refs" in the future.
So let's stick with the easy proposal for the time being and wire up the
extension.
[1]: <pull.1408.git.1667846164.gitgitgadget@gmail.com>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-12-29 15:26:47 +08:00
|
|
|
|
|
|
|
if (ref_storage_format != REF_STORAGE_FORMAT_FILES)
|
|
|
|
git_config_set("extensions.refstorage",
|
|
|
|
ref_storage_format_to_name(ref_storage_format));
|
2024-06-06 13:28:57 +08:00
|
|
|
else if (reinit)
|
|
|
|
git_config_set_gently("extensions.refstorage", NULL);
|
2023-05-16 14:33:44 +08:00
|
|
|
}
|
|
|
|
|
2023-12-12 15:00:41 +08:00
|
|
|
static int is_reinit(void)
|
|
|
|
{
|
|
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
char junk[2];
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
git_path_buf(&buf, "HEAD");
|
|
|
|
ret = !access(buf.buf, R_OK) || readlink(buf.buf, junk, sizeof(junk) - 1) != -1;
|
|
|
|
strbuf_release(&buf);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2024-06-06 13:29:01 +08:00
|
|
|
void create_reference_database(enum ref_storage_format ref_storage_format,
|
2023-12-29 15:26:39 +08:00
|
|
|
const char *initial_branch, int quiet)
|
2023-12-12 15:00:41 +08:00
|
|
|
{
|
|
|
|
struct strbuf err = STRBUF_INIT;
|
2024-05-17 16:19:09 +08:00
|
|
|
char *to_free = NULL;
|
2023-12-12 15:00:41 +08:00
|
|
|
int reinit = is_reinit();
|
|
|
|
|
2023-12-29 15:26:39 +08:00
|
|
|
repo_set_ref_storage_format(the_repository, ref_storage_format);
|
2024-05-17 16:18:19 +08:00
|
|
|
if (ref_store_create_on_disk(get_main_ref_store(the_repository), 0, &err))
|
2023-12-12 15:00:41 +08:00
|
|
|
die("failed to set up refs db: %s", err.buf);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Point the HEAD symref to the initial branch with if HEAD does
|
|
|
|
* not yet exist.
|
|
|
|
*/
|
|
|
|
if (!reinit) {
|
|
|
|
char *ref;
|
|
|
|
|
|
|
|
if (!initial_branch)
|
2024-05-17 16:19:09 +08:00
|
|
|
initial_branch = to_free =
|
|
|
|
repo_default_branch_name(the_repository, quiet);
|
2023-12-12 15:00:41 +08:00
|
|
|
|
|
|
|
ref = xstrfmt("refs/heads/%s", initial_branch);
|
|
|
|
if (check_refname_format(ref, 0) < 0)
|
|
|
|
die(_("invalid initial branch name: '%s'"),
|
|
|
|
initial_branch);
|
|
|
|
|
2024-05-21 02:20:04 +08:00
|
|
|
if (refs_update_symref(get_main_ref_store(the_repository), "HEAD", ref, NULL) < 0)
|
2023-12-12 15:00:41 +08:00
|
|
|
exit(1);
|
|
|
|
free(ref);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (reinit && initial_branch)
|
|
|
|
warning(_("re-init: ignored --initial-branch=%s"),
|
|
|
|
initial_branch);
|
|
|
|
|
|
|
|
strbuf_release(&err);
|
2024-05-17 16:19:09 +08:00
|
|
|
free(to_free);
|
2023-12-12 15:00:41 +08:00
|
|
|
}
|
|
|
|
|
2023-05-16 14:33:44 +08:00
|
|
|
static int create_default_files(const char *template_path,
|
|
|
|
const char *original_git_dir,
|
|
|
|
const struct repository_format *fmt,
|
2023-12-12 15:00:46 +08:00
|
|
|
int init_shared_repository)
|
2023-05-16 14:33:44 +08:00
|
|
|
{
|
|
|
|
struct stat st1;
|
|
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
char *path;
|
|
|
|
int reinit;
|
|
|
|
int filemode;
|
|
|
|
const char *work_tree = get_git_work_tree();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* First copy the templates -- we might have the default
|
|
|
|
* config file there, in which case we would want to read
|
|
|
|
* from it after installing.
|
|
|
|
*
|
|
|
|
* Before reading that config, we also need to clear out any cached
|
|
|
|
* values (since we've just potentially changed what's available on
|
|
|
|
* disk).
|
|
|
|
*/
|
2024-04-17 17:39:09 +08:00
|
|
|
copy_templates(template_path);
|
2023-05-16 14:33:44 +08:00
|
|
|
git_config_clear();
|
|
|
|
reset_shared_repository();
|
|
|
|
git_config(git_default_config, NULL);
|
|
|
|
|
2023-12-12 15:00:41 +08:00
|
|
|
reinit = is_reinit();
|
|
|
|
|
2023-05-16 14:33:44 +08:00
|
|
|
/*
|
|
|
|
* We must make sure command-line options continue to override any
|
|
|
|
* values we might have just re-read from the config.
|
|
|
|
*/
|
|
|
|
if (init_shared_repository != -1)
|
|
|
|
set_shared_repository(init_shared_repository);
|
setup: remove unnecessary variable
The TODO comment suggested to heed core.bare from template config file
if no command line override given. And the prev_bare_repository
variable seems to have been placed for this sole purpose as it is not
used anywhere else.
However, it was clarified by Junio [1] that such values (including
core.bare) are ignored intentionally and does not make sense to
propagate them from template config to repository config. Also, the
directories for the worktree and repository are already created, and
therefore the bare/non-bare decision has already been made, by the
point we reach the codepath where the TODO comment is placed.
Therefore, prev_bare_repository does not have a usecase with/without
supporting core.bare from template. And the removal of
prev_bare_repository is safe as proved by the later part of the
comment:
"Unfortunately, the line above is equivalent to
is_bare_repository_cfg = !work_tree;
which ignores the config entirely even if no `--[no-]bare`
command line option was present.
To see why, note that before this function, there was this call:
prev_bare_repository = is_bare_repository()
expanding the right hand side:
= is_bare_repository_cfg && !get_git_work_tree()
= is_bare_repository_cfg && !work_tree
note that the last simplification above is valid because nothing
calls repo_init() or set_git_work_tree() between any of the
relevant calls in the code, and thus the !get_git_work_tree()
calls will return the same result each time. So, what we are
interested in computing is the right hand side of the line of
code just above this comment:
prev_bare_repository || !work_tree
= is_bare_repository_cfg && !work_tree || !work_tree
= !work_tree
because "A && !B || !B == !B" for all boolean values of A & B."
Therefore, remove the TODO comment and remove prev_bare_repository
variable. Also, update relevant testcases and remove one redundant
testcase.
[1]: https://lore.kernel.org/git/xmqqjzonpy9l.fsf@gitster.g/
Helped-by: Elijah Newren <newren@gmail.com>
Helped-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Ghanshyam Thakkar <shyamthakkar001@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-03-04 23:18:11 +08:00
|
|
|
|
|
|
|
is_bare_repository_cfg = !work_tree;
|
2023-05-16 14:33:44 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We would have created the above under user's umask -- under
|
|
|
|
* shared-repository settings, we would need to fix them up.
|
|
|
|
*/
|
|
|
|
if (get_shared_repository()) {
|
2024-09-12 19:29:24 +08:00
|
|
|
adjust_shared_perm(repo_get_git_dir(the_repository));
|
2023-05-16 14:33:44 +08:00
|
|
|
}
|
|
|
|
|
setup: introduce "extensions.refStorage" extension
Introduce a new "extensions.refStorage" extension that allows us to
specify the ref storage format used by a repository. For now, the only
supported format is the "files" format, but this list will likely soon
be extended to also support the upcoming "reftable" format.
There have been discussions on the Git mailing list in the past around
how exactly this extension should look like. One alternative [1] that
was discussed was whether it would make sense to model the extension in
such a way that backends are arbitrarily stackable. This would allow for
a combined value of e.g. "loose,packed-refs" or "loose,reftable", which
indicates that new refs would be written via "loose" files backend and
compressed into "packed-refs" or "reftable" backends, respectively.
It is arguable though whether this flexibility and the complexity that
it brings with it is really required for now. It is not foreseeable that
there will be a proliferation of backends in the near-term future, and
the current set of existing formats and formats which are on the horizon
can easily be configured with the much simpler proposal where we have a
single value, only.
Furthermore, if we ever see that we indeed want to gain the ability to
arbitrarily stack the ref formats, then we can adapt the current
extension rather easily. Given that Git clients will refuse any unknown
value for the "extensions.refStorage" extension they would also know to
ignore a stacked "loose,packed-refs" in the future.
So let's stick with the easy proposal for the time being and wire up the
extension.
[1]: <pull.1408.git.1667846164.gitgitgadget@gmail.com>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-12-29 15:26:47 +08:00
|
|
|
initialize_repository_version(fmt->hash_algo, fmt->ref_storage_format, 0);
|
2023-05-16 14:33:44 +08:00
|
|
|
|
|
|
|
/* Check filemode trustability */
|
|
|
|
path = git_path_buf(&buf, "config");
|
|
|
|
filemode = TEST_FILEMODE;
|
|
|
|
if (TEST_FILEMODE && !lstat(path, &st1)) {
|
|
|
|
struct stat st2;
|
|
|
|
filemode = (!chmod(path, st1.st_mode ^ S_IXUSR) &&
|
|
|
|
!lstat(path, &st2) &&
|
|
|
|
st1.st_mode != st2.st_mode &&
|
|
|
|
!chmod(path, st1.st_mode));
|
|
|
|
if (filemode && !reinit && (st1.st_mode & S_IXUSR))
|
|
|
|
filemode = 0;
|
|
|
|
}
|
|
|
|
git_config_set("core.filemode", filemode ? "true" : "false");
|
|
|
|
|
|
|
|
if (is_bare_repository())
|
|
|
|
git_config_set("core.bare", "true");
|
|
|
|
else {
|
|
|
|
git_config_set("core.bare", "false");
|
|
|
|
/* allow template config file to override the default */
|
|
|
|
if (log_all_ref_updates == LOG_REFS_UNSET)
|
|
|
|
git_config_set("core.logallrefupdates", "true");
|
|
|
|
if (needs_work_tree_config(original_git_dir, work_tree))
|
|
|
|
git_config_set("core.worktree", work_tree);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!reinit) {
|
|
|
|
/* Check if symlink is supported in the work tree */
|
|
|
|
path = git_path_buf(&buf, "tXXXXXX");
|
|
|
|
if (!close(xmkstemp(path)) &&
|
|
|
|
!unlink(path) &&
|
|
|
|
!symlink("testing", path) &&
|
|
|
|
!lstat(path, &st1) &&
|
|
|
|
S_ISLNK(st1.st_mode))
|
|
|
|
unlink(path); /* good */
|
|
|
|
else
|
|
|
|
git_config_set("core.symlinks", "false");
|
|
|
|
|
|
|
|
/* Check if the filesystem is case-insensitive */
|
|
|
|
path = git_path_buf(&buf, "CoNfIg");
|
|
|
|
if (!access(path, F_OK))
|
|
|
|
git_config_set("core.ignorecase", "true");
|
|
|
|
probe_utf8_pathname_composition();
|
|
|
|
}
|
|
|
|
|
|
|
|
strbuf_release(&buf);
|
|
|
|
return reinit;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void create_object_directory(void)
|
|
|
|
{
|
|
|
|
struct strbuf path = STRBUF_INIT;
|
|
|
|
size_t baselen;
|
|
|
|
|
|
|
|
strbuf_addstr(&path, get_object_directory());
|
|
|
|
baselen = path.len;
|
|
|
|
|
|
|
|
safe_create_dir(path.buf, 1);
|
|
|
|
|
|
|
|
strbuf_setlen(&path, baselen);
|
|
|
|
strbuf_addstr(&path, "/pack");
|
|
|
|
safe_create_dir(path.buf, 1);
|
|
|
|
|
|
|
|
strbuf_setlen(&path, baselen);
|
|
|
|
strbuf_addstr(&path, "/info");
|
|
|
|
safe_create_dir(path.buf, 1);
|
|
|
|
|
|
|
|
strbuf_release(&path);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void separate_git_dir(const char *git_dir, const char *git_link)
|
|
|
|
{
|
|
|
|
struct stat st;
|
|
|
|
|
|
|
|
if (!stat(git_link, &st)) {
|
|
|
|
const char *src;
|
|
|
|
|
|
|
|
if (S_ISREG(st.st_mode))
|
|
|
|
src = read_gitfile(git_link);
|
|
|
|
else if (S_ISDIR(st.st_mode))
|
|
|
|
src = git_link;
|
|
|
|
else
|
|
|
|
die(_("unable to handle file type %d"), (int)st.st_mode);
|
|
|
|
|
|
|
|
if (rename(src, git_dir))
|
|
|
|
die_errno(_("unable to move %s to %s"), src, git_dir);
|
|
|
|
repair_worktrees(NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
write_file(git_link, "gitdir: %s", git_dir);
|
|
|
|
}
|
|
|
|
|
2024-08-16 16:57:03 +08:00
|
|
|
struct default_format_config {
|
|
|
|
int hash;
|
2024-08-16 16:57:12 +08:00
|
|
|
enum ref_storage_format ref_format;
|
2024-08-16 16:57:03 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static int read_default_format_config(const char *key, const char *value,
|
|
|
|
const struct config_context *ctx UNUSED,
|
|
|
|
void *payload)
|
2023-05-16 14:33:44 +08:00
|
|
|
{
|
2024-08-16 16:57:03 +08:00
|
|
|
struct default_format_config *cfg = payload;
|
|
|
|
char *str = NULL;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!strcmp(key, "init.defaultobjectformat")) {
|
|
|
|
ret = git_config_string(&str, key, value);
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
cfg->hash = hash_algo_by_name(str);
|
|
|
|
if (cfg->hash == GIT_HASH_UNKNOWN)
|
|
|
|
warning(_("unknown hash algorithm '%s'"), str);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2024-08-16 16:57:12 +08:00
|
|
|
if (!strcmp(key, "init.defaultrefformat")) {
|
|
|
|
ret = git_config_string(&str, key, value);
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
cfg->ref_format = ref_storage_format_by_name(str);
|
|
|
|
if (cfg->ref_format == REF_STORAGE_FORMAT_UNKNOWN)
|
|
|
|
warning(_("unknown ref storage format '%s'"), str);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2024-08-16 16:57:03 +08:00
|
|
|
ret = 0;
|
|
|
|
out:
|
|
|
|
free(str);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2024-08-16 16:56:58 +08:00
|
|
|
static void repository_format_configure(struct repository_format *repo_fmt,
|
|
|
|
int hash, enum ref_storage_format ref_format)
|
2023-05-16 14:33:44 +08:00
|
|
|
{
|
2024-08-16 16:57:03 +08:00
|
|
|
struct default_format_config cfg = {
|
|
|
|
.hash = GIT_HASH_UNKNOWN,
|
2024-08-16 16:57:12 +08:00
|
|
|
.ref_format = REF_STORAGE_FORMAT_UNKNOWN,
|
2024-08-16 16:57:03 +08:00
|
|
|
};
|
|
|
|
struct config_options opts = {
|
|
|
|
.respect_includes = 1,
|
|
|
|
.ignore_repo = 1,
|
|
|
|
.ignore_worktree = 1,
|
|
|
|
};
|
2024-08-16 16:56:58 +08:00
|
|
|
const char *env;
|
|
|
|
|
2024-08-16 16:57:03 +08:00
|
|
|
config_with_options(read_default_format_config, &cfg, NULL, NULL, &opts);
|
|
|
|
|
2023-05-16 14:33:44 +08:00
|
|
|
/*
|
|
|
|
* If we already have an initialized repo, don't allow the user to
|
|
|
|
* specify a different algorithm, as that could cause corruption.
|
|
|
|
* Otherwise, if the user has specified one on the command line, use it.
|
|
|
|
*/
|
2024-08-16 16:56:58 +08:00
|
|
|
env = getenv(GIT_DEFAULT_HASH_ENVIRONMENT);
|
2023-05-16 14:33:44 +08:00
|
|
|
if (repo_fmt->version >= 0 && hash != GIT_HASH_UNKNOWN && hash != repo_fmt->hash_algo)
|
|
|
|
die(_("attempt to reinitialize repository with different hash"));
|
|
|
|
else if (hash != GIT_HASH_UNKNOWN)
|
|
|
|
repo_fmt->hash_algo = hash;
|
|
|
|
else if (env) {
|
|
|
|
int env_algo = hash_algo_by_name(env);
|
|
|
|
if (env_algo == GIT_HASH_UNKNOWN)
|
|
|
|
die(_("unknown hash algorithm '%s'"), env);
|
|
|
|
repo_fmt->hash_algo = env_algo;
|
2024-08-16 16:57:03 +08:00
|
|
|
} else if (cfg.hash != GIT_HASH_UNKNOWN) {
|
|
|
|
repo_fmt->hash_algo = cfg.hash;
|
2023-05-16 14:33:44 +08:00
|
|
|
}
|
2024-08-16 16:56:58 +08:00
|
|
|
repo_set_hash_algo(the_repository, repo_fmt->hash_algo);
|
2023-12-29 15:26:52 +08:00
|
|
|
|
2024-08-16 16:56:58 +08:00
|
|
|
env = getenv("GIT_DEFAULT_REF_FORMAT");
|
2023-12-29 15:26:39 +08:00
|
|
|
if (repo_fmt->version >= 0 &&
|
2024-08-16 16:56:58 +08:00
|
|
|
ref_format != REF_STORAGE_FORMAT_UNKNOWN &&
|
|
|
|
ref_format != repo_fmt->ref_storage_format) {
|
2023-12-29 15:26:39 +08:00
|
|
|
die(_("attempt to reinitialize repository with different reference storage format"));
|
2024-08-16 16:56:58 +08:00
|
|
|
} else if (ref_format != REF_STORAGE_FORMAT_UNKNOWN) {
|
|
|
|
repo_fmt->ref_storage_format = ref_format;
|
|
|
|
} else if (env) {
|
|
|
|
ref_format = ref_storage_format_by_name(env);
|
|
|
|
if (ref_format == REF_STORAGE_FORMAT_UNKNOWN)
|
|
|
|
die(_("unknown ref storage format '%s'"), env);
|
|
|
|
repo_fmt->ref_storage_format = ref_format;
|
2024-08-16 16:57:12 +08:00
|
|
|
} else if (cfg.ref_format != REF_STORAGE_FORMAT_UNKNOWN) {
|
|
|
|
repo_fmt->ref_storage_format = cfg.ref_format;
|
2023-12-29 15:26:39 +08:00
|
|
|
}
|
2024-08-16 16:56:58 +08:00
|
|
|
repo_set_ref_storage_format(the_repository, repo_fmt->ref_storage_format);
|
2023-12-29 15:26:39 +08:00
|
|
|
}
|
|
|
|
|
2023-05-16 14:33:44 +08:00
|
|
|
int init_db(const char *git_dir, const char *real_git_dir,
|
2023-12-29 15:26:39 +08:00
|
|
|
const char *template_dir, int hash,
|
2024-06-06 13:29:01 +08:00
|
|
|
enum ref_storage_format ref_storage_format,
|
2023-12-29 15:26:39 +08:00
|
|
|
const char *initial_branch,
|
2023-05-16 14:33:44 +08:00
|
|
|
int init_shared_repository, unsigned int flags)
|
|
|
|
{
|
|
|
|
int reinit;
|
|
|
|
int exist_ok = flags & INIT_DB_EXIST_OK;
|
|
|
|
char *original_git_dir = real_pathdup(git_dir, 1);
|
|
|
|
struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
|
|
|
|
|
|
|
|
if (real_git_dir) {
|
|
|
|
struct stat st;
|
|
|
|
|
|
|
|
if (!exist_ok && !stat(git_dir, &st))
|
|
|
|
die(_("%s already exists"), git_dir);
|
|
|
|
|
|
|
|
if (!exist_ok && !stat(real_git_dir, &st))
|
|
|
|
die(_("%s already exists"), real_git_dir);
|
|
|
|
|
|
|
|
set_git_dir(real_git_dir, 1);
|
2024-09-12 19:29:24 +08:00
|
|
|
git_dir = repo_get_git_dir(the_repository);
|
2023-05-16 14:33:44 +08:00
|
|
|
separate_git_dir(git_dir, original_git_dir);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
set_git_dir(git_dir, 1);
|
2024-09-12 19:29:24 +08:00
|
|
|
git_dir = repo_get_git_dir(the_repository);
|
2023-05-16 14:33:44 +08:00
|
|
|
}
|
|
|
|
startup_info->have_repository = 1;
|
|
|
|
|
2024-08-16 16:56:58 +08:00
|
|
|
/*
|
|
|
|
* Check to see if the repository version is right.
|
2023-05-16 14:33:44 +08:00
|
|
|
* Note that a newly created repository does not have
|
|
|
|
* config file, so this will not fail. What we are catching
|
|
|
|
* is an attempt to reinitialize new repository with an old tool.
|
|
|
|
*/
|
|
|
|
check_repository_format(&repo_fmt);
|
|
|
|
|
2024-08-16 16:56:58 +08:00
|
|
|
repository_format_configure(&repo_fmt, hash, ref_storage_format);
|
2023-12-29 15:26:43 +08:00
|
|
|
|
2024-05-22 18:38:46 +08:00
|
|
|
/*
|
|
|
|
* Ensure `core.hidedotfiles` is processed. This must happen after we
|
|
|
|
* have set up the repository format such that we can evaluate
|
|
|
|
* includeIf conditions correctly in the case of re-initialization.
|
|
|
|
*/
|
|
|
|
git_config(platform_core_config, NULL);
|
|
|
|
|
|
|
|
safe_create_dir(git_dir, 0);
|
|
|
|
|
|
|
|
reinit = create_default_files(template_dir, original_git_dir,
|
2024-06-04 04:11:11 +08:00
|
|
|
&repo_fmt, init_shared_repository);
|
2024-05-22 18:38:46 +08:00
|
|
|
|
2023-12-12 15:00:46 +08:00
|
|
|
if (!(flags & INIT_DB_SKIP_REFDB))
|
2023-12-29 15:26:39 +08:00
|
|
|
create_reference_database(repo_fmt.ref_storage_format,
|
|
|
|
initial_branch, flags & INIT_DB_QUIET);
|
2023-05-16 14:33:44 +08:00
|
|
|
create_object_directory();
|
|
|
|
|
|
|
|
if (get_shared_repository()) {
|
|
|
|
char buf[10];
|
|
|
|
/* We do not spell "group" and such, so that
|
|
|
|
* the configuration can be read by older version
|
|
|
|
* of git. Note, we use octal numbers for new share modes,
|
|
|
|
* and compatibility values for PERM_GROUP and
|
|
|
|
* PERM_EVERYBODY.
|
|
|
|
*/
|
|
|
|
if (get_shared_repository() < 0)
|
|
|
|
/* force to the mode value */
|
|
|
|
xsnprintf(buf, sizeof(buf), "0%o", -get_shared_repository());
|
|
|
|
else if (get_shared_repository() == PERM_GROUP)
|
|
|
|
xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_GROUP);
|
|
|
|
else if (get_shared_repository() == PERM_EVERYBODY)
|
|
|
|
xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_EVERYBODY);
|
|
|
|
else
|
|
|
|
BUG("invalid value for shared_repository");
|
|
|
|
git_config_set("core.sharedrepository", buf);
|
|
|
|
git_config_set("receive.denyNonFastforwards", "true");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(flags & INIT_DB_QUIET)) {
|
|
|
|
int len = strlen(git_dir);
|
|
|
|
|
|
|
|
if (reinit)
|
|
|
|
printf(get_shared_repository()
|
|
|
|
? _("Reinitialized existing shared Git repository in %s%s\n")
|
|
|
|
: _("Reinitialized existing Git repository in %s%s\n"),
|
|
|
|
git_dir, len && git_dir[len-1] != '/' ? "/" : "");
|
|
|
|
else
|
|
|
|
printf(get_shared_repository()
|
|
|
|
? _("Initialized empty shared Git repository in %s%s\n")
|
|
|
|
: _("Initialized empty Git repository in %s%s\n"),
|
|
|
|
git_dir, len && git_dir[len-1] != '/' ? "/" : "");
|
|
|
|
}
|
|
|
|
|
2023-11-06 18:46:05 +08:00
|
|
|
clear_repository_format(&repo_fmt);
|
2023-05-16 14:33:44 +08:00
|
|
|
free(original_git_dir);
|
|
|
|
return 0;
|
|
|
|
}
|