Git的安装
Linux下
直接sudoapt-get install git-core
Windows下
直接下载安装 https://git-for-windows.github.io/
安装完成后,还需要最后一步设置,在命令行输入:
$ git config –global user.name “Your Name”
$ git config –globaluser.email “email@example.com”
设置名称和邮箱是为了在提交代码的时候知道是谁提交的。
创建版本库
版本库又名仓库,可以简单的理解为一个目录,这个目录里面所以的文件都可以被Git管理起来,每个文件的修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来的某个时刻可以还原。
创建仓库只需要在需要变成仓库的目录下输入 gitinit
创建完后这个目录下会出现 .git 的目录,这个目录是用来Git来跟踪管理版本库的。
如果要删除这个仓库只需要将这个目录删除掉。
在Linux下建立目录并设置为Git仓库的代码:
mkdirlearngit
cdlearngit
gitinit
(显示当前目录) pwd
把文件添加到版本库
注意:千万不要使用Windows自带的记事本编辑任何文本文件。原因是Microsoft开发记事本的团队使用了一个非常弱智的行为来保存UTF-8编码的文件,他们自作聪明地在每个文件开头添加了0xefbbbf(十六进制)的字符。
建议使用notepad++代替。
将文件添加到仓库:
第一步,用命令git add告诉Git,把文件添加到仓库:
git add //git add readme.txt
第二步,用命令git commit告诉Git,把文件提交到仓库:
git commit -m “wrote update info”
简单解释一下git commit命令,-m后面输入的是本次提交的说明,可以输入任意内容,当然最好是有意义的,这样你就能从历史记录里方便地找到改动记录。
为什么Git添加文件需要add,commit一共两步呢?因为commit可以一次提交很多文件,所以你可以多次add不同的文件,比如:
$ git add file1.txt
$ git add file2.txt file3.txt
$ git commit -m “add 3files.”
$ git add . —>代表将所有文件进行添加
小结
初始化一个Git仓库,使用git init命令。
添加文件到Git仓库,分两步:
l 第一步,使用命令git add ,注意,可反复多次使用,添加多个文件;
l 第二步,使用命令git commit,完成。
Git查看文件的更改
当我们将文件内容进行更改后可以用git status命令查看结果。
git status命令可以让我们时刻掌握仓库当前的状态
我们可以使用git diff命令查看我们修改了什么。
git diff git diff顾名思义就是查看difference
小结
l 要随时掌握工作区的状态,使用gitstatus命令。
l 如果gitstatus告诉你有文件被修改过,用git diff可以查看修改内容。
版本退回
我们可以用git log命令查看修改历史。
git log命令显示从最近到最远的提交日志,如果输出信息太多可以加上
–pretty=oneline参数。
git log–pretty=oneline –>显示信息为:版本号(commit id)和git commit -m提交时设置的提示说明。
首先,Git必须知道当前版本是哪个版本,在Git中,用HEAD表示当前版本,也就是最新提交的commit id为3628164…882e1e0,上一个版本就是HEAD^,上上一个版本就是HEAD^^,当然往上100个版本写100个^比较容易数不过来,所以写成HEAD~100。
我们使用 git reset命令来进行版本的回退。
git reset –hard HEAD^ —->返回上一个版本
也可以是 git reset –hard HEAD~1
(Windows系统下 git reset –hard HEAD^^ 才会返回上一个版本)
然后 stackoverflow 解释:If you’re using Windows, you could do git log HEAD^^, I believe. – Name McChange Nov 4 ‘13 at 0:58 The ^ is an escape character in the Windows Command Line. Use ^^ instead of ^.
其他解释:在windows的命令行中 git reset -hard HEAD^ 中 ^是一个特殊字符,使用时必须用双引号引起来才能正确运行 git reset -hard HEAD”^” 或者 git reset -hard”HEAD^”
这样一来最新的版本已经看不到了,但只要命令窗口还为关闭,只要能找到需要回到最新的版本号的commit id,于是就可以回到未来的某个版本。
git reset –hard 3628164 —–>在这里版本号不需要写全 Git会自己匹配查找
但如果你命令窗口关闭了,或者到了第二天需要返回到曾经的最新版本,则必须要找到当时版本的commit id。在git中提供了一个gitreflog命令来记录你的每一次命令。
小结
l HEAD指向的版本就是当前版本,因此,Git允许我们在版本的历史之间穿梭,使用命令git reset –hard commit_id。
l 穿梭前,用git log可以查看提交历史,以便确定要回退到哪个版本。
l 要重返未来,用git reflog查看命令历史,以便确定要回到未来的哪个版本。
Git的工作区和暂存区
工作区(WorkingDirectory)
就是本地电脑可以看到的目录
版本库(Repository)
工作区中有一个隐藏的目录 .git,这个不算工作区,而是Git的版本库。
Git的版本库中存放了很多东西,其中最重要的就是成为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master,以及指向master的一个指针叫HEAD。
文件往Git版本库中添加的时候,是分两步执行的:
第一步是: git add把文件添加进去,实际上就是把文件修改添加到暂存区。
第二步是: git commit提交更改,实际上就是把暂存区的所有内容提交到当前分支。
因为我们创建Git版本库时,Git自动为我们创建了唯一一个master分支,所以 git commit就是往master分支上提交修改。
管理修改
Git跟踪并管理的是修改,而非文件。
第一次修改 -> git add -> 第二次修改 -> git commit
由于Git管理的是修改,当你使用git add命令后,在工作区的第一次修改会被放入暂存区,但是第二次修改的并没有放在暂存区,所以git commit提交的是第一次修改的版本。
提交后,用git diff HEAD – 命令可以查看工作区和版本库里面最新版本的区别。
撤销修改
Git中使用 git checkout -- < filename> 丢掉工作区的所有修改。
命令git checkout – 的意思是,把文件在工作区的所有修改撤销,这里有两种情况:
一种是:文件自修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态;
一种是:文件已经添加到暂存区后,又作了修改,现在撤销修改就回到添加到暂存区后的状态。
总之,就是让这个文件回到最近的一次 git commit 或 git add 时的状态。
Git中使用gitreset HEAD <filename>命令可以将暂存区的修改撤销,重新放回工作区。
git reset 命令既可以回退版本,也可以将暂存区的修改退回到工作区。当我们用HEAD时,表示最新版。
小结
场景一:当你乱改了工作区的某个文件的内容,想直接丢弃工作区的时,用命令gitcheckout –
场景二:当你不但乱改了工作区的某个文件的内容,还添加到了暂存区,想丢弃修改,分两步,首先使用命令gitreset HEAD ,就回到了场景一,第二部按场景一操作。
场景三:已经提交了不合适的修改到了版本库时,想要撤回本次提交,可以采用版本回退(git reset –hard HEAD^)回退到上一个版本,不过前提是没有推送到远程库。
删除文件
一般情况下,通常可以直接将在文件管理器中把没用的文件删除了,或者使用rm命令删掉:rm<filename>
如果确实要将文件从版本库中删除,那就使用命令git rm删掉,并且git commit.
如果删错了,因为版本库中还有这个文件,所以可以使用命令 git checkout -- <filename>将误删的文件恢复到最新版本。
远程仓库
Git是一个分布式版本控制系统,同一个Git仓库,可以分布在不同的机器上。最早,只有一台机器有一个原始版本库,此后,别的机器可以“克隆”这个原始版本库,而且每台机器的版本库其实都是一样的,并没有主次之分。
然而,现阶段还没有必要自己搭建Git服务器,可以直接使用GitHub来作为Git服务器仓库。
由于本地Git仓库和GitHub仓库之间的传输是通过SSH加密的,所以需要进行一下的设置。
第1步:创建SSH Key,在用户主目录下,看看有没有.ssh目录和这个目录下有没有id_rsa和id_rsa.pub这两个文件,如果已经有了,可直接跳到下一步。如果没有,打开Shell(Windows下打开Git Bash),创建SSH Key:
$ ssh-keygen -t rsa -C "youremail@example.com"
你需要把邮件地址换成你自己的邮件地址(其实就是公钥的名字),然后一路回车,使用默认值即可。也可以直接ssh-keygen
之后在用户目录下就可以找到 .ssh目录,里面有id_rsa和id_rsa.pub两个文件,这两个就是SSH Key的密钥对,id_rsa是私钥,不能泄露出去,id_rsa.pub是公钥。
第2步:登陆GitHub,打开Account settings,SSH Keys界面。然后,点“Add SSH Key”,填上任意Title,在Key文本框里粘贴id_rsa.pub文件的内容:
点“Add Key”,你就应该看到已经添加的Key。
替换密钥时需要将原来的.ssh文件夹删掉并重新生成即可。
添加远程库
目标:在GitHub新建一个Git仓库,让两个仓库进行远程同步。
首先在GitHub上新建一个Git仓库。然后在本地的Git仓库运行:
gitremote add origin git@github.com:<GitHub用户名>/<新建的GitHub仓库名.git>
这里面 origin是远程库的名称,是Git的默认叫法,也可以进行修改。
下一步,就可以把本地库的所以内容推送到远程库上:
gitpush -u origin master
把本地库的内容推送到远程,使用git push命令,实际上是把当前分支master推送到远程。
由于远程库是空的,我们第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令。
从现在起,只要本地做了提交,就可以通过命令:git pushorigin master 把本地的master分支的最新修改推送到GitHub。
小结
要关联一个远程库使用命令gitremote add origin git@server-name:path/repo-name.git
关联后使用命令 git push -u origin master 第一次推送master分支的所有内容。
此后每次版本更新只需要使用命令 git push origin master 推送最新的修改。
从远程库克隆
上次我们讲了先有本地库,后有远程库的时候,如何关联远程库。现在,我们从零开始,先创建远程库,然后,从远程库克隆。
首先,登陆GitHub,创建一个新的仓库,名字叫gitskills
然后选择Initialize this repositorywith a README,这样GitHub会自动为我们创建一个README.md文件。
现在我们使用命令远程克隆一个本地库:
git clone git@github.com:
小结
要克隆一个仓库,首先必须知道仓库的地址,然后使用git clone命令克隆。
Git支持多种协议,包括https,但通过ssh支持的原生git协议速度最快。
分支管理
分支在实际中有什么用呢?假设你准备开发一个新功能,但是需要两周才能完成,第一周你写了50%的代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别人不能使用。如果等代码全部写完再一次提交,又存在丢失每天进度的巨大风险。现在有了分支,就不用怕了。你创建了一个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样,既安全,又不影响别人工作。
创建与合并分支
在版本回退里,你已经知道,每次提交,Git都把它们串成一条时间线,这条时间线就是一个分支。截止到目前,只有一条时间线,在Git里,这个分支叫主分支,即master分支。HEAD严格来说不是指向提交,而是指向master,master才是指向提交的,所以,HEAD指向的就是当前分支。
一开始的时候,master分支是一条线,Git用master指向最新的提交,再用HEAD指向master,就能确定当前分支,以及当前分支的提交点:
每次提交,master分支都会向前移动一步,这样,随着你不断提交,master分支的线也越来越长。
当我们创建新的分支,例如dev时,Git新建了一个指针叫dev,指向master相同的提交,再把HEAD指向dev,就表示当前分支在dev上:
Git创建一个分支很快,因为除了增加一个dev指针,改改HEAD的指向,工作区的文件都没有任何变化。
不过,从现在开始,对工作区的修改和提交就是针对dev分支了,比如新提交一次后,dev指针往前移动一步,而master指针不变。
假如我们在dev上的工作完成了,就可以把dev合并到master上。Git怎么合并呢?最简单的方法,就是直接把master指向dev的当前提交,就完成了合并。
所以Git合并分支也很快,只是改改指针,工作区内容也不变。
合并完分支后,甚至可以删除dev分支。删除dev分支就是把dev指针给删掉,删掉后,我们就剩下了一条master分支:
实战:
首先我们创建 dev 分支,然后切换到 dev 分支。
gitcheckout -b dev
git checkout命令加上-b参数表示创建并切换,相当于以下两条命令:
git branch dev
gitcheckout dev
然后,用git branch命令查看当前分支:
git branch
git branch命令会列出所有分支,当前分支前面会标一个*号。
然后我们就可以在dev分支上正常提交。等到,dev分支工作完成时,我们就可以切回master分支。
gitcheckout master
切换回master分支后,再查看之前在dev分支提交的文件,会发现才添加的内容还没有出现,那时因为那个提交是在dev分支上,而master分支此刻的提交点并没有变。
所以现在,我们要把dev分支的工作成果合并到master分支上:
git merge dev
git merge命令用于合并指定分支到当前分支。合并后,再查看文件内容,就可以看到,和dev分支的最新提交是完全一样的。
合并完成后,我们就可以删除dev分支了:
git branch -d dev
删除后,查看branch,就只剩下master分支了:
git branch
小结
Git鼓励大量使用分支:
查看分支:git branch
创建分支:git branch
切换分支:git checkout
创建+切换分支:git checkout -b
合并某分支到当前分支:git merge
删除分支:git branch -d
解决冲突
准备新的feature1分支,继续我们的新分支开发:
$ gitcheckout -b feature1
Switched to a new branch’feature1’
修改readme.txt最后一行,改为:
Creating a new branch isquick AND simple.
在feature1分支上提交:
$ git add readme.txt
$ git commit -m “AND simple”
[feature1 75a857c] AND simple
1 file changed, 1 insertion(+), 1 deletion(-)
切换到master分支:
$ git checkout master
Switched to branch ‘master’
Your branch is ahead of’origin/master’ by 1 commit.
Git还会自动提示我们当前master分支比远程的master分支要超前1个提交。
在master分支上把readme.txt文件的最后一行改为:
Creating a new branch is quick & simple.
提交:
$ gitadd readme.txt
$ gitcommit -m “& simple”
[master 400b400] & simple
1 file changed, 1 insertion(+), 1 deletion(-)
现在,master分支和feature1分支各自都分别有新的提交,变成了这样:
这种情况下,Git无法执行“快速合并”,只能试图把各自的修改合并起来,但这种合并就可能会有冲突,我们试试看:
$ git merge feature1
Auto-merging readme.txt
CONFLICT (content): Mergeconflict in readme.txt
Automatic merge failed; fixconflicts and then commit the result.
果然冲突了!Git告诉我们,readme.txt文件存在冲突,必须手动解决冲突后再提交。git status也可以告诉我们冲突的文件:
$ git status
On branch master
no changes added to commit (use “gitadd” and/or “git commit -a”)
我们可以直接查看readme.txt的内容:
Git is a distributed versioncontrol system.
Git is free softwaredistributed under the GPL.
Git has a mutable indexcalled stage.
Git tracks changes of files.
<<<<<<< HEAD
Creating a new branch isquick & simple.
Creating a new branch isquick AND simple.
>>>>>> feature1
Git用<<<<<<<,=======,>>>>>>>标记出不同分支的内容,我们修改如下后保存:
Creating a new branch is quick and simple.
再提交:
$ git add readme.txt
$ git commit -m “conflict fixed”
现在,master分支和feature1分支变成了下图所示:
用带参数的git log也可以看到分支的合并情况:
$ git log –graph –pretty=oneline–abbrev-commit
* 59bc1cb conflict fixed
|\
| *75a857c AND simple
* |400b400 & simple
|/
*fec145a branch test
…
最后,删除feature1分支:
$ git branch -d feature1
工作完成。
小结
当Git无法自动合并分支时,就必须首先解决冲突。解决冲突后,再提交,合并完成。
用git log –graph命令可以看到分支合并图。
分支管理策略
通常合并分支的时,Git一般会Fast forward模式,但这种模式下,删除分支后,会丢掉分支信息。如果强制禁用Fast forward模式,Git就会在merge时生成一个新的commit,这样,从分支的历史就可以看出分支的信息。
下面我们使用--no-ff方式的git merge:
首先,仍然创建并切换dev分支:
git checkout -b dev
修改readme.txt文件,并提交一个新的commit:
git add readme.txt
git commit -m “addmerge”
现在,我们切换回master:
git checkout master
准备合并dev分支,请注意–no-ff参数,表示禁用Fast forward:
git merge –no-ff -m”merge with no-ff” dev
因为本次合并要创建一个新的commit,所以加上-m参数,把commit描述写进去。合并后,我们用git log看看分支历史:
git log –graph–pretty=oneline –abbrev-commit
可以看到,不使用Fast forward模式,merge后就像这样:
分支策略
在实际开发中,我们应该按照几个基本原则进行分支管理:首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本;你和你的小伙伴们每个人都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就可以了。所以,团队合作的分支看起来就像这样:
小结
Git分支十分强大,在团队开发中应该充分应用。合并分支时,加上–no-ff参数就可以用普通模式合并,合并后的历史有分支,能看出来曾经做过合并,而fast forward合并就看不出来曾经做过合并。
Bug分支
在Git中,由于分支是如此的强大,所以,每个bug都可以通过一个新的临时分支来修复,修复后,合并分支,然后将临时分支删除。
Git提供了一个stash功能,可以把当前工作现场存储起来,等以后恢复现场后继续工作: git stash
首先确定要在哪个分支上修复bug,假定需要在master分支上修复,就从master创建临时分支:
git checkout master
git checkout -b issue-101
现在修复bug,然后提交:
git add readme.txt
git commit -m “fix bug101”
修复完成后,切换到master分支,并完成合并,最后删除issue-101分支:
git checkout master
git merge –no-ff -m”merged bug fix 101” issue-101
git branch -d issue-101
接着回到dev分支
git checkout dev
git status
工作区是干净的,用git stash list命令查看刚才的工作现场存到哪了。
git stash list
工作现场还在,Git把stash内容存在某个地方了,但是需要恢复一下,有两个办法:
一是用git stash apply恢复,但是恢复后,stash内容并不删除,你需要用git stash drop来删除;
另一种方式是用git stash pop,恢复的同时把stash内容也删了:
git stash pop
再用git stash list查看,就看不到任何stash内容了:
git stash list
你可以多次stash,恢复的时候,先用git stash list查看,然后恢复指定的stash,用命令:
git stash apply stash@{0}
小结
修复bug时,我们会通过创建新的bug分支进行修复,然后合并,最后删除;当手头工作没有完成时,先把工作现场git stash一下,然后去修复bug,修复后,再git stash pop,回到工作现场。
Feature分支
添加一个新功能时,为了不把主分支搞乱了,所以最好建一个feature分支,在上面开发,完成后合并,最后,删除该feature分支。
git checkout -b feature-new —创建了一个名字是feature-new的分支
开发完毕后:
git add new.py
git status
git commit -m “add feature new”
切回dev,准备合并:
git checkout dev
将feature分支和bug分支合并,然后删除。
但如果,由于一些原因,新功能被取消,所以这个分支必须就地销毁。
git branch -d feature-new 但由于分支还没有合并,所以这样销毁会失败,如果强行删除,需要使用命令gitbranch -D feature-new
小结
开发一个新feature,最好新建一个分支。如果要丢弃一个没有被合并的分支,可以通过git branch -D 强行删除。
多人协作
当你从远程仓库克隆时,实际上Git自动的把本地的master分支和远程的master对应起来了,并且,远程仓库的默认名称是origin。
要查看远程库的信息,用git remote,或者使用git remote -v显示更详细的信息。git remote -v显示了抓取和推送的origin的地址。如果没有推送权限,就看不到push的地址。
推送分支
推送分支,就是把该分支推送到远程库。推送时要指定本地分支,这样,Git就会把该分支推送到远程库对应的远程分支上。
git push origin master
如果要推送其他分支,如dev就要改成 git push origin dev
但是,并不一定要把本地分支往远程分支推送。
master分支是主分支,因此要时刻与远程同步;
dev分支是开发分支,团队所有成员都要在上面工作,所以也需要与远程同步。
bug分支只用于在本地修复bug,所以就没必要推到远程了。
feature是否推送到远程,取决于你是否要和小伙伴合作在上面开发。
总之,就是在Git中,分支完全可以在本地自己藏着玩,是否推送,视心情而定!
抓取分支
多人协作时,大家都会往master和dev分支上推送各自的修改。
但现在一个小伙伴从远程clone时,默认情况下只能看到你本地的master分支。现在,你的小伙伴要在dev分支上开发,就必须创建远程origin和dev分支到本地,于是用
git checkout -b dev origin/dev
现在,他就可以在dev上继续修改,然后时不时把dev分支push到远程。
git clone git@github.com:tomatoyan/learngit.git
git config user.name “Bob”
git config user.email “bob@example.com”
git branch
git checkout -b dev origin/dev
git add hello.py
git commit -m “add hello info”
git push origin dev
如果你的小伙伴已经向origin/dev分支推送了他的提交,而碰巧你也对同样的文件作了修改,并试图推送:
$ git add hello.py
$ git commit -m “add coding: utf-8”
[dev bd6ae48] add coding: utf-8
1 filechanged, 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 ‘git@github.com:michaelliao/learngit.git’
hint: Updates were rejected because the tip ofyour current branch is behind
hint: its remote counterpart. Merge the remotechanges (e.g. ‘git pull’)
hint: before pushing again.
hint: See the ‘Note about fast-forwards’ in’git push –help’ for details.
推送失败,因为你的小伙伴的最新提交和你试图推送的提交有冲突,Git已经提示我们,先用git pull把最新的提交origin/dev抓下来,然后,在本地仓库,解决冲突,再推送。
$ git pull
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 3 (delta 0)
Unpacking objects: 100% (3/3), done.
From github.com:michaelliao/learngit
fc38031..291bea8 dev -> origin/dev
There is no tracking information for thecurrent branch.
Please specify which branch you want to mergewith.
See git-pull(1) for details
gitpull
If you wish to set tracking information forthis branch you can do so with:
gitbranch –set-upstream dev origin/
git pull也失败了,原因是没有指定本地dev分支与远程origin/dev分支的链接,根据提示,设置dev和origin/dev的链接。
$ git branch –set-upstream dev origin/dev
Branch dev set up to track remote branch dev from origin.
再pull:
$ git pull
Auto-merging hello.py
CONFLICT (content): Merge conflict in hello.py
Automatic merge failed; fix conflicts and then commit the result.
这回git pull成功,但是合并有冲突,需要手动解决,解决的方法和分支管理中的解决冲突完全一样。解决后,提交,再push:
$ git commit -m “merge & fix hello.py”
[dev adca45d] merge & fix hello.py
$ git push origin dev
Counting objects: 10, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (6/6), 747 bytes, done.
Total 6 (delta 0), reused 0 (delta 0)
To git@github.com:michaelliao/learngit.git
291bea8..adca45d dev -> dev
因此,多人协作的工作模式是这样:
1、 首先,可以试图用git push origin 推送自己的修改;
2、 如果推送失败,则因为远程分支比你的本地更新,需要先用git pull试图合并;
3、 如果合并有冲突,则解决冲突,并在本地提交;
4、 没有冲突或者解决掉冲突后,再用git push origin branch-name推送就能成功。
如果git pull提示“no tracking information”,则说明本地分支和远程分支的链接关系没有创建,用命令git branch –set-upstreambranch-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 log –graph –pretty=oneline –abbrev-commit
标签管理
发布一个版本时,我们通常先在版本库中打一个标签(tag),这样,就唯一确定了打标签时刻的版本。将来无论什么时候,取某个标签的版本,就是把那个打标签的时刻的历史版本取出来。所以,标签也是版本库的一个快照。
Git的标签虽然是版本库的快照,但其实它就是指向某个commit的指针,所以,创建和删除标签都是瞬间完成的。
Git有commit,为什么还要引入tag?
“请把上周一的那个版本打包发布,commit号是6a5819e…”
“一串乱七八糟的数字不好找!”
如果换一个办法:
“请把上周一的那个版本打包发布,版本号是v1.2”
“好的,按照tag v1.2查找commit就行!”
所以,tag就是一个让人容易记住的有意义的名字,它跟某个commit绑在一起。
创建标签
在Git中打标签很简单,首先,切换到需要打标签的分支上。
git tag <name>就可以打一个新标签:git tagv1.0
然后可以用命令git tag查看所有的标签。
默认的标签是打在最新提交的commit上的,有时候,如果忘了打标签,方法是找到历史提交commit id,然后打上就好了。
git log –pretty=oneline–abbrev-commit
要对add merge的这次提交加标签,它对应的commit id是6224937,则敲入
git tag v1.0 6224937
这里的标签不是按照时间顺序列出,而是按照字母排序的。可以用git show查看标签信息。
还可以创建带有说明的标签,用-a指定标签名,-m指定说明文字。
git tag -a -m <”说明文字”>
还可以通过-s用私钥签名一个标签:
git tag -s -m <”说明文字”>
签名采用PGP签名,因此,必须首先安装gpg(GnuPG),如果没有找到gpg,或者没有gpg密钥对,就会报错。
用命令git show 可以看到PGP签名信息:
小结
命令git tag <name>用于新建一个标签,默认为HEAD,也可以指定一个commit id
git tag -a -m <“说明文字”>可以指定标签信息
git tag -s -m <“说明文字”>可以用PGP签名标签
命令git tag可以查看所有标签
操作标签
标签的删除:git tag -d v0.1
git tag -d
推送标签去远程
git push origin
或者一次性推送全部尚未推送到远程的本地标签:
git push origin –tags
但如果标签推送到了远程,要删除就麻烦一点了,先从本地删除:
git tag -d
然后,再从远程删除。删除命令也是push,格式如下:
git push origin:refs/tags/
小结
l 命令git push origin 可以推送一个本地标签;
l 命令git push origin –tags 可以推送全部未推送过的本地标签;
l 命令git tag -d 可以删除一个本地标签;
l 命令git push origin :refs/tags/ 可以删除一个远程标签。
使用GitHub
如何参与GitHub上的开源项目。
git clone git@github.com:michaelliao/boostrap.git
一定要从自己的帐号下clone仓库,这样才能推送修改。如果从boostrap的作者的仓库地址git@github.com:twbs/boostrap.git克隆,因为没有权限,你将不能修改推送。
如果你想修复boostrap的一个bug,或者新增一个功能,立刻就可以开始干活,干完后就可以往自己的仓库推送。
如果你希望boostrap的官方库能接受你的修改,你就可以在GitHub上发起一个pull request。当然,对方是否接受你的pull request就不一定了。
小结
l 在GitHub上,可以任意Fork开源仓库;
l 自己拥有Fork后的仓库的读写权限;
l 可以推送pull request给官方仓库来贡献代码。
自定义Git
我们可以配置user.name和user.email。
我们可以让Git显示颜色
git config –globalcolor.ui true
这样Git会适当地显示不同的颜色。
忽略特殊文件
在Git工作区的根目录下创建一个特殊的 .gitignore文件,然后把要忽略的文件名添加进去,Git就会自动忽略这些文件。不要从头写 .gitignore文件,GitHub已经准备为我们准备了各种配置文件,只需要组合一下就可以使用了。所有配置文件可以直接在线浏览:https://github.com/github/gitignore
忽略文件的原则是:
1.忽略操作系统自动生成的文件,比如缩略图等;
2.忽略编译生成的中间文件、可执行文件等,也就是如果一个文件是通过另一个文件自动生成的,那自动生成的文件就没必要放进版本库,比如Java编译产生的.class文件;
3.忽略你自己的带有敏感信息的配置文件,比如存放口令的配置文件。
最后一步就是把 .gitignore也提交到Git。
当然检验.gitignore的标准是git status命令是不是说working directory clean。
使用Windows时,如果你在资源管理器里新建一个.gitignore文件,它会非常弱智地提示你必须输入文件名,但是在文本编辑器里“保存”或者“另存为”就可以把文件保存为.gitignore了。有些时候,你想添加一个文件到Git,但发现添加不了,原因是这个文件被.gitignore忽略了:
$ git add App.class
The following paths areignored by one of your .gitignore files:
App.class
Use -f if you really want toadd them.
如果你确实想添加该文件,可以用-f强制添加到Git:
$ git add -f App.class
或者你发现,可能是.gitignore写得有问题,需要找出来到底哪个规则写错了,可以用gitcheck-ignore命令检查:
$ git check-ignore -v App.class
.gitignore:3:*.class App.class
Git会告诉我们,.gitignore的第3行规则忽略了该文件,于是我们就可以知道应该修订哪个规则。
小结
l 忽略那些文件,需要编写 .gitignore;
l .gitignore文件本身要放到版本库里,并且可以对 .gitignore做版本管理。
配置别名
我们可以对命令配置成别名
比如可以用git st就表示git status
我们只需敲一行命令,告诉Git,以后st就表示 status
git config –globalalias.st status
当然还有别的命令可以简写,很多人都用co表示checkout,ci表示commit,br表示branch:
$ git config –globalalias.co checkout
$ git config –globalalias.ci commit
$ git config –globalalias.br branch
–global参数是全局参数,也就是这些命令在这台电脑的所有Git仓库下都有用。
配置文件
配置Git时,加上 –global 是针对当前用户起作用的,如果不加,那只针对当前的仓库起作用。
每个仓库的Git配置文件都放在 .git/config文件中:
$ cat .git/config
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
[remote “origin”]
url = git@github.com:tomato/learngit.git
fetch = +refs/heads/:refs/remotes/origin/
[branch “master”]
remote = origin
merge = refs/heads/master
[alias]
last = log -1
别名就在[alias]后面,要删除别名,直接把对应的行删掉即可。
而当前用户的Git配置文件放在用户主目录下的一个隐藏文件.gitconfig中:
$ cat .gitconfig
[alias]
co = checkout
ci = commit
br = branch
st = status
[user]
name = Your Name
email = your@email.com
配置别名也可以直接修改这个文件,如果改错了,可以删掉文件重新通过命令配置。
搭建Git服务器
搭建Git服务器需要准备一台运行Linux的机器,强烈推荐用Ubuntu或Debian,这样,通过几条简单的apt命令就可以完成安装。
假设你已经有sudo权限的用户账号,下面,正式开始安装。
第一步,安装git:
sudo apt-get install git
第二步,创建一个git用户,来运行git服务:
sudo adduser git
第三步,创建证书登录:
收集所有需要登录的用户的公钥,就是他们自己的id_rsa.pub文件,把所有公钥导入到/home/git/.ssh/authorized_keys文件里,一行一个。
第四步,初始化Git仓库:
先选定一个目录作为一个Git仓库,假定是 /srv/sample.git ,在 /srv 目录下输入命令:
sudo git init–bare sample.git
Git就会创建一个裸仓库,裸仓库没有工作区,因为服务器上的Git仓库纯粹是为了共享,所以不让用户直接登录到服务器上去改工作区,并且服务器上的Git仓库通常都以.git结尾。然后,把owner改为git:
sudo chown -R git:gitsample.git
第五步,禁用shell登录,出于安全考虑,第二件创建的git用户不允许登录shell,这可以通过编辑 /etc/passwd 文件完成。找到类似下面的一行:
git:x:1001:1001:,,,:/home/git:/bin/bash
改为:
git:x:1001:1001:,,,:/home/git:/usr/bin/git-shell
这样,git用户可以通过ssh使用git,但无法登录shell,因为我们为git用户指定的git-shell每一次登陆就自动退出。
第六步,克隆远程仓库。现在可以通过git clone命令克隆远程仓库,在各自的电脑上运行。
gitclone git@server:/srv/sample.git
剩下的便是推送了。
管理公钥
如果团队很小,把每个人的公钥收集起来放到服务器的/home/git/.ssh/authorized_keys文件里就是可行的。如果团队有几百号人,就没法这么玩了,这时,可以用Gitosis来管理公钥。这里我们不介绍怎么玩Gitosis了,几百号人的团队基本都在500强了,相信找个高水平的Linux管理员问题不大。
管理权限
有很多不但视源代码如生命,而且视员工为窃贼的公司,会在版本控制系统里设置一套完善的权限控制,每个人是否有读写权限会精确到每个分支甚至每个目录下。因为Git是为Linux源代码托管而开发的,所以Git也继承了开源社区的精神,不支持权限控制。不过,因为Git支持钩子(hook),所以,可以在服务器端编写一系列脚本来控制提交等操作,达到权限控制的目的。Gitolite就是这个工具。这里我们也不介绍Gitolite了,不要把有限的生命浪费到权限斗争中。
小结
搭建Git服务器非常简单,通常10分钟即可完成;要方便管理公钥,用Gitosis;要像SVN那样变态地控制权限,用Gitolite。
参考文献:
http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000
https://git-scm.com/book/en/v2
https://git-scm.com/book/zh/v2