diff options
author | Junio C Hamano <gitster@pobox.com> | 2024-01-24 00:40:37 +0300 |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2024-01-24 00:40:37 +0300 |
commit | d14db9ca76189f2c5d6bf303c21874212aaf72d9 (patch) | |
tree | 006004c2d6a129a629c99fb215930972341c0257 | |
parent | 8b6b11d7eb90306db0e565a48aba14b5c086cf79 (diff) | |
parent | 8f4c00de954f809e83daf8b1425de82561f3721e (diff) |
Merge branch 'ps/worktree-refdb-initialization' into jch
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
-rw-r--r-- | builtin/worktree.c | 53 | ||||
-rw-r--r-- | refs.c | 6 | ||||
-rw-r--r-- | refs.h | 4 | ||||
-rw-r--r-- | refs/debug.c | 4 | ||||
-rw-r--r-- | refs/files-backend.c | 37 | ||||
-rw-r--r-- | refs/packed-backend.c | 1 | ||||
-rw-r--r-- | refs/refs-internal.h | 4 | ||||
-rw-r--r-- | setup.c | 17 | ||||
-rw-r--r-- | worktree.c | 27 | ||||
-rw-r--r-- | worktree.h | 12 |
10 files changed, 96 insertions, 69 deletions
diff --git a/builtin/worktree.c b/builtin/worktree.c index cac83a9419..6d7da11746 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -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,21 +495,33 @@ 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; } @@ -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, @@ -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 diff --git a/refs/debug.c b/refs/debug.c index b9775f2c37..634681ca44 100644 --- a/refs/debug.c +++ b/refs/debug.c @@ -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; } diff --git a/refs/files-backend.c b/refs/files-backend.c index b288fc97db..75dcc21ecb 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -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; diff --git a/refs/packed-backend.c b/refs/packed-backend.c index bf04b93381..a499a91c7e 100644 --- a/refs/packed-backend.c +++ b/refs/packed-backend.c @@ -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. */ diff --git a/refs/refs-internal.h b/refs/refs-internal.h index 8e9f04cc67..82219829b0 100644 --- a/refs/refs-internal.h +++ b/refs/refs-internal.h @@ -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, @@ -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); /* diff --git a/worktree.c b/worktree.c index dbc670d369..b02a05a74a 100644 --- a/worktree.c +++ b/worktree.c @@ -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; diff --git a/worktree.h b/worktree.h index ce45b66de9..f14784a2ff 100644 --- a/worktree.h +++ b/worktree.h @@ -58,6 +58,13 @@ struct worktree *find_worktree(struct worktree **list, 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. */ @@ -135,6 +142,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) */ void free_worktrees(struct worktree **); |