From: Junio C Hamano Date: Mon, 29 Aug 2011 04:20:28 +0000 (-0700) Subject: Merge branch 'nd/maint-clone-gitdir' X-Git-Tag: v1.7.7-rc1~35 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/2730f55527143d3476c159cebbdb63d5e6a5c2a8?ds=inline;hp=-c Merge branch 'nd/maint-clone-gitdir' * nd/maint-clone-gitdir: clone: allow to clone from .git file read_gitfile_gently(): rename misnamed function to read_gitfile() --- 2730f55527143d3476c159cebbdb63d5e6a5c2a8 diff --combined builtin/clone.c index 4d66a7f4e8,ec57f3dbe4..488f48e9a5 --- a/builtin/clone.c +++ b/builtin/clone.c @@@ -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 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; @@@ -318,14 -282,7 +335,14 @@@ } 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; @@@ -565,7 -499,6 +582,7 @@@ 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 @@@ -605,8 -538,8 +622,8 @@@ 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 83b1ec1396,1abf715050..607c2ea612 --- a/cache.h +++ 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 @@@ -17,27 -16,13 +17,27 @@@ #endif #include -#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 03d29e8d48,2228c4e9a1..e96edcfebc --- a/environment.c +++ b/environment.c @@@ -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) @@@ -137,8 -111,6 +137,8 @@@ 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 6f313a9e0c,c98c006e7a..e5b2b1ac2b --- a/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 ca7ee496e5,1b06db53fc..27c1d4787a --- a/setup.c +++ 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 1ba9646d34,b8b0326c50..f71d5ed8b0 --- a/submodule.c +++ b/submodule.c @@@ -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 d87214cfbf,501bd3fb6c..e8103144bb --- a/t/t5601-clone.sh +++ b/t/t5601-clone.sh @@@ -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