使用Git的多个工作目录?


Multiple working directories with Git?

我不确定这是否得到了Git的支持,但从理论上讲,它似乎对我有用。

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

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

我知道本地的Git克隆解决方案(默认为硬链接共享对象,以及使用原始repo建立备用对象存储的--shared选项),但是这些解决方案只会减少磁盘空间的使用,特别是在--shared的情况下,似乎充满了危险。

有没有一种方法可以使用一个.git文件夹,并且有两个工作目录由它支持?还是Git硬编码以便在任何时候只签出一个工作目录?


Git 2.5自2015年7月起提出替换contrib/workdir/git-new-workdir:Git Worktree好的。

见Junio C Hamano提交的第68A2E6A号决议(gitster)。好的。

发行说明提到:好的。

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.

Ok.

见提交799767CC9(Git 2.5RC2)好的。

这意味着你现在可以做一个git worktree add []。好的。

Create and checkout 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:

Ok.

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.

Ok.

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.

Ok.

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:

Ok.

  • 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:

Ok.

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

Ok.

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.

Ok.

警告:还有一个git worktree的"bugs"部分需要注意。好的。

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

注:使用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日)ZZO(rappazzo)。(由Junio C Hamano--gitster合并,提交A46DCFB,2015年10月26日)好的。

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.

Ok.

1
2
3
4
$ 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.

Ok.

The porcelain format has a line per attribute.

Ok.

  • 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

例如:好的。

1
2
3
4
5
6
7
8
9
10
11
12
$ 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

注意:如果移动WorkTree文件夹,则需要手动更新gitdir文件。好的。

参见NGUY提交的"提交618244E"(2016年1月22日)和"提交D4CDDD6"(2016年1月18日)?不知道吗?杜伊(pclouds)。帮助者:Eric Sunshine(sunshineco)。(由Junio C Hamano——EDOCX1【1】——于2016年2月10日在提交文件D0A1CBC中合并)好的。

Git 2.8(2016年3月)中的新文件将包括:好的。

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.

Ok.

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

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

Ok.

见Kazuki Yamaguchi的Commit F292244(2016年3月29日)(rhenium)。帮助者:Eric Sunshine(sunshineco)。(由Junio C Hamano——EDOCX1【1】——于2016年4月13日提交4FCA4E3)好的。

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

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

同样,在Git2.9(2016年6月)之前,重命名在另一个工作树中签出的分支没有调整所述其他工作树中的符号头。好的。

见提交18EB3A9(2016年4月8日),提交70999E9,提交2233066(2016年3月27日),提交人:Kazuki Yamaguchi(rhenium)。(由Junio C Hamano——EDOCX1【1】——于2016年4月18日提交741A694)好的。

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.

Ok.

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

Ok.

1
2
3
4
5
6
7
8
9
10
11
  % 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.

Ok.

锁定机制由Git 2.10(2016年第3季度)正式支持。好的。

参见提交080739B、提交6D30862、提交58142C0、提交346EF53、提交346EF53、提交58142C0、提交346EF53、提交346EF53(2016年6月13日)和提交984AD9E、由NGUY提交6835314(2016年6月3日)?不知道吗?杜伊(pclouds)。推荐人:Eric Sunshine(sunshineco)。(由Junio C Hamano——EDOCX1【1】——于2016年7月28日提交2c608e0)好的。

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

Ok.

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

Ok.

Git 2.13(2017年第2季度)在Nguy的承诺507E6E9(2017年4月12日)中添加一个lock期权。不知道吗?杜伊(pclouds)。推荐人:大卫·泰勒(dt)。帮助者:杰夫·金(peff)。(由Junio C Hamano——gitster于2017年4月26日提交E311597合并)好的。

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

Ok.

因此,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季度)增加"EDOCX1"〔15〕选项,使"EDOCX1"〔12〕减少详细。好的。

见Elia Pinto(devzero2000的Commit 371979c(2018年8月15日)。帮助者:马丁?gren、duy nguyen(pclouds)和eric sunshine(sunshineco)。(由Junio C Hamano——EDOCX1【1】——于2018年8月27日提交A988CE9)好的。

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.

Ok.

请注意,"git worktree add用于执行"使用stat查找可用名称"然后是mkdir,这是一个容易发生种族冲突的国家。通过使用mkdir并在循环中对EEXIST作出反应,用git 2.22(2019年第2季度)解决了这一问题。好的。

见Michal Suchanek(hramrach)提交的7AF01F2(2019年2月20日)。(由Junio C Hamano--gitster--于2019年2月20日和4月9日合并)好的。

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.

Ok.

Git2.22(2019年第2季度)修复了判断Git存储库是否具有工作树的逻辑,以防止"git branch -D删除当前选中的分支出了差错。对于具有不寻常名称的存储库,这种逻辑的实现被破坏了,不幸的是,这是目前子模块的标准。好的。

见Jonathan Tan(jhowtan的Commit F3534C9(2019年4月19日)。(由Junio C Hamano——EDOCX1【5】——于2019年5月8日提交EC2642A合并)好的。

代码拉取请求178洞察力好的。worktree更新is_bare启发式

当运行"git branch -D 时,git通常首先检查是否分支当前已签出。但是,如果该存储库的git目录不在"EDOCX1"〔12〕处,则不执行此检查,例如,如果该存储库是其git目录存储为"EDOCX1"〔13〕的子模块,则是这种情况。这会导致分支被删除,即使它已被签出。好的。

这是因为worktree.c中的get_main_worktree()is_bare设置为WorkTree只使用启发式的repo,如果WorkTree的路径不以"EDOCX1"〔17〕结尾,否则不裸露。该is_bare代码是在92718b7中引入的("worktree:向工作树结构添加细节",2015-10-08,git v2.7.0-rc0),遵循pre-core.bare启发式。好的。

这个补丁有两个功能:好的。

  • get_main_worktree()改为使用7d1864c中引入的is_bare_repository()(介绍is撘bare ou repository()和core.bare配置变量,2007-01-07,git v1.5.0-rc1),并在e90fdc3中更新("清理工作树处理",2007-08-01,git v1.5.3-rc4)。这就解决了上述的"git branch -D 问题。

然而。。。如果存储库有core.bare=1,但"git"命令正从它的一个辅助工作树运行,则is_bare_repository()返回false(这很好,因为有可用的工作树)。好的。

而且,当主工作树是裸的时,将其视为非裸的会导致问题:好的。

例如,无法从主工作树的头引用的辅助工作树中删除分支,即使主工作树是空的。好的。

为了避免这种情况,在设置is_bare时也要检查core.bare。如果是core.bare=1,相信它,否则使用is_bare_repository()。好的。

好啊。


git发行版附带了一个名为git-new-workdir的脚本。您可以使用它,如下所示:

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

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

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


我遇到这个问题,希望能找到一个我在这里找不到的解决方案。所以现在我找到了我需要的东西,我决定把它贴在这里给其他人。

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

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

  • 什么是"裸存储库"。它本质上是.git目录的内容,而不位于工作树中。

  • 您可以在命令行上使用git选项--git-dir=指定正在使用的repo的位置(.gitdir的位置)。

  • 您可以使用--work-tree=指定工作副本的位置。

  • 什么是"镜像回购"。

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

    因此,对于我的用例,我通过执行以下操作得到了我需要的:

    1
    2
    3
    4
    5
    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

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

    对于我的使用案例,在同一台计算机上同时存在多个签出,而不需要编辑它们,这是完美的工作。我不知道是否有任何方法可以在没有脚本(如其他答案中所述)的情况下为多个工作树创建多个头部,但我希望这对其他人有所帮助。


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

    我假设您希望有两个工作目录,而不是远程的两个克隆,因为您不希望将一些分支推送到远程。否则,你的遥控器的两个克隆就可以正常工作了——你只需要做一些推拉动作来保持这三个克隆的同步。