[TOC]
0x00 三步骤四个区五状态 描述:下面所有讨论我们都假设只使用一个分支,也就是主分支master的情况,虽然这种作法并不符合git规范,但是现实情况中绝大部分用户是直接在master分支上进行工作的,所以在这里我们不去引入更加复杂的各种分支的情况,也不涉及标签tag的操作,只讲在最简单的主分支上如何回退。
Git 是一个分布式的版本控制工具,因此远程和本地可以视为两个独立的 Git 仓库,下图是一张经典的 Git 中的数据流与存储级别的介绍,其中储存级别主要包含几部分:
上图是一张经典的 Git 中的数据流与存储级别的介绍,其中储存级别主要包含几部分:
                weiyigeek.top-工作流程
             
 
 
注意: 
工作区有一个隐藏目录.git,这个不算工作区而是Git的版本库(Repository):。 
 
正常情况下我们的工作流就是3个步骤:1 2 3 git add .   git commit -m "comment"   git push   
四个区和其他版本控制管理工具SVN的一个不同之处,多引入了一个暂存区(Stage)的概念
工作区(Working Area) : 
就是你在电脑里能看到的目录,比如我的learngit文件夹就是一个工作区 
 
 
暂存区(Stage) :
Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master,以及指向master的一个指针叫HEAD。 
 
本地仓库(Local Repository)
远程仓库(Remote Repository) 
 
五种状态,以上4个区进入每一个区成功之后会产生一个状态,再加上最初始的一个状态,一共是5种状态1 2 3 4 5 未修改(Origin)    已修改(Modified)  已暂存(Staged) 已提交(Committed) 已推送(Pushed) 
检查状态与对比 如何查看我们进行到上面哪一个区,哪一个状态? 1 2 3 4 5 6 7 8 9 $ git status  $ git add . $ git commit -m "add distributed"  $ git status   
                weiyigeek.top-git status
             
采用git diff可以查看不同区里面的提交的数据,告诉我们更改了什么内容,检查修改的二级命令都相同都是diff,只是参数有所不同。1 2 3 4 5 6 7 8 git diff            git diff --cached   git diff master origin/master  
                weiyigeek.top-git diff
             
总结: 
要随时掌握工作区的状态,使用git status命令。git status告诉你有文件被修改过,用git diff可以查看修改内容。 
 
0x01 撤销与丢弃 撤销修改 了解清楚如何检查各种修改之后,我们开始尝试各种撤销操作。
1 2 3 4 5 6 7 git checkout .   git reset --hard  
1 2 3 4 5 6 git reset  git checkout . git reset --hard 
1 2 git reset --hard origin/master   
1 2 3 git reset --hard HEAD^   git push -f 
_总结_:
以上4种状态的撤销我们都用到了同一个命令git reset –hard,前2种状态的用法甚至完全一样,所以只要掌握了git reset –hard这个命令的用法,从此你再也不用担心提交错误了。 
 
管理修改 为什么Git比其他版本控制系统设计得优秀,因为Git跟踪并管理的是修改,而非文件。你会问,什么是修改? 
示例:1 2 3 4 5 6 7 8 $ echo  "change 1 id"  > readme.txt  $ git add readme.txt   $ git status   $ echo  "change 2 id"  >> readme.txt  $ git commit -m "git tracks changes"   $ git status 
咦,怎么第二次的修改没有被提交?第一次修改 -> git add -> 第二次修改 -> git commit,Git管理的是修改,当你用git add命令后,在工作区的第一次修改被放入暂存区,准备提交,但是在工作区的第二次修改并没有放入暂存区,所以git commit只负责把暂存区的修改提交了,也就是第一次的修改被提交了,第二次的修改不会被提交。
那怎么提交第二次修改呢? 1 2 第一次修改 -> git add -> 第二次修改 -> git add -> git commit -> git status  
丢弃修改 比如:您在您的项目中修改了文件并添加文件到了暂存区,却发现错误需要进行更正;1 2 3 4 5 6 git checkout -- file $ git checkout -- readme.txt    * 一种是readme.txt自修改后还没有被放到暂存区,现在撤销修改就回到和版本库一模一样的状态; * 一种是readme.txt已经添加到暂存区后,又作了修改,现在撤销修改就回到添加到暂存区后的状态(即没有更改)。 
总之,就是让这个文件回到最近一次git commit或git add时的状态。git checkout -- file命令中的--很重要,没有–,就变成了“切换到另一个分支”的命令,我们在后面的分支管理中会再次遇到git checkout命令
1 2 3 4 $ git reset HEAD readme.txt Unstaged changes after reset: M       readme.txt 
_总结_:
场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令git checkout – file。 
场景2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令git reset HEAD file,就回到了场景1,第二步按场景1操作。 
场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,参考版本回退一节,不过前提是没有推送到远程库。 
 
时光机穿梭 描述:当然了在实际工作中,我们脑子里怎么可能记得一个几千行的文件每次都改了什么内容,不然要版本控制系统干什么。git log,你看到的一大串类似3628164...882e1e0的是commit id(版本号),和SVN不一样Git的commit id不是1,2,3……递增的数字,而是一个SHA1计算出来的一个非常大的数字用十六进制表示1 2 3 $git log --pretty=oneline c75b5b8afcff97b3c37c4d57d574b0bc868f6f2f (HEAD -> master, origin/master, origin/HEAD) git study c1ee2c4ec8f7d6a8e9b44f91b5fa59ea3aec13d8 all 
为什么commit id需要用这么一大串数字表示呢? 
每提交一个新版本,实际上Git就会把它们自动串成一条时间线。如果使用可视化工具查看Git历史,就可以更清楚地看到提交历史的时间线;
如果准备把readme.txt回退到上一个版本,也就是“distributed”的那个版本,怎么做呢? 上一个版本就是HEAD^,上上一个版本就是HEAD^^,当然往上100个版本写100个^比较容易数不过来所以写成HEAD~100。1 2 3 4 5 6 7 8 9 10 11 12 13 14 git log  HEAD^   git log  HEAD^^  git log  HEAD^^^   git log  HEAD~3 git reset --hard HEAD^     HEAD is now at ea34578 add distributed   
                weiyigeek.top-gitlog回退版本查看
             
最新的那个版本append GPL已经看不到了!好比你从21世纪坐时光穿梭机来到了19世纪,想再回去已经回不去了,肿么办? 1 2 3 $ git reset --hard 3628164    
Git的版本回退速度非常快,因为Git在内部有个指向当前版本的HEAD指针,当你回退版本的时候,Git仅仅是把HEAD从指向append GPL,改为指向add distributed,然后顺便把工作区的文件更新了;所以你让HEAD指向哪个版本号,你就把当前版本定位在哪。
                weiyigeek.top-HEAD版本回退
             
现在你回退到了某个版本,关掉了电脑,第二天早上就后悔了,想恢复到新版本怎么办?找不到新版本的commit id怎么办? 
答:当你用$git reset --hard HEAD^回退到add distributed版本时,再想恢复到append GPL,就必须找到append GPL的commit id。1 2 3 4 5 $ git reflog ea34578 HEAD@{0}: reset: moving to HEAD^ 3628164 HEAD@{1}: commit: append GPL  ea34578 HEAD@{2}: commit: add distributed cb926e7 HEAD@{3}: commit (initial): wrote a readme file 
_总结_:
HEAD指向的版本就是当前版本,因此Git允许我们在版本的历史之间穿梭,使用命令git reset –hard commit_id。 
穿梭前用git log可以查看提交历史,以便确定要回退到哪个版本,要重返未来用git reflog查看命令历史,以便确定要回到未来的哪个版本。 
删除文件 在Git中,删除也是一个修改操作,我们实战一下,先添加一个新文件test.txt到Git并且提交:1 2 3 4 5 6 7 8 $ git add test.txt $ git commit -m "add test.txt"  [master 94cdc44] add test.txt $ rm test.txt $ git status 
现在你有两个选择,一是确实要从版本库中删除该文件,那就用命令git rm删掉,并且git commit,现在文件就从版本库中被删除了。1 2 3 4 $ git rm test.txt   rm 'test.txt'  $ git rm --cached test.txt   $ git commit -m "remove test.txt"  
另一种情况是删错了,因为版本库里还有呢,所以可以很轻松地把误删的文件恢复到最新版本:1 2 $ git checkout -- test.txt 
利用文本过滤方式,这些脚本会在文件签出前(”smudge”)和提交到暂存区前(”clean”)被调用。这些过滤器能够做各种有趣的事。
                weiyigeek.top-checkout与add
             
0x02 分支管理 描述:分支就是科幻电影里面的平行宇宙,当你正在电脑前努力学习Git的时候,另一个你正在另一个平行宇宙里努力学习SVN。
如果两个平行宇宙互不干扰,那对现在的你也没啥影响。不过在某个时间点,两个平行宇宙合并了,结果你既学会了Git又学会了SVN!
                weiyigeek.top-漫画图
             
分支在实际中有什么用呢? 
现在有了分支你就创建了一个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样既安全,又不影响别人工作。
Git的分支是与众不同的,无论创建、切换和删除分支,Git在1秒钟之内就能完成!无论你的版本库是1个文件还是1万个文件。
专有名词: 
跟踪分支:tracking branch 
远程跟踪分支: remote tracking branch 
 
创建与合并分支 在版本回退里,每次提交,Git都把它们串成一条时间线,这条时间线就是一个分支。只有一条时间线,在Git里,这个分支叫主分支即master分支。
HEAD严格来说不是指向提交,而是指向master才是指向提交的,所以HEAD指向的地址就是当前分支。
                weiyigeek.top-创建分支
             
(3) 不过从现在开始,对工作区的修改和提交就是针对dev分支了,比如新提交一次后,dev指针往前移动一步,而master指针不变: 
(4) 假如我们在dev上的工作完成了,就可以把dev合并到master上。
Git怎么合并呢?最简单的方法,就是直接把master指向dev的当前提交,就完成了合并 
 
 
 
                weiyigeek.top-分支合并
             
(5) 所以Git合并分支也很快!就改改指针,工作区内容也不变!
合并完分支后,甚至可以删除dev分支。删除dev分支就是把dev指针给删掉,删掉后,我们就剩下了一条master分支 
 
 
 
                weiyigeek.top-主分支
             
开始实战演示 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 git checkout -b dev  $ git branch dev    $ git checkout dev  git branch  * dev   master echo  "qweqwe"  > readme.txtgit add . git commit -m "branch test"  $ git checkout master  git merge dev ubuntu@WeiyiGeek:/mnt/e/githubProject/test $ git branch -D dev Deleted branch dev (was 50aad8f). ubuntu@WeiyiGeek:/mnt/e/githubProject/test $ git branch * master 
每次合并都能Fast-forward,我们后面会讲其他方式的合并。
因为创建、合并和删除分支非常快,所以Git鼓励你 使用分支完成某个任务,合并后再删掉分支,这和直接在master分支上工作效果是一样的,但过程更安全。
冲突解决 人生不如意之事十之八九,合并分支往往也不是一帆风顺的。
实际案例:1 2 3 4 5 6 7 8 9 10 11 12 13 $ git checkout -b feature1 $ echo  "Creating a new branch is quick AND simple."  >> readme.txt $ git add readme.txt $ git commit -m "feature1 AND simple"  $ git checkout master   $ echo  "Creating a new branch is quick & simple."  >> readme.txt $ git add readme.txt $ git commit -m "Master & simple"  
                weiyigeek.top-无法快速合并
             
这种情况下,Git无法执行“快速合并”,只能试图把各自的修改合并起来,但这种合并就可能会有冲突,我们试试看:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 $ git merge feature1 Auto-merging readme.txt  CONFLICT (content): Merge conflict in  readme.txt  Automatic merge failed; fix conflicts and then  commit the result.  $ git status  Git tracks changes of files. \<<<<<<< HEAD   Creating a new branch is quick & simple. ======= Creating a new branch is quick AND simple. \>>>>>>> feature1   $ git add readme.txt $ git commit -m "conflict fixed"  $ git branch -d feature1 Deleted branch feature1 (was 75a857c). 
                weiyigeek.top-主从分支更改同一文件合并冲突解决
             
用带参数的git log也可以看到分支的合并情况:1 2 3 4 5 6 7 8 9 $ git log  --graph --pretty=oneline --abbrev-commit *   59bc1cb conflict fixed   //冲突解决 |\ | * 75a857c AND simple * | 400b400 & simple |/ * fec145a branch test  ... $ git log  --oneline --decorate --graph 
总结: 
当Git无法自动合并分支时,就必须首先解决冲突。解决冲突后,再提交,合并完成。 
 
分支管理策略 描述:通常合并分支时,如果可能,Git会用Fast forward模式,但这种模式下,删除分支后,会丢掉分支信息。如果要强制禁用Fast forward模式,Git就会在merge时生成一个新的commit,这样从分支历史上就可以看出分支信息。
实际案例:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 $ git checkout -b dev $ git add readme.txt $ git commit -m "add merge"  $ git checkout master $ git merge --no-ff -m "merge with no-ff"  dev Merge made by the 'recursive'  strategy.  readme.txt |    1 + 1 file changed, 1 insertion(+) $ git log  --graph --pretty=oneline --abbrev-commit  *   7825a50 merge with no-ff |\ | * 6224937 add merge |/ *   59bc1cb conflict fixed ... 
分支策略在实际开发中,我们应该按照几个基本原则进行分支管理:
首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活; 
那在哪干活呢?干活都在dev分支上,也就是说dev分支是不稳定的,到某个时候,比如1.0版本发布时再把dev分支合并到master上,再到master分支发布1.0版本;每个人都有自己的分支,时不时地往dev分支上合并就可以了。 
 
_总结_:
Git分支十分强大,在团队开发中应该充分应用。 
合并分支时加上--no-ff参数就可以用普通模式合并,合并后的历史有分支能看出来曾经做过合并,而fast forward合并就看不出来曾经做过合并。 
 
缺陷BUG分支 软件开发中bug就像家常便饭一样。有了bug就需要修复在Git中,由于分支是如此的强大,所以每个bug都可以通过一个新的临时分支来修复,修复后合并分支,然后将临时分支删除。
当你接到一个修复一个代号101的bug的任务时很自然地,你想创建一个分支issue-101来修复它,但是等等,当前正在dev上进行的工作还没有提交:1 2 3 4 5 6 7 8 9 10 11 12 13 $ git status 
幸好Git还提供了一个stash功能,可以把当前工作现场“储藏”起来,等以后恢复现场后继续工作;1 2 3 $ git stash Saved working directory and index state WIP on dev: 6224937 add merge HEAD is now at 6224937 add merge 
1 2 3 4 5 6 $ git checkout master Switched to branch 'master'  Your branch is ahead of 'origin/master'  by 6 commits. $ git checkout -b issue-101   Switched to a new branch 'issue-101'  
现在修复bug,需要把“Git is free software …”改为“Git is a free software …”,然后提交:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 $ git add readme.txt $ git commit -m "fix bug 101"  [issue-101 cc17032] fix bug 101 1 file changed, 1 insertion(+), 1 deletion(-) $ git checkout master $ git merge --no-ff -m "merged bug fix 101"  issue-101 $ git branch -d issue-101 
现在,是时候接着回到dev分支干活了!1 2 3 4 5 6 7 8 9 10 $ git checkout dev Switched to branch 'dev'  $ git status $ git stash list stash@{0}: WIP on dev: 6224937 add merge 
工作现场还在,Git把stash内容存在某个地方了,但是需要恢复一下,有两个办法:
(1)用git stash apply恢复但是恢复后,stash内容并不删除,你需要用git stash drop来删除; 
(2)用git stash pop,恢复的同时把stash内容也删了: 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 $ git stash pop $ git stash list   
总结: 
修复bug时,我们会通过创建新的bug分支进行修复,然后合并,最后删除; 
当手头工作没有完成时,先把工作现场git stash一下然后去修复bug,修复后,再git stash pop回到工作现场。 
 
未合并分支 软件开发中,总有无穷无尽的新的功能要不断添加进来。
现在你终于接到了一个新任务,开发代号为Vulcan的新功能,于是准备开发:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 $ git checkout -b feature-vulcan $ git add vulcan.py $ git status $ git commit -m "add feature vulcan"  $ git checkout dev $ git branch -d feature-vulcan error: The branch 'feature-vulcan'  is not fully merged.  If you are sure you want to delete it, run 'git branch -D feature-vulcan' . $ git branch -D feature-vulcan   
0x03 远程分支关联 多人协作分支 1 2 3 4 5 6 $ git remote  origin $ git remote -v  origin  git@github.com:michaelliao/learngit.git (fetch) origin  git@github.com:michaelliao/learngit.git (push) 
1 2 3 $ git push origin master $ git push origin dev    
但是并不是一定要把本地分支往远程推送,那么哪些分支需要推送,哪些不需要呢?
master分支是主分支,因此要时刻与远程同步; 
dev分支是开发分支,团队所有成员都需要在上面工作,所以也需要与远程同步; 
bug分支只用于在本地修复bug,就没必要推到远程了,除非老板要看看你每周到底修复了几个bug; 
feature(n. 特色,特征;容貌;特写或专题节目)分支是否推到远程,取决于你是否和你的小伙伴合作在上面开发。 
 
抓取分支 
多人协作时,大家都会往master和dev分支上推送各自的修改。现在模拟一个你的小伙伴,可以在另一台电脑(注意要把SSH Key添加到GitHub)或者同一台电脑的另一个目录下克隆:
1 2 $ git clone  git@github.com:michaelliao/learngit.git Cloning into 'learngit' ... 
当你的小伙伴从远程库clone时,默认情况下你的小伙伴只能看到本地的master分支。
现在你的小伙伴要在dev分支上开发,就必须创建远程origin的dev分支到本地,于是他用这个命令创建本地dev分支:1 2 $ git checkout -b dev origin/dev 
现在他就可以在dev上继续修改,然后时不时地把dev分支push到远程:1 2 3 4 5 6 $ git commit -m "add /usr/bin/env"  [dev 291bea8] add /usr/bin/env 1 file changed, 1 insertion(+)  $ git push origin dev    fc38031..291bea8 
你的小伙伴已经向origin/dev分支推送了他的提交,而碰巧你也对同样的文件作了修改,并试图推送:1 2 3 4 5 6 7 8 9 10 11 $ git add hello.py $ git commit -m "add coding: utf-8"  [dev bd6ae48] add coding: utf-8 1 file changed, 1 insertion(+)   $ git push origin dev To git@github.com:michaelliao/learngit.git  ! [rejected]        dev -> dev (non-fast-forward) error: failed to push some refs to  hint: its remote counterpart. Merge the remote changes (e.g. 'git pull' ) 
推送失败,因为你的小伙伴的最新提交和你试图推送的提交有冲突,解决办法也很简单Git已经提示我们,先用git pull把最新的提交从origin/dev抓下来,然后,在本地合并,解决冲突再推送:
但是 git pull 也失败了,原因是没有指定本地dev分支与远程origin/dev分支的关联,根据提示设置dev和origin/dev的链接
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 $ git branch --set -upstream dev origin/dev   Branch dev set  up to track remote branch dev from origin. $ git pull Auto-merging hello.py CONFLICT (content): Merge conflict in  hello.py Automatic merge failed; fix conflicts and then  commit the result.   $ git commit -m "merge & fix hello.py"  [dev adca45d] merge & fix hello.py  $ git push origin dev    291bea8..adca45d  dev -> dev 
因此多人协作的工作模式通常是这样,一旦熟悉了就非常简单:
首先,可以试图用git push origin branch-name推送自己的修改; 
如果推送失败,则因为远程分支比你的本地更新,需要先用git pull试图合并; 
如果合并有冲突,则解决冲突,并在本地提交; 
没有冲突或者解决掉冲突后,再用git push origin branch-name推送就能成功! 
如果git pull提示“no tracking information”,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream branch-name origin/branch-name 
 
总结: 
查看远程库信息,使用git remote -v; 
本地新建的分支如果不推送到远程,对其他人就是不可见的; 
从本地推送分支,使用git push origin branch-name,如果推送失败先用git pull抓取远程的新提交; 
在本地创建和远程分支对应的分支,使用git checkout -b branch-name origin/branch-name,本地和远程分支的名称最好一致; 
建立本地分支和远程分支的关联,使用git branch –set-upstream branch-name origin/branch-name; 
从远程抓取分支,使用git pull,如果有冲突,要先处理冲突,最后在git push origin branch-name推送 
 
分支之间commit 实际案例:1 2 git checkout <branch-name> && git cherry-pick <commit-id> 
0x04 标签管理 描述:发布一个版本时我们通常先在版本库中打一个标签(tag),这样就唯一确定了打标签时刻的版本。将来无论什么时候,取某个标签的版本,就是把那个打标签的时刻的历史版本取出来,所以标签也是版本库的一个快照。
Git的标签虽然是版本库的快照,但其实它就是指向某个commit的指针(跟分支很像对不对?但是分支可以移动,标签不能移动 ),所以创建和删除标签都是瞬间完成的,所以tag就是一个让人容易记住的有意义的名字,它跟某个commitID绑在一起。
Git有commit,为什么还要引入tag? 
创建标签 在Git中打标签非常简单,首先切换到需要打标签的分支上:1 2 3 4 5 6 $ git branch * dev   master  $ git checkout master Switched to branch 'master'  
然后敲命令git tag <name>就可以打一个新标签:1 2 3 4 5 $ git tag v1.0     $ git tag v1.0 
有时候如果忘了打标签,比如现在已经是周五了但应该在周一打的标签没有打怎么办?1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 $ git log  --pretty=oneline --abbrev-commit 6a5819e merged bug fix 101 cc17032 fix bug 101 7825a50 merge with no-ff 6224937 add merge 59bc1cb conflict fixed 400b400 & simple 75a857c AND simple fec145a branch test  d17efd8 remove test.txt   $ git tag v0.9 6224937 $ git tag v0.9 v1.0 $ git show v0.9   commit 622493706ab447b6bb37e4e2a2f276a20fed2ab4 Author: Michael Liao <askxuefeng@gmail.com> Date:   Thu Aug 22 11:22:08 2013 +0800       add merge $ git tag -a v0.1 -m "version 0.1 released"  3628164   $ git show v0.1 tag v0.1 Tagger: Michael Liao <askxuefeng@gmail.com> Date:   Mon Aug 26 07:28:11 2013 +0800   version 0.1 released   commit 3628164fb26d48395383f8f31179f24e0882e1e0 Author: Michael Liao <askxuefeng@gmail.com> Date:   Tue Aug 20 15:11:49 2013 +0800       append GPL 
签名采用PGP签名,因此,必须首先安装gpg(GnuPG),如果没有找到gpg,或者没有gpg密钥对就会报错:1 2 3 gpg: signing failed: secret key not available error: gpg failed to sign the data error: unable to sign the tag 
1 $ git tag -s v0.2 -m "signed version 0.2 released"  fec145a 
用命令git show 可以看到PGP签名信息:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 $ git show v0.2 tag v0.2 Tagger: Michael Liao <askxuefeng@gmail.com> Date:   Mon Aug 26 07:28:33 2013 +0800   signed version 0.2 released -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (Darwin)   iQEcBAABAgAGBQJSGpMhAAoJEPUxHyDAhBpT4QQIAKeHfR3bo... -----END PGP SIGNATURE-----   commit fec145accd63cdc9ed95a2f557ea0658a2a6537f Author: Michael Liao <askxuefeng@gmail.com> Date:   Thu Aug 22 10:37:30 2013 +0800       branch test  
 
总结: 
命令git tag 用于新建一个标签,默认为HEAD,也可以指定一个commit id,git tag可以查看所有标签。  
git tag -a  -m “blablabla…”可以指定标签信息;  
git tag -s  -m “blablabla…”可以用PGP签名标签;  
 
操作标签 如果标签打错了也可以删除指定的标签,因为创建的标签都只存储在本地,不会自动推送到远程;所以打错的标签可以在本地安全删除。1 2 $ git tag -d v0.1 Deleted tag 'v0.1'  (was e078af9) 
如果要推送某个标签到远程,使用命令git push origin <tagname>:1 2 3 4 5 6 7 8 9 10 11 12 13 $ git push origin v1.0 Total 0 (delta 0), reused 0 (delta 0) To git@github.com:michaelliao/learngit.git  * [new tag]         v1.0 -> v1.0   $ git push origin --tags Counting objects: 1, done . Writing objects: 100% (1/1), 554 bytes, done . Total 1 (delta 0), reused 0 (delta 0) To git@github.com:michaelliao/learngit.git  * [new tag]         v0.2 -> v0.2  * [new tag]         v0.9 -> v0.9 
如果标签已经推送到远程,要删除远程标签就麻烦一点,操作完成后要想看看是否真的从远程库删除了标签,可以登陆GitHub查看1 2 3 4 5 6 7 8 $ git tag -d v0.9 Deleted tag 'v0.9'  (was 6224937) $ git push origin :refs/tags/v0.9 To git@github.com:michaelliao/learngit.git  - [deleted]         v0.9 
总结: 
命令git push origin 可以推送一个本地标签;  
命令git push origin –tags 可以推送全部未推送过的本地标签; 
命令git tag -d 可以删除一个本地标签;  
命令git push origin :refs/tags/可以删除一个远程标签。  
 
0x05 总结 .git 仓库元数据 
config*    配置文件     
description    描述仅供 Git Web 程序使用     
HEAD    当前被检出的分支     
index    暂存区信息     
hooks/    客户端或服务端的钩子脚本(hook scripts)     
info/    全局性排除(global exclude)文件,不希望被记录在 .gitignore 文件中的忽略模式(ignored patterns)     
objects/    所有数据内容     
refs/    数据(分支)的提交对象的指针