1
0
Fork 0
mirror of https://github.com/git/git.git synced 2024-06-03 15:46:17 +02:00

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
This commit is contained in:
Junio C Hamano 2017-09-19 10:47:53 +09:00
commit 8a044c7f1d
13 changed files with 308 additions and 136 deletions

View File

@ -184,6 +184,14 @@ explicitly.
Pretend as if all objects mentioned by reflogs are listed on the Pretend as if all objects mentioned by reflogs are listed on the
command line as `<commit>`. command line as `<commit>`.
--single-worktree::
By default, all working trees will be examined by the
following options when there are more than one (see
linkgit:git-worktree[1]): `--all`, `--reflog` and
`--indexed-objects`.
This option forces them to examine the current working tree
only.
--ignore-missing:: --ignore-missing::
Upon seeing an invalid object name in the input, pretend as if Upon seeing an invalid object name in the input, pretend as if
the bad input was not given. the bad input was not given.

View File

@ -32,11 +32,8 @@ Iteration functions
* `for_each_glob_ref_in()` the previous and `for_each_ref_in()` combined. * `for_each_glob_ref_in()` the previous and `for_each_ref_in()` combined.
* `head_ref_submodule()`, `for_each_ref_submodule()`, * Use `refs_` API for accessing submodules. The submodule ref store could
`for_each_ref_in_submodule()`, `for_each_tag_ref_submodule()`, be obtained with `get_submodule_ref_store()`.
`for_each_branch_ref_submodule()`, `for_each_remote_ref_submodule()`
do the same as the functions described above but for a specified
submodule.
* `for_each_rawref()` can be used to learn about broken ref and symref. * `for_each_rawref()` can be used to learn about broken ref and symref.

View File

@ -10,6 +10,7 @@
#include "progress.h" #include "progress.h"
#include "list-objects.h" #include "list-objects.h"
#include "packfile.h" #include "packfile.h"
#include "worktree.h"
struct connectivity_progress { struct connectivity_progress {
struct progress *progress; struct progress *progress;
@ -177,6 +178,7 @@ void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
/* detached HEAD is not included in the list above */ /* detached HEAD is not included in the list above */
head_ref(add_one_ref, revs); head_ref(add_one_ref, revs);
other_head_refs(add_one_ref, revs);
/* Add all reflog info */ /* Add all reflog info */
if (mark_reflog) if (mark_reflog)

110
refs.c
View File

@ -336,12 +336,6 @@ int for_each_tag_ref(each_ref_fn fn, void *cb_data)
return refs_for_each_tag_ref(get_main_ref_store(), 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 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) 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); return refs_for_each_ref_in(refs, "refs/heads/", fn, cb_data);
@ -352,12 +346,6 @@ int for_each_branch_ref(each_ref_fn fn, void *cb_data)
return refs_for_each_branch_ref(get_main_ref_store(), 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 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) 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); return refs_for_each_ref_in(refs, "refs/remotes/", fn, cb_data);
@ -368,12 +356,6 @@ int for_each_remote_ref(each_ref_fn fn, void *cb_data)
return refs_for_each_remote_ref(get_main_ref_store(), 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 refs_for_each_remote_ref(get_submodule_ref_store(submodule),
fn, cb_data);
}
int head_ref_namespaced(each_ref_fn fn, void *cb_data) int head_ref_namespaced(each_ref_fn fn, void *cb_data)
{ {
struct strbuf buf = STRBUF_INIT; struct strbuf buf = STRBUF_INIT;
@ -1266,19 +1248,13 @@ int refs_rename_ref_available(struct ref_store *refs,
return ok; 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; struct object_id oid;
int flag; int flag;
if (submodule) { if (!refs_read_ref_full(refs, "HEAD", RESOLVE_REF_READING,
if (resolve_gitlink_ref(submodule, "HEAD", oid.hash) == 0) oid.hash, &flag))
return fn("HEAD", &oid, 0, cb_data);
return 0;
}
if (!read_ref_full("HEAD", RESOLVE_REF_READING, oid.hash, &flag))
return fn("HEAD", &oid, flag, cb_data); return fn("HEAD", &oid, flag, cb_data);
return 0; return 0;
@ -1286,7 +1262,7 @@ int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
int head_ref(each_ref_fn fn, void *cb_data) 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( struct ref_iterator *refs_ref_iterator_begin(
@ -1344,11 +1320,6 @@ int for_each_ref(each_ref_fn fn, void *cb_data)
return refs_for_each_ref(get_main_ref_store(), fn, 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 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, int refs_for_each_ref_in(struct ref_store *refs, const char *prefix,
each_ref_fn fn, void *cb_data) each_ref_fn fn, void *cb_data)
{ {
@ -1370,23 +1341,15 @@ int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, unsig
prefix, fn, 0, flag, cb_data); prefix, fn, 0, flag, cb_data);
} }
int for_each_ref_in_submodule(const char *submodule, const char *prefix, int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,
each_ref_fn fn, void *cb_data) each_ref_fn fn, void *cb_data,
{ unsigned int broken)
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)
{ {
unsigned int flag = 0; unsigned int flag = 0;
if (broken) if (broken)
flag = DO_FOR_EACH_INCLUDE_BROKEN; flag = DO_FOR_EACH_INCLUDE_BROKEN;
return do_for_each_ref(get_submodule_ref_store(submodule), return do_for_each_ref(refs, prefix, fn, 0, flag, cb_data);
prefix, fn, 0, flag, cb_data);
} }
int for_each_replace_ref(each_ref_fn fn, void *cb_data) int for_each_replace_ref(each_ref_fn fn, void *cb_data)
@ -1521,25 +1484,10 @@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
int resolve_gitlink_ref(const char *submodule, const char *refname, int resolve_gitlink_ref(const char *submodule, const char *refname,
unsigned char *sha1) unsigned char *sha1)
{ {
size_t len = strlen(submodule);
struct ref_store *refs; struct ref_store *refs;
int flags; int flags;
while (len && submodule[len - 1] == '/') refs = get_submodule_ref_store(submodule);
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);
}
if (!refs) if (!refs)
return -1; return -1;
@ -1654,31 +1602,32 @@ struct ref_store *get_submodule_ref_store(const char *submodule)
{ {
struct strbuf submodule_sb = STRBUF_INIT; struct strbuf submodule_sb = STRBUF_INIT;
struct ref_store *refs; struct ref_store *refs;
int ret; char *to_free = NULL;
size_t len;
if (!submodule || !*submodule) { if (!submodule)
/* return NULL;
* FIXME: This case is ideally not allowed. But that
* can't happen until we clean up all the callers. len = strlen(submodule);
*/ while (len && is_dir_sep(submodule[len - 1]))
return get_main_ref_store(); 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); refs = lookup_ref_store_map(&submodule_ref_stores, submodule);
if (refs) if (refs)
return refs; goto done;
strbuf_addstr(&submodule_sb, submodule); strbuf_addstr(&submodule_sb, submodule);
ret = is_nonbare_repository_dir(&submodule_sb); if (!is_nonbare_repository_dir(&submodule_sb))
strbuf_release(&submodule_sb); goto done;
if (!ret)
return NULL;
ret = submodule_to_gitdir(&submodule_sb, submodule); if (submodule_to_gitdir(&submodule_sb, submodule))
if (ret) { goto done;
strbuf_release(&submodule_sb);
return NULL;
}
/* assume that add_submodule_odb() has been called */ /* assume that add_submodule_odb() has been called */
refs = ref_store_init(submodule_sb.buf, refs = ref_store_init(submodule_sb.buf,
@ -1686,7 +1635,10 @@ struct ref_store *get_submodule_ref_store(const char *submodule)
register_ref_store_map(&submodule_ref_stores, "submodule", register_ref_store_map(&submodule_ref_stores, "submodule",
refs, submodule); refs, submodule);
done:
strbuf_release(&submodule_sb); strbuf_release(&submodule_sb);
free(to_free);
return refs; return refs;
} }

20
refs.h
View File

@ -275,6 +275,8 @@ typedef int each_ref_fn(const char *refname,
* modifies the reference also returns a nonzero value to immediately * modifies the reference also returns a nonzero value to immediately
* stop the iteration. Returned references are sorted. * stop the iteration. Returned references are sorted.
*/ */
int refs_head_ref(struct ref_store *refs,
each_ref_fn fn, void *cb_data);
int refs_for_each_ref(struct ref_store *refs, int refs_for_each_ref(struct ref_store *refs,
each_ref_fn fn, void *cb_data); each_ref_fn fn, void *cb_data);
int refs_for_each_ref_in(struct ref_store *refs, const char *prefix, int refs_for_each_ref_in(struct ref_store *refs, const char *prefix,
@ -289,6 +291,9 @@ int refs_for_each_remote_ref(struct ref_store *refs,
int head_ref(each_ref_fn fn, void *cb_data); int head_ref(each_ref_fn fn, void *cb_data);
int for_each_ref(each_ref_fn fn, void *cb_data); int for_each_ref(each_ref_fn fn, void *cb_data);
int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data); int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data);
int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,
each_ref_fn fn, void *cb_data,
unsigned int broken);
int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data,
unsigned int broken); unsigned int broken);
int for_each_tag_ref(each_ref_fn fn, void *cb_data); int for_each_tag_ref(each_ref_fn fn, void *cb_data);
@ -299,21 +304,6 @@ int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data);
int for_each_glob_ref_in(each_ref_fn fn, const char *pattern, int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
const char *prefix, void *cb_data); const char *prefix, void *cb_data);
int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
int for_each_ref_submodule(const char *submodule,
each_ref_fn fn, void *cb_data);
int for_each_ref_in_submodule(const char *submodule, const char *prefix,
each_ref_fn fn, void *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 for_each_tag_ref_submodule(const char *submodule,
each_ref_fn fn, void *cb_data);
int for_each_branch_ref_submodule(const char *submodule,
each_ref_fn fn, void *cb_data);
int for_each_remote_ref_submodule(const char *submodule,
each_ref_fn fn, void *cb_data);
int head_ref_namespaced(each_ref_fn fn, void *cb_data); int head_ref_namespaced(each_ref_fn fn, void *cb_data);
int for_each_namespaced_ref(each_ref_fn fn, void *cb_data); int for_each_namespaced_ref(each_ref_fn fn, void *cb_data);

View File

@ -106,15 +106,6 @@ static void files_reflog_path(struct files_ref_store *refs,
struct strbuf *sb, struct strbuf *sb,
const char *refname) 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)) { switch (ref_type(refname)) {
case REF_TYPE_PER_WORKTREE: case REF_TYPE_PER_WORKTREE:
case REF_TYPE_PSEUDOREF: case REF_TYPE_PSEUDOREF:
@ -2059,23 +2050,63 @@ static struct ref_iterator_vtable files_reflog_iterator_vtable = {
files_reflog_iterator_abort 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 files_reflog_iterator *iter = xcalloc(1, sizeof(*iter));
struct ref_iterator *ref_iterator = &iter->base; struct ref_iterator *ref_iterator = &iter->base;
struct strbuf sb = STRBUF_INIT; struct strbuf sb = STRBUF_INIT;
base_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable); 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->dir_iterator = dir_iterator_begin(sb.buf);
iter->ref_store = ref_store; iter->ref_store = ref_store;
strbuf_release(&sb); strbuf_release(&sb);
return ref_iterator; 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 * 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. * by HEAD), then add an extra REF_LOG_ONLY update for HEAD.

View File

@ -20,6 +20,7 @@
#include "cache-tree.h" #include "cache-tree.h"
#include "bisect.h" #include "bisect.h"
#include "packfile.h" #include "packfile.h"
#include "worktree.h"
volatile show_early_output_fn_t show_early_output; volatile show_early_output_fn_t show_early_output;
@ -1132,6 +1133,7 @@ struct all_refs_cb {
int warned_bad_reflog; int warned_bad_reflog;
struct rev_info *all_revs; struct rev_info *all_revs;
const char *name_for_errormsg; const char *name_for_errormsg;
struct ref_store *refs;
}; };
int ref_excluded(struct string_list *ref_excludes, const char *path) int ref_excluded(struct string_list *ref_excludes, const char *path)
@ -1168,6 +1170,7 @@ static void init_all_refs_cb(struct all_refs_cb *cb, struct rev_info *revs,
cb->all_revs = revs; cb->all_revs = revs;
cb->all_flags = flags; cb->all_flags = flags;
revs->rev_input_given = 1; revs->rev_input_given = 1;
cb->refs = NULL;
} }
void clear_ref_exclusion(struct string_list **ref_excludes_p) void clear_ref_exclusion(struct string_list **ref_excludes_p)
@ -1188,12 +1191,19 @@ void add_ref_exclusion(struct string_list **ref_excludes_p, const char *exclude)
string_list_append(*ref_excludes_p, exclude); string_list_append(*ref_excludes_p, exclude);
} }
static void handle_refs(const char *submodule, struct rev_info *revs, unsigned flags, static void handle_refs(struct ref_store *refs,
int (*for_each)(const char *, each_ref_fn, void *)) struct rev_info *revs, unsigned flags,
int (*for_each)(struct ref_store *, each_ref_fn, void *))
{ {
struct all_refs_cb cb; struct all_refs_cb cb;
if (!refs) {
/* this could happen with uninitialized submodules */
return;
}
init_all_refs_cb(&cb, revs, flags); 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) static void handle_one_reflog_commit(struct object_id *oid, void *cb_data)
@ -1229,17 +1239,41 @@ static int handle_one_reflog(const char *path, const struct object_id *oid,
struct all_refs_cb *cb = cb_data; struct all_refs_cb *cb = cb_data;
cb->warned_bad_reflog = 0; cb->warned_bad_reflog = 0;
cb->name_for_errormsg = path; 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; 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) void add_reflogs_to_pending(struct rev_info *revs, unsigned flags)
{ {
struct all_refs_cb cb; struct all_refs_cb cb;
cb.all_revs = revs; cb.all_revs = revs;
cb.all_flags = flags; cb.all_flags = flags;
cb.refs = get_main_ref_store();
for_each_reflog(handle_one_reflog, &cb); 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, static void add_cache_tree(struct cache_tree *it, struct rev_info *revs,
@ -1263,13 +1297,13 @@ static void add_cache_tree(struct cache_tree *it, struct rev_info *revs,
} }
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; int i;
read_cache(); for (i = 0; i < istate->cache_nr; i++) {
for (i = 0; i < active_nr; i++) { struct cache_entry *ce = istate->cache[i];
struct cache_entry *ce = active_cache[i];
struct blob *blob; struct blob *blob;
if (S_ISGITLINK(ce->ce_mode)) if (S_ISGITLINK(ce->ce_mode))
@ -1282,13 +1316,39 @@ void add_index_objects_to_pending(struct rev_info *revs, unsigned flags)
ce->ce_mode, ce->name); ce->ce_mode, ce->name);
} }
if (active_cache_tree) { if (istate->cache_tree) {
struct strbuf path = STRBUF_INIT; struct strbuf path = STRBUF_INIT;
add_cache_tree(active_cache_tree, revs, &path); add_cache_tree(istate->cache_tree, revs, &path);
strbuf_release(&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, static int add_parents_only(struct rev_info *revs, const char *arg_, int flags,
int exclude_parent) int exclude_parent)
{ {
@ -2069,23 +2129,25 @@ void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
ctx->argc -= n; 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; struct strbuf bisect_refs = STRBUF_INIT;
int status; int status;
strbuf_addf(&bisect_refs, "refs/bisect/%s", term); 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); strbuf_release(&bisect_refs);
return status; 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, static int handle_revision_pseudo_opt(const char *submodule,
@ -2094,8 +2156,22 @@ static int handle_revision_pseudo_opt(const char *submodule,
{ {
const char *arg = argv[0]; const char *arg = argv[0];
const char *optarg; const char *optarg;
struct ref_store *refs;
int argcount; 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! * NOTE!
* *
@ -2107,22 +2183,29 @@ static int handle_revision_pseudo_opt(const char *submodule,
* register it in the list at the top of handle_revision_opt. * register it in the list at the top of handle_revision_opt.
*/ */
if (!strcmp(arg, "--all")) { if (!strcmp(arg, "--all")) {
handle_refs(submodule, revs, *flags, for_each_ref_submodule); handle_refs(refs, revs, *flags, refs_for_each_ref);
handle_refs(submodule, revs, *flags, head_ref_submodule); 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); clear_ref_exclusion(&revs->ref_excludes);
} else if (!strcmp(arg, "--branches")) { } 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); clear_ref_exclusion(&revs->ref_excludes);
} else if (!strcmp(arg, "--bisect")) { } else if (!strcmp(arg, "--bisect")) {
read_bisect_terms(&term_bad, &term_good); read_bisect_terms(&term_bad, &term_good);
handle_refs(submodule, revs, *flags, for_each_bad_bisect_ref); handle_refs(refs, revs, *flags, for_each_bad_bisect_ref);
handle_refs(submodule, revs, *flags ^ (UNINTERESTING | BOTTOM), for_each_good_bisect_ref); handle_refs(refs, revs, *flags ^ (UNINTERESTING | BOTTOM),
for_each_good_bisect_ref);
revs->bisect = 1; revs->bisect = 1;
} else if (!strcmp(arg, "--tags")) { } 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); clear_ref_exclusion(&revs->ref_excludes);
} else if (!strcmp(arg, "--remotes")) { } 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); clear_ref_exclusion(&revs->ref_excludes);
} else if ((argcount = parse_long_opt("glob", argv, &optarg))) { } else if ((argcount = parse_long_opt("glob", argv, &optarg))) {
struct all_refs_cb cb; struct all_refs_cb cb;
@ -2169,6 +2252,8 @@ static int handle_revision_pseudo_opt(const char *submodule,
return error("invalid argument to --no-walk"); return error("invalid argument to --no-walk");
} else if (!strcmp(arg, "--do-walk")) { } else if (!strcmp(arg, "--do-walk")) {
revs->no_walk = 0; revs->no_walk = 0;
} else if (!strcmp(arg, "--single-worktree")) {
revs->single_worktree = 1;
} else { } else {
return 0; return 0;
} }

View File

@ -96,6 +96,7 @@ struct rev_info {
topo_order:1, topo_order:1,
simplify_merges:1, simplify_merges:1,
simplify_by_decoration:1, simplify_by_decoration:1,
single_worktree:1,
tag_objects:1, tag_objects:1,
tree_objects:1, tree_objects:1,
blob_objects:1, blob_objects:1,

View File

@ -69,6 +69,13 @@ int is_staging_gitmodules_ok(const struct index_state *istate)
return 1; 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.<name>" section of the * Try to update the "path" entry in the "submodule.<name>" section of the
* .gitmodules file. Return 0 only if a .gitmodules file was found, a section * .gitmodules file. Return 0 only if a .gitmodules file was found, a section
@ -1627,6 +1634,8 @@ static int find_first_merges(struct object_array *result, const char *path,
oid_to_hex(&a->object.oid)); oid_to_hex(&a->object.oid));
init_revisions(&revs, NULL); init_revisions(&revs, NULL);
rev_opts.submodule = path; 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); setup_revisions(ARRAY_SIZE(rev_args)-1, rev_args, &revs, &rev_opts);
/* save all revisions from the above list that contain b */ /* save all revisions from the above list that contain b */

View File

@ -49,4 +49,34 @@ test_expect_success 'create_symref(FOO, refs/heads/master)' '
test_cmp expected actual test_cmp expected actual
' '
test_expect_success 'for_each_reflog()' '
echo $_z40 > .git/logs/PSEUDO-MAIN &&
mkdir -p .git/logs/refs/bisect &&
echo $_z40 > .git/logs/refs/bisect/random &&
echo $_z40 > .git/worktrees/wt/logs/PSEUDO-WT &&
mkdir -p .git/worktrees/wt/logs/refs/bisect &&
echo $_z40 > .git/worktrees/wt/logs/refs/bisect/wt-random &&
$RWT for-each-reflog | cut -c 42- | sort >actual &&
cat >expected <<-\EOF &&
HEAD 0x1
PSEUDO-WT 0x0
refs/bisect/wt-random 0x0
refs/heads/master 0x0
refs/heads/wt-master 0x0
EOF
test_cmp expected actual &&
$RMAIN for-each-reflog | cut -c 42- | sort >actual &&
cat >expected <<-\EOF &&
HEAD 0x1
PSEUDO-MAIN 0x0
refs/bisect/random 0x0
refs/heads/master 0x0
refs/heads/wt-master 0x0
EOF
test_cmp expected actual
'
test_done test_done

View File

@ -283,4 +283,41 @@ test_expect_success 'prune: handle alternate object database' '
git -C B prune git -C B prune
' '
test_expect_success 'prune: handle index in multiple worktrees' '
git worktree add second-worktree &&
echo "new blob for second-worktree" >second-worktree/blob &&
git -C second-worktree add blob &&
git prune --expire=now &&
git -C second-worktree show :blob >actual &&
test_cmp second-worktree/blob actual
'
test_expect_success 'prune: handle HEAD in multiple worktrees' '
git worktree add --detach third-worktree &&
echo "new blob for third-worktree" >third-worktree/blob &&
git -C third-worktree add blob &&
git -C third-worktree commit -m "third" &&
rm .git/worktrees/third-worktree/index &&
test_must_fail git -C third-worktree show :blob &&
git prune --expire=now &&
git -C third-worktree show HEAD:blob >actual &&
test_cmp third-worktree/blob actual
'
test_expect_success 'prune: handle HEAD reflog in multiple worktrees' '
git config core.logAllRefUpdates true &&
echo "lost blob for third-worktree" >expected &&
(
cd third-worktree &&
cat ../expected >blob &&
git add blob &&
git commit -m "second commit in third" &&
git reset --hard HEAD^
) &&
git prune --expire=now &&
SHA1=`git hash-object expected` &&
git -C third-worktree show "$SHA1" >actual &&
test_cmp expected actual
'
test_done test_done

View File

@ -386,3 +386,25 @@ int submodule_uses_worktrees(const char *path)
closedir(dir); closedir(dir);
return ret; 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;
}

View File

@ -1,6 +1,8 @@
#ifndef WORKTREE_H #ifndef WORKTREE_H
#define WORKTREE_H #define WORKTREE_H
#include "refs.h"
struct worktree { struct worktree {
char *path; char *path;
char *id; char *id;
@ -70,6 +72,12 @@ extern void free_worktrees(struct worktree **);
extern const struct worktree *find_shared_symref(const char *symref, extern const struct worktree *find_shared_symref(const char *symref,
const char *target); const char *target);
/*
* Similar to head_ref() for all HEADs _except_ one from the current
* worktree, which is covered by head_ref().
*/
int other_head_refs(each_ref_fn fn, void *cb_data);
int is_worktree_being_rebased(const struct worktree *wt, const char *target); int is_worktree_being_rebased(const struct worktree *wt, const char *target);
int is_worktree_being_bisected(const struct worktree *wt, const char *target); int is_worktree_being_bisected(const struct worktree *wt, const char *target);