git worktree with relative path?
Asked Answered
G

5

15

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?

Grundyism answered 15/3, 2021 at 9:33 Comment(10)
You'd definitely be safer using local mounts to make the absolute paths work. (That's not too hard to do; Docker uses reposiitioning mounts for instance.)Madonia
Definitely. Or using chroot / arch-chroot etc. But.. why not?Grundyism
It does seem like they should work. It would be nice if they did, and/or if (as in the feature request Chris Maes linked) you could convert to relative as needed, or fix up relative paths after moving things.Madonia
In the discussion linked by stackoverflow.com/users/2082964/chris-maes there's a link to a Go converter. It seems like a simple job to convert between relative and absolute. Git needs absolute paths only when adding/removing worktrees, from what I can tell.Grundyism
I don't see the Go converter link... it would be interesting to look at.Madonia
github.com/harobed/fix-git-worktree -- but it looks like it would be really simple to make a Bash script (I think his version handles some cases which no longer apply to today's git)Grundyism
I make a simple bash script for my personal use here: github.com/Kristian-Tan/git-worktree-relative I will post an answer explaining on how to use it laterHominoid
@Hominoid looks good, some notes: 1) please use expr instead of the strpos hack (some folks hate it, but it's POSIX & will never go away); 2) no need for substr, you're not using its "enhancements" 3) cat <<'EOF' for help messages instead of 1000 echo's 4) we need mkabs to revert mkrel before worktree remove, otherwise git refusesGrundyism
@Grundyism thanks to point it out, please see the updated repo. I'll add the reverting back relative to absolute laterHominoid
@Grundyism I have posted it as answer (also script to revert back relative to absolute)Hominoid
H
10

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

Hominoid answered 16/3, 2021 at 12:45 Comment(2)
Great, will be testing. One thing I find useful is to just set -x for verbose, just in case it never gets implemented -- it prints to stderr and is usually enough to help debugGrundyism
verbose output mode implementedHominoid
D
4

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...

Dolora answered 15/3, 2021 at 10:15 Comment(1)
But both break without relative paths, too, no? Only with relative paths can you move both the worktree and parent repo together...Rattish
I
1

"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
Intend answered 25/6, 2022 at 22:19 Comment(0)
G
0

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

Grundyism answered 23/3, 2021 at 11:20 Comment(0)
T
0

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.

Treasonable answered 30/11, 2022 at 19:1 Comment(1)
Script to produce the absolute gitdir given the relative: echo -n "gitdir: $(dirname $(pwd))" ; sed 's/^gitdir: .*\/.git/\/.git/' .gitTreasonable

© 2022 - 2024 — McMap. All rights reserved.