我在一个拥有分支策略的团队中工作,有时会重写某些分支的上游提交历史记录。
能够将我的历史记录中的最后N次提交移动到存储区,然后重置HEAD,然后将这些存储条件移回我的本地历史记录,同时保留提交消息,这将是非常有用的。
如何在本地分支和存储之间移动提交?有没有办法为N次提交自动执行此操作?例如创建可以调用git stashHistory 5; git popHistory 5
之类的别名。假设当前工作目录在执行此操作时没有任何更改。
答案 0 :(得分:6)
从技术上讲,你根本无法移动提交,而不是你的意思。此外,"藏匿"实际上只是一个引用名称(refs/stash
)。 git stash
脚本使用reflog隐藏该单个名称下的多个项目。当你运行git stash save
创建一个新的时,脚本会创建一个 new 提交(与目前为止的每个其他提交都不同),并将存储点指向它。 1 我认为有可能以这种方式实现你的目标,但这是尝试它的错误方法。
幸运的是,这无论如何都不是问题。你真正想要的只是一个普通的git rebase
! git rebase
命令只是自动执行一系列git cherry-pick
操作,并且在此过程中某处git reset
。
这是如何运作的。您的工作在您的分支B
上,当您的上游团队upstream/B
指向我在此处标记o
(旧)的提交链时,您就开始这样做了:
..- * - o - o - o <-- upstream/B
\
x - y <-- B
现在你的上游团队去了&#34;重写历史&#34;在upstream/B
中,将新的o
替换为新的n
s(可能是4 n
s,偶数)。旧的仍然存在,尤其是你的回购,因为你的B
包含了它们:
n - n - n - n <-- upstream/B
/
..- * - o - o - o <-- upstream/B@{1}, in reflog
\
x - y <-- B
我认为您要做的是将x
和y
(或您的示例中的5个较长的链)复制到新的,略有不同的提交x'
和{{ 1}},产生此图:
y'
要执行此操作,您只需告诉 x' - y' <-- B
/
n - n - n - n <-- upstream/B
/
..- * - o - o - o <-- upstream/B@{1}, in reflog
\
x - y <-- [reflog only]
在git rebase
之后(upstream/B@{1}
)将分支x
重新定位到B
之后立即开始提交新的upstream/B
:
$ git rebase --onto upstream/B 'upstream/B@{1}' B
这就是为什么我在图形图纸中留下了reflog标签upstream/B@{1}
:在git fetch
更新upstream/B
之后进行了其中一次重写,它就是让你的标签轻松找到x
。请注意,如果upstream/B
已多次更新,则reflog编号可能会超过1
。 (我还将...@{1}
放在上面的单引号中,以保护它免受贝壳的影响 - 有少数人喜欢吃牙箍。有可能你的机会没有,而且不需要它们。)
最新版本的git(2.x,虽然我认为它有1.9)有一个名为git merge-base
--fork-point
的新标记选项,用于帮助自动找出{{1}的位置即使reflog号不再仅仅o
,转换为x
转换。因此,如果你有一个足够新的git,你可以完全自动化整个事情。如果没有,您可以手动计数(如您所做),或手动浏览1
reflog以确保upstream/B
是正确的后缀。在所有情况下,您需要找到@{1}
- 至 - o
转换,因为您必须将参数传递给x
。
(您可能不需要git rebase
参数,但包含它是安全的。没有--onto
git选择&#34; on-to&#34;来自分支&#39; ; s所谓的&#34;上游&#34;。这是一个非常令人困惑的术语,特别是因为其中一个--onto
参数实际上是被称为 rebase
,这意味着不同的东西!上游参数是我们使用upstream
的地方;它标识了从rebase cherry-pick复制过程中排除的提交。)
1 事实上,至少两个新提交 - 两个用于普通upstream/B@{1}
,三个如果你添加git stash save
或-a
- 以及-u
引用点为合并提交的那个。它并不是人们通常认为的合并;脚本只是使用 - 可能会说&#34;滥用&#34; - 合并提交的多父提交方面能够存储这些提交并使用单个引用名来稍后查找所有这些提交。 / p>