usretc
usretc

Reputation: 834

git worktree with relative path?

I have a master and a setup branch in my repo. I'm keeping the setup branch checked out as a worktree inside the main repo folder via

git worktree add ./local/setup
echo '/local' > .gitignore

So the main repo folder is on master, and the local/setup folder is on setup. Everything is fine and dandy, I can work on my setup files without having to switch branches, I can commit from within local/setup etc.

But if I try to move the entire repo, or access it from a different Linux boot (/home/myrepo becomes /mnt/ubu/home/myrepo), things break. The problem seems to be that git's worktree functionality records absolute paths, in

myrepo/.git/worktrees/setup/gitdir
myrepo/local/setup/.git

Can I convert these to relative paths to make the repo + embedded worktree relocatable? I'm not sure what the paths in those files should be relative to, but I can experiment. Is this setup dangerous?

Upvotes: 18

Views: 3808

Answers (6)

VonC
VonC

Reputation: 1324218

Can I convert these to relative paths to make the repo + embedded worktree relocatable?

Yes, with git worktree repair and a recent enough Git.

Git 2.48 (Q1 2025), batch 16, introduces a new repository extension to prevent older Git versions from mis-interpreting worktrees created with relative paths.

See commit 2037ca8, commit e6df1ee, commit 298d291, commit b701634, commit 4dac9e3, commit 5976310, commit 1860ba1, commit d897f2c (29 Nov 2024) by Caleb White (calebdw).
(Merged by Junio C Hamano -- gitster -- in commit 3b11c91, 13 Dec 2024)

worktree: add relative cli/config options to repair command

Signed-off-by: Caleb White

This teaches the worktree repair command to respect the --[no-]relative-paths CLI option and worktree.useRelativePaths config setting.
If an existing worktree with an absolute path is repaired with --relative-paths, the links will be replaced with relative paths, even if the original path was correct.
This allows a user to covert existing worktrees between absolute/relative as desired.

To simplify things, both linking files are written when one of the files needs to be repaired.
In some cases, this fixes the other file before it is checked, in other cases this results in a correct file being written with the same contents.

git worktree now includes in its man page:

With repair, the linking files will be updated if there's an absolute/relative mismatch, even if the links are correct.

Upvotes: 1

Brent Bradburn
Brent Bradburn

Reputation: 54869

Relative paths can be specified in the worktree '.git' file by replacing the absolute path with a relative one.

Below is an example for the typical case where the gitdir is under ../.git (includes optional sed scripting):

$ sed 's/ \/.*\/.git/ ..\/.git/' .git
gitdir: ../.git/worktrees/mybranch

Use sed --in-place to modify the '.git' file in place or sed --in-place=.old to also save the original.


To use git worktree commands, you need to be concerned about the paths indicated by git worktree list. In some cases, these may need to be absolute paths. To update these paths, you can manually modify the associated 'gitdir' files (under '.git/worktree/mybranch').

Introduced in Git 2.29, there is also a handy option called git worktree repair (docs):

Repair working tree administrative files, if possible, if they have become corrupted or outdated due to external factors.

For instance, if the main working tree (or bare repository) is moved, linked working trees will be unable to locate it. Running repair in the main working tree will reestablish the connection from linked working trees back to the main working tree.

Upvotes: 0

Mateen Ulhaq
Mateen Ulhaq

Reputation: 27201

"Manual" process

Create a .bare repo:

mkdir repo
cd repo
git clone --bare [email protected]:user/repo.git .bare
echo "gitdir: ./.bare" > .git

Add some worktree branches:

$ git worktree add master
$ git worktree add branch_dir branch/subbranch

List the worktrees:

$ git worktree list
/home/username/repo/.bare       (bare)
/home/username/repo/master      e4e5d4d [master]
/home/username/repo/branch_dir  6ed5ed5 [branch/subbranch]

$ ls .bare/worktrees
branch_dir
master

Set the relative paths:

echo "gitdir: ../.bare/worktrees/master" > master/.git
echo "gitdir: ../.bare/worktrees/branch_dir" > branch_dir/.git

Optionally (error prone):

echo "../../../master/.git" > .bare/worktrees/master/gitdir
echo "../../../branch_dir/.git" > .bare/worktrees/branch_dir/gitdir

Upvotes: 1

Kristian
Kristian

Reputation: 2505

I make a simple bash script for my personal use here:

https://github.com/Kristian-Tan/git-worktree-relative

Note that this answer is just a copy-paste from my README.md

...

My solution

  • Bash script to replace the content of {worktree}/.git file and {repo}/.git/worktrees/{wtname}/gitdir
  • Why bash: almost everyone who use git will use it in some kind of bash-shell-like environment (ex: bash shell in linux, git bash in windows)
  • Requirements (should be available on every bash shell):
  • Another bash script to change it back to absolute path (since git worktree remove may refuse on relative path)

Usage

  • Execute the script in your worktree (or supply the worktree directory path in -w options)
  • It will read path to repository from {worktree}/.git file
  • Options:
    • -v = verbose (not implemented yet)
    • -w worktree_target = directory of worktree to be made relative (will default to current directory if not supplied)
    • -r repository_target = directory of repository (including worktree directory inside .git, will be read from {worktree_target}/.git file if not supplied)
    • -h = show help
  • This solution works for broken link (ex: worktree directory moved OR parent git directory moved): just supply the repository path in -r repositor_target flag
  • This solution works for worktree inside parent repository
  • example:
    • repository in /home/myuser/repo/myproject ; worktree in /home/myuser/www/myproject ; worktree is connected with repository (link is not broken)
      cd /home/myuser/www/myproject
      git-worktree-relative
      # OR
      git-worktree-relative -w /home/myuser/www/myproject
      
    • repository in /home/myuser/repo/myproject ; worktree in /home/myuser/www/myproject ; worktree is NOT connected with repository (link broken)
      cd /home/myuser/www/myproject
      git-worktree-relative -r /home/myuser/repo/myproject/.git/worktrees/myproject
      # OR
      git-worktree-relative -w /home/myuser/www/myproject -r /home/myuser/repo/myproject/.git/worktrees/myproject
      
    • to detect if link is broken, run command 'git status' in worktree directory
  • Reversing relative worktree back to absolute: just change git-worktree-relative command with git-worktree-absolute (same command line argument)
    • command git worktree remove requires the path to be absolute: you can use this reverse script to revert it back to absolute path before removing

Installation

Automatic Installation

  • copy paste below command into your terminal:
git clone https://github.com/Kristian-Tan/git-worktree-relative.git
cd git-worktree-relative
sudo bash install.sh
  • or this one-line: git clone https://github.com/Kristian-Tan/git-worktree-relative.git ; cd git-worktree-relative ; sudo bash install.sh
  • or another one-line: /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Kristian-Tan/git-worktree-relative/HEAD/get)"

Manual Installation

  • installation for all users:
    • copy git-worktree-relative.sh and git-worktree-absolute.sh to /usr/bin or /bin (you can also remove the extension)
    • give other user permission to execute it
    • example:
      cp git-worktree-relative.sh /usr/bin/git-worktree-relative
      cp git-worktree-absolute.sh /usr/bin/git-worktree-absolute
      chown root:root /usr/bin/git-worktree-relative
      chown root:root /usr/bin/git-worktree-absolute
      chmod 0755 /usr/bin/git-worktree-relative
      chmod 0755 /usr/bin/git-worktree-absolute
    
  • installation for one user:
    • copy it to any directory that is added to your PATH variable

Uninstallation

  • just remove copied files (or just use uninstall.sh script: git clone https://github.com/Kristian-Tan/git-worktree-relative.git ; sudo bash git-worktree-relative/uninstall.sh)
  • or another one-line: /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Kristian-Tan/git-worktree-relative/HEAD/remove)"

...

Credits

Upvotes: 10

usretc
usretc

Reputation: 834

I rolled out my own git-worktree-path. It's minimally hackish (no messing with internal git paths, almost everything goes through git rev-parse) and maximally DRY-ish

Upvotes: 0

Chris Maes
Chris Maes

Reputation: 37742

You are not the first to ask this question, see this feature-request from 2016

Relative paths seem to complicate things:

  • when moving a worktree (note that a worktree can be inside or outside the parent repository)
  • when moving the parent repository

So it seems this never got implemented...

Upvotes: 4

Related Questions