{"thread":{"id":"62311","subject":"Re: `git worktree list` when bare repository is named `.git`","startedAt":"2024-10-11T03:52:06Z","lastAt":"2024-10-21T19:09:49Z","messageCount":7,"participants":["Caleb White","Rebecca Turner","Patrick Callahan"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"504775","messageId":"D4SO70M9Z1QI.1AC4QF9ZG8T4L@pm.me","threadId":"62311","inReplyTo":null,"subject":"Re: `git worktree list` when bare repository is named `.git`","fromName":"Caleb White","fromEmail":"cdwhite3@pm.me","sentAt":"2024-10-11T03:52:02Z","receivedAt":"2024-10-11T03:52:06Z","isPatch":false,"sender":{"key":"cdwhite3@pm.me","avatar":"https://avatars.githubusercontent.com/u/4176520?v=4"},"body":"> It seems that `.git` is always stripped from the `git worktree list` output (including in `--porcelain` mode). This becomes relevant with bare repositories. Here is a bare repository functioning as expected:\n> \n> $ mkdir bare1\n> $ cd bare1\n> $ git init --bare\n> Initialized empty Git repository in /private/tmp/bare1/\n> $ git worktree list\n> /private/tmp/bare1  (bare)\n> \n> But if we create a bare repository in a directory named `.git`, `git worktree list` displays the parent directory as the worktree, even though `git status` doesn't recognize it as a worktree:\n> \n> $ mkdir -p bare2/.git\n> $ cd bare2/.git\n> $ git init --bare\n> Initialized empty Git repository in /private/tmp/bare2/.git/\n> $ git worktree list\n> /private/tmp/bare2  (bare)\n> $ cd /tmp/bare2\n> $ git status\n> fatal: this operation must be run in a work tree\n> \n> However, Git _will_ recognize the parent directory (/tmp/bare2) as a Git repository for the purposes of commands like `git rev-parse --git-dir`. I suspect this can be fixed in `get_main_worktree` by only stripping a `.git` suffix from the path if the main worktree is not bare.\n\nThe behavior you are seeing is correct and expected (and I use bare\nrepositories with worktrees like this).\n\nThe actual bare repository can be the directory, in a .git directory,\nor in any other directory (as long as it has a .git file that points\nto the actual bare repository). The directory will still be considered a\nbare repository. For example:\n\n    mkdir -p repo/.bare\n    cd repo/.bare\n    Initialized empty Git repository in /private/tmp/repo/.bare/\n    git init --bare\n    cd ..\n    echo 'gitdir: .bare' >.git\n    git worktree list\n    git worktree add master\n    /private/tmp/repo  (bare)\n    /private/tmp/repo/master\n\nBare repositories by definition do not have a working tree, and therefore it is\nexpected that `git status` fails (can only be executed in a worktree) while\nother commands succeed.\n\nFor the purposes of `git worktree list`, the `.git` suffix **should not** be\nshown. You should never try to use `git worktree list` to get the actual path\nof the repository, the docs give the following warning:\n\n    See gitrepository-layout[5] for more information. The rule of thumb is do\n    not make any assumption about whether a path belongs to $GIT_DIR or\n    $GIT_COMMON_DIR when you need to directly access something inside $GIT_DIR.\n    Use git rev-parse --git-path to get the final path.\n\nBest,\n"},{"id":"504778","messageId":"444a412d-bf4c-4bbe-8250-18d8bc86fd21@app.fastmail.com","threadId":"62311","inReplyTo":"D4SO70M9Z1QI.1AC4QF9ZG8T4L@pm.me","subject":"Re: `git worktree list` when bare repository is named `.git`","fromName":"Rebecca Turner","fromEmail":"rbt@fastmail.com","sentAt":"2024-10-11T04:59:06Z","receivedAt":"2024-10-11T04:59:28Z","isPatch":false,"sender":{"key":"rbt@fastmail.com","avatar":null},"body":"> The actual bare repository can be the directory, in a .git directory,\n> or in any other directory (as long as it has a .git file that points\n> to the actual bare repository). The directory will still be considered a\n> bare repository.\n\nPerhaps your example got scrambled, but I can't quite reproduce it:\n\n$ mkdir -p repo/.bare\n$ cd repo/.bare\n$ git init --bare\nInitialized empty Git repository in /private/tmp/repo/.bare/\n$ git worktree list\n/private/tmp/repo/.bare  (bare)\n$ cd ..\n$ git worktree list\nfatal: not a git repository (or any of the parent directories): .git\n$ echo 'gitdir: .bare' >.git\n$ git worktree list\n/private/tmp/repo/.bare  (bare)\n\nIt seems like the $GIT_DIR is shown in the worktree list here, and when I add\na `.git` file pointing to `.bare` manually, that doesn't get listed. (Which I\nsuppose makes sense, because it's not a `git-worktree(1)` worktree, but still\nseems a little bit odd?)\n\n> For the purposes of `git worktree list`, the `.git` suffix **should not** be\n> shown. You should never try to use `git worktree list` to get the actual path\n> of the repository, the docs give the following warning:\n>\n>     See gitrepository-layout[5] for more information. The rule of thumb is do\n>     not make any assumption about whether a path belongs to $GIT_DIR or\n>     $GIT_COMMON_DIR when you need to directly access something inside $GIT_DIR.\n>     Use git rev-parse --git-path to get the final path.\n\nI understood this note to be talking about paths _within_ the `$GIT_DIR` or\n`$GIT_COMMON_DIR` itself; I see no reason why `git worktree list` wouldn't list\na bare repository _consistently_ as either the `$GIT_DIR` or the parent of the\n`$GIT_DIR`.\n\nWhat I'd like to do is get the path of the worktree so that I can move it. `git\nworktree list` gives me this information _except_ for bare repositories in\ndirectories named `.git`. I'm happy to have a special case for this, but I'd\nlike to understand the principle here.\n\nMaybe I'm just not supposed to name a bare repository `.git`? The\n`gitrepository-layout(5)` page does seem to imply this is mutually exclusive\nwith bare repositories:\n\n> A Git repository comes in two different flavours:\n>\n> •    a .git directory at the root of the working tree;\n>\n> •    a <project>.git directory that is a bare repository (i.e. without its\n>      own working tree), that is typically used for exchanging histories with\n>      others by pushing into it and fetching from it.\n\nThanks for your help!\n"},{"id":"504801","messageId":"D4SQFBEB1HYZ.QDOLCYY80DIZ@pm.me","threadId":"62311","inReplyTo":"444a412d-bf4c-4bbe-8250-18d8bc86fd21@app.fastmail.com","subject":"Re: `git worktree list` when bare repository is named `.git`","fromName":"Caleb White","fromEmail":"cdwhite3@pm.me","sentAt":"2024-10-11T05:36:57Z","receivedAt":"2024-10-11T05:37:10Z","isPatch":false,"sender":{"key":"cdwhite3@pm.me","avatar":"https://avatars.githubusercontent.com/u/4176520?v=4"},"body":"On Thu Oct 10, 2024 at 11:59 PM CDT, Rebecca Turner wrote:\n> Perhaps your example got scrambled, but I can't quite reproduce it:\n>\n> $ mkdir -p repo/.bare\n> $ cd repo/.bare\n> $ git init --bare\n> Initialized empty Git repository in /private/tmp/repo/.bare/\n> $ git worktree list\n> /private/tmp/repo/.bare  (bare)\n> $ cd ..\n> $ git worktree list\n> fatal: not a git repository (or any of the parent directories): .git\n> $ echo 'gitdir: .bare' >.git\n> $ git worktree list\n> /private/tmp/repo/.bare  (bare)\n\n> It seems like the $GIT_DIR is shown in the worktree list here, and when I add\n> a `.git` file pointing to `.bare` manually, that doesn't get listed. (Which I\n> suppose makes sense, because it's not a `git-worktree(1)` worktree, but still\n> seems a little bit odd?)\n\nAh, I was mistaken. It will show the actual path if the gitdir points\nto a separate directory.\n\n> I understood this note to be talking about paths _within_ the `$GIT_DIR` or\n> `$GIT_COMMON_DIR` itself; I see no reason why `git worktree list` wouldn't list\n> a bare repository _consistently_ as either the `$GIT_DIR` or the parent of the\n> `$GIT_DIR`.\n>\n> What I'd like to do is get the path of the worktree so that I can move it. `git\n> worktree list` gives me this information _except_ for bare repositories in\n> directories named `.git`. I'm happy to have a special case for this, but I'd\n> like to understand the principle here.\n\nWhy would you move the `.git` directory? If you're trying to move the\nrepository, then wouldn't you just move the directory that contains the\n`.git` directory?\n\nI think the main reason why the `.git` path is trimmed is because it\ndoesn't make sense to show it in non-bare repositories. No one wants to\nsee the `.git` path in a normal repository.\n\n    # global git\n    git worktree list\n    ~/sources/git  3f20f8dd05 [wt_relative_paths]\n\n    # locally modified git\n    ./git worktree list\n    ~/sources/git/.git  3f20f8dd05 [wt_relative_paths]\n\nI would rather not have the `.git` show even in bare repositories,\nif a user has moved the bare repository to `.git`, then that would\nindicate that the *intent* is for the parent directory to essentially\nact as the repository (and be moved as a cohesive unit if moving).\n\n> Maybe I'm just not supposed to name a bare repository `.git`? The\n> `gitrepository-layout(5)` page does seem to imply this is mutually exclusive\n> with bare repositories:\n>\n>> A Git repository comes in two different flavours:\n>>\n>> •    a .git directory at the root of the working tree;\n>>\n>> •    a <project>.git directory that is a bare repository (i.e. without its\n>>      own working tree), that is typically used for exchanging histories with\n>>      others by pushing into it and fetching from it.\n\nThere's nothing wrong with naming a bare repository `.git`. I've been\ndoing it for a while now and it works just fine.\n\nI've become a big fan of using bare repositories with worktrees,\nparticularly in high-trafficked repositories where I'm constantly\nswitching between branches.\n\nWhen I was first doing research on this, I found a ton of articles with\nall kinds of different ways to do it. Some folks put their worktrees in\nthe same directory as the actual repository (intermixed with their\ncode), some polluted the parent directory, some created a detached\ncommit that removed all files from the default working tree and then\ncreated the worktrees, some used a bare repository but then just created\nthe worktrees in the same directory, etc. I finally came across an\narticle that showed the `.bare` method above and I thought that was the\ncleanest method. However, after using it for a while, I realized that\nI could just move `.bare` to `.git` and it would work just fine (and\nI could remove an extra file). I've been using that method ever since.\n\nBest,\n\n"},{"id":"504843","messageId":"517c8829-f98f-4fed-af4d-b84182fb253e@app.fastmail.com","threadId":"62311","inReplyTo":"D4SQFBEB1HYZ.QDOLCYY80DIZ@pm.me","subject":"Re: `git worktree list` when bare repository is named `.git`","fromName":"Rebecca Turner","fromEmail":"rbt@fastmail.com","sentAt":"2024-10-11T16:19:27Z","receivedAt":"2024-10-11T16:19:48Z","isPatch":false,"sender":{"key":"rbt@fastmail.com","avatar":null},"body":"> Why would you move the `.git` directory? If you're trying to move the\n> repository, then wouldn't you just move the directory that contains the\n> `.git` directory?\n\nAh, I should give some context here. I'm using worktrees with the layout you\ndescribe later in your email:\n\n    my-repo/\n      .git/      <- bare git directory\n      main/      <- worktree for main branch\n      feature1/  <- worktree for feature work\n      ...\n\nI'm writing a tool to manage these layouts for you. I want to provide two\nfeatures:\n\n1. The ability to add a new worktree in a slightly more magical manner; in\n   particular, I want to be able to do `git my-tool add feature2` and add a new\n   worktree in the same directory as all the other worktrees.\n\n   For a non-bare main worktree, that directory is the parent of the main\n   worktree.\n\n   For a bare main worktree named `.git`, it's the path of the main\n   worktree. (Nothing in the `git worktree list` output indicates this is the\n   case!)\n\n   For other bare worktrees, it's the parent of the main worktree.\n\n2. The ability to convert an existing repository in this layout.\n\n   This requires separating the `$GIT_DIR` from the worktree and then\n   reassociating them, in order to convert the non-bare main worktree into a\n   bare main worktree and a second linked worktree. (In particular, I'd like to\n   avoid the cost of copying all the files in a large checkout.)\n\n> I think the main reason why the `.git` path is trimmed is because it\n> doesn't make sense to show it in non-bare repositories. No one wants to\n> see the `.git` path in a normal repository.\n>\n>     # global git\n>     git worktree list\n>     ~/sources/git  3f20f8dd05 [wt_relative_paths]\n>\n>     # locally modified git\n>     ./git worktree list\n>     ~/sources/git/.git  3f20f8dd05 [wt_relative_paths]\n\nI definitely agree with this!\n\n> I would rather not have the `.git` show even in bare repositories,\n> if a user has moved the bare repository to `.git`, then that would\n> indicate that the *intent* is for the parent directory to essentially\n> act as the repository (and be moved as a cohesive unit if moving).\n\nI suppose that makes sense? Perhaps this is an area where the documentation\nneeds some additional notes.\n\n> When I was first doing research on this, I found a ton of articles with\n> all kinds of different ways to do it. Some folks put their worktrees in\n> the same directory as the actual repository (intermixed with their\n> code), some polluted the parent directory, some created a detached\n> commit that removed all files from the default working tree and then\n> created the worktrees, some used a bare repository but then just created\n> the worktrees in the same directory, etc. I finally came across an\n> article that showed the `.bare` method above and I thought that was the\n> cleanest method. However, after using it for a while, I realized that\n> I could just move `.bare` to `.git` and it would work just fine (and\n> I could remove an extra file). I've been using that method ever since.\n\nYes, exactly! My frustration with this technique is how difficult it is to use.\nI have existing checkouts I'd like to convert to worktree repositories, and\n`git clone --bare` doesn't create remote-tracking branches, so it's strangely\ndifficult to set up repositories like this. I'm hoping to ease some of this\nwith my new tool.\n\nThanks again for your help,\n-- Rebecca\n"},{"id":"504850","messageId":"D4T4N4E0BR32.14QK0OEESB5CH@pm.me","threadId":"62311","inReplyTo":"517c8829-f98f-4fed-af4d-b84182fb253e@app.fastmail.com","subject":"Re: `git worktree list` when bare repository is named `.git`","fromName":"Caleb White","fromEmail":"cdwhite3@pm.me","sentAt":"2024-10-11T16:45:22Z","receivedAt":"2024-10-11T16:45:28Z","isPatch":false,"sender":{"key":"cdwhite3@pm.me","avatar":"https://avatars.githubusercontent.com/u/4176520?v=4"},"body":"On Fri Oct 11, 2024 at 11:19 AM CDT, Rebecca Turner wrote:\n> Ah, I should give some context here. I'm using worktrees with the layout you\n> describe later in your email:\n>\n>     my-repo/\n>       .git/      <- bare git directory\n>       main/      <- worktree for main branch\n>       feature1/  <- worktree for feature work\n>       ...\n>\n> I'm writing a tool to manage these layouts for you. I want to provide two\n> features:\n>\n> 1. The ability to add a new worktree in a slightly more magical manner; in\n>    particular, I want to be able to do `git my-tool add feature2` and add a new\n>    worktree in the same directory as all the other worktrees.\n\nIf your repository is already set up in the layout you describe, you can\njust execute `git worktree add` (I have this aliased to `g w add`, I'm a\nlazy typer haha) in the `my-repo` directory.\n\n>    For a non-bare main worktree, that directory is the parent of the main\n>    worktree.\n\nI would *not* do this, you may just want to not support this case. I\nimagine most folks have a common directory for all their repositories,\nand polluting the parent directory with worktrees sounds like a bad idea.\n\n>\n>    For a bare main worktree named `.git`, it's the path of the main\n>    worktree. (Nothing in the `git worktree list` output indicates this is the\n>    case!)\n>\n>    For other bare worktrees, it's the parent of the main worktree.\n\nNote that you can use `rev-parse` to get the actual directory:\n\n    git rev-parse --absolute-git-dir\n    ~/sources/bare-repo/.git\n\n> 2. The ability to convert an existing repository in this layout.\n>\n>    This requires separating the `$GIT_DIR` from the worktree and then\n>    reassociating them, in order to convert the non-bare main worktree into a\n>    bare main worktree and a second linked worktree. (In particular, I'd like to\n>    avoid the cost of copying all the files in a large checkout.)\n\nTo convert an existing repository to this layout, all you should have to\ndo is:\n- Add `bare = true` to the `[core]` section of the `.git/config` file\n- Remove everything except the `.git` directory\n- Create a new worktree for the default branch\n- Profit!\n\n>> When I was first doing research on this, I found a ton of articles with\n>> all kinds of different ways to do it. Some folks put their worktrees in\n>> the same directory as the actual repository (intermixed with their\n>> code), some polluted the parent directory, some created a detached\n>> commit that removed all files from the default working tree and then\n>> created the worktrees, some used a bare repository but then just created\n>> the worktrees in the same directory, etc. I finally came across an\n>> article that showed the `.bare` method above and I thought that was the\n>> cleanest method. However, after using it for a while, I realized that\n>> I could just move `.bare` to `.git` and it would work just fine (and\n>> I could remove an extra file). I've been using that method ever since.\n>\n> Yes, exactly! My frustration with this technique is how difficult it is to use.\n> I have existing checkouts I'd like to convert to worktree repositories, and\n> `git clone --bare` doesn't create remote-tracking branches, so it's strangely\n> difficult to set up repositories like this. I'm hoping to ease some of this\n> with my new tool.\n\nI've created a helper script[1] that allows me to clone a bare repository\nto use with worktrees. In case you don't know, any script in your PATH\nthat starts with `git-` can be called as `git <script-name>`. I then\nhave a git alias for this script and so I can run the following to set\neverything up:\n\n    git cloneb https://github.com/git/git\n\nThe script also allows setting up an upstream remote at the same time\nin case you've forked the repository.\n\n    git cloneb --remote=https://github.com/git/git https://github.com/<user>/git\n\n[1]: https://github.com/calebdw/dotfiles/blob/master/scripts/git-clone-bare-for-worktrees\n\nBest,\n\n"},{"id":"504885","messageId":"CACt=GQpQNvhuCJgkOcjefkaC+TToEMEp1V3Kt15t68zYpN0W4A@mail.gmail.com","threadId":"62311","inReplyTo":"517c8829-f98f-4fed-af4d-b84182fb253e@app.fastmail.com","subject":"Re: `git worktree list` when bare repository is named `.git`","fromName":"Patrick Callahan","fromEmail":"pat.callahan1@gmail.com","sentAt":"2024-10-11T22:17:57Z","receivedAt":"2024-10-11T22:18:35Z","isPatch":false,"sender":{"key":"pat.callahan1@gmail.com","avatar":null},"body":"On Fri, Oct 11, 2024 at 12:21 PM Rebecca Turner <rbt@fastmail.com> wrote:\n>\n> > Why would you move the `.git` directory? If you're trying to move the\n> > repository, then wouldn't you just move the directory that contains the\n> > `.git` directory?\n>\n> Ah, I should give some context here. I'm using worktrees with the layout you\n> describe later in your email:\n>\n>     my-repo/\n>       .git/      <- bare git directory\n>       main/      <- worktree for main branch\n>       feature1/  <- worktree for feature work\n>       ...\n>\n> I'm writing a tool to manage these layouts for you. I want to provide two\n> features:\n>\n> 1. The ability to add a new worktree in a slightly more magical manner; in\n>    particular, I want to be able to do `git my-tool add feature2` and add a new\n>    worktree in the same directory as all the other worktrees.\n>\n>    For a non-bare main worktree, that directory is the parent of the main\n>    worktree.\n>\n>    For a bare main worktree named `.git`, it's the path of the main\n>    worktree. (Nothing in the `git worktree list` output indicates this is the\n>    case!)\n>\n>    For other bare worktrees, it's the parent of the main worktree.\n\n\n Rebecca,\n\nI'm working on a tool to manage worktrees as well. Can we compare notes?\nMy directory structure separates repositories and worktrees in\nseparate directories, but the goals seem similar.\n\ngit documentation seems to treat the bare repository concept as if one\nonly uses bare repositories on a server, not locally in a development\nenvironment.\n\nBut, I've found that local worktree-based development is possible for\nmultiple applications, libraries, toolchains, and CI, but it isn't\nvery easy to maintain by hand, so tooling is a must. With some\nscripting, it works well for many bare repositories, numerous branch\nworktrees, and multiple build-and-run scenarios. Switching between\ntasks is almost instantaneous, with no need to stash or un-stash\nanything.\n\nI've managed to come up with a bash implementation with just three\ncommands for starting an editor/ide, building and running.  Parameters\nto these commands set the context for whatever it is I'm working on at\nthe moment. I'm working on separate commands to maintain the\nenvironment.  I need to do such things as clone an upstream repo,\nclone\n\n-Pat Callahan\n Framingham, Ma\n\nHere's the list of requirements I'm working with:\n\nOverview\n\n- Use only bare repositories and worktrees\n- Start an Ide, a build, a run for whatever I'm working on at the moment\n- Support development work on multiple applications, libraries, or tools\n- No limit to the number of working contexts.\n- Instant focus on a specific context: a set of repositories,\ngit-references. and worktrees\n- Instant switch between different working contexts within the same\napplication or between different applications.\n\nRepositories\n\n- Bare clones of a set of official and forked repositories from\nGithub, Gitlab, or Sourceforge\n- Local Bare Repos Only\n- All Repos in one directory as repo-name.git\n- Worktrees as needed for building\n\nWorktrees\n\n- All Worktrees in another single directory as repo-name.git-reference\n (git reference being a branch, tag, or commit\n- Worktree synchronization by git pull upstream, or git pull upstream --rebase\n- Directories\n- Hierarchy as flat as possible\n- Automatic setup of out-of-tree builds using symbolic links to one or\nmore worktrees\n\nIDE or Code Editor support\n\n- Automatic setup of multiple multi-root workspaces based on the list\nof worktrees used to build a specific branch.\n\nBuilding and Running\n\n- A default build script and custom build scripts where appropriate\n- A default application run script with custom run scripts where appropriate\n- Straightforward, flexible command line syntax\n  Example: Build four separate cmake build types: Debug,\nRelWithDebInfo for each of two branches\n\n    b app-name branch-name1 branch-name-2 d rd r m\n"},{"id":"505725","messageId":"fc908a02-4901-4c14-8b37-e246ee7065e1@app.fastmail.com","threadId":"62311","inReplyTo":"CACt=GQry+mR7fVSUEdpPjsgSoDURk4W-DRPqLkom-f9Q0KBuTQ@mail.gmail.com","subject":"Re: `git worktree list` when bare repository is named `.git`","fromName":"Rebecca Turner","fromEmail":"rbt@fastmail.com","sentAt":"2024-10-21T19:09:27Z","receivedAt":"2024-10-21T19:09:49Z","isPatch":false,"sender":{"key":"rbt@fastmail.com","avatar":null},"body":"Pat,\n\n> I'm working on a tool to manage worktrees as well. Can we compare notes?\nI've mostly finished my tool. Here's the code: https://github.com/9999years/git-prole\n\nI'm not sure the repository layout my tool creates will match your expectations, but it'll be an interesting point of comparison. Enjoy!\n\nBest,\n-- Rebecca\n"}]}