如何使用 Git 拥有多个工作目录?

发布于 2024-11-14 11:26:54 字数 397 浏览 7 评论 0 原文

我不确定这是否受 Git 支持,但理论上它似乎应该对我有用。

我的工作流程通常涉及同时编辑多个分支中的文件。换句话说,我经常想在一个分支中打开几个文件,同时在另一个分支中编辑另一个文件的内容。

我的典型解决方案是进行两次结帐,但遗憾的是我无法在它们之间共享分支和引用。我想要的是只有两个工作目录由同一个 .git 文件夹管理。

我知道本地 git clone 解决方案(默认设置是硬链接共享对象,以及 --shared 选项,它使用原始存储库),但这些解决方案只会减少磁盘空间的使用,尤其是在 --shared 的情况下,似乎充满了危险。

有没有一种方法可以使用一个 .git 文件夹,并有两个由它支持的工作目录?或者 Git 是否被硬编码为在任何时候只检出一个工作目录?

I'm not sure if this is something supported by Git, but in theory it seems like it should work to me.

My workflow often involves my editing of files in multiple branches simultaneously. In other words, I often want to open a few files in one branch is while I edit the contents of another file in another branch.

My typical solution to this is to make two checkouts, but it's a shame I can't share branches and refs between them. What I would like is to just have two working directories managed by the same .git folder.

I'm aware of local git clone solutions (the default, which is to hardlink shared objects, and the --shared option, which sets up an alternate object store with the original repo), but these solutions only cut down on disk space usage, and especially in the case of --shared, seem fraught with peril.

Is there a way to use one .git folder, and have two working directories backed by it? Or is Git hardcoded to have just one working directory checked out at any time?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

傲娇萝莉攻 2024-11-21 11:26:54

Git 2.5 自 2015 年 7 月起提议替换 contrib/workdir/git-new-workdirgit worktree

请参阅提交 68a2e6a by 朱尼奥·C·滨野(gitster

发行说明提到

contrib/workdir/git-new-workdir 的替代品,不依赖符号链接,并通过让借用者和借用者相互了解来使对象和引用的共享更安全。

请参阅 commit 799767cc9 (Git 2.5rc2)

这意味着您现在可以 执行 git 工作树添加 <路径> [<分支>]

创建并签出到其中。新的工作目录
链接到当前存储库,共享除工作之外的所有内容
目录特定文件,例如 HEAD、索引等。
git worktree 部分补充:

git 存储库可以支持多个工作树,允许您一次签出多个分支。
使用git worktree add,一个新的工作树与存储库相关联。

这个新的工作树称为“链接工作树”,而不是由“git init”或“git clone”准备的“主工作树” >“
存储库有一个主工作树(如果它不是裸存储库)和零个或多个链接的工作树。

详细信息:

每个链接的工作树在存储库的目录中都有一个私有子目录
$GIT_DIR/worktrees 目录。
私有子目录的名称通常是链接工作树路径的基本名称,可能会附加一个数字以使其唯一。
例如,当 $GIT_DIR=/path/main/.git 命令 git worktree add /path/other/test-next next 创建:

  • /path/other/test-next 中的链接工作树和
  • 还创建一个 $GIT_DIR/worktrees/test-next 目录(或者 $GIT_DIR/worktrees/test-next1 如果 test-next已被占用)。

在链接的工作树中:

  • $GIT_DIR 设置为指向此私有目录(例如示例中的 /path/main/.git/worktrees/test-next)并且
  • $GIT_COMMON_DIR 设置为指向主工作树的 $GIT_DIR(例如 /path/main/.git)。< /里>

这些设置是在位于链接工作树顶部目录的 .git 文件中进行的。

使用完链接的工作树后,您只需将其删除即可。
存储库中工作树的管理文件最终将自动删除(请参阅 git config 中的 gc.pruneworktreesexpire ),或者您可以运行 git worktree prune > 在主工作树或任何链接的工作树中
清理所有过时的管理文件。


警告:仍然存在 git worktree “BUGS” 需要注意的部分。

子模块的支持不完整< /strong>.
不建议对一个超级项目进行多次签出。



注意:使用 git 2.7rc1(2015 年 11 月),您可以列出您的工作树。
请参阅 提交 bb9c03b提交 92718b7, 提交 5193490, 提交 1ceb7f9, 提交 1ceb7f9, 提交 5193490, 提交 1ceb7f9, 提交 1ceb7f9(2015 年 10 月 8 日),提交 92718b7, 提交 5193490, 提交 1ceb7f9, 提交 1ceb7f9(2015 年 10 月 8 日),提交 5193490, 提交 1ceb7f9(2015 年 10 月 8 日),提交 1ceb7f9(2015 年 10 月 8 日),以及 提交 ac6c561(2015 年 10 月 2 日),作者:迈克尔·拉帕佐(拉帕佐
(由 Junio C Hamano -- gitster -- 合并于 提交a46dcfb,2015 年 10 月 26 日)

worktree:添加“list”命令

'git worktree list' 迭代工作树列表,并输出
工作树的详细信息,包括工作树的路径、当前的
检查修订版和分支,以及工作树是否是空的。

$ git 工作树列表
/路径/到/裸源(裸)
/path/to/linked-worktree abcd1234 [主]
/path/to/other-linked-worktree 1234abc(分离的 HEAD)

还有瓷器格式选项可供选择。

瓷器格式的每个属性都有一行。

  • 列出的属性带有由单个空格分隔的标签和值。
  • 布尔属性(例如“bare”和“detached”)仅作为标签列出,并且仅当且仅当值为 true 时才出现。
  • 空行表示工作树的结尾

例如:

$ git worktree list --porcelain

worktree /path/to/bare-source
bare

worktree /path/to/linked-worktree
HEAD abcd1234abcd1234abcd1234abcd1234abcd1234
branch refs/heads/master

worktree /path/to/other-linked-worktree
HEAD 1234abc1234abc1234abc1234abc1234abc1234a
detached

注意:如果您移动工作树文件夹,则需要手动更新gitdir文件。

请参阅提交 618244e(2016 年 1 月 22 日)和提交 d4cddd6(2016 年 1 月 18 日),作者:阮泰Ngọc Duy (pclouds)
帮助者:Eric Sunshine (sunshineco)
(由 Junio C Hamano -- gitster -- 合并于 提交d0a1cbc,2016 年 2 月 10 日)

git 中的新文档 2.8(2016 年 3 月)将包括:

如果移动链接的工作树,则需要更新“gitdir”文件
在条目的目录中。
例如,如果链接的工作树移动到 /newpath/test-next 且其 .git 文件指向 /path/main/.git/worktrees /test-next,然后更新
/path/main/.git/worktrees/test-next/gitdir 改为引用 /newpath/test-next


删除分支时要小心:在 git 2.9(2016 年 6 月)之前,您可以删除另一个工作树中正在使用的分支。

当使用“git worktree”功能时,允许“git Branch -d
删除在另一个工作树中签出的分支。

请参阅 commit f292244(2016 年 3 月 29 日),作者:山口一树(
帮助者:Eric Sunshine (sunshineco)
(由 Junio C Hamano -- gitster -- 合并于 提交4fca4e3,2016 年 4 月 13 日)

branch -d:拒绝删除当前已签出的分支

当当前工作树签出分支时,删除该分支
禁止分支。
但是,当该分支仅由其他工作树签出时,错误删除会成功。
使用 find_shared_symref() 检查分支是否正在使用,而不仅仅是
与当前工作树的 HEAD 进行比较。


同样,在 git 2.9(2016 年 6 月)之前,重命名在另一个工作树中检出的分支不会调整所述另一个工作树中的符号 HEAD。

请参阅提交 18eb3a9(2016 年 4 月 8 日)和提交 70999e9, 提交 2233066(2016 年 3 月 27 日),作者:山口一树(
(由 Junio C Hamano -- gitster -- 合并于 提交741a694,2016 年 4 月 18 日)

branch -m:更新所有每个工作树的 HEAD

重命名分支时,当前仅重命名当前工作树的 HEAD
已更新,但它必须更新指向的所有工作树的 HEAD
旧分支。

这是当前行为,/path/to/wt 的 HEAD 未更新:

 % git 工作树列表
 /路径/到2c3c5f2 [主]
 /path/to/wt 2c3c5f2 [旧名称]
 % git 分支 -m master master2
 % git 工作树列表
 /路径/到2c3c5f2 [master2]
 /path/to/wt 2c3c5f2 [旧名称]
 % git 分支 -m 旧名称 新名称
 % git 工作树列表
 /路径/到2c3c5f2 [master2]
 /path/to/wt 0000000 [旧名称]

此补丁通过更新所有相关的工作树 HEAD 修复了此问题
重命名分支时。


git 2.10(2016 年第 3 季度)正式支持锁定机制,

请参阅 commit 080739b提交 6d30862, 提交 58142c0, 提交 346ef53, 提交 346ef53, 提交 58142c0, 提交 346ef53, 提交 346ef53(2016 年 6 月 13 日),以及 提交 984ad9e, 提交 6835314(2016 年 6 月 3 日),作者:阮Thái Ngọc Duy (pclouds)
建议者:Eric Sunshine (sunshineco)
(由 Junio C Hamano -- gitster -- 合并于 提交2c608e0,2016 年 7 月 28 日)

git worktree lock [--reason <string>] <worktree>
git worktree unlock <worktree>

如果链接的工作树存储在便携式设备或网络共享上
它并不总是安装的,您可以防止其管理文件
通过发出 git worktree lock 命令进行修剪(可选)
指定 --reason 来解释工作树被锁定的原因。

:如果工作树路径中的最后一个路径组件在工作树中是唯一的,则可用于识别工作树。
例如,如果您只需在“/abc/def/ghi”和“/abc/def/ggg”处工作树,则“ghi”或“def/ghi”足以指向以前的工作树。



Git 2.13(2017 年第 2 季度)在 提交 507e6e9(4 月 12 日2017),作者:Nguyễn Thái Ngọc Duy (pclouds)
建议者:David Taylor (dt)
帮助者:Jeff King (peff)
(由 Junio C Hamano -- gitster -- 合并于 提交e311597,2017 年 4 月 26 日)

允许在创建工作树后立即锁定它。
这有助于防止“git worktree add; git worktree lock”和
git worktree prune”。

因此,git worktree add' --lock 相当于 git worktree add 之后的 git worktree lock,但没有竞争条件。


Git 2.17+(2018 年第 2 季度)添加了 git worktree move / git worktree remove请参阅此答案


Git 2.19(2018 年第 3 季度)添加“--quiet”选项以减少“git worktree add
冗长。

请参阅 提交 371979c(2018 年 8 月 15 日),作者:埃利亚·平托(devzero2000
帮助者:Martin Ågren [电子邮件受保护]Duy Nguyen (pclouds)Eric Sunshine (sunshineco).
(由 Junio C Hamano -- gitster -- 合并于 提交a988ce9,2018 年 8 月 27 日)

worktree:添加--quiet选项

将“--quiet”选项添加到 git worktree,就像其他 git 命令一样。
add”是唯一受其影响的命令,因为除“list”之外的所有其他命令目前默认情况下都是静默的。


请注意,“git worktree add”用于执行“使用 stat 查找可用名称”
然后是mkdir”,这很容易出现竞争。
Git 2.22(2019 年第 2 季度)已通过使用 mkdir 并在循环中响应 EEXIST 修复了此问题。

请参阅 提交 7af01f2(2019 年 2 月 20 日),作者:米哈尔·苏哈内克(hramrach
(由 Junio C Hamano -- gitster -- 合并于 提交20fe798,2019 年 4 月 9 日)

worktree:修复worktree添加竞赛

Git 运行一个统计循环来查找可用的工作树名称,并且
然后对找到的名称执行mkdir
将其转为 mkdir 循环,以避免再次调用 worktree add 查找相同的自由名称并首先创建目录。


Git 2.22(2019 年第 2 季度)修复了判断 Git 存储库是否具有工作树的逻辑,以保护“gitbranch-D”不删除当前检查的分支
错误地退出。
对于具有不寻常名称的存储库,此逻辑的实现被破坏,不幸的是,这已成为当今子模块的常态。

请参阅 提交 f3534c9(2019 年 4 月 19 日),作者:乔纳森·谭(jhowtan)
(由 Junio C Hamano -- gitster -- 合并于 提交ec2642a,2019 年 5 月 8 日)

worktree:更新is_bare启发式

当运行“gitbranch -D ”时,Git 通常首先检查是否
分支当前已签出。
但是,如果该存储库的 Git 目录不在“/.git”,则不会执行此检查,如果该存储库是一个子模块,其 Git 目录存储为例如“super/.git/modules/”。
这会导致分支被删除,即使它已签出。

这是因为 worktree.c 中的 get_main_worktree()
工作树仅使用启发式,如果工作树的
路径不以“/.git”结尾,否则不裸露。
is_bare 代码是在 92718b7 (" worktree:将详细信息添加到工作树结构中”, 2015-10-08,Git v2.7.0-rc0),遵循 pre-core.bare 启发式。

这个补丁做了两件事:

  • get_main_worktree() 使用 is_bare_repository(),在 7d1864c (“引入 is_bare_repository() 和 core.bare 配置变量”,2007-01-07,Git v1.5.0-rc1)并在 e90fdc3(“清理工作树处理”,2007-08-01,Git v1.5.3-rc4)。
    这解决了上述“gitbranch -D ”问题。
    然而...
  • 如果存储库具有 core.bare=1,但“git”命令是从其辅助工作树之一运行的,is_bare_repository() 返回 false(这很好,因为有可用的工作树)。
    并且,当主工作树是裸露时将其视为非裸露会导致问题:例如,无法从主工作树的 HEAD 引用的辅助工作树中删除分支,即使该主工作树是裸露的。

为了避免这种情况,请在设置 is_bare 时检查 core.bare
如果core.bare=1,则信任它,否则,使用is_bare_repository()


在 Git 2.29(2020 年第 4 季度)中,“worktree”API 可以更好地确定工作树路径。

请参阅提交918d8ff提交 1c4854e, 提交 246756f, 提交 62573a5(2020 年 7 月 31 日),作者:埃里克·阳光(sunshineco
(由 Junio C Hamano -- gitster -- 合并于 提交197253e,2020 年 8 月 10 日)

worktree:删除虚假和不必要的路径修改< /H2>

签名:Eric Sunshine

.git/worktrees/&lt; id&gtd;/gitdir 的内容必须是“ /path/path/to/worktree/.git ”的形式的路径。
任何其他内容都将指示损坏的“ gitdir ”文件。

要确定工作室的路径本身,只是剥离“ /。git ”后缀,这确实是从开始时确定工作室路径的方式。

然而, noreferrer“> batch#7 )扩展了途径的途径。
如果它无法从路径中剥离“ git ”,则它将当前的工作目录报告为链接的Worktree路径:

  if(!strbuf_strip_suffix(&amp; worktree_path,“ /.git”)){){
    strbuf_reset(&amp; worktree_path);
    strbuf_add_absolute_path(&amp; worktree_path,“。”);
    strbuf_strip_suffix(&amp; worktree_path,“/。”);
}  

这种逻辑显然是虚假的;通常永远不会是正确的行为。它在

有可能将此逻辑引入某种程度上处理损坏的“ gitdir ”文件,以便它返回 某种有意义的值,但返回当前工作的工作目录没有帮助。实际上,这是非常误导的(除了当前目录是“ gitdir >”条目的一种特定情况下,
此外,向用户报告损坏的价值,而不是对其进行整理并直接隐藏它,这可能有助于诊断问题。

因此,将此伪造的路径弹出并将逻辑恢复到仅剥离的原始行为“ /。git ”。

Git 2.5 proposes since July 2015 a replacement for contrib/workdir/git-new-workdir: git worktree

See commit 68a2e6a by Junio C Hamano (gitster).

The release note mentions:

A replacement for contrib/workdir/git-new-workdir that does not rely on symbolic links and make sharing of objects and refs safer by making the borrowee and borrowers aware of each other.

See commit 799767cc9 (Git 2.5rc2)

That means you now can do a git worktree add <path> [<branch>]

Create <path> and checkout <branch> into it. The new working directory
is linked to the current repository, sharing everything except working
directory specific files such as HEAD, index, etc.
The git worktree section adds:

A git repository can support multiple working trees, allowing you to check out more than one branch at a time.
With git worktree add, a new working tree is associated with the repository.

This new working tree is called a "linked working tree" as opposed to the "main working tree" prepared by "git init" or "git clone".
A repository has one main working tree (if it's not a bare repository) and zero or more linked working trees.

details:

Each linked working tree has a private sub-directory in the repository's
$GIT_DIR/worktrees directory.
The private sub-directory's name is usually the base name of the linked working tree's path, possibly appended with a number to make it unique.
For example, when $GIT_DIR=/path/main/.git the command git worktree add /path/other/test-next next creates:

  • the linked working tree in /path/other/test-next and
  • also creates a $GIT_DIR/worktrees/test-next directory (or $GIT_DIR/worktrees/test-next1 if test-next is already taken).

Within a linked working tree:

  • $GIT_DIR is set to point to this private directory (e.g. /path/main/.git/worktrees/test-next in the example) and
  • $GIT_COMMON_DIR is set to point back to the main working tree's $GIT_DIR (e.g. /path/main/.git).

These settings are made in a .git file located at the top directory of the linked working tree.

When you are done with a linked working tree you can simply delete it.
The working tree's administrative files in the repository will eventually be removed automatically (see gc.pruneworktreesexpire in git config), or you can run git worktree prune in the main or any linked working tree to
clean up any stale administrative files.


Warning: there is still a git worktree "BUGS" section to be aware of.

The support for submodules is incomplete.
It is NOT recommended to make multiple checkouts of a superproject.


Note: with git 2.7rc1 (Nov 2015) you are able to list your worktrees.
See commit bb9c03b, commit 92718b7, commit 5193490, commit 1ceb7f9, commit 1ceb7f9, commit 5193490, commit 1ceb7f9, commit 1ceb7f9 (08 Oct 2015), commit 92718b7, commit 5193490, commit 1ceb7f9, commit 1ceb7f9 (08 Oct 2015), commit 5193490, commit 1ceb7f9 (08 Oct 2015), commit 1ceb7f9 (08 Oct 2015), and commit ac6c561 (02 Oct 2015) by Michael Rappazzo (rappazzo).
(Merged by Junio C Hamano -- gitster -- in commit a46dcfb, 26 Oct 2015)

worktree: add 'list' command

'git worktree list' iterates through the worktree list, and outputs
details of the worktree including the path to the worktree, the currently
checked out revision and branch, and if the work tree is bare.

$ git worktree list
/path/to/bare-source (bare)
/path/to/linked-worktree abcd1234 [master]
/path/to/other-linked-worktree 1234abc (detached HEAD)

There is also porcelain format option available.

The porcelain format has a line per attribute.

  • Attributes are listed with a label and value separated by a single space.
  • Boolean attributes (like 'bare' and 'detached') are listed as a label only, and are only present if and only if the value is true.
  • An empty line indicates the end of a worktree

For instance:

$ git worktree list --porcelain

worktree /path/to/bare-source
bare

worktree /path/to/linked-worktree
HEAD abcd1234abcd1234abcd1234abcd1234abcd1234
branch refs/heads/master

worktree /path/to/other-linked-worktree
HEAD 1234abc1234abc1234abc1234abc1234abc1234a
detached

Note: if you MOVE a worktree folder, you need to manually update the gitdir file.

See commit 618244e (22 Jan 2016), and commit d4cddd6 (18 Jan 2016) by Nguyễn Thái Ngọc Duy (pclouds).
Helped-by: Eric Sunshine (sunshineco).
(Merged by Junio C Hamano -- gitster -- in commit d0a1cbc, 10 Feb 2016)

The new doc in git 2.8 (March 2016) will include:

If you move a linked working tree, you need to update the 'gitdir' file
in the entry's directory.
For example, if a linked working tree is moved to /newpath/test-next and its .git file points to /path/main/.git/worktrees/test-next, then update
/path/main/.git/worktrees/test-next/gitdir to reference /newpath/test-next instead.


Be careful when deleting a branch: before git 2.9 (June 2016), you could delete one in use in another working tree.

When "git worktree" feature is in use, "git branch -d" allowed
deletion of a branch that is checked out in another worktree.

See commit f292244 (29 Mar 2016) by Kazuki Yamaguchi (rhenium).
Helped-by: Eric Sunshine (sunshineco).
(Merged by Junio C Hamano -- gitster -- in commit 4fca4e3, 13 Apr 2016)

branch -d: refuse deleting a branch which is currently checked out

When a branch is checked out by current working tree, deleting the
branch is forbidden.
However when the branch is checked out only by other working trees, deleting incorrectly succeeds.
Use find_shared_symref() to check if the branch is in use, not just
comparing with the current working tree's HEAD.


Similarly, before git 2.9 (June 2016), renaming a branch checked out in another worktree did not adjust the symbolic HEAD in said other worktree.

See commit 18eb3a9 (08 Apr 2016), and commit 70999e9, commit 2233066 (27 Mar 2016) by Kazuki Yamaguchi (rhenium).
(Merged by Junio C Hamano -- gitster -- in commit 741a694, 18 Apr 2016)

branch -m: update all per-worktree HEADs

When renaming a branch, currently only the HEAD of current working tree
is updated, but it must update HEADs of all working trees which point at
the old branch.

This is the current behavior, /path/to/wt's HEAD is not updated:

 % git worktree list
 /path/to     2c3c5f2 [master]
 /path/to/wt  2c3c5f2 [oldname]
 % git branch -m master master2
 % git worktree list
 /path/to     2c3c5f2 [master2]
 /path/to/wt  2c3c5f2 [oldname]
 % git branch -m oldname newname
 % git worktree list
 /path/to     2c3c5f2 [master2]
 /path/to/wt  0000000 [oldname]

This patch fixes this issue by updating all relevant worktree HEADs
when renaming a branch.


The locking mechanism is officially supported with git 2.10 (Q3 2016)

See commit 080739b, commit 6d30862, commit 58142c0, commit 346ef53, commit 346ef53, commit 58142c0, commit 346ef53, commit 346ef53 (13 Jun 2016), and commit 984ad9e, commit 6835314 (03 Jun 2016) by Nguyễn Thái Ngọc Duy (pclouds).
Suggested-by: Eric Sunshine (sunshineco).
(Merged by Junio C Hamano -- gitster -- in commit 2c608e0, 28 Jul 2016)

git worktree lock [--reason <string>] <worktree>
git worktree unlock <worktree>

If a linked working tree is stored on a portable device or network share
which is not always mounted, you can prevent its administrative files from
being pruned by issuing the git worktree lock command, optionally
specifying --reason to explain why the working tree is locked.

<worktree>: If the last path components in the working tree's path is unique among working trees, it can be used to identify worktrees.
For example if you only have to working trees at "/abc/def/ghi" and "/abc/def/ggg", then "ghi" or "def/ghi" is enough to point to the former working tree.


Git 2.13 (Q2 2017) add a lock option in commit 507e6e9 (12 Apr 2017) by Nguyễn Thái Ngọc Duy (pclouds).
Suggested-by: David Taylor (dt).
Helped-by: Jeff King (peff).
(Merged by Junio C Hamano -- gitster -- in commit e311597, 26 Apr 2017)

Allow to lock a worktree immediately after it's created.
This helps prevent a race between "git worktree add; git worktree lock" and
"git worktree prune".

So git worktree add' --lock is the equivalent of git worktree lock after git worktree add, but without race condition.


Git 2.17+ (Q2 2018) adds git worktree move / git worktree remove: see this answer.


Git 2.19 (Q3 2018) add a "--quiet" option to make "git worktree add" less
verbose.

See commit 371979c (15 Aug 2018) by Elia Pinto (devzero2000).
Helped-by: Martin Ågren [email protected], Duy Nguyen (pclouds), and Eric Sunshine (sunshineco).
(Merged by Junio C Hamano -- gitster -- in commit a988ce9, 27 Aug 2018)

worktree: add --quiet option

Add the '--quiet' option to git worktree, as for the other git commands.
'add' is the only command affected by it since all other commands, except 'list', are currently silent by default.


Note that "git worktree add" used to do a "find an available name with stat
and then mkdir", which is race-prone.
This has been fixed with Git 2.22 (Q2 2019) by using mkdir and reacting to EEXIST in a loop.

See commit 7af01f2 (20 Feb 2019) by Michal Suchanek (hramrach).
(Merged by Junio C Hamano -- gitster -- in commit 20fe798, 09 Apr 2019)

worktree: fix worktree add race

Git runs a stat loop to find a worktree name that's available and
then does mkdir on the found name.
Turn it to mkdir loop to avoid another invocation of worktree add finding the same free name and creating the directory first.


Git 2.22 (Q2 2019) fixes the logic to tell if a Git repository has a working tree protects "git branch -D" from removing the branch that is currently checked
out by mistake.
The implementation of this logic was broken for repositories with unusual name, which unfortunately is the norm for submodules these days.

See commit f3534c9 (19 Apr 2019) by Jonathan Tan (jhowtan).
(Merged by Junio C Hamano -- gitster -- in commit ec2642a, 08 May 2019)

worktree: update is_bare heuristics

When "git branch -D <name>" is run, Git usually first checks if that
branch is currently checked out.
But this check is not performed if the Git directory of that repository is not at "<repo>/.git", which is the case if that repository is a submodule that has its Git directory stored as "super/.git/modules/<repo>", for example.
This results in the branch being deleted even though it is checked out.

This is because get_main_worktree() in worktree.c sets is_bare on a
worktree only using the heuristic that a repo is bare if the worktree's
path does not end in "/.git", and not bare otherwise.
This is_bare code was introduced in 92718b7 ("worktree: add details to the worktree struct", 2015-10-08, Git v2.7.0-rc0), following a pre-core.bare heuristic.

This patch does 2 things:

  • Teach get_main_worktree() to use is_bare_repository() instead, introduced in 7d1864c ("Introduce is_bare_repository() and core.bare configuration variable", 2007-01-07, Git v1.5.0-rc1) and updated in e90fdc3 ("Clean up work-tree handling", 2007-08-01, Git v1.5.3-rc4).
    This solves the "git branch -D <name>" problem described above.
    However...
  • If a repository has core.bare=1 but the "git" command is being run from one of its secondary worktrees, is_bare_repository() returns false (which is fine, since there is a worktree available).
    And, treating the main worktree as non-bare when it is bare causes issues: for example, failure to delete a branch from a secondary worktree that is referred to by a main worktree's HEAD, even if that main worktree is bare.

In order to avoid that, also check core.bare when setting is_bare.
If core.bare=1, trust it, and otherwise, use is_bare_repository().


With Git 2.29 (Q4 2020), the "worktree" API offers a better determination of a worktree path.

See commit 918d8ff, commit 1c4854e, commit 246756f, commit 62573a5 (31 Jul 2020) by Eric Sunshine (sunshineco).
(Merged by Junio C Hamano -- gitster -- in commit 197253e, 10 Aug 2020)

worktree: drop bogus and unnecessary path munging

Signed-off-by: Eric Sunshine

The content of .git/worktrees/<id>/gitdir must be a path of the form "/path/to/worktree/.git".
Any other content would be indicative of a corrupt "gitdir" file.

To determine the path of the worktree itself one merely strips the "/.git" suffix, and this is indeed how the worktree path was determined from inception.

However, 5193490442 ("worktree: add a function to get worktree details", 2015-10-08, Git v2.7.0-rc0 -- merge listed in batch #7) extended the path manipulation in a mysterious way.
If it is unable to strip "/.git" from the path, then it instead reports the current working directory as the linked worktree's path:

if (!strbuf_strip_suffix(&worktree_path, "/.git")) {
    strbuf_reset(&worktree_path);
    strbuf_add_absolute_path(&worktree_path, ".");
    strbuf_strip_suffix(&worktree_path, "/.");
}  

This logic is clearly bogus; it can never be generally correct behavior. It materialized out of thin air in 5193490442 with neither explanation nor tests to illustrate a case in which it would be desirable.

It's possible that this logic was introduced to somehow deal with a corrupt "gitdir" file, so that it returns some sort of meaningful value, but returning the current working directory is not helpful. In fact, it is quite misleading (except in the one specific case when the current directory is the worktree whose "gitdir" entry is corrupt).
Moreover, reporting the corrupt value to the user, rather than fibbing about it and hiding it outright, is more helpful since it may aid in diagnosing the problem.

Therefore, drop this bogus path munging and restore the logic to the original behavior of merely stripping "/.git".

零度° 2024-11-21 11:26:54

git 发行版附带 名为 git-new-workdir 的贡献脚本
您可以按如下方式使用它:

git-new-workdir project-dir new-workdir branch

其中 project-dir 是包含 .git 存储库的目录的名称。
此脚本创建另一个 .git 目录,其中包含许多指向原始目录的符号链接,但无法共享的文件(如当前分支)除外,允许您在两个不同的分支中工作。

听起来有点脆弱,但这是一个选择。

The git distribution comes with a contributed script called git-new-workdir.
You would use it as follows:

git-new-workdir project-dir new-workdir branch

where project-dir is the name of the directory containing your .git repository.
This scripts creates another .git directory with many symlinks to the original one except for files that cannot be shared (like the current branch), allowing you to work in two different branches.

It sounds a bit fragile, but it's an option.

老街孤人 2024-11-21 11:26:54

我遇到了这个问题,希望找到我在这里找不到的解决方案。现在我确实找到了我需要的东西,我决定将其发布在这里供其他人使用。

警告:如果您需要同时编辑多个分支(例如OP状态),这可能不是一个好的解决方案。它用于签出多个分支> 同时您打算编辑。 (多个工作目录由一个 .git 文件夹支持。)

自从我第一次遇到这个问题以来,我学到了一些东西:

  1. What a "裸存储库”是。它本质上是 .git 目录的内容,而不位于工作树中。

  2. 事实上,您可以使用 git 选项在命令行上指定您正在使用的存储库的位置(.git 目录的位置)--git-dir=

  3. 的位置

  4. 什么是“镜像存储库”。

最后一点是一个非常重要的区别。我实际上不想在存储库上工作,我只需要同时签出不同分支和/或标签的副本。事实上,我需要保证分支最终不会与我的远程分支不同。所以镜子对我来说是完美的。

因此,对于我的用例,我通过这样做得到了我需要的东西:

git clone --mirror <remoteurl> <localgitdir> # Where localgitdir doesn't exist yet
mkdir firstcopy
mkdir secondcopy
git --git-dir=<localgitdir> --work-tree=firstcopy checkout -f branch1
git --git-dir=<localgitdir> --work-tree=secondcopy checkout -f branch2

对此的一个重要警告是,两个副本没有单独的 HEAD。因此,在上述操作之后,运行 git --git-dir= --work-tree=firstcopy status 将从branch2到branch1的所有差异显示为未提交的更改 - 因为 HEAD 指向branch2。 (这就是为什么我使用 -f 选项来checkout,因为我实际上根本不打算在本地进行任何更改。我可以签出任何标签或分支工作树,只要我使用 -f 选项。)

对于我的用例,在同一台计算机上共存多个结帐而无需编辑它们,这非常有效。我不知道是否有任何方法可以在没有其他答案中涵盖的脚本的情况下为多个工作树提供多个 HEAD,但我希望这对其他人有帮助。

I came across this question hoping for a solution I didn't find here. So now that I did find what I needed, I decided to post it here for others.

Caveat: This is probably not a good solution if you need to edit multiple branches simultaneously, like OP states. It is for having multiple branches checked out simultaneously that you don't intend to edit. (Multiple working directories backed by one .git folder.)

There were a few things I've learned since I came to this question the first time:

  1. What a "bare repository" is. It is essentially the contents of the .git directory, without being located in a working tree.

  2. The fact that you can specify the location of the repo you are using (the location of your .git dir) on the command line with the git option --git-dir=

  3. The fact that you can specify the location of your working copy with --work-tree=

  4. What a "mirror repo" is.

This last is a pretty important distinction. I don't actually want to work on the repo, I just need to have copies of different branches and/or tags checked out simultaneously. In actual fact, I need to guarantee that the branches don't end up different from my remote's branches. So a mirror is perfect for me.

So for my use case, I got what I needed by doing:

git clone --mirror <remoteurl> <localgitdir> # Where localgitdir doesn't exist yet
mkdir firstcopy
mkdir secondcopy
git --git-dir=<localgitdir> --work-tree=firstcopy checkout -f branch1
git --git-dir=<localgitdir> --work-tree=secondcopy checkout -f branch2

The big caveat about this is that there isn't a separate HEAD for the two copies. So after the above, running git --git-dir=<localgitdir> --work-tree=firstcopy status will show all the differences from branch2 to branch1 as uncommitted changes - because HEAD is pointing at branch2. (That's why I use the -f option to checkout, because I'm not actually planning to make any changes locally at all. I can checkout any tag or branch for any work-tree, as long as I use the -f option.)

For my use case of having multiple checkouts co-existing on the same computer without needing to edit them, this works perfectly. I don't know if there is any way to have multiple HEADs for the multiple work trees without a script such as is covered in the other answers, but I hope this is helpful to someone else anyway.

可是我不能没有你 2024-11-21 11:26:54

我能想到的唯一解决方案是克隆两个目录并将它们添加为彼此的远程存储库。然后,您可以继续将内容从已更改的存储库提取到另一存储库,而无需实际将任何内容推送到远程存储库。

我假设您想要有两个工作目录,而不是远程的两个克隆,因为您不想将某些分支推送到远程。否则,遥控器的两个克隆就可以正常工作 - 您只需要进行一些推拉操作即可使所有三个克隆保持同步。

The only solution I can think of is to clone two directories and add them as remote repositories of each other. You can then keep pulling stuff from the changed one into the other without actually pushing anything to the remote repository.

I am assuming you want to have two working directories and not two clones of the remote because you don't want to push some branches to the remote. Otherwise, two clones of your remote would work just fine - you just need to do some pushes and pulls to keep all three in sync.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文