[git] 如何合并最近的若干个 commit?

105 阅读2分钟

背景

当我们在一个特性分支进行开发时,有时候需要合并最近的若干个 commit\text{commit}(例如在提 Pull Request\text{Pull Request} 前,可以将该特性分支的所有 commit\text{commit} 合并为一个 commit\text{commit},以便将来查看这个 Pull Request\text{Pull Request} 的变更)

要点

基于 git reset\text{git reset} 命令的方案

借助以下两个命令,可以将当前分支的最近 n\text{n}commit\text{commit} 合并成一个 commit\text{commit}

git reset --soft HEAD~n
git commit -m "Please write your commit message here"

注意事项

  • 执行第一个命令时,需要将 n\text{n} 替换为您需要的数字(例如 5\text{5}
  • 执行第二个命令时,请将 "Please write your commit message here" 替换为合适的内容

基于 git rebase\text{git rebase} 命令的方案

借助以下命令,可以将当前分支的最近 n\text{n}commit\text{commit} 合并成一个 commit\text{commit}

git rebase -i HEAD~n

注意事项

  • 执行这个命令时,需要将 n\text{n} 替换为您需要的数字(例如 5\text{5}
  • 执行这个命令时,需要操作文本编辑器(有可能是 Vim\text{Vim} 或者 Emacs\text{Emacs}),如果您对这个环节不熟悉,可以参考文末的参考资料
  • 如果您是第一次使用这个命令,请不要用重要的项目来进行试验,以防误操作

正文

为了便于讨论,我们来杜撰一个例子 ⬇️

示例

准备工作

请将以下代码保存为 main.sh (具体的名称并不重要)

mkdir temp
cd temp

git init
echo "nothing here" >> README.txt
git add README.txt
git commit -m "initial commit"

BOUND=5

for ((i=1; i<=BOUND; i++))
do
    FILE_NAME="file_$i.txt"
    touch "$FILE_NAME"

    echo "This is file number $i" > "$FILE_NAME"
    echo "Created file: $FILE_NAME"
    
    git add "$FILE_NAME"
    git commit -m "Created file: $FILE_NAME"
done

echo "All files created successfully!"

通过执行下方的命令就可以运行 main.sh ⬇️

bash main.sh

运行完 main.sh 之后,当前目录中会多出一个 temp 目录。在当前目录执行 tree temp 命令后,应该可以看到如下的内容

temp
├── file_1.txt
├── file_2.txt
├── file_3.txt
├── file_4.txt
├── file_5.txt
└── README.txt

1 directory, 6 files

如果用 IntelliJ IDEA (Community Edition) 打开 temp 目录,应该可以看到类似这样的效果 ⬇️

image.png

从图中可以看到,我们一共创建了 6\text{6}commit\text{commit}。如果我们想把最近的 5\text{5}commit\text{commit} 合并在一起,那么至少有两种方案 ⬇️

方案一:基于 git reset\text{git reset} 命令的解决方案

通过执行如下命令,可以切换到 temp 目录中 ⬇️

cd temp

执行如下命令,就可以将最近的 5\text{5}commit\text{commit} 合并为一个

git reset --soft HEAD~5
git commit -m "Please write your commit message here"

请注意

  • 在第一个命令中,数字 5\text{5} 并没有特殊之处,您可以使用数字 n\text{n} 以合并最近的 n\text{n}commit\text{commit}
  • git commit\text{git commit} 命令的 -m\text{-{}m} 选项之后,需要提供对应的 message\text{message}

执行完上述两个命令后,借助 IntelliJ IDEA (Community Edition) 再次观察,结果符合预期 ⬇️

image.png

方案二:基于 git rebase\text{git rebase} 命令的解决方案

通过执行如下命令,可以切换到 temp 目录中 ⬇️

cd temp

执行如下命令,就可以将最近的 5\text{5}commit\text{commit} 合并为一个

git rebase -i HEAD~5

在这个命令中,数字 5\text{5} 并没有特殊之处,您可以使用数字 n\text{n} 以合并最近的 n\text{n}commit\text{commit}。执行这个命令后,会看到一个可编辑的界面,效果类似这样 ⬇️ 需要将红色框里的 4\text{4} (即 5 - 1\text{5 - 1})个 pick\text{pick} 修改为 s\text{s}(黄色框中解释了 s\text{s} 的含义)

image.png

修改后,会是类似这样的效果 ⬇️ (红色框里的内容已经从 pick\text{pick} 变为 s\text{s}

image.png

退出这个编辑界面(如果是在 Vim\text{Vim} 编辑器中,输入 :wq\text{:wq} 就可以保存+退出),退出之后,会来到另一个编辑界面 ⬇️ 在这个编辑界面中,我们可以调整 commit message\text{commit message} (下图红色框中展示了最近 5\text{5}commit\text{commit} 原本的 commit message\text{commit message}

image.png

我们可以选择不修改原始的 commit message\text{commit message},那就直接退出这个编辑界面(如果是在 Vim\text{Vim} 编辑器中,输入 :wq\text{:wq} 就可以保存+退出)。退出之后,可以看到 git rebase\text{git rebase} 命令执行成功了 ⬇️

image.png

我们在 IntelliJ IDEA (Community Edition) 里也可以进行验证 ⬇️

image.png

参考资料

关于 git reset\text{git reset} 命令的更多介绍,可以参考以下两个链接(两个链接来自同一个网站,第一个链接的内容是简体中文,第二个链接的内容是英文)

关于 git rebase\text{git rebase} 命令的更多介绍,可以参考以下两个链接(两个链接来自同一个网站,第一个链接的内容是简体中文,第二个链接的内容是英文)

Vim\text{Vim} 的网站: welcome home : vim online

Emacs\text{Emacs} 的网站: GNU Emacs - GNU Project