mirror of
https://github.com/git/git.git
synced 2024-11-27 20:14:30 +08:00
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:
commit
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;
|
int i;
|
||||||
|
|
||||||
for (i = 0; worktrees[i]; i++) {
|
for (i = 0; worktrees[i]; i++) {
|
||||||
|
struct ref_store *refs;
|
||||||
|
|
||||||
if (worktrees[i]->is_detached)
|
if (worktrees[i]->is_detached)
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(oldref, worktrees[i]->head_ref))
|
if (worktrees[i]->head_ref &&
|
||||||
|
strcmp(oldref, worktrees[i]->head_ref))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (set_worktree_head_symref(get_worktree_git_dir(worktrees[i]),
|
refs = get_worktree_ref_store(worktrees[i]);
|
||||||
newref, logmsg)) {
|
if (refs_create_symref(refs, "HEAD", newref, logmsg))
|
||||||
ret = -1;
|
ret = error(_("HEAD of working tree %s is not updated"),
|
||||||
error(_("HEAD of working tree %s is not updated"),
|
worktrees[i]->path);
|
||||||
worktrees[i]->path);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
free_worktrees(worktrees);
|
free_worktrees(worktrees);
|
||||||
|
@ -217,6 +217,8 @@ const char *get_git_dir(void)
|
|||||||
|
|
||||||
const char *get_git_common_dir(void)
|
const char *get_git_common_dir(void)
|
||||||
{
|
{
|
||||||
|
if (!git_dir)
|
||||||
|
setup_git_env();
|
||||||
return git_common_dir;
|
return git_common_dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
106
refs.c
106
refs.c
@ -11,6 +11,7 @@
|
|||||||
#include "object.h"
|
#include "object.h"
|
||||||
#include "tag.h"
|
#include "tag.h"
|
||||||
#include "submodule.h"
|
#include "submodule.h"
|
||||||
|
#include "worktree.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* List of all available backends
|
* List of all available backends
|
||||||
@ -1477,32 +1478,32 @@ int resolve_gitlink_ref(const char *submodule, const char *refname,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct submodule_hash_entry
|
struct ref_store_hash_entry
|
||||||
{
|
{
|
||||||
struct hashmap_entry ent; /* must be the first member! */
|
struct hashmap_entry ent; /* must be the first member! */
|
||||||
|
|
||||||
struct ref_store *refs;
|
struct ref_store *refs;
|
||||||
|
|
||||||
/* NUL-terminated name of submodule: */
|
/* NUL-terminated identifier of the ref store: */
|
||||||
char submodule[FLEX_ARRAY];
|
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 void *keydata)
|
||||||
{
|
{
|
||||||
const struct submodule_hash_entry *e1 = entry, *e2 = entry_or_key;
|
const struct ref_store_hash_entry *e1 = entry, *e2 = entry_or_key;
|
||||||
const char *submodule = keydata ? keydata : e2->submodule;
|
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(
|
static struct ref_store_hash_entry *alloc_ref_store_hash_entry(
|
||||||
const char *submodule, struct ref_store *refs)
|
const char *name, struct ref_store *refs)
|
||||||
{
|
{
|
||||||
struct submodule_hash_entry *entry;
|
struct ref_store_hash_entry *entry;
|
||||||
|
|
||||||
FLEX_ALLOC_STR(entry, submodule, submodule);
|
FLEX_ALLOC_STR(entry, name, name);
|
||||||
hashmap_entry_init(entry, strhash(submodule));
|
hashmap_entry_init(entry, strhash(name));
|
||||||
entry->refs = refs;
|
entry->refs = refs;
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
@ -1513,20 +1514,23 @@ static struct ref_store *main_ref_store;
|
|||||||
/* A hashmap of ref_stores, stored by submodule name: */
|
/* A hashmap of ref_stores, stored by submodule name: */
|
||||||
static struct hashmap submodule_ref_stores;
|
static struct hashmap submodule_ref_stores;
|
||||||
|
|
||||||
/*
|
/* A hashmap of ref_stores, stored by worktree id: */
|
||||||
* Return the ref_store instance for the specified submodule. If that
|
static struct hashmap worktree_ref_stores;
|
||||||
* 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;
|
|
||||||
|
|
||||||
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(). */
|
/* It's initialized on demand in register_ref_store(). */
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
entry = hashmap_get_from_hash(&submodule_ref_stores,
|
entry = hashmap_get_from_hash(map, strhash(name), name);
|
||||||
strhash(submodule), submodule);
|
|
||||||
return entry ? entry->refs : NULL;
|
return entry ? entry->refs : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1553,29 +1557,24 @@ struct ref_store *get_main_ref_store(void)
|
|||||||
if (main_ref_store)
|
if (main_ref_store)
|
||||||
return main_ref_store;
|
return main_ref_store;
|
||||||
|
|
||||||
main_ref_store = ref_store_init(get_git_dir(),
|
main_ref_store = ref_store_init(get_git_dir(), REF_STORE_ALL_CAPS);
|
||||||
(REF_STORE_READ |
|
|
||||||
REF_STORE_WRITE |
|
|
||||||
REF_STORE_ODB |
|
|
||||||
REF_STORE_MAIN));
|
|
||||||
return main_ref_store;
|
return main_ref_store;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Register the specified ref_store to be the one that should be used
|
* Associate a ref store with a name. It is a fatal error to call this
|
||||||
* for submodule. It is a fatal error to call this function twice for
|
* function twice for the same name.
|
||||||
* the same submodule.
|
|
||||||
*/
|
*/
|
||||||
static void register_submodule_ref_store(struct ref_store *refs,
|
static void register_ref_store_map(struct hashmap *map,
|
||||||
const char *submodule)
|
const char *type,
|
||||||
|
struct ref_store *refs,
|
||||||
|
const char *name)
|
||||||
{
|
{
|
||||||
if (!submodule_ref_stores.tablesize)
|
if (!map->tablesize)
|
||||||
hashmap_init(&submodule_ref_stores, submodule_hash_cmp, 0);
|
hashmap_init(map, ref_store_hash_cmp, 0);
|
||||||
|
|
||||||
if (hashmap_put(&submodule_ref_stores,
|
if (hashmap_put(map, alloc_ref_store_hash_entry(name, refs)))
|
||||||
alloc_submodule_hash_entry(submodule, refs)))
|
die("BUG: %s ref_store '%s' initialized twice", type, name);
|
||||||
die("BUG: ref_store for submodule '%s' initialized twice",
|
|
||||||
submodule);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ref_store *get_submodule_ref_store(const char *submodule)
|
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();
|
return get_main_ref_store();
|
||||||
}
|
}
|
||||||
|
|
||||||
refs = lookup_submodule_ref_store(submodule);
|
refs = lookup_ref_store_map(&submodule_ref_stores, submodule);
|
||||||
if (refs)
|
if (refs)
|
||||||
return 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 */
|
/* assume that add_submodule_odb() has been called */
|
||||||
refs = ref_store_init(submodule_sb.buf,
|
refs = ref_store_init(submodule_sb.buf,
|
||||||
REF_STORE_READ | REF_STORE_ODB);
|
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);
|
strbuf_release(&submodule_sb);
|
||||||
return refs;
|
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,
|
void base_ref_store_init(struct ref_store *refs,
|
||||||
const struct ref_storage_be *be)
|
const struct ref_storage_be *be)
|
||||||
{
|
{
|
||||||
|
12
refs.h
12
refs.h
@ -5,6 +5,7 @@ struct object_id;
|
|||||||
struct ref_store;
|
struct ref_store;
|
||||||
struct strbuf;
|
struct strbuf;
|
||||||
struct string_list;
|
struct string_list;
|
||||||
|
struct worktree;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Resolve a reference, recursively following symbolic refererences.
|
* 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);
|
const char *target, const char *logmsg);
|
||||||
int create_symref(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 {
|
enum action_on_err {
|
||||||
UPDATE_REFS_MSG_ON_ERR,
|
UPDATE_REFS_MSG_ON_ERR,
|
||||||
UPDATE_REFS_DIE_ON_ERR,
|
UPDATE_REFS_DIE_ON_ERR,
|
||||||
@ -655,5 +646,6 @@ struct ref_store *get_main_ref_store(void);
|
|||||||
* submodule==NULL.
|
* submodule==NULL.
|
||||||
*/
|
*/
|
||||||
struct ref_store *get_submodule_ref_store(const char *submodule);
|
struct ref_store *get_submodule_ref_store(const char *submodule);
|
||||||
|
struct ref_store *get_worktree_ref_store(const struct worktree *wt);
|
||||||
|
|
||||||
#endif /* REFS_H */
|
#endif /* REFS_H */
|
||||||
|
@ -2240,50 +2240,6 @@ static int files_create_symref(struct ref_store *ref_store,
|
|||||||
return ret;
|
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,
|
static int files_reflog_exists(struct ref_store *ref_store,
|
||||||
const char *refname)
|
const char *refname)
|
||||||
{
|
{
|
||||||
|
@ -482,6 +482,10 @@ struct ref_store;
|
|||||||
#define REF_STORE_WRITE (1 << 1) /* can perform update operations */
|
#define REF_STORE_WRITE (1 << 1) /* can perform update operations */
|
||||||
#define REF_STORE_ODB (1 << 2) /* has access to object database */
|
#define REF_STORE_ODB (1 << 2) /* has access to object database */
|
||||||
#define REF_STORE_MAIN (1 << 3)
|
#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
|
* Initialize the ref_store for the specified gitdir. These functions
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include "cache.h"
|
#include "cache.h"
|
||||||
#include "refs.h"
|
#include "refs.h"
|
||||||
|
#include "worktree.h"
|
||||||
|
|
||||||
static const char *notnull(const char *arg, const char *name)
|
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);
|
strbuf_release(&sb);
|
||||||
|
|
||||||
*refs = get_submodule_ref_store(gitdir);
|
*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
|
} else
|
||||||
die("unknown backend %s", argv[0]);
|
die("unknown backend %s", argv[0]);
|
||||||
|
|
||||||
|
52
t/t1407-worktree-ref-store.sh
Executable file
52
t/t1407-worktree-ref-store.sh
Executable file
@ -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);
|
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) {
|
int flags;
|
||||||
if (worktree->is_detached) {
|
const char *target;
|
||||||
get_sha1_hex(head_ref->buf, worktree->head_sha1);
|
|
||||||
} else {
|
target = refs_resolve_ref_unsafe(get_worktree_ref_store(wt),
|
||||||
resolve_ref_unsafe(head_ref->buf, 0, worktree->head_sha1, NULL);
|
"HEAD",
|
||||||
worktree->head_ref = strbuf_detach(head_ref, NULL);
|
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 worktree *worktree = NULL;
|
||||||
struct strbuf path = STRBUF_INIT;
|
struct strbuf path = STRBUF_INIT;
|
||||||
struct strbuf worktree_path = STRBUF_INIT;
|
struct strbuf worktree_path = STRBUF_INIT;
|
||||||
struct strbuf head_ref = STRBUF_INIT;
|
|
||||||
int is_bare = 0;
|
int is_bare = 0;
|
||||||
int is_detached = 0;
|
|
||||||
|
|
||||||
strbuf_add_absolute_path(&worktree_path, get_git_common_dir());
|
strbuf_add_absolute_path(&worktree_path, get_git_common_dir());
|
||||||
is_bare = !strbuf_strip_suffix(&worktree_path, "/.git");
|
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 = xcalloc(1, sizeof(*worktree));
|
||||||
worktree->path = strbuf_detach(&worktree_path, NULL);
|
worktree->path = strbuf_detach(&worktree_path, NULL);
|
||||||
worktree->is_bare = is_bare;
|
worktree->is_bare = is_bare;
|
||||||
worktree->is_detached = is_detached;
|
add_head_info(worktree);
|
||||||
if (!parse_ref(path.buf, &head_ref, &is_detached))
|
|
||||||
add_head_info(&head_ref, worktree);
|
|
||||||
|
|
||||||
strbuf_release(&path);
|
strbuf_release(&path);
|
||||||
strbuf_release(&worktree_path);
|
strbuf_release(&worktree_path);
|
||||||
strbuf_release(&head_ref);
|
|
||||||
return worktree;
|
return worktree;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,8 +72,6 @@ static struct worktree *get_linked_worktree(const char *id)
|
|||||||
struct worktree *worktree = NULL;
|
struct worktree *worktree = NULL;
|
||||||
struct strbuf path = STRBUF_INIT;
|
struct strbuf path = STRBUF_INIT;
|
||||||
struct strbuf worktree_path = STRBUF_INIT;
|
struct strbuf worktree_path = STRBUF_INIT;
|
||||||
struct strbuf head_ref = STRBUF_INIT;
|
|
||||||
int is_detached = 0;
|
|
||||||
|
|
||||||
if (!id)
|
if (!id)
|
||||||
die("Missing linked worktree name");
|
die("Missing linked worktree name");
|
||||||
@ -127,19 +91,14 @@ static struct worktree *get_linked_worktree(const char *id)
|
|||||||
strbuf_reset(&path);
|
strbuf_reset(&path);
|
||||||
strbuf_addf(&path, "%s/worktrees/%s/HEAD", get_git_common_dir(), id);
|
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 = xcalloc(1, sizeof(*worktree));
|
||||||
worktree->path = strbuf_detach(&worktree_path, NULL);
|
worktree->path = strbuf_detach(&worktree_path, NULL);
|
||||||
worktree->id = xstrdup(id);
|
worktree->id = xstrdup(id);
|
||||||
worktree->is_detached = is_detached;
|
add_head_info(worktree);
|
||||||
add_head_info(&head_ref, worktree);
|
|
||||||
|
|
||||||
done:
|
done:
|
||||||
strbuf_release(&path);
|
strbuf_release(&path);
|
||||||
strbuf_release(&worktree_path);
|
strbuf_release(&worktree_path);
|
||||||
strbuf_release(&head_ref);
|
|
||||||
return worktree;
|
return worktree;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -337,8 +296,6 @@ const struct worktree *find_shared_symref(const char *symref,
|
|||||||
const char *target)
|
const char *target)
|
||||||
{
|
{
|
||||||
const struct worktree *existing = NULL;
|
const struct worktree *existing = NULL;
|
||||||
struct strbuf path = STRBUF_INIT;
|
|
||||||
struct strbuf sb = STRBUF_INIT;
|
|
||||||
static struct worktree **worktrees;
|
static struct worktree **worktrees;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
@ -348,6 +305,11 @@ const struct worktree *find_shared_symref(const char *symref,
|
|||||||
|
|
||||||
for (i = 0; worktrees[i]; i++) {
|
for (i = 0; worktrees[i]; i++) {
|
||||||
struct worktree *wt = worktrees[i];
|
struct worktree *wt = worktrees[i];
|
||||||
|
const char *symref_target;
|
||||||
|
unsigned char sha1[20];
|
||||||
|
struct ref_store *refs;
|
||||||
|
int flags;
|
||||||
|
|
||||||
if (wt->is_bare)
|
if (wt->is_bare)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -362,25 +324,15 @@ const struct worktree *find_shared_symref(const char *symref,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
strbuf_reset(&path);
|
refs = get_worktree_ref_store(wt);
|
||||||
strbuf_reset(&sb);
|
symref_target = refs_resolve_ref_unsafe(refs, symref, 0,
|
||||||
strbuf_addf(&path, "%s/%s",
|
sha1, &flags);
|
||||||
get_worktree_git_dir(wt),
|
if ((flags & REF_ISSYMREF) && !strcmp(symref_target, target)) {
|
||||||
symref);
|
|
||||||
|
|
||||||
if (parse_ref(path.buf, &sb, NULL)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!strcmp(sb.buf, target)) {
|
|
||||||
existing = wt;
|
existing = wt;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
strbuf_release(&path);
|
|
||||||
strbuf_release(&sb);
|
|
||||||
|
|
||||||
return existing;
|
return existing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
struct worktree {
|
struct worktree {
|
||||||
char *path;
|
char *path;
|
||||||
char *id;
|
char *id;
|
||||||
char *head_ref;
|
char *head_ref; /* NULL if HEAD is broken or detached */
|
||||||
char *lock_reason; /* internal use */
|
char *lock_reason; /* internal use */
|
||||||
unsigned char head_sha1[20];
|
unsigned char head_sha1[20];
|
||||||
int is_detached;
|
int is_detached;
|
||||||
|
Loading…
Reference in New Issue
Block a user