# 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.
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
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
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
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
perl/PM.stamp: FORCE
@$(FIND) perl -type f -name '*.pm' | sort >$@+ && \
+ $(PERL_PATH) -V >>$@+ && \
{ cmp $@+ $@ >/dev/null 2>/dev/null || mv $@+ $@; } && \
$(RM) $@+
@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
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: $@; \
}
/* 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))
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, ...)
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")
#include "refs/refs-internal.h"
#include "object.h"
#include "tag.h"
+ #include "submodule.h"
/*
* List of all available backends
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 */
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);
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)
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[] = {
{
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;
}
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;
if (!warn_ambiguous_refs)
break;
}
+ strbuf_release(&path);
free(last_branch);
return logs_found;
}
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) ||
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;
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)
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) ||
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) {
/*
*/
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)
* (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;
}
* 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);
}
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);
* 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)
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)
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)
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)
/* 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);
}
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);
}
/* 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;
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;
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);
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;
}
}
/* 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);
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,
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);
+ }
#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
}
/*
+ * 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.<name>.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);
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
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");
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))
/*
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);
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,
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);
}
}
{
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;
/* 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);
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);
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;
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;
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;
}
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;
}
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");
}
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;
}
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;
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;
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;
}
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)
{
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;
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.
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);
/* 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) {
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);
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, '/');
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;
+ }
struct diff_options;
struct argv_array;
-struct sha1_array;
+struct oid_array;
+struct remote;
enum {
RECURSE_SUBMODULES_ONLY = -5,
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 <path>/.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);
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);
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
/test-genrandom
/test-hashmap
/test-index-version
+/test-lazy-init-name-hash
/test-line-buffer
/test-match-trees
/test-mergesort
/test-path-utils
/test-prio-queue
/test-read-cache
+ /test-ref-store
/test-regex
/test-revision-walking
/test-run-command