builtin/worktree: create refdb via ref backend

When creating a worktree we create the worktree's ref database manually by first writing a "HEAD" file so that the directory is recognized as a Git repository by other commands, and then running git-update-ref(1) or git-symbolic-ref(1) to write the actual value. But while this is fine for the files backend, this logic simply assumes too much about how the ref backend works and will leave behind an invalid ref database once any other ref backend lands. Refactor the code to instead use `refs_init_db()` to initialize the ref database so that git-worktree(1) itself does not need to know about how to initialize it. This will allow future ref backends to customize how the per-worktree ref database is set up. Furthermore, as we now already have a worktree ref store around, we can also avoid spawning external commands to write the HEAD reference and instead use the refs API to do so. Note that we do not have an equivalent to passing the `--quiet` flag to git-symbolic-ref(1) as we did before. This flag does not have an effect anyway though, as git-symbolic-ref(1) only honors it when reading a symref, but never when writing one. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed Jan 8, 2024 at 11:05 UTC 8f4c00de954f809e83daf8b1425de82561f3721e
1 file changed +22 -26
builtin/worktree.c
+22 -26
@@ -416,7 +416,6 @@ static int add_worktree(const char *path, const char *refname,
416 struct strbuf sb_git = STRBUF_INIT, sb_repo = STRBUF_INIT;
417 struct strbuf sb = STRBUF_INIT, realpath = STRBUF_INIT;
418 const char *name;
419 - struct child_process cp = CHILD_PROCESS_INIT;
419 struct strvec child_env = STRVEC_INIT;
420 unsigned int counter = 0;
421 int len, ret;
@@ -424,7 +423,8 @@ static int add_worktree(const char *path, const char *refname,
423 struct commit *commit = NULL;
424 int is_branch = 0;
425 struct strbuf sb_name = STRBUF_INIT;
427 - struct worktree **worktrees;
426 + struct worktree **worktrees, *wt = NULL;
427 + struct ref_store *wt_refs;
428
429 worktrees = get_worktrees();
430 check_candidate_path(path, opts->force, worktrees, "add");
@@ -500,15 +500,26 @@ static int add_worktree(const char *path, const char *refname,
500 write_file(sb.buf, "../..");
501
502 /*
503 - * This is to keep resolve_ref() happy. We need a valid HEAD
504 - * or is_git_directory() will reject the directory. Any value which
505 - * looks like an object ID will do since it will be immediately
506 - * replaced by the symbolic-ref or update-ref invocation in the new
507 - * worktree.
503 + * Set up the ref store of the worktree and create the HEAD reference.
504 */
509 - strbuf_reset(&sb);
510 - strbuf_addf(&sb, "%s/HEAD", sb_repo.buf);
511 - write_file(sb.buf, "%s", oid_to_hex(null_oid()));
505 + wt = get_linked_worktree(name, 1);
506 + if (!wt) {
507 + ret = error(_("could not find created worktree '%s'"), name);
508 + goto done;
509 + }
510 + wt_refs = get_worktree_ref_store(wt);
511 +
512 + ret = refs_init_db(wt_refs, REFS_INIT_DB_IS_WORKTREE, &sb);
513 + if (ret)
514 + goto done;
515 +
516 + if (!is_branch && commit)
517 + ret = refs_update_ref(wt_refs, NULL, "HEAD", &commit->object.oid,
518 + NULL, 0, UPDATE_REFS_MSG_ON_ERR);
519 + else
520 + ret = refs_create_symref(wt_refs, "HEAD", symref.buf, NULL);
521 + if (ret)
522 + goto done;
523
524 /*
525 * If the current worktree has sparse-checkout enabled, then copy
@@ -527,22 +538,6 @@ static int add_worktree(const char *path, const char *refname,
538
539 strvec_pushf(&child_env, "%s=%s", GIT_DIR_ENVIRONMENT, sb_git.buf);
540 strvec_pushf(&child_env, "%s=%s", GIT_WORK_TREE_ENVIRONMENT, path);
530 - cp.git_cmd = 1;
531 -
532 - if (!is_branch && commit) {
533 - strvec_pushl(&cp.args, "update-ref", "HEAD",
534 - oid_to_hex(&commit->object.oid), NULL);
535 - } else {
536 - strvec_pushl(&cp.args, "symbolic-ref", "HEAD",
537 - symref.buf, NULL);
538 - if (opts->quiet)
539 - strvec_push(&cp.args, "--quiet");
540 - }
541 -
542 - strvec_pushv(&cp.env, child_env.v);
543 - ret = run_command(&cp);
544 - if (ret)
545 - goto done;
541
542 if (opts->orphan &&
543 (ret = make_worktree_orphan(refname, opts, &child_env)))
@@ -588,6 +583,7 @@ done:
583 strbuf_release(&sb_git);
584 strbuf_release(&sb_name);
585 strbuf_release(&realpath);
586 + free_worktree(wt);
587 return ret;
588 }
589