Skip to content

Commit e90fdc3

Browse files
dschogitster
authored andcommitted
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent d7ac12b commit e90fdc3

File tree

10 files changed

+216
-221
lines changed

10 files changed

+216
-221
lines changed

builtin-init-db.c

Lines changed: 11 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -174,36 +174,7 @@ static void copy_templates(const char *git_dir, int len, const char *template_di
174174
closedir(dir);
175175
}
176176

177-
/*
178-
* Get the full path to the working tree specified in $GIT_WORK_TREE
179-
* or NULL if no working tree is specified.
180-
*/
181-
static const char *get_work_tree(void)
182-
{
183-
const char *git_work_tree;
184-
char cwd[PATH_MAX];
185-
static char worktree[PATH_MAX];
186-
187-
git_work_tree = getenv(GIT_WORK_TREE_ENVIRONMENT);
188-
if (!git_work_tree)
189-
return NULL;
190-
if (!getcwd(cwd, sizeof(cwd)))
191-
die("Unable to read current working directory");
192-
if (chdir(git_work_tree))
193-
die("Cannot change directory to specified working tree '%s'",
194-
git_work_tree);
195-
if (git_work_tree[0] != '/') {
196-
if (!getcwd(worktree, sizeof(worktree)))
197-
die("Unable to read current working directory");
198-
git_work_tree = worktree;
199-
}
200-
if (chdir(cwd))
201-
die("Cannot come back to cwd");
202-
return git_work_tree;
203-
}
204-
205-
static int create_default_files(const char *git_dir, const char *git_work_tree,
206-
const char *template_path)
177+
static int create_default_files(const char *git_dir, const char *template_path)
207178
{
208179
unsigned len = strlen(git_dir);
209180
static char path[PATH_MAX];
@@ -282,16 +253,16 @@ static int create_default_files(const char *git_dir, const char *git_work_tree,
282253
}
283254
git_config_set("core.filemode", filemode ? "true" : "false");
284255

285-
if (is_bare_repository() && !git_work_tree) {
256+
if (is_bare_repository())
286257
git_config_set("core.bare", "true");
287-
}
288258
else {
259+
const char *work_tree = get_git_work_tree();
289260
git_config_set("core.bare", "false");
290261
/* allow template config file to override the default */
291262
if (log_all_ref_updates == -1)
292263
git_config_set("core.logallrefupdates", "true");
293-
if (git_work_tree)
294-
git_config_set("core.worktree", git_work_tree);
264+
if (work_tree != git_work_tree_cfg)
265+
git_config_set("core.worktree", work_tree);
295266
}
296267
return reinit;
297268
}
@@ -308,7 +279,6 @@ static const char init_db_usage[] =
308279
int cmd_init_db(int argc, const char **argv, const char *prefix)
309280
{
310281
const char *git_dir;
311-
const char *git_work_tree;
312282
const char *sha1_dir;
313283
const char *template_dir = NULL;
314284
char *path;
@@ -329,7 +299,11 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
329299
usage(init_db_usage);
330300
}
331301

332-
git_work_tree = get_work_tree();
302+
git_work_tree_cfg = xcalloc(PATH_MAX, 1);
303+
if (!getcwd(git_work_tree_cfg, PATH_MAX))
304+
die ("Cannot access current working directory.");
305+
if (access(get_git_work_tree(), X_OK))
306+
die ("Cannot access work tree '%s'", get_git_work_tree());
333307

334308
/*
335309
* Set up the default .git directory contents
@@ -346,7 +320,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
346320
*/
347321
check_repository_format();
348322

349-
reinit = create_default_files(git_dir, git_work_tree, template_dir);
323+
reinit = create_default_files(git_dir, template_dir);
350324

351325
/*
352326
* And set up the object store.

builtin-ls-files.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -469,9 +469,11 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
469469
break;
470470
}
471471

472-
if (require_work_tree &&
473-
(!is_inside_work_tree() || is_inside_git_dir()))
474-
die("This operation must be run in a work tree");
472+
if (require_work_tree && !is_inside_work_tree()) {
473+
const char *work_tree = get_git_work_tree();
474+
if (!work_tree || chdir(work_tree))
475+
die("This operation must be run in a work tree");
476+
}
475477

476478
pathspec = get_pathspec(prefix, argv + i);
477479

builtin-rev-parse.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,13 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
321321
}
322322
if (!strcmp(arg, "--show-cdup")) {
323323
const char *pfx = prefix;
324+
if (!is_inside_work_tree()) {
325+
const char *work_tree =
326+
get_git_work_tree();
327+
if (work_tree)
328+
printf("%s\n", work_tree);
329+
continue;
330+
}
324331
while (pfx) {
325332
pfx = strchr(pfx, '/');
326333
if (pfx) {

cache.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,13 +208,15 @@ enum object_type {
208208
extern int is_bare_repository_cfg;
209209
extern int is_bare_repository(void);
210210
extern int is_inside_git_dir(void);
211+
extern char *git_work_tree_cfg;
211212
extern int is_inside_work_tree(void);
212213
extern const char *get_git_dir(void);
213214
extern char *get_object_directory(void);
214215
extern char *get_refs_directory(void);
215216
extern char *get_index_file(void);
216217
extern char *get_graft_file(void);
217218
extern int set_git_dir(const char *path);
219+
extern const char *get_git_work_tree(void);
218220

219221
#define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
220222

environment.c

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ int pager_in_use;
3535
int pager_use_color = 1;
3636
int auto_crlf = 0; /* 1: both ways, -1: only when adding git objects */
3737

38+
/* This is set by setup_git_dir_gently() and/or git_default_config() */
39+
char *git_work_tree_cfg;
40+
static const char *work_tree;
41+
3842
static const char *git_dir;
3943
static char *git_object_dir, *git_index_file, *git_refs_dir, *git_graft_file;
4044

@@ -62,15 +66,8 @@ static void setup_git_env(void)
6266

6367
int is_bare_repository(void)
6468
{
65-
const char *dir, *s;
66-
if (0 <= is_bare_repository_cfg)
67-
return is_bare_repository_cfg;
68-
69-
dir = get_git_dir();
70-
if (!strcmp(dir, DEFAULT_GIT_DIR_ENVIRONMENT))
71-
return 0;
72-
s = strrchr(dir, '/');
73-
return !s || strcmp(s + 1, DEFAULT_GIT_DIR_ENVIRONMENT);
69+
/* if core.bare is not 'false', let's see if there is a work tree */
70+
return is_bare_repository_cfg && !get_git_work_tree();
7471
}
7572

7673
const char *get_git_dir(void)
@@ -80,6 +77,26 @@ const char *get_git_dir(void)
8077
return git_dir;
8178
}
8279

80+
const char *get_git_work_tree(void)
81+
{
82+
static int initialized = 0;
83+
if (!initialized) {
84+
work_tree = getenv(GIT_WORK_TREE_ENVIRONMENT);
85+
/* core.bare = true overrides implicit and config work tree */
86+
if (!work_tree && is_bare_repository_cfg < 1) {
87+
work_tree = git_work_tree_cfg;
88+
/* make_absolute_path also normalizes the path */
89+
if (work_tree && !is_absolute_path(work_tree))
90+
work_tree = xstrdup(make_absolute_path(git_path(work_tree)));
91+
} else if (work_tree)
92+
work_tree = xstrdup(make_absolute_path(work_tree));
93+
initialized = 1;
94+
if (work_tree)
95+
is_bare_repository_cfg = 0;
96+
}
97+
return work_tree;
98+
}
99+
83100
char *get_object_directory(void)
84101
{
85102
if (!git_object_dir)

git-sh-setup.sh

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,7 @@ cd_to_toplevel () {
5959
}
6060

6161
require_work_tree () {
62-
test $(git rev-parse --is-inside-work-tree) = true &&
63-
test $(git rev-parse --is-inside-git-dir) = false ||
62+
test $(git rev-parse --is-inside-work-tree) = true ||
6463
die "fatal: $0 cannot be used without a working tree."
6564
}
6665

git.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -272,9 +272,14 @@ static int run_command(struct cmd_struct *p, int argc, const char **argv)
272272
prefix = setup_git_directory();
273273
if (p->option & USE_PAGER)
274274
setup_pager();
275-
if ((p->option & NEED_WORK_TREE) &&
276-
(!is_inside_work_tree() || is_inside_git_dir()))
277-
die("%s must be run in a work tree", p->cmd);
275+
if (p->option & NEED_WORK_TREE) {
276+
const char *work_tree = get_git_work_tree();
277+
const char *git_dir = get_git_dir();
278+
if (!is_absolute_path(git_dir))
279+
set_git_dir(make_absolute_path(git_dir));
280+
if (!work_tree || chdir(work_tree))
281+
die("%s must be run in a work tree", p->cmd);
282+
}
278283
trace_argv_printf(argv, argc, "trace: built-in: git");
279284

280285
status = p->fn(argc, argv, prefix);

0 commit comments

Comments
 (0)