Skip to content

Enhance: [Worktrees] offer to repair linked worktrees after the main repository's folder moves #739

Closed

Description

@goneng

Split out of #675, whose slice 5 (#731) repairs a linked worktree whose own folder was moved. The opposite case, the main repository's folder moving, was left out.

What happens

Each linked worktree's .git file holds an absolute path to <main>/.git/worktrees/<id>. Once the main folder is moved or renamed, that path is stale:

  • From the moved main repository, git worktree list --porcelain still lists the linked worktree with its HEAD and branch, and not as prunable. So the WORKTREES group shows it as healthy.
  • Inside the linked worktree, every git command fails with fatal: not a git repository: <old main>/.git/worktrees/<id>.
  • git worktree repair with no path, run in the main worktree, rewrites the broken .git files. It prints repair: .git file broken: <path> for each one and exits 0.

Checked with git 2.50.1 (Apple Git-155).

Why an automatic repair and not a menu action

Git documents a single fix for this case, git worktree repair from the main worktree, and it only touches links that are already broken. Unlike #731, the user has nothing to choose. Tower documents repair only for a moved worktree folder, and Fork and GitKraken document none. So GitX should repair on its own, with the user approving the change first because it writes into folders outside this repository.

Proposal

  1. Detect it on refs reload by reading each linked worktree's .git file and comparing its gitdir: with this repository's worktrees/<id> folder. The porcelain alone cannot show it.

  2. Ask once with a sheet on the repository window, listing the worktrees whose link is broken:

    This repository was moved, so N of its worktrees can no longer find it:
      <path>  (branch)
      ...
    GitX can reconnect them with "git worktree repair".
                                           [Cancel]  [Repair]
    
  3. On Repair, run git worktree repair and reload the worktree list. If git reports an error, show it and treat the result the same as Cancel.

  4. On git before 2.29, which has no repair, the sheet only states the situation. It lists the same worktrees, names the git version found and the 2.29 needed, suggests upgrading git (or pointing GitX at a newer one in Preferences, where the git path is set), and has a single OK button. The rows are marked as below, and Repair Worktree Links is shown disabled with a tooltip naming both versions.

Git before 2.7 needs no handling: it has no worktree list --porcelain, so the WORKTREES group is empty and nothing is detected.

If the user cancels

The main repository works normally after the move; only the linked worktrees are broken, so Cancel must not block this window. GitX continues and marks the broken worktrees:

  • The rows are dimmed like a missing folder, with a tooltip saying the link to this repository is broken.
  • The row menu offers Repair Worktree Links, which runs the same repair.
  • Open Worktree on such a row shows git's error and offers the repair again.
  • Cancel (or OK on an older git) is not remembered: the sheet comes back each time the repository is opened while any link is still broken. This stays accurate when the user repairs from the command line instead.

Rejected alternatives: letting git fail with its raw not a git repository error, which names a path that no longer exists and does not say how to fix it; and closing the window or quitting, which refuses to show a repository that works over a problem in other folders.

Out of scope

  • Opening a broken linked worktree directly (File > Open on its folder). GitX then has no way to find the main repository, since the path it knows is stale. It could offer to locate the main repository and run the repair from there, but that is a separate flow.
  • Relative worktree links: worktrees created with worktree.useRelativePaths (git 2.48) should not break this way, since their links do not hold absolute paths. Not verified.

Activity

  1. self-assigned this
    on Sep 29, 2026
  2. changed the title [-]Enhance: [Worktrees] repair linked worktrees after the main repository's folder moves[/-] [+]Enhance: [Worktrees] offer to repair linked worktrees after the main repository's folder moves[/+] on Sep 29, 2026
  3. goneng commented on Oct 8, 2026

    @goneng
    CollaboratorAuthor

    Before building this, I checked the proposal against git's own repair code (repair_gitfile in worktree.c) and against a moved main repository with git 2.55. The approach holds, with three corrections and one addition.

    Detection

    No git command reports this: worktree list --porcelain shows the outside worktrees as healthy, and repair has no dry run. Proposal step 1 matches what repair_gitfile does, so it should mirror it exactly. For each linked worktree whose folder exists:

    1. Read <wt>/.git and resolve a relative gitdir: against the worktree folder.
    2. If that path is not a git directory, the link is broken (git: .git file broken).
    3. If it is one, but its real path differs from <commondir>/worktrees/<id>, the link points elsewhere (git: .git file incorrect).

    This is file reads only, so it is cheap enough for every refs reload.

    Correction 1: a worktree inside the main folder becomes prunable

    A worktree nested in the main folder moves with it, so its folder still exists, but both of its link files hold the old path. The porcelain then reports it as prunable gitdir file points to non-existent location, and worktree prune --dry-run lists it for removal.

    • A plain git worktree repair skips it, because git considers its folder missing.
    • git worktree repair <new path> fixes it. The new path is the old one with the old main folder replaced by the new one, and the old main folder can be read from the stale .git file.
    • GitX's Prune ([Worktree] Prune worktrees, and lock or unlock them (part 3) #723) would offer to remove this worktree's admin entry. That would leave its files on disk as a worktree git no longer knows about. Detection must cover this case, and Prune must leave it alone.

    Correction 2: relative links break too

    The "Out of scope" note expects links made with worktree.useRelativePaths (git 2.48) not to break. They do break when only the main folder moves, the same way absolute ones do. They survive only when the worktree moves with it, for example a nested worktree.

    Correction 3: repair can rewrite the style of healthy links

    Since git 2.48, repair also rewrites any link whose style does not match the requested one, healthy or not. The style always applies to every linked worktree: a path argument does not limit it, because repair goes through all of them afterwards.

    Command Result for every link
    repair follows worktree.useRelativePaths, absolute when unset
    repair --relative-paths relative
    repair --no-relative-paths absolute

    Addition: let the user choose the link style

    The sheet can offer:

    • Repair, keeping the current style
    • Repair as Relative Paths (--relative-paths)
    • Repair as Absolute Paths (--no-relative-paths)
    • Cancel

    The style is one per run, so "keeping the current style" can only be done exactly when all links share one style. GitX would read that from the .git files and pass the matching flag. When the links are mixed, git cannot keep each one's style. GitX should then not offer Repair, and leave the choice between the other two to the user. Writing the link files itself would duplicate git's logic, so I am not proposing it.

    With git 2.29 to 2.47 there are no flags and links come out absolute, so the sheet offers only Repair and Cancel. If a link is relative, the sheet also says it will be rewritten as absolute. This needs a second version gate at 2.48, next to PBGitWorktreeRepairVersion.

    An NSAlert with four buttons is crowded. An alternative is one Repair button with a style pop-up as the accessory view.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions