Skip to content

Commit c4b2ce6

Browse files
committed
Merge branch 'nd/init-gitdir'
* nd/init-gitdir: init, clone: support --separate-git-dir for .git file git-init.txt: move description section up Conflicts: builtin/clone.c
2 parents ffc5e3c + b57fb80 commit c4b2ce6

File tree

7 files changed

+176
-31
lines changed

7 files changed

+176
-31
lines changed

Documentation/git-clone.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ SYNOPSIS
1212
'git clone' [--template=<template_directory>]
1313
[-l] [-s] [--no-hardlinks] [-q] [-n] [--bare] [--mirror]
1414
[-o <name>] [-b <name>] [-u <upload-pack>] [--reference <repository>]
15+
[--separate-git-dir|-L <git dir>]
1516
[--depth <depth>] [--recursive|--recurse-submodules] [--] <repository>
1617
[<directory>]
1718

@@ -176,6 +177,15 @@ objects from the source repository into a pack in the cloned repository.
176177
repository does not have a worktree/checkout (i.e. if any of
177178
`--no-checkout`/`-n`, `--bare`, or `--mirror` is given)
178179

180+
-L=<git dir>::
181+
--separate-git-dir=<git dir>::
182+
Instead of placing the cloned repository where it is supposed
183+
to be, place the cloned repository at the specified directory,
184+
then make a filesytem-agnostic git symbolic link to there.
185+
The result is git repository can be separated from working
186+
tree.
187+
188+
179189
<repository>::
180190
The (possibly remote) repository to clone from. See the
181191
<<URLS,URLS>> section below for more information on specifying

Documentation/git-init.txt

Lines changed: 34 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,32 @@ git-init - Create an empty git repository or reinitialize an existing one
88

99
SYNOPSIS
1010
--------
11-
'git init' [-q | --quiet] [--bare] [--template=<template_directory>] [--shared[=<permissions>]] [directory]
11+
'git init' [-q | --quiet] [--bare] [--template=<template_directory>]
12+
[--separate-git-dir|-L <git dir>]
13+
[--shared[=<permissions>]] [directory]
1214

1315

16+
DESCRIPTION
17+
-----------
18+
19+
This command creates an empty git repository - basically a `.git`
20+
directory with subdirectories for `objects`, `refs/heads`,
21+
`refs/tags`, and template files. An initial `HEAD` file that
22+
references the HEAD of the master branch is also created.
23+
24+
If the `$GIT_DIR` environment variable is set then it specifies a path
25+
to use instead of `./.git` for the base of the repository.
26+
27+
If the object storage directory is specified via the
28+
`$GIT_OBJECT_DIRECTORY` environment variable then the sha1 directories
29+
are created underneath - otherwise the default `$GIT_DIR/objects`
30+
directory is used.
31+
32+
Running 'git init' in an existing repository is safe. It will not
33+
overwrite things that are already there. The primary reason for
34+
rerunning 'git init' is to pick up newly added templates (or to move
35+
the repository to another place if --separate-git-dir is given).
36+
1437
OPTIONS
1538
-------
1639

@@ -31,6 +54,16 @@ current working directory.
3154
Specify the directory from which templates will be used. (See the "TEMPLATE
3255
DIRECTORY" section below.)
3356

57+
-L=<git dir>::
58+
--separate-git-dir=<git dir>::
59+
60+
Instead of initializing the repository where it is supposed to be,
61+
place a filesytem-agnostic git symbolic link there, pointing to the
62+
specified git path, and initialize a git repository at the path. The
63+
result is git repository can be separated from working tree. If this
64+
is reinitialization, the repository will be moved to the specified
65+
path.
66+
3467
--shared[=(false|true|umask|group|all|world|everybody|0xxx)]::
3568

3669
Specify that the git repository is to be shared amongst several users. This
@@ -74,32 +107,6 @@ line, the command is run inside the directory (possibly after creating it).
74107
--
75108

76109

77-
DESCRIPTION
78-
-----------
79-
This command creates an empty git repository - basically a `.git` directory
80-
with subdirectories for `objects`, `refs/heads`, `refs/tags`, and
81-
template files.
82-
An initial `HEAD` file that references the HEAD of the master branch
83-
is also created.
84-
85-
If the `$GIT_DIR` environment variable is set then it specifies a path
86-
to use instead of `./.git` for the base of the repository.
87-
88-
If the object storage directory is specified via the `$GIT_OBJECT_DIRECTORY`
89-
environment variable then the sha1 directories are created underneath -
90-
otherwise the default `$GIT_DIR/objects` directory is used.
91-
92-
Running 'git init' in an existing repository is safe. It will not overwrite
93-
things that are already there. The primary reason for rerunning 'git init'
94-
is to pick up newly added templates.
95-
96-
Note that 'git init' is the same as 'git init-db'. The command
97-
was primarily meant to initialize the object database, but over
98-
time it has become responsible for setting up the other aspects
99-
of the repository, such as installing the default hooks and
100-
setting the configuration variables. The old name is retained
101-
for backward compatibility reasons.
102-
103110
TEMPLATE DIRECTORY
104111
------------------
105112

builtin/clone.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ static int option_local, option_no_hardlinks, option_shared, option_recursive;
4242
static char *option_template, *option_reference, *option_depth;
4343
static char *option_origin = NULL;
4444
static char *option_branch = NULL;
45+
static const char *real_git_dir;
4546
static char *option_upload_pack = "git-upload-pack";
4647
static int option_verbosity;
4748
static int option_progress;
@@ -80,6 +81,8 @@ static struct option builtin_clone_options[] = {
8081
"path to git-upload-pack on the remote"),
8182
OPT_STRING(0, "depth", &option_depth, "depth",
8283
"create a shallow clone of that depth"),
84+
OPT_STRING('L', "separate-git-dir", &real_git_dir, "gitdir",
85+
"separate git dir from working tree"),
8386

8487
OPT_END()
8588
};
@@ -467,7 +470,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
467470

468471
if (safe_create_leading_directories_const(git_dir) < 0)
469472
die(_("could not create leading directories of '%s'"), git_dir);
470-
set_git_dir(real_path(git_dir));
473+
474+
set_git_dir_init(git_dir, real_git_dir, 0);
475+
if (real_git_dir)
476+
git_dir = real_git_dir;
471477

472478
if (0 <= option_verbosity) {
473479
if (option_bare)

builtin/init-db.c

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
static int init_is_bare_repository = 0;
2222
static int init_shared_repository = -1;
2323
static const char *init_db_template_dir;
24+
static const char *git_link;
2425

2526
static void safe_create_dir(const char *dir, int share)
2627
{
@@ -311,11 +312,67 @@ static void create_object_directory(void)
311312
free(path);
312313
}
313314

315+
int set_git_dir_init(const char *git_dir, const char *real_git_dir,
316+
int exist_ok)
317+
{
318+
if (real_git_dir) {
319+
struct stat st;
320+
321+
if (!exist_ok && !stat(git_dir, &st))
322+
die("%s already exists", git_dir);
323+
324+
if (!exist_ok && !stat(real_git_dir, &st))
325+
die("%s already exists", real_git_dir);
326+
327+
/*
328+
* make sure symlinks are resolved because we'll be
329+
* moving the target repo later on in separate_git_dir()
330+
*/
331+
git_link = xstrdup(real_path(git_dir));
332+
}
333+
else {
334+
real_git_dir = real_path(git_dir);
335+
git_link = NULL;
336+
}
337+
set_git_dir(real_path(real_git_dir));
338+
return 0;
339+
}
340+
341+
static void separate_git_dir(const char *git_dir)
342+
{
343+
struct stat st;
344+
FILE *fp;
345+
346+
if (!stat(git_link, &st)) {
347+
const char *src;
348+
349+
if (S_ISREG(st.st_mode))
350+
src = read_gitfile_gently(git_link);
351+
else if (S_ISDIR(st.st_mode))
352+
src = git_link;
353+
else
354+
die("unable to handle file type %d", st.st_mode);
355+
356+
if (rename(src, git_dir))
357+
die_errno("unable to move %s to %s", src, git_dir);
358+
}
359+
360+
fp = fopen(git_link, "w");
361+
if (!fp)
362+
die("Could not create git link %s", git_link);
363+
fprintf(fp, "gitdir: %s\n", git_dir);
364+
fclose(fp);
365+
}
366+
314367
int init_db(const char *template_dir, unsigned int flags)
315368
{
316369
int reinit;
370+
const char *git_dir = get_git_dir();
371+
372+
if (git_link)
373+
separate_git_dir(git_dir);
317374

318-
safe_create_dir(get_git_dir(), 0);
375+
safe_create_dir(git_dir, 0);
319376

320377
init_is_bare_repository = is_bare_repository();
321378

@@ -352,7 +409,6 @@ int init_db(const char *template_dir, unsigned int flags)
352409
}
353410

354411
if (!(flags & INIT_DB_QUIET)) {
355-
const char *git_dir = get_git_dir();
356412
int len = strlen(git_dir);
357413

358414
/*
@@ -420,6 +476,7 @@ static const char *const init_db_usage[] = {
420476
int cmd_init_db(int argc, const char **argv, const char *prefix)
421477
{
422478
const char *git_dir;
479+
const char *real_git_dir = NULL;
423480
const char *work_tree;
424481
const char *template_dir = NULL;
425482
unsigned int flags = 0;
@@ -433,11 +490,16 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
433490
"specify that the git repository is to be shared amongst several users",
434491
PARSE_OPT_OPTARG | PARSE_OPT_NONEG, shared_callback, 0},
435492
OPT_BIT('q', "quiet", &flags, "be quiet", INIT_DB_QUIET),
493+
OPT_STRING('L', "separate-git-dir", &real_git_dir, "gitdir",
494+
"separate git dir from working tree"),
436495
OPT_END()
437496
};
438497

439498
argc = parse_options(argc, argv, prefix, init_db_options, init_db_usage, 0);
440499

500+
if (real_git_dir && !is_absolute_path(real_git_dir))
501+
real_git_dir = xstrdup(real_path(real_git_dir));
502+
441503
if (argc == 1) {
442504
int mkdir_tried = 0;
443505
retry:
@@ -528,7 +590,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
528590
set_git_work_tree(real_path(work_tree));
529591
}
530592

531-
set_git_dir(real_path(git_dir));
593+
set_git_dir_init(git_dir, real_git_dir, 1);
532594

533595
return init_db(template_dir, flags);
534596
}

cache.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,7 @@ extern void verify_non_filename(const char *prefix, const char *name);
437437

438438
#define INIT_DB_QUIET 0x0001
439439

440+
extern int set_git_dir_init(const char *git_dir, const char *real_git_dir, int);
440441
extern int init_db(const char *template_dir, unsigned int flags);
441442

442443
#define alloc_nr(x) (((x)+16)*3/2)

t/t0001-init.sh

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,4 +371,50 @@ test_expect_success 'init prefers command line to GIT_DIR' '
371371
! test -d otherdir/refs
372372
'
373373

374+
test_expect_success 'init with separate gitdir' '
375+
rm -rf newdir &&
376+
git init --separate-git-dir realgitdir newdir &&
377+
echo "gitdir: `pwd`/realgitdir" >expected &&
378+
test_cmp expected newdir/.git &&
379+
test -d realgitdir/refs
380+
'
381+
382+
test_expect_success 're-init to update git link' '
383+
(
384+
cd newdir &&
385+
git init --separate-git-dir ../surrealgitdir
386+
) &&
387+
echo "gitdir: `pwd`/surrealgitdir" >expected &&
388+
test_cmp expected newdir/.git &&
389+
test -d surrealgitdir/refs &&
390+
! test -d realgitdir/refs
391+
'
392+
393+
test_expect_success 're-init to move gitdir' '
394+
rm -rf newdir realgitdir surrealgitdir &&
395+
git init newdir &&
396+
(
397+
cd newdir &&
398+
git init --separate-git-dir ../realgitdir
399+
) &&
400+
echo "gitdir: `pwd`/realgitdir" >expected &&
401+
test_cmp expected newdir/.git &&
402+
test -d realgitdir/refs
403+
'
404+
405+
test_expect_success 're-init to move gitdir symlink' '
406+
rm -rf newdir realgitdir &&
407+
git init newdir &&
408+
(
409+
cd newdir &&
410+
mv .git here &&
411+
ln -s here .git &&
412+
git init -L ../realgitdir
413+
) &&
414+
echo "gitdir: `pwd`/realgitdir" >expected &&
415+
test_cmp expected newdir/.git &&
416+
test -d realgitdir/refs &&
417+
! test -d newdir/here
418+
'
419+
374420
test_done

t/t5601-clone.sh

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,4 +191,17 @@ test_expect_success 'do not respect url-encoding of non-url path' '
191191
git clone x+y xy-regular
192192
'
193193

194+
test_expect_success 'clone separate gitdir' '
195+
rm -rf dst &&
196+
git clone --separate-git-dir realgitdir src dst &&
197+
echo "gitdir: `pwd`/realgitdir" >expected &&
198+
test_cmp expected dst/.git &&
199+
test -d realgitdir/refs
200+
'
201+
202+
test_expect_success 'clone separate gitdir where target already exists' '
203+
rm -rf dst &&
204+
test_must_fail git clone --separate-git-dir realgitdir src dst
205+
'
206+
194207
test_done

0 commit comments

Comments
 (0)