Git notes
git是一个分布式版本控制系统,不同类型的版本控制软件还有svn,mercurial,vss,SourceAnywhere等。 而Github是一个集成了git的服务。
不管是使用Github,Coding还是Gitlab,我们都需要熟悉Git这一版本管理工具。本文简单介绍Git的工作原理,并总结了一些常用的git指令,冲突解决等,以便自己查看。
基本介绍
(图片均来自网络)

从上图可以看出git的组成和基本指令。git在本地的包括工作区,暂存区,以及本地仓库,stash。远端仓库就是Github,Coding,Gitlab等工具。
Git的存储方式
因为每一个版本之间可能有重叠,为了节省空间,git不是将每一个独立存放,而是以快照的形式存放。如下图所示:
Git分支
一般有两类分支:
- Merge分支(通常为master分支):Merge分支是为了可以随时发布release而创建的分支,可以使用Jenkins之类的CI工具进行自动化编译以及测试。
- Topic分支:用来进行功能开发的
HEAD
HEAD指向的是现在使用中的分支的最后一次更新。通常默认指向master分支的最后一次更新。通过移动HEAD,就可以变更使用的分支。
我们经常需要根据一个提交去查找它的祖先提交,如查找 HEAD 的第三个祖先提交。 这时就可以用到Git 中的 ~ 和 ^,其具体用法见文章。
Git中
~和^的区别?在git中,我们其实可以通过^和~来定位某个具体的commit,而不用每次都去敲繁琐的hash值。
^代表父提交,当一个提交有多个父提交时,可以通过在^后面跟上一个数字,表示第几个父提交,^相当于^1.1
2
3
4
5
6
7
8
9$ git log --graph --oneline
* f44239d D
* 7a3fb3d C
|\
| * 07b920c B
|/
* 71bd2cf A
...这时候,我们是不能通过
~去找到07b920c (B)这个提交的。 如果一个提交有多个父提交,那么~只会找第一个父提交。 那么我们应该怎么找到07b920c (B)呢? 答案是:HEAD~^2<rev>^<n>用来表示一个提交的第 n 个父提交,如果不指定 n,那么默认为 1。 和~不同的是,HEAD^^^并不等价于HEAD^3,而是等价与HEAD^1^1^1。~<n>相当于连续的<n>个^.(<commit>|HEAD)~n = (<commit>|HEAD)^^^…(^的个数为n)
分支规范
A successful Git branching model:

分支合并
在 git 中合并分支有两种选择:merge 和 rebase。
merge

- fast-forward:

- non fast-forward:

rebase
4 以补丁的形式打到 3 上,形成了新的提交 4’。

rebase类似于打补丁。rebase的时候,修改冲突后的提交不是使用commit命令,而是执行rebase命令指定 –continue选项。若要取消rebase,指定 –abort选项。
但是,无论哪一种,都有可能产生冲突。
两种方法区别:
- merge
保持修改内容的历史记录,但是历史记录会很复杂。 - rebase
历史记录简单,是在原有提交的基础上将差异内容反映进去。
因此,可能导致原本的提交内容无法正常运行。
Git合并及冲突解决
冲突发生一般由于对同一个文件的修改不一致时。当有冲突发生时,一般需要手动合并冲突(也可以借助软件,如smartgit)。冲突标示一般为:
1 | <<<<<<<HEAD |
手动合并后需要并删除标示行。
可能发生冲突的情况时:
本地拉取
git pull = git fetch + git merge- 当远端有更新,本地库没有有变化,拉取操作远端会覆盖本地库(远端版本高于本地)
- 当远端修改,本地库也有修改,拉取操作会产生冲突文件(远端版本和本地版本冲突)
- 当本地库修改,而远端没有变化,拉取不会产生变化(本地版本高于远端版本)
- 当本地库版本低于远端版本,则无法推送,必须先拉取在操作,否则报错
远程推送
- 首先,可以试图用git push origin branch-name推送自己的修改;
- 如果推送失败,则因为远程分支比你的本地更新,需要先用git pull试图合并;如果git pull提示“no tracking information”,则说明本地分支和远程分支的链接关系没有创建,用命令git branch –set-upstream branch-name origin/branch-name。
- 如果合并有冲突,则解决冲突,并在本地提交;
- 没有冲突或者解决掉冲突后,再用git push origin branch-name推送就能成功!
分支合并
- 当资源分支版本高于目标分支时,合并,资源分支同名文件会覆盖目标分支;
- 当目标分支版本高于资源分支时,合并,提示Already up-to-date.(已经更新),目标分支内容不会变化;
Git常用指令
此处总结一些常用的Git指令,一以便查用。
初始
1 | # 第一次安裝git之後,先設定自己的名字與信箱,因為Git用名字與信箱來分辨貢獻者 |
远端数据库操作
1 | git pull |
Git记录查看
1 | # 显示git flow |
Git分支(branch)
1 | git branch # 列出本地分支(前方有星號的為目前所在分支) |
stash
还未提交的修改内容以及新添加的文件,留在索引区域或工作树的情况下切换到其他的分支时,修改内容会从原来的分支移动到目标分支。
但是如果在checkout的目标分支中相同的文件也有修改,checkout会失败的。这时要么先提交修改内容,要么用stash暂时保存修改内容后再checkout。
stash是临时保存文件修改内容的区域。stash可以暂时保存工作树和索引里还没提交的修改内容,您可以事后再取出暂存的修改,应用到原先的分支或其他的分支上。
使用時機:
- 你不得不修改一個緊急Bug,你可以先把目前工作目錄的變更丟到stash,這時候你的工作目錄和上次剛提交內容的狀況一樣,等到修完Bug後再把stash中剛剛做到一半的東西還原以繼續
- 今天的工作時間結束後,還有未完成的部分
- pull到一個不乾淨的樹而不用merge
1 | git stash # 將目前把目前工作區的修改丟到stash裡 |
Git合併(merge)
1 | git merge [branch/commit] |
產生衝突時:先用git diff/status查看,修改后再提交
Git比較
1 | git diff [commit] # 比較指定版本和當前目錄的差別(不包含staging area)(預設為HEAD) |
Git标签
Git可以使用2种标签:轻标签和注解标签。打上的标签是固定的,不能像分支那样可以移动位置。
- 轻标签
- 添加名称
- 注解标签
- 添加名称
- 添加注解
- 添加签名
一般情况下,发布标签是采用注解标签来添加注解或签名的。轻标签是为了在本地暂时使用或一次性使用。
1 | # 添加轻标签 |
改写提交
1 | # e.g. |
模式说明:
| 模式名称 | HEAD的位置 | 索引 | 工作树 |
|---|---|---|---|
| soft | 修改 | 不修改 | 不修改 |
| mixed | 修改 | 修改 | 不修改 |
| hard | 修改 | 修改 | 修改 |
第三方平台使用
添加SSH Key到远端服务器
- 打开terminal,输入命令
ls -al ~/.ssh,检查是否显示有id_rsa.pub或者id_dsa.pub存在,如果存在请直接跳至第3步。 - 在bash中输入ssh-keygen -t rsa -C ”yourEmail@example.com”
- 打开id_rsa.pub文件,并且复制全部内容
- 打开第三方平台账户,添加SSH Keys
就此,就可以使用ssh链接免密码上传本地仓库到远端仓库,或反之。
References
猴子都能懂的git入门(讲得蛮清晰也很完整的一个教程)