git - 在重新定位之前的壁球

Pay*_*ian 9 git rebase git-rebase

我有两个分支:mastertest-branch(分支master).我的工作看起来如下:

  1. git checkout master
  2. git checkout -b test-branch
  3. 进行一系列更改并提交它们
  4. 进行更多更改并进行另一次提交
  5. git checkout master
  6. git pull - >其他人已经做出改变大师
  7. git checkout test-branch
  8. git rebase -i master
  9. pick将交互式控制台中的第一个更改为s
  10. 我必须解决两个合并冲突,一个用于第一个提交,另一个用于第二个提交

我想做的是在重新定位test-branch之前压缩所有提交,这样我只需要解决一次合并冲突.这可能吗?如果是这样,怎么样?

tor*_*rek 6

有可能,甚至很容易.Git就是这样,有很多不同的方法可以做到.

从字面上做你最初的建议:

...在变基之前压缩测试分支上的所有提交

如果你在分支上运行之前这样做是最简单的.(我知道你没有作为命令列出,但你确实在第6步中运行:git mergemastergit mergegit merge

  1. git pull - >其他人已经做出改变大师

因为git pull只是git fetch紧随其后git merge.)但是之后仍然很容易做到; 我们只需要定位正确的commit-ID.

让我们绘制您在步骤4中提交的提交图:

...<- o <- *            <-- master, origin/master
             \
               A <- B   <-- HEAD=test-branch
Run Code Online (Sandbox Code Playgroud)

该图显示的是有两个标签1指向我标记的提交*,即masterorigin/master.提交*点返回提交o指向更多提交:这是分支的历史master.

您创建的分支的标签(现在是,因此是HEAD=部分)指向提交B.B然后提交指向提交A,指向提交*.那是分支的历史test-branch:你在at时创建它*,然后添加A,然后添加B.

这里有两种方法可以轻松压缩提交A,B此时:

  1. git rebase -i master

    这为您提供了一个交互式编辑会话,您可以在其中"选择"第一个提交然后"压缩"第二个提交,它将收集两个提交消息并让您以通常的方式编辑结果.然后它进行(单个)新提交,其树是提交的B.

  2. git reset --soft master; git commit

    这并没有打开底垫的交互式编辑会话:它只是让临时区域和树的承诺B(这是--soft部分git reset --soft),移动标签test-branch回点提交*直接(这是git reset一部分git reset --soft),并且使像往常一样新的提交(git commit).

    缺点是您必须编写新的提交消息,但您可以从提交AB以任何方式恢复提交消息.例如,您可以使用-c-C标记git commit(您必须标识提交,A或者B,例如,使用@{1}@{1}^@{yesterday}或其他一些reflog说明符).或者,在执行此操作之前git reset --soft,您可以使用git log日志消息并将其保存在文件中,或者其他任何内容.

    (第二种方法闪耀时,而不是只有两次提交壁球,你有42左右.)

这两种方法都做了同样的事情:它们添加了一个新的提交(让我们称之为AB),离开AB后面以及有点灰色,我无法正确绘制:

              AB        <-- HEAD=test-branch
             /
...<- o <- *            <-- master, origin/master
             \
               A <- B   [ghost version of test-branch]
Run Code Online (Sandbox Code Playgroud)

您的分支的幽灵版本对于大多数正常使用是不可见的,并且最终(默认情况下在30天左右之后)被垃圾收集掉.(在那之前它仍然在你的存储库中,并在你的reflog中,这样你就可以找到原始提交A,B以防万一你需要它们.)

如果你已经完成了第6步怎么办?在这种情况下,您仍必须识别提交*.你可以我写这篇文章时那样按照jsexpert建议的方式来做,或者你可以通过以下方式找到它git merge-base:

$ mergebase=$(git merge-base HEAD master)
# then pick just ONE of the next two
$ git rebase -i $mergebase
$ git reset --soft $mergebase; git commit
Run Code Online (Sandbox Code Playgroud)

这是如何工作的.之后git checkout master; git fetch; git merge; git checkout test-branch(步骤5和6,或多或少),您的提交图现在看起来更像是:

...<- o <- * <- o       <-- master, origin/master
             \
               A <- B   <-- HEAD=test-branch
Run Code Online (Sandbox Code Playgroud)

那个并且指向它的新o提交- 或者它可能是整个提交链 - 是"在路上",但是(现在你在哪里)的"合并基础" 并且是提交:在两个之前最近的共享提交分支发散.masterorigin/mastertest-branchmaster*

然后我们简单地定位rebasereset --soft在那个提交.当我们完成并有一个新的AB提交时,它看起来像这样:

              AB        <-- HEAD=test-branch
             /
...<- o <- * <- o       <-- master, origin/master
             \
               A <- B   [ghost version of test-branch]
Run Code Online (Sandbox Code Playgroud)

一旦你的压扁AB提交,然后你可以git rebase说在master通常的方式.

请注意,另一个答案是做同样的事情,它只是*通过计算提交来识别提交.如果您知道提示test-branch和"有趣"提交之间有两个提交*,则HEAD~2标识相同的提交$(git merge-base HEAD master).使用git merge-base只是让你避免计数.


1 "引用"是实际的一般git术语.在这种情况下,它们是分支名称,我使用"label"一词将它们与此提交图形成的分支历史区分开来.git中的"branch"一词用来指代至少这两个不同的东西,它会让人感到困惑.


Cod*_*ard 5

当然可以,youiu 只需要输入:git rebase -i HEAD~<# of commits to squash>

-i 用于交互式变基。执行此操作后,您将看到 vi,其中包含每次提交下一步要做什么的说明。

关于它的非常详细的帖子可以在这里找到:重写历史