Merge branch 'nd/maint-clone-gitdir'
authorJunio C Hamano <gitster@pobox.com>
Mon, 29 Aug 2011 04:20:28 +0000 (21:20 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 29 Aug 2011 04:20:28 +0000 (21:20 -0700)
* nd/maint-clone-gitdir:
clone: allow to clone from .git file
read_gitfile_gently(): rename misnamed function to read_gitfile()

1  2 
builtin/clone.c
cache.h
environment.c
refs.c
setup.c
submodule.c
t/t5601-clone.sh
diff --combined builtin/clone.c
index 4d66a7f4e82e5e31a0250491bbe22f9ec9ac3bfe,ec57f3dbe4a629b502f7c9e60f89244ef446af4f..488f48e9a571fa9b958cd95b92808b44f4541982
@@@ -39,24 -39,13 +39,24 @@@ static const char * const builtin_clone
  
  static int option_no_checkout, option_bare, option_mirror;
  static int option_local, option_no_hardlinks, option_shared, option_recursive;
 -static char *option_template, *option_reference, *option_depth;
 +static char *option_template, *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;
 +static struct string_list option_config;
 +static struct string_list option_reference;
 +
 +static int opt_parse_reference(const struct option *opt, const char *arg, int unset)
 +{
 +      struct string_list *option_reference = opt->value;
 +      if (!arg)
 +              return -1;
 +      string_list_append(option_reference, arg);
 +      return 0;
 +}
  
  static struct option builtin_clone_options[] = {
        OPT__VERBOSITY(&option_verbosity),
@@@ -82,8 -71,8 +82,8 @@@
                    "initialize submodules in the clone"),
        OPT_STRING(0, "template", &option_template, "template-directory",
                   "directory from which templates will be used"),
 -      OPT_STRING(0, "reference", &option_reference, "repo",
 -                 "reference repository"),
 +      OPT_CALLBACK(0 , "reference", &option_reference, "repo",
 +                   "reference repository", &opt_parse_reference),
        OPT_STRING('o', "origin", &option_origin, "branch",
                   "use <branch> instead of 'origin' to track upstream"),
        OPT_STRING('b', "branch", &option_branch, "branch",
@@@ -94,8 -83,7 +94,8 @@@
                    "create a shallow clone of that depth"),
        OPT_STRING(0, "separate-git-dir", &real_git_dir, "gitdir",
                   "separate git dir from working tree"),
 -
 +      OPT_STRING_LIST('c', "config", &option_config, "key=value",
 +                      "set config inside the new repository"),
        OPT_END()
  };
  
@@@ -113,9 -101,26 +113,26 @@@ static char *get_repo_path(const char *
        for (i = 0; i < ARRAY_SIZE(suffix); i++) {
                const char *path;
                path = mkpath("%s%s", repo, suffix[i]);
-               if (is_directory(path)) {
+               if (stat(path, &st))
+                       continue;
+               if (S_ISDIR(st.st_mode)) {
                        *is_bundle = 0;
                        return xstrdup(absolute_path(path));
+               } else if (S_ISREG(st.st_mode) && st.st_size > 8) {
+                       /* Is it a "gitfile"? */
+                       char signature[8];
+                       int len, fd = open(path, O_RDONLY);
+                       if (fd < 0)
+                               continue;
+                       len = read_in_full(fd, signature, 8);
+                       close(fd);
+                       if (len != 8 || strncmp(signature, "gitdir: ", 8))
+                               continue;
+                       path = read_gitfile(path);
+                       if (path) {
+                               *is_bundle = 0;
+                               return xstrdup(absolute_path(path));
+                       }
                }
        }
  
@@@ -209,80 -214,39 +226,80 @@@ static void strip_trailing_slashes(cha
        *end = '\0';
  }
  
 -static void setup_reference(const char *repo)
 +static int add_one_reference(struct string_list_item *item, void *cb_data)
  {
 -      const char *ref_git;
 -      char *ref_git_copy;
 -
 +      char *ref_git;
 +      struct strbuf alternate = STRBUF_INIT;
        struct remote *remote;
        struct transport *transport;
        const struct ref *extra;
  
 -      ref_git = real_path(option_reference);
 -
 -      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)))
 +      /* Beware: real_path() and mkpath() return static buffer */
 +      ref_git = xstrdup(real_path(item->string));
 +      if (is_directory(mkpath("%s/.git/objects", ref_git))) {
 +              char *ref_git_git = xstrdup(mkpath("%s/.git", ref_git));
 +              free(ref_git);
 +              ref_git = ref_git_git;
 +      } else if (!is_directory(mkpath("%s/objects", ref_git)))
                die(_("reference repository '%s' is not a local directory."),
 -                  option_reference);
 -
 -      ref_git_copy = xstrdup(ref_git);
 +                  item->string);
  
 -      add_to_alternates_file(ref_git_copy);
 +      strbuf_addf(&alternate, "%s/objects", ref_git);
 +      add_to_alternates_file(alternate.buf);
 +      strbuf_release(&alternate);
  
 -      remote = remote_get(ref_git_copy);
 -      transport = transport_get(remote, ref_git_copy);
 +      remote = remote_get(ref_git);
 +      transport = transport_get(remote, ref_git);
        for (extra = transport_get_remote_refs(transport); extra;
             extra = extra->next)
                add_extra_ref(extra->name, extra->old_sha1, 0);
  
        transport_disconnect(transport);
 +      free(ref_git);
 +      return 0;
 +}
  
 -      free(ref_git_copy);
 +static void setup_reference(void)
 +{
 +      for_each_string_list(&option_reference, add_one_reference, NULL);
  }
  
 -static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest)
 +static void copy_alternates(struct strbuf *src, struct strbuf *dst,
 +                          const char *src_repo)
 +{
 +      /*
 +       * Read from the source objects/info/alternates file
 +       * and copy the entries to corresponding file in the
 +       * destination repository with add_to_alternates_file().
 +       * Both src and dst have "$path/objects/info/alternates".
 +       *
 +       * Instead of copying bit-for-bit from the original,
 +       * we need to append to existing one so that the already
 +       * created entry via "clone -s" is not lost, and also
 +       * to turn entries with paths relative to the original
 +       * absolute, so that they can be used in the new repository.
 +       */
 +      FILE *in = fopen(src->buf, "r");
 +      struct strbuf line = STRBUF_INIT;
 +
 +      while (strbuf_getline(&line, in, '\n') != EOF) {
 +              char *abs_path, abs_buf[PATH_MAX];
 +              if (!line.len || line.buf[0] == '#')
 +                      continue;
 +              if (is_absolute_path(line.buf)) {
 +                      add_to_alternates_file(line.buf);
 +                      continue;
 +              }
 +              abs_path = mkpath("%s/objects/%s", src_repo, line.buf);
 +              normalize_path_copy(abs_buf, abs_path);
 +              add_to_alternates_file(abs_buf);
 +      }
 +      strbuf_release(&line);
 +      fclose(in);
 +}
 +
 +static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
 +                                 const char *src_repo, int src_baselen)
  {
        struct dirent *de;
        struct stat buf;
                }
                if (S_ISDIR(buf.st_mode)) {
                        if (de->d_name[0] != '.')
 -                              copy_or_link_directory(src, dest);
 +                              copy_or_link_directory(src, dest,
 +                                                     src_repo, src_baselen);
 +                      continue;
 +              }
 +
 +              /* Files that cannot be copied bit-for-bit... */
 +              if (!strcmp(src->buf + src_baselen, "/info/alternates")) {
 +                      copy_alternates(src, dest, src_repo);
                        continue;
                }
  
@@@ -348,20 -305,17 +365,20 @@@ static const struct ref *clone_local(co
                                     const char *dest_repo)
  {
        const struct ref *ret;
 -      struct strbuf src = STRBUF_INIT;
 -      struct strbuf dest = STRBUF_INIT;
        struct remote *remote;
        struct transport *transport;
  
 -      if (option_shared)
 -              add_to_alternates_file(src_repo);
 -      else {
 +      if (option_shared) {
 +              struct strbuf alt = STRBUF_INIT;
 +              strbuf_addf(&alt, "%s/objects", src_repo);
 +              add_to_alternates_file(alt.buf);
 +              strbuf_release(&alt);
 +      } else {
 +              struct strbuf src = STRBUF_INIT;
 +              struct strbuf dest = STRBUF_INIT;
                strbuf_addf(&src, "%s/objects", src_repo);
                strbuf_addf(&dest, "%s/objects", dest_repo);
 -              copy_or_link_directory(&src, &dest);
 +              copy_or_link_directory(&src, &dest, src_repo, src.len);
                strbuf_release(&src);
                strbuf_release(&dest);
        }
@@@ -406,9 -360,8 +423,9 @@@ static void remove_junk_on_signal(int s
  static struct ref *wanted_peer_refs(const struct ref *refs,
                struct refspec *refspec)
  {
 -      struct ref *local_refs = NULL;
 -      struct ref **tail = &local_refs;
 +      struct ref *head = copy_ref(find_ref_by_name(refs, "HEAD"));
 +      struct ref *local_refs = head;
 +      struct ref **tail = head ? &head->next : &local_refs;
  
        get_fetch_map(refs, refspec, &tail, 0);
        if (!option_mirror)
@@@ -421,32 -374,13 +438,32 @@@ static void write_remote_refs(const str
  {
        const struct ref *r;
  
 -      for (r = local_refs; r; r = r->next)
 +      for (r = local_refs; r; r = r->next) {
 +              if (!r->peer_ref)
 +                      continue;
                add_extra_ref(r->peer_ref->name, r->old_sha1, 0);
 +      }
  
        pack_refs(PACK_REFS_ALL);
        clear_extra_refs();
  }
  
 +static int write_one_config(const char *key, const char *value, void *data)
 +{
 +      return git_config_set_multivar(key, value ? value : "true", "^$", 0);
 +}
 +
 +static void write_config(struct string_list *config)
 +{
 +      int i;
 +
 +      for (i = 0; i < config->nr; i++) {
 +              if (git_config_parse_parameter(config->items[i].string,
 +                                             write_one_config, NULL) < 0)
 +                      die("unable to write parameters to config file");
 +      }
 +}
 +
  int cmd_clone(int argc, const char **argv, const char *prefix)
  {
        int is_bundle = 0, is_local;
                        printf(_("Cloning into %s...\n"), dir);
        }
        init_db(option_template, INIT_DB_QUIET);
 +      write_config(&option_config);
  
        /*
         * At this point, the config exists, so we do not need the
        git_config_set(key.buf, repo);
        strbuf_reset(&key);
  
 -      if (option_reference)
 -              setup_reference(git_dir);
 +      if (option_reference.nr)
 +              setup_reference();
  
        fetch_pattern = value.buf;
        refspec = parse_fetch_refspec(1, &fetch_pattern);
diff --combined cache.h
index 83b1ec13963f9222b9988eacffcac98b95becd82,1abf71505007a9ca8476e24cf1a097c0d9f8b302..607c2ea612889c46e81ae375b6985db18e529a8e
+++ b/cache.h
@@@ -6,7 -6,6 +6,7 @@@
  #include "hash.h"
  #include "advice.h"
  #include "gettext.h"
 +#include "convert.h"
  
  #include SHA1_HEADER
  #ifndef git_SHA_CTX
  #endif
  
  #include <zlib.h>
 -#if defined(NO_DEFLATE_BOUND) || ZLIB_VERNUM < 0x1200
 -#define deflateBound(c,s)  ((s) + (((s) + 7) >> 3) + (((s) + 63) >> 6) + 11)
 -#endif
 -
 -void git_inflate_init(z_streamp strm);
 -void git_inflate_end(z_streamp strm);
 -int git_inflate(z_streamp strm, int flush);
 +typedef struct git_zstream {
 +      z_stream z;
 +      unsigned long avail_in;
 +      unsigned long avail_out;
 +      unsigned long total_in;
 +      unsigned long total_out;
 +      unsigned char *next_in;
 +      unsigned char *next_out;
 +} git_zstream;
 +
 +void git_inflate_init(git_zstream *);
 +void git_inflate_init_gzip_only(git_zstream *);
 +void git_inflate_end(git_zstream *);
 +int git_inflate(git_zstream *, int flush);
 +
 +void git_deflate_init(git_zstream *, int level);
 +void git_deflate_init_gzip(git_zstream *, int level);
 +void git_deflate_end(git_zstream *);
 +int git_deflate_end_gently(git_zstream *);
 +int git_deflate(git_zstream *, int flush);
 +unsigned long git_deflate_bound(git_zstream *, unsigned long);
  
  #if defined(DT_UNKNOWN) && !defined(NO_D_TYPE_IN_DIRENT)
  #define DTYPE(de)     ((de)->d_type)
@@@ -394,7 -379,6 +394,7 @@@ static inline enum object_type object_t
  }
  
  #define GIT_DIR_ENVIRONMENT "GIT_DIR"
 +#define GIT_NAMESPACE_ENVIRONMENT "GIT_NAMESPACE"
  #define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE"
  #define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
  #define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY"
@@@ -435,16 -419,13 +435,16 @@@ extern char *get_object_directory(void)
  extern char *get_index_file(void);
  extern char *get_graft_file(void);
  extern int set_git_dir(const char *path);
 +extern const char *get_git_namespace(void);
 +extern const char *strip_namespace(const char *namespaced_ref);
  extern const char *get_git_work_tree(void);
- extern const char *read_gitfile_gently(const char *path);
+ extern const char *read_gitfile(const char *path);
  extern void set_git_work_tree(const char *tree);
  
  #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
  
  extern const char **get_pathspec(const char *prefix, const char **pathspec);
 +extern const char *pathspec_prefix(const char *prefix, const char **pathspec);
  extern void setup_work_tree(void);
  extern const char *setup_git_directory_gently(int *);
  extern const char *setup_git_directory(void);
@@@ -601,6 -582,35 +601,6 @@@ extern int fsync_object_files
  extern int core_preload_index;
  extern int core_apply_sparse_checkout;
  
 -enum safe_crlf {
 -      SAFE_CRLF_FALSE = 0,
 -      SAFE_CRLF_FAIL = 1,
 -      SAFE_CRLF_WARN = 2
 -};
 -
 -extern enum safe_crlf safe_crlf;
 -
 -enum auto_crlf {
 -      AUTO_CRLF_FALSE = 0,
 -      AUTO_CRLF_TRUE = 1,
 -      AUTO_CRLF_INPUT = -1
 -};
 -
 -extern enum auto_crlf auto_crlf;
 -
 -enum eol {
 -      EOL_UNSET,
 -      EOL_CRLF,
 -      EOL_LF,
 -#ifdef NATIVE_CRLF
 -      EOL_NATIVE = EOL_CRLF
 -#else
 -      EOL_NATIVE = EOL_LF
 -#endif
 -};
 -
 -extern enum eol core_eol;
 -
  enum branch_track {
        BRANCH_TRACK_UNSPECIFIED = -1,
        BRANCH_TRACK_NEVER = 0,
@@@ -737,7 -747,7 +737,7 @@@ extern char *expand_user_path(const cha
  char *enter_repo(char *path, int strict);
  static inline int is_absolute_path(const char *path)
  {
 -      return path[0] == '/' || has_dos_drive_prefix(path);
 +      return is_dir_sep(path[0]) || has_dos_drive_prefix(path);
  }
  int is_directory(const char *);
  const char *real_path(const char *path);
@@@ -770,16 -780,10 +770,16 @@@ extern int hash_sha1_file(const void *b
  extern int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
  extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
  extern int force_object_loose(const unsigned char *sha1, time_t mtime);
 +extern void *map_sha1_file(const unsigned char *sha1, unsigned long *size);
 +extern int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz);
 +extern int parse_sha1_header(const char *hdr, unsigned long *sizep);
  
  /* global flag to enable extra checks when accessing packed objects */
  extern int do_check_packed_object_crc;
  
 +/* for development: log offset of pack access */
 +extern const char *log_pack_access;
 +
  extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned long size, const char *type);
  
  extern int move_temp_to_file(const char *tmpfile, const char *filename);
@@@ -1005,7 -1009,7 +1005,7 @@@ extern struct packed_git *find_sha1_pac
  extern void pack_report(void);
  extern int open_pack_index(struct packed_git *);
  extern void close_pack_index(struct packed_git *);
 -extern unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned int *);
 +extern unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned long *);
  extern void close_pack_windows(struct packed_git *);
  extern void unuse_pack(struct pack_window **);
  extern void free_pack_by_name(const char *);
@@@ -1017,36 -1021,7 +1017,36 @@@ extern off_t find_pack_entry_one(const 
  extern void *unpack_entry(struct packed_git *, off_t, enum object_type *, unsigned long *);
  extern unsigned long unpack_object_header_buffer(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep);
  extern unsigned long get_size_from_delta(struct packed_git *, struct pack_window **, off_t);
 -extern const char *packed_object_info_detail(struct packed_git *, off_t, unsigned long *, unsigned long *, unsigned int *, unsigned char *);
 +extern int unpack_object_header(struct packed_git *, struct pack_window **, off_t *, unsigned long *);
 +
 +struct object_info {
 +      /* Request */
 +      unsigned long *sizep;
 +
 +      /* Response */
 +      enum {
 +              OI_CACHED,
 +              OI_LOOSE,
 +              OI_PACKED,
 +              OI_DBCACHED
 +      } whence;
 +      union {
 +              /*
 +               * struct {
 +               *      ... Nothing to expose in this case
 +               * } cached;
 +               * struct {
 +               *      ... Nothing to expose in this case
 +               * } loose;
 +               */
 +              struct {
 +                      struct packed_git *pack;
 +                      off_t offset;
 +                      unsigned int is_delta;
 +              } packed;
 +      } u;
 +};
 +extern int sha1_object_info_extended(const unsigned char *, struct object_info *);
  
  /* Dumb servers support */
  extern int update_server_info(int);
@@@ -1088,8 -1063,6 +1088,8 @@@ extern int config_error_nonbool(const c
  extern const char *get_log_output_encoding(void);
  extern const char *get_commit_output_encoding(void);
  
 +extern int git_config_parse_parameter(const char *, config_fn_t fn, void *data);
 +
  extern const char *config_exclusive_filename;
  
  #define MAX_GITNAME (1000)
@@@ -1156,6 -1129,13 +1156,6 @@@ extern void trace_strbuf(const char *ke
  
  void packet_trace_identity(const char *prog);
  
 -/* convert.c */
 -/* returns 1 if *dst was used */
 -extern int convert_to_git(const char *path, const char *src, size_t len,
 -                          struct strbuf *dst, enum safe_crlf checksafe);
 -extern int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst);
 -extern int renormalize_buffer(const char *path, const char *src, size_t len, struct strbuf *dst);
 -
  /* add */
  /*
   * return 0 if success, 1 - if addition of a file failed and
@@@ -1195,7 -1175,7 +1195,7 @@@ extern int ws_blank_line(const char *li
  #define ws_tab_width(rule)     ((rule) & WS_TAB_WIDTH_MASK)
  
  /* ls-files */
 -int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset);
 +int report_path_error(const char *ps_matched, const char **pathspec, const char *prefix);
  void overlay_tree_on_cache(const char *tree_name, const char *prefix);
  
  char *alias_lookup(const char *alias);
diff --combined environment.c
index 03d29e8d48f46428c4efdacac84e6a0e1ddf4a0b,2228c4e9a14ed6b1b936c838b52f66909429d9d2..e96edcfebc4174a5166c11e7a511ea792e7a2639
@@@ -8,7 -8,6 +8,7 @@@
   * are.
   */
  #include "cache.h"
 +#include "refs.h"
  
  char git_default_email[MAX_GITNAME];
  char git_default_name[MAX_GITNAME];
@@@ -37,7 -36,6 +37,7 @@@ size_t packed_git_window_size = DEFAULT
  size_t packed_git_limit = DEFAULT_PACKED_GIT_LIMIT;
  size_t delta_base_cache_limit = 16 * 1024 * 1024;
  unsigned long big_file_threshold = 512 * 1024 * 1024;
 +const char *log_pack_access;
  const char *pager_program;
  int pager_use_color = 1;
  const char *editor_program;
@@@ -67,9 -65,6 +67,9 @@@ int core_preload_index = 0
  char *git_work_tree_cfg;
  static char *work_tree;
  
 +static const char *namespace;
 +static size_t namespace_len;
 +
  static const char *git_dir;
  static char *git_object_dir, *git_index_file, *git_graft_file;
  
@@@ -91,33 -86,12 +91,33 @@@ const char * const local_repo_env[LOCAL
        NULL
  };
  
 +static char *expand_namespace(const char *raw_namespace)
 +{
 +      struct strbuf buf = STRBUF_INIT;
 +      struct strbuf **components, **c;
 +
 +      if (!raw_namespace || !*raw_namespace)
 +              return xstrdup("");
 +
 +      strbuf_addstr(&buf, raw_namespace);
 +      components = strbuf_split(&buf, '/');
 +      strbuf_reset(&buf);
 +      for (c = components; *c; c++)
 +              if (strcmp((*c)->buf, "/") != 0)
 +                      strbuf_addf(&buf, "refs/namespaces/%s", (*c)->buf);
 +      strbuf_list_free(components);
 +      if (check_ref_format(buf.buf) != CHECK_REF_FORMAT_OK)
 +              die("bad git namespace path \"%s\"", raw_namespace);
 +      strbuf_addch(&buf, '/');
 +      return strbuf_detach(&buf, NULL);
 +}
 +
  static void setup_git_env(void)
  {
        git_dir = getenv(GIT_DIR_ENVIRONMENT);
        git_dir = git_dir ? xstrdup(git_dir) : NULL;
        if (!git_dir) {
-               git_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);
+               git_dir = read_gitfile(DEFAULT_GIT_DIR_ENVIRONMENT);
                git_dir = git_dir ? xstrdup(git_dir) : NULL;
        }
        if (!git_dir)
                git_graft_file = git_pathdup("info/grafts");
        if (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT))
                read_replace_refs = 0;
 +      namespace = expand_namespace(getenv(GIT_NAMESPACE_ENVIRONMENT));
 +      namespace_len = strlen(namespace);
  }
  
  int is_bare_repository(void)
@@@ -159,20 -131,6 +159,20 @@@ const char *get_git_dir(void
        return git_dir;
  }
  
 +const char *get_git_namespace(void)
 +{
 +      if (!namespace)
 +              setup_git_env();
 +      return namespace;
 +}
 +
 +const char *strip_namespace(const char *namespaced_ref)
 +{
 +      if (prefixcmp(namespaced_ref, get_git_namespace()) != 0)
 +              return NULL;
 +      return namespaced_ref + namespace_len;
 +}
 +
  static int git_work_tree_initialized;
  
  /*
diff --combined refs.c
index 6f313a9e0cdec2400a993cf7c3dfa87529a493ae,c98c006e7a0270db2ebeb628cacc6027645eb4b2..e5b2b1ac2bf535e931165ff1cb4691163c1843a2
--- 1/refs.c
--- 2/refs.c
+++ b/refs.c
@@@ -451,7 -451,7 +451,7 @@@ int resolve_gitlink_ref(const char *pat
        memcpy(gitdir + len, "/.git", 6);
        len += 5;
  
-       tmp = read_gitfile_gently(gitdir);
+       tmp = read_gitfile(gitdir);
        if (tmp) {
                free(gitdir);
                len = strlen(tmp);
@@@ -584,7 -584,7 +584,7 @@@ int read_ref(const char *ref, unsigned 
  static int do_one_ref(const char *base, each_ref_fn fn, int trim,
                      int flags, void *cb_data, struct ref_list *entry)
  {
 -      if (strncmp(base, entry->name, trim))
 +      if (prefixcmp(entry->name, base))
                return 0;
  
        if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN)) {
@@@ -728,12 -728,12 +728,12 @@@ int head_ref_submodule(const char *subm
  
  int for_each_ref(each_ref_fn fn, void *cb_data)
  {
 -      return do_for_each_ref(NULL, "refs/", fn, 0, 0, cb_data);
 +      return do_for_each_ref(NULL, "", fn, 0, 0, cb_data);
  }
  
  int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
  {
 -      return do_for_each_ref(submodule, "refs/", fn, 0, 0, cb_data);
 +      return do_for_each_ref(submodule, "", fn, 0, 0, cb_data);
  }
  
  int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)
@@@ -782,31 -782,6 +782,31 @@@ int for_each_replace_ref(each_ref_fn fn
        return do_for_each_ref(NULL, "refs/replace/", fn, 13, 0, cb_data);
  }
  
 +int head_ref_namespaced(each_ref_fn fn, void *cb_data)
 +{
 +      struct strbuf buf = STRBUF_INIT;
 +      int ret = 0;
 +      unsigned char sha1[20];
 +      int flag;
 +
 +      strbuf_addf(&buf, "%sHEAD", get_git_namespace());
 +      if (resolve_ref(buf.buf, sha1, 1, &flag))
 +              ret = fn(buf.buf, sha1, flag, cb_data);
 +      strbuf_release(&buf);
 +
 +      return ret;
 +}
 +
 +int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)
 +{
 +      struct strbuf buf = STRBUF_INIT;
 +      int ret;
 +      strbuf_addf(&buf, "%srefs/", get_git_namespace());
 +      ret = do_for_each_ref(NULL, buf.buf, fn, 0, 0, cb_data);
 +      strbuf_release(&buf);
 +      return ret;
 +}
 +
  int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
        const char *prefix, void *cb_data)
  {
@@@ -844,7 -819,7 +844,7 @@@ int for_each_glob_ref(each_ref_fn fn, c
  
  int for_each_rawref(each_ref_fn fn, void *cb_data)
  {
 -      return do_for_each_ref(NULL, "refs/", fn, 0,
 +      return do_for_each_ref(NULL, "", fn, 0,
                               DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
  }
  
@@@ -1851,12 -1826,6 +1851,12 @@@ int update_ref(const char *action, cons
        return 0;
  }
  
 +int ref_exists(char *refname)
 +{
 +      unsigned char sha1[20];
 +      return !!resolve_ref(refname, sha1, 1, NULL);
 +}
 +
  struct ref *find_ref_by_name(const struct ref *list, const char *name)
  {
        for ( ; list; list = list->next)
diff --combined setup.c
index ca7ee496e59580487043344eb9907528ef33aa5c,1b06db53fc24f1347e2a73bbecb44a059882e898..27c1d4787a2c2efd8420a225b43e36b76464ce42
+++ b/setup.c
@@@ -40,6 -40,34 +40,6 @@@ char *prefix_path(const char *prefix, i
        return sanitized;
  }
  
 -/*
 - * Unlike prefix_path, this should be used if the named file does
 - * not have to interact with index entry; i.e. name of a random file
 - * on the filesystem.
 - */
 -const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
 -{
 -      static char path[PATH_MAX];
 -#ifndef WIN32
 -      if (!pfx_len || is_absolute_path(arg))
 -              return arg;
 -      memcpy(path, pfx, pfx_len);
 -      strcpy(path + pfx_len, arg);
 -#else
 -      char *p;
 -      /* don't add prefix to absolute paths, but still replace '\' by '/' */
 -      if (is_absolute_path(arg))
 -              pfx_len = 0;
 -      else if (pfx_len)
 -              memcpy(path, pfx, pfx_len);
 -      strcpy(path + pfx_len, arg);
 -      for (p = path + pfx_len; *p; p++)
 -              if (*p == '\\')
 -                      *p = '/';
 -#endif
 -      return path;
 -}
 -
  int check_filename(const char *prefix, const char *arg)
  {
        const char *name;
@@@ -236,38 -264,6 +236,38 @@@ const char **get_pathspec(const char *p
        return pathspec;
  }
  
 +const char *pathspec_prefix(const char *prefix, const char **pathspec)
 +{
 +      const char **p, *n, *prev;
 +      unsigned long max;
 +
 +      if (!pathspec)
 +              return prefix ? xmemdupz(prefix, strlen(prefix)) : NULL;
 +
 +      prev = NULL;
 +      max = PATH_MAX;
 +      for (p = pathspec; (n = *p) != NULL; p++) {
 +              int i, len = 0;
 +              for (i = 0; i < max; i++) {
 +                      char c = n[i];
 +                      if (prev && prev[i] != c)
 +                              break;
 +                      if (!c || c == '*' || c == '?')
 +                              break;
 +                      if (c == '/')
 +                              len = i+1;
 +              }
 +              prev = n;
 +              if (len < max) {
 +                      max = len;
 +                      if (!max)
 +                              break;
 +              }
 +      }
 +
 +      return max ? xmemdupz(prev, max) : NULL;
 +}
 +
  /*
   * Test if it looks like we're at a git directory.
   * We want to see:
@@@ -379,7 -375,7 +379,7 @@@ static int check_repository_format_gent
   * Try to read the location of the git directory from the .git file,
   * return path to git directory if found.
   */
- const char *read_gitfile_gently(const char *path)
+ const char *read_gitfile(const char *path)
  {
        char *buf;
        char *dir;
@@@ -441,7 -437,7 +441,7 @@@ static const char *setup_explicit_git_d
        if (PATH_MAX - 40 < strlen(gitdirenv))
                die("'$%s' too big", GIT_DIR_ENVIRONMENT);
  
-       gitfile = (char*)read_gitfile_gently(gitdirenv);
+       gitfile = (char*)read_gitfile(gitdirenv);
        if (gitfile) {
                gitfile = xstrdup(gitfile);
                gitdirenv = gitfile;
@@@ -665,7 -661,7 +665,7 @@@ static const char *setup_git_directory_
        if (one_filesystem)
                current_device = get_device_or_die(".", NULL);
        for (;;) {
-               gitfile = (char*)read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);
+               gitfile = (char*)read_gitfile(DEFAULT_GIT_DIR_ENVIRONMENT);
                if (gitfile)
                        gitdirenv = gitfile = xstrdup(gitfile);
                else {
@@@ -714,11 -710,6 +714,11 @@@ const char *setup_git_directory_gently(
        const char *prefix;
  
        prefix = setup_git_directory_gently_1(nongit_ok);
 +      if (prefix)
 +              setenv("GIT_PREFIX", prefix, 1);
 +      else
 +              setenv("GIT_PREFIX", "", 1);
 +
        if (startup_info) {
                startup_info->have_repository = !nongit_ok || !*nongit_ok;
                startup_info->prefix = prefix;
diff --combined submodule.c
index 1ba9646d3484fe2a11c1faba339c4e333186085e,b8b0326c5090f545ebaf5e26146588e7009eed01..f71d5ed8b0e0f202084ecc6b10636196f2763b16
@@@ -32,7 -32,7 +32,7 @@@ static int add_submodule_odb(const cha
        const char *git_dir;
  
        strbuf_addf(&objects_directory, "%s/.git", path);
-       git_dir = read_gitfile_gently(objects_directory.buf);
+       git_dir = read_gitfile(objects_directory.buf);
        if (git_dir) {
                strbuf_reset(&objects_directory);
                strbuf_addstr(&objects_directory, git_dir);
@@@ -388,7 -388,6 +388,7 @@@ void check_for_new_submodule_commits(un
                while (parent) {
                        struct diff_options diff_opts;
                        diff_setup(&diff_opts);
 +                      DIFF_OPT_SET(&diff_opts, RECURSIVE);
                        diff_opts.output_format |= DIFF_FORMAT_CALLBACK;
                        diff_opts.format_callback = submodule_collect_changed_cb;
                        if (diff_setup_done(&diff_opts) < 0)
@@@ -479,7 -478,7 +479,7 @@@ int fetch_populated_submodules(int num_
                strbuf_addf(&submodule_path, "%s/%s", work_tree, ce->name);
                strbuf_addf(&submodule_git_dir, "%s/.git", submodule_path.buf);
                strbuf_addf(&submodule_prefix, "%s%s/", prefix, ce->name);
-               git_dir = read_gitfile_gently(submodule_git_dir.buf);
+               git_dir = read_gitfile(submodule_git_dir.buf);
                if (!git_dir)
                        git_dir = submodule_git_dir.buf;
                if (is_directory(git_dir)) {
@@@ -517,7 -516,7 +517,7 @@@ unsigned is_submodule_modified(const ch
        const char *git_dir;
  
        strbuf_addf(&buf, "%s/.git", path);
-       git_dir = read_gitfile_gently(buf.buf);
+       git_dir = read_gitfile(buf.buf);
        if (!git_dir)
                git_dir = buf.buf;
        if (!is_directory(git_dir)) {
diff --combined t/t5601-clone.sh
index d87214cfbf95d44baa9683b6b05a78743ee78bfb,501bd3fb6cc9c8795a0ea9570512668abaaa2693..e8103144bb026afb12f5b058b9ec399b70abebbd
@@@ -202,32 -202,13 +202,36 @@@ test_expect_success 'clone separate git
        test_cmp expected dst/.git
  '
  
+ test_expect_success 'clone from .git file' '
+       git clone dst/.git dst2
+ '
  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_expect_success 'clone --reference from original' '
 +      git clone --shared --bare src src-1 &&
 +      git clone --bare src src-2 &&
 +      git clone --reference=src-2 --bare src-1 target-8 &&
 +      grep /src-2/ target-8/objects/info/alternates
 +'
 +
 +test_expect_success 'clone with more than one --reference' '
 +      git clone --bare src src-3 &&
 +      git clone --bare src src-4 &&
 +      git clone --reference=src-3 --reference=src-4 src target-9 &&
 +      grep /src-3/ target-9/.git/objects/info/alternates &&
 +      grep /src-4/ target-9/.git/objects/info/alternates
 +'
 +
 +test_expect_success 'clone from original with relative alternate' '
 +      mkdir nest &&
 +      git clone --bare src nest/src-5 &&
 +      echo ../../../src/.git/objects >nest/src-5/objects/info/alternates &&
 +      git clone --bare nest/src-5 target-10 &&
 +      grep /src/\\.git/objects target-10/objects/info/alternates
 +'
 +
  test_done