From: Junio C Hamano Date: Thu, 20 Apr 2017 04:37:19 +0000 (-0700) Subject: Merge branch 'nd/files-backend-git-dir' X-Git-Tag: v2.13.0-rc0~9 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/5ab8f2261fa2b595abe433dd50be0f2aaec14aa0?ds=inline;hp=-c Merge branch 'nd/files-backend-git-dir' The "submodule" specific field in the ref_store structure is replaced with a more generic "gitdir" that can later be used also when dealing with ref_store that represents the set of refs visible from the other worktrees. * nd/files-backend-git-dir: (28 commits) refs.h: add a note about sorting order of for_each_ref_* t1406: new tests for submodule ref store t1405: some basic tests on main ref store t/helper: add test-ref-store to test ref-store functions refs: delete pack_refs() in favor of refs_pack_refs() files-backend: avoid ref api targeting main ref store refs: new transaction related ref-store api refs: add new ref-store api refs: rename get_ref_store() to get_submodule_ref_store() and make it public files-backend: replace submodule_allowed check in files_downcast() refs: move submodule code out of files-backend.c path.c: move some code out of strbuf_git_path_submodule() refs.c: make get_main_ref_store() public and use it refs.c: kill register_ref_store(), add register_submodule_ref_store() refs.c: flatten get_ref_store() a bit refs: rename lookup_ref_store() to lookup_submodule_ref_store() refs.c: introduce get_main_ref_store() files-backend: remove the use of git_path() files-backend: add and use files_ref_path() files-backend: add and use files_reflog_path() ... --- 5ab8f2261fa2b595abe433dd50be0f2aaec14aa0 diff --combined Makefile index d595ea3837,5f3844e33e..c6dbeccf5e --- a/Makefile +++ b/Makefile @@@ -140,13 -140,6 +140,13 @@@ all: # Define PPC_SHA1 environment variable when running make to make use of # a bundled SHA1 routine optimized for PowerPC. # +# Define DC_SHA1 to unconditionally enable the collision-detecting sha1 +# algorithm. This is slower, but may detect attempted collision attacks. +# Takes priority over other *_SHA1 knobs. +# +# Define OPENSSL_SHA1 environment variable when running make to link +# with the SHA1 routine from openssl library. +# # Define SHA1_MAX_BLOCK_SIZE to limit the amount of data that will be hashed # in one call to the platform's SHA1_Update(). e.g. APPLE_COMMON_CRYPTO # wants 'SHA1_MAX_BLOCK_SIZE=1024L*1024L*1024L' defined. @@@ -621,7 -614,6 +621,7 @@@ TEST_PROGRAMS_NEED_X += test-fake-ss TEST_PROGRAMS_NEED_X += test-genrandom TEST_PROGRAMS_NEED_X += test-hashmap TEST_PROGRAMS_NEED_X += test-index-version +TEST_PROGRAMS_NEED_X += test-lazy-init-name-hash TEST_PROGRAMS_NEED_X += test-line-buffer TEST_PROGRAMS_NEED_X += test-match-trees TEST_PROGRAMS_NEED_X += test-mergesort @@@ -630,6 -622,7 +630,7 @@@ TEST_PROGRAMS_NEED_X += test-parse-opti TEST_PROGRAMS_NEED_X += test-path-utils TEST_PROGRAMS_NEED_X += test-prio-queue TEST_PROGRAMS_NEED_X += test-read-cache + TEST_PROGRAMS_NEED_X += test-ref-store TEST_PROGRAMS_NEED_X += test-regex TEST_PROGRAMS_NEED_X += test-revision-walking TEST_PROGRAMS_NEED_X += test-run-command @@@ -1391,10 -1384,6 +1392,10 @@@ ifdef APPLE_COMMON_CRYPT SHA1_MAX_BLOCK_SIZE = 1024L*1024L*1024L endif +ifdef OPENSSL_SHA1 + EXTLIBS += $(LIB_4_CRYPTO) + BASIC_CFLAGS += -DSHA1_OPENSSL +else ifdef BLK_SHA1 LIB_OBJS += block-sha1/sha1.o BASIC_CFLAGS += -DSHA1_BLK @@@ -1407,11 -1396,8 +1408,11 @@@ ifdef APPLE_COMMON_CRYPT COMPAT_CFLAGS += -DCOMMON_DIGEST_FOR_OPENSSL BASIC_CFLAGS += -DSHA1_APPLE else - EXTLIBS += $(LIB_4_CRYPTO) - BASIC_CFLAGS += -DSHA1_OPENSSL + DC_SHA1 := YesPlease + LIB_OBJS += sha1dc/sha1.o + LIB_OBJS += sha1dc/ubc_check.o + BASIC_CFLAGS += -DSHA1_DC +endif endif endif endif @@@ -1851,7 -1837,6 +1852,7 @@@ perl/perl.mak: perl/PM.stam perl/PM.stamp: FORCE @$(FIND) perl -type f -name '*.pm' | sort >$@+ && \ + $(PERL_PATH) -V >>$@+ && \ { cmp $@+ $@ >/dev/null 2>/dev/null || mv $@+ $@; } && \ $(RM) $@+ @@@ -2239,7 -2224,6 +2240,7 @@@ GIT-BUILD-OPTIONS: FORC @echo NO_PYTHON=\''$(subst ','\'',$(subst ','\'',$(NO_PYTHON)))'\' >>$@+ @echo NO_UNIX_SOCKETS=\''$(subst ','\'',$(subst ','\'',$(NO_UNIX_SOCKETS)))'\' >>$@+ @echo PAGER_ENV=\''$(subst ','\'',$(subst ','\'',$(PAGER_ENV)))'\' >>$@+ + @echo DC_SHA1=\''$(subst ','\'',$(subst ','\'',$(DC_SHA1)))'\' >>$@+ ifdef TEST_OUTPUT_DIRECTORY @echo TEST_OUTPUT_DIRECTORY=\''$(subst ','\'',$(subst ','\'',$(TEST_OUTPUT_DIRECTORY)))'\' >>$@+ endif @@@ -2349,17 -2333,9 +2350,17 @@@ check: common-cmds. C_SOURCES = $(patsubst %.o,%.c,$(C_OBJ)) %.cocci.patch: %.cocci $(C_SOURCES) @echo ' ' SPATCH $<; \ + ret=0; \ for f in $(C_SOURCES); do \ - $(SPATCH) --sp-file $< $$f $(SPATCH_FLAGS); \ - done >$@ 2>$@.log; \ + $(SPATCH) --sp-file $< $$f $(SPATCH_FLAGS) || \ + { ret=$$?; break; }; \ + done >$@+ 2>$@.log; \ + if test $$ret != 0; \ + then \ + cat $@.log; \ + exit 1; \ + fi; \ + mv $@+ $@; \ if test -s $@; \ then \ echo ' ' SPATCH result: $@; \ diff --combined path.c index 22248436bf,03e7e33b6e..302f9af2bd --- a/path.c +++ b/path.c @@@ -471,39 -471,19 +471,19 @@@ const char *worktree_git_path(const str } /* Returns 0 on success, negative on failure. */ - #define SUBMODULE_PATH_ERR_NOT_CONFIGURED -1 static int do_submodule_path(struct strbuf *buf, const char *path, const char *fmt, va_list args) { - const char *git_dir; struct strbuf git_submodule_common_dir = STRBUF_INIT; struct strbuf git_submodule_dir = STRBUF_INIT; - const struct submodule *sub; - int err = 0; + int ret; - strbuf_addstr(buf, path); - strbuf_complete(buf, '/'); - strbuf_addstr(buf, ".git"); - - git_dir = read_gitfile(buf->buf); - if (git_dir) { - strbuf_reset(buf); - strbuf_addstr(buf, git_dir); - } - if (!is_git_directory(buf->buf)) { - gitmodules_config(); - sub = submodule_from_path(null_sha1, path); - if (!sub) { - err = SUBMODULE_PATH_ERR_NOT_CONFIGURED; - goto cleanup; - } - strbuf_reset(buf); - strbuf_git_path(buf, "%s/%s", "modules", sub->name); - } - - strbuf_addch(buf, '/'); - strbuf_addbuf(&git_submodule_dir, buf); + ret = submodule_to_gitdir(&git_submodule_dir, path); + if (ret) + goto cleanup; + strbuf_complete(&git_submodule_dir, '/'); + strbuf_addbuf(buf, &git_submodule_dir); strbuf_vaddf(buf, fmt, args); if (get_common_dir_noenv(&git_submodule_common_dir, git_submodule_dir.buf)) @@@ -514,8 -494,7 +494,7 @@@ cleanup: strbuf_release(&git_submodule_dir); strbuf_release(&git_submodule_common_dir); - - return err; + return ret; } char *git_pathdup_submodule(const char *path, const char *fmt, ...) @@@ -1272,21 -1251,6 +1251,21 @@@ char *xdg_config_home(const char *filen return NULL; } +char *xdg_cache_home(const char *filename) +{ + const char *home, *cache_home; + + assert(filename); + cache_home = getenv("XDG_CACHE_HOME"); + if (cache_home && *cache_home) + return mkpathdup("%s/git/%s", cache_home, filename); + + home = getenv("HOME"); + if (home) + return mkpathdup("%s/.cache/git/%s", home, filename); + return NULL; +} + GIT_PATH_FUNC(git_path_cherry_pick_head, "CHERRY_PICK_HEAD") GIT_PATH_FUNC(git_path_revert_head, "REVERT_HEAD") GIT_PATH_FUNC(git_path_squash_msg, "SQUASH_MSG") diff --combined refs.c index c2472184a7,bad05ba861..d1e1b4399b --- a/refs.c +++ b/refs.c @@@ -9,6 -9,7 +9,7 @@@ #include "refs/refs-internal.h" #include "object.h" #include "tag.h" + #include "submodule.h" /* * List of all available backends @@@ -170,11 -171,23 +171,23 @@@ int refname_is_safe(const char *refname return 1; } + char *refs_resolve_refdup(struct ref_store *refs, + const char *refname, int resolve_flags, + unsigned char *sha1, int *flags) + { + const char *result; + + result = refs_resolve_ref_unsafe(refs, refname, resolve_flags, + sha1, flags); + return xstrdup_or_null(result); + } + char *resolve_refdup(const char *refname, int resolve_flags, unsigned char *sha1, int *flags) { - return xstrdup_or_null(resolve_ref_unsafe(refname, resolve_flags, - sha1, flags)); + return refs_resolve_refdup(get_main_ref_store(), + refname, resolve_flags, + sha1, flags); } /* The argument to filter_refs */ @@@ -184,13 -197,20 +197,20 @@@ struct ref_filter void *cb_data; }; - int read_ref_full(const char *refname, int resolve_flags, unsigned char *sha1, int *flags) + int refs_read_ref_full(struct ref_store *refs, const char *refname, + int resolve_flags, unsigned char *sha1, int *flags) { - if (resolve_ref_unsafe(refname, resolve_flags, sha1, flags)) + if (refs_resolve_ref_unsafe(refs, refname, resolve_flags, sha1, flags)) return 0; return -1; } + int read_ref_full(const char *refname, int resolve_flags, unsigned char *sha1, int *flags) + { + return refs_read_ref_full(get_main_ref_store(), refname, + resolve_flags, sha1, flags); + } + int read_ref(const char *refname, unsigned char *sha1) { return read_ref_full(refname, RESOLVE_REF_READING, sha1, NULL); @@@ -285,34 -305,52 +305,52 @@@ void warn_dangling_symrefs(FILE *fp, co for_each_rawref(warn_if_dangling_symref, &data); } + int refs_for_each_tag_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data) + { + return refs_for_each_ref_in(refs, "refs/tags/", fn, cb_data); + } + int for_each_tag_ref(each_ref_fn fn, void *cb_data) { - return for_each_ref_in("refs/tags/", fn, cb_data); + return refs_for_each_tag_ref(get_main_ref_store(), fn, cb_data); } int for_each_tag_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data) { - return for_each_ref_in_submodule(submodule, "refs/tags/", fn, cb_data); + return refs_for_each_tag_ref(get_submodule_ref_store(submodule), + fn, cb_data); + } + + int refs_for_each_branch_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data) + { + return refs_for_each_ref_in(refs, "refs/heads/", fn, cb_data); } int for_each_branch_ref(each_ref_fn fn, void *cb_data) { - return for_each_ref_in("refs/heads/", fn, cb_data); + return refs_for_each_branch_ref(get_main_ref_store(), fn, cb_data); } int for_each_branch_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data) { - return for_each_ref_in_submodule(submodule, "refs/heads/", fn, cb_data); + return refs_for_each_branch_ref(get_submodule_ref_store(submodule), + fn, cb_data); + } + + int refs_for_each_remote_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data) + { + return refs_for_each_ref_in(refs, "refs/remotes/", fn, cb_data); } int for_each_remote_ref(each_ref_fn fn, void *cb_data) { - return for_each_ref_in("refs/remotes/", fn, cb_data); + return refs_for_each_remote_ref(get_main_ref_store(), fn, cb_data); } int for_each_remote_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data) { - return for_each_ref_in_submodule(submodule, "refs/remotes/", fn, cb_data); + return refs_for_each_remote_ref(get_submodule_ref_store(submodule), + fn, cb_data); } int head_ref_namespaced(each_ref_fn fn, void *cb_data) @@@ -366,11 -404,11 +404,11 @@@ int for_each_glob_ref(each_ref_fn fn, c const char *prettify_refname(const char *name) { - return name + ( - starts_with(name, "refs/heads/") ? 11 : - starts_with(name, "refs/tags/") ? 10 : - starts_with(name, "refs/remotes/") ? 13 : - 0); + if (skip_prefix(name, "refs/heads/", &name) || + skip_prefix(name, "refs/tags/", &name) || + skip_prefix(name, "refs/remotes/", &name)) + ; /* nothing */ + return name; } static const char *ref_rev_parse_rules[] = { @@@ -429,31 -467,29 +467,31 @@@ int expand_ref(const char *str, int len { const char **p, *r; int refs_found = 0; + struct strbuf fullref = STRBUF_INIT; *ref = NULL; for (p = ref_rev_parse_rules; *p; p++) { - char fullref[PATH_MAX]; unsigned char sha1_from_ref[20]; unsigned char *this_result; int flag; this_result = refs_found ? sha1_from_ref : sha1; - mksnpath(fullref, sizeof(fullref), *p, len, str); - r = resolve_ref_unsafe(fullref, RESOLVE_REF_READING, + strbuf_reset(&fullref); + strbuf_addf(&fullref, *p, len, str); + r = resolve_ref_unsafe(fullref.buf, RESOLVE_REF_READING, this_result, &flag); if (r) { if (!refs_found++) *ref = xstrdup(r); if (!warn_ambiguous_refs) break; - } else if ((flag & REF_ISSYMREF) && strcmp(fullref, "HEAD")) { - warning("ignoring dangling symref %s.", fullref); - } else if ((flag & REF_ISBROKEN) && strchr(fullref, '/')) { - warning("ignoring broken ref %s.", fullref); + } else if ((flag & REF_ISSYMREF) && strcmp(fullref.buf, "HEAD")) { + warning("ignoring dangling symref %s.", fullref.buf); + } else if ((flag & REF_ISBROKEN) && strchr(fullref.buf, '/')) { + warning("ignoring broken ref %s.", fullref.buf); } } + strbuf_release(&fullref); return refs_found; } @@@ -462,22 -498,21 +500,22 @@@ int dwim_log(const char *str, int len, char *last_branch = substitute_branch_name(&str, &len); const char **p; int logs_found = 0; + struct strbuf path = STRBUF_INIT; *log = NULL; for (p = ref_rev_parse_rules; *p; p++) { unsigned char hash[20]; - char path[PATH_MAX]; const char *ref, *it; - mksnpath(path, sizeof(path), *p, len, str); - ref = resolve_ref_unsafe(path, RESOLVE_REF_READING, + strbuf_reset(&path); + strbuf_addf(&path, *p, len, str); + ref = resolve_ref_unsafe(path.buf, RESOLVE_REF_READING, hash, NULL); if (!ref) continue; - if (reflog_exists(path)) - it = path; - else if (strcmp(ref, path) && reflog_exists(ref)) + if (reflog_exists(path.buf)) + it = path.buf; + else if (strcmp(ref, path.buf) && reflog_exists(ref)) it = ref; else continue; @@@ -488,7 -523,6 +526,7 @@@ if (!warn_ambiguous_refs) break; } + strbuf_release(&path); free(last_branch); return logs_found; } @@@ -596,16 -630,20 +634,20 @@@ static int delete_pseudoref(const char return 0; } - int delete_ref(const char *msg, const char *refname, - const unsigned char *old_sha1, unsigned int flags) + int refs_delete_ref(struct ref_store *refs, const char *msg, + const char *refname, + const unsigned char *old_sha1, + unsigned int flags) { struct ref_transaction *transaction; struct strbuf err = STRBUF_INIT; - if (ref_type(refname) == REF_TYPE_PSEUDOREF) + if (ref_type(refname) == REF_TYPE_PSEUDOREF) { + assert(refs == get_main_ref_store()); return delete_pseudoref(refname, old_sha1); + } - transaction = ref_transaction_begin(&err); + transaction = ref_store_transaction_begin(refs, &err); if (!transaction || ref_transaction_delete(transaction, refname, old_sha1, flags, msg, &err) || @@@ -620,6 -658,13 +662,13 @@@ return 0; } + int delete_ref(const char *msg, const char *refname, + const unsigned char *old_sha1, unsigned int flags) + { + return refs_delete_ref(get_main_ref_store(), msg, refname, + old_sha1, flags); + } + int copy_reflog_msg(char *buf, const char *msg) { char *cp = buf; @@@ -779,11 -824,20 +828,20 @@@ int read_ref_at(const char *refname, un return 1; } - struct ref_transaction *ref_transaction_begin(struct strbuf *err) + struct ref_transaction *ref_store_transaction_begin(struct ref_store *refs, + struct strbuf *err) { + struct ref_transaction *tr; assert(err); - return xcalloc(1, sizeof(struct ref_transaction)); + tr = xcalloc(1, sizeof(struct ref_transaction)); + tr->ref_store = refs; + return tr; + } + + struct ref_transaction *ref_transaction_begin(struct strbuf *err) + { + return ref_store_transaction_begin(get_main_ref_store(), err); } void ref_transaction_free(struct ref_transaction *transaction) @@@ -900,18 -954,20 +958,20 @@@ int update_ref_oid(const char *msg, con old_oid ? old_oid->hash : NULL, flags, onerr); } - int update_ref(const char *msg, const char *refname, - const unsigned char *new_sha1, const unsigned char *old_sha1, - unsigned int flags, enum action_on_err onerr) + int refs_update_ref(struct ref_store *refs, const char *msg, + const char *refname, const unsigned char *new_sha1, + const unsigned char *old_sha1, unsigned int flags, + enum action_on_err onerr) { struct ref_transaction *t = NULL; struct strbuf err = STRBUF_INIT; int ret = 0; if (ref_type(refname) == REF_TYPE_PSEUDOREF) { + assert(refs == get_main_ref_store()); ret = write_pseudoref(refname, new_sha1, old_sha1, &err); } else { - t = ref_transaction_begin(&err); + t = ref_store_transaction_begin(refs, &err); if (!t || ref_transaction_update(t, refname, new_sha1, old_sha1, flags, msg, &err) || @@@ -942,13 -998,21 +1002,22 @@@ return 0; } + int update_ref(const char *msg, const char *refname, + const unsigned char *new_sha1, + const unsigned char *old_sha1, + unsigned int flags, enum action_on_err onerr) + { + return refs_update_ref(get_main_ref_store(), msg, refname, new_sha1, + old_sha1, flags, onerr); + } + char *shorten_unambiguous_ref(const char *refname, int strict) { int i; static char **scanf_fmts; static int nr_rules; char *short_name; + struct strbuf resolved_buf = STRBUF_INIT; if (!nr_rules) { /* @@@ -1007,6 -1071,7 +1076,6 @@@ */ for (j = 0; j < rules_to_fail; j++) { const char *rule = ref_rev_parse_rules[j]; - char refname[PATH_MAX]; /* skip matched rule */ if (i == j) @@@ -1017,10 -1082,9 +1086,10 @@@ * (with this previous rule) to a valid ref * read_ref() returns 0 on success */ - mksnpath(refname, sizeof(refname), - rule, short_name_len, short_name); - if (ref_exists(refname)) + strbuf_reset(&resolved_buf); + strbuf_addf(&resolved_buf, rule, + short_name_len, short_name); + if (ref_exists(resolved_buf.buf)) break; } @@@ -1028,13 -1092,10 +1097,13 @@@ * short name is non-ambiguous if all previous rules * haven't resolved to a valid ref */ - if (j == rules_to_fail) + if (j == rules_to_fail) { + strbuf_release(&resolved_buf); return short_name; + } } + strbuf_release(&resolved_buf); free(short_name); return xstrdup(refname); } @@@ -1127,14 -1188,17 +1196,17 @@@ const char *find_descendant_ref(const c return NULL; } - int rename_ref_available(const char *old_refname, const char *new_refname) + int refs_rename_ref_available(struct ref_store *refs, + const char *old_refname, + const char *new_refname) { struct string_list skip = STRING_LIST_INIT_NODUP; struct strbuf err = STRBUF_INIT; int ok; string_list_insert(&skip, old_refname); - ok = !verify_refname_available(new_refname, NULL, &skip, &err); + ok = !refs_verify_refname_available(refs, new_refname, + NULL, &skip, &err); if (!ok) error("%s", err.buf); @@@ -1175,10 -1239,9 +1247,9 @@@ int head_ref(each_ref_fn fn, void *cb_d * non-zero value, stop the iteration and return that value; * otherwise, return 0. */ - static int do_for_each_ref(const char *submodule, const char *prefix, + static int do_for_each_ref(struct ref_store *refs, const char *prefix, each_ref_fn fn, int trim, int flags, void *cb_data) { - struct ref_store *refs = get_ref_store(submodule); struct ref_iterator *iter; if (!refs) @@@ -1190,19 -1253,30 +1261,30 @@@ return do_for_each_ref_iterator(iter, fn, cb_data); } + int refs_for_each_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data) + { + return do_for_each_ref(refs, "", fn, 0, 0, cb_data); + } + int for_each_ref(each_ref_fn fn, void *cb_data) { - return do_for_each_ref(NULL, "", fn, 0, 0, cb_data); + return refs_for_each_ref(get_main_ref_store(), fn, cb_data); } int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data) { - return do_for_each_ref(submodule, "", fn, 0, 0, cb_data); + return refs_for_each_ref(get_submodule_ref_store(submodule), fn, cb_data); + } + + int refs_for_each_ref_in(struct ref_store *refs, const char *prefix, + each_ref_fn fn, void *cb_data) + { + return do_for_each_ref(refs, prefix, fn, strlen(prefix), 0, cb_data); } int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data) { - return do_for_each_ref(NULL, prefix, fn, strlen(prefix), 0, cb_data); + return refs_for_each_ref_in(get_main_ref_store(), prefix, fn, cb_data); } int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, unsigned int broken) @@@ -1211,19 -1285,23 +1293,23 @@@ if (broken) flag = DO_FOR_EACH_INCLUDE_BROKEN; - return do_for_each_ref(NULL, prefix, fn, 0, flag, cb_data); + return do_for_each_ref(get_main_ref_store(), + prefix, fn, 0, flag, cb_data); } int for_each_ref_in_submodule(const char *submodule, const char *prefix, - each_ref_fn fn, void *cb_data) + each_ref_fn fn, void *cb_data) { - return do_for_each_ref(submodule, prefix, fn, strlen(prefix), 0, cb_data); + return refs_for_each_ref_in(get_submodule_ref_store(submodule), + prefix, fn, cb_data); } int for_each_replace_ref(each_ref_fn fn, void *cb_data) { - return do_for_each_ref(NULL, git_replace_ref_base, fn, - strlen(git_replace_ref_base), 0, cb_data); + return do_for_each_ref(get_main_ref_store(), + git_replace_ref_base, fn, + strlen(git_replace_ref_base), + 0, cb_data); } int for_each_namespaced_ref(each_ref_fn fn, void *cb_data) @@@ -1231,19 -1309,25 +1317,25 @@@ 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); + ret = do_for_each_ref(get_main_ref_store(), + buf.buf, fn, 0, 0, cb_data); strbuf_release(&buf); return ret; } - int for_each_rawref(each_ref_fn fn, void *cb_data) + int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data) { - return do_for_each_ref(NULL, "", fn, 0, + return do_for_each_ref(refs, "", fn, 0, DO_FOR_EACH_INCLUDE_BROKEN, cb_data); } + int for_each_rawref(each_ref_fn fn, void *cb_data) + { + return refs_for_each_rawref(get_main_ref_store(), fn, cb_data); + } + /* This function needs to return a meaningful errno on failure */ - const char *resolve_ref_recursively(struct ref_store *refs, + const char *refs_resolve_ref_unsafe(struct ref_store *refs, const char *refname, int resolve_flags, unsigned char *sha1, int *flags) @@@ -1322,7 -1406,7 +1414,7 @@@ /* backend functions */ int refs_init_db(struct strbuf *err) { - struct ref_store *refs = get_ref_store(NULL); + struct ref_store *refs = get_main_ref_store(); return refs->be->init_db(refs, err); } @@@ -1330,7 -1414,7 +1422,7 @@@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags, unsigned char *sha1, int *flags) { - return resolve_ref_recursively(get_ref_store(NULL), refname, + return refs_resolve_ref_unsafe(get_main_ref_store(), refname, resolve_flags, sha1, flags); } @@@ -1351,16 -1435,16 +1443,16 @@@ int resolve_gitlink_ref(const char *sub /* We need to strip off one or more trailing slashes */ char *stripped = xmemdupz(submodule, len); - refs = get_ref_store(stripped); + refs = get_submodule_ref_store(stripped); free(stripped); } else { - refs = get_ref_store(submodule); + refs = get_submodule_ref_store(submodule); } if (!refs) return -1; - if (!resolve_ref_recursively(refs, refname, 0, sha1, &flags) || + if (!refs_resolve_ref_unsafe(refs, refname, 0, sha1, &flags) || is_null_sha1(sha1)) return -1; return 0; @@@ -1403,17 -1487,13 +1495,13 @@@ static struct ref_store *main_ref_store static struct hashmap submodule_ref_stores; /* - * Return the ref_store instance for the specified submodule (or the - * main repository if submodule is NULL). If that ref_store hasn't - * been initialized yet, return NULL. + * Return the ref_store instance for the specified submodule. If that + * ref_store hasn't been initialized yet, return NULL. */ - static struct ref_store *lookup_ref_store(const char *submodule) + static struct ref_store *lookup_submodule_ref_store(const char *submodule) { struct submodule_hash_entry *entry; - if (!submodule) - return main_ref_store; - if (!submodule_ref_stores.tablesize) /* It's initialized on demand in register_ref_store(). */ return NULL; @@@ -1423,34 -1503,12 +1511,12 @@@ return entry ? entry->refs : NULL; } - /* - * Register the specified ref_store to be the one that should be used - * for submodule (or the main repository if submodule is NULL). It is - * a fatal error to call this function twice for the same submodule. - */ - static void register_ref_store(struct ref_store *refs, const char *submodule) - { - if (!submodule) { - if (main_ref_store) - die("BUG: main_ref_store initialized twice"); - - main_ref_store = refs; - } else { - if (!submodule_ref_stores.tablesize) - hashmap_init(&submodule_ref_stores, submodule_hash_cmp, 0); - - if (hashmap_put(&submodule_ref_stores, - alloc_submodule_hash_entry(submodule, refs))) - die("BUG: ref_store for submodule '%s' initialized twice", - submodule); - } - } - /* * Create, record, and return a ref_store instance for the specified - * submodule (or the main repository if submodule is NULL). + * gitdir. */ - static struct ref_store *ref_store_init(const char *submodule) + static struct ref_store *ref_store_init(const char *gitdir, + unsigned int flags) { const char *be_name = "files"; struct ref_storage_be *be = find_ref_storage_backend(be_name); @@@ -1459,33 -1517,76 +1525,76 @@@ if (!be) die("BUG: reference backend %s is unknown", be_name); - refs = be->init(submodule); - register_ref_store(refs, submodule); + refs = be->init(gitdir, flags); return refs; } - struct ref_store *get_ref_store(const char *submodule) + struct ref_store *get_main_ref_store(void) + { + if (main_ref_store) + return main_ref_store; + + main_ref_store = ref_store_init(get_git_dir(), + (REF_STORE_READ | + REF_STORE_WRITE | + REF_STORE_ODB | + REF_STORE_MAIN)); + return main_ref_store; + } + + /* + * Register the specified ref_store to be the one that should be used + * for submodule. It is a fatal error to call this function twice for + * the same submodule. + */ + static void register_submodule_ref_store(struct ref_store *refs, + const char *submodule) + { + if (!submodule_ref_stores.tablesize) + hashmap_init(&submodule_ref_stores, submodule_hash_cmp, 0); + + if (hashmap_put(&submodule_ref_stores, + alloc_submodule_hash_entry(submodule, refs))) + die("BUG: ref_store for submodule '%s' initialized twice", + submodule); + } + + struct ref_store *get_submodule_ref_store(const char *submodule) { + struct strbuf submodule_sb = STRBUF_INIT; struct ref_store *refs; + int ret; if (!submodule || !*submodule) { - refs = lookup_ref_store(NULL); + /* + * FIXME: This case is ideally not allowed. But that + * can't happen until we clean up all the callers. + */ + return get_main_ref_store(); + } - if (!refs) - refs = ref_store_init(NULL); - } else { - refs = lookup_ref_store(submodule); + refs = lookup_submodule_ref_store(submodule); + if (refs) + return refs; - if (!refs) { - struct strbuf submodule_sb = STRBUF_INIT; + strbuf_addstr(&submodule_sb, submodule); + ret = is_nonbare_repository_dir(&submodule_sb); + strbuf_release(&submodule_sb); + if (!ret) + return NULL; - strbuf_addstr(&submodule_sb, submodule); - if (is_nonbare_repository_dir(&submodule_sb)) - refs = ref_store_init(submodule); - strbuf_release(&submodule_sb); - } + ret = submodule_to_gitdir(&submodule_sb, submodule); + if (ret) { + strbuf_release(&submodule_sb); + return NULL; } + /* assume that add_submodule_odb() has been called */ + refs = ref_store_init(submodule_sb.buf, + REF_STORE_READ | REF_STORE_ODB); + register_submodule_ref_store(refs, submodule); + + strbuf_release(&submodule_sb); return refs; } @@@ -1496,50 -1597,58 +1605,58 @@@ void base_ref_store_init(struct ref_sto } /* backend functions */ - int pack_refs(unsigned int flags) + int refs_pack_refs(struct ref_store *refs, unsigned int flags) { - struct ref_store *refs = get_ref_store(NULL); - return refs->be->pack_refs(refs, flags); } + int refs_peel_ref(struct ref_store *refs, const char *refname, + unsigned char *sha1) + { + return refs->be->peel_ref(refs, refname, sha1); + } + int peel_ref(const char *refname, unsigned char *sha1) { - struct ref_store *refs = get_ref_store(NULL); + return refs_peel_ref(get_main_ref_store(), refname, sha1); + } - return refs->be->peel_ref(refs, refname, sha1); + int refs_create_symref(struct ref_store *refs, + const char *ref_target, + const char *refs_heads_master, + const char *logmsg) + { + return refs->be->create_symref(refs, ref_target, + refs_heads_master, + logmsg); } int create_symref(const char *ref_target, const char *refs_heads_master, const char *logmsg) { - struct ref_store *refs = get_ref_store(NULL); - - return refs->be->create_symref(refs, ref_target, refs_heads_master, - logmsg); + return refs_create_symref(get_main_ref_store(), ref_target, + refs_heads_master, logmsg); } int ref_transaction_commit(struct ref_transaction *transaction, struct strbuf *err) { - struct ref_store *refs = get_ref_store(NULL); + struct ref_store *refs = transaction->ref_store; return refs->be->transaction_commit(refs, transaction, err); } - int verify_refname_available(const char *refname, - const struct string_list *extra, - const struct string_list *skip, - struct strbuf *err) + int refs_verify_refname_available(struct ref_store *refs, + const char *refname, + const struct string_list *extra, + const struct string_list *skip, + struct strbuf *err) { - struct ref_store *refs = get_ref_store(NULL); - return refs->be->verify_refname_available(refs, refname, extra, skip, err); } - int for_each_reflog(each_ref_fn fn, void *cb_data) + int refs_for_each_reflog(struct ref_store *refs, each_ref_fn fn, void *cb_data) { - struct ref_store *refs = get_ref_store(NULL); struct ref_iterator *iter; iter = refs->be->reflog_iterator_begin(refs); @@@ -1547,43 -1656,84 +1664,84 @@@ return do_for_each_ref_iterator(iter, fn, cb_data); } - int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, - void *cb_data) + int for_each_reflog(each_ref_fn fn, void *cb_data) { - struct ref_store *refs = get_ref_store(NULL); + return refs_for_each_reflog(get_main_ref_store(), fn, cb_data); + } + int refs_for_each_reflog_ent_reverse(struct ref_store *refs, + const char *refname, + each_reflog_ent_fn fn, + void *cb_data) + { return refs->be->for_each_reflog_ent_reverse(refs, refname, fn, cb_data); } + int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, + void *cb_data) + { + return refs_for_each_reflog_ent_reverse(get_main_ref_store(), + refname, fn, cb_data); + } + + int refs_for_each_reflog_ent(struct ref_store *refs, const char *refname, + each_reflog_ent_fn fn, void *cb_data) + { + return refs->be->for_each_reflog_ent(refs, refname, fn, cb_data); + } + int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_data) { - struct ref_store *refs = get_ref_store(NULL); + return refs_for_each_reflog_ent(get_main_ref_store(), refname, + fn, cb_data); + } - return refs->be->for_each_reflog_ent(refs, refname, fn, cb_data); + int refs_reflog_exists(struct ref_store *refs, const char *refname) + { + return refs->be->reflog_exists(refs, refname); } int reflog_exists(const char *refname) { - struct ref_store *refs = get_ref_store(NULL); + return refs_reflog_exists(get_main_ref_store(), refname); + } - return refs->be->reflog_exists(refs, refname); + int refs_create_reflog(struct ref_store *refs, const char *refname, + int force_create, struct strbuf *err) + { + return refs->be->create_reflog(refs, refname, force_create, err); } int safe_create_reflog(const char *refname, int force_create, struct strbuf *err) { - struct ref_store *refs = get_ref_store(NULL); + return refs_create_reflog(get_main_ref_store(), refname, + force_create, err); + } - return refs->be->create_reflog(refs, refname, force_create, err); + int refs_delete_reflog(struct ref_store *refs, const char *refname) + { + return refs->be->delete_reflog(refs, refname); } int delete_reflog(const char *refname) { - struct ref_store *refs = get_ref_store(NULL); + return refs_delete_reflog(get_main_ref_store(), refname); + } - return refs->be->delete_reflog(refs, refname); + int refs_reflog_expire(struct ref_store *refs, + const char *refname, const unsigned char *sha1, + unsigned int flags, + reflog_expiry_prepare_fn prepare_fn, + reflog_expiry_should_prune_fn should_prune_fn, + reflog_expiry_cleanup_fn cleanup_fn, + void *policy_cb_data) + { + return refs->be->reflog_expire(refs, refname, sha1, flags, + prepare_fn, should_prune_fn, + cleanup_fn, policy_cb_data); } int reflog_expire(const char *refname, const unsigned char *sha1, @@@ -1593,31 -1743,38 +1751,38 @@@ reflog_expiry_cleanup_fn cleanup_fn, void *policy_cb_data) { - struct ref_store *refs = get_ref_store(NULL); - - return refs->be->reflog_expire(refs, refname, sha1, flags, - prepare_fn, should_prune_fn, - cleanup_fn, policy_cb_data); + return refs_reflog_expire(get_main_ref_store(), + refname, sha1, flags, + prepare_fn, should_prune_fn, + cleanup_fn, policy_cb_data); } int initial_ref_transaction_commit(struct ref_transaction *transaction, struct strbuf *err) { - struct ref_store *refs = get_ref_store(NULL); + struct ref_store *refs = transaction->ref_store; return refs->be->initial_transaction_commit(refs, transaction, err); } - int delete_refs(struct string_list *refnames, unsigned int flags) + int refs_delete_refs(struct ref_store *refs, struct string_list *refnames, + unsigned int flags) { - struct ref_store *refs = get_ref_store(NULL); - return refs->be->delete_refs(refs, refnames, flags); } - int rename_ref(const char *oldref, const char *newref, const char *logmsg) + int delete_refs(struct string_list *refnames, unsigned int flags) { - struct ref_store *refs = get_ref_store(NULL); + return refs_delete_refs(get_main_ref_store(), refnames, flags); + } + int refs_rename_ref(struct ref_store *refs, const char *oldref, + const char *newref, const char *logmsg) + { return refs->be->rename_ref(refs, oldref, newref, logmsg); } + + int rename_ref(const char *oldref, const char *newref, const char *logmsg) + { + return refs_rename_ref(get_main_ref_store(), oldref, newref, logmsg); + } diff --combined submodule.c index 620504d325,a31f68812c..5615d7392e --- a/submodule.c +++ b/submodule.c @@@ -14,16 -14,14 +14,16 @@@ #include "blob.h" #include "thread-utils.h" #include "quote.h" +#include "remote.h" #include "worktree.h" static int config_fetch_recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND; +static int config_update_recurse_submodules = RECURSE_SUBMODULES_DEFAULT; static int parallel_jobs = 1; static struct string_list changed_submodule_paths = STRING_LIST_INIT_NODUP; static int initialized_fetch_ref_tips; -static struct sha1_array ref_tips_before_fetch; -static struct sha1_array ref_tips_after_fetch; +static struct oid_array ref_tips_before_fetch; +static struct oid_array ref_tips_after_fetch; /* * The following flag is set if the .gitmodules file is unmerged. We then @@@ -214,68 -212,37 +214,68 @@@ void gitmodules_config_sha1(const unsig } /* + * NEEDSWORK: With the addition of different configuration options to determine + * if a submodule is of interests, the validity of this function's name comes + * into question. Once the dust has settled and more concrete terminology is + * decided upon, come up with a more proper name for this function. One + * potential candidate could be 'is_submodule_active()'. + * * Determine if a submodule has been initialized at a given 'path' */ int is_submodule_initialized(const char *path) { int ret = 0; - const struct submodule *module = NULL; + char *key = NULL; + char *value = NULL; + const struct string_list *sl; + const struct submodule *module = submodule_from_path(null_sha1, path); - module = submodule_from_path(null_sha1, path); + /* early return if there isn't a path->module mapping */ + if (!module) + return 0; - if (module) { - char *key = xstrfmt("submodule.%s.url", module->name); - char *value = NULL; + /* submodule..active is set */ + key = xstrfmt("submodule.%s.active", module->name); + if (!git_config_get_bool(key, &ret)) { + free(key); + return ret; + } + free(key); - ret = !git_config_get_string(key, &value); + /* submodule.active is set */ + sl = git_config_get_value_multi("submodule.active"); + if (sl) { + struct pathspec ps; + struct argv_array args = ARGV_ARRAY_INIT; + const struct string_list_item *item; - free(value); - free(key); + for_each_string_list_item(item, sl) { + argv_array_push(&args, item->string); + } + + parse_pathspec(&ps, 0, 0, NULL, args.argv); + ret = match_pathspec(&ps, path, strlen(path), 0, NULL, 1); + + argv_array_clear(&args); + clear_pathspec(&ps); + return ret; } + /* fallback to checking if the URL is set */ + key = xstrfmt("submodule.%s.url", module->name); + ret = !git_config_get_string(key, &value); + + free(value); + free(key); return ret; } -/* - * Determine if a submodule has been populated at a given 'path' - */ -int is_submodule_populated(const char *path) +int is_submodule_populated_gently(const char *path, int *return_error_code) { int ret = 0; char *gitdir = xstrfmt("%s/.git", path); - if (resolve_gitdir(gitdir)) + if (resolve_gitdir_gently(gitdir, return_error_code)) ret = 1; free(gitdir); @@@ -391,23 -358,6 +391,23 @@@ static void print_submodule_summary(str strbuf_release(&sb); } +static void prepare_submodule_repo_env_no_git_dir(struct argv_array *out) +{ + const char * const *var; + + for (var = local_repo_env; *var; var++) { + if (strcmp(*var, CONFIG_DATA_ENVIRONMENT)) + argv_array_push(out, *var); + } +} + +void prepare_submodule_repo_env(struct argv_array *out) +{ + prepare_submodule_repo_env_no_git_dir(out); + argv_array_pushf(out, "%s=%s", GIT_DIR_ENVIRONMENT, + DEFAULT_GIT_DIR_ENVIRONMENT); +} + /* Helper function to display the submodule header line prior to the full * summary output. If it can locate the submodule objects directory it will * attempt to lookup both the left and right commits and put them into the @@@ -577,7 -527,6 +577,7 @@@ void show_submodule_inline_diff(FILE *f if (!(dirty_submodule & DIRTY_SUBMODULE_MODIFIED)) argv_array_push(&cp.args, oid_to_hex(new)); + prepare_submodule_repo_env(&cp.env_array); if (run_command(&cp)) fprintf(f, "(diff failed)\n"); @@@ -596,62 -545,41 +596,62 @@@ void set_config_fetch_recurse_submodule config_fetch_recurse_submodules = value; } +void set_config_update_recurse_submodules(int value) +{ + config_update_recurse_submodules = value; +} + +int should_update_submodules(void) +{ + return config_update_recurse_submodules == RECURSE_SUBMODULES_ON; +} + +const struct submodule *submodule_from_ce(const struct cache_entry *ce) +{ + if (!S_ISGITLINK(ce->ce_mode)) + return NULL; + + if (!should_update_submodules()) + return NULL; + + return submodule_from_path(null_sha1, ce->name); +} + static int has_remote(const char *refname, const struct object_id *oid, int flags, void *cb_data) { return 1; } -static int append_sha1_to_argv(const unsigned char sha1[20], void *data) +static int append_oid_to_argv(const struct object_id *oid, void *data) { struct argv_array *argv = data; - argv_array_push(argv, sha1_to_hex(sha1)); + argv_array_push(argv, oid_to_hex(oid)); return 0; } -static int check_has_commit(const unsigned char sha1[20], void *data) +static int check_has_commit(const struct object_id *oid, void *data) { int *has_commit = data; - if (!lookup_commit_reference(sha1)) + if (!lookup_commit_reference(oid->hash)) *has_commit = 0; return 0; } -static int submodule_has_commits(const char *path, struct sha1_array *commits) +static int submodule_has_commits(const char *path, struct oid_array *commits) { int has_commit = 1; if (add_submodule_odb(path)) return 0; - sha1_array_for_each_unique(commits, check_has_commit, &has_commit); + oid_array_for_each_unique(commits, check_has_commit, &has_commit); return has_commit; } -static int submodule_needs_pushing(const char *path, struct sha1_array *commits) +static int submodule_needs_pushing(const char *path, struct oid_array *commits) { if (!submodule_has_commits(path, commits)) /* @@@ -673,7 -601,7 +673,7 @@@ int needs_pushing = 0; argv_array_push(&cp.args, "rev-list"); - sha1_array_for_each_unique(commits, append_sha1_to_argv, &cp.args); + oid_array_for_each_unique(commits, append_oid_to_argv, &cp.args); argv_array_pushl(&cp.args, "--not", "--remotes", "-n", "1" , NULL); prepare_submodule_repo_env(&cp.env_array); @@@ -695,18 -623,18 +695,18 @@@ return 0; } -static struct sha1_array *submodule_commits(struct string_list *submodules, +static struct oid_array *submodule_commits(struct string_list *submodules, const char *path) { struct string_list_item *item; item = string_list_insert(submodules, path); if (item->util) - return (struct sha1_array *) item->util; + return (struct oid_array *) item->util; - /* NEEDSWORK: should we have sha1_array_init()? */ - item->util = xcalloc(1, sizeof(struct sha1_array)); - return (struct sha1_array *) item->util; + /* NEEDSWORK: should we have oid_array_init()? */ + item->util = xcalloc(1, sizeof(struct oid_array)); + return (struct oid_array *) item->util; } static void collect_submodules_from_diff(struct diff_queue_struct *q, @@@ -718,11 -646,11 +718,11 @@@ for (i = 0; i < q->nr; i++) { struct diff_filepair *p = q->queue[i]; - struct sha1_array *commits; + struct oid_array *commits; if (!S_ISGITLINK(p->two->mode)) continue; commits = submodule_commits(submodules, p->two->path); - sha1_array_append(commits, p->two->oid.hash); + oid_array_append(commits, &p->two->oid); } } @@@ -742,11 -670,11 +742,11 @@@ static void free_submodules_sha1s(struc { struct string_list_item *item; for_each_string_list_item(item, submodules) - sha1_array_clear((struct sha1_array *) item->util); + oid_array_clear((struct oid_array *) item->util); string_list_clear(submodules, 1); } -int find_unpushed_submodules(struct sha1_array *commits, +int find_unpushed_submodules(struct oid_array *commits, const char *remotes_name, struct string_list *needs_pushing) { struct rev_info rev; @@@ -759,7 -687,7 +759,7 @@@ /* argv.argv[0] will be ignored by setup_revisions */ argv_array_push(&argv, "find_unpushed_submodules"); - sha1_array_for_each_unique(commits, append_sha1_to_argv, &argv); + oid_array_for_each_unique(commits, append_oid_to_argv, &argv); argv_array_push(&argv, "--not"); argv_array_pushf(&argv, "--remotes=%s", remotes_name); @@@ -774,7 -702,7 +774,7 @@@ argv_array_clear(&argv); for_each_string_list_item(submodule, &submodules) { - struct sha1_array *commits = (struct sha1_array *) submodule->util; + struct oid_array *commits = (struct oid_array *) submodule->util; if (submodule_needs_pushing(submodule->string, commits)) string_list_insert(needs_pushing, submodule->string); @@@ -784,11 -712,7 +784,11 @@@ return needs_pushing->nr; } -static int push_submodule(const char *path, int dry_run) +static int push_submodule(const char *path, + const struct remote *remote, + const char **refspec, int refspec_nr, + const struct string_list *push_options, + int dry_run) { if (add_submodule_odb(path)) return 1; @@@ -799,20 -723,6 +799,20 @@@ if (dry_run) argv_array_push(&cp.args, "--dry-run"); + if (push_options && push_options->nr) { + const struct string_list_item *item; + for_each_string_list_item(item, push_options) + argv_array_pushf(&cp.args, "--push-option=%s", + item->string); + } + + if (remote->origin != REMOTE_UNCONFIGURED) { + int i; + argv_array_push(&cp.args, remote->name); + for (i = 0; i < refspec_nr; i++) + argv_array_push(&cp.args, refspec[i]); + } + prepare_submodule_repo_env(&cp.env_array); cp.git_cmd = 1; cp.no_stdin = 1; @@@ -825,67 -735,20 +825,67 @@@ return 1; } -int push_unpushed_submodules(struct sha1_array *commits, - const char *remotes_name, +/* + * Perform a check in the submodule to see if the remote and refspec work. + * Die if the submodule can't be pushed. + */ +static void submodule_push_check(const char *path, const struct remote *remote, + const char **refspec, int refspec_nr) +{ + struct child_process cp = CHILD_PROCESS_INIT; + int i; + + argv_array_push(&cp.args, "submodule--helper"); + argv_array_push(&cp.args, "push-check"); + argv_array_push(&cp.args, remote->name); + + for (i = 0; i < refspec_nr; i++) + argv_array_push(&cp.args, refspec[i]); + + prepare_submodule_repo_env(&cp.env_array); + cp.git_cmd = 1; + cp.no_stdin = 1; + cp.no_stdout = 1; + cp.dir = path; + + /* + * Simply indicate if 'submodule--helper push-check' failed. + * More detailed error information will be provided by the + * child process. + */ + if (run_command(&cp)) + die("process for submodule '%s' failed", path); +} + +int push_unpushed_submodules(struct oid_array *commits, + const struct remote *remote, + const char **refspec, int refspec_nr, + const struct string_list *push_options, int dry_run) { int i, ret = 1; struct string_list needs_pushing = STRING_LIST_INIT_DUP; - if (!find_unpushed_submodules(commits, remotes_name, &needs_pushing)) + if (!find_unpushed_submodules(commits, remote->name, &needs_pushing)) return 1; + /* + * Verify that the remote and refspec can be propagated to all + * submodules. This check can be skipped if the remote and refspec + * won't be propagated due to the remote being unconfigured (e.g. a URL + * instead of a remote name). + */ + if (remote->origin != REMOTE_UNCONFIGURED) + for (i = 0; i < needs_pushing.nr; i++) + submodule_push_check(needs_pushing.items[i].string, + remote, refspec, refspec_nr); + + /* Actually push the submodules */ for (i = 0; i < needs_pushing.nr; i++) { const char *path = needs_pushing.items[i].string; fprintf(stderr, "Pushing submodule '%s'\n", path); - if (!push_submodule(path, dry_run)) { + if (!push_submodule(path, remote, refspec, refspec_nr, + push_options, dry_run)) { fprintf(stderr, "Unable to push submodule '%s'\n", path); ret = 0; } @@@ -954,23 -817,23 +954,23 @@@ static void submodule_collect_changed_c static int add_sha1_to_array(const char *ref, const struct object_id *oid, int flags, void *data) { - sha1_array_append(data, oid->hash); + oid_array_append(data, oid); return 0; } -void check_for_new_submodule_commits(unsigned char new_sha1[20]) +void check_for_new_submodule_commits(struct object_id *oid) { if (!initialized_fetch_ref_tips) { for_each_ref(add_sha1_to_array, &ref_tips_before_fetch); initialized_fetch_ref_tips = 1; } - sha1_array_append(&ref_tips_after_fetch, new_sha1); + oid_array_append(&ref_tips_after_fetch, oid); } -static int add_sha1_to_argv(const unsigned char sha1[20], void *data) +static int add_oid_to_argv(const struct object_id *oid, void *data) { - argv_array_push(data, sha1_to_hex(sha1)); + argv_array_push(data, oid_to_hex(oid)); return 0; } @@@ -986,11 -849,11 +986,11 @@@ static void calculate_changed_submodule init_revisions(&rev, NULL); argv_array_push(&argv, "--"); /* argv[0] program name */ - sha1_array_for_each_unique(&ref_tips_after_fetch, - add_sha1_to_argv, &argv); + oid_array_for_each_unique(&ref_tips_after_fetch, + add_oid_to_argv, &argv); argv_array_push(&argv, "--not"); - sha1_array_for_each_unique(&ref_tips_before_fetch, - add_sha1_to_argv, &argv); + oid_array_for_each_unique(&ref_tips_before_fetch, + add_oid_to_argv, &argv); setup_revisions(argv.argc, argv.argv, &rev, NULL); if (prepare_revision_walk(&rev)) die("revision walk setup failed"); @@@ -1016,8 -879,8 +1016,8 @@@ } argv_array_clear(&argv); - sha1_array_clear(&ref_tips_before_fetch); - sha1_array_clear(&ref_tips_after_fetch); + oid_array_clear(&ref_tips_before_fetch); + oid_array_clear(&ref_tips_after_fetch); initialized_fetch_ref_tips = 0; } @@@ -1178,78 -1041,67 +1178,78 @@@ out unsigned is_submodule_modified(const char *path, int ignore_untracked) { - ssize_t len; struct child_process cp = CHILD_PROCESS_INIT; - const char *argv[] = { - "status", - "--porcelain", - NULL, - NULL, - }; struct strbuf buf = STRBUF_INIT; + FILE *fp; unsigned dirty_submodule = 0; - const char *line, *next_line; const char *git_dir; + int ignore_cp_exit_code = 0; strbuf_addf(&buf, "%s/.git", path); git_dir = read_gitfile(buf.buf); if (!git_dir) git_dir = buf.buf; - if (!is_directory(git_dir)) { + if (!is_git_directory(git_dir)) { + if (is_directory(git_dir)) + die(_("'%s' not recognized as a git repository"), git_dir); strbuf_release(&buf); /* The submodule is not checked out, so it is not modified */ return 0; - } strbuf_reset(&buf); + argv_array_pushl(&cp.args, "status", "--porcelain=2", NULL); if (ignore_untracked) - argv[2] = "-uno"; + argv_array_push(&cp.args, "-uno"); - cp.argv = argv; prepare_submodule_repo_env(&cp.env_array); cp.git_cmd = 1; cp.no_stdin = 1; cp.out = -1; cp.dir = path; if (start_command(&cp)) - die("Could not run 'git status --porcelain' in submodule %s", path); + die("Could not run 'git status --porcelain=2' in submodule %s", path); - len = strbuf_read(&buf, cp.out, 1024); - line = buf.buf; - while (len > 2) { - if ((line[0] == '?') && (line[1] == '?')) { + fp = xfdopen(cp.out, "r"); + while (strbuf_getwholeline(&buf, fp, '\n') != EOF) { + /* regular untracked files */ + if (buf.buf[0] == '?') dirty_submodule |= DIRTY_SUBMODULE_UNTRACKED; - if (dirty_submodule & DIRTY_SUBMODULE_MODIFIED) - break; - } else { - dirty_submodule |= DIRTY_SUBMODULE_MODIFIED; - if (ignore_untracked || - (dirty_submodule & DIRTY_SUBMODULE_UNTRACKED)) - break; + + if (buf.buf[0] == 'u' || + buf.buf[0] == '1' || + buf.buf[0] == '2') { + /* T = line type, XY = status, SSSS = submodule state */ + if (buf.len < strlen("T XY SSSS")) + die("BUG: invalid status --porcelain=2 line %s", + buf.buf); + + if (buf.buf[5] == 'S' && buf.buf[8] == 'U') + /* nested untracked file */ + dirty_submodule |= DIRTY_SUBMODULE_UNTRACKED; + + if (buf.buf[0] == 'u' || + buf.buf[0] == '2' || + memcmp(buf.buf + 5, "S..U", 4)) + /* other change */ + dirty_submodule |= DIRTY_SUBMODULE_MODIFIED; } - next_line = strchr(line, '\n'); - if (!next_line) + + if ((dirty_submodule & DIRTY_SUBMODULE_MODIFIED) && + ((dirty_submodule & DIRTY_SUBMODULE_UNTRACKED) || + ignore_untracked)) { + /* + * We're not interested in any further information from + * the child any more, neither output nor its exit code. + */ + ignore_cp_exit_code = 1; break; - next_line++; - len -= (next_line - line); - line = next_line; + } } - close(cp.out); + fclose(fp); - if (finish_command(&cp)) - die("'git status --porcelain' failed in submodule %s", path); + if (finish_command(&cp) && !ignore_cp_exit_code) + die("'git status --porcelain=2' failed in submodule %s", path); strbuf_release(&buf); return dirty_submodule; @@@ -1329,7 -1181,7 +1329,7 @@@ int bad_to_remove_submodule(const char cp.dir = path; if (start_command(&cp)) { if (flags & SUBMODULE_REMOVAL_DIE_ON_ERROR) - die(_("could not start 'git status in submodule '%s'"), + die(_("could not start 'git status' in submodule '%s'"), path); ret = -1; goto out; @@@ -1342,7 -1194,7 +1342,7 @@@ if (finish_command(&cp)) { if (flags & SUBMODULE_REMOVAL_DIE_ON_ERROR) - die(_("could not run 'git status in submodule '%s'"), + die(_("could not run 'git status' in submodule '%s'"), path); ret = -1; } @@@ -1351,151 -1203,6 +1351,151 @@@ out return ret; } +static const char *get_super_prefix_or_empty(void) +{ + const char *s = get_super_prefix(); + if (!s) + s = ""; + return s; +} + +static int submodule_has_dirty_index(const struct submodule *sub) +{ + struct child_process cp = CHILD_PROCESS_INIT; + + prepare_submodule_repo_env_no_git_dir(&cp.env_array); + + cp.git_cmd = 1; + argv_array_pushl(&cp.args, "diff-index", "--quiet", + "--cached", "HEAD", NULL); + cp.no_stdin = 1; + cp.no_stdout = 1; + cp.dir = sub->path; + if (start_command(&cp)) + die("could not recurse into submodule '%s'", sub->path); + + return finish_command(&cp); +} + +static void submodule_reset_index(const char *path) +{ + struct child_process cp = CHILD_PROCESS_INIT; + prepare_submodule_repo_env_no_git_dir(&cp.env_array); + + cp.git_cmd = 1; + cp.no_stdin = 1; + cp.dir = path; + + argv_array_pushf(&cp.args, "--super-prefix=%s%s/", + get_super_prefix_or_empty(), path); + argv_array_pushl(&cp.args, "read-tree", "-u", "--reset", NULL); + + argv_array_push(&cp.args, EMPTY_TREE_SHA1_HEX); + + if (run_command(&cp)) + die("could not reset submodule index"); +} + +/** + * Moves a submodule at a given path from a given head to another new head. + * For edge cases (a submodule coming into existence or removing a submodule) + * pass NULL for old or new respectively. + */ +int submodule_move_head(const char *path, + const char *old, + const char *new, + unsigned flags) +{ + int ret = 0; + struct child_process cp = CHILD_PROCESS_INIT; + const struct submodule *sub; + + sub = submodule_from_path(null_sha1, path); + + if (!sub) + die("BUG: could not get submodule information for '%s'", path); + + if (old && !(flags & SUBMODULE_MOVE_HEAD_FORCE)) { + /* Check if the submodule has a dirty index. */ + if (submodule_has_dirty_index(sub)) + return error(_("submodule '%s' has dirty index"), path); + } + + if (!(flags & SUBMODULE_MOVE_HEAD_DRY_RUN)) { + if (old) { + if (!submodule_uses_gitfile(path)) + absorb_git_dir_into_superproject("", path, + ABSORB_GITDIR_RECURSE_SUBMODULES); + } else { + struct strbuf sb = STRBUF_INIT; + strbuf_addf(&sb, "%s/modules/%s", + get_git_common_dir(), sub->name); + connect_work_tree_and_git_dir(path, sb.buf); + strbuf_release(&sb); + + /* make sure the index is clean as well */ + submodule_reset_index(path); + } + } + + prepare_submodule_repo_env_no_git_dir(&cp.env_array); + + cp.git_cmd = 1; + cp.no_stdin = 1; + cp.dir = path; + + argv_array_pushf(&cp.args, "--super-prefix=%s%s/", + get_super_prefix_or_empty(), path); + argv_array_pushl(&cp.args, "read-tree", NULL); + + if (flags & SUBMODULE_MOVE_HEAD_DRY_RUN) + argv_array_push(&cp.args, "-n"); + else + argv_array_push(&cp.args, "-u"); + + if (flags & SUBMODULE_MOVE_HEAD_FORCE) + argv_array_push(&cp.args, "--reset"); + else + argv_array_push(&cp.args, "-m"); + + argv_array_push(&cp.args, old ? old : EMPTY_TREE_SHA1_HEX); + argv_array_push(&cp.args, new ? new : EMPTY_TREE_SHA1_HEX); + + if (run_command(&cp)) { + ret = -1; + goto out; + } + + if (!(flags & SUBMODULE_MOVE_HEAD_DRY_RUN)) { + if (new) { + struct child_process cp1 = CHILD_PROCESS_INIT; + /* also set the HEAD accordingly */ + cp1.git_cmd = 1; + cp1.no_stdin = 1; + cp1.dir = path; + + argv_array_pushl(&cp1.args, "update-ref", "HEAD", + new ? new : EMPTY_TREE_SHA1_HEX, NULL); + + if (run_command(&cp1)) { + ret = -1; + goto out; + } + } else { + struct strbuf sb = STRBUF_INIT; + + strbuf_addf(&sb, "%s/.git", path); + unlink_or_warn(sb.buf); + strbuf_release(&sb); + + if (is_empty_dir(path)) + rmdir_or_warn(path); + } + } +out: + return ret; +} + static int find_first_merges(struct object_array *result, const char *path, struct commit *a, struct commit *b) { @@@ -1514,7 -1221,7 +1514,7 @@@ memset(&rev_opts, 0, sizeof(rev_opts)); /* get all revisions that merge commit a */ - snprintf(merged_revision, sizeof(merged_revision), "^%s", + xsnprintf(merged_revision, sizeof(merged_revision), "^%s", oid_to_hex(&a->object.oid)); init_revisions(&revs, NULL); rev_opts.submodule = path; @@@ -1664,6 -1371,18 +1664,6 @@@ int parallel_submodules(void return parallel_jobs; } -void prepare_submodule_repo_env(struct argv_array *out) -{ - const char * const *var; - - for (var = local_repo_env; *var; var++) { - if (strcmp(*var, CONFIG_DATA_ENVIRONMENT)) - argv_array_push(out, *var); - } - argv_array_pushf(out, "%s=%s", GIT_DIR_ENVIRONMENT, - DEFAULT_GIT_DIR_ENVIRONMENT); -} - /* * Embeds a single submodules git directory into the superprojects git dir, * non recursively. @@@ -1695,8 -1414,11 +1695,8 @@@ static void relocate_single_git_dir_int die(_("could not create directory '%s'"), new_git_dir); real_new_git_dir = real_pathdup(new_git_dir, 1); - if (!prefix) - prefix = get_super_prefix(); - fprintf(stderr, _("Migrating git directory of '%s%s' from\n'%s' to\n'%s'\n"), - prefix ? prefix : "", path, + get_super_prefix_or_empty(), path, real_old_git_dir, real_new_git_dir); relocate_gitdir(path, real_old_git_dir, real_new_git_dir); @@@ -1723,6 -1445,8 +1723,6 @@@ void absorb_git_dir_into_superproject(c /* Not populated? */ if (!sub_git_dir) { - char *real_new_git_dir; - const char *new_git_dir; const struct submodule *sub; if (err_code == READ_GITFILE_ERR_STAT_FAILED) { @@@ -1745,8 -1469,13 +1745,8 @@@ sub = submodule_from_path(null_sha1, path); if (!sub) die(_("could not lookup name for submodule '%s'"), path); - new_git_dir = git_path("modules/%s", sub->name); - if (safe_create_leading_directories_const(new_git_dir) < 0) - die(_("could not create directory '%s'"), new_git_dir); - real_new_git_dir = real_pathdup(new_git_dir, 1); - connect_work_tree_and_git_dir(path, real_new_git_dir); - - free(real_new_git_dir); + connect_work_tree_and_git_dir(path, + git_path("modules/%s", sub->name)); } else { /* Is it already absorbed into the superprojects git dir? */ char *real_sub_git_dir = real_pathdup(sub_git_dir, 1); @@@ -1767,7 -1496,8 +1767,7 @@@ if (flags & ~ABSORB_GITDIR_RECURSE_SUBMODULES) die("BUG: we don't know how to pass the flags down?"); - if (get_super_prefix()) - strbuf_addstr(&sb, get_super_prefix()); + strbuf_addstr(&sb, get_super_prefix_or_empty()); strbuf_addstr(&sb, path); strbuf_addch(&sb, '/'); @@@ -1866,3 -1596,34 +1866,34 @@@ const char *get_superproject_working_tr return ret; } + + int submodule_to_gitdir(struct strbuf *buf, const char *submodule) + { + const struct submodule *sub; + const char *git_dir; + int ret = 0; + + strbuf_reset(buf); + strbuf_addstr(buf, submodule); + strbuf_complete(buf, '/'); + strbuf_addstr(buf, ".git"); + + git_dir = read_gitfile(buf->buf); + if (git_dir) { + strbuf_reset(buf); + strbuf_addstr(buf, git_dir); + } + if (!is_git_directory(buf->buf)) { + gitmodules_config(); + sub = submodule_from_path(null_sha1, submodule); + if (!sub) { + ret = -1; + goto cleanup; + } + strbuf_reset(buf); + strbuf_git_path(buf, "%s/%s", "modules", sub->name); + } + + cleanup: + return ret; + } diff --combined submodule.h index 486371d2c3,fce2fb64d2..1277480add --- a/submodule.h +++ b/submodule.h @@@ -3,8 -3,7 +3,8 @@@ struct diff_options; struct argv_array; -struct sha1_array; +struct oid_array; +struct remote; enum { RECURSE_SUBMODULES_ONLY = -5, @@@ -42,13 -41,7 +42,13 @@@ extern int submodule_config(const char extern void gitmodules_config(void); extern void gitmodules_config_sha1(const unsigned char *commit_sha1); extern int is_submodule_initialized(const char *path); -extern int is_submodule_populated(const char *path); +/* + * Determine if a submodule has been populated at a given 'path' by checking if + * the /.git resolves to a valid git repository. + * If return_error_code is NULL, die on error. + * Otherwise the return error code is the same as of resolve_gitdir_gently. + */ +extern int is_submodule_populated_gently(const char *path, int *return_error_code); extern int parse_submodule_update_strategy(const char *value, struct submodule_update_strategy *dst); extern const char *submodule_strategy_to_string(const struct submodule_update_strategy *s); @@@ -65,15 -58,7 +65,15 @@@ extern void show_submodule_inline_diff( const char *del, const char *add, const char *reset, const struct diff_options *opt); extern void set_config_fetch_recurse_submodules(int value); -extern void check_for_new_submodule_commits(unsigned char new_sha1[20]); +extern void set_config_update_recurse_submodules(int value); +/* Check if we want to update any submodule.*/ +extern int should_update_submodules(void); +/* + * Returns the submodule struct if the given ce entry is a submodule + * and it should be updated. Returns NULL otherwise. + */ +extern const struct submodule *submodule_from_ce(const struct cache_entry *ce); +extern void check_for_new_submodule_commits(struct object_id *oid); extern int fetch_populated_submodules(const struct argv_array *options, const char *prefix, int command_line_option, int quiet, int max_parallel_jobs); @@@ -88,24 -73,21 +88,30 @@@ extern int merge_submodule(unsigned cha const unsigned char base[20], const unsigned char a[20], const unsigned char b[20], int search); -extern int find_unpushed_submodules(struct sha1_array *commits, +extern int find_unpushed_submodules(struct oid_array *commits, const char *remotes_name, struct string_list *needs_pushing); -extern int push_unpushed_submodules(struct sha1_array *commits, - const char *remotes_name, +extern int push_unpushed_submodules(struct oid_array *commits, + const struct remote *remote, + const char **refspec, int refspec_nr, + const struct string_list *push_options, int dry_run); extern void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir); extern int parallel_submodules(void); + /* + * Given a submodule path (as in the index), return the repository + * path of that submodule in 'buf'. Return -1 on error or when the + * submodule is not initialized. + */ + int submodule_to_gitdir(struct strbuf *buf, const char *submodule); +#define SUBMODULE_MOVE_HEAD_DRY_RUN (1<<0) +#define SUBMODULE_MOVE_HEAD_FORCE (1<<1) +extern int submodule_move_head(const char *path, + const char *old, + const char *new, + unsigned flags); + /* * Prepare the "env_array" parameter of a "struct child_process" for executing * a submodule by clearing any repo-specific envirionment variables, but diff --combined t/helper/.gitignore index 758ed2e8fa,5f68aa8f8a..d1fa751cf9 --- a/t/helper/.gitignore +++ b/t/helper/.gitignore @@@ -11,7 -11,6 +11,7 @@@ /test-genrandom /test-hashmap /test-index-version +/test-lazy-init-name-hash /test-line-buffer /test-match-trees /test-mergesort @@@ -20,6 -19,7 +20,7 @@@ /test-path-utils /test-prio-queue /test-read-cache + /test-ref-store /test-regex /test-revision-walking /test-run-command