Merge branch 'ps/worktree-refdb-initialization'

Instead of manually creating refs/ hierarchy on disk upon a
creation of a secondary worktree, which is only usable via the
files backend, use the refs API to populate it.

* ps/worktree-refdb-initialization:
  builtin/worktree: create refdb via ref backend
  worktree: expose interface to look up worktree by name
  builtin/worktree: move setup of commondir file earlier
  refs/files: skip creation of "refs/{heads,tags}" for worktrees
  setup: move creation of "refs/" into the files backend
  refs: prepare `refs_init_db()` for initializing worktree refs
This commit is contained in:
Junio C Hamano
2024-01-26 08:54:46 -08:00
10 changed files with 96 additions and 69 deletions

View File

@ -416,7 +416,6 @@ static int add_worktree(const char *path, const char *refname,
struct strbuf sb_git = STRBUF_INIT, sb_repo = STRBUF_INIT;
struct strbuf sb = STRBUF_INIT, realpath = STRBUF_INIT;
const char *name;
struct child_process cp = CHILD_PROCESS_INIT;
struct strvec child_env = STRVEC_INIT;
unsigned int counter = 0;
int len, ret;
@ -424,7 +423,8 @@ static int add_worktree(const char *path, const char *refname,
struct commit *commit = NULL;
int is_branch = 0;
struct strbuf sb_name = STRBUF_INIT;
struct worktree **worktrees;
struct worktree **worktrees, *wt = NULL;
struct ref_store *wt_refs;
worktrees = get_worktrees();
check_candidate_path(path, opts->force, worktrees, "add");
@ -495,20 +495,32 @@ static int add_worktree(const char *path, const char *refname,
strbuf_realpath(&realpath, get_git_common_dir(), 1);
write_file(sb_git.buf, "gitdir: %s/worktrees/%s",
realpath.buf, name);
/*
* This is to keep resolve_ref() happy. We need a valid HEAD
* or is_git_directory() will reject the directory. Any value which
* looks like an object ID will do since it will be immediately
* replaced by the symbolic-ref or update-ref invocation in the new
* worktree.
*/
strbuf_reset(&sb);
strbuf_addf(&sb, "%s/HEAD", sb_repo.buf);
write_file(sb.buf, "%s", oid_to_hex(null_oid()));
strbuf_reset(&sb);
strbuf_addf(&sb, "%s/commondir", sb_repo.buf);
write_file(sb.buf, "../..");
/*
* Set up the ref store of the worktree and create the HEAD reference.
*/
wt = get_linked_worktree(name, 1);
if (!wt) {
ret = error(_("could not find created worktree '%s'"), name);
goto done;
}
wt_refs = get_worktree_ref_store(wt);
ret = refs_init_db(wt_refs, REFS_INIT_DB_IS_WORKTREE, &sb);
if (ret)
goto done;
if (!is_branch && commit)
ret = refs_update_ref(wt_refs, NULL, "HEAD", &commit->object.oid,
NULL, 0, UPDATE_REFS_MSG_ON_ERR);
else
ret = refs_create_symref(wt_refs, "HEAD", symref.buf, NULL);
if (ret)
goto done;
/*
* If the current worktree has sparse-checkout enabled, then copy
* the sparse-checkout patterns from the current worktree.
@ -526,22 +538,6 @@ static int add_worktree(const char *path, const char *refname,
strvec_pushf(&child_env, "%s=%s", GIT_DIR_ENVIRONMENT, sb_git.buf);
strvec_pushf(&child_env, "%s=%s", GIT_WORK_TREE_ENVIRONMENT, path);
cp.git_cmd = 1;
if (!is_branch && commit) {
strvec_pushl(&cp.args, "update-ref", "HEAD",
oid_to_hex(&commit->object.oid), NULL);
} else {
strvec_pushl(&cp.args, "symbolic-ref", "HEAD",
symref.buf, NULL);
if (opts->quiet)
strvec_push(&cp.args, "--quiet");
}
strvec_pushv(&cp.env, child_env.v);
ret = run_command(&cp);
if (ret)
goto done;
if (opts->orphan &&
(ret = make_worktree_orphan(refname, opts, &child_env)))
@ -587,6 +583,7 @@ done:
strbuf_release(&sb_git);
strbuf_release(&sb_name);
strbuf_release(&realpath);
free_worktree(wt);
return ret;
}

6
refs.c
View File

@ -1997,11 +1997,9 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs,
}
/* backend functions */
int refs_init_db(struct strbuf *err)
int refs_init_db(struct ref_store *refs, int flags, struct strbuf *err)
{
struct ref_store *refs = get_main_ref_store(the_repository);
return refs->be->init_db(refs, err);
return refs->be->init_db(refs, flags, err);
}
const char *resolve_ref_unsafe(const char *refname, int resolve_flags,

4
refs.h
View File

@ -126,7 +126,9 @@ int should_autocreate_reflog(const char *refname);
int is_branch(const char *refname);
int refs_init_db(struct strbuf *err);
#define REFS_INIT_DB_IS_WORKTREE (1 << 0)
int refs_init_db(struct ref_store *refs, int flags, struct strbuf *err);
/*
* Return the peeled value of the oid currently being iterated via

View File

@ -33,10 +33,10 @@ struct ref_store *maybe_debug_wrap_ref_store(const char *gitdir, struct ref_stor
return (struct ref_store *)res;
}
static int debug_init_db(struct ref_store *refs, struct strbuf *err)
static int debug_init_db(struct ref_store *refs, int flags, struct strbuf *err)
{
struct debug_ref_store *drefs = (struct debug_ref_store *)refs;
int res = drefs->refs->be->init_db(drefs->refs, err);
int res = drefs->refs->be->init_db(drefs->refs, flags, err);
trace_printf_key(&trace_refs, "init_db: %d\n", res);
return res;
}

View File

@ -3218,21 +3218,46 @@ static int files_reflog_expire(struct ref_store *ref_store,
return -1;
}
static int files_init_db(struct ref_store *ref_store, struct strbuf *err UNUSED)
static int files_init_db(struct ref_store *ref_store,
int flags,
struct strbuf *err UNUSED)
{
struct files_ref_store *refs =
files_downcast(ref_store, REF_STORE_WRITE, "init_db");
struct strbuf sb = STRBUF_INIT;
/*
* Create .git/refs/{heads,tags}
* We need to create a "refs" dir in any case so that older versions of
* Git can tell that this is a repository. This serves two main purposes:
*
* - Clients will know to stop walking the parent-directory chain when
* detecting the Git repository. Otherwise they may end up detecting
* a Git repository in a parent directory instead.
*
* - Instead of failing to detect a repository with unknown reference
* format altogether, old clients will print an error saying that
* they do not understand the reference format extension.
*/
files_ref_path(refs, &sb, "refs/heads");
strbuf_addf(&sb, "%s/refs", ref_store->gitdir);
safe_create_dir(sb.buf, 1);
adjust_shared_perm(sb.buf);
strbuf_reset(&sb);
files_ref_path(refs, &sb, "refs/tags");
safe_create_dir(sb.buf, 1);
/*
* There is no need to create directories for common refs when creating
* a worktree ref store.
*/
if (!(flags & REFS_INIT_DB_IS_WORKTREE)) {
/*
* Create .git/refs/{heads,tags}
*/
strbuf_reset(&sb);
files_ref_path(refs, &sb, "refs/heads");
safe_create_dir(sb.buf, 1);
strbuf_reset(&sb);
files_ref_path(refs, &sb, "refs/tags");
safe_create_dir(sb.buf, 1);
}
strbuf_release(&sb);
return 0;

View File

@ -1245,6 +1245,7 @@ static const char PACKED_REFS_HEADER[] =
"# pack-refs with: peeled fully-peeled sorted \n";
static int packed_init_db(struct ref_store *ref_store UNUSED,
int flags UNUSED,
struct strbuf *err UNUSED)
{
/* Nothing to do. */

View File

@ -529,7 +529,9 @@ typedef struct ref_store *ref_store_init_fn(struct repository *repo,
const char *gitdir,
unsigned int flags);
typedef int ref_init_db_fn(struct ref_store *refs, struct strbuf *err);
typedef int ref_init_db_fn(struct ref_store *refs,
int flags,
struct strbuf *err);
typedef int ref_transaction_prepare_fn(struct ref_store *refs,
struct ref_transaction *transaction,

17
setup.c
View File

@ -1926,23 +1926,8 @@ void create_reference_database(unsigned int ref_storage_format,
struct strbuf err = STRBUF_INIT;
int reinit = is_reinit();
/*
* We need to create a "refs" dir in any case so that older versions of
* Git can tell that this is a repository. This serves two main purposes:
*
* - Clients will know to stop walking the parent-directory chain when
* detecting the Git repository. Otherwise they may end up detecting
* a Git repository in a parent directory instead.
*
* - Instead of failing to detect a repository with unknown reference
* format altogether, old clients will print an error saying that
* they do not understand the reference format extension.
*/
safe_create_dir(git_path("refs"), 1);
adjust_shared_perm(git_path("refs"));
repo_set_ref_storage_format(the_repository, ref_storage_format);
if (refs_init_db(&err))
if (refs_init_db(get_main_ref_store(the_repository), 0, &err))
die("failed to set up refs db: %s", err.buf);
/*

View File

@ -12,18 +12,23 @@
#include "wt-status.h"
#include "config.h"
void free_worktree(struct worktree *worktree)
{
if (!worktree)
return;
free(worktree->path);
free(worktree->id);
free(worktree->head_ref);
free(worktree->lock_reason);
free(worktree->prune_reason);
free(worktree);
}
void free_worktrees(struct worktree **worktrees)
{
int i = 0;
for (i = 0; worktrees[i]; i++) {
free(worktrees[i]->path);
free(worktrees[i]->id);
free(worktrees[i]->head_ref);
free(worktrees[i]->lock_reason);
free(worktrees[i]->prune_reason);
free(worktrees[i]);
}
for (i = 0; worktrees[i]; i++)
free_worktree(worktrees[i]);
free (worktrees);
}
@ -75,8 +80,8 @@ static struct worktree *get_main_worktree(int skip_reading_head)
return worktree;
}
static struct worktree *get_linked_worktree(const char *id,
int skip_reading_head)
struct worktree *get_linked_worktree(const char *id,
int skip_reading_head)
{
struct worktree *worktree = NULL;
struct strbuf path = STRBUF_INIT;

View File

@ -57,6 +57,13 @@ struct worktree *find_worktree(struct worktree **list,
const char *prefix,
const char *arg);
/*
* Look up the worktree corresponding to `id`, or NULL of no such worktree
* exists.
*/
struct worktree *get_linked_worktree(const char *id,
int skip_reading_head);
/*
* Return the worktree corresponding to `path`, or NULL if no such worktree
* exists.
@ -134,6 +141,11 @@ void repair_worktrees(worktree_repair_fn, void *cb_data);
*/
void repair_worktree_at_path(const char *, worktree_repair_fn, void *cb_data);
/*
* Free up the memory for a worktree.
*/
void free_worktree(struct worktree *);
/*
* Free up the memory for worktree(s)
*/