不得不说 Git 改变了我们的编程习惯,随时版本备份百利而无一害。我们不能只停留在熟练使用 add
,pull
,push
等基本命令的层次,是时候扩展技能了。
一、Config
先 git config --help
打印出使用说明:
1 | NAME |
可以看到,config
也有很多用法,我们只选一些常见的操作来说明:
1 | # 取消全局 用户名/邮箱 配置 |
二、Stash
我们先来看看 git stash --help
的使用帮助:
1 | NAME |
上述解释很清楚了,我们来看看一些应用场景:git stash
可用来暂存当前正在进行的工作, 比如想 pull
最新代码, 又不想加新 commit
, 或者另外一种情况,为了 fix
一个紧急的 bug
, 先 stash
, 使返回到自己上一个 commit
, 改完 bug
之后再 stash pop
, 继续原来的工作。
基本用法如下:
1 | git stash |
当然,我们可以为每次 stash
加上别名:
1 | git stash save "fix jartto's blog bugs" |
多次使用 git stash
命令后,栈里将充满了未提交的代码,这时候我们可以使用 list
命令来选择应用回来的版本:
1 | git stash list |
确定好要应用回来的版本,找到对应版本号,使用:
1 | git stash apply stash@{1} |
这样就可以将你指定版本号为 stash@{1}
的工作取出来。
既然是 list
,那可能会用到删除某次的暂存,请慎用:
1 | git stash drop stash@{1} |
嗯,很好,当所有暂存版本都应用后,我们可以选择是否清除 stash
,命令如下:
1 | git stash clear |
了解更多,请参考:
git stash 和 git stash pop
git stash
三、Remote
我们先来看看 git remote --help
的使用帮助:
1 | NAME |
同样,我们挑几个常用命令来说明:
1.git
显示远程分支:
1 | git remote show origin |
2.git
删除远程分支:
1 | git push origin --delete jartto-hotfix |
3.添加源:
1 | git remote add origin git@192.168.1.1:test/jartto-blog.git |
4.修改源地址:
1 | git remote set-url origin git@jartto.wang:test/jartto-blog.git |
5.删除源
1 | # 查看源列表 |
四、Cherry-pick
先看看 Git
怎么定义 cherry-pick
命令的:
Given one or more existing commits, apply the change each one introduces, recording a new commit for each. This requires your working tree to be clean (no modifications from the HEAD commit).
When it is not obvious how to apply a change, the following happens:
- The current branch and HEAD pointer stay at the last commit successfully made.
- The CHERRY_PICK_HEAD ref is set to point at the commit that introduced the change that is difficult to apply.
- Paths in which the change applied cleanly are updated both in the index file and in your working tree.
- For conflicting paths, the index file records up to three versions, as described in the “TRUE MERGE” section of git-merge(1). The working tree files will include a description of the conflict bracketed by the usual conflict markers <<<<<<< and >>>>>>>.
- No other modifications are made.
1 | NAME |
从打印出来的帮助信息中也可以看出来 cherry-pick
的用法和场景比较复杂,我们先从 git
帮助文档中的示例下手:
1 | # Apply the change introduced by the commit at the tip of the master branch and create a new commit with this change. |
当执行完 cherry-pick
以后,将会生成一个新的提交;这个新的提交的哈希值和原来的不同,但标识名一样;
这么多说明文档,可能也很难理解,我们来列举一些实际场景:
1.项目刚上线完毕,你需要紧急更改线上 bug
,你基于线上版本切出了 hotfix
版本并提交,修复了问题:
1 | git checkout -b hotfix release |
此时,你切回自己的开发分之,发现版本落后了,可以有两个操作:
1 | # 方法一:拉取分支变更 |
2.如果你的应用已经发布了一个版本2.0, 代码分支叫 release-2.0
, 现在正在开发 3.0, 代码的分支叫 dev-3.0
. 那么有一天产品说, 要把正在开发的某个特性提前上线, 也就是说要把 dev-3.0
分支上的某些更改移到 2.x 的版本上, 那么怎么办呢?
基于 release-2.0
分支新建分支release-2.1
, 并且到新创建的分支上:
1 | git checkout -b release-2.1 release-2.0 |
将 dev-3.0
分支上的某些 commit
在 release-2.1
分支上重演,例如:
1 | git cherry-pick |
cherry-pick
会重演某些 commit
, 即把某些 commit
的更改重新执行一遍。
3.cherry-pick
不仅可以用在不同分支之间, 还可以用在同一个分支上。比如说你在某一个向某个分支中添加了一个功能, 后来处于某种原因把它给删除了,然而后来某一天你又要添加上这个功能了,这时候就可以使用 cherry-pick
把添加那个功能的 commit
,再重演一遍。请参考:git cherry-pick 用法。
4.知乎上看到了另一种场景,有一条主分支 master
,进行过两次提交( m0
和 m1
)。此时,新开了一个分支 develop
做开发,进行了三次提交( d0
、d1
和 d2
)。如果只想将 d2
这次提交合并到主分支 master
就会产生一个冲突,需要用户手动去编辑,问题是,如何避免冲突?
答一:你无法 cherry-pick
的原因是,你 d2
修改的文件,已经在 d1
(or d0
) 被修改过了,所以 cherry-pick
并不知道如何删除和增加对应的 lines
,所以就冲突了。你唯一的办法就是解决冲突。
答二:cherry-pick
只会将一个 commit
引入的更改应用到当前的 commit
上。而你的命令:
1 | git cherry-pick d2的哈希码 |
只会将从 d1->d2
的修改引入到 m1
中,这样当然会造成冲突。正确的做法是:
1 | git checkout master |
--no-commit
参数是防止 cherry-pick
每一次应用更改都 commit
一次。
除此之外,还有一个命令是等效的:
1 | git merge d2 --squash |
五、Reset
先来理解两个概念:
1.四个区
- 工作区(Working Area)
- 暂存区(Stage)
- 本地仓库(Local Repository)
- 远程仓库(Remote Repository)
2.五种状态
代码进入每一个区成功之后会产生一个状态,再加上最初始的一个状态,一共是5种状态:
- 未修改(Origin)
- 已修改(Modified)
- 已暂存(Staged)
- 已提交(Committed)
- 已推送(Pushed)
下面来说具体说明几种状态中的撤销操作:
1.已修改,未暂存
如果我们只是修改了文件,但还没有执行 git add .
,这时候我们的文件还在工作区,并没有进入暂存区,可以用如下命令进行撤销操作:
1 | git checkout . |
或者执行:
1 | git reset --hard |
简单来说,git add .
的反向操作就是 git checkout .
2.已暂存,未提交
假如你已经执行了 git add .
,但还没有执行 git commit -m "comment"
。这个阶段,想要撤销,可以执行如下命令:
1 | git reset |
当然,也可以执行:
1 | git reset --hard |
git reset
只是把修改退回到了 git add .
之前的状态,也就是说文件本身还处于已修改未暂存状态,你如果想退回未修改状态,还需要执行 git checkout .
。
3.已提交,未推送
你的手太快,你既执行了 git add .
,又执行了 git commit
,这时候你的代码已经进入了你的本地仓库,然而你后悔了,怎么办?不要着急,还有办法。
1 | git reset --hard origin/master |
还是这个 git reset --hard
命令,只不过这次多了一个参数origin/master,正如我们上面讲过的,origin/master代表远程仓库,既然你已经污染了你的本地仓库,那么就从远程仓库把代码取回来吧。
4.已推送
更糟糕的是,你既 git add
了,又 git commit
了,并且还 git push
了,这时你的代码已经进入远程仓库。如果想要撤销,执行如下命令:
1 | git reset --hard HEAD^ |
简单来说:由于你的本地仓库和远程仓库是等价的,你只需要先恢复本地仓库,再强制 push
到远程仓库就好了:
此处参考,Git 的 4 个阶段的撤销更改