动机
久仰Git版本管理的大名,自己在做一些项目时也发现版本的管理是个头疼的问题。因此作为一项硬技能,有必要好好学一下。
Git原理
首先理解一下工作区、版本库的概念。
工作区:仓库目录下除
.git之外的,用户进行一系列新建、修改文件的区域。-
版本库:仓库目录下的一个(隐藏)目录
.git,这个就是Git的版本库。- 版本库中有暂存区(
stage或index)、(自动创建的)master分支和指向该分支的指针HEAD。
- 版本库中有暂存区(

创建版本库
在目标目录下执行git init,把目标目录变为Git可以管理的仓库。执行后便可发现当前目录自动生成了一个.git目录,这个目录是Git来跟踪管理版本库的。
没事不要手动修改这个目录里面的文件,不然改乱了,就把
Git仓库给破坏了。
执行git add <file>,把文件添加到暂存区。
执行git commit -m "message",把暂存区的文件提交。-m后面输入的是本次提交的说明,当需要查看历史记录时显示的就是这个说明,所以要写得有意义些,便于区分。
可以一次add许多文件到缓存区,如
git add file1.txt, file2.txt,然后一次性commit提交到仓库中。
git add .:add所有文件的所有变化,在Git 2.x中与git add -A等效(在Git 1.x中不等效,区别在于git add .不包含文件删除操作,可参见Stackoverflow)
几个重要且常用的命令
-
git status:查看当前仓库状态信息。 -
git ls-files:列出Git跟踪了哪些内容。 -
git rm --cached <file>:如果一些文件已经被track了,但实际上并不想track,可以使用该命令将其移去。参考
使用.gitignore文件
.gitignore为一纯文本文件,显式地告诉Git哪些文件不要被track,可参见bmcblog。一个文件示例如下:
1
2
3
4
5
6
7
# Binaries for programs and plugins
*.exe
*.dll
*.so
# Dependency directories
vendor/
版本管理
撤销在暂存区的内容
已经执行了git add把文件加到了暂存区,但是想撤销这次操作,可使用:参考
1
git reset
回退版本
该操作把分支内容直接回退回老版本。
Git版本回退一般有两种方式:
- 使用
HEAD标识。在Git中,用HEAD表示上一次commit后的版本1
git reset --hard HEAD - 使用
commit id。git log和git reflog可获取历史版本的commit id,使用1
git reset --hard <commit id>
可以恢复到对应的版本。
commit id不用写全,一般写前面5位就可以了,Git会自动补全。使用
git reflog查看commit id
git reset只会回滚modified的文件,新增的文件在reset后仍存在。
几个重要且常用的命令
-
git log:查看历史修改记录。-
git log --pretty=oneline::历史记录简洁输出。 - 输出的前面一串数字是
commit id(版本号),是标识commit版本的依据。 - 当回退到旧版本时,使用该命令将只会输出该旧版本之前的历史记录(像是时光穿梭到了旧版本对应的时刻),如果想要查看包含以往每一次的操作,可使用下面的这条
git reflog命令。
-
-
git reflog:查看使用过的每一次命令以及对应的版本号。
分支管理
Git分支原理
Git把每次的提交串成一条时间线,一条时间线就是一个分支,分支间的切换实质上是HEAD指向的切换。详见廖雪峰-创建与合并分支。Git中鼓励大量使用分支。 
创建与切换分支
- 创建分支:
git branch <branch_name> - 切换至分支:
git checkout <branch_name>或git switch <branch_name> - 创建并切换至该分支(等效与上述两条命令):
git branch -b <branch_name>或git switch -c <branch_name> - 查看当前所有分支:
git branch- 当前分支前会标有星号。
合并分支
使用git merge <branch_A>将指定分支branch_A合并到当前分支上。默认合并模式是Fast-forward模式。Fast-foward模式可以无痛作用于在一个分支上进行单线延伸后的合并。
- 如果执行了fast forward,开发者根本看不到被合并的分支,就像在
master上直接commit一样(参考-segmentfault)。- 合并分支时,加上
--no-ff参数就可以用普通模式合并,即git merge --no-ff <branch_A>
合并过程中的冲突解决
当从同一节点分叉出的两个分支分别在同一位置做了不同的修改,那么直接git merge将提示存在文件出现冲突。此时必须手动解决冲突后再提交,具体操作为:
- 直接在本地打开提示冲突的文件,在发生冲突的地方进行编辑,解决冲突;
git merge的合并冲突结果会直接反映在工作区,也就是说在工作区冲突文件对应位置处增加了显式的提示,具体格式为:用<<<<<<<,=======,>>>>>>>标记出不同分支的内容,比如1 2 3 4 5
<<<<<<< HEAD Creating a new branch is quick & simple. ======= Creating a new branch is quick AND simple. >>>>>>> feature1
- 进行正常的提交操作,即依次进行
git add和git commit即可。
删除分支
使用git branch -d <branch_name>删除指定分支。
分支管理策略
实际开发中,应该按照几个基本原则进行分支管理:
-
master分支应该是非常稳定的,始终拥有(存档)着最稳健的版本; -
dev分支是不稳定的,有了稳定的功能后再合并到master分支上; - 团队合作时,每个人都在
dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并。
“修改操作”的复制
想象一个场景,master分支上有一个bug,于是在master分支上创建临时分支issue-101,然后在issue-101分支上修复后提交(对应<commit_id>),然后将切换到master分支,将issue-101分支合并。master分支上的bug修复了,但是因为dev分支是从早期master分支分出来的,所以也存在这个bug,这个时候可以切换到dev分支,执行
1
git cherry-pick <commit_id>
将issue-101在<commit_id>对应的那次提交里所作的“修改操作”复制到dev分支上。dev分支上的那个bug也就被解决了。
这里有一个可视化交互的
Git分支教程,挺有意思。
标签管理
创建标签
Git的标签(tag)实际上就是一个指向某个commit的指针,与commit_id作用实际上是一样的,但是更适合人类记忆。另外,对应里程碑性的提交,也可以特意为其打上一个tag。打tag的方法为
1
git tag <tag_name>
默认tag是打在最新提交的commit上的。如果想要为某一个commit_id打上tag,可以
1
git tag <tag_name> <commit_id>
查看标签
使用git tag查看当前所有标签。标签不是按时间顺序列出,而是按字母排序的。 使用git show <tag_name>查看标签信息(该标签对应的commit_id、提交作者、日期、所作的修改等)。
操作标签
使用git tag -d <tag_name>删除标签。 使用git push origin <tag_name>将标签对应的提交推送到远程。
远程仓库
初次使用
Windows
初次使用自己的远程仓库需要创建SSH key来在github上进行Git仓库托管。 (注:Windows下主目录为C:\Users\用户名\)
Ubuntu
同样需要创建SSH key来在github上进行Git仓库托管。 (注: Ubuntu的ssh文件在~/.ssh)

添加远程库
当先有本地仓库,希望同步到远程仓库时:
- 在github上新建一个远程仓库;
- 把本地仓库和远程仓库关联;
1
git remote add origin git@github.com:<account_name>/<repo_name>.git
这里的
origin是设定的远程库的名字,后面跟的是仓库的地址。origin是Git默认的叫法,也可以改为其他名字。 - 把本地库的所有内容推送到远程库。
1
git push -u origin master即将本地
master分支的内容推送到远程库origin的master分支上(在本地master分支推送就推到远程的master分支上,在本地dev分支推送就对应推送到远程的dev分支上)。由于初次提交时,远程库是空的,这里还加了参数-u,它把本地的master分支和远程的master分支关联起来(注意这里有一个本地分支和远程分支的关联问题,后面会提到多次),简化后续推送的命令。在以后的推送,只需1
git push origin master
克隆远程仓库
当已有远程仓库,希望克隆到本地时:
1
git clone <repo_addr>
当从远程仓库克隆时,在下载文件的同时,也自动把本地的master分支和远程的master分支关联起来了,并且,远程仓库的默认名称是origin。
github为仓库提供了多个地址(https协议、ssh协议),不过优先选用ssh协议,速度更快而且方便。比如
1
git clone git@github.com:ZhangGe6/onnx-modifier.git
- 可能遇到的问题:fatal: Could not read from remote repository. Please make sure you have the correct access rights and the repository exists.
- 解决:检查本地电脑是否已经创建SSH key(新电脑有时会忘记)。
远程克隆别人的仓库时,默认只能看到master分支,如果需要看到其他分支,比如dev分支时,则需要
1
git checkout -b dev origin/dev
远程分支管理
查看远程库的信息:
使用git remote查看远程库的信息, 加上-v参数可以显示更详细的信息,即pull和push的权限。
1
2
3
4
5
$ git remote
origin
$ git remote -v
origin git@github.com:michaelliao/learngit.git (fetch)
origin git@github.com:michaelliao/learngit.git (push)
推送分支
推送分支,就是把指定分支上的所有本地提交推送到远程库,使用
1
git push origin <branch_name>
即将<branch_name>指定的分支推送到远程对应分支。
注:实际推送哪个分支与当前在哪个分支无关。也就是说,即便当前处于
dev分支,执行git push origin master会把本地的master分支推送到远程master分支,而不是远程dev分支云云。
抓取分支
抓取分支,就是把远程库中的(当前工作区所在分支在远程库中关联的)分支直接抓取下来到工作区,使用
1
git pull
如果出现
1
2
3
4
There is no tracking information for the current branch.
...
If you wish to set tracking information for this branch you can do so with:
git branch --set-upstream-to=origin/<branch> <cur_local_branch>
则按照提示执行
1
git branch --set-upstream-to=origin/<remote_branch> <cur_local_branch>
即将远程库中的remote_branch分支和本地cur_local_branch关联起来,以后在cur_local_branch下执行git pull操作时都会从关联的remote_branch进行抓取。
冲突解决
推送冲突解决
当两次push发生冲突(比如两次push在对应同一个位置做了不同的修改)时,则需要把上一次的推送pull下来,在本地解决冲突后,重新push上去。
抓取冲突解决
抓取冲突的解决,实质上就是本地的冲突解决了。在对应冲突的地方做好增删保留即可。
协作:
致谢
以上内容根据廖雪峰老师的Git教程总结摘取而来。廖雪峰老师的这份教程使用具体实例,深入浅出地讲解了常用的Git的原理和常用操作,非常适合初学者,跟着实际操作一番后收获颇丰。感谢廖老师的工作!
其他操作
以下是一些平时使用中积累的其他操作。
Git 版本升级(至最新版本)
- 查看当前
Git版本:git --version - 升级
Git版本:- 添加源:
sudo add-apt-repository ppa:git-core/ppa - 更新安装列表:
sudo apt-get update - 升级
Git:sudo apt-get install git
- 添加源:
Partial Clone
使用Github镜像Gitee
亲测clone那真是快得不是一点两点..
Git状态码
-
M: modified -
A: added -
D: deleted -
R: renamed -
C: copied -
U: updated but unmerged
Git从历史记录中删除大文件和查看文件大小
查看个人在github的所有comment
点击这个神奇的链接: https://github.com/notifications/subscriptions?reason=comment
trouble shoot
ssh: Could not resolve hostname github.com: Name or service not known
1
ssh -T git@github.com