基础篇
1 Git Commit
git commit
2 Git Branch
git branch newImage
git commit
git checkout newImage
git commit
创建一个新的分支同时切换到新创建的分支,可以通过以下命令实现
git checkout -b <your-branch-name>
3 Git Merge
第一种合并分支的方法
把 bugFix 合并到 main 里
git merge bugFix
git checkout bugFix
git merge main
4 Git Rebase
第二种合并分支的方法
git rebase
Rebase 实际上就是取出一系列的提交记录,“复制”它们,然后在另外一个地方逐个的放下去。
Rebase 的优势就是可以创造更线性的提交历史。
只使用 Rebase ,代码库的提交历史将会变得异常清晰。
git rebase main
git rebase bugFix
高级篇
1 分离 HEAD
git checkout c1
git checkout main
git commit
git checkout c2
git checkout c1
2 相对引用(^)
git log
查看提交记录的哈希值。
fed2da64c0efc5293610bdd892f82a58e8cbc5d8
只需要提供能够唯一标识提交记录的前几个字符即可
fed2
使用 ^
向上移动 1 个提交记录
使用 ~<num>
向上移动多个提交记录,如 ~3
git checkout main^
git checkout c3
git checkout HEAD^
git checkout HEAD^
git checkout HEAD^
3 相对引用(~)
git checkout HEAD~4
强制修改分支位置
使用相对引用最多的就是移动分支。可以直接使用 -f 选项让分支指向另一个提交。例如:
git branch -f main HEAD~3
上面的命令会将 main 分支强制指向 HEAD 的第 3 级父提交。
eg:
git branch -f main c6
git checkout HEAD~1
git branch -f bugFix HEAD~1
4 撤销变更
Git 里撤销变更的方法很多。和提交一样,撤销变更由底层部分(暂存区的独立文件或者片段)和上层部分(变更到底是通过哪种方式被撤销的)组成。这里讨论的是后者。
主要有两种方法用来撤销变更 —— 一是 git reset
,还有就是 git revert
。
Git Reset
git reset HEAD~1
Git Revert
本地分支中使用 git reset 很方便,但是这种“改写历史”的方法对团队一起使用的远程分支是无效的!
为了撤销更改并分享给团队,需要使用 git revert。
git revert HEAD
因为新提交记录 C2'
引入了更改 —— 这些更改刚好是用来撤销 C2
这个提交的。也就是说 C2'
的状态与C1
是相同的。
revert
之后就可以把更改推送到远程仓库与别人分享。
移动提交记录
1 git Cherry-pick
命令形式
git cherry-pick <提交号>...
将一些提交复制到当前所在的位置(HEAD)下面, Cherry-pick 是最直接的方式。
eg
将 side
分支上的工作复制到main
分支
git cherry-pick c2 c4
eg
git cherry-pick c3
git cherry-pick c4
git cherry-pick c7
2 交互式 rebase
交互式 rebase 指的是使用带参数 --interactive
的 rebase 命令
, 简写为 -i
当 rebase UI界面打开时, 能做3件事:
- 调整提交记录的顺序(通过鼠标拖放来完成)
- 删除不想要的提交(通过切换 pick 的状态来完成,关闭就意味着不想要这个提交记录)
- 合并提交
eg
git rebase -i HEAD~4
杂项
1 只取一个提交记录
git rebase -i
git cherry-pick
git rebase -i main
git rebase bugFix main
2 提交的技巧 #1
- 先用
git rebase -i
将提交重新排序,然后把我们想要修改的提交记录挪到最前 - 然后用
git commit --amend
来进行一些小修改 - 接着再用
git rebase -i
来将他们调回原来的顺序 - 最后我们把
main
移到修改的最前端(用你自己喜欢的方法),就大功告成啦!
3 提交的技巧 #2
git checkout main
git cherry-pick C2
git commit --amend
git cherry-pick C3
4 Git Tag
git tag v1 c1
5 Git Describe
git describe
的语法是:
git describe <ref>
<ref>
可以是任何能被 Git 识别成提交记录的引用,如果你没有指定的话,Git 会以你目前所检出的位置(HEAD
)。
它输出的结果是这样的:
<tag>_<numCommits>_g<hash>
tag
表示的是离ref
最近的标签, numCommits
是表示这个 ref
与 tag
相差有多少个提交记录,hash
表示的是你所给定的ref
所表示的提交记录哈希值的前几位。
当 ref
提交记录上有某个标签时,则只输出标签名称
高级话题
1 多次 Rebase
$ git rebase main bugFix
$ git rebase bugFix side
$ git rebase side another
$ git rebase another main
2 两个父节点
git branch bugWork main^^2^
3 纠缠不清的分支
$ git checkout one
$ git cherry-pick C4 C3 C2
$ git checkout two
$ git cherry-pick C5 C4 C3 C2
$ git branch -f three C2
Push & pull – Git远程仓库
远程仓库却有一系列强大的特性
-
首先也是最重要的的点, 远程仓库是一个强大的备份。本地仓库也有恢复文件到指定版本的能力, 但所有的信息都是保存在本地的。有了远程仓库以后,即使丢失了本地所有数据, 仍可以通过远程仓库拿回你丢失的数据。
-
远程让代码社交化了!
远程仓库进行可视化操作
(GitHub 或 Phabricator)
1 Git Clone
git clone
2 远程分支
为什么有 o/
?
远程分支有一个命名规范 的格式是:
<remote name>/<branch name>
一个名为 o/main
的分支,那么这个分支就叫 main
,远程仓库的名称就是 o
。
主要的远程仓库命名为 origin
,并不是 o
。这是因为用 git clone
某个仓库时,Git 已经把远程仓库的名称设置为 origin
了
不过 origin
对于 UI 来说太长了,因此不得不使用简写 o
)
但使用真正的 Git 时, 远程仓库默认为 origin
!
3 Git Fetch
git fetch
git fetch
完成了仅有的但是很重要的两步:
- 从远程仓库下载本地仓库中缺失的提交记录
- 更新远程分支指针(如 o/main)
git fetch
实际上将本地仓库中的远程分支更新成了远程仓库相应分支最新的状态。
如果你还记得上一节课程中我们说过的,远程分支反映了远程仓库在你最后一次与它通信时的状态,git fetch
就是你与远程仓库通信的方式了!希望我说的够明白了,你已经了解 git fetch
与远程分支之间的关系了吧。
git fetch
通常通过互联网(使用 http://
或git://
协议) 与远程仓库通信。
git fetch
并不会改变本地仓库的状态。它不会更新 main
分支,也不会修改磁盘上的文件。
git fetch
为单纯的下载操作。
4 Git Pull
git fetch
git merge o/main
git pull
git pull
就是 git fetch
和 git merge
的缩写!
5 模拟团队合作
git clone
git fakeTeamwork 2
git commit
git pull
6 Git Push
git commit
git push
git commit
git push
7 偏离的提交历史
假设你周一克隆了一个仓库,然后开始研发某个新功能。到周五时,你新功能开发测试完毕,可以发布了。但是 —— 天啊!你的同事这周写了一堆代码,还改了许多你的功能中使用的 API,这些变动会导致你新开发的功能变得不可用。但是他们已经将那些提交推送到远程仓库了,因此你的工作就变成了基于项目旧版的代码,与远程仓库最新的代码不匹配了。
这种情况下, git push 就不知道该如何操作了。如果你执行 git push,Git 应该让远程仓库回到星期一那天的状态吗?还是直接在新代码的基础上添加你的代码,亦或由于你的提交已经过时而直接忽略你的提交?
因为这情况(历史偏离)有许多的不确定性,Git 是不会允许你 push 变更的。实际上它会强制你先合并远程最新的代码,然后才能分享你的工作。
git push
什么都没有变,因为命令失败了!git push
失败是因为你最新提交的 C3
基于远程分支中的 C1
。而远程仓库中该分支已经更新到 C2
了,所以 Git
拒绝了你的推送请求。
那该如何解决这个问题呢?很简单,你需要做的就是使你的工作基于最新的远程分支。
有许多方法做到这一点呢,不过最直接的方法就是通过 rebase 调整你的工作。咱们继续,看看怎么 rebase!
git fetch
git rebase o/main
git push
我们用 git fetch
更新了本地仓库中的远程分支,然后用 rebase 将我们的工作移动到最新的提交记录下,最后再用 git push 推送到远程仓库。
还有其它的方法可以在远程仓库变更了以后更新我的工作吗? 当然有,我们还可以使用 merge
尽管git merge
不会移动你的工作(它会创建新的合并提交),但是它会告诉 Git 你已经合并了远程仓库的所有变更。这是因为远程分支现在是你本地分支的祖先,也就是说你的提交已经包含了远程分支的所有变化。
看下演示…
git fetch
git merge o/main
git push
我们用 git fetch
更新了本地仓库中的远程分支,然后合并了新变更到我们的本地分支(为了包含远程仓库的变更),最后我们用 git push
把工作推送到远程仓库
git pull
就是 fetch
和 merge
的简写,类似的 git pull --rebase
就是 fetch
和 rebase
的简写!
git pull --rebase
git push
eg
git clone
git fakeTeamwork
git commit
git pull --rebase
git push
8 锁定的Master
git checkout -b feature c2
git push origin feature
关于 origin 和它的周边 —— Git 远程仓库高级操作
1 推送主分支
$ git fetch
$ git rebase o/main side1
$ git rebase side1 side2
$ git rebase side2 side3
$ git rebase side3 main
$ git push
2 合并远程仓库
$ git checkout main
$ git pull
$ git merge side1
$ git merge side2
$ git merge side3
$ git push
3 远程追踪
···
$ git checkout -b side o/main
$ git commit
$ git pull --rebase
$ git push·
···
4 Git Push的参数
$ git push origin main
$ git push origin foo
5 Git Push的参数2
$ git fetch origin main~1:foo
$ git fetch origin foo:main
$ git checkout foo
$ git merge main
6 Git fetch的参数
$ git fetch origin main~1:foo
$ git fetch origin foo:main
$ git checkout foo
$ git merge main
7 没有source的source
$ git push origin :foo
$ git fetch origin :bar
8 Git pull 的参数
以下命令在 Git 中是等效的:
git pull origin foo
相当于:
git fetch origin foo; git merge o/foo
还有…
git pull origin bar~1:bugFix
相当于:
git fetch origin bar~1:bugFix; git merge bugFix
git pull 实际上就是 fetch + merge 的缩写, git pull 唯一关注的是提交最终合并到哪里(也就是为 git fetch 所提供的 destination 参数)
$ git pull origin bar:foo
$ git pull origin main:side
参考文献
[1] https://codechina_dev.gitcode.host/learn-git-branching/?locale=zh_CN