本文整理了项目团队协作过程中常规的Git操作及相关知识点。
【注】 【本文借鉴/参考资料】https://www.liaoxuefeng.com/wiki/896043488029600
1. Git的基本概念及分布式/集中式版本控制系统的简介
1.1. 什么是Git
Git 是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目。是目前世界上最先进的分布式版本控制系统。
Git 诞生于2005年,是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。随着2008年GitHub
网站上线,为开源项目免费提供Git存储,无数开源项目开始迁移至GitHub,包括jQuery,PHP,Ruby等等。
Git 与常用的版本控制工具 CVS
, Subversion
等不同,它采用了分布式版本库的方式,不必依赖服务器端软件支持。
Git 不仅仅是个版本控制系统,它也是个内容管理系统(CMS)/工作管理系统等。
Git的官方教程文档: https://git-scm.com/book/
1.2. 集中式与分布式版本管理系统的特征和区别
上一代主流版本控制系统如:CVS
/SVN
都是集中式的版本控制系统,而Git
是分布式版本控制系统。
1.2.1. 集中式版本控制系统;关键词:中央服务器
使用集中式版本控制系统,版本库是集中存放在中央服务器的。而参与项目研发的人员用的都是自己的电脑;所以每次工作的主要流程是这样:
- 从中央服务器取得最新的版本
- 本机开发/编辑
- 推送本机内容至中央服务器
中央服务器就好比是一个图书馆;如果有人要修订一本书的内容,必须先从图书馆借出书,然后进行编辑;编辑完成后再送回图书馆。
集中式版本控制系统最大的局限是必须联网才能工作,所以一般比较适合局域网内工作。随着“社会化编程”的概念兴起以、项目技术/模块的复杂化以及研发团队体量的增加,这一局限就越来越明显。在遇到互联网的速度非常慢的时候,效率会非常低。如中央服务器发生故障发生宕机甚至数据损坏的情况,更是会对项目造成非常大的损失。
1.2.2. 分布式版本控制系统;关键词:脱机独立版本库
分布式版本控制系统没有”中央服务器”的概念,每个开发者的电脑上都是一个完整的版本库。这样,在开发者工作的时候,就不需要时刻联网了,因为版本库就在开发者的工作机上。
既然每个人电脑上都有一个完整的版本库,那多个开发者如何协作呢?比方说一个开发者在自己的开发机上更改了文件A,另一位开发者也在自己的的开发机上更改了文件A;这时,两位开发者之间只需把各自的修改推送给对方,就可以互相看到对方的修改了。
和集中式版本控制系统相比,分布式版本控制系统的安全性要高很多。因为每个开发机都有一个完整的版本库,某一个开发者的电脑坏掉了不要紧,从其他开发者那里复制一个就可以了。而集中式版本控制系统的中央服务器要是出了问题,项目则无法继续进行。
在实际使用分布式版本控制系统的时候,通常情况下很少在两人之间的电脑上推送版本库的修改。因此,分布式版本控制系统通常也有一台充当”中央服务器”的设备,但这个服务器的作用仅仅是用来方便”交换”或”同步”所有分机的修改;没有它大家也一样可以管理项目文件版本,只是交换修改不方便而已。
与SVN
等集中式版本控制系统相比,Git
除了具备不必联网的优势,还有极其强大的分支管理方案,将SVN
等远远抛在了后面。
此外,CVS
作为最早的开源而且免费的集中式版本控制系统,直到现在还有不少人在用。由于CVS自身设计的问题,会造成提交文件不完整,版本库莫名其妙损坏的情况。同样是开源而且免费的SVN
修正了CVS
的一些稳定性问题,是目前用得最多的集中式版本库控制系统。
分布式版本控制系统除了Git
以及促使Git
诞生的BitKeeper
外,还有类似Git
的Mercurial
和Bazaar
等。这些分布式版本控制系统各有特点,但目前最快、最简单也最流行的依然是Git
。
除了分散式的特点之外,Git 的设计也针对性能,安全性和柔软性作了特别优化。
1.2.3. 版本控制系统有一个共同的局限: 仅适用于文本格式文件的版本跟踪而不适用于二进制文件的版本管理
目前所有的版本控制系统,只能跟踪文本文件的改动,比如TXT文件,网页,所有的程序代码等等,Git也不例外。版本控制系统记录项目每次的改动,可以精确地记录改动的位置和内容;比如在第5行加了一个单词“Linux”,在第8行删了一个单词“Windows”。
而图片、视频这些二进制文件,虽然也能由版本控制系统管理,但没法跟踪文件的变化,只能把二进制文件每次改动串起来,也就是只知道图片从100KB改成了120KB,但到底改了哪些内容,版本控制系统无法记录。
不幸的是,Microsoft的Word/Excel格式文件是二进制格式,因此,版本控制系统是没法跟踪Word/Excel文件的改动的。如果要真正使用版本控制系统,就要以纯文本方式编写文件。
因为文本是有编码的,比如中文有常用的GBK编码,日文有Shift_JIS编码,如果没有历史遗留问题,强烈建议使用标准的UTF-8编码,所有语言使用同一种编码,既没有冲突,又被所有平台所支持。
2. Git仓库的常用操作命令及重要概念解析
2.1. Git仓库的创建及初始化: git init
mkdir test_repo
cd test_repo
git init
或
//创建一个空的仓库
git init --bare test_repo
在这个目录下会有一个隐藏的目录:.git
.这个目录是Git来跟踪管理版本库的,原则上不要手动修改这个目录里面的文件,否则会破坏Git仓库。
2.2. Git仓库的常规版本维护命令
2.2.1. 提交项目文件至本地仓库: git add 和 git commit
//将文件添加至暂存区
git add <file>
//将文件提交到版本库
git commit -m <message>
如:
git add file1.txt
git add file2.txt file3.txt
git commit -m "add 3 files."
2.2.2. 工作区、暂存区和版本库
2.2.2.1. 工作区
工作区(Working Directory
)即项目开发代码所在的目录,里面包含:.java
、.xml
、.yml
、.properties
、.pom
等代码/配置文档等文本文件。
如位于我们常用的IDE-workspace的项目文件夹即一个个项目的工作区:
2.2.2.2. 版本库
工作区有一个隐藏目录.git
,这个不算工作区,而是Git的版本库。这里有branches
、refs
等仓库属性数据,如Git为我们自动创建的第一个分支master,以及指向master的一个指针叫HEAD。项目工作区的每次修改和提交将写入这些路径。
2.2.2.3. 暂存区
Git
和其他版本控制系统如SVN
的一个不同之处就是有暂存区的概念。
暂存区(stage
或index
)位于版本库的路径下。
2.2.2.4. 工作区 → 暂存区 → 版本库 的推送过程
上文提到,将工作区的修订文件添加至Git版本库中的时候,是分两步执行的:
git add
将添加需要记录的变更,实际上就是将文件修改添加到暂存区;git commit
提交更改,实际上就是将暂存区的所有内容提交到版本库的当前分支。
这个过程可以简单理解为:需要提交的文件修改add
到暂存区,然后,一次性commit暂存区的所有修改至版本库选定分支。这样完成一次版本修改的提交。
2.2.3. git status 查看版本变更状态
git status
命令用于显示工作目录和暂存区的状态。
使用此命令能看到那些修改已提交到暂存区, 哪些没有提交到暂存区; 哪些文件没有被Git tracked到。git status
不显示已经commit到项目历史中去信息。需要查看项目历史的信息要使用git log
原命令。
为了了解一次更新在工作区/暂存区/版本库储存的过程,我们可以在工作区新建一个文本文件,经历一下此过程:
- 首先创建一个名为LICENSE的文档:
此时以git status
命令,查看当前版本库状态:
此时Git
显示,工作区新建了一个名为LICENSE的文档而未添加到暂存区,所以它的状态是Untracked。
用
git add
命令添加新增文件至暂存区:git add LICENSE
此时以git status
命令,查看当前版本库状态:
现在,工作区的更新已添加至版本库暂存区。
用
git commit
命令提交更新至版本库:git commit -m "新增LICENSE文件"
此时以git status
命令,查看当前版本库状态:
提交至版本库之后,如果工作区没有做任何修改,那么工作区就是”干净”的。
2.2.4. 版本的回顾/提交日志: git log
git log
命令显示从最近到最远的提交日志。
如果commit次数比较多,输出信息可读性差,可以试试加上--pretty=oneline
参数:
git log --pretty=oneline
每提交一个新版本,实际上Git
就会把它们自动串成一条时间线。如果使用可视化工具查看Git历史,就可以更清楚地看到提交历史的时间线:
2.2.5. 版本的回退: git reset
如果一个文件修改提交至版本库而想退回历史版本,可以使用git reset
命令退回选定版本:
git reset --hard <commit id>
也就是说,版本的回退必须提供需回退至那一次commit的commit id
。
Git的版本回退速度非常快,因为Git在内部有个指向当前版本的HEAD指针;当项目回退版本的时候,Git仅仅是把HEAD从指向相应的commit id
。
如果需要回退的版本是近期提交的版本且确实不记得
commit id
,还有另一个方法: 在Git中,用HEAD
表示当前版本,也就是最新的提交1094adb...
,当前版本的上一个版本就是HEAD^
,上上一个版本就是HEAD^^
,当然往上100个版本写100个^
比较容易数不过来,所以写成HEAD~100
。但是相对来说这样操作可能会造成版本定位出错,实际项目中应尽量避免用这种方法进行回退。
2.2.6. 版本的历史命令查看: git reflog
git reflog
通常用于查看命令历史,以及相应提交的版本。
2.2.7. 版本的对比: git diff
比较工作区与暂存区:
git diff
比较暂存区与最新本地版本库:
git diff --cached <文件名>
查看工作区文件与版本库中最新版本的区别:
git diff HEAD -- <文件名>
比较工作区与指定commit id
的差异:
git diff <commit id> <文件名>
比较暂存区与指定commit id
的差异:
git diff --cached <commit id> <文件名>
查看版本库中两个commit id
版本的区别:
git diff <commit id> <commit id>
2.2.8. 修改的丢弃: git checkout –
现在我们对LICENSE
文件新增一行修改:
现在假设,我们做了上述一次编辑之后;我们反悔了,而不想将其提交至仓库。
此时可以用git checkout -- <file>
命令,丢弃工作区的修改;让这个文件回到最近一次git commit
或git add
时的状态。
这里有两种情况:
修改同步情况 | 撤回效果 |
文件自修改后还没有被`add`到暂存区 | 回到文件最近一次`commit`至版本库的状态 |
文件自修改后已`add`到暂存区;在此基础上又做了一次修改 | 回到文件最近一次`add`到暂存区的状态 |
【注意】这里的
git checkout -- <file>
与git checkout <branch>
命令非常相似,但两者的意义有很大的区别。
所以使用checkout file命令时一定注意不能忘记--
符号,表示丢弃一个文件的修改。
而git checkout <branch>
命令的作用则是将版本库库切换到另一个分支。
我们尝试使用git checkout -- <file>
命令:
此时我们打开LICENSE
文件,发现内容以回退到更改之前的状态。
2.2.9. 已暂存修改的撤销(unstage): git reset HEAD
现在我们再对LICENSE
文件新增一行修改并add
到暂存区:
此时,文件的修改已add
到暂存区。现在我们想要撤销暂存区的修改,要怎样做呢?
这时我们可以使用git reset HEAD <file>
命令来达到撤销暂存区修改的目的:
git reset
命令既可以回退版本,也可以将暂存区的修改回退到工作区。当我们用HEAD
时,表示最新的版本。
现在再用git status
查看一下,可以发现暂存区是干净的,工作区有修改:
如果还要在此基础上丢弃工作区的修改,即使用上一步的git checkout -- <file>
命令达到目的:
此时我们可以发现,已暂存的修改已完全撤销:
2.2.10. 已commit文件的移除: git rm
现在我们新建一个文件: to_remove
,并commit
至版本库:
现在,我们在工作区不再使用to_remove
这个文件。所以,我们在工作区直接将这个文件删除:
此时使用git status
命令,查看版本库状态:
如果这次文件的删除也需要同步到版本库中,使用git rm <file>
命令并commit
即可(如果是先在工作区删除文件,使用git rm <file>
和git add <file>
命令的效果是一样的;因为在Git中,添加/编辑和删除都可以定义为一次修改操作):
git rm to_remove //或 git add to_remove
git commit -m "removed file to_remove"
如工作区的文件为误删,版本库中仍需保留这个to_remove
文件,我们仍然可以用git checkout -- <file>
命令来恢复误删文件。
git checkout -- <file>
命令其实是用版本库里的版本替换工作区的版本。无论工作区是修改还是删除,都可以”一键还原”。
2.3. Git远程仓库的相关维护
远程仓库即可用作备份,也可用作多人协作的统一版本仓库。
2.3.1. 为工作区添加/关联远程仓库: git remote add <远程仓库别名> <远程仓库地址>
如:
git remote add remote_repo git@106.15.77.211:/srv/git_repositories/test_repo.git
2.3.2. 查看远程库的信息: git remote
要查看远程库的信息,用git remote
命令。如:
用git remote -v
可以显示更详细的信息:
括号中的fetch
和push
分别表示这个地址是可以抓取或推送的远程仓库地址。如果没有推送权限,就看不到标记push
的地址。
2.3.3. 将本地仓库内容推送至远程仓库: git push <远程仓库别名> <远程仓库分支>
如:
git push -u remote_repo master
-u
参数的作用: 在我们第一次推送远程仓库master分支时,加上-u
参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会将本地的master分支和远程的master分支关联起来;在以后的推送或者拉取时就可以简化命令。
2.3.4. 从仓库抓取/仓库的克隆: git clone <本地/远程仓库地址>
如 :
git clone git@106.15.77.211:/srv/git_repositories/test_repo.git //克隆远程test_repo.git仓库
-
git clone /srv/git_repositories/test_repo.git //克隆本地test_repo.git仓库
Git
支持多种协议,包括https
,但通过ssh支持的原生git
协议速度最快。
2.3.5. 远程仓库分支的获取: git pull <远程仓库别名> <远程仓库分支>
多人协作时,开发者们都会往master
和dev
分支上push
各自的修改。
我们假设一种情况:
开发者A已经向项目远程仓库dev
分支推送了他的提交:
而此时开发者B也对同样的文件作了修改,并试图推送:
此时发现推送失败:
这是因为开发者A的最新提交和开发者B试图推送的提交有冲突。
解决办法也很简单,Git已经提示我们,先用git pull
把最新的提交从origin/master
抓下来;然后,在本地合并,解决冲突;再推送:
git pull
成功,但是合并有冲突,需要手动解决.
解决的方法和【分支管理】中的解决冲突完全一样。解决后,提交;再push
:
其中
<<<<<<< HEAD
到=======
中间的内容是local提交的。
=======
到>>>>>>> commit-id
是远程仓库中的内容。
此时可以看到,push
成功:
2.4. Git分支(branch) 的维护
分支在实际项目中有什么用呢?其作用是并行开发。
在项目不同功能模块并行开发的过程中,往往同时存在多个最新代码状态。此时,通过创建多个分支,在每个分支中各自进一步进行完全不同的作业。待这些分支的作业完成之后再合并起来(通常以Git默认创建的master
主干分支为中心,待分支作业完成再统一合并到master
主干分支)。
通过灵活运用分支,可以让多人同时进行并行开发。
几乎每一种版本控制系统都以某种形式支持分支。使用分支意味着开发者可以从开发主线上分离开来,然后在不影响主线的同时继续工作。在很多版本控制系统中,这是个昂贵的过程,常常需要创建一个源代码目录的完整副本,对大型项目来说会花费很长时间。而这个过程中,Git和许多其他版本控制系统不同,分支的创建与切换的速度非常快。这就是HEAD
指针的意义:
2.4.1. Git分支(branch)和指针(HEAD)的概念
2.4.1.1. branch
分支代表了一条独立的开发流水线。
Git
中的分支,其实本质上仅仅是个指向 commit
对象的可变指针。
2.4.1.2. HEAD
简单来说,HEAD
就是当前活跃分支的游标,也就是一个版本库已选分支的当前位置(commit
)。当前commit
在哪里,HEAD
就在哪里;这是一个永远自动指向当前commit
的引用。
在 Git
中,HEAD
对应的是一个名为.git/HEAD
的文件。
可以看到HEAD
指向master
分支。
接下来再来看一下refs/heads/master
中的内容:
master
分支也只是一个存放40位sha-1值的文件而已,正是当前分支所指向commit
的sha-1值。
HEAD
与branch
两者的区别 :
HEAD
指针可以指向快照节点(commit
),也可以指向分支(branch
)。当指向branch
时,提交后会和branch
指针一起向后移动;当不指向branch
时,提交时则会在一个detached
状态。分支(
branch
)永远指向 分支上的最新提交(commit
) 。
2.4.2. Git仓库分支的查看和创建: git branch
git branch
命令会列出版本库所有分支,当前所在分支前面会标一个*
号。如:
git branch <新建分支名称>
命令可以新建一个分支。如:
2.4.3. Git仓库分支的切换: git checkout <分支名称>
git checkout <分支名称>
命令可以将当前所在分支切换至另一个分支:
git checkout -b <新建分支名称>
命令,checkout
加上-b
参数表示创建新分支并切换至新建分支;相当于以下两条命令:
git branch <新建分支名称>
git checkout <新建分支名称>
切换到另一个分支后,对工作区的修改和提交就只针对当前这个分支了。比如新提交一次后,当前分支的指针即向前移动一步,而其余分支的指针则不变。
通常情况下,不同的模块开发工作可以在新建的其他分支上完成。待每个分支的进度完成之后,分别合并(
merge
)至master主干分支即可。合并完成后,这些分支甚至可以删除。
在这里我们可以试着在dev_002
这个分支上,对LICENSE
这个文件提交一些改动:
此时,git status
已显示识别到改动:
我们将这次改动直接提交到当前分支:
dev_002
分支的工作完成。接下来,我们切换回到master
分支:
再查看一次LICENSE
文件,发现刚才添加的内容不见了:
这是因为上一步的改动是提交在dev_002
分支上,而master
分支此刻的提交点并没有变。
2.4.4. Git仓库分支的合并: git merge
git merge <待合并分支名称>
命令用于合并指定分支到当前分支。
现在,我们要将上一步中dev_002
分支的作业合并到master
分支上:
合并后,再查看LICENSE
的内容,就可以看到,现在文件和dev_002
分支的最新提交是完全一样的:
因为在Git中,对分支的创建、合并和删除等维护都非常快。
所以使用Git做项目版本仓库时,比较推荐各开发者使用分支完成相关的分工任务;合并后再删除相应分支。这和直接在master
分支上处理的工作效果是一样的,但过程更安全。
-
用
git log --graph
命令可以看到分支合并图。
2.4.5. Git仓库分支的删除: git branch -d <分支名称>
合并至主干分支后,就可以放心地删除dev_002
分支了:
2.4.6. Git仓库分支合并时冲突的解决
在合并的分支各自有修改时,Git则无法自动合并分支。此时我们必须首先解决冲突。解决冲突后,再提交,合并完成。
在完成上一步dev_002
分支的作业并合并至master
主干分支的任务;我们接下来在dev_001
开展提交开发作业:
提交成功后,我们同样并计划合并至master
主干分支:
切换至master
分支后,在master
分支上对LICENSE
文件又添加了一行修改:
提交本次master
分支的修改:
现在,我们要将dev_001
分支的作业合并至master
分支:
这时我们发现,merge
命令没有执行成功。
这是因为,现在master
分支和dev_001
分支各自都分别有新的提交,变成了这样:
此时,git status
也可以告诉我们冲突的文件:
也可以直接查看LICENSE
文件的内容:
Git
用<<<<<<<
,=======
,>>>>>>>
标记出了文件在不同分支的内容。
我们手动修改文件后再保存:
再次提交修改:
提交成功后,master
分支和dev_001
分支变成了如下状态:
使用git log
命令也可看到两个分支合并的情况:
解决冲突就是将Git合并失败的文件手动编辑为我们希望的内容,再提交。
2.4.7. Git分支管理的策略
在实际开发中,我们应该按照几个基本原则进行分支管理:
- 首先,
master
分支通常应该是非常稳定的,也就是仅用来发布/整合新版本;平时不在上面直接做改动; - 各个模块作业的更新都在各自的
dev
分支上;也就是说,dev
分支是不稳定的。到分支开发完成后,如1.0版本发布时,再将dev
分支合并到master
分支上;然后在master
分支发布1.0版本。
这样,开发者们平时在dev
分支继续研发,随时向主干分支合并即可:
2.4.8. Git分支合并的两种模式: fast-forward 、–squash 和 –no-ff
fast-forward
: 通常,合并分支时默认使用fast-forward
方式。即当条件允许的时候,Git直接将HEAD
指针指向合并分支的头,完成合并。属于”快进方式”,不过这种情况如果删除相关分支,则会丢失分支信息;因为在这个过程中没有创建commit
。
git merge --squash
是用来将一些不必要commit
进行压缩。比如说,一个feature
在开发的时候写的commit
很乱,那么我们合并的时候不希望把这些历史commit
带过来;于是使用--squash
进行合并,此时文件已经同合并后一样了,但不移动HEAD
,不提交。需要进行一次额外的commit
来”总结”一下,然后完成最终的合并。
--no-ff
指的是强行关闭fast-forward
方式。
--no-ff
:不使用fast-forward方式合并,保留分支的commit历史
--squash
:使用squash方式合并,把多次分支commit历史压缩为一次
2.4.9. 工作现场的暂存: git stash
git stash
命令能够将当前所在分支所有未提交的修改(包含: 工作区
和暂存区
)保存至堆栈中,用于后续恢复当前工作目录。
经常有这样的事情发生: 当开发者正在进行项目中某一部分的工作,里面的东西处于一个比较杂乱的状态;而突然有件事情需要开发者切换到另一个分支上进行相关的工作(如:发现一个bug需要到master
分支立即修复 )。
此时,当前分支上已进行了一半的工作且暂未提交;而且工作只进行到一半,暂时没办法马上法提交,否则之后回来可能无法回到这个工作点。
解决这个问题的办法就是git stash
命令。可以将当前的工作现场”储藏”起来,等其他事项解决完成后,重新恢复现场继续工作。
“储藏”可以获取开发者工作目录的中间状态,也就是你修改过的被追踪的文件和暂存的变更(即: 工作区
和暂存区
的文件修改);并将它保存到一个未完结变更的堆栈中,随时可以重新应用。
接下来我们可以简单尝试一下这个过程:
首先我们回到test_repo
这个项目,切换到dev_001
这个分支,对LICENSE
这个文件进行一部分修改:
修改未完成,还没有add
或commit
;这时我们突然收到一个通知,说项目有一个bug需要立即修复。这时我们通常会切换回master
分支或新建一个分支来修复这个bug。
但是在当前这个dev_001
分支,相关的改动未做提交,直接切换至其他分支是不行的:
而dev_001
分支上的工作只进行到一半,还没法提交。当前的bug却必须要立即处理。此时该怎么办?git stash
功能,可以解决这个问题:
现在,用git status
查看工作区,工作区就变成”干净”的状态,可以放心地创建分支来修复bug。
首先确定要在哪个分支上修复bug。假定需要在master
分支上修复,就从master
创建临时分支:
在新分支上修复完bug之后,合并到master
分支即可:
接下来,即可重新回到dev_001
分支继续完成开发任务:
现在dev_001
分支的工作区是”干净”的。我们需要做的是,将上一步”stash”的工作现场”找回”:
2.4.9.1. 查看暂存的工作: git stash list
使用git stash list
命令,可以查看当前分支已暂存的全部记录:
2.4.9.2. 已暂存的工作的恢复: git stash apply stash@{<_index_>} / git stash pop
恢复已暂存的工作,有两种方式:
git stash apply stash@{<_index_>}; //如: git stash apply stash@{0};
git stash drop
或
git stash pop //恢复的同时即删除stash
如果还原暂存内容之前,有从远程仓库pull内容到本地,可能会出现:
Auto-merging <file>
CONFLICT (content): Merge conflict in <file>
提示,即系统自动合并修改的内容,但是其中有冲突,需要解决其中的冲突。
打开冲突的文件,通常会看到类似如下的内容:其中
Updated upstream
和=====
之间的内容就是远程仓库pull下来的内容;====
和Stashed changes
之间的内容就是本地修改的内容。
碰到这种情况,git也不知道哪行内容是需要的,所以要自行确定需要的内容。
接下来再用git stash list
查看,就看不到任何stash内容了:
2.4.10. 版本历史的压缩: git rebase
rebase
操作可以将本地未push
的分叉提交历史整理成直线;
rebase
的目的是使得我们在查看历史提交的变化时更容易,因为分叉的提交需要三方对比。
2.5. Git标签(tag) 的维护
与其他版本控制系统(如: SVN
/ CVS
)相同,Git可以为历史中的某一个提交(commit
)打上标签,以示重要。
比较有代表性的是人们会使用这个功能来标记发布结点(如: v1.0 等等)。
将来无论什么时候,取某个标签的版本,就是把那个打标签的时刻的历史版本取出来。所以,标签也是版本库的一个快照。
Git的标签虽然是版本库的快照,但其实它就是指向某个commit
的指针(与分支的概念很像;但是分支可以移动,标签则不能移动)。
2.5.1. 列出标签: git tag
用命令git show <标签名称>
可以看到标签的描述信息。
2.5.2. 创建标签: git tag <标签名称> -a <tag命名> -m <标签描述信息> <标签所指commit_id>
标签总是和某个
commit
直接关联。如果这个commit
既出现在master
分支,又出现在dev
分支,那么在这两个分支上都可以看到这个标签。
2.5.3. 删除标签: git tag -d <标签名称>
因为创建的标签都只存储在本地,不会自动推送到远程。所以,打错的标签可以在本地安全删除。
2.5.4. 标签改动推送至远程版本库: git push origin
命令
git push origin <标签名称>
可以推送一个本地标签;
命令
git push origin --tags
可以推送全部未推送过的本地标签;
命令
git tag -d <标签名称>;git push <远程仓库别名> :refs/tags/<标签名称>
可以删除一个远程标签。
2.6. Git的其他自定义配置
2.6.1. 忽略特殊文件: .gitignore
很多时候,某些与项目无直接关联的文件会Git工作目录中,但又不能将他们提交到项目版本库。比如保存了数据库密码的配置文件、IDE环境配置文件等等;每次git status都会显示Untracked files …,非常影响工作效率和体验。
其实这个问题解决起来也很简单: 在Git工作区的根目录下创建一个特殊的.gitignore
文件,然后将要忽略的文件名填进去,Git就会自动忽略这些文件。
相关技术选型对应的.gitignore
配置文件可以直接在GitHub-gitignore项目参考:https://github.com/github/gitignore
忽略文件的几个原则:
- 忽略操作系统自动生成的文件,比如缩略图等;
- 忽略编译生成的中间文件、可执行文件等,也就是如果一个文件是通过另一个文件自动生成的,那自动生成的文件就没必要放进版本库,比如Java编译产生的.class文件;
- 忽略你自己的带有敏感信息的配置文件,比如存放口令的配置文件。
-
.gitignore
文件本身也是可以放到版本库里,并且可以对其做版本管理的。