【问题标题】:Git - fast-forward by 1 or N commitsGit - 快进 1 或 N 次提交
【发布时间】:2021-03-08 08:05:14
【问题描述】:

我返回了一些提交:

> 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。我想从某个已知点开始,逐步向原点提交。

为什么不this thread ("git fast-forward one commit")

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

【问题讨论】:

    标签: git


    【解决方案1】:

    如果HEADorigin/some-branch 落后N 提交并且可以快进,如果HEADorigin/some-branch 之间的提交是线性,您可以使用git merge origin/some-branch~(N-1) 来“git 快进一次提交”。例如,git merge origin/some-branch~49 会产生 behind origin/some-branch by 49 commits。您也可以使用git reset origin/some-branch~49 --hard 来达到目标​​。

    但在许多情况下,提交历史不是线性的。您仍然必须手动选择要合并或重置的提交。 origin/some-branch~49 可能是有效的提交,但不是您想要的。在这些情况下,git reset --hard somecommit 更好,因为git merge 可能会引入新的合并提交。

    线性情况,dev 落后于master 4 次提交:

    * 8a79cfc master
    * d21f40f 
    * f11b7fc 
    * 93a4590 
    o 864a80d HEAD -> dev
    

    要 git 快进一个提交到 93a4590git merge master~3git reset master~3 --hard

    并行情况,dev 落后于master 6 个提交:

    *   9dd6d4e master
    |\  
    | * 5363dc7 
    * | 8a79cfc 
    |/  
    * d21f40f 
    * f11b7fc 
    * 93a4590 
    o 864a80d HEAD -> dev
    

    master~5864a80d 现在是 dev,所以 git merge master~5 不会“git fast-forward 1 commit”。

    【讨论】:

    • 为避免使用git merge 意外创建合并提交,可以使用--ff-only 选项。
    • 谢谢,那会工作的。在大多数情况下,历史将是线性的,如果不是,我想我可以手动处理其中的某些部分(通过使用提交哈希),然后在不再有歧义时返回数字。
    【解决方案2】:

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

    我喜欢水平而不是垂直地绘制我的提交图。 Git 将它们垂直绘制,较新的提交朝向顶部,导致 ElpieKay 显示的那种git log --decorate --oneline --graph 输出。我用较新的提交来绘制它们,以强调单个分支中分支和合并的并行性质:

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

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

    但是哪个提交比H 更进一步?是提交I,还是提交K?或者是,both 提交,I K

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

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

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

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

    如果您在提交 G 时处于分离的 HEAD 上,则下一个提交转发是提交 H,但在那之后,下一次提交转发需要 indirection of 子句:是我们试图向branch1 移动,即提交J?或者我们是否正在尝试向branch2 移动,即提交L

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

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

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

    • HEAD 的后代(不包括HEAD 本身),以及
    • branch1 的祖先(包括由名称 branch1 标识的提交)

    这个约束——HEAD 的后代和branch1 的祖先——正是--ancestry-path 的含义。

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

    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 添加到此是很容易的,但可惜,这不起作用。您可以使用--reversehead -n 1 而不是使用tail -n 1,但我不喜欢这可能导致的潜在断管错误。

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

    【讨论】:

    • 感谢您详细说明图形形状。在大多数情况下,我可以使用 ~ 运算符,但更好地理解图表也很有帮助。
    猜你喜欢
    • 2017-12-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-27
    相关资源
    最近更新 更多