首页 > 解决方案 > Git - 快进 1 或 N 次提交

问题描述

我返回了一些提交:

> git reset --hard e7aec0fea8ed66e17c277298eebecae58ff044aa
> git status

On branch some-branch
Your branch is behind 'origin/some-branch' by 51 commits, and can be fast-forwarded.

我想合并一个较新的提交,但不是最新的。概念上是这样的:

> git ???
> git status

On branch some-branch
Your branch is behind 'origin/some-branch' by 50 commits, and can be fast-forwarded.

理想情况下,它将允许选择步数:

Your branch is behind 'origin/some-branch' by 50 commits, and can be fast-forwarded.

> git ??? --steps 2
> git status

On branch some-branch
Your branch is behind 'origin/some-branch' by 48 commits, and can be fast-forwarded.

这在git中可能吗?

为什么不平分呢?

可能 看起来 我 想要git bisect, 但 很难 决定 什么 时候git bisect good什么 时候git bisect bad. 我想从某个已知点开始,并逐步向原点提交进行。

为什么不是这个线程(“git fast-forward one commit”)

该线程的解决方案允许 ff 到指定的提交而不是主提交,但您必须手动指定提交哈希。我正在寻找一种不需要哈希的方法。

标签: git

解决方案


我已经更新了您链接到的问题的答案,请注意git mff( git merge --ff-only)不需要哈希 ID。但是您的特定目标——移动到没有特定名称的给定提交——确实需要哈希 ID相对表达式。正如ElpieKay 所指出的,任何相关表达式都必须考虑提交图的“形状”。

我喜欢水平而不是垂直地绘制我的提交图。git log --decorate --oneline --graphGit 将它们垂直绘制,将更新的提交放在顶部,从而产生ElpieKay 显示的那种输出。我用较新的提交来绘制它们,以强调单个分支内分支和合并的并行性质:

          I--J
         /    \
...--G--H      M--N   <-- branch
         \    /
          K--L

假设您已提交G签出,作为分离的HEAD. 您可能希望“前进”一次提交。这很简单:一个更接近于 commit 的提交N,在 branch 的顶端branch,是 commit H。因此,如果您向前推进一个提交,那就是您结束的地方。

但是哪个提交是向前迈出的一步H?是 commitI还是 commit K?或者它可能是两个提交I K

(剧透:两者兼而有之。)

这意味着您实现 1 或 N 次提交的目标可能是不可能的。如果这里有一个分支和合并的泡沫,你必须选择一个“边”去旅行。当然,您也可以返回并尝试另一边,但您需要知道您已经遇到了这种情况。

如果你没有这个问题,你仍然可以有一个相关的问题。假设我们有这个图:

          I--J   <-- branch1
         /
...--G--H
         \
          K--L   <-- branch2

如果您在 commit 时处于分离的 HEAD 上G,则下一个 commit forward 是 commit H,但在那之后,下一个 commit forward 需要一个in the direction of子句:我们是否正试图走向branch1,即 commit J?或者我们是否正试图走向branch2,即承诺L

在这种特殊情况下,git rev-list --ancestry-path可以来拯救我们。我们可以将向某个方向推进一个提交的想法自动化,因为:

git rev-list --topo-order --ancestry-path HEAD..branch1

将按照 Git 的自然(向后)顺序列出当前提交(HEAD)“之间”的提交,branch1它们是:

  • HEAD(不包括HEAD其自身)的后代,以及
  • 的祖先branch1(包括由 name 标识的提交branch1

这种约束——的后代HEAD和祖先branch1——正是--ancestry-path 意味着什么。

在我们的示例中,意味着我们将列出 commits H-I-J,但顺序相反。所以列出的最后一个提交哈希 ID 是我们想要的。1 因此,我们可以创建一个别名,允许我们移动分离的 HEAD当前分支名称,无论我们在哪个位置,朝着某个给定分支名称的方向前进一步,其中:

target=$(git rev-list --topo-order --ancestry-path $endpoint | tail -n 1)
if [ "$target" = "" ]; then
    echo "we have reached the endpoint $endpoint"
else
    git merge --ff-only $target
fi

您可以将其包装成一个小脚本,让您一次推进一个提交。使其前进n步(n > 1)所需的工作留作练习。


1在此添加--reverse和很-n 1容易,但是很可惜,这不起作用。您可以使用--reverseandhead -n 1而不是 using tail -n 1,但我不喜欢这可能导致的潜在断管错误。

--topo-order选项主要是偏执狂,可能不是必需的,但我还没有向自己证明这一点,所以我把它留在了这里的答案中,因为它有点神奇。


推荐阅读