зеркало из https://github.com/microsoft/git.git
Merge branch 'nd/worktree-kill-parse-ref'
"git gc" did not interact well with "git worktree"-managed per-worktree refs. * nd/worktree-kill-parse-ref: refs: kill set_worktree_head_symref() worktree.c: kill parse_ref() in favor of refs_resolve_ref_unsafe() refs: introduce get_worktree_ref_store() refs: add REFS_STORE_ALL_CAPS refs.c: make submodule ref store hashmap generic environment.c: fix potential segfault by get_git_common_dir()
This commit is contained in:
Коммит
4b44b7b1df
15
branch.c
15
branch.c
|
@ -353,17 +353,18 @@ int replace_each_worktree_head_symref(const char *oldref, const char *newref,
|
|||
int i;
|
||||
|
||||
for (i = 0; worktrees[i]; i++) {
|
||||
struct ref_store *refs;
|
||||
|
||||
if (worktrees[i]->is_detached)
|
||||
continue;
|
||||
if (strcmp(oldref, worktrees[i]->head_ref))
|
||||
if (worktrees[i]->head_ref &&
|
||||
strcmp(oldref, worktrees[i]->head_ref))
|
||||
continue;
|
||||
|
||||
if (set_worktree_head_symref(get_worktree_git_dir(worktrees[i]),
|
||||
newref, logmsg)) {
|
||||
ret = -1;
|
||||
error(_("HEAD of working tree %s is not updated"),
|
||||
worktrees[i]->path);
|
||||
}
|
||||
refs = get_worktree_ref_store(worktrees[i]);
|
||||
if (refs_create_symref(refs, "HEAD", newref, logmsg))
|
||||
ret = error(_("HEAD of working tree %s is not updated"),
|
||||
worktrees[i]->path);
|
||||
}
|
||||
|
||||
free_worktrees(worktrees);
|
||||
|
|
|
@ -217,6 +217,8 @@ const char *get_git_dir(void)
|
|||
|
||||
const char *get_git_common_dir(void)
|
||||
{
|
||||
if (!git_dir)
|
||||
setup_git_env();
|
||||
return git_common_dir;
|
||||
}
|
||||
|
||||
|
|
106
refs.c
106
refs.c
|
@ -11,6 +11,7 @@
|
|||
#include "object.h"
|
||||
#include "tag.h"
|
||||
#include "submodule.h"
|
||||
#include "worktree.h"
|
||||
|
||||
/*
|
||||
* List of all available backends
|
||||
|
@ -1477,32 +1478,32 @@ int resolve_gitlink_ref(const char *submodule, const char *refname,
|
|||
return 0;
|
||||
}
|
||||
|
||||
struct submodule_hash_entry
|
||||
struct ref_store_hash_entry
|
||||
{
|
||||
struct hashmap_entry ent; /* must be the first member! */
|
||||
|
||||
struct ref_store *refs;
|
||||
|
||||
/* NUL-terminated name of submodule: */
|
||||
char submodule[FLEX_ARRAY];
|
||||
/* NUL-terminated identifier of the ref store: */
|
||||
char name[FLEX_ARRAY];
|
||||
};
|
||||
|
||||
static int submodule_hash_cmp(const void *entry, const void *entry_or_key,
|
||||
static int ref_store_hash_cmp(const void *entry, const void *entry_or_key,
|
||||
const void *keydata)
|
||||
{
|
||||
const struct submodule_hash_entry *e1 = entry, *e2 = entry_or_key;
|
||||
const char *submodule = keydata ? keydata : e2->submodule;
|
||||
const struct ref_store_hash_entry *e1 = entry, *e2 = entry_or_key;
|
||||
const char *name = keydata ? keydata : e2->name;
|
||||
|
||||
return strcmp(e1->submodule, submodule);
|
||||
return strcmp(e1->name, name);
|
||||
}
|
||||
|
||||
static struct submodule_hash_entry *alloc_submodule_hash_entry(
|
||||
const char *submodule, struct ref_store *refs)
|
||||
static struct ref_store_hash_entry *alloc_ref_store_hash_entry(
|
||||
const char *name, struct ref_store *refs)
|
||||
{
|
||||
struct submodule_hash_entry *entry;
|
||||
struct ref_store_hash_entry *entry;
|
||||
|
||||
FLEX_ALLOC_STR(entry, submodule, submodule);
|
||||
hashmap_entry_init(entry, strhash(submodule));
|
||||
FLEX_ALLOC_STR(entry, name, name);
|
||||
hashmap_entry_init(entry, strhash(name));
|
||||
entry->refs = refs;
|
||||
return entry;
|
||||
}
|
||||
|
@ -1513,20 +1514,23 @@ static struct ref_store *main_ref_store;
|
|||
/* A hashmap of ref_stores, stored by submodule name: */
|
||||
static struct hashmap submodule_ref_stores;
|
||||
|
||||
/*
|
||||
* Return the ref_store instance for the specified submodule. If that
|
||||
* ref_store hasn't been initialized yet, return NULL.
|
||||
*/
|
||||
static struct ref_store *lookup_submodule_ref_store(const char *submodule)
|
||||
{
|
||||
struct submodule_hash_entry *entry;
|
||||
/* A hashmap of ref_stores, stored by worktree id: */
|
||||
static struct hashmap worktree_ref_stores;
|
||||
|
||||
if (!submodule_ref_stores.tablesize)
|
||||
/*
|
||||
* Look up a ref store by name. If that ref_store hasn't been
|
||||
* registered yet, return NULL.
|
||||
*/
|
||||
static struct ref_store *lookup_ref_store_map(struct hashmap *map,
|
||||
const char *name)
|
||||
{
|
||||
struct ref_store_hash_entry *entry;
|
||||
|
||||
if (!map->tablesize)
|
||||
/* It's initialized on demand in register_ref_store(). */
|
||||
return NULL;
|
||||
|
||||
entry = hashmap_get_from_hash(&submodule_ref_stores,
|
||||
strhash(submodule), submodule);
|
||||
entry = hashmap_get_from_hash(map, strhash(name), name);
|
||||
return entry ? entry->refs : NULL;
|
||||
}
|
||||
|
||||
|
@ -1553,29 +1557,24 @@ struct ref_store *get_main_ref_store(void)
|
|||
if (main_ref_store)
|
||||
return main_ref_store;
|
||||
|
||||
main_ref_store = ref_store_init(get_git_dir(),
|
||||
(REF_STORE_READ |
|
||||
REF_STORE_WRITE |
|
||||
REF_STORE_ODB |
|
||||
REF_STORE_MAIN));
|
||||
main_ref_store = ref_store_init(get_git_dir(), REF_STORE_ALL_CAPS);
|
||||
return main_ref_store;
|
||||
}
|
||||
|
||||
/*
|
||||
* Register the specified ref_store to be the one that should be used
|
||||
* for submodule. It is a fatal error to call this function twice for
|
||||
* the same submodule.
|
||||
* Associate a ref store with a name. It is a fatal error to call this
|
||||
* function twice for the same name.
|
||||
*/
|
||||
static void register_submodule_ref_store(struct ref_store *refs,
|
||||
const char *submodule)
|
||||
static void register_ref_store_map(struct hashmap *map,
|
||||
const char *type,
|
||||
struct ref_store *refs,
|
||||
const char *name)
|
||||
{
|
||||
if (!submodule_ref_stores.tablesize)
|
||||
hashmap_init(&submodule_ref_stores, submodule_hash_cmp, 0);
|
||||
if (!map->tablesize)
|
||||
hashmap_init(map, ref_store_hash_cmp, 0);
|
||||
|
||||
if (hashmap_put(&submodule_ref_stores,
|
||||
alloc_submodule_hash_entry(submodule, refs)))
|
||||
die("BUG: ref_store for submodule '%s' initialized twice",
|
||||
submodule);
|
||||
if (hashmap_put(map, alloc_ref_store_hash_entry(name, refs)))
|
||||
die("BUG: %s ref_store '%s' initialized twice", type, name);
|
||||
}
|
||||
|
||||
struct ref_store *get_submodule_ref_store(const char *submodule)
|
||||
|
@ -1592,7 +1591,7 @@ struct ref_store *get_submodule_ref_store(const char *submodule)
|
|||
return get_main_ref_store();
|
||||
}
|
||||
|
||||
refs = lookup_submodule_ref_store(submodule);
|
||||
refs = lookup_ref_store_map(&submodule_ref_stores, submodule);
|
||||
if (refs)
|
||||
return refs;
|
||||
|
||||
|
@ -1611,12 +1610,39 @@ struct ref_store *get_submodule_ref_store(const char *submodule)
|
|||
/* assume that add_submodule_odb() has been called */
|
||||
refs = ref_store_init(submodule_sb.buf,
|
||||
REF_STORE_READ | REF_STORE_ODB);
|
||||
register_submodule_ref_store(refs, submodule);
|
||||
register_ref_store_map(&submodule_ref_stores, "submodule",
|
||||
refs, submodule);
|
||||
|
||||
strbuf_release(&submodule_sb);
|
||||
return refs;
|
||||
}
|
||||
|
||||
struct ref_store *get_worktree_ref_store(const struct worktree *wt)
|
||||
{
|
||||
struct ref_store *refs;
|
||||
const char *id;
|
||||
|
||||
if (wt->is_current)
|
||||
return get_main_ref_store();
|
||||
|
||||
id = wt->id ? wt->id : "/";
|
||||
refs = lookup_ref_store_map(&worktree_ref_stores, id);
|
||||
if (refs)
|
||||
return refs;
|
||||
|
||||
if (wt->id)
|
||||
refs = ref_store_init(git_common_path("worktrees/%s", wt->id),
|
||||
REF_STORE_ALL_CAPS);
|
||||
else
|
||||
refs = ref_store_init(get_git_common_dir(),
|
||||
REF_STORE_ALL_CAPS);
|
||||
|
||||
if (refs)
|
||||
register_ref_store_map(&worktree_ref_stores, "worktree",
|
||||
refs, id);
|
||||
return refs;
|
||||
}
|
||||
|
||||
void base_ref_store_init(struct ref_store *refs,
|
||||
const struct ref_storage_be *be)
|
||||
{
|
||||
|
|
12
refs.h
12
refs.h
|
@ -5,6 +5,7 @@ struct object_id;
|
|||
struct ref_store;
|
||||
struct strbuf;
|
||||
struct string_list;
|
||||
struct worktree;
|
||||
|
||||
/*
|
||||
* Resolve a reference, recursively following symbolic refererences.
|
||||
|
@ -401,16 +402,6 @@ int refs_create_symref(struct ref_store *refs, const char *refname,
|
|||
const char *target, const char *logmsg);
|
||||
int create_symref(const char *refname, const char *target, const char *logmsg);
|
||||
|
||||
/*
|
||||
* Update HEAD of the specified gitdir.
|
||||
* Similar to create_symref("relative-git-dir/HEAD", target, NULL), but
|
||||
* this can update the main working tree's HEAD regardless of where
|
||||
* $GIT_DIR points to.
|
||||
* Return 0 if successful, non-zero otherwise.
|
||||
* */
|
||||
int set_worktree_head_symref(const char *gitdir, const char *target,
|
||||
const char *logmsg);
|
||||
|
||||
enum action_on_err {
|
||||
UPDATE_REFS_MSG_ON_ERR,
|
||||
UPDATE_REFS_DIE_ON_ERR,
|
||||
|
@ -655,5 +646,6 @@ struct ref_store *get_main_ref_store(void);
|
|||
* submodule==NULL.
|
||||
*/
|
||||
struct ref_store *get_submodule_ref_store(const char *submodule);
|
||||
struct ref_store *get_worktree_ref_store(const struct worktree *wt);
|
||||
|
||||
#endif /* REFS_H */
|
||||
|
|
|
@ -2240,50 +2240,6 @@ static int files_create_symref(struct ref_store *ref_store,
|
|||
return ret;
|
||||
}
|
||||
|
||||
int set_worktree_head_symref(const char *gitdir, const char *target, const char *logmsg)
|
||||
{
|
||||
/*
|
||||
* FIXME: this obviously will not work well for future refs
|
||||
* backends. This function needs to die.
|
||||
*/
|
||||
struct files_ref_store *refs =
|
||||
files_downcast(get_main_ref_store(),
|
||||
REF_STORE_WRITE,
|
||||
"set_head_symref");
|
||||
|
||||
static struct lock_file head_lock;
|
||||
struct ref_lock *lock;
|
||||
struct strbuf head_path = STRBUF_INIT;
|
||||
const char *head_rel;
|
||||
int ret;
|
||||
|
||||
strbuf_addf(&head_path, "%s/HEAD", absolute_path(gitdir));
|
||||
if (hold_lock_file_for_update(&head_lock, head_path.buf,
|
||||
LOCK_NO_DEREF) < 0) {
|
||||
struct strbuf err = STRBUF_INIT;
|
||||
unable_to_lock_message(head_path.buf, errno, &err);
|
||||
error("%s", err.buf);
|
||||
strbuf_release(&err);
|
||||
strbuf_release(&head_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* head_rel will be "HEAD" for the main tree, "worktrees/wt/HEAD" for
|
||||
linked trees */
|
||||
head_rel = remove_leading_path(head_path.buf,
|
||||
absolute_path(get_git_common_dir()));
|
||||
/* to make use of create_symref_locked(), initialize ref_lock */
|
||||
lock = xcalloc(1, sizeof(struct ref_lock));
|
||||
lock->lk = &head_lock;
|
||||
lock->ref_name = xstrdup(head_rel);
|
||||
|
||||
ret = create_symref_locked(refs, lock, head_rel, target, logmsg);
|
||||
|
||||
unlock_ref(lock); /* will free lock */
|
||||
strbuf_release(&head_path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int files_reflog_exists(struct ref_store *ref_store,
|
||||
const char *refname)
|
||||
{
|
||||
|
|
|
@ -482,6 +482,10 @@ struct ref_store;
|
|||
#define REF_STORE_WRITE (1 << 1) /* can perform update operations */
|
||||
#define REF_STORE_ODB (1 << 2) /* has access to object database */
|
||||
#define REF_STORE_MAIN (1 << 3)
|
||||
#define REF_STORE_ALL_CAPS (REF_STORE_READ | \
|
||||
REF_STORE_WRITE | \
|
||||
REF_STORE_ODB | \
|
||||
REF_STORE_MAIN)
|
||||
|
||||
/*
|
||||
* Initialize the ref_store for the specified gitdir. These functions
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "cache.h"
|
||||
#include "refs.h"
|
||||
#include "worktree.h"
|
||||
|
||||
static const char *notnull(const char *arg, const char *name)
|
||||
{
|
||||
|
@ -32,6 +33,23 @@ static const char **get_store(const char **argv, struct ref_store **refs)
|
|||
strbuf_release(&sb);
|
||||
|
||||
*refs = get_submodule_ref_store(gitdir);
|
||||
} else if (skip_prefix(argv[0], "worktree:", &gitdir)) {
|
||||
struct worktree **p, **worktrees = get_worktrees(0);
|
||||
|
||||
for (p = worktrees; *p; p++) {
|
||||
struct worktree *wt = *p;
|
||||
|
||||
if (!wt->id) {
|
||||
/* special case for main worktree */
|
||||
if (!strcmp(gitdir, "main"))
|
||||
break;
|
||||
} else if (!strcmp(gitdir, wt->id))
|
||||
break;
|
||||
}
|
||||
if (!*p)
|
||||
die("no such worktree: %s", gitdir);
|
||||
|
||||
*refs = get_worktree_ref_store(*p);
|
||||
} else
|
||||
die("unknown backend %s", argv[0]);
|
||||
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
#!/bin/sh
|
||||
|
||||
test_description='test worktree ref store api'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
RWT="test-ref-store worktree:wt"
|
||||
RMAIN="test-ref-store worktree:main"
|
||||
|
||||
test_expect_success 'setup' '
|
||||
test_commit first &&
|
||||
git worktree add -b wt-master wt &&
|
||||
(
|
||||
cd wt &&
|
||||
test_commit second
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'resolve_ref(<shared-ref>)' '
|
||||
SHA1=`git rev-parse master` &&
|
||||
echo "$SHA1 refs/heads/master 0x0" >expected &&
|
||||
$RWT resolve-ref refs/heads/master 0 >actual &&
|
||||
test_cmp expected actual &&
|
||||
$RMAIN resolve-ref refs/heads/master 0 >actual &&
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_expect_success 'resolve_ref(<per-worktree-ref>)' '
|
||||
SHA1=`git -C wt rev-parse HEAD` &&
|
||||
echo "$SHA1 refs/heads/wt-master 0x1" >expected &&
|
||||
$RWT resolve-ref HEAD 0 >actual &&
|
||||
test_cmp expected actual &&
|
||||
|
||||
SHA1=`git rev-parse HEAD` &&
|
||||
echo "$SHA1 refs/heads/master 0x1" >expected &&
|
||||
$RMAIN resolve-ref HEAD 0 >actual &&
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_expect_success 'create_symref(FOO, refs/heads/master)' '
|
||||
$RWT create-symref FOO refs/heads/master nothing &&
|
||||
echo refs/heads/master >expected &&
|
||||
git -C wt symbolic-ref FOO >actual &&
|
||||
test_cmp expected actual &&
|
||||
|
||||
$RMAIN create-symref FOO refs/heads/wt-master nothing &&
|
||||
echo refs/heads/wt-master >expected &&
|
||||
git symbolic-ref FOO >actual &&
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_done
|
102
worktree.c
102
worktree.c
|
@ -19,54 +19,25 @@ void free_worktrees(struct worktree **worktrees)
|
|||
free (worktrees);
|
||||
}
|
||||
|
||||
/*
|
||||
* read 'path_to_ref' into 'ref'. Also if is_detached is not NULL,
|
||||
* set is_detached to 1 (0) if the ref is detached (is not detached).
|
||||
*
|
||||
* $GIT_COMMON_DIR/$symref (e.g. HEAD) is practically outside $GIT_DIR so
|
||||
* for linked worktrees, `resolve_ref_unsafe()` won't work (it uses
|
||||
* git_path). Parse the ref ourselves.
|
||||
*
|
||||
* return -1 if the ref is not a proper ref, 0 otherwise (success)
|
||||
*/
|
||||
static int parse_ref(char *path_to_ref, struct strbuf *ref, int *is_detached)
|
||||
{
|
||||
if (is_detached)
|
||||
*is_detached = 0;
|
||||
if (!strbuf_readlink(ref, path_to_ref, 0)) {
|
||||
/* HEAD is symbolic link */
|
||||
if (!starts_with(ref->buf, "refs/") ||
|
||||
check_refname_format(ref->buf, 0))
|
||||
return -1;
|
||||
} else if (strbuf_read_file(ref, path_to_ref, 0) >= 0) {
|
||||
/* textual symref or detached */
|
||||
if (!starts_with(ref->buf, "ref:")) {
|
||||
if (is_detached)
|
||||
*is_detached = 1;
|
||||
} else {
|
||||
strbuf_remove(ref, 0, strlen("ref:"));
|
||||
strbuf_trim(ref);
|
||||
if (check_refname_format(ref->buf, 0))
|
||||
return -1;
|
||||
}
|
||||
} else
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the head_sha1 and head_ref (if not detached) to the given worktree
|
||||
* Update head_sha1, head_ref and is_detached of the given worktree
|
||||
*/
|
||||
static void add_head_info(struct strbuf *head_ref, struct worktree *worktree)
|
||||
static void add_head_info(struct worktree *wt)
|
||||
{
|
||||
if (head_ref->len) {
|
||||
if (worktree->is_detached) {
|
||||
get_sha1_hex(head_ref->buf, worktree->head_sha1);
|
||||
} else {
|
||||
resolve_ref_unsafe(head_ref->buf, 0, worktree->head_sha1, NULL);
|
||||
worktree->head_ref = strbuf_detach(head_ref, NULL);
|
||||
}
|
||||
}
|
||||
int flags;
|
||||
const char *target;
|
||||
|
||||
target = refs_resolve_ref_unsafe(get_worktree_ref_store(wt),
|
||||
"HEAD",
|
||||
RESOLVE_REF_READING,
|
||||
wt->head_sha1, &flags);
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
if (flags & REF_ISSYMREF)
|
||||
wt->head_ref = xstrdup(target);
|
||||
else
|
||||
wt->is_detached = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -77,9 +48,7 @@ static struct worktree *get_main_worktree(void)
|
|||
struct worktree *worktree = NULL;
|
||||
struct strbuf path = STRBUF_INIT;
|
||||
struct strbuf worktree_path = STRBUF_INIT;
|
||||
struct strbuf head_ref = STRBUF_INIT;
|
||||
int is_bare = 0;
|
||||
int is_detached = 0;
|
||||
|
||||
strbuf_add_absolute_path(&worktree_path, get_git_common_dir());
|
||||
is_bare = !strbuf_strip_suffix(&worktree_path, "/.git");
|
||||
|
@ -91,13 +60,10 @@ static struct worktree *get_main_worktree(void)
|
|||
worktree = xcalloc(1, sizeof(*worktree));
|
||||
worktree->path = strbuf_detach(&worktree_path, NULL);
|
||||
worktree->is_bare = is_bare;
|
||||
worktree->is_detached = is_detached;
|
||||
if (!parse_ref(path.buf, &head_ref, &is_detached))
|
||||
add_head_info(&head_ref, worktree);
|
||||
add_head_info(worktree);
|
||||
|
||||
strbuf_release(&path);
|
||||
strbuf_release(&worktree_path);
|
||||
strbuf_release(&head_ref);
|
||||
return worktree;
|
||||
}
|
||||
|
||||
|
@ -106,8 +72,6 @@ static struct worktree *get_linked_worktree(const char *id)
|
|||
struct worktree *worktree = NULL;
|
||||
struct strbuf path = STRBUF_INIT;
|
||||
struct strbuf worktree_path = STRBUF_INIT;
|
||||
struct strbuf head_ref = STRBUF_INIT;
|
||||
int is_detached = 0;
|
||||
|
||||
if (!id)
|
||||
die("Missing linked worktree name");
|
||||
|
@ -127,19 +91,14 @@ static struct worktree *get_linked_worktree(const char *id)
|
|||
strbuf_reset(&path);
|
||||
strbuf_addf(&path, "%s/worktrees/%s/HEAD", get_git_common_dir(), id);
|
||||
|
||||
if (parse_ref(path.buf, &head_ref, &is_detached) < 0)
|
||||
goto done;
|
||||
|
||||
worktree = xcalloc(1, sizeof(*worktree));
|
||||
worktree->path = strbuf_detach(&worktree_path, NULL);
|
||||
worktree->id = xstrdup(id);
|
||||
worktree->is_detached = is_detached;
|
||||
add_head_info(&head_ref, worktree);
|
||||
add_head_info(worktree);
|
||||
|
||||
done:
|
||||
strbuf_release(&path);
|
||||
strbuf_release(&worktree_path);
|
||||
strbuf_release(&head_ref);
|
||||
return worktree;
|
||||
}
|
||||
|
||||
|
@ -337,8 +296,6 @@ const struct worktree *find_shared_symref(const char *symref,
|
|||
const char *target)
|
||||
{
|
||||
const struct worktree *existing = NULL;
|
||||
struct strbuf path = STRBUF_INIT;
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
static struct worktree **worktrees;
|
||||
int i = 0;
|
||||
|
||||
|
@ -348,6 +305,11 @@ const struct worktree *find_shared_symref(const char *symref,
|
|||
|
||||
for (i = 0; worktrees[i]; i++) {
|
||||
struct worktree *wt = worktrees[i];
|
||||
const char *symref_target;
|
||||
unsigned char sha1[20];
|
||||
struct ref_store *refs;
|
||||
int flags;
|
||||
|
||||
if (wt->is_bare)
|
||||
continue;
|
||||
|
||||
|
@ -362,25 +324,15 @@ const struct worktree *find_shared_symref(const char *symref,
|
|||
}
|
||||
}
|
||||
|
||||
strbuf_reset(&path);
|
||||
strbuf_reset(&sb);
|
||||
strbuf_addf(&path, "%s/%s",
|
||||
get_worktree_git_dir(wt),
|
||||
symref);
|
||||
|
||||
if (parse_ref(path.buf, &sb, NULL)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(sb.buf, target)) {
|
||||
refs = get_worktree_ref_store(wt);
|
||||
symref_target = refs_resolve_ref_unsafe(refs, symref, 0,
|
||||
sha1, &flags);
|
||||
if ((flags & REF_ISSYMREF) && !strcmp(symref_target, target)) {
|
||||
existing = wt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
strbuf_release(&path);
|
||||
strbuf_release(&sb);
|
||||
|
||||
return existing;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
struct worktree {
|
||||
char *path;
|
||||
char *id;
|
||||
char *head_ref;
|
||||
char *head_ref; /* NULL if HEAD is broken or detached */
|
||||
char *lock_reason; /* internal use */
|
||||
unsigned char head_sha1[20];
|
||||
int is_detached;
|
||||
|
|
Загрузка…
Ссылка в новой задаче