]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'nd/init-gitdir'
authorJunio C Hamano <gitster@pobox.com>
Sat, 2 Apr 2011 00:57:37 +0000 (17:57 -0700)
committerJunio C Hamano <gitster@pobox.com>
Sat, 2 Apr 2011 00:57:37 +0000 (17:57 -0700)
* nd/init-gitdir:
  init, clone: support --separate-git-dir for .git file
  git-init.txt: move description section up

Conflicts:
builtin/clone.c

1  2 
builtin/clone.c
builtin/init-db.c
cache.h
t/t0001-init.sh
t/t5601-clone.sh

diff --combined builtin/clone.c
index 0b5601a9b080e079ec077a8a765be24f2bf9bd29,097beca3ab4675be9fb324d6624be92b5330c816..4144bcf5ca320057b5ef1f5dac9c2df19aba250b
@@@ -8,7 -8,7 +8,7 @@@
   * Clone a repository into a different directory that does not yet exist.
   */
  
 -#include "cache.h"
 +#include "builtin.h"
  #include "parse-options.h"
  #include "fetch-pack.h"
  #include "refs.h"
@@@ -42,6 -42,7 +42,7 @@@ static int option_local, option_no_hard
  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;
@@@ -80,6 -81,8 +81,8 @@@ static struct option builtin_clone_opti
                   "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()
  };
@@@ -208,7 -211,7 +211,7 @@@ static void setup_reference(const char 
        if (is_directory(mkpath("%s/.git/objects", ref_git)))
                ref_git = mkpath("%s/.git", ref_git);
        else if (!is_directory(mkpath("%s/objects", ref_git)))
 -              die("reference repository '%s' is not a local directory.",
 +              die(_("reference repository '%s' is not a local directory."),
                    option_reference);
  
        ref_git_copy = xstrdup(ref_git);
@@@ -235,15 -238,15 +238,15 @@@ static void copy_or_link_directory(stru
  
        dir = opendir(src->buf);
        if (!dir)
 -              die_errno("failed to open '%s'", src->buf);
 +              die_errno(_("failed to open '%s'"), src->buf);
  
        if (mkdir(dest->buf, 0777)) {
                if (errno != EEXIST)
 -                      die_errno("failed to create directory '%s'", dest->buf);
 +                      die_errno(_("failed to create directory '%s'"), dest->buf);
                else if (stat(dest->buf, &buf))
 -                      die_errno("failed to stat '%s'", dest->buf);
 +                      die_errno(_("failed to stat '%s'"), dest->buf);
                else if (!S_ISDIR(buf.st_mode))
 -                      die("%s exists and is not a directory", dest->buf);
 +                      die(_("%s exists and is not a directory"), dest->buf);
        }
  
        strbuf_addch(src, '/');
                strbuf_setlen(dest, dest_len);
                strbuf_addstr(dest, de->d_name);
                if (stat(src->buf, &buf)) {
 -                      warning ("failed to stat %s\n", src->buf);
 +                      warning (_("failed to stat %s\n"), src->buf);
                        continue;
                }
                if (S_ISDIR(buf.st_mode)) {
                }
  
                if (unlink(dest->buf) && errno != ENOENT)
 -                      die_errno("failed to unlink '%s'", dest->buf);
 +                      die_errno(_("failed to unlink '%s'"), dest->buf);
                if (!option_no_hardlinks) {
                        if (!link(src->buf, dest->buf))
                                continue;
                        if (option_local)
 -                              die_errno("failed to create link '%s'", dest->buf);
 +                              die_errno(_("failed to create link '%s'"), dest->buf);
                        option_no_hardlinks = 1;
                }
                if (copy_file_with_time(dest->buf, src->buf, 0666))
 -                      die_errno("failed to copy file to '%s'", dest->buf);
 +                      die_errno(_("failed to copy file to '%s'"), dest->buf);
        }
        closedir(dir);
  }
@@@ -305,7 -308,7 +308,7 @@@ static const struct ref *clone_local(co
        ret = transport_get_remote_refs(transport);
        transport_disconnect(transport);
        if (0 <= option_verbosity)
 -              printf("done.\n");
 +              printf(_("done.\n"));
        return ret;
  }
  
@@@ -383,16 -386,15 +386,16 @@@ int cmd_clone(int argc, const char **ar
  
        junk_pid = getpid();
  
 +      packet_trace_identity("clone");
        argc = parse_options(argc, argv, prefix, builtin_clone_options,
                             builtin_clone_usage, 0);
  
        if (argc > 2)
 -              usage_msg_opt("Too many arguments.",
 +              usage_msg_opt(_("Too many arguments."),
                        builtin_clone_usage, builtin_clone_options);
  
        if (argc == 0)
 -              usage_msg_opt("You must specify a repository to clone.",
 +              usage_msg_opt(_("You must specify a repository to clone."),
                        builtin_clone_usage, builtin_clone_options);
  
        if (option_mirror)
  
        if (option_bare) {
                if (option_origin)
 -                      die("--bare and --origin %s options are incompatible.",
 +                      die(_("--bare and --origin %s options are incompatible."),
                            option_origin);
                option_no_checkout = 1;
        }
                repo = repo_name;
        is_local = path && !is_bundle;
        if (is_local && option_depth)
 -              warning("--depth is ignored in local clones; use file:// instead.");
 +              warning(_("--depth is ignored in local clones; use file:// instead."));
  
        if (argc == 2)
                dir = xstrdup(argv[1]);
  
        dest_exists = !stat(dir, &buf);
        if (dest_exists && !is_empty_dir(dir))
 -              die("destination path '%s' already exists and is not "
 -                      "an empty directory.", dir);
 +              die(_("destination path '%s' already exists and is not "
 +                      "an empty directory."), dir);
  
        strbuf_addf(&reflog_msg, "clone: from %s", repo);
  
        else {
                work_tree = getenv("GIT_WORK_TREE");
                if (work_tree && !stat(work_tree, &buf))
 -                      die("working tree '%s' already exists.", work_tree);
 +                      die(_("working tree '%s' already exists."), work_tree);
        }
  
        if (option_bare || work_tree)
        if (!option_bare) {
                junk_work_tree = work_tree;
                if (safe_create_leading_directories_const(work_tree) < 0)
 -                      die_errno("could not create leading directories of '%s'",
 +                      die_errno(_("could not create leading directories of '%s'"),
                                  work_tree);
                if (!dest_exists && mkdir(work_tree, 0755))
 -                      die_errno("could not create work tree dir '%s'.",
 +                      die_errno(_("could not create work tree dir '%s'."),
                                  work_tree);
                set_git_work_tree(work_tree);
        }
        setenv(CONFIG_ENVIRONMENT, mkpath("%s/config", git_dir), 1);
  
        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)
 -              printf("Cloning into %s%s...\n",
 -                     option_bare ? "bare repository " : "", dir);
 +      if (0 <= option_verbosity) {
 +              if (option_bare)
 +                      printf(_("Cloning into bare repository %s...\n"), dir);
 +              else
 +                      printf(_("Cloning into %s...\n"), dir);
 +      }
        init_db(option_template, INIT_DB_QUIET);
  
        /*
                transport = transport_get(remote, remote->url[0]);
  
                if (!transport->get_refs_list || !transport->fetch)
 -                      die("Don't know how to clone %s", transport->url);
 +                      die(_("Don't know how to clone %s"), transport->url);
  
                transport_set_option(transport, TRANS_OPT_KEEP, "yes");
  
                        strbuf_release(&head);
  
                        if (!our_head_points_at) {
 -                              warning("Remote branch %s not found in "
 -                                      "upstream %s, using HEAD instead",
 +                              warning(_("Remote branch %s not found in "
 +                                      "upstream %s, using HEAD instead"),
                                        option_branch, option_origin);
                                our_head_points_at = remote_head_points_at;
                        }
                        our_head_points_at = remote_head_points_at;
        }
        else {
 -              warning("You appear to have cloned an empty repository.");
 +              warning(_("You appear to have cloned an empty repository."));
                our_head_points_at = NULL;
                remote_head_points_at = NULL;
                remote_head = NULL;
        } else {
                /* Nothing to checkout out */
                if (!option_no_checkout)
 -                      warning("remote HEAD refers to nonexistent ref, "
 -                              "unable to checkout.\n");
 +                      warning(_("remote HEAD refers to nonexistent ref, "
 +                              "unable to checkout.\n"));
                option_no_checkout = 1;
        }
  
  
                if (write_cache(fd, active_cache, active_nr) ||
                    commit_locked_index(lock_file))
 -                      die("unable to write new index file");
 +                      die(_("unable to write new index file"));
  
                err |= run_hook(NULL, "post-checkout", sha1_to_hex(null_sha1),
                                sha1_to_hex(our_head_points_at->old_sha1), "1",
diff --combined builtin/init-db.c
index 6621e5671cbc4cbe5631a40f918aa4f0f05bb62e,887939923236e65eda305c3c633879bf7084dc3f..b7370d9bb8f235f1cf39b1ffa768498ca1e88654
@@@ -21,6 -21,7 +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)
  {
@@@ -31,7 -32,7 +32,7 @@@
                }
        }
        else if (share && adjust_shared_perm(dir))
 -              die("Could not make %s writable by group", dir);
 +              die(_("Could not make %s writable by group"), dir);
  }
  
  static void copy_templates_1(char *path, int baselen,
                namelen = strlen(de->d_name);
                if ((PATH_MAX <= baselen + namelen) ||
                    (PATH_MAX <= template_baselen + namelen))
 -                      die("insanely long template name %s", de->d_name);
 +                      die(_("insanely long template name %s"), de->d_name);
                memcpy(path + baselen, de->d_name, namelen+1);
                memcpy(template + template_baselen, de->d_name, namelen+1);
                if (lstat(path, &st_git)) {
                        if (errno != ENOENT)
 -                              die_errno("cannot stat '%s'", path);
 +                              die_errno(_("cannot stat '%s'"), path);
                }
                else
                        exists = 1;
  
                if (lstat(template, &st_template))
 -                      die_errno("cannot stat template '%s'", template);
 +                      die_errno(_("cannot stat template '%s'"), template);
  
                if (S_ISDIR(st_template.st_mode)) {
                        DIR *subdir = opendir(template);
                        int baselen_sub = baselen + namelen;
                        int template_baselen_sub = template_baselen + namelen;
                        if (!subdir)
 -                              die_errno("cannot opendir '%s'", template);
 +                              die_errno(_("cannot opendir '%s'"), template);
                        path[baselen_sub++] =
                                template[template_baselen_sub++] = '/';
                        path[baselen_sub] =
                        int len;
                        len = readlink(template, lnk, sizeof(lnk));
                        if (len < 0)
 -                              die_errno("cannot readlink '%s'", template);
 +                              die_errno(_("cannot readlink '%s'"), template);
                        if (sizeof(lnk) <= len)
 -                              die("insanely long symlink %s", template);
 +                              die(_("insanely long symlink %s"), template);
                        lnk[len] = 0;
                        if (symlink(lnk, path))
 -                              die_errno("cannot symlink '%s' '%s'", lnk, path);
 +                              die_errno(_("cannot symlink '%s' '%s'"), lnk, path);
                }
                else if (S_ISREG(st_template.st_mode)) {
                        if (copy_file(path, template, st_template.st_mode))
 -                              die_errno("cannot copy '%s' to '%s'", template,
 +                              die_errno(_("cannot copy '%s' to '%s'"), template,
                                          path);
                }
                else
 -                      error("ignoring template %s", template);
 +                      error(_("ignoring template %s"), template);
        }
  }
  
@@@ -129,7 -130,7 +130,7 @@@ static void copy_templates(const char *
                return;
        template_len = strlen(template_dir);
        if (PATH_MAX <= (template_len+strlen("/config")))
 -              die("insanely long template path %s", template_dir);
 +              die(_("insanely long template path %s"), template_dir);
        strcpy(template_path, template_dir);
        if (template_path[template_len-1] != '/') {
                template_path[template_len++] = '/';
        }
        dir = opendir(template_path);
        if (!dir) {
 -              warning("templates not found %s", template_dir);
 +              warning(_("templates not found %s"), template_dir);
                return;
        }
  
  
        if (repository_format_version &&
            repository_format_version != GIT_REPO_VERSION) {
 -              warning("not copying templates of "
 -                      "a wrong format version %d from '%s'",
 +              warning(_("not copying templates of "
 +                      "a wrong format version %d from '%s'"),
                        repository_format_version,
                        template_dir);
                closedir(dir);
@@@ -188,7 -189,7 +189,7 @@@ static int create_default_files(const c
        int filemode;
  
        if (len > sizeof(path)-50)
 -              die("insane git directory %s", git_dir);
 +              die(_("insane git directory %s"), git_dir);
        memcpy(path, git_dir, len);
  
        if (len && path[len-1] != '/')
@@@ -311,11 -312,67 +312,67 @@@ static void create_object_directory(voi
        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();
+       if (git_link)
+               separate_git_dir(git_dir);
  
-       safe_create_dir(get_git_dir(), 0);
+       safe_create_dir(git_dir, 0);
  
        init_is_bare_repository = is_bare_repository();
  
        }
  
        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",
 -                     shared_repository ? " shared" : "",
 +
 +              /*
 +               * TRANSLATORS: The first '%s' is either "Reinitialized
 +               * existing" or "Initialized empty", the second " shared" or
 +               * "", and the last '%s%s' is the verbatim directory name.
 +               */
 +              printf(_("%s%s Git repository in %s%s\n"),
 +                     reinit ? _("Reinitialized existing") : _("Initialized empty"),
 +                     shared_repository ? _(" shared") : "",
                       git_dir, len && git_dir[len-1] != '/' ? "/" : "");
        }
  
@@@ -381,7 -431,7 +437,7 @@@ static int guess_repository_type(const 
        if (!strcmp(".", git_dir))
                return 1;
        if (!getcwd(cwd, sizeof(cwd)))
 -              die_errno("cannot tell cwd");
 +              die_errno(_("cannot tell cwd"));
        if (!strcmp(git_dir, cwd))
                return 1;
        /*
@@@ -420,6 -470,7 +476,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;
                        "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:
                                        errno = EEXIST;
                                        /* fallthru */
                                case -1:
 -                                      die_errno("cannot mkdir %s", argv[0]);
 +                                      die_errno(_("cannot mkdir %s"), argv[0]);
                                        break;
                                default:
                                        break;
                                }
                                shared_repository = saved;
                                if (mkdir(argv[0], 0777) < 0)
 -                                      die_errno("cannot mkdir %s", argv[0]);
 +                                      die_errno(_("cannot mkdir %s"), argv[0]);
                                mkdir_tried = 1;
                                goto retry;
                        }
 -                      die_errno("cannot chdir to %s", argv[0]);
 +                      die_errno(_("cannot chdir to %s"), argv[0]);
                }
        } else if (0 < argc) {
                usage(init_db_usage[0]);
        git_dir = getenv(GIT_DIR_ENVIRONMENT);
        work_tree = getenv(GIT_WORK_TREE_ENVIRONMENT);
        if ((!git_dir || is_bare_repository_cfg == 1) && work_tree)
 -              die("%s (or --work-tree=<directory>) not allowed without "
 -                  "specifying %s (or --git-dir=<directory>)",
 +              die(_("%s (or --work-tree=<directory>) not allowed without "
 +                        "specifying %s (or --git-dir=<directory>)"),
                    GIT_WORK_TREE_ENVIRONMENT,
                    GIT_DIR_ENVIRONMENT);
  
                if (!git_work_tree_cfg) {
                        git_work_tree_cfg = xcalloc(PATH_MAX, 1);
                        if (!getcwd(git_work_tree_cfg, PATH_MAX))
 -                              die_errno ("Cannot access current working directory");
 +                              die_errno (_("Cannot access current working directory"));
                }
                if (work_tree)
                        set_git_work_tree(real_path(work_tree));
                else
                        set_git_work_tree(git_work_tree_cfg);
                if (access(get_git_work_tree(), X_OK))
 -                      die_errno ("Cannot access work tree '%s'",
 +                      die_errno (_("Cannot access work tree '%s'"),
                                   get_git_work_tree());
        }
        else {
                        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);
  }
diff --combined cache.h
index 9f06d215790ef98c2138ab26c1a9b4ddafd33c55,0b99487caffa5d663839329b3d168012cf1c7738..2674f4cf5a74aaa8e6137985d552d7b6123a432c
+++ b/cache.h
@@@ -5,7 -5,6 +5,7 @@@
  #include "strbuf.h"
  #include "hash.h"
  #include "advice.h"
 +#include "gettext.h"
  
  #include SHA1_HEADER
  #ifndef git_SHA_CTX
@@@ -437,6 -436,7 +437,7 @@@ extern void verify_non_filename(const c
  
  #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)
@@@ -543,7 -543,6 +544,7 @@@ extern NORETURN void unable_to_lock_ind
  extern int hold_lock_file_for_update(struct lock_file *, const char *path, int);
  extern int hold_lock_file_for_append(struct lock_file *, const char *path, int);
  extern int commit_lock_file(struct lock_file *);
 +extern void update_index_if_able(struct index_state *, struct lock_file *);
  
  extern int hold_locked_index(struct lock_file *, int);
  extern int commit_locked_index(struct lock_file *);
@@@ -557,7 -556,6 +558,7 @@@ extern int trust_executable_bit
  extern int trust_ctime;
  extern int quote_path_fully;
  extern int has_symlinks;
 +extern int minimum_abbrev, default_abbrev;
  extern int ignore_case;
  extern int assume_unchanged;
  extern int prefer_symlink_refs;
@@@ -727,7 -725,6 +728,7 @@@ int set_shared_perm(const char *path, i
  #define adjust_shared_perm(path) set_shared_perm((path), 0)
  int safe_create_leading_directories(char *path);
  int safe_create_leading_directories_const(const char *path);
 +int mkdir_in_gitdir(const char *path);
  extern char *expand_user_path(const char *path);
  char *enter_repo(char *path, int strict);
  static inline int is_absolute_path(const char *path)
@@@ -778,8 -775,8 +779,8 @@@ static inline unsigned int hexval(unsig
  }
  
  /* Convert to/from hex/sha1 representation */
 -#define MINIMUM_ABBREV 4
 -#define DEFAULT_ABBREV 7
 +#define MINIMUM_ABBREV minimum_abbrev
 +#define DEFAULT_ABBREV default_abbrev
  
  struct object_context {
        unsigned char tree[20];
@@@ -1025,6 -1022,7 +1026,6 @@@ extern const char *git_etc_gitconfig(vo
  extern int check_repository_format_version(const char *var, const char *value, void *cb);
  extern int git_env_bool(const char *, int);
  extern int git_config_system(void);
 -extern int git_config_global(void);
  extern int config_error_nonbool(const char *);
  extern const char *get_log_output_encoding(void);
  extern const char *get_commit_output_encoding(void);
@@@ -1086,14 -1084,9 +1087,14 @@@ extern void alloc_report(void)
  /* trace.c */
  __attribute__((format (printf, 1, 2)))
  extern void trace_printf(const char *format, ...);
 +extern void trace_vprintf(const char *key, const char *format, va_list ap);
  __attribute__((format (printf, 2, 3)))
  extern void trace_argv_printf(const char **argv, const char *format, ...);
  extern void trace_repo_setup(const char *prefix);
 +extern int trace_want(const char *key);
 +extern void trace_strbuf(const char *key, const struct strbuf *buf);
 +
 +void packet_trace_identity(const char *prog);
  
  /* convert.c */
  /* returns 1 if *dst was used */
diff --combined t/t0001-init.sh
index 0ee9f179939f073ea8e918a6a94328daf88e8d8a,b2e6919da1477e6a1b53182c3061abde844ffc67..a5816d0e4f3744f8ba30666d8bb5828abf6cda51
@@@ -47,7 -47,7 +47,7 @@@ test_expect_success 'plain nested in ba
  
  test_expect_success 'plain through aliased command, outside any git repo' '
        (
 -              sane_unset GIT_DIR GIT_WORK_TREE GIT_CONFIG_NOGLOBAL &&
 +              sane_unset GIT_DIR GIT_WORK_TREE &&
                HOME=$(pwd)/alias-config &&
                export HOME &&
                mkdir alias-config &&
@@@ -180,7 -180,7 +180,7 @@@ test_expect_success 'GIT_DIR & GIT_WORK
        fi
  '
  
 -test_expect_success 'reinit' '
 +test_expect_success C_LOCALE_OUTPUT 'reinit' '
  
        (
                sane_unset GIT_CONFIG GIT_WORK_TREE GIT_CONFIG &&
@@@ -231,6 -231,7 +231,6 @@@ test_expect_success 'init with init.tem
                git config -f "$test_config"  init.templatedir "${HOME}/templatedir-source" &&
                mkdir templatedir-set &&
                cd templatedir-set &&
 -              sane_unset GIT_CONFIG_NOGLOBAL &&
                sane_unset GIT_TEMPLATE_DIR &&
                NO_SET_GIT_TEMPLATE_DIR=t &&
                export NO_SET_GIT_TEMPLATE_DIR &&
  test_expect_success 'init --bare/--shared overrides system/global config' '
        (
                test_config="$HOME"/.gitconfig &&
 -              sane_unset GIT_CONFIG_NOGLOBAL &&
                git config -f "$test_config" core.bare false &&
                git config -f "$test_config" core.sharedRepository 0640 &&
                mkdir init-bare-shared-override &&
  test_expect_success 'init honors global core.sharedRepository' '
        (
                test_config="$HOME"/.gitconfig &&
 -              sane_unset GIT_CONFIG_NOGLOBAL &&
                git config -f "$test_config" core.sharedRepository 0666 &&
                mkdir shared-honor-global &&
                cd shared-honor-global &&
@@@ -371,4 -374,50 +371,50 @@@ test_expect_success 'init prefers comma
        ! 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
diff --combined t/t5601-clone.sh
index a92d145be6e168c44e1cd09e6629e87cde0d156f,c467b6797e61fc65c1780d200a0465f13e5fb74d..5a068b21e4aeac79863df657d6388ec4c2732d55
@@@ -31,7 -31,7 +31,7 @@@ test_expect_success 'clone with excess 
  
  '
  
 -test_expect_success 'output from clone' '
 +test_expect_success C_LOCALE_OUTPUT 'output from clone' '
        rm -fr dst &&
        git clone -n "file://$(pwd)/src" dst >output &&
        test $(grep Clon output | wc -l) = 1
@@@ -164,6 -164,7 +164,6 @@@ test_expect_success 'clone a void' 
  test_expect_success 'clone respects global branch.autosetuprebase' '
        (
                test_config="$HOME/.gitconfig" &&
 -              unset GIT_CONFIG_NOGLOBAL &&
                git config -f "$test_config" branch.autosetuprebase remote &&
                rm -fr dst &&
                git clone src dst &&
@@@ -191,4 -192,17 +191,17 @@@ test_expect_success 'do not respect url
        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