【问题标题】:Single working branch with Git使用 Git 的单个工作分支
【发布时间】:2011-11-12 00:02:34
【问题描述】:

赏金简短说明

是否有一种可移植方式来使用具有多个签出功能的单个存储库?作为拥有的替代方案 多个克隆,其中开销过多(推/拉/同步...)和覆盖.git/objects 硬链接的风险(甚至在 Windows 上都不起作用)。


刚接触 git,很想听听有经验的 git 用户的想法。

git 一次只能使用一个分支是否有概念上的原因?当大多数时候我需要同时处理至少两个不同的分支时,在分支之间来回切换似乎是绝对不切实际的,例如构建,并行运行。

好的,所以也许开发人员并不需要同时在两个分支上工作。但是签出另一个分支不会自动携带被忽略的东西,比如构建输出文件。所以需要重新进行重建。

有这个脚本git-new-workdir 应该允许多个工作分支,但一方面,它不是 git 版本的一部分,尽管它已经存在了大约 3 年,所以我不相信它会保留我的文件一致。其次,我找不到它作为 Windows 发行版的一部分,这是我用于开发的机器之一。

所以唯一的官方选择是为每个分支创建一个新的“克隆”,这似乎是不正确的,因为每个克隆都是一个成熟的存储库。我什至不知道如何调用克隆目录——我是使用存储库名称还是分支名称,还是两者都使用?如果我从那个分支创建另一个分支怎么办,a.s.o.

实际用例更新 (@Philip 的建议)

我通常在两个主要/次要版本上工作,其中的功能同时进行。还有一个偶尔的开发分支,在合并到将来的某个版本之前,会进入实验性功能。

在功能开发过程中,行为通常会退化,将其与更改前的行为进行比较会更有效。签出以前的分支/修订版还不够好,因为很多时候它归结为并排调试,这意味着需要同时在硬盘上签出两个修订版。

因此,更方便和自然的方法是在自己的目录中保留一些所谓的“活动”分支,每个分支都有自己的已编译二进制文件。这也将节省编译时间和偶尔的本地设置(即需要在每次结帐后更改配置文件以使产品运行)。

【问题讨论】:

  • 为什么您认为工作树中需要两个分支?你想做什么?
  • 我想比较同一产品在不同版本之间的行为,我想避免在返回另一个分支时从头开始重建所有内容。我必须同时处理三个独立的功能。
  • @Nick:你能描述一下你的情况吗?例如您是否拥有三台机器并且并排运行三个变体;或者也许一台机器运行三个线程,两个在后台,你的开发在前台?对于前一种情况,只需使用独立的克隆,对于后一种情况,有基于工作流的选择。
  • @Nick:那么在并行调试期间您的“签入”策略是什么?它只是在发现故障时临时保存,还是您使用正确的提交消息“同时”更新两个版本? IE。故障定位后,所有[除了一个]都将被丢弃。
  • 看起来 Bazaar 通过shared repositories 原生支持此功能,感兴趣的人可以参考。 More info.

标签: git


【解决方案1】:

签出时的分支只是指向提交 (within a graph of commit) 的指针。
因此,Git 无法同时检查该图的两个提交

其实见“Multiple working directories with Git?”。
使用 Git 2.5+(2015 年第二季度),您可以为一个 git 存储库拥有多个工作树git checkout --to=<path>

这允许您在<path> 的单独工作目录中签出一个分支。一个新的工作目录链接到当前存储库。

该链接是“可移植的”(即即使在 Windows 上也可以使用),因为它记录在主 repo $GIT_DIR/worktrees 目录中。


原始答案(2011 年):

(来源:Scott Chason,ProGIT Book,http://progit.org/book/ch3-1.html,CC-BY-NC-SA)

如果您需要同时处理两个不同的分支,只需克隆您的 repo 并选择 (git checkout) 该克隆中的另一个分支。您可以使用分支的名称作为该克隆的根目录的名称。
您可以在任何这些存储库中创建分支。


所以问题是为什么 Git 不能有两个指针,每个目录一个? ——

正如“Popularity of Git/Mercurial/Bazaar vs. which to recommend”中提到的,Git 的核心是内容管理。每个提交都代表一个完整的存储库快照。
您无法查看同一个容器(工作目录)中的两个内容,即使您可以保留对任意数量的指针(分支)的引用。

您只用一个内容填充工作目录。

【讨论】:

  • 那么问题是为什么 Git 不能有两个指针,每个目录一个?
  • @Nick 在分支之间切换几乎没有成本时,同时拥有两个分支作为工作副本的好处在哪里?您是否同时在两个分支机构工作?或者只是想 peek - 如果是这样,请使用 gitweb。
  • @VonC:你有什么想法解决这个problem关于将提交推送到不同分支的问题吗?
  • @Nick:当然你可以在每个存储库中拥有任意数量的分支,只是你只能拥有一个 current 分支。当您使用 git 时,大多数工具处理您的工作副本(即您在键入 ls 时看到的内容)、索引(即暂存区域或构成下一次提交的内容)之间的关系和当前提交(即HEAD,通常是分支的尖端)。如果最后一个状态不是唯一的,那么 git 将是一场噩梦。
  • @Nick:当您执行git checkout new-branch 时,时间戳只会针对已更改的文件更新,因此如果您使用考虑mtimes 的构建工具(例如make),仅应该重建最小的文件集。
【解决方案2】:

实际上,git-new-workdir 正是针对这个问题而设计的。我有同样的问题,使用它没有任何问题。关于您的预订:

有这个脚本 git-new-workdir 应该允许多个工作分支,但一方面,它不是 git 版本的一部分,尽管它已经存在了大约 3 年,所以我不相信它保持我的文件一致。

嗯,我(和许多其他人)已经使用它多年了,我自己没有遇到任何错误,也没有听说过任何真正的问题(除了一些小的限制,见下文)。这听起来可能不多,但即使使用 git 本身(或任何 -free- SW 项目,就此而言),你得到的唯一真正保证是“还没有人发现问题”。我不知道为什么git-new-workdir 仍在贡献中,但我认为这不会阻止你。

其次,我找不到它作为 Windows 发行版的一部分,这是我用于开发的机器之一。

这可能只是因为您的发行版选择不包含 contrib/ 内容。

如果你在 Cygwin 下使用 git,你可以直接下载并使用原始的 git-new-workdir 脚本。很好用,我自己用。如果您使用 msysGit,则有可用的端口: https://github.com/joero74/git-new-workdir/blob/master/git-new-workdir.cmdhttps://github.com/dansmith65/git/blob/master/contrib/workdir/git-new-workdir-win 。不过,我不知道它们的质量。

git-new-workdir的限制

为了完整起见,以下是我所知道的限制:

【讨论】:

    【解决方案3】:

    如果你想这样做,

    我 100% 建议编写一个自定义应用程序1,以一种万无一失的方式管理此过程。

    起点可以是

    GIT_WORK_TREE=/worktrees/stable   git checkout -f origin/stable
    GIT_WORK_TREE=/worktrees/unstable git checkout -f origin/unstable
    

    等等

    我会特别确保您的所有工作副本(远程)都不像一个普通存储库,因此普通的 git 命令不适用。

    使用通常的 git(子)命令是灾难的根源,因为有一天您会遇到一个脚本,它不按应有的方式(或您预期的方式)使用 GIT_DIR、GIT_WORK_TREE、GIT_INDEX。


    1(可能基于 NGit,或用于 Python、Ruby 的类似 Git 绑定——水在您的世界中是可移植的)

    【讨论】:

      【解决方案4】:

      如果我理解正确,您正在寻找能够CopyOut 特定分支头或在分支上提交到独立目录的能力,以进行调查和比较,同时仍然检查您当前的开发/错误修复分支作为您的主要 git '参考'。

      您希望独立“复制”的目录不太可能直接重新提交到 git。

      一种未经测试的方法是使用基本的git 命令及其[--git-dir=<path>] [--work-tree=<path>] 选项,并通过仔细的脚本调整/更正您的HEAD/索引,就像您执行CopyOut 一样,然后回到您的当前分支。

      通常的警告适用..

      1. 创建新工作目录
      2. 启动指向 work_dir 的 git
      3. 保存当前 HEAD
      4. 结帐所需的分支/提交(它应该进入 work_dir)
      5. 将 HEAD 设置回保存的值(这是一个 hack,如果不采取措施保持索引正确,可能无法正常工作!)
      6. 关闭 git 以“忘记”work_dir 设置
      7. 在旧目录及其关联分支中重新启动 git

      这肯定需要仔细测试,并且可能需要围绕第 3 步和第 5 步进行调整,以使第 6-7 步正确。

      如果有需要提交到相应分支的错误修复,则 git work-tree 选项可以提供一种从复制目录引入更改的机制。

      更新 --------------------------------
      clunx 说明基于 Windows 用户的观点。

      # this is run from your main work dir (that contains .git)
      Copy .git/HEAD MyBackup
      # HEAD will contain the line similar to "ref: refs/heads/master"
      mkdir <path>
      # the path should be 'somewhere else', not in your existing directory
      # If the path doesn't exist the following checkout will fail
      Git --work-tree=<path> checkout <branch>
      # this will extract the latest commit on that branch to the work tree path
      # or use a commit sha1 instead of <branch>, if you want an exact commit
      # Note: your index will be updated to reflect the checked out files, as will HEAD.
      Copy MyBackup .git/HEAD
      # set the HEAD back to where it was
      Git reset
      # reset the index appropriate for the previous Head.
      # note we dropped the --work-tree parameter
      

      通过在正确的时间将--work-tree 指向正确的位置并确保索引设置正确,可以使用类似的技术从提取的路径中获取任何修订。不要忘记通常的注意事项 [备份、备份和备份]。

      这在我拥有的测试回购中对我有用。一些操作是在 Windows 中进行的。

      【讨论】:

      • 这听起来很有趣——但是,我不确定其中的一些步骤是什么意思。当您说“保存当前 HEAD”时,您的意思是保存“.git/HEAD”文件?此外,当您说“关闭 git”时,您可能指的是 GUI 工具。如果您可以在类似实用程序的命令方面更详细地说明这一点,那就太好了。
      • @Nick:我已经用我测试过的命令序列更新了我的答案。
      • 谢谢——以为你忘记了。正在玩它 - 看起来很笨重,可能需要一些调整,会回来的。
      • 我离开了,但没有忘记。我认为,如果您已经有一个固定的可用目的地和一个存储 HEAD 文件的地方,事情会容易得多。我没有尝试为它创建别名。有人反对它,但我不知道为什么。
      【解决方案5】:

      您想要的是alternates,它允许一个repo 依赖于另一个repo 的对象。它的使用不是很常见,因为磁盘空间很便宜,而且您必须小心不要意外删除依赖 repo 需要的对象。

      【讨论】:

      • 是的,听起来很像。这意味着最接近多个工作目录的方法是将.git/objects 目录重用于多个存储库。看不到删除所需对象的任何风险。我想 .git/objects 目录包含整个历史,无论删除/添加什么,它都只会增长。或者你指的是删除分支?
      • 刚刚找到git clone -s 的引用,它使用替代而不是硬链接到本地​​存储库。它谈到了删除分支在删除分支和未引用提交方面的危险(虽然不确定这是怎么可能的)。
      • 如果你从不删除未合并的分支,你不会有问题。如果您删除父 repo 中包含仍在克隆中使用的提交的未合并分支,则会发生此问题。
      • Alternates 几乎与常规 git 克隆相同,唯一的区别是您共享一些磁盘空间。 OP 明确拒绝使用“开销太大(推/拉/同步...)”的克隆,所以这可能没有太大帮助。
      【解决方案6】:

      也许submodules 可以帮助您拥有独立的目录?

      【讨论】:

      • 有趣的方法——在某种程度上可能会有所帮助,假设将分支安排到子模块中。
      【解决方案7】:

      如果您使用本地路径 git clone,则 .git/objects 下的所有内容(即存储库中的大部分提交和数据)都尽可能硬链接到旧存储库,并且几乎不占用磁盘空间。所以如果你想要两个不同的工作目录,在本地克隆你的 repo 并不是很昂贵。尝试从一个存储库管理两个不同的工作目录可能会奏效,但这并不值得。

      基本上,运行git clone /path/to/old/repo new_repo,一切都会正常运行。

      【讨论】:

      • 我听说过克隆硬链接——但我似乎无法让它们在 Windows (msysgit) 上运行,这很可能是由于 Cygwin 对硬/软链接的限制。
      • 好吧,我知道硬链接在 cygwin 中是可能的,你可能是对的,msysgit 不使用它们。不过,硬盘空间很便宜。有两个回购副本特别不受欢迎吗?
      • 当然不是磁盘空间的问题。必须克隆整个存储库(具有所有不同的分支和修订版)只是为了能够在两个分支上并行工作,这似乎违反直觉。
      • Windows 硬链接仅与 ~Vista、~W7 一起出现——mklink 命令。我不认为 msysgit 区分 XP 和更高版本....
      • @Nick,通常 git 存储库不包含分支 - 因为克隆的存储库 分支。它与 SVN 不同,其中分支、标签等都在同一个存储库中。
      猜你喜欢
      • 2018-06-22
      • 1970-01-01
      • 2012-07-29
      • 2018-08-25
      • 2016-10-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多