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:
commit
8a044c7f1d
@ -184,6 +184,14 @@ explicitly.
|
||||
Pretend as if all objects mentioned by reflogs are listed on the
|
||||
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::
|
||||
Upon seeing an invalid object name in the input, pretend as if
|
||||
the bad input was not given.
|
||||
|
@ -32,11 +32,8 @@ Iteration functions
|
||||
|
||||
* `for_each_glob_ref_in()` the previous and `for_each_ref_in()` combined.
|
||||
|
||||
* `head_ref_submodule()`, `for_each_ref_submodule()`,
|
||||
`for_each_ref_in_submodule()`, `for_each_tag_ref_submodule()`,
|
||||
`for_each_branch_ref_submodule()`, `for_each_remote_ref_submodule()`
|
||||
do the same as the functions described above but for a specified
|
||||
submodule.
|
||||
* Use `refs_` API for accessing submodules. The submodule ref store could
|
||||
be obtained with `get_submodule_ref_store()`.
|
||||
|
||||
* `for_each_rawref()` can be used to learn about broken ref and symref.
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "progress.h"
|
||||
#include "list-objects.h"
|
||||
#include "packfile.h"
|
||||
#include "worktree.h"
|
||||
|
||||
struct connectivity_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 */
|
||||
head_ref(add_one_ref, revs);
|
||||
other_head_refs(add_one_ref, revs);
|
||||
|
||||
/* Add all reflog info */
|
||||
if (mark_reflog)
|
||||
|
110
refs.c
110
refs.c
@ -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);
|
||||
}
|
||||
|
||||
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 @@ 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);
|
||||
}
|
||||
|
||||
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 @@ 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);
|
||||
}
|
||||
|
||||
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;
|
||||
@ -1266,19 +1248,13 @@ int refs_rename_ref_available(struct ref_store *refs,
|
||||
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 +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)
|
||||
{
|
||||
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 +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);
|
||||
}
|
||||
|
||||
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 +1341,15 @@ int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, unsig
|
||||
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 +1484,10 @@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
|
||||
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 +1602,32 @@ struct ref_store *get_submodule_ref_store(const char *submodule)
|
||||
{
|
||||
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 +1635,10 @@ struct ref_store *get_submodule_ref_store(const char *submodule)
|
||||
register_ref_store_map(&submodule_ref_stores, "submodule",
|
||||
refs, submodule);
|
||||
|
||||
done:
|
||||
strbuf_release(&submodule_sb);
|
||||
free(to_free);
|
||||
|
||||
return refs;
|
||||
}
|
||||
|
||||
|
20
refs.h
20
refs.h
@ -275,6 +275,8 @@ typedef int each_ref_fn(const char *refname,
|
||||
* modifies the reference also returns a nonzero value to immediately
|
||||
* 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,
|
||||
each_ref_fn fn, void *cb_data);
|
||||
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 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 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,
|
||||
unsigned int broken);
|
||||
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,
|
||||
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 for_each_namespaced_ref(each_ref_fn fn, void *cb_data);
|
||||
|
||||
|
@ -106,15 +106,6 @@ static void files_reflog_path(struct files_ref_store *refs,
|
||||
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:
|
||||
@ -2059,23 +2050,63 @@ static struct ref_iterator_vtable files_reflog_iterator_vtable = {
|
||||
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.
|
||||
|
131
revision.c
131
revision.c
@ -20,6 +20,7 @@
|
||||
#include "cache-tree.h"
|
||||
#include "bisect.h"
|
||||
#include "packfile.h"
|
||||
#include "worktree.h"
|
||||
|
||||
volatile show_early_output_fn_t show_early_output;
|
||||
|
||||
@ -1132,6 +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 +1170,7 @@ static void init_all_refs_cb(struct all_refs_cb *cb, struct rev_info *revs,
|
||||
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 +1191,19 @@ void add_ref_exclusion(struct string_list **ref_excludes_p, const char *exclude)
|
||||
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 +1239,41 @@ static int handle_one_reflog(const char *path, const struct object_id *oid,
|
||||
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 +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;
|
||||
|
||||
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 +1316,39 @@ void add_index_objects_to_pending(struct rev_info *revs, unsigned flags)
|
||||
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 +2129,25 @@ void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
|
||||
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 +2156,22 @@ static int handle_revision_pseudo_opt(const char *submodule,
|
||||
{
|
||||
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 +2183,29 @@ static int handle_revision_pseudo_opt(const char *submodule,
|
||||
* 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 +2252,8 @@ static int handle_revision_pseudo_opt(const char *submodule,
|
||||
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;
|
||||
}
|
||||
|
@ -96,6 +96,7 @@ struct rev_info {
|
||||
topo_order:1,
|
||||
simplify_merges:1,
|
||||
simplify_by_decoration:1,
|
||||
single_worktree:1,
|
||||
tag_objects:1,
|
||||
tree_objects:1,
|
||||
blob_objects:1,
|
||||
|
@ -69,6 +69,13 @@ int is_staging_gitmodules_ok(const struct index_state *istate)
|
||||
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
|
||||
* .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));
|
||||
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 */
|
||||
|
@ -49,4 +49,34 @@ test_expect_success 'create_symref(FOO, refs/heads/master)' '
|
||||
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
|
||||
|
@ -283,4 +283,41 @@ test_expect_success 'prune: handle alternate object database' '
|
||||
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
|
||||
|
22
worktree.c
22
worktree.c
@ -386,3 +386,25 @@ int submodule_uses_worktrees(const char *path)
|
||||
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;
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
#ifndef WORKTREE_H
|
||||
#define WORKTREE_H
|
||||
|
||||
#include "refs.h"
|
||||
|
||||
struct worktree {
|
||||
char *path;
|
||||
char *id;
|
||||
@ -70,6 +72,12 @@ extern void free_worktrees(struct worktree **);
|
||||
extern const struct worktree *find_shared_symref(const char *symref,
|
||||
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_bisected(const struct worktree *wt, const char *target);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user