From: Junio C Hamano Date: Tue, 19 Sep 2017 01:47:53 +0000 (+0900) Subject: Merge branch 'nd/prune-in-worktree' X-Git-Tag: v2.15.0-rc0~92 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/8a044c7f1d56cef657be342e40de0795d688e882?hp=-c Merge branch 'nd/prune-in-worktree' "git gc" and friends when multiple worktrees are used off of a single repository did not consider the index and per-worktree refs of other worktrees as the root for reachability traversal, making objects that are in use only in other worktrees to be subject to garbage collection. * nd/prune-in-worktree: refs.c: reindent get_submodule_ref_store() refs.c: remove fallback-to-main-store code get_submodule_ref_store() rev-list: expose and document --single-worktree revision.c: --reflog add HEAD reflog from all worktrees files-backend: make reflog iterator go through per-worktree reflog revision.c: --all adds HEAD from all worktrees refs: remove dead for_each_*_submodule() refs.c: move for_each_remote_ref_submodule() to submodule.c revision.c: use refs_for_each*() instead of for_each_*_submodule() refs: add refs_head_ref() refs: move submodule slash stripping code to get_submodule_ref_store refs.c: refactor get_submodule_ref_store(), share common free block revision.c: --indexed-objects add objects from all worktrees revision.c: refactor add_index_objects_to_pending() refs.c: use is_dir_sep() in resolve_gitlink_ref() revision.h: new flag in struct rev_info wrt. worktree-related refs --- 8a044c7f1d56cef657be342e40de0795d688e882 diff --combined reachable.c index d1ac5d97ef,492e87b9fa..88d7d679da --- a/reachable.c +++ b/reachable.c @@@ -9,7 -9,7 +9,8 @@@ #include "cache-tree.h" #include "progress.h" #include "list-objects.h" +#include "packfile.h" + #include "worktree.h" struct connectivity_progress { struct progress *progress; @@@ -177,6 -177,7 +178,7 @@@ void mark_reachable_objects(struct rev_ /* detached HEAD is not included in the list above */ head_ref(add_one_ref, revs); + other_head_refs(add_one_ref, revs); /* Add all reflog info */ if (mark_reflog) diff --combined refs.c index b0106b8162,206af61d62..a2b97fba78 --- a/refs.c +++ b/refs.c @@@ -336,12 -336,6 +336,6 @@@ int for_each_tag_ref(each_ref_fn fn, vo 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 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); @@@ -352,12 -346,6 +346,6 @@@ int for_each_branch_ref(each_ref_fn fn 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 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); @@@ -368,12 -356,6 +356,6 @@@ int for_each_remote_ref(each_ref_fn fn 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 refs_for_each_remote_ref(get_submodule_ref_store(submodule), - fn, cb_data); - } - int head_ref_namespaced(each_ref_fn fn, void *cb_data) { struct strbuf buf = STRBUF_INIT; @@@ -579,21 -561,6 +561,21 @@@ enum ref_type ref_type(const char *refn return REF_TYPE_NORMAL; } +long get_files_ref_lock_timeout_ms(void) +{ + static int configured = 0; + + /* The default timeout is 100 ms: */ + static int timeout_ms = 100; + + if (!configured) { + git_config_get_int("core.filesreflocktimeout", &timeout_ms); + configured = 1; + } + + return timeout_ms; +} + static int write_pseudoref(const char *pseudoref, const unsigned char *sha1, const unsigned char *old_sha1, struct strbuf *err) { @@@ -606,9 -573,7 +588,9 @@@ strbuf_addf(&buf, "%s\n", sha1_to_hex(sha1)); filename = git_path("%s", pseudoref); - fd = hold_lock_file_for_update(&lock, filename, LOCK_DIE_ON_ERROR); + fd = hold_lock_file_for_update_timeout(&lock, filename, + LOCK_DIE_ON_ERROR, + get_files_ref_lock_timeout_ms()); if (fd < 0) { strbuf_addf(err, "could not open '%s' for writing: %s", filename, strerror(errno)); @@@ -651,9 -616,8 +633,9 @@@ static int delete_pseudoref(const char int fd; unsigned char actual_old_sha1[20]; - fd = hold_lock_file_for_update(&lock, filename, - LOCK_DIE_ON_ERROR); + fd = hold_lock_file_for_update_timeout( + &lock, filename, LOCK_DIE_ON_ERROR, + get_files_ref_lock_timeout_ms()); if (fd < 0) die_errno(_("Could not open '%s' for writing"), filename); if (read_ref(pseudoref, actual_old_sha1)) @@@ -1266,19 -1230,13 +1248,13 @@@ int refs_rename_ref_available(struct re return ok; } - int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data) + int refs_head_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data) { struct object_id oid; int flag; - if (submodule) { - if (resolve_gitlink_ref(submodule, "HEAD", oid.hash) == 0) - return fn("HEAD", &oid, 0, cb_data); - - return 0; - } - - if (!read_ref_full("HEAD", RESOLVE_REF_READING, oid.hash, &flag)) + if (!refs_read_ref_full(refs, "HEAD", RESOLVE_REF_READING, + oid.hash, &flag)) return fn("HEAD", &oid, flag, cb_data); return 0; @@@ -1286,7 -1244,7 +1262,7 @@@ int head_ref(each_ref_fn fn, void *cb_data) { - return head_ref_submodule(NULL, fn, cb_data); + return refs_head_ref(get_main_ref_store(), fn, cb_data); } struct ref_iterator *refs_ref_iterator_begin( @@@ -1344,11 -1302,6 +1320,6 @@@ int for_each_ref(each_ref_fn fn, void * 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 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) { @@@ -1370,23 -1323,15 +1341,15 @@@ int for_each_fullref_in(const char *pre 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) - { - return refs_for_each_ref_in(get_submodule_ref_store(submodule), - prefix, fn, cb_data); - } - - int for_each_fullref_in_submodule(const char *submodule, const char *prefix, - each_ref_fn fn, void *cb_data, - unsigned int broken) + int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix, + each_ref_fn fn, void *cb_data, + unsigned int broken) { unsigned int flag = 0; if (broken) flag = DO_FOR_EACH_INCLUDE_BROKEN; - return do_for_each_ref(get_submodule_ref_store(submodule), - prefix, fn, 0, flag, cb_data); + return do_for_each_ref(refs, prefix, fn, 0, flag, cb_data); } int for_each_replace_ref(each_ref_fn fn, void *cb_data) @@@ -1521,25 -1466,10 +1484,10 @@@ const char *resolve_ref_unsafe(const ch int resolve_gitlink_ref(const char *submodule, const char *refname, unsigned char *sha1) { - size_t len = strlen(submodule); struct ref_store *refs; int flags; - while (len && submodule[len - 1] == '/') - len--; - - if (!len) - return -1; - - if (submodule[len]) { - /* We need to strip off one or more trailing slashes */ - char *stripped = xmemdupz(submodule, len); - - refs = get_submodule_ref_store(stripped); - free(stripped); - } else { - refs = get_submodule_ref_store(submodule); - } + refs = get_submodule_ref_store(submodule); if (!refs) return -1; @@@ -1654,31 -1584,32 +1602,32 @@@ struct ref_store *get_submodule_ref_sto { struct strbuf submodule_sb = STRBUF_INIT; struct ref_store *refs; - int ret; + char *to_free = NULL; + size_t len; - if (!submodule || !*submodule) { - /* - * 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 (!submodule) + return NULL; + + len = strlen(submodule); + while (len && is_dir_sep(submodule[len - 1])) + len--; + if (!len) + return NULL; + + if (submodule[len]) + /* We need to strip off one or more trailing slashes */ + submodule = to_free = xmemdupz(submodule, len); refs = lookup_ref_store_map(&submodule_ref_stores, submodule); if (refs) - return refs; + goto done; strbuf_addstr(&submodule_sb, submodule); - ret = is_nonbare_repository_dir(&submodule_sb); - strbuf_release(&submodule_sb); - if (!ret) - return NULL; + if (!is_nonbare_repository_dir(&submodule_sb)) + goto done; - ret = submodule_to_gitdir(&submodule_sb, submodule); - if (ret) { - strbuf_release(&submodule_sb); - return NULL; - } + if (submodule_to_gitdir(&submodule_sb, submodule)) + goto done; /* assume that add_submodule_odb() has been called */ refs = ref_store_init(submodule_sb.buf, @@@ -1686,7 -1617,10 +1635,10 @@@ register_ref_store_map(&submodule_ref_stores, "submodule", refs, submodule); + done: strbuf_release(&submodule_sb); + free(to_free); + return refs; } diff --combined refs/files-backend.c index 3a7bf6a146,d4d22882ef..cda790dcbc --- a/refs/files-backend.c +++ b/refs/files-backend.c @@@ -106,15 -106,6 +106,6 @@@ static void files_reflog_path(struct fi struct strbuf *sb, const char *refname) { - if (!refname) { - /* - * FIXME: of course this is wrong in multi worktree - * setting. To be fixed real soon. - */ - strbuf_addf(sb, "%s/logs", refs->gitcommondir); - return; - } - switch (ref_type(refname)) { case REF_TYPE_PER_WORKTREE: case REF_TYPE_PSEUDOREF: @@@ -537,9 -528,7 +528,9 @@@ retry if (!lock->lk) lock->lk = xcalloc(1, sizeof(struct lock_file)); - if (hold_lock_file_for_update(lock->lk, ref_file.buf, LOCK_NO_DEREF) < 0) { + if (hold_lock_file_for_update_timeout( + lock->lk, ref_file.buf, LOCK_NO_DEREF, + get_files_ref_lock_timeout_ms()) < 0) { if (errno == ENOENT && --attempts_remaining > 0) { /* * Maybe somebody just deleted one of the @@@ -867,9 -856,7 +858,9 @@@ static int create_reflock(const char *p { struct lock_file *lk = cb; - return hold_lock_file_for_update(lk, path, LOCK_NO_DEREF) < 0 ? -1 : 0; + return hold_lock_file_for_update_timeout( + lk, path, LOCK_NO_DEREF, + get_files_ref_lock_timeout_ms()) < 0 ? -1 : 0; } /* @@@ -2059,23 -2046,63 +2050,63 @@@ static struct ref_iterator_vtable files files_reflog_iterator_abort }; - static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store) + static struct ref_iterator *reflog_iterator_begin(struct ref_store *ref_store, + const char *gitdir) { - struct files_ref_store *refs = - files_downcast(ref_store, REF_STORE_READ, - "reflog_iterator_begin"); struct files_reflog_iterator *iter = xcalloc(1, sizeof(*iter)); struct ref_iterator *ref_iterator = &iter->base; struct strbuf sb = STRBUF_INIT; base_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable); - files_reflog_path(refs, &sb, NULL); + strbuf_addf(&sb, "%s/logs", gitdir); iter->dir_iterator = dir_iterator_begin(sb.buf); iter->ref_store = ref_store; strbuf_release(&sb); + return ref_iterator; } + static enum iterator_selection reflog_iterator_select( + struct ref_iterator *iter_worktree, + struct ref_iterator *iter_common, + void *cb_data) + { + if (iter_worktree) { + /* + * We're a bit loose here. We probably should ignore + * common refs if they are accidentally added as + * per-worktree refs. + */ + return ITER_SELECT_0; + } else if (iter_common) { + if (ref_type(iter_common->refname) == REF_TYPE_NORMAL) + return ITER_SELECT_1; + + /* + * The main ref store may contain main worktree's + * per-worktree refs, which should be ignored + */ + return ITER_SKIP_1; + } else + return ITER_DONE; + } + + static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store) + { + struct files_ref_store *refs = + files_downcast(ref_store, REF_STORE_READ, + "reflog_iterator_begin"); + + if (!strcmp(refs->gitdir, refs->gitcommondir)) { + return reflog_iterator_begin(ref_store, refs->gitcommondir); + } else { + return merge_ref_iterator_begin( + reflog_iterator_begin(ref_store, refs->gitdir), + reflog_iterator_begin(ref_store, refs->gitcommondir), + reflog_iterator_select, refs); + } + } + /* * If update is a direct update of head_ref (the reference pointed to * by HEAD), then add an extra REF_LOG_ONLY update for HEAD. @@@ -2099,10 -2126,11 +2130,10 @@@ static int split_head_update(struct ref /* * First make sure that HEAD is not already in the - * transaction. This insertion is O(N) in the transaction + * transaction. This check is O(lg N) in the transaction * size, but it happens at most once per transaction. */ - item = string_list_insert(affected_refnames, "HEAD"); - if (item->util) { + if (string_list_has_string(affected_refnames, "HEAD")) { /* An entry already existed */ strbuf_addf(err, "multiple updates for 'HEAD' (including one " @@@ -2117,14 -2145,6 +2148,14 @@@ update->new_oid.hash, update->old_oid.hash, update->msg); + /* + * Add "HEAD". This insertion is O(N) in the transaction + * size, but it happens at most once per transaction. + * Add new_update->refname instead of a literal "HEAD". + */ + if (strcmp(new_update->refname, "HEAD")) + BUG("%s unexpectedly not 'HEAD'", new_update->refname); + item = string_list_insert(affected_refnames, new_update->refname); item->util = new_update; return 0; @@@ -2151,12 -2171,13 +2182,12 @@@ static int split_symref_update(struct f /* * First make sure that referent is not already in the - * transaction. This insertion is O(N) in the transaction + * transaction. This check is O(lg N) in the transaction * size, but it happens at most once per symref in a * transaction. */ - item = string_list_insert(affected_refnames, referent); - if (item->util) { - /* An entry already existed */ + if (string_list_has_string(affected_refnames, referent)) { + /* An entry already exists */ strbuf_addf(err, "multiple updates for '%s' (including one " "via symref '%s') are not allowed", @@@ -2191,17 -2212,6 +2222,17 @@@ update->flags |= REF_LOG_ONLY | REF_NODEREF; update->flags &= ~REF_HAVE_OLD; + /* + * Add the referent. This insertion is O(N) in the transaction + * size, but it happens at most once per symref in a + * transaction. Make sure to add new_update->refname, which will + * be valid as long as affected_refnames is in use, and NOT + * referent, which might soon be freed by our caller. + */ + item = string_list_insert(affected_refnames, new_update->refname); + if (item->util) + BUG("%s unexpectedly found in affected_refnames", + new_update->refname); item->util = new_update; return 0; @@@ -2273,7 -2283,7 +2304,7 @@@ static int lock_ref_for_update(struct f struct strbuf referent = STRBUF_INIT; int mustexist = (update->flags & REF_HAVE_OLD) && !is_null_oid(&update->old_oid); - int ret; + int ret = 0; struct ref_lock *lock; files_assert_main_repository(refs, "lock_ref_for_update"); @@@ -2285,7 -2295,7 +2316,7 @@@ ret = split_head_update(update, transaction, head_ref, affected_refnames, err); if (ret) - return ret; + goto out; } ret = lock_raw_ref(refs, update->refname, mustexist, @@@ -2299,7 -2309,7 +2330,7 @@@ strbuf_addf(err, "cannot lock ref '%s': %s", original_update_refname(update), reason); free(reason); - return ret; + goto out; } update->backend_data = lock; @@@ -2318,12 -2328,10 +2349,12 @@@ strbuf_addf(err, "cannot lock ref '%s': " "error reading reference", original_update_refname(update)); - return -1; + ret = TRANSACTION_GENERIC_ERROR; + goto out; } } else if (check_old_oid(update, &lock->old_oid, err)) { - return TRANSACTION_GENERIC_ERROR; + ret = TRANSACTION_GENERIC_ERROR; + goto out; } } else { /* @@@ -2337,15 -2345,13 +2368,15 @@@ referent.buf, transaction, affected_refnames, err); if (ret) - return ret; + goto out; } } else { struct ref_update *parent_update; - if (check_old_oid(update, &lock->old_oid, err)) - return TRANSACTION_GENERIC_ERROR; + if (check_old_oid(update, &lock->old_oid, err)) { + ret = TRANSACTION_GENERIC_ERROR; + goto out; + } /* * If this update is happening indirectly because of a @@@ -2382,8 -2388,7 +2413,8 @@@ "cannot update ref '%s': %s", update->refname, write_err); free(write_err); - return TRANSACTION_GENERIC_ERROR; + ret = TRANSACTION_GENERIC_ERROR; + goto out; } else { update->flags |= REF_NEEDS_COMMIT; } @@@ -2397,14 -2402,10 +2428,14 @@@ if (close_ref(lock)) { strbuf_addf(err, "couldn't close '%s.lock'", update->refname); - return TRANSACTION_GENERIC_ERROR; + ret = TRANSACTION_GENERIC_ERROR; + goto out; } } - return 0; + +out: + strbuf_release(&referent); + return ret; } /* diff --combined revision.c index 94a5e98525,6eba4131b4..f9a90d71d2 --- a/revision.c +++ b/revision.c @@@ -19,7 -19,7 +19,8 @@@ #include "dir.h" #include "cache-tree.h" #include "bisect.h" +#include "packfile.h" + #include "worktree.h" volatile show_early_output_fn_t show_early_output; @@@ -1132,6 -1132,7 +1133,7 @@@ struct all_refs_cb int warned_bad_reflog; struct rev_info *all_revs; const char *name_for_errormsg; + struct ref_store *refs; }; int ref_excluded(struct string_list *ref_excludes, const char *path) @@@ -1168,6 -1169,7 +1170,7 @@@ static void init_all_refs_cb(struct all cb->all_revs = revs; cb->all_flags = flags; revs->rev_input_given = 1; + cb->refs = NULL; } void clear_ref_exclusion(struct string_list **ref_excludes_p) @@@ -1188,12 -1190,19 +1191,19 @@@ void add_ref_exclusion(struct string_li string_list_append(*ref_excludes_p, exclude); } - static void handle_refs(const char *submodule, struct rev_info *revs, unsigned flags, - int (*for_each)(const char *, each_ref_fn, void *)) + static void handle_refs(struct ref_store *refs, + struct rev_info *revs, unsigned flags, + int (*for_each)(struct ref_store *, each_ref_fn, void *)) { struct all_refs_cb cb; + + if (!refs) { + /* this could happen with uninitialized submodules */ + return; + } + init_all_refs_cb(&cb, revs, flags); - for_each(submodule, handle_one_ref, &cb); + for_each(refs, handle_one_ref, &cb); } static void handle_one_reflog_commit(struct object_id *oid, void *cb_data) @@@ -1229,17 -1238,41 +1239,41 @@@ static int handle_one_reflog(const cha struct all_refs_cb *cb = cb_data; cb->warned_bad_reflog = 0; cb->name_for_errormsg = path; - for_each_reflog_ent(path, handle_one_reflog_ent, cb_data); + refs_for_each_reflog_ent(cb->refs, path, + handle_one_reflog_ent, cb_data); return 0; } + static void add_other_reflogs_to_pending(struct all_refs_cb *cb) + { + struct worktree **worktrees, **p; + + worktrees = get_worktrees(0); + for (p = worktrees; *p; p++) { + struct worktree *wt = *p; + + if (wt->is_current) + continue; + + cb->refs = get_worktree_ref_store(wt); + refs_for_each_reflog(cb->refs, + handle_one_reflog, + cb); + } + free_worktrees(worktrees); + } + void add_reflogs_to_pending(struct rev_info *revs, unsigned flags) { struct all_refs_cb cb; cb.all_revs = revs; cb.all_flags = flags; + cb.refs = get_main_ref_store(); for_each_reflog(handle_one_reflog, &cb); + + if (!revs->single_worktree) + add_other_reflogs_to_pending(&cb); } static void add_cache_tree(struct cache_tree *it, struct rev_info *revs, @@@ -1263,13 -1296,13 +1297,13 @@@ } - void add_index_objects_to_pending(struct rev_info *revs, unsigned flags) + static void do_add_index_objects_to_pending(struct rev_info *revs, + struct index_state *istate) { int i; - read_cache(); - for (i = 0; i < active_nr; i++) { - struct cache_entry *ce = active_cache[i]; + for (i = 0; i < istate->cache_nr; i++) { + struct cache_entry *ce = istate->cache[i]; struct blob *blob; if (S_ISGITLINK(ce->ce_mode)) @@@ -1282,13 -1315,39 +1316,39 @@@ ce->ce_mode, ce->name); } - if (active_cache_tree) { + if (istate->cache_tree) { struct strbuf path = STRBUF_INIT; - add_cache_tree(active_cache_tree, revs, &path); + add_cache_tree(istate->cache_tree, revs, &path); strbuf_release(&path); } } + void add_index_objects_to_pending(struct rev_info *revs, unsigned int flags) + { + struct worktree **worktrees, **p; + + read_cache(); + do_add_index_objects_to_pending(revs, &the_index); + + if (revs->single_worktree) + return; + + worktrees = get_worktrees(0); + for (p = worktrees; *p; p++) { + struct worktree *wt = *p; + struct index_state istate = { NULL }; + + if (wt->is_current) + continue; /* current index already taken care of */ + + if (read_index_from(&istate, + worktree_git_path(wt, "index")) > 0) + do_add_index_objects_to_pending(revs, &istate); + discard_index(&istate); + } + free_worktrees(worktrees); + } + static int add_parents_only(struct rev_info *revs, const char *arg_, int flags, int exclude_parent) { @@@ -2069,23 -2128,25 +2129,25 @@@ void parse_revision_opt(struct rev_inf ctx->argc -= n; } - static int for_each_bisect_ref(const char *submodule, each_ref_fn fn, void *cb_data, const char *term) { + static int for_each_bisect_ref(struct ref_store *refs, each_ref_fn fn, + void *cb_data, const char *term) + { struct strbuf bisect_refs = STRBUF_INIT; int status; strbuf_addf(&bisect_refs, "refs/bisect/%s", term); - status = for_each_fullref_in_submodule(submodule, bisect_refs.buf, fn, cb_data, 0); + status = refs_for_each_fullref_in(refs, bisect_refs.buf, fn, cb_data, 0); strbuf_release(&bisect_refs); return status; } - static int for_each_bad_bisect_ref(const char *submodule, each_ref_fn fn, void *cb_data) + static int for_each_bad_bisect_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data) { - return for_each_bisect_ref(submodule, fn, cb_data, term_bad); + return for_each_bisect_ref(refs, fn, cb_data, term_bad); } - static int for_each_good_bisect_ref(const char *submodule, each_ref_fn fn, void *cb_data) + static int for_each_good_bisect_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data) { - return for_each_bisect_ref(submodule, fn, cb_data, term_good); + return for_each_bisect_ref(refs, fn, cb_data, term_good); } static int handle_revision_pseudo_opt(const char *submodule, @@@ -2094,8 -2155,22 +2156,22 @@@ { const char *arg = argv[0]; const char *optarg; + struct ref_store *refs; int argcount; + if (submodule) { + /* + * We need some something like get_submodule_worktrees() + * before we can go through all worktrees of a submodule, + * .e.g with adding all HEADs from --all, which is not + * supported right now, so stick to single worktree. + */ + if (!revs->single_worktree) + die("BUG: --single-worktree cannot be used together with submodule"); + refs = get_submodule_ref_store(submodule); + } else + refs = get_main_ref_store(); + /* * NOTE! * @@@ -2107,22 -2182,29 +2183,29 @@@ * register it in the list at the top of handle_revision_opt. */ if (!strcmp(arg, "--all")) { - handle_refs(submodule, revs, *flags, for_each_ref_submodule); - handle_refs(submodule, revs, *flags, head_ref_submodule); + handle_refs(refs, revs, *flags, refs_for_each_ref); + handle_refs(refs, revs, *flags, refs_head_ref); + if (!revs->single_worktree) { + struct all_refs_cb cb; + + init_all_refs_cb(&cb, revs, *flags); + other_head_refs(handle_one_ref, &cb); + } clear_ref_exclusion(&revs->ref_excludes); } else if (!strcmp(arg, "--branches")) { - handle_refs(submodule, revs, *flags, for_each_branch_ref_submodule); + handle_refs(refs, revs, *flags, refs_for_each_branch_ref); clear_ref_exclusion(&revs->ref_excludes); } else if (!strcmp(arg, "--bisect")) { read_bisect_terms(&term_bad, &term_good); - handle_refs(submodule, revs, *flags, for_each_bad_bisect_ref); - handle_refs(submodule, revs, *flags ^ (UNINTERESTING | BOTTOM), for_each_good_bisect_ref); + handle_refs(refs, revs, *flags, for_each_bad_bisect_ref); + handle_refs(refs, revs, *flags ^ (UNINTERESTING | BOTTOM), + for_each_good_bisect_ref); revs->bisect = 1; } else if (!strcmp(arg, "--tags")) { - handle_refs(submodule, revs, *flags, for_each_tag_ref_submodule); + handle_refs(refs, revs, *flags, refs_for_each_tag_ref); clear_ref_exclusion(&revs->ref_excludes); } else if (!strcmp(arg, "--remotes")) { - handle_refs(submodule, revs, *flags, for_each_remote_ref_submodule); + handle_refs(refs, revs, *flags, refs_for_each_remote_ref); clear_ref_exclusion(&revs->ref_excludes); } else if ((argcount = parse_long_opt("glob", argv, &optarg))) { struct all_refs_cb cb; @@@ -2169,6 -2251,8 +2252,8 @@@ return error("invalid argument to --no-walk"); } else if (!strcmp(arg, "--do-walk")) { revs->no_walk = 0; + } else if (!strcmp(arg, "--single-worktree")) { + revs->single_worktree = 1; } else { return 0; } diff --combined submodule.c index 3cea8221e0,61a38adcd4..075c55f3ca --- a/submodule.c +++ b/submodule.c @@@ -69,6 -69,13 +69,13 @@@ int is_staging_gitmodules_ok(const stru return 1; } + static int for_each_remote_ref_submodule(const char *submodule, + each_ref_fn fn, void *cb_data) + { + return refs_for_each_remote_ref(get_submodule_ref_store(submodule), + fn, cb_data); + } + /* * Try to update the "path" entry in the "submodule." section of the * .gitmodules file. Return 0 only if a .gitmodules file was found, a section @@@ -165,18 -172,31 +172,18 @@@ void set_diffopt_flags_from_submodule_c { const struct submodule *submodule = submodule_from_path(&null_oid, path); if (submodule) { - if (submodule->ignore) - handle_ignore_submodules_arg(diffopt, submodule->ignore); - else if (is_gitmodules_unmerged(&the_index)) - DIFF_OPT_SET(diffopt, IGNORE_SUBMODULES); - } -} + const char *ignore; + char *key; -/* For loading from the .gitmodules file. */ -static int git_modules_config(const char *var, const char *value, void *cb) -{ - if (starts_with(var, "submodule.")) - return parse_submodule_config_option(var, value); - return 0; -} + key = xstrfmt("submodule.%s.ignore", submodule->name); + if (repo_config_get_string_const(the_repository, key, &ignore)) + ignore = submodule->ignore; + free(key); -/* Loads all submodule settings from the config. */ -int submodule_config(const char *var, const char *value, void *cb) -{ - if (!strcmp(var, "submodule.recurse")) { - int v = git_config_bool(var, value) ? - RECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF; - config_update_recurse_submodules = v; - return 0; - } else { - return git_modules_config(var, value, cb); + if (ignore) + handle_ignore_submodules_arg(diffopt, ignore); + else if (is_gitmodules_unmerged(&the_index)) + DIFF_OPT_SET(diffopt, IGNORE_SUBMODULES); } } @@@ -208,6 -228,55 +215,6 @@@ int option_parse_recurse_submodules_wor return 0; } -void load_submodule_cache(void) -{ - if (config_update_recurse_submodules == RECURSE_SUBMODULES_OFF) - return; - - gitmodules_config(); - git_config(submodule_config, NULL); -} - -static int gitmodules_cb(const char *var, const char *value, void *data) -{ - struct repository *repo = data; - return submodule_config_option(repo, var, value); -} - -void repo_read_gitmodules(struct repository *repo) -{ - if (repo->worktree) { - char *gitmodules; - - if (repo_read_index(repo) < 0) - return; - - gitmodules = repo_worktree_path(repo, GITMODULES_FILE); - - if (!is_gitmodules_unmerged(repo->index)) - git_config_from_file(gitmodules_cb, gitmodules, repo); - - free(gitmodules); - } -} - -void gitmodules_config(void) -{ - repo_read_gitmodules(the_repository); -} - -void gitmodules_config_oid(const struct object_id *commit_oid) -{ - struct strbuf rev = STRBUF_INIT; - struct object_id oid; - - if (gitmodule_oid_from_commit(commit_oid, &oid, &rev)) { - git_config_from_blob_oid(submodule_config, rev.buf, - &oid, NULL); - } - strbuf_release(&rev); -} - /* * Determine if a submodule has been initialized at a given 'path' */ @@@ -336,38 -405,24 +343,38 @@@ void die_path_inside_submodule(const st } } -int parse_submodule_update_strategy(const char *value, - struct submodule_update_strategy *dst) +enum submodule_update_type parse_submodule_update_type(const char *value) { - free((void*)dst->command); - dst->command = NULL; if (!strcmp(value, "none")) - dst->type = SM_UPDATE_NONE; + return SM_UPDATE_NONE; else if (!strcmp(value, "checkout")) - dst->type = SM_UPDATE_CHECKOUT; + return SM_UPDATE_CHECKOUT; else if (!strcmp(value, "rebase")) - dst->type = SM_UPDATE_REBASE; + return SM_UPDATE_REBASE; else if (!strcmp(value, "merge")) - dst->type = SM_UPDATE_MERGE; - else if (skip_prefix(value, "!", &value)) { - dst->type = SM_UPDATE_COMMAND; - dst->command = xstrdup(value); - } else + return SM_UPDATE_MERGE; + else if (*value == '!') + return SM_UPDATE_COMMAND; + else + return SM_UPDATE_UNSPECIFIED; +} + +int parse_submodule_update_strategy(const char *value, + struct submodule_update_strategy *dst) +{ + enum submodule_update_type type; + + free((void*)dst->command); + dst->command = NULL; + + type = parse_submodule_update_type(value); + if (type == SM_UPDATE_UNSPECIFIED) return -1; + + dst->type = type; + if (type == SM_UPDATE_COMMAND) + dst->command = xstrdup(value + 1); + return 0; } @@@ -430,7 -485,9 +437,7 @@@ static int prepare_submodule_summary(st return prepare_revision_walk(rev); } -static void print_submodule_summary(struct rev_info *rev, FILE *f, - const char *line_prefix, - const char *del, const char *add, const char *reset) +static void print_submodule_summary(struct rev_info *rev, struct diff_options *o) { static const char format[] = " %m %s"; struct strbuf sb = STRBUF_INIT; @@@ -441,12 -498,18 +448,12 @@@ ctx.date_mode = rev->date_mode; ctx.output_encoding = get_log_output_encoding(); strbuf_setlen(&sb, 0); - strbuf_addstr(&sb, line_prefix); - if (commit->object.flags & SYMMETRIC_LEFT) { - if (del) - strbuf_addstr(&sb, del); - } - else if (add) - strbuf_addstr(&sb, add); format_commit_message(commit, format, &sb, &ctx); - if (reset) - strbuf_addstr(&sb, reset); strbuf_addch(&sb, '\n'); - fprintf(f, "%s", sb.buf); + if (commit->object.flags & SYMMETRIC_LEFT) + diff_emit_submodule_del(o, sb.buf); + else + diff_emit_submodule_add(o, sb.buf); } strbuf_release(&sb); } @@@ -473,9 -536,11 +480,9 @@@ void prepare_submodule_repo_env(struct * attempt to lookup both the left and right commits and put them into the * left and right pointers. */ -static void show_submodule_header(FILE *f, const char *path, - const char *line_prefix, +static void show_submodule_header(struct diff_options *o, const char *path, struct object_id *one, struct object_id *two, - unsigned dirty_submodule, const char *meta, - const char *reset, + unsigned dirty_submodule, struct commit **left, struct commit **right, struct commit_list **merge_bases) { @@@ -484,10 -549,11 +491,10 @@@ int fast_forward = 0, fast_backward = 0; if (dirty_submodule & DIRTY_SUBMODULE_UNTRACKED) - fprintf(f, "%sSubmodule %s contains untracked content\n", - line_prefix, path); + diff_emit_submodule_untracked(o, path); + if (dirty_submodule & DIRTY_SUBMODULE_MODIFIED) - fprintf(f, "%sSubmodule %s contains modified content\n", - line_prefix, path); + diff_emit_submodule_modified(o, path); if (is_null_oid(one)) message = "(new submodule)"; @@@ -529,29 -595,31 +536,29 @@@ } output_header: - strbuf_addf(&sb, "%s%sSubmodule %s ", line_prefix, meta, path); + strbuf_addf(&sb, "Submodule %s ", path); strbuf_add_unique_abbrev(&sb, one->hash, DEFAULT_ABBREV); strbuf_addstr(&sb, (fast_backward || fast_forward) ? ".." : "..."); strbuf_add_unique_abbrev(&sb, two->hash, DEFAULT_ABBREV); if (message) - strbuf_addf(&sb, " %s%s\n", message, reset); + strbuf_addf(&sb, " %s\n", message); else - strbuf_addf(&sb, "%s:%s\n", fast_backward ? " (rewind)" : "", reset); - fwrite(sb.buf, sb.len, 1, f); + strbuf_addf(&sb, "%s:\n", fast_backward ? " (rewind)" : ""); + diff_emit_submodule_header(o, sb.buf); strbuf_release(&sb); } -void show_submodule_summary(FILE *f, const char *path, - const char *line_prefix, +void show_submodule_summary(struct diff_options *o, const char *path, struct object_id *one, struct object_id *two, - unsigned dirty_submodule, const char *meta, - const char *del, const char *add, const char *reset) + unsigned dirty_submodule) { struct rev_info rev; struct commit *left = NULL, *right = NULL; struct commit_list *merge_bases = NULL; - show_submodule_header(f, path, line_prefix, one, two, dirty_submodule, - meta, reset, &left, &right, &merge_bases); + show_submodule_header(o, path, one, two, dirty_submodule, + &left, &right, &merge_bases); /* * If we don't have both a left and a right pointer, there is no @@@ -563,11 -631,11 +570,11 @@@ /* Treat revision walker failure the same as missing commits */ if (prepare_submodule_summary(&rev, path, left, right, merge_bases)) { - fprintf(f, "%s(revision walker failed)\n", line_prefix); + diff_emit_submodule_error(o, "(revision walker failed)\n"); goto out; } - print_submodule_summary(&rev, f, line_prefix, del, add, reset); + print_submodule_summary(&rev, o); out: if (merge_bases) @@@ -576,18 -644,21 +583,18 @@@ clear_commit_marks(right, ~0); } -void show_submodule_inline_diff(FILE *f, const char *path, - const char *line_prefix, +void show_submodule_inline_diff(struct diff_options *o, const char *path, struct object_id *one, struct object_id *two, - unsigned dirty_submodule, const char *meta, - const char *del, const char *add, const char *reset, - const struct diff_options *o) + unsigned dirty_submodule) { const struct object_id *old = &empty_tree_oid, *new = &empty_tree_oid; struct commit *left = NULL, *right = NULL; struct commit_list *merge_bases = NULL; - struct strbuf submodule_dir = STRBUF_INIT; struct child_process cp = CHILD_PROCESS_INIT; + struct strbuf sb = STRBUF_INIT; - show_submodule_header(f, path, line_prefix, one, two, dirty_submodule, - meta, reset, &left, &right, &merge_bases); + show_submodule_header(o, path, one, two, dirty_submodule, + &left, &right, &merge_bases); /* We need a valid left and right commit to display a difference */ if (!(left || is_null_oid(one)) || @@@ -599,16 -670,16 +606,16 @@@ if (right) new = two; - fflush(f); cp.git_cmd = 1; cp.dir = path; - cp.out = dup(fileno(f)); + cp.out = -1; cp.no_stdin = 1; /* TODO: other options may need to be passed here. */ argv_array_pushl(&cp.args, "diff", "--submodule=diff", NULL); + argv_array_pushf(&cp.args, "--color=%s", want_color(o->use_color) ? + "always" : "never"); - argv_array_pushf(&cp.args, "--line-prefix=%s", line_prefix); if (DIFF_OPT_TST(o, REVERSE_DIFF)) { argv_array_pushf(&cp.args, "--src-prefix=%s%s/", o->b_prefix, path); @@@ -631,17 -702,11 +638,17 @@@ 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"); + if (start_command(&cp)) + diff_emit_submodule_error(o, "(diff failed)\n"); + + while (strbuf_getwholeline_fd(&sb, cp.out, '\n') != EOF) + diff_emit_submodule_pipethrough(o, sb.buf, sb.len); + + if (finish_command(&cp)) + diff_emit_submodule_error(o, "(diff failed)\n"); done: - strbuf_release(&submodule_dir); + strbuf_release(&sb); if (merge_bases) free_commit_list(merge_bases); if (left) @@@ -1082,6 -1147,7 +1089,6 @@@ int submodule_touches_in_range(struct o struct argv_array args = ARGV_ARRAY_INIT; int ret; - gitmodules_config(); /* No need to check if there are no submodules configured */ if (!submodule_from_path(NULL, NULL)) return 0; @@@ -1130,27 -1196,19 +1137,27 @@@ static int get_next_submodule(struct ch continue; submodule = submodule_from_path(&null_oid, ce->name); - if (!submodule) - submodule = submodule_from_name(&null_oid, ce->name); default_argv = "yes"; if (spf->command_line_option == RECURSE_SUBMODULES_DEFAULT) { - if (submodule && - submodule->fetch_recurse != - RECURSE_SUBMODULES_NONE) { - if (submodule->fetch_recurse == - RECURSE_SUBMODULES_OFF) + int fetch_recurse = RECURSE_SUBMODULES_NONE; + + if (submodule) { + char *key; + const char *value; + + fetch_recurse = submodule->fetch_recurse; + key = xstrfmt("submodule.%s.fetchRecurseSubmodules", submodule->name); + if (!repo_config_get_string_const(the_repository, key, &value)) { + fetch_recurse = parse_fetch_recurse_submodules_arg(key, value); + } + free(key); + } + + if (fetch_recurse != RECURSE_SUBMODULES_NONE) { + if (fetch_recurse == RECURSE_SUBMODULES_OFF) continue; - if (submodule->fetch_recurse == - RECURSE_SUBMODULES_ON_DEMAND) { + if (fetch_recurse == RECURSE_SUBMODULES_ON_DEMAND) { if (!unsorted_string_list_lookup(&changed_submodule_paths, ce->name)) continue; default_argv = "on-demand"; @@@ -1627,6 -1685,8 +1634,8 @@@ static int find_first_merges(struct obj oid_to_hex(&a->object.oid)); init_revisions(&revs, NULL); rev_opts.submodule = path; + /* FIXME: can't handle linked worktrees in submodules yet */ + revs.single_worktree = path != NULL; setup_revisions(ARRAY_SIZE(rev_args)-1, rev_args, &revs, &rev_opts); /* save all revisions from the above list that contain b */ @@@ -1988,6 -2048,7 +1997,6 @@@ int submodule_to_gitdir(struct strbuf * strbuf_addstr(buf, git_dir); } if (!is_git_directory(buf->buf)) { - gitmodules_config(); sub = submodule_from_path(&null_oid, submodule); if (!sub) { ret = -1; diff --combined worktree.c index c0c5a2b373,389e2e952c..8aaeea0377 --- a/worktree.c +++ b/worktree.c @@@ -30,7 -30,7 +30,7 @@@ static void add_head_info(struct worktr target = refs_resolve_ref_unsafe(get_worktree_ref_store(wt), "HEAD", - RESOLVE_REF_READING, + 0, wt->head_sha1, &flags); if (!target) return; @@@ -386,3 -386,25 +386,25 @@@ int submodule_uses_worktrees(const cha closedir(dir); return ret; } + + int other_head_refs(each_ref_fn fn, void *cb_data) + { + struct worktree **worktrees, **p; + int ret = 0; + + worktrees = get_worktrees(0); + for (p = worktrees; *p; p++) { + struct worktree *wt = *p; + struct ref_store *refs; + + if (wt->is_current) + continue; + + refs = get_worktree_ref_store(wt); + ret = refs_head_ref(refs, fn, cb_data); + if (ret) + break; + } + free_worktrees(worktrees); + return ret; + }