Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

如何优雅地合并多个 Commit #13

Open
JasinYip opened this issue Jan 5, 2016 · 25 comments
Open

如何优雅地合并多个 Commit #13

JasinYip opened this issue Jan 5, 2016 · 25 comments

Comments

@JasinYip
Copy link

JasinYip commented Jan 5, 2016

大家都知道 Git 是一种分布式的版本控制工具。正因为它有「分布式」这个特性,所以理论上我们其实可以不需要连接网络,就可以进行版本管理。然而,在实际上这是不可能的,因为你还需要上网查 Git 的 中种命令(逃


哈哈,玩笑开完,进入正题吧!

我为什么要写这篇文章呢?因为实在太多同学跑来问我「到底怎么合并 commit?」了,每次都重复讲一遍这种做法完全不符合程序猿的风格啊!

那么,就先让我们来看这么一个情况,我们执行以下命令获得四个 Commit:

mkdir test
cd test

git init

echo "0" >> a
git add a
git commit -m "Commit-0"

echo "1" >> a
git add a
git commit -m "Commit-1"

echo "2" >> a
git add a
git commit -m "Commit-2"

echo "3" >> a
git add a
git commit -m "Commit-3"

我们可以看到 Git 的历史长成这样:

* b1b8189 - (HEAD -> master) Commit-3
* 5756e15 - Commit-2
* e7ba81d - Commit-1
* 5d39ff2 - Commit-0

那么问题来了,如何把 e7ba81d(Commit-1)5756e15(Commit-2)b1b8189(Commit-3) 合并到一起,并且只保留 e7ba81d(Commit-1) 的 Git message Commit-1 呢?

这个时候我们就要祭出我们这篇文章的主角—— git rebase -i 了!
这里我不想直接搬出写文档的那套,把所有的选项都介绍完,我们就把这次要用到的讲一下。

-i 实际上就是 --interactive 的简写,在使用 git rebase -i 时,我们要在后面再添加一个参数,这个参数应该是 最新的一个想保留的 Commit。这句话读起来有点坳口,所以这个情况下通常需要举个例子。就我们前面提到的那个例子中,这个「最新的一个想保留的 Commit」就是 5d39ff2(Commit-0),于是我们的命令看起来就长这样:

git rebase -i 5d39ff2

当然,我们也可以通过 HEAD~3 来指定该 Commit:

git rebase -i HEAD~3

按下回车后,我们会进入到这么一个界面:

pick e7ba81d Commit-1
pick 5756e15 Commit-2
pick b1b8189 Commit-3

# Rebase 5d39ff2..b1b8189 onto 5d39ff2 (3 command(s))
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out

前面三行是我们需要操作的三个 Commit,每行最前面的是对该 Commit 操作的 Command。关于每个 Command 具体做什么,下面的注释写得非常清楚。为了完成我们的需求,我们可以关注到这两个命令:

s, squash = use commit, but meld into previous commit
f, fixup = like "squash", but discard this commit's log message

为了让大家看得更明白,我不厌其烦地翻译一下:

  • squash:使用该 Commit,但会被合并到前一个 Commit 当中
  • fixup:就像 squash 那样,但会抛弃这个 Commit 的 Commit message

看样子两个命令都可以完成我们的需求,那么让我们先试一下 squash!由于我们是想把三个 Commit 都合并在一起,并且使 Commit Message 写成 Commit-1,所以我们需要把 5756e15(Commit-2)b1b8189(Commit-3) 前面的 pick 都改为squash,于是它看起来像这样:

pick e7ba81d Commit-1
squash 5756e15 Commit-2
squash b1b8189 Commit-3

当然,因为我很懒,所以通常我会使用它的缩写:

pick e7ba81d Commit-1
s 5756e15 Commit-2
s b1b8189 Commit-3

完成后,使用 :wq 保存并退出。这个时候,我们进入到了下一个界面:

# This is a combination of 3 commits.
# The first commit's message is:
Commit-1

# This is the 2nd commit message:

Commit-2

# This is the 3rd commit message:

Commit-3

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date:      Tue Jan 5 23:27:22 2016 +0800
#
# rebase in progress; onto 5d39ff2
# You are currently editing a commit while rebasing branch 'master' on '5d39ff2'.
#
# Changes to be committed:
#   modified:   a

通过下面的注释,我们可以知道,这里其实就是一个编写 Commit Message 的界面,带 # 的行会被忽略掉,其余的行就会作为我们的新 Commit Message。为了完成我们的需求,我们修改成这样:

Commit-1

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date:      Tue Jan 5 23:27:22 2016 +0800
#
# rebase in progress; onto 5d39ff2
# You are currently editing a commit while rebasing branch 'master' on '5d39ff2'.
#
# Changes to be committed:
#   modified:   a

使用 :wq 后,再看一下我们的 log:

* 2d7b687 - (HEAD -> master) Commit-1
* 5d39ff2 - Commit-0

任务完成!


至于 fixup 怎么用,我觉得大家现在应该已经知道了,因为我已经展示过 squash 的用法了,相信你再去看一下上面的 fixup 的解释就能明白了。

那么这篇文章就这样粗略地讲了一下 git rebase -i 的最基础的用法,希望能帮助到大家,有任何问题可以直接在评论里问我吧~

@jaysimon
Copy link

Q:最好能说明一下最后的a文件时什么样的状态,处在commit 几的状态

@kevin1202
Copy link

很清楚,感谢

@yanhaijing
Copy link

差评,一个图都没有

@gzhjs
Copy link

gzhjs commented Aug 8, 2017

赞!!但是感觉会导致大家滥用rebase,毕竟它会导致一些历史被丢掉

@yanhaijing
Copy link

@gzhjs push的就不能rebase了

@GeeLaw
Copy link

GeeLaw commented Aug 8, 2017

@gzhjs 我经常把弱智的错误通过 fixup 弄掉。

@yanhaijing 可以 --force,虽然会让别人很麻烦(如果别人 pull 了你的 branch)。

@yanhaijing
Copy link

@GeeLaw 这样不好,最好的就是 禁止push -f

@countzj
Copy link

countzj commented Sep 5, 2017

这个太麻烦,用tortoiseGit可以轻松搞定,调出提交日志,选择需要合并的日志,就会出来合并按钮,修改一个注释就搞定了!

@countzj
Copy link

countzj commented Sep 5, 2017

还没发现sourcetree怎么合并commit,知道的可以分享一下

@yanhaijing
Copy link

命令行多好用,一次学会,受益终身

@keer3
Copy link

keer3 commented Sep 11, 2017

如果想把

* 5756e15 - Commit-2
* e7ba81d - Commit-1

这两个commit合并有什么办法?

@countzj
Copy link

countzj commented Sep 11, 2017

如果合并commit-2和commit-1,就在git rebase -i 后面跟上commit1和2的序列号就可以了吧

@GeeLaw
Copy link

GeeLaw commented Sep 11, 2017

@SUNSHUMIN

p 575...
f e7b...
p b1b...

之后 commit-3 的 hash 自然会变化。

@0x-matt
Copy link

0x-matt commented Oct 17, 2018

文件少的话可以先: git reset --soft 然后重新 git commit ... 这样又简单又好理解。
rebase -i 合并比较麻烦,利用 rebase 做分支之间的变基会比较好;

@totoleo
Copy link

totoleo commented Oct 23, 2018

@gzhjs push的就不能rebase了

可以强制 push

@mixj93
Copy link

mixj93 commented Jan 7, 2019

thx~

@iwfan
Copy link

iwfan commented Jan 16, 2019

请问我如果最后还想合并 Commit-0 应该如何做呢?

@selqgun
Copy link

selqgun commented Mar 7, 2019

这个太麻烦,用tortoiseGit可以轻松搞定,调出提交日志,选择需要合并的日志,就会出来合并按钮,修改一个注释就搞定了!

怎么我的tortoiseGit 日志界面选中commit后没有合并的按钮?

@TrillStones
Copy link

rebase 可以 只合并中间的commit 吗? 例如在本文中就是:
e7ba81d(Commit-1)、5756e15(Commit-2) 这两个commit

@flyingzl
Copy link

合并完后怎么操作? 比如我在 master 上进行合并,合并完毕后

* (HEAD detached from refs/heads/master)
  hello
  master

怎么把把内容 push 到 master?

@taikulawo
Copy link

@flyingzl 你说的push是push到 远程仓库吗?如果是的话

git push --force-with-lease

https://stackoverflow.com/a/37460330/7529562

@luzihang123
Copy link

luzihang123 commented Dec 12, 2019

提问:

  • 提交PR的场景:
    1、fock代码,修改,commit,push
    2、发起PR
    3、发现commit快了,还有一些想修改的代码,开始修改,再次commit,push

  • 现在如何对PR进行修改?
    我的方法:
    1、关闭上一个PR
    2、rebase本地代码最近2次提交,然后强制覆盖远端仓库
    3、提交一个新的PR

我的方法对吗?

@iqlong
Copy link

iqlong commented Sep 2, 2022

@gzhjs push的就不能rebase了
您好,那已经push的commit如何合并

@faf-xff
Copy link

faf-xff commented Sep 5, 2022

@gzhjs push的就不能rebase了
您好,那已经push的commit如何合并
https://blog.csdn.net/qq_21744873/article/details/82629343 我看了这个成功了,最后执行git push -f

@xgqfrms
Copy link

xgqfrms commented Mar 21, 2023

@JasinYip

The crucial last step is missing.

# git push -f
$ git push -f origin feat-xxx

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests