Skip to content

Commit

Permalink
init, clone: support --separate-git-dir for .git file
Browse files Browse the repository at this point in the history
--separate-git-dir tells git to create git dir at the specified
location, instead of where it is supposed to be. A .git file that
points to that location will be put in place so that it appears normal
to repo discovery process.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
Nguyễn Thái Ngọc Duy authored and Junio C Hamano committed Mar 20, 2011
1 parent 9d379f4 commit b57fb80
Show file tree
Hide file tree
Showing 7 changed files with 157 additions and 6 deletions.
10 changes: 10 additions & 0 deletions Documentation/git-clone.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ SYNOPSIS
'git clone' [--template=<template_directory>]
[-l] [-s] [--no-hardlinks] [-q] [-n] [--bare] [--mirror]
[-o <name>] [-b <name>] [-u <upload-pack>] [--reference <repository>]
[--separate-git-dir|-L <git dir>]
[--depth <depth>] [--recursive|--recurse-submodules] [--] <repository>
[<directory>]

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

-L=<git dir>::
--separate-git-dir=<git dir>::
Instead of placing the cloned repository where it is supposed
to be, place the cloned repository at the specified directory,
then make a filesytem-agnostic git symbolic link to there.
The result is git repository can be separated from working
tree.


<repository>::
The (possibly remote) repository to clone from. See the
<<URLS,URLS>> section below for more information on specifying
Expand Down
17 changes: 15 additions & 2 deletions Documentation/git-init.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ git-init - Create an empty git repository or reinitialize an existing one

SYNOPSIS
--------
'git init' [-q | --quiet] [--bare] [--template=<template_directory>] [--shared[=<permissions>]] [directory]
'git init' [-q | --quiet] [--bare] [--template=<template_directory>]
[--separate-git-dir|-L <git dir>]
[--shared[=<permissions>]] [directory]


DESCRIPTION
Expand All @@ -29,7 +31,8 @@ directory is used.

Running 'git init' in an existing repository is safe. It will not
overwrite things that are already there. The primary reason for
rerunning 'git init' is to pick up newly added templates.
rerunning 'git init' is to pick up newly added templates (or to move
the repository to another place if --separate-git-dir is given).

OPTIONS
-------
Expand All @@ -51,6 +54,16 @@ current working directory.
Specify the directory from which templates will be used. (See the "TEMPLATE
DIRECTORY" section below.)

-L=<git dir>::
--separate-git-dir=<git dir>::

Instead of initializing the repository where it is supposed to be,
place a filesytem-agnostic git symbolic link there, pointing to the
specified git path, and initialize a git repository at the path. The
result is git repository can be separated from working tree. If this
is reinitialization, the repository will be moved to the specified
path.

--shared[=(false|true|umask|group|all|world|everybody|0xxx)]::

Specify that the git repository is to be shared amongst several users. This
Expand Down
8 changes: 7 additions & 1 deletion builtin/clone.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ static int option_local, option_no_hardlinks, option_shared, option_recursive;
static char *option_template, *option_reference, *option_depth;
static char *option_origin = NULL;
static char *option_branch = NULL;
static const char *real_git_dir;
static char *option_upload_pack = "git-upload-pack";
static int option_verbosity;
static int option_progress;
Expand Down Expand Up @@ -80,6 +81,8 @@ static struct option builtin_clone_options[] = {
"path to git-upload-pack on the remote"),
OPT_STRING(0, "depth", &option_depth, "depth",
"create a shallow clone of that depth"),
OPT_STRING('L', "separate-git-dir", &real_git_dir, "gitdir",
"separate git dir from working tree"),

OPT_END()
};
Expand Down Expand Up @@ -466,7 +469,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)

if (safe_create_leading_directories_const(git_dir) < 0)
die("could not create leading directories of '%s'", git_dir);
set_git_dir(real_path(git_dir));

set_git_dir_init(git_dir, real_git_dir, 0);
if (real_git_dir)
git_dir = real_git_dir;

if (0 <= option_verbosity)
printf("Cloning into %s%s...\n",
Expand Down
68 changes: 65 additions & 3 deletions builtin/init-db.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
static int init_is_bare_repository = 0;
static int init_shared_repository = -1;
static const char *init_db_template_dir;
static const char *git_link;

static void safe_create_dir(const char *dir, int share)
{
Expand Down Expand Up @@ -311,11 +312,67 @@ static void create_object_directory(void)
free(path);
}

int set_git_dir_init(const char *git_dir, const char *real_git_dir,
int exist_ok)
{
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);

/*
* make sure symlinks are resolved because we'll be
* moving the target repo later on in separate_git_dir()
*/
git_link = xstrdup(real_path(git_dir));
}
else {
real_git_dir = real_path(git_dir);
git_link = NULL;
}
set_git_dir(real_path(real_git_dir));
return 0;
}

static void separate_git_dir(const char *git_dir)
{
struct stat st;
FILE *fp;

if (!stat(git_link, &st)) {
const char *src;

if (S_ISREG(st.st_mode))
src = read_gitfile_gently(git_link);
else if (S_ISDIR(st.st_mode))
src = git_link;
else
die("unable to handle file type %d", st.st_mode);

if (rename(src, git_dir))
die_errno("unable to move %s to %s", src, git_dir);
}

fp = fopen(git_link, "w");
if (!fp)
die("Could not create git link %s", git_link);
fprintf(fp, "gitdir: %s\n", git_dir);
fclose(fp);
}

int init_db(const char *template_dir, unsigned int flags)
{
int reinit;
const char *git_dir = get_git_dir();

safe_create_dir(get_git_dir(), 0);
if (git_link)
separate_git_dir(git_dir);

safe_create_dir(git_dir, 0);

init_is_bare_repository = is_bare_repository();

Expand Down Expand Up @@ -352,7 +409,6 @@ int init_db(const char *template_dir, unsigned int flags)
}

if (!(flags & INIT_DB_QUIET)) {
const char *git_dir = get_git_dir();
int len = strlen(git_dir);
printf("%s%s Git repository in %s%s\n",
reinit ? "Reinitialized existing" : "Initialized empty",
Expand Down Expand Up @@ -414,6 +470,7 @@ static const char *const init_db_usage[] = {
int cmd_init_db(int argc, const char **argv, const char *prefix)
{
const char *git_dir;
const char *real_git_dir = NULL;
const char *work_tree;
const char *template_dir = NULL;
unsigned int flags = 0;
Expand All @@ -427,11 +484,16 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
"specify that the git repository is to be shared amongst several users",
PARSE_OPT_OPTARG | PARSE_OPT_NONEG, shared_callback, 0},
OPT_BIT('q', "quiet", &flags, "be quiet", INIT_DB_QUIET),
OPT_STRING('L', "separate-git-dir", &real_git_dir, "gitdir",
"separate git dir from working tree"),
OPT_END()
};

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

if (real_git_dir && !is_absolute_path(real_git_dir))
real_git_dir = xstrdup(real_path(real_git_dir));

if (argc == 1) {
int mkdir_tried = 0;
retry:
Expand Down Expand Up @@ -522,7 +584,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
set_git_work_tree(real_path(work_tree));
}

set_git_dir(real_path(git_dir));
set_git_dir_init(git_dir, real_git_dir, 1);

return init_db(template_dir, flags);
}
1 change: 1 addition & 0 deletions cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,7 @@ extern void verify_non_filename(const char *prefix, const char *name);

#define INIT_DB_QUIET 0x0001

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

#define alloc_nr(x) (((x)+16)*3/2)
Expand Down
46 changes: 46 additions & 0 deletions t/t0001-init.sh
Original file line number Diff line number Diff line change
Expand Up @@ -374,4 +374,50 @@ test_expect_success 'init prefers command line to GIT_DIR' '
! test -d otherdir/refs
'

test_expect_success 'init with separate gitdir' '
rm -rf newdir &&
git init --separate-git-dir realgitdir newdir &&
echo "gitdir: `pwd`/realgitdir" >expected &&
test_cmp expected newdir/.git &&
test -d realgitdir/refs
'

test_expect_success 're-init to update git link' '
(
cd newdir &&
git init --separate-git-dir ../surrealgitdir
) &&
echo "gitdir: `pwd`/surrealgitdir" >expected &&
test_cmp expected newdir/.git &&
test -d surrealgitdir/refs &&
! test -d realgitdir/refs
'

test_expect_success 're-init to move gitdir' '
rm -rf newdir realgitdir surrealgitdir &&
git init newdir &&
(
cd newdir &&
git init --separate-git-dir ../realgitdir
) &&
echo "gitdir: `pwd`/realgitdir" >expected &&
test_cmp expected newdir/.git &&
test -d realgitdir/refs
'

test_expect_success 're-init to move gitdir symlink' '
rm -rf newdir realgitdir &&
git init newdir &&
(
cd newdir &&
mv .git here &&
ln -s here .git &&
git init -L ../realgitdir
) &&
echo "gitdir: `pwd`/realgitdir" >expected &&
test_cmp expected newdir/.git &&
test -d realgitdir/refs &&
! test -d newdir/here
'

test_done
13 changes: 13 additions & 0 deletions t/t5601-clone.sh
Original file line number Diff line number Diff line change
Expand Up @@ -192,4 +192,17 @@ test_expect_success 'do not respect url-encoding of non-url path' '
git clone x+y xy-regular
'

test_expect_success 'clone separate gitdir' '
rm -rf dst &&
git clone --separate-git-dir realgitdir src dst &&
echo "gitdir: `pwd`/realgitdir" >expected &&
test_cmp expected dst/.git &&
test -d realgitdir/refs
'

test_expect_success 'clone separate gitdir where target already exists' '
rm -rf dst &&
test_must_fail git clone --separate-git-dir realgitdir src dst
'

test_done

0 comments on commit b57fb80

Please sign in to comment.