💪git 几个高级用法

2,998 阅读5分钟

1 cherry-pick

cherry-pick可用于把其他分支的commit,移到当前分支。

实践一下:

基于 master 切出 test 和 test2 两条分支,分别在这两条分支进行两次提交,如图所示


如果我们需要把 test 和 test2 分支的第一次提交 移到 master 上,那么可以使用 cherry-pick(注意:使用merge合并的方式会把所有提交合并过来,而这里我们只需要 test 和 test2 第一次提交)

使用 git log 查看 test 和 test2 中第一次提交的 commit id ,在 cherry-pick 中使用。

然后在 master 分支中cherry-pick

// 4d5a7b1 为 test第一次提交的 commit id, 3d56b9a 为 test2 第一次提交的commit id
git cherry-pick 4d5a7b1 3d56b9a

此时如果无冲突,那么 test 和 test2 分支第一次提交的内容将会移到master中

如果有冲突,那么解决后使用 git add,添加后再执行 git cherry-pick --continue 

当然你也可以退出 cherry-pick,使用 git cherry-pick --quit 会退出 cherry-pick,但是刚刚 cherry-pick 的结果会留下

如果你希望回到 cherry-pick 之前,那么可以使用 git cherry-pick --abort 

如果 cherry-pick 已经顺利执行完,而你又想回到 cherry-pick 之前,那么可以使用版本回退啦。

注意:

1 cherry-pick 一个分支的多个 commit 时,请按顺序填写 commit id,或者使用 "..." 限定范围,如下。

// 从start-commit-id 到 end-commit-id 不包含 start-commit-id 
git cherry-pick <start-commit-id>…<end-commit-id>

// 从start-commit-id 到 end-commit-id 包含 start-commit-id
git cherry-pick <start-commit-id>^…<end-commit-id>

2 假如有一个 commit,是从其他分支合并过来形成的,那么 cherry-pick 这个 commit 将会报错,是因为 git 不知道取哪个分支的内容,使用 -m 选项即可。参考这里

error: commit fd30d42926885f792976a094b1aa0c96e8228240 is a merge but no -m option was given.


2 查看某个文件的历史

git log --all --full-history -- package-lock.json

--all 展示包括其他分支的所有历史

--full-history 查看某个文件历史时,git会自动简化历史(History Simplification),甚至会隐藏一些提交。--full-history可以关闭History Simplification。


3 --allow-unrelated-histories

git 2.9 后默认不允许合并两个不相关的没有共同祖先的分支,比如新建本地仓库,新建github仓库,本地仓库 git remote add origin <repository url> ,然后git pull origin master,会报错。

fatal: refusing to merge unrelated histories

可以使用--allow-unrelated-histories解决

// pull时
git pull origin <branch> --allow-unrelated-histories

// merge时
git merge <branch> --allow-unrelated-histories

4 创建干净历史的分支

git checkout --orphan <new-branch>

--orphan创建出来的分支没有任何提交历史

5 git clone深度

// 克隆仓库,且只克隆最近 1 次提交历史,如果想克隆最近 n 次的历史,把 1 改成 n 即可。
git clone --depth=1 <repository url>


6 merge squash 与 rebase

merge

首先说下 merge,merge 会把某分支的所有 commit 合并过来,如果有冲突,解决冲突后还需再 commit 一次,也即会多产生一个 commit,这对于有洁癖的人来说会比较难受,所以有了 merge squash 与 rebase

merge squash

merge squash 会把某分支所有 commit 压缩成一个

# 使用 --squash 方式合并 dev 分支代码
git merge --squash dev

如上例子,当使用 squash 方式合并 dev 分支时,你会发现,dev 的代码被合并过来了并且代码被添加到了暂存区,需要自己再 commit 一次,这样就相当于把 dev 的代码压缩成了一个 commit ,这里暴露了 squash commit 的一个缺点,假设 dev 的代码是由张三写的,李四使用 squash 的方式合并过来后提交 commit ,那么 dev 代码的 author 就变成了李四,张三白干了。。。

rebase

rebase:变基,从字面上理解 = re + base 重新修改基底的意思

假设提交记录提交如下:master 有两次 commit,在第一次 commit 切出 test 分支,test 分支有三次提交。


现在 test 分支的基底是 master 的第一次 commit (也即 init 那个) 我们改变 test 分支的基底

git checkout test
git rebase master

这样相当于把 test 分支的基底设置到 master 的最末那个 commit 里,也就相当于 test 的修改是基于 master 的了

这里操作和 cherry pick 差不多,如果有冲突,也是修改冲突后,add 添加到暂存区,然后 

git rebase --continue,退出的话也是用 abort git rebase --abort

最后切到 master 合并变基后的 test 分支即可

git checkout master
git merge test

这样就把 test 的 commit 拿到 master 上了,并且不会像 merge --squash 那样改掉了 commit 的 author

git rebase 拓展

git rebase 还有一些其他用法,可参考

合并多个 commit & 复制一段 commit 到其他分支

git pull --rabase 

git rebase 的危险性:不要在多人合作的分支使用 git rebase