git pull 策略

默认情况下,git pull 会将远端分支的 commit 以 merge 的方式合并到本地分支之上。然而,大多数情况下本地分支是下游,远端分支是上游,将本地的 commit rebase 到远端分支之上是更好的做法。这可以保持 commit history 整洁。
git pull 提供 —merge 和 —rebase 两种策略,默认为前者。为了不用每次 git pull 指定 —rebase 选项,可以配置改变默认策略:

  1. git config --global pull.rebase true

Clone, Fork, and Pull Request

全局配置完成后,就能正式进入开发和提交补丁的流程了。
不像大部分教程介绍的 fork, clone, and pull request 流程,实际上符合上游优先直觉的参与过程应该是 clone, fork, and pull request 才对。
第一步,当你看到一个有趣的项目,起了阅读代码和开发的念头,此时你并不知道自己是不是真的会提交一个补丁,所以做简单的做法是直接从上游地址克隆源代码。例如,要想克隆 Apache Pulsar 的源代码,可以执行以下命令:

  1. git clone https://github.com/apache/pulsar.git

随后,你就可以打开项目源代码开始阅读,做一些变更并观察效果。
第二步,当你发现本地的变更值得推送回上游时,由于你没有权限直接 push commit 到上游仓库,你需要从上游仓库 fork 一份到自己的工作空间当中,从而进一步推送修改和提交补丁。同样以 Apache Pulsar 为例,

  1. 打开 http://github.com/apache/pulsar 仓库页面。
  2. 点击页面右上方的 Fork 按钮。
  3. 选择正确的账号并确认。
  4. 成功后,应该可以在个人账号下看到 fork 出来的 pulsar 仓库。

第三步,fork 动作只是发生在 GitHub 平台上,接下来还需要在本地 Git 仓库配置 remote 信息,注意一下 Git 操作的当前路径都是项目根目录。

  1. git remote add dev https://github.com/<your-user-name>/<project>.git

还是以 Apache Pulsar 为例,将用户名和仓库名相应替换后运行上述命令,成功返回后查询当前 remote 配置信息:

  1. git remote -v
  2. #OUTPUT:
  3. #dev https://github.com/tisonkun/pulsar.git (fetch)
  4. #dev https://github.com/tisonkun/pulsar.git (push)
  5. #origin https://github.com/apache/pulsar.git (fetch)
  6. #origin https://github.com/apache/pulsar.git (push)

接下来,就可以切出开发分支,commit 变更并提交补丁了。
在命令行界面完成 commit 动作并将改动分支推送到 fork 仓库上:

  1. git checkout -b feature-branch
  2. git add .
  3. git commit -s -m "commit message"
  4. git push --set-upstream dev

打开上游仓库页面。如果你是刚推送的分支,GitHub 会有横幅提示,点击 Compare & pull request 从最近推送的对应分支创建一个 Pull Request 请求;否则,需要从 Pull requests 页面点击 New pull request 按钮,点击蓝色的 compare cross forks 字样,选择 HEAD 信息为 fork 仓库和对应的分支来创建。
相比于常见的 fork and clone 流程以及后续将 fork 分支作为 origin 而把上游称为 upstream 的做法,这里介绍的 clone and fork 流程有以下几点好处。
第一,顺序自然。不是每个克隆的项目在一开始就都是为了提交补丁,这样的顺序不需要在一开始就创建一个 fork 仓库。
第二,默认分支自动拉取上游,无需额外的配置。大部分情况下,fork 仓库的默认分支都不会再更新。如果 origin 是 fork 仓库,要想拉取上游默认分支,要么需要修改仓库配置里的分支上游,要么就要在 fork 仓库页面上 Sync fork 在拉取,都需要额外的操作。
第三,推送分支的命令更简洁。如果 origin 是 fork 仓库,则 git push 时需要在 —set-upstream origin 后再加一个分支名。

暂存变更

开发者经常会在当前 codebase 上做一些尝试性的修改,这些修改可能是半成品,不能作为一个完整的 commit 提交。
这个时候,你又有了新的点子或者想要切换到另一件事情上。如果你不想把这些变更用一个类似 save 的无意义提交暂存下来,或者就是在默认分支上随便改改不能提交的,可以使用 git stash 命令暂存所有变更,随后想要重做刚才尝试下的修改,执行 git stash pop 把暂存的变更复原。

清理无用的分支

经过一段时间的开发,本地经常会出现各种无用的分支,无论是废弃不再继续,还是上游已经合并。为了清理本地仓库状态,可以执行以下命令:

  1. git fetch -p
  2. git fetch dev -p
  3. # Exclude more branches from removal by piping `grep -v`
  4. git branch | grep -v main | xargs git branch -D

Rebase

某些开源项目维护者会要求补丁作者 rebase commit 以改善 review 进程。
对于要求在合并前 rebase 成一个 commit 的要求,你可以反驳 GitHub 已经提供了 squash and merge 功能,无需开发者在手动操作。
对于希望把 commit 按改动点重新梳理的请求,大部分情况下还是重做更加迅速。Rebase 的概念和执行方式相当复杂,建议阅读 rebase 的官方文档并总是使用 git rebase -i 走交互式界面执行。

合并到上一个 commit

添加修改:git add .
合并到上一次的提交并使用原来的 commit 信息:

  1. git commit --amend --no-edit

合并到上一次的提交并修改 commit 信息

  1. git commit --amend

修改最后一次提交 commit 的信息

  1. # 修改最近提交的 commit 信息
  2. $ git commit --amend --message="modify message by daodaotest" --author="jiangliheng <jiang_liheng@163.com>"
  3. # 仅修改 message 信息
  4. $ git commit --amend --message="modify message by daodaotest"
  5. # 仅修改 author 信息
  6. $ git commit --amend --author="jiangliheng <jiang_liheng@163.com>"