init, clone: support --separate-git-dir for .git file
--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>
This commit is contained in:
parent
9d379f4fd0
commit
b57fb80a7d
@ -12,6 +12,7 @@ SYNOPSIS
|
|||||||
'git clone' [--template=<template_directory>]
|
'git clone' [--template=<template_directory>]
|
||||||
[-l] [-s] [--no-hardlinks] [-q] [-n] [--bare] [--mirror]
|
[-l] [-s] [--no-hardlinks] [-q] [-n] [--bare] [--mirror]
|
||||||
[-o <name>] [-b <name>] [-u <upload-pack>] [--reference <repository>]
|
[-o <name>] [-b <name>] [-u <upload-pack>] [--reference <repository>]
|
||||||
|
[--separate-git-dir|-L <git dir>]
|
||||||
[--depth <depth>] [--recursive|--recurse-submodules] [--] <repository>
|
[--depth <depth>] [--recursive|--recurse-submodules] [--] <repository>
|
||||||
[<directory>]
|
[<directory>]
|
||||||
|
|
||||||
@ -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
|
repository does not have a worktree/checkout (i.e. if any of
|
||||||
`--no-checkout`/`-n`, `--bare`, or `--mirror` is given)
|
`--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>::
|
<repository>::
|
||||||
The (possibly remote) repository to clone from. See the
|
The (possibly remote) repository to clone from. See the
|
||||||
<<URLS,URLS>> section below for more information on specifying
|
<<URLS,URLS>> section below for more information on specifying
|
||||||
|
@ -8,7 +8,9 @@ git-init - Create an empty git repository or reinitialize an existing one
|
|||||||
|
|
||||||
SYNOPSIS
|
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
|
DESCRIPTION
|
||||||
@ -29,7 +31,8 @@ directory is used.
|
|||||||
|
|
||||||
Running 'git init' in an existing repository is safe. It will not
|
Running 'git init' in an existing repository is safe. It will not
|
||||||
overwrite things that are already there. The primary reason for
|
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
|
OPTIONS
|
||||||
-------
|
-------
|
||||||
@ -51,6 +54,16 @@ current working directory.
|
|||||||
Specify the directory from which templates will be used. (See the "TEMPLATE
|
Specify the directory from which templates will be used. (See the "TEMPLATE
|
||||||
DIRECTORY" section below.)
|
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)]::
|
--shared[=(false|true|umask|group|all|world|everybody|0xxx)]::
|
||||||
|
|
||||||
Specify that the git repository is to be shared amongst several users. This
|
Specify that the git repository is to be shared amongst several users. This
|
||||||
|
@ -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_template, *option_reference, *option_depth;
|
||||||
static char *option_origin = NULL;
|
static char *option_origin = NULL;
|
||||||
static char *option_branch = NULL;
|
static char *option_branch = NULL;
|
||||||
|
static const char *real_git_dir;
|
||||||
static char *option_upload_pack = "git-upload-pack";
|
static char *option_upload_pack = "git-upload-pack";
|
||||||
static int option_verbosity;
|
static int option_verbosity;
|
||||||
static int option_progress;
|
static int option_progress;
|
||||||
@ -80,6 +81,8 @@ static struct option builtin_clone_options[] = {
|
|||||||
"path to git-upload-pack on the remote"),
|
"path to git-upload-pack on the remote"),
|
||||||
OPT_STRING(0, "depth", &option_depth, "depth",
|
OPT_STRING(0, "depth", &option_depth, "depth",
|
||||||
"create a shallow clone of that 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()
|
OPT_END()
|
||||||
};
|
};
|
||||||
@ -466,7 +469,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
|
|||||||
|
|
||||||
if (safe_create_leading_directories_const(git_dir) < 0)
|
if (safe_create_leading_directories_const(git_dir) < 0)
|
||||||
die("could not create leading directories of '%s'", git_dir);
|
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)
|
if (0 <= option_verbosity)
|
||||||
printf("Cloning into %s%s...\n",
|
printf("Cloning into %s%s...\n",
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
static int init_is_bare_repository = 0;
|
static int init_is_bare_repository = 0;
|
||||||
static int init_shared_repository = -1;
|
static int init_shared_repository = -1;
|
||||||
static const char *init_db_template_dir;
|
static const char *init_db_template_dir;
|
||||||
|
static const char *git_link;
|
||||||
|
|
||||||
static void safe_create_dir(const char *dir, int share)
|
static void safe_create_dir(const char *dir, int share)
|
||||||
{
|
{
|
||||||
@ -311,11 +312,67 @@ static void create_object_directory(void)
|
|||||||
free(path);
|
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 init_db(const char *template_dir, unsigned int flags)
|
||||||
{
|
{
|
||||||
int reinit;
|
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();
|
init_is_bare_repository = is_bare_repository();
|
||||||
|
|
||||||
@ -352,7 +409,6 @@ int init_db(const char *template_dir, unsigned int flags)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!(flags & INIT_DB_QUIET)) {
|
if (!(flags & INIT_DB_QUIET)) {
|
||||||
const char *git_dir = get_git_dir();
|
|
||||||
int len = strlen(git_dir);
|
int len = strlen(git_dir);
|
||||||
printf("%s%s Git repository in %s%s\n",
|
printf("%s%s Git repository in %s%s\n",
|
||||||
reinit ? "Reinitialized existing" : "Initialized empty",
|
reinit ? "Reinitialized existing" : "Initialized empty",
|
||||||
@ -414,6 +470,7 @@ static const char *const init_db_usage[] = {
|
|||||||
int cmd_init_db(int argc, const char **argv, const char *prefix)
|
int cmd_init_db(int argc, const char **argv, const char *prefix)
|
||||||
{
|
{
|
||||||
const char *git_dir;
|
const char *git_dir;
|
||||||
|
const char *real_git_dir = NULL;
|
||||||
const char *work_tree;
|
const char *work_tree;
|
||||||
const char *template_dir = NULL;
|
const char *template_dir = NULL;
|
||||||
unsigned int flags = 0;
|
unsigned int flags = 0;
|
||||||
@ -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",
|
"specify that the git repository is to be shared amongst several users",
|
||||||
PARSE_OPT_OPTARG | PARSE_OPT_NONEG, shared_callback, 0},
|
PARSE_OPT_OPTARG | PARSE_OPT_NONEG, shared_callback, 0},
|
||||||
OPT_BIT('q', "quiet", &flags, "be quiet", INIT_DB_QUIET),
|
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()
|
OPT_END()
|
||||||
};
|
};
|
||||||
|
|
||||||
argc = parse_options(argc, argv, prefix, init_db_options, init_db_usage, 0);
|
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) {
|
if (argc == 1) {
|
||||||
int mkdir_tried = 0;
|
int mkdir_tried = 0;
|
||||||
retry:
|
retry:
|
||||||
@ -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_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);
|
return init_db(template_dir, flags);
|
||||||
}
|
}
|
||||||
|
1
cache.h
1
cache.h
@ -436,6 +436,7 @@ extern void verify_non_filename(const char *prefix, const char *name);
|
|||||||
|
|
||||||
#define INIT_DB_QUIET 0x0001
|
#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);
|
extern int init_db(const char *template_dir, unsigned int flags);
|
||||||
|
|
||||||
#define alloc_nr(x) (((x)+16)*3/2)
|
#define alloc_nr(x) (((x)+16)*3/2)
|
||||||
|
@ -374,4 +374,50 @@ test_expect_success 'init prefers command line to GIT_DIR' '
|
|||||||
! test -d otherdir/refs
|
! 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
|
test_done
|
||||||
|
@ -192,4 +192,17 @@ test_expect_success 'do not respect url-encoding of non-url path' '
|
|||||||
git clone x+y xy-regular
|
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
|
test_done
|
||||||
|
Loading…
x
Reference in New Issue
Block a user