@matheusbsbs / matheus-bagliano / commits / b57fb80a7d
Fork 0

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>

Nguyễn Thái Ngọc Duy committed Mar 19, 2011 at 22:16 UTC b57fb80a7d7d19102b31ab94a28ed43ea1ee07bb
7 files changed +157 -6
Documentation/git-clone.txt
+10
@@ -12,6 +12,7 @@ SYNOPSIS
12 'git clone' [--template=<template_directory>]
13 [-l] [-s] [--no-hardlinks] [-q] [-n] [--bare] [--mirror]
14 [-o <name>] [-b <name>] [-u <upload-pack>] [--reference <repository>]
15 + [--separate-git-dir|-L <git dir>]
16 [--depth <depth>] [--recursive|--recurse-submodules] [--] <repository>
17 [<directory>]
18
@@ -176,6 +177,15 @@ objects from the source repository into a pack in the cloned repository.
177 repository does not have a worktree/checkout (i.e. if any of
178 `--no-checkout`/`-n`, `--bare`, or `--mirror` is given)
179
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 +
189 <repository>::
190 The (possibly remote) repository to clone from. See the
191 <<URLS,URLS>> section below for more information on specifying
Documentation/git-init.txt
+15 -2
@@ -8,7 +8,9 @@ git-init - Create an empty git repository or reinitialize an existing one
8
9 SYNOPSIS
10 --------
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]
14
15
16 DESCRIPTION
@@ -29,7 +31,8 @@ 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
32 -rerunning 'git init' is to pick up newly added templates.
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
37 OPTIONS
38 -------
@@ -51,6 +54,16 @@ current working directory.
54 Specify the directory from which templates will be used. (See the "TEMPLATE
55 DIRECTORY" section below.)
56
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 +
67 --shared[=(false|true|umask|group|all|world|everybody|0xxx)]::
68
69 Specify that the git repository is to be shared amongst several users. This
builtin/clone.c
+7 -1
@@ -42,6 +42,7 @@ static int option_local, option_no_hardlinks, option_shared, option_recursive;
42 static char *option_template, *option_reference, *option_depth;
43 static char *option_origin = NULL;
44 static char *option_branch = NULL;
45 +static const char *real_git_dir;
46 static char *option_upload_pack = "git-upload-pack";
47 static int option_verbosity;
48 static int option_progress;
@@ -80,6 +81,8 @@ static struct option builtin_clone_options[] = {
81 "path to git-upload-pack on the remote"),
82 OPT_STRING(0, "depth", &option_depth, "depth",
83 "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"),
86
87 OPT_END()
88 };
@@ -466,7 +469,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
469
470 if (safe_create_leading_directories_const(git_dir) < 0)
471 die("could not create leading directories of '%s'", git_dir);
469 - set_git_dir(real_path(git_dir));
472 +
473 + set_git_dir_init(git_dir, real_git_dir, 0);
474 + if (real_git_dir)
475 + git_dir = real_git_dir;
476
477 if (0 <= option_verbosity)
478 printf("Cloning into %s%s...\n",
builtin/init-db.c
+65 -3
@@ -21,6 +21,7 @@
21 static int init_is_bare_repository = 0;
22 static int init_shared_repository = -1;
23 static const char *init_db_template_dir;
24 +static const char *git_link;
25
26 static void safe_create_dir(const char *dir, int share)
27 {
@@ -311,11 +312,67 @@ static void create_object_directory(void)
312 free(path);
313 }
314
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 +
367 int init_db(const char *template_dir, unsigned int flags)
368 {
369 int reinit;
370 + const char *git_dir = get_git_dir();
371
318 - safe_create_dir(get_git_dir(), 0);
372 + if (git_link)
373 + separate_git_dir(git_dir);
374 +
375 + safe_create_dir(git_dir, 0);
376
377 init_is_bare_repository = is_bare_repository();
378
@@ -352,7 +409,6 @@ int init_db(const char *template_dir, unsigned int flags)
409 }
410
411 if (!(flags & INIT_DB_QUIET)) {
355 - const char *git_dir = get_git_dir();
412 int len = strlen(git_dir);
413 printf("%s%s Git repository in %s%s\n",
414 reinit ? "Reinitialized existing" : "Initialized empty",
@@ -414,6 +470,7 @@ static const char *const init_db_usage[] = {
470 int cmd_init_db(int argc, const char **argv, const char *prefix)
471 {
472 const char *git_dir;
473 + const char *real_git_dir = NULL;
474 const char *work_tree;
475 const char *template_dir = NULL;
476 unsigned int flags = 0;
@@ -427,11 +484,16 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
484 "specify that the git repository is to be shared amongst several users",
485 PARSE_OPT_OPTARG | PARSE_OPT_NONEG, shared_callback, 0},
486 OPT_BIT('q', "quiet", &flags, "be quiet", INIT_DB_QUIET),
487 + OPT_STRING('L', "separate-git-dir", &real_git_dir, "gitdir",
488 + "separate git dir from working tree"),
489 OPT_END()
490 };
491
492 argc = parse_options(argc, argv, prefix, init_db_options, init_db_usage, 0);
493
494 + if (real_git_dir && !is_absolute_path(real_git_dir))
495 + real_git_dir = xstrdup(real_path(real_git_dir));
496 +
497 if (argc == 1) {
498 int mkdir_tried = 0;
499 retry:
@@ -522,7 +584,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
584 set_git_work_tree(real_path(work_tree));
585 }
586
525 - set_git_dir(real_path(git_dir));
587 + set_git_dir_init(git_dir, real_git_dir, 1);
588
589 return init_db(template_dir, flags);
590 }
cache.h
+1
@@ -436,6 +436,7 @@ extern void verify_non_filename(const char *prefix, const char *name);
436
437 #define INIT_DB_QUIET 0x0001
438
439 +extern int set_git_dir_init(const char *git_dir, const char *real_git_dir, int);
440 extern int init_db(const char *template_dir, unsigned int flags);
441
442 #define alloc_nr(x) (((x)+16)*3/2)
t/t0001-init.sh
+46
@@ -374,4 +374,50 @@ test_expect_success 'init prefers command line to GIT_DIR' '
374 ! test -d otherdir/refs
375 '
376
377 +test_expect_success 'init with separate gitdir' '
378 + rm -rf newdir &&
379 + git init --separate-git-dir realgitdir newdir &&
380 + echo "gitdir: `pwd`/realgitdir" >expected &&
381 + test_cmp expected newdir/.git &&
382 + test -d realgitdir/refs
383 +'
384 +
385 +test_expect_success 're-init to update git link' '
386 + (
387 + cd newdir &&
388 + git init --separate-git-dir ../surrealgitdir
389 + ) &&
390 + echo "gitdir: `pwd`/surrealgitdir" >expected &&
391 + test_cmp expected newdir/.git &&
392 + test -d surrealgitdir/refs &&
393 + ! test -d realgitdir/refs
394 +'
395 +
396 +test_expect_success 're-init to move gitdir' '
397 + rm -rf newdir realgitdir surrealgitdir &&
398 + git init newdir &&
399 + (
400 + cd newdir &&
401 + git init --separate-git-dir ../realgitdir
402 + ) &&
403 + echo "gitdir: `pwd`/realgitdir" >expected &&
404 + test_cmp expected newdir/.git &&
405 + test -d realgitdir/refs
406 +'
407 +
408 +test_expect_success 're-init to move gitdir symlink' '
409 + rm -rf newdir realgitdir &&
410 + git init newdir &&
411 + (
412 + cd newdir &&
413 + mv .git here &&
414 + ln -s here .git &&
415 + git init -L ../realgitdir
416 + ) &&
417 + echo "gitdir: `pwd`/realgitdir" >expected &&
418 + test_cmp expected newdir/.git &&
419 + test -d realgitdir/refs &&
420 + ! test -d newdir/here
421 +'
422 +
423 test_done
t/t5601-clone.sh
+13
@@ -192,4 +192,17 @@ test_expect_success 'do not respect url-encoding of non-url path' '
192 git clone x+y xy-regular
193 '
194
195 +test_expect_success 'clone separate gitdir' '
196 + rm -rf dst &&
197 + git clone --separate-git-dir realgitdir src dst &&
198 + echo "gitdir: `pwd`/realgitdir" >expected &&
199 + test_cmp expected dst/.git &&
200 + test -d realgitdir/refs
201 +'
202 +
203 +test_expect_success 'clone separate gitdir where target already exists' '
204 + rm -rf dst &&
205 + test_must_fail git clone --separate-git-dir realgitdir src dst
206 +'
207 +
208 test_done