在存储库的副本之间传输隐藏的更改

Transfer stashed changes between copies of a repository

我有两个存储库副本,每个副本都有几十个隐藏状态(stash@{0}stash@{1}、...)。

我需要删除其中一个副本,并且我想将所有隐藏的更改从我正在删除的副本转移到我将保留的副本。我想保留有关 parent 的所有信息和所有隐藏状态的日期,以及索引的状态。

我一直在阅读文档,但找不到任何 simple/direct 方法来执行此操作。有可能吗?


更新 1:我希望将更改保留为隐藏状态而不是提交的原因之一是通过使用 --index 标志我可以检索暂存的提交更改和工作目录中的更改。如果我创建一个提交,我也会破坏隐藏状态的信息,其中 parent、索引和工作副本都彼此不同。我的隐藏状态通常对应于初步测试工作,这些工作离可编译性还很远,还没有准备好提交,目前我没有时间去整理它们。


更新2:我想我知道如何查看我要备份的信息了,例如

$ git show stash{5}
commit eb5731e828f467dbe9214d0e6a350f33898c1363
Merge: c9608582 1d6cb78d
Author: Author <author@example.com>
Date:   Wed Sep 20 18:54:51 2017 +0100

清楚地产生工作目录状态的id(commit行)和日期,Merge:行中的id是parent提交id和id指数。

我不知道如何将所有这些信息传输到存储库的第二个副本,作为它的新隐藏状态。


更新 3:澄清:存储库的两个副本都已经有隐藏状态。

Stashes 不应该那样移动,你真的应该做私人分支,你可以在不发布的情况下进行多次提交。但是由于隐藏实际上是作为隐藏提交实现的,如果您愿意进行一些黑客攻击,您可以可以复制它们。

这些说明对我有用,但它们可能取决于当前的 git 版本或其他细节,YMMV:

  1. 将文件 .git/stash.git/logs/refs/stash 从源存储库复制到目标。 警告!这将覆盖您的整个隐藏列表!。您可以尝试连接两个 .git/logs/refs/stash 文件以合并两个存储(先是旧的),但显然您不能合并存储的尖端。
  2. 运行 源存储库中的此命令:git config uploadpack.allowAnySHA1InWant true.
  3. 运行 源代码库中的这个命令并将输出复制到 file/clipboard/whatever(我将其称为 $REFS):git stash list --format=%H.
  4. 在目标存储库 运行 中:git fetch SOURCE $REFS$REFS 步骤 3 的输出)。
  5. 使用 git stash list 检查目标存储库中的一切是否正常。
  6. 运行 git gc 并检查任何 warning: reflog of 'refs/stash' references pruned commits 可能显示缺失数据的消息。

诀窍在于 git 中的存储实际上是作为名为 stash 的隐藏引用的引用日志实现的。因此,如果您复制 ref、reflog 并获取所有引用的提交,它就会正常工作。并且由于您无法通过哈希获取提交,除非它是 public ref 的一部分(而 stash 不是),因此您需要放宽该条件,这就是为什么您需要 uploadpack.allowAnySHA1InWant = true

注意:如果源代码库中没有 .git/stash 文件,则表示它已打包。您可以使用 git rev-parse stash.

获取该文件中应包含的内容

注意 2:如果您在目标目录中有一个存储,并且您认为手动编辑 reflogs 不是一个好主意(不是!),您可以使用不同的名称复制原始存储文件,比如stash2。然后,您可以使用 git reflog stash2 查看此替代存储并使用 git apply stash2@{N}.

应用它们

更新 - 我确实想继续并同意其他人的观点,导致这种情况的工作流程可能不是最好的工作流程。[1]但每个人都忙于喋喋不休,没有多少人提供关于你如何从你现在的位置到达你想去的地方的实际答案。所以:


隐藏信息存储在提交集合中,加上一个带有大量操作的引用日志的引用。处理 reflog 将是您所要求的最难的部分。

不仅 reflog 被认为是本地数据结构(因此不存在共享它的内置行为),而且每个 repo 可能都有一个冲突的 reflog 代表本地的隐藏状态堆栈,以及如何将它们组合起来并不简单。

一种方法可能看起来像这样。我会调用您要删除的存储库 source,以及您要保留的存储库 target

首先,在 source 中的每个存储状态创建易于共享的引用。

$ cd /path/to/source
$ git tag stash-s0 stash
$ git tag stash-s1 stash@{1} 
$ git tag stash-s2 stash@{2}
// etc.

您还需要记下所有隐藏消息。他们在隐藏的 reflog 中。

$ git reflog stash
1111111 stash@{0}: Custom Stash Message Here
2222222 stash@{1}: WIP on master: 1234567 2

(您可以将它们存储为标签上的注释,但在我看来,这并不比其他任何东西更方便...)

现在您需要将这些标签(及其历史记录)复制到 target;这将确保所有隐藏的数据都存在

$ cd /path/to/target
$ git fetch --tags file://localhost/path/to/source

(file://localhost/path/to/sourcesource 的一种可能 URL,假设它可以从 target 在本地访问;您可以使用任何 git URL,或者如果 source 已经是 target 的已配置遥控器,您可以使用遥控器名称而不是 url。)

棘手的部分来了;您需要在 target.

上重建 stash reflog

首先,您需要跟踪 targets stash reflog 中已有的任何条目。您可以使用标签来做到这一点,就像 source.

中的隐藏一样
$ git tag stash-t0 stash
$ git tag stash-t1 stash@{1}
// etc.

并且,再次记下现有的隐藏条目消息

$ git reflog stash
3333333 stash@{0}: WIP on master: 7654321 2

然后您可以删除存储引用。通常我不会绕过 git 接口,但在这种情况下,没有 "safe" 方法可以做到这一点。

$ rm .git/refs/stash
$ rm .git/logs/refs/stash

最后您可以构建新的存储堆栈。您的第一个命令将是

$ git update-ref --create-reflog -m "<stash-message-1>" refs/stash <tag-name-1>

或者,在足够新的 git

版本上
$ git stash store -m "<stash-message-1>" <tag-name-1>

其中 <stash-message-1><tag-name-1> 是您为现在 last(最旧/"bottom")存储记录的存储消息在堆栈上,以及您用来保存该隐藏状态的标签。每个后续命令将是

$ git update-ref -m "<stash-message-n>" refs/stash <tag-name-n>

$ git stash store -m "<stash-message-n>" <tag-name-n>

在藏匿列表中"forward through time"。

然后你就可以去掉你用过的标签了。

$ git tag -d stash-s1
// ...

[1] 在 git 中创建临时分支然后根据需要使用交互式变基来清理历史记录是可行的,因为您最终准备好将其迁移到 "real" 分支。无论如何,存储与真正的提交一样 "heavy-weight",因为存储 真正的提交。存储非常适合将一些更改暂时搁置一分钟,这样您就可以真正快速地将工作树用于其他用途,但长期存储并不是最好的选择。

将 A 的藏品转移给 B,B 可能有自己的藏品。

  1. 将A的所有stashes推送给B。对于第N个stashes,git push <path_to_B> stash@{N}:refs/tags/stashN。 运行 将它们全部推送的循环。

  2. 在 A 中,运行 git rev-parse refs/stash 你会得到一个散列。在 B 中,运行echo $hash > .git/refs/stash。将 $hash 替换为您在 A.

  3. 中获得的真实哈希
  4. 将 A 的 .git/logs/refs/stash 的内容追加到 B 的 .git/logs/refs/stash 中。 B原.git/logs/refs/stash的最后一行是hash1 hash2 name email timestamp message。 A的第一行是hash3 hash4 name email timestamp message。将B的附加.git/logs/refs/stash.

  5. 中的hash3改为hash2
  6. B中,删除stash0stashN的所有标签。例如git tag -d stash0