Skip to content

Commit aec7fa5

Browse files
calebdwttaylorr
authored andcommitted
worktree: add write_worktree_linking_files function
A new helper function, `write_worktree_linking_files()`, centralizes the logic for computing and writing either relative or absolute paths, based on the provided configuration. This function accepts `strbuf` pointers to both the worktree’s `.git` link and the repository’s `gitdir`, and then writes the appropriate path to each. This also teachs `git worktree repair` to fix the linking files if there is an absolute/relative paths but the links are correct. E.g., `git worktree repair` can be used to convert a valid worktree between absolute and relative paths. Signed-off-by: Caleb White <[email protected]> Signed-off-by: Taylor Blau <[email protected]>
1 parent b073f8e commit aec7fa5

File tree

3 files changed

+74
-67
lines changed

3 files changed

+74
-67
lines changed

builtin/worktree.c

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -417,8 +417,7 @@ static int add_worktree(const char *path, const char *refname,
417417
const struct add_opts *opts)
418418
{
419419
struct strbuf sb_git = STRBUF_INIT, sb_repo = STRBUF_INIT;
420-
struct strbuf sb = STRBUF_INIT, sb_tmp = STRBUF_INIT;
421-
struct strbuf sb_path_realpath = STRBUF_INIT, sb_repo_realpath = STRBUF_INIT;
420+
struct strbuf sb = STRBUF_INIT;
422421
const char *name;
423422
struct strvec child_env = STRVEC_INIT;
424423
unsigned int counter = 0;
@@ -494,10 +493,7 @@ static int add_worktree(const char *path, const char *refname,
494493

495494
strbuf_reset(&sb);
496495
strbuf_addf(&sb, "%s/gitdir", sb_repo.buf);
497-
strbuf_realpath(&sb_path_realpath, path, 1);
498-
strbuf_realpath(&sb_repo_realpath, sb_repo.buf, 1);
499-
write_file(sb.buf, "%s/.git", relative_path(sb_path_realpath.buf, sb_repo_realpath.buf, &sb_tmp));
500-
write_file(sb_git.buf, "gitdir: %s", relative_path(sb_repo_realpath.buf, sb_path_realpath.buf, &sb_tmp));
496+
write_worktree_linking_files(sb_git, sb);
501497
strbuf_reset(&sb);
502498
strbuf_addf(&sb, "%s/commondir", sb_repo.buf);
503499
write_file(sb.buf, "../..");
@@ -581,12 +577,9 @@ static int add_worktree(const char *path, const char *refname,
581577

582578
strvec_clear(&child_env);
583579
strbuf_release(&sb);
584-
strbuf_release(&sb_tmp);
585580
strbuf_release(&symref);
586581
strbuf_release(&sb_repo);
587-
strbuf_release(&sb_repo_realpath);
588582
strbuf_release(&sb_git);
589-
strbuf_release(&sb_path_realpath);
590583
strbuf_release(&sb_name);
591584
free_worktree(wt);
592585
return ret;

worktree.c

Lines changed: 60 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -381,29 +381,24 @@ int validate_worktree(const struct worktree *wt, struct strbuf *errmsg,
381381
void update_worktree_location(struct worktree *wt, const char *path_)
382382
{
383383
struct strbuf path = STRBUF_INIT;
384-
struct strbuf repo = STRBUF_INIT;
385-
struct strbuf file = STRBUF_INIT;
386-
struct strbuf tmp = STRBUF_INIT;
384+
struct strbuf dotgit = STRBUF_INIT;
385+
struct strbuf gitdir = STRBUF_INIT;
387386

388387
if (is_main_worktree(wt))
389388
BUG("can't relocate main worktree");
390389

391-
strbuf_realpath(&repo, git_common_path("worktrees/%s", wt->id), 1);
390+
strbuf_realpath(&gitdir, git_common_path("worktrees/%s/gitdir", wt->id), 1);
392391
strbuf_realpath(&path, path_, 1);
392+
strbuf_addf(&dotgit, "%s/.git", path.buf);
393393
if (fspathcmp(wt->path, path.buf)) {
394-
strbuf_addf(&file, "%s/gitdir", repo.buf);
395-
write_file(file.buf, "%s/.git", relative_path(path.buf, repo.buf, &tmp));
396-
strbuf_reset(&file);
397-
strbuf_addf(&file, "%s/.git", path.buf);
398-
write_file(file.buf, "gitdir: %s", relative_path(repo.buf, path.buf, &tmp));
394+
write_worktree_linking_files(dotgit, gitdir);
399395

400396
free(wt->path);
401397
wt->path = strbuf_detach(&path, NULL);
402398
}
403399
strbuf_release(&path);
404-
strbuf_release(&repo);
405-
strbuf_release(&file);
406-
strbuf_release(&tmp);
400+
strbuf_release(&dotgit);
401+
strbuf_release(&gitdir);
407402
}
408403

409404
int is_worktree_being_rebased(const struct worktree *wt,
@@ -582,9 +577,9 @@ static void repair_gitfile(struct worktree *wt,
582577
worktree_repair_fn fn, void *cb_data)
583578
{
584579
struct strbuf dotgit = STRBUF_INIT;
580+
struct strbuf gitdir = STRBUF_INIT;
585581
struct strbuf repo = STRBUF_INIT;
586582
struct strbuf backlink = STRBUF_INIT;
587-
struct strbuf tmp = STRBUF_INIT;
588583
char *dotgit_contents = NULL;
589584
const char *repair = NULL;
590585
int err;
@@ -600,6 +595,7 @@ static void repair_gitfile(struct worktree *wt,
600595

601596
strbuf_realpath(&repo, git_common_path("worktrees/%s", wt->id), 1);
602597
strbuf_addf(&dotgit, "%s/.git", wt->path);
598+
strbuf_addf(&gitdir, "%s/gitdir", repo.buf);
603599
dotgit_contents = xstrdup_or_null(read_gitfile_gently(dotgit.buf, &err));
604600

605601
if (dotgit_contents) {
@@ -617,18 +613,20 @@ static void repair_gitfile(struct worktree *wt,
617613
repair = _(".git file broken");
618614
else if (fspathcmp(backlink.buf, repo.buf))
619615
repair = _(".git file incorrect");
616+
else if (use_relative_paths == is_absolute_path(dotgit_contents))
617+
repair = _(".git file absolute/relative path mismatch");
620618

621619
if (repair) {
622620
fn(0, wt->path, repair, cb_data);
623-
write_file(dotgit.buf, "gitdir: %s", relative_path(repo.buf, wt->path, &tmp));
621+
write_worktree_linking_files(dotgit, gitdir);
624622
}
625623

626624
done:
627625
free(dotgit_contents);
628626
strbuf_release(&repo);
629627
strbuf_release(&dotgit);
628+
strbuf_release(&gitdir);
630629
strbuf_release(&backlink);
631-
strbuf_release(&tmp);
632630
}
633631

634632
static void repair_noop(int iserr UNUSED,
@@ -653,45 +651,30 @@ void repair_worktrees(worktree_repair_fn fn, void *cb_data)
653651

654652
void repair_worktree_after_gitdir_move(struct worktree *wt, const char *old_path)
655653
{
656-
struct strbuf path = STRBUF_INIT;
657-
struct strbuf repo = STRBUF_INIT;
658654
struct strbuf gitdir = STRBUF_INIT;
659655
struct strbuf dotgit = STRBUF_INIT;
660-
struct strbuf olddotgit = STRBUF_INIT;
661-
struct strbuf tmp = STRBUF_INIT;
662656

663657
if (is_main_worktree(wt))
664658
goto done;
665659

666-
strbuf_realpath(&repo, git_common_path("worktrees/%s", wt->id), 1);
667-
strbuf_addf(&gitdir, "%s/gitdir", repo.buf);
660+
strbuf_realpath(&gitdir, git_common_path("worktrees/%s/gitdir", wt->id), 1);
668661

669-
if (strbuf_read_file(&olddotgit, gitdir.buf, 0) < 0)
662+
if (strbuf_read_file(&dotgit, gitdir.buf, 0) < 0)
670663
goto done;
671664

672-
strbuf_rtrim(&olddotgit);
673-
if (is_absolute_path(olddotgit.buf)) {
674-
strbuf_addbuf(&dotgit, &olddotgit);
675-
} else {
676-
strbuf_addf(&dotgit, "%s/worktrees/%s/%s", old_path, wt->id, olddotgit.buf);
665+
strbuf_rtrim(&dotgit);
666+
if (!is_absolute_path(dotgit.buf)) {
667+
strbuf_insertf(&dotgit, 0, "%s/worktrees/%s/", old_path, wt->id);
677668
strbuf_realpath_forgiving(&dotgit, dotgit.buf, 0);
678669
}
679670

680671
if (!file_exists(dotgit.buf))
681672
goto done;
682673

683-
strbuf_addbuf(&path, &dotgit);
684-
strbuf_strip_suffix(&path, "/.git");
685-
686-
write_file(dotgit.buf, "gitdir: %s", relative_path(repo.buf, path.buf, &tmp));
687-
write_file(gitdir.buf, "%s", relative_path(dotgit.buf, repo.buf, &tmp));
674+
write_worktree_linking_files(dotgit, gitdir);
688675
done:
689-
strbuf_release(&path);
690-
strbuf_release(&repo);
691676
strbuf_release(&gitdir);
692677
strbuf_release(&dotgit);
693-
strbuf_release(&olddotgit);
694-
strbuf_release(&tmp);
695678
}
696679

697680
void repair_worktrees_after_gitdir_move(const char *old_path)
@@ -766,13 +749,10 @@ void repair_worktree_at_path(const char *path,
766749
worktree_repair_fn fn, void *cb_data)
767750
{
768751
struct strbuf dotgit = STRBUF_INIT;
769-
struct strbuf realdotgit = STRBUF_INIT;
770752
struct strbuf backlink = STRBUF_INIT;
771753
struct strbuf inferred_backlink = STRBUF_INIT;
772754
struct strbuf gitdir = STRBUF_INIT;
773755
struct strbuf olddotgit = STRBUF_INIT;
774-
struct strbuf realolddotgit = STRBUF_INIT;
775-
struct strbuf tmp = STRBUF_INIT;
776756
char *dotgit_contents = NULL;
777757
const char *repair = NULL;
778758
int err;
@@ -784,25 +764,25 @@ void repair_worktree_at_path(const char *path,
784764
goto done;
785765

786766
strbuf_addf(&dotgit, "%s/.git", path);
787-
if (!strbuf_realpath(&realdotgit, dotgit.buf, 0)) {
767+
if (!strbuf_realpath(&dotgit, dotgit.buf, 0)) {
788768
fn(1, path, _("not a valid path"), cb_data);
789769
goto done;
790770
}
791771

792-
infer_backlink(realdotgit.buf, &inferred_backlink);
772+
infer_backlink(dotgit.buf, &inferred_backlink);
793773
strbuf_realpath_forgiving(&inferred_backlink, inferred_backlink.buf, 0);
794-
dotgit_contents = xstrdup_or_null(read_gitfile_gently(realdotgit.buf, &err));
774+
dotgit_contents = xstrdup_or_null(read_gitfile_gently(dotgit.buf, &err));
795775
if (dotgit_contents) {
796776
if (is_absolute_path(dotgit_contents)) {
797777
strbuf_addstr(&backlink, dotgit_contents);
798778
} else {
799-
strbuf_addbuf(&backlink, &realdotgit);
779+
strbuf_addbuf(&backlink, &dotgit);
800780
strbuf_strip_suffix(&backlink, ".git");
801781
strbuf_addstr(&backlink, dotgit_contents);
802782
strbuf_realpath_forgiving(&backlink, backlink.buf, 0);
803783
}
804784
} else if (err == READ_GITFILE_ERR_NOT_A_FILE) {
805-
fn(1, realdotgit.buf, _("unable to locate repository; .git is not a file"), cb_data);
785+
fn(1, dotgit.buf, _("unable to locate repository; .git is not a file"), cb_data);
806786
goto done;
807787
} else if (err == READ_GITFILE_ERR_NOT_A_REPO) {
808788
if (inferred_backlink.len) {
@@ -815,11 +795,11 @@ void repair_worktree_at_path(const char *path,
815795
*/
816796
strbuf_swap(&backlink, &inferred_backlink);
817797
} else {
818-
fn(1, realdotgit.buf, _("unable to locate repository; .git file does not reference a repository"), cb_data);
798+
fn(1, dotgit.buf, _("unable to locate repository; .git file does not reference a repository"), cb_data);
819799
goto done;
820800
}
821801
} else {
822-
fn(1, realdotgit.buf, _("unable to locate repository; .git file broken"), cb_data);
802+
fn(1, dotgit.buf, _("unable to locate repository; .git file broken"), cb_data);
823803
goto done;
824804
}
825805

@@ -841,39 +821,35 @@ void repair_worktree_at_path(const char *path,
841821
* in the "copy" repository. In this case, point the "copy" worktree's
842822
* .git file at the "copy" repository.
843823
*/
844-
if (inferred_backlink.len && fspathcmp(backlink.buf, inferred_backlink.buf)) {
824+
if (inferred_backlink.len && fspathcmp(backlink.buf, inferred_backlink.buf))
845825
strbuf_swap(&backlink, &inferred_backlink);
846-
}
847826

848827
strbuf_addf(&gitdir, "%s/gitdir", backlink.buf);
849828
if (strbuf_read_file(&olddotgit, gitdir.buf, 0) < 0)
850829
repair = _("gitdir unreadable");
830+
else if (use_relative_paths == is_absolute_path(olddotgit.buf))
831+
repair = _("gitdir absolute/relative path mismatch");
851832
else {
852833
strbuf_rtrim(&olddotgit);
853-
if (is_absolute_path(olddotgit.buf)) {
854-
strbuf_addbuf(&realolddotgit, &olddotgit);
855-
} else {
856-
strbuf_addf(&realolddotgit, "%s/%s", backlink.buf, olddotgit.buf);
857-
strbuf_realpath_forgiving(&realolddotgit, realolddotgit.buf, 0);
834+
if (!is_absolute_path(olddotgit.buf)) {
835+
strbuf_insertf(&olddotgit, 0, "%s/", backlink.buf);
836+
strbuf_realpath_forgiving(&olddotgit, olddotgit.buf, 0);
858837
}
859-
if (fspathcmp(realolddotgit.buf, realdotgit.buf))
838+
if (fspathcmp(olddotgit.buf, dotgit.buf))
860839
repair = _("gitdir incorrect");
861840
}
862841

863842
if (repair) {
864843
fn(0, gitdir.buf, repair, cb_data);
865-
write_file(gitdir.buf, "%s", relative_path(realdotgit.buf, backlink.buf, &tmp));
844+
write_worktree_linking_files(dotgit, gitdir);
866845
}
867846
done:
868847
free(dotgit_contents);
869848
strbuf_release(&olddotgit);
870-
strbuf_release(&realolddotgit);
871849
strbuf_release(&backlink);
872850
strbuf_release(&inferred_backlink);
873851
strbuf_release(&gitdir);
874-
strbuf_release(&realdotgit);
875852
strbuf_release(&dotgit);
876-
strbuf_release(&tmp);
877853
}
878854

879855
int should_prune_worktree(const char *id, struct strbuf *reason, char **wtpath, timestamp_t expire)
@@ -1034,3 +1010,29 @@ int init_worktree_config(struct repository *r)
10341010
free(main_worktree_file);
10351011
return res;
10361012
}
1013+
1014+
void write_worktree_linking_files(struct strbuf dotgit, struct strbuf gitdir)
1015+
{
1016+
struct strbuf path = STRBUF_INIT;
1017+
struct strbuf repo = STRBUF_INIT;
1018+
struct strbuf tmp = STRBUF_INIT;
1019+
1020+
strbuf_addbuf(&path, &dotgit);
1021+
strbuf_strip_suffix(&path, "/.git");
1022+
strbuf_realpath(&path, path.buf, 1);
1023+
strbuf_addbuf(&repo, &gitdir);
1024+
strbuf_strip_suffix(&repo, "/gitdir");
1025+
strbuf_realpath(&repo, repo.buf, 1);
1026+
1027+
if (use_relative_paths) {
1028+
write_file(gitdir.buf, "%s/.git", relative_path(path.buf, repo.buf, &tmp));
1029+
write_file(dotgit.buf, "gitdir: %s", relative_path(repo.buf, path.buf, &tmp));
1030+
} else {
1031+
write_file(gitdir.buf, "%s/.git", path.buf);
1032+
write_file(dotgit.buf, "gitdir: %s", repo.buf);
1033+
}
1034+
1035+
strbuf_release(&path);
1036+
strbuf_release(&repo);
1037+
strbuf_release(&tmp);
1038+
}

worktree.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,4 +217,16 @@ void strbuf_worktree_ref(const struct worktree *wt,
217217
*/
218218
int init_worktree_config(struct repository *r);
219219

220+
/**
221+
* Write the .git file and gitdir file that links the worktree to the repository.
222+
*
223+
* The `dotgit` parameter is the path to the worktree's .git file, and `gitdir`
224+
* is the path to the repository's `gitdir` file.
225+
*
226+
* Example
227+
* dotgit: "/path/to/foo/.git"
228+
* gitdir: "/path/to/repo/worktrees/foo/gitdir"
229+
*/
230+
void write_worktree_linking_files(struct strbuf dotgit, struct strbuf gitdir);
231+
220232
#endif

0 commit comments

Comments
 (0)