在存储库的副本之间转移隐藏的更改

时间:2018-08-28 14:50:09

标签: git git-stash

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

我需要删除其中一份副本,我想将所有已隐藏的更改从要删除的副本转移到我将保留的副本中。我想保留所有隐藏状态的父母和日期以及索引状态的所有信息。

我一直在阅读文档,但找不到任何简单/直接的方法来做到这一点。可能吗?


更新1 :我想将更改保持为隐匿状态而不是提交的原因之一是,通过使用--index标志,我可以检索已暂存的两个更改在工作目录中提交和更改。如果创建提交,我还将销毁父,索引和工作副本彼此不同的隐匿状态的信息。我的隐匿状态通常对应于初步的测试工作,这些工作远非可编译,尚未准备好提交,目前我没有时间进行数十个整理工作。


更新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是父提交ID和索引的ID。

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


更新3 :澄清:存储库的两个副本都已处于隐匿状态。

3 个答案:

答案 0 :(得分:5)

更新-我确实想继续与其他人达成共识,导致这种情况的工作流程可能不是最佳工作流程。[1]但是每个人都忙于此事,没有多少人提供有关您如何从自己的位置到想要成为的位置的实际答案。所以:


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

不仅reflog被认为是本地数据结构(因此不存在共享它的内置行为),而且每个存储库大概都有一个冲突的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.

您还需要记录所有隐藏消息。他们在藏匿记录中。

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

(您可以将它们作为注释存储在标签上,但是IMO确实比其他任何东西都方便……)

现在,您需要将这些标签(及其历史记录)复制到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。)

现在是棘手的部分;您需要在stash上重建target reflog。

首先,您需要跟踪target的{​​{1}}引用日志中已经存在的所有条目。您可以使用标签来完成此操作,就像stash中的隐藏标签一样。

source

然后再次记下现有的隐藏条目消息

$ git tag stash-t0 stash
$ git tag stash-t1 stash@{1}
// etc.

然后,您可以删除存储参考。通常,我不会绕过git接口,但是在这种情况下,这并不是像有一种“安全”的方式来实现。

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

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

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

或者,在足够新版本的git

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

其中$ git stash store -m "<stash-message-1>" <tag-name-1> <stash-message-1>是您记录的隐藏消息,这些消息现在将成为堆栈中 last (最旧/最底部)的隐藏消息,以及标签您曾经分别保留了该隐藏状态。每个后续命令将是

<tag-name-1>

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

在隐藏列表中“向前移动”。

然后您可以取消使用的标签。

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

[1]在git中创建临时分支,然后根据需要使用交互式rebase清理历史记录是可行的,因为您最终准备将其迁移到“真实”分支中。无论如何,隐藏是和实际提交一样“重”的,因为隐藏是真正的提交。暂存器可以很好地将某些更改推迟一分钟,因此您可以将工作树用于其他真正快速的事情,但是寿命长的暂存器不是最好的选择。

答案 1 :(得分:2)

存储区并不是要那样移动的,您应该真正进行私有分支,在该分支中您可以执行多个提交而无需发布它们。但是,由于隐藏实际上是作为隐藏的提交实现的,因此,如果您愿意进行一些黑客操作,您可以 将其复制。

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

  1. 将文件.git/stash.git/logs/refs/stash从源存储库复制到目标。 警告!这将覆盖您的整个存储清单!。您可以尝试同时合并两个.git/logs/refs/stash文件以合并两个存储区(较旧的优先),但是显然您无法合并存储区的尖端。
  2. 在源存储库:git config uploadpack.allowAnySHA1InWant true中运行此命令。
  3. 在源存储库中运行此命令,然后将输出复制到文件/剪贴板/任何文件(我将其称为$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并获取所有引用的提交,则它将正常工作。而且由于除非它是公共引用的一部分(并且stash不是)的一部分,否则您不能通过哈希来获取提交,因此您需要放宽该条件,这就是为什么您需要uploadpack.allowAnySHA1InWant = true

注意:如果源存储库中没有.git/stash文件,则表示文件已打包。您可以使用git rev-parse stash获得该文件上的内容。

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

答案 2 :(得分:0)

将A的存储区转移到B,而B可能有其自己的存储区。

  1. 将A的所有存储卡都推到B。对于第N个存储卡,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. 将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附加的hash3中将hash2更改为.git/logs/refs/stash

  4. 在B中,删除所有标签,从stash0stashN。例如git tag -d stash0