GIT 技能手册

基础设置

Git 是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目。 Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。 Git 与常用的版本控制工具 CVS, Subversion 等不同,它采用了分布式版本库的方式,不必服务器端软件支持。

设置全局用户名和邮箱

git config --global user.name 'alan' 
git config --global user.email 'langwdalan@gmail.com'

当前项目设置用户名和邮箱

git config user.name 'alan' 
git config user.email 'langwdalan@gmail.com'

创建SSH Key

ssh-keygen -t rsa -C "langwdalan@gmail.com"

基本操作

git clone
git add .
git commit -m "提交信息"
git push

进阶操作

DIFF

比较两个分支

git diff branch1 branch2 > alan.diff

比较两个分支(包括二进制文件)

git diff branch1 branch2 --binary > alan.diff

比较当前HEAD修改

git diff HEAD > alan.diff

rebase

git rebase -i head~4

pick 68fdb81 第一次提交
squash b98e7be 第二次提交
pick a15c25a 第三次提交
fixup 6810ed4 第四次提交
 
# Rebase 83d844c..6810ed4 onto 83d844c (4 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
# d, drop = remove commit

使用rebase进行分支合并

git rebase [merge 的来源分支名]

使用上一个 commit

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

git commit --amend --no-edit

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

git commit --amend

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

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

# 仅修改 message 信息
$ git commit --amend --message="modify message by daodaotest"

# 仅修改 author 信息
$ git commit --amend --author="jiangliheng <jiang_liheng@163.com>"

强制提交

git push --force

Push an Empty Commit

git commit --allow-empty -m "Empty commit"

log

将日志输出到文件里

git log > alan.txt

简单显示 git 日志

git log --pretty=oneline

回退本地修改

git reset --hard <需要回退到的版本号(只需输入前几位)>

hardsoft还有mixed 的区别可以看这里

强制将本地版本覆盖要远端

git push origin <分支名> --force

撤销远程版本的某一次提交

比如

A1–A2–B1

如果想撤销A2的提交

git revert HEAD                     //撤销最近一次提交
git revert HEAD~1                   //撤销上上次的提交,注意:数字从0开始
git revert 0ffaacc                  //撤销0ffaacc这次提交

git revert 命令意思是撤销某次提交。它会产生一个新的提交,虽然代码回退了,但是版本依然是向前的,所以,当你用revert回退之后,所有人pull之后,他们的代码也自动的回退了。 但是,要注意以下几点:

从一个分支合并特定的commits到另一个分支

单个commit

比如

dd2e86 - 946992 -9143a9 - a6fd86 - 5a6057 [master]
           \
            76cada - 62ecb3 - b886a0 [feature]

比如,feature 分支上的commit 62ecb3 非常重要,它含有一个bug的修改,或其他人想访问的内容。无论什么原因,你现在只需要将62ecb3 合并到master,而不合并feature上的其他commits,所以我们用git cherry-pick命令来做:

git checkout master
git cherry-pick 62ecb3

这样就好啦。现在62ecb3 就被合并到master分支,并在master中添加了commit(作为一个新的commit)。 cherry-pick 和merge比较类似,如果git不能合并代码改动(比如遇到合并冲突),git需要你自己来解决冲突并手动添加commit。

多个commit

还以上例为例,假设你需要合并feature分支的commit 76cada ~62ecb3 到master分支。 首先需要基于feature创建一个新的分支,并指明新分支的最后一个commit:

git checkout -b newbranch 62ecb3

然后,rebase这个新分支的commit到master(–ontomaster)。76cada^ 指明你想从哪个特定的commit开始。

git rebase --onto master 76cada^

得到的结果就是feature分支的commit 76cada ~62ecb3 都被合并到了master分支。

Clone, Fork, and Pull Request

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

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 操作的当前路径都是项目根目录。

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

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

git remote -v
#OUTPUT:
#dev    https://github.com/tisonkun/pulsar.git (fetch)
#dev    https://github.com/tisonkun/pulsar.git (push)
#origin https://github.com/apache/pulsar.git (fetch)
#origin https://github.com/apache/pulsar.git (push)

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

git checkout -b feature-branch
git add .
git commit -s -m "commit message"
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 后再加一个分支名。

根据 commit 拆分 PR

有没有遇到过 Code Review 时发现内容太多,想拆分 Commit 却没法下手? 我以前使用的方案是提交多个连续 PR,然后在 PR B 里面写清楚本 PR 依赖于 A PR,请先合并 A 再 Code Review。

有没有更好的方案?当然有:git rebase 自带一个 --onto 功能可以快速拆分 Commits,高效又可靠。