【问题标题】:Transfer stashed changes between copies of a repository在存储库的副本之间传输隐藏的更改
【发布时间】:2019-02-03 06:09:01
【问题描述】:

我有两个存储库副本,每个副本都有几十个隐藏状态(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:澄清:存储库的两个副本都已隐藏状态。

【问题讨论】:

  • 您可能应该将存储转换为单独分支上的正确提交。存储不打算共享或转移。
  • 我知道存储并不容易/意味着共享或转移,但考虑到 git 工具的灵活性,我希望这样做是可能的。
  • 本地分支应该用于存储实验性的、未准备好的工作等。在它们完成并合并到您的开发或其他“官方”之前,您不必将这些本地分支推送到任何地方" 分支。
  • @phd:感谢您的参考,但据我所知,该问题并未讨论保留相应索引状态的可能性,并且似乎没有一个答案可以保留它们。跨度>

标签: git git-stash


【解决方案1】:

更新 - 我确实想继续并同意其他人的观点,即导致这种情况的工作流程可能不是最好的工作流程。[1]但是每个人都在忙着讨论这个问题,没有多少人提供实用的答案来说明你如何从现在的位置到达你想成为的位置。所以:


存储信息存储在提交集合中,外加一个带有大量操作的 reflog 的 ref。处理 reflog 将是您所要求的最困难的部分。

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

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

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

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

您还需要记录所有隐藏消息。它们在 stash reflog 中。

$ 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。)

现在是棘手的部分;您需要在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 接口,但在这种情况下,并不是有一种“安全”的方式来做到这一点。

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

其中&lt;stash-message-1&gt;&lt;tag-name-1&gt; 是您为现在将成为last(最旧/“底部”)存储在堆栈中的内容记录的存储消息,以及您用来保留的标记该存储状态,分别。每个后续命令将是

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

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

在存储列表中“向前推进”。

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

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

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

【讨论】:

  • 我会注意到,如果我自己这样做(我停止使用 stash 进行任何长时间运行,因此我不必这样做:-)),我不会费心重建 reflog在目标中,我只需保留额外的标签或其他参考(我可能会使用一组备用参考)。但实际上我可能会首先在原始仓库中使用git stash branch,将每个存储库变成一个真正的分支,之后一切都很容易。
  • @torek - 我当然同意过度使用存储(或使用单个存储超过很短的时间)不是首选做法的观点。至于上述程序的这些变化是否可以被 OP 接受,我不能说。似乎问题是关于如何以继续使用 stash 命令访问更改的能力结束,所以我朝着这个目标回答。
  • 还有一点值得一提:现代 Git 有 git stash store 来进行 reflog 操作,这至少使您不必直接使用 git update-ref
  • 这个答案比我的更能满足 OP 的需求。我将重申其他人所说的话。需要这样做表明可能存在工作流程问题。
  • @torek - 这是真的,因为它是一个特制的命令,所以用它代替update-ref 是有意义的。也就是说,重建 stash reflog 的“困难部分”是保持提交消息的正确性,不幸的是 git stash store 对此没有帮助(或阻碍)。
【解决方案2】:

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

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

  1. 将文件.git/stash.git/logs/refs/stash 从源存储库复制到目标。 警告!这将覆盖您的整个存储列表!。您可以尝试连接两个 .git/logs/refs/stash 文件以合并两个 stash(旧的优先),但显然您无法合并 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 并获取所有引用的提交,它将正常工作。而且由于您无法通过哈希获取提交,除非它是公共 ref 的一部分(而 stash 不是)您需要放宽该条件,这就是您需要 uploadpack.allowAnySHA1InWant = true

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

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

【讨论】:

  • 这是可行的如果目标仓库还没有存储;正如您所注意到的,如果确实如此,那么这将不会得到预期的结果。 (从这个问题来看,我的假设是确实如此。)
  • @MarkAdelsberger:没错,您可以尝试连接两个存储库中的.git/logs/refs/stash 文件。我认为它应该有效。
  • 我不建议以这种方式手动操作日志。
  • @MarkAdelsberger:完全同意。同样以 OP 的方式使用存储。但有时你必须做你必须做的事......
  • @MarkAdelsberger:或者他们可以用不同的名称复制旧的存储库stash2。在不编辑日志文件内容的情况下,它的工作方式或多或少是相同的。
【解决方案3】:

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

  1. 将 A 的所有存储推送到 B。对于第 N 个存储,git push &lt;path_to_B&gt; stash@{N}:refs/tags/stashN。运行一个循环将它们全部推送。

  2. 在 A 中,运行 git rev-parse refs/stash 并得到一个哈希值。在 B 中,运行 echo $hash &gt; .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 附加的.git/logs/refs/stash 中的hash3 更改为hash2

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-15
    • 2011-08-12
    相关资源
    最近更新 更多