看了廖雪峰写的教程,由浅入深,花了三天的时间学完了,做一下学习笔记吧
Git简介
Linus创建Linux之后为方便管理Linux代码,自己用C写了这个分布式版本控制系统。2008年的时候GitHub网站上线,为开源项目免费提供Git存储。
集中式vs分布式:
集中式版本控制系统,版本库存放在中央服务器,每个人用的都是自己的电脑,用的时候先从服务器上获得新版本,工作完了再推送给中央服务器。缺点是必须联网使用,效率低。
分布式版本控制系统,没有中央服务器,每个人的电脑都是一个完整的版本库,工作的时候不需要联网,要想实现协作,每个人只需把各自修改的部分推送给对方。一般有一台电脑充当中央服务器,仅仅是为了方便大家交换修改。分布式安全性更高。
安装Git
Linux、Unix、Mac、Windows几大平台均能运行Git,最常用Linux就只记录Linux上的安装了。
首先,尝试在shell里输入git看看有没有安装:
在Ubuntu里安装很简单,一条命令sudo apt-get install git
若在其他 Linux版本里可以去Git官网下载源代码,解压后输入:./config, make, sudo make install
安装好之后,为本地git库设置:
$ git config --global user.name "Your Name"
$ git config --global user.email "[email protected]"
创建版本库
1、创建git库
合适的地方创建一个空目录,进入目录后执行git init命令把这个目录变成可以管理的仓库;
$ mkdir git
$ cd git
$ git init
注意,仓库创建好之后会生成一个隐藏的文件夹.git,用来跟踪管理版本,不能随意修改
2、添加文件到git库
注意版本控制系统只能跟踪文本文件的改动,没法跟踪二进制文件的变化。
添加文件分两步:
第一步,使用命令git add 添加文件到仓库
第二部,使用命令git commit –m “XXX” 提交改动,引号内为说明,一般输入改动记录
查看库状态、修改文件
假如对某个文件进行修改,可以运行git status 命令查看状态
上面显示文件修改过,但是没有准备提交。
虽然git告诉我们test.txt文件被修改了,但是没有显示具体修改了什么内容,这个时候需要git diff命令查看
结果显示在第二行添加了一句话。
在明确知道有哪些改动之后就可以提交到仓库了,提交修改跟提交新文件是同样的操作:
git add test.txt
在执行第二部之前,再次运行git status看看当前仓库的状态:
发现有改动准备提交,下一步就可以放心的提交了:
git commit –m “add one line”
提交完之后可再次查看状态:
总结:
?git status可以查看当前工作区的状态
?如git status告诉你有文件修改了,用git diff查看具体修改的内容,以决定是否提交修改
版本退回
在不断对文件进行修改后,然后不断提交修改到版本库里,每次提交到git就像保存了一个“快照”,在git里这个快照被称为commit,随时都能恢复到最近的一个版本。
首先用git log命令查看历史记录:
可以看见从最近到最远的提交日志,若嫌显示太乱可以加上–pretty=oneline参数:
每次改动只在一行内显示commit id(版本号)和改动内容。
好了,现在准备将test.txt文件回退到上一个版本:
在git中HEAD表示当前版本,上一个版本就是HEAD^,上上个版本就是HEAD^^,简略写法如当前往上100个版本写成HEAD~100。
使用git reset –hard HEAD^命令就回退到了上一个版本:
可以看到回到了第一个版本,这个时候再用git log查看版本记录:
发现之前的版本不见了,好比你从21世纪坐时光穿梭机来到了19世纪,想再回去21世纪已经回不去了,肿么办?
办法还是有的,只要能找到之前版本的commit id(版本号)就可以,使用git reflog查看每一次的命令:
发现之前的版本号为afa4020,这时就可以再恢复到哪个版本:
注:版本号仅仅是前几位,没有写全(全部的太长了0-0),git会根据前几位自动去找
总结:
?HEAD 指向当前的版本号,git允许在不同版本之间穿梭,git reset –hard commit_id
?穿梭前,用git log查看历史记录,以确定退回到哪个版本
?要重返未来,用git reflog查看历史命令以确定未来的版本号
工作区和暂存区
工作区就是在电脑上能看见的目录,比如之前新建的目录git就是一个工作区;
版本库,在工作区有一个隐藏的目录.git,这个就是版本库。
在版本库中最重要的一部分是称为stage的暂存区,还有自动创建的第一个分支master,以及指向mater的指针HEAD。
前几节讲到把文件添加到版本库分两步执行:
第一步git add 实际就是将文件添加到暂存区;
第二部git commit提交修改,就是将暂存区中的内容提交到当前分支,暂存区就会被清空。
管理修改
这一节还是为了进一步了解git工作机制。
举个例子,我们对一个文件进行一次修改,然后添加:git add filename
然后第二次对该文件进行修改,不执行add命令,然后执行git commit -m “…”
我们会发现仅仅是提交了第一次修改,第二次修改并没有提交。也就是说git管理的是修改,并不会对工作区的文件进行修改。
撤销修改
情况一:修改了文件,但没有add到暂存区,需要撤销文件的修改:git checkout – test.txt 或者 手动还原修改
情况二:修改文件后add到了暂存区,又再次修改了文件,需要撤销第二次修改:git checkout – test.txt 相当于把暂存区的恢复到工作区,此时暂存区有第一次的修改需要commit提交到分支。
情况三:修改文件后add到了暂存区,需要撤销修改:git reset HEAD test.txt 将暂存区修改删除,此时工作区还有修改没撤销,然后git checkout – test.txt 撤销工作区的修改
情况四:修改后add并commit了,需要撤销修改:git reset –hard HEAD^ 也就是版本退回
总结:一句话总结就是git checkout – 会使文件回到最近一次git commit或者git add前文件的状态,这个地方有点绕,必须花时间才能弄明白的。。。。。
删除文件
在工作区删除一个文件rm test.txt ,这个时候工作区和版本库就不一致了,git status会告诉你那些文件删除了,现在有两个选择:
一、确实是要从版本库中删除
git rm test.txt
git commit –m “remove test.txt”
二、恢复删除的文件:
git checkout – test.txt
远程仓库
我们可以自己搭建我们自己的git服务器,现阶段有一个神奇的网站GitHub提供git仓库托管服务,所以只需要免费注册一个GitHub账号:打开https://github.com/pricing进行注册。
本地Git库跟远程GitHub仓库之间传输是通过SSH加密的,所以需要一点设置:
第一步,创建SSH Key。在用户主目录下,看看有没有.ssh目录,如果有再看看目录下有没 没有id_rsa和id_rsa.pub两个文件,若都有,可直接下一步;若没有,打开shell创建
$ ssh-keygen –t rsa –C “your email [email protected]***.com”
这时在主目录下的.ssh目录下就有id_rsa和id_rsa.pub两个文件,id_rsa是私钥,id_rsa.pub是公钥
第二步,登录Github,打开设置,在左侧SSH keys里添加,点击“New SSH key”,title任意填 ,在key文本框里粘贴id_rsa.pub文件的内容。
这时Github就能识别你的电脑,允许你推送修改。多个电脑可添加多个公钥。
登录GitHub后右上角添加 new respository按钮,创建一个新的仓库。在respository name处填写git,其他保持默认,就成功创建了一个新的git仓库。目前,这个仓库还是空的,首先我们把本地git库关联我们的远程仓库:
$ git remote add origin https://github.com/JasonLiu1991/git.git
千万注意将JasonLiu1991替换成你的GitHub的账户名。
添加后,远程库的名字就是origin,这个是git默认的叫法,也可以改成别的
这个地方常用的命令:
git remote 不带选项时,就是列出所有远程主机:
git remote -v -v选项就是查看远程主机的网址:
git remote show <主机名> 查看该主机的详细信息
git remote add <主机名> <网址> 添加远程主机
git remote rm <主机名> 删除远程主机
git remote rename <原主机名> <新主机名> 重命名
下一步就是将本地库的内容推送到远程库:
$ git push –u origin master
该命令的意思就是将本地的master分支推送到远程origin主机上。
由于远程库是空的,第一次推送master分支时加上-u参数,Git不仅会将master分支推送到远程的master分支,还会将本地的master分支关联远程的master分支,方便以后。
这时,在GitHub页面上就看到了同步过来的内容,以后本地有修改提交了就可以通过命令:
$ git push origin master
至此,就拥有了完整的分布式版本库。
git push命令详解:
$ git push <远程主机名> <本地分支名>:<远程分支名>
如果省略远程分支名,则表示将本地分支推送到与之存在“追踪关系”的远程分支,若远程分支不存在就自动创建。git push origin master 就是将本地的master分支推送到origin主机的master分支。如果后者不存在,则会被新建。
如果省略本地分支名,则表示删除指定的远程分支,因为这等同于推送一个空的本地分支到远程分支
$ git push origin :master
等同于
$ git push origin --delete master
注:
这里还会有一个问题就是每次push的时候都需要输入账户名密码,有点麻烦,解决办法:
在本地git库的目录下有一个.git目录,.git里有一个config文件,
将url = https:// github.com/JasonLiu1991/git. git替换成:
url = https://[UserName]:[Password]@github.com/JasonLiu1991/git. git即可
从远程库克隆
$ git clone <版本库的网址>
该命令就会在本地生成一个目录,与远程的版本库同名
分支管理
在版本退回一节里,每次提交Git都将他们串成一条时间线,这条时间线就是一个分支。目前只有一条时间线,叫做主分支master。master指向提交,HEAD指向当前分支:
每次提交,master向前移动一步。
当我们创建分支,例如dev,git就新建了一个指针叫dev,创建时指向master相同的提交,HEAD指向dev,表示当前分支在dev上:
图上可以看出,git创建一个分支就是增加了一个指针,移动了一下HEAD,文件没有变化。
从现在开始,对工作区的修改就是针对dev分支了,比如提交修改,就是dev向前移动一步:
假如在dev上的工作完成了,就可以把dev合并到master上,方法就是直接把master指针指向dev当前的提交:
合并完之后甚至可以删除dev分支。
原理弄明白了就开始操作
首先,建立分支dev,并切换到分支dev
$ git checkout -b dev
参数-b表示创建并切换,相当于以下两条命令:
$git branch dev 创建分支
$git checkout dev 切换到分支
$ git branch 查看当前分支,前面带*号表示当前分支:
现在我们再提交修改就是发生在dev分支之上了,当然可以随时切换:git branch master
切换回master分支之后,我们想把dev的工作成果合并到master:
$ git merge dev
git merge 命令用于合并指定分支到当前分支:
可以看到是Fast-forward,这是快速合并。
合并完成后就可以删除dev分支了:
$ git branch –d dev
小结:
Git鼓励大量使用分支:
查看分支:git branch
创建分支:git branch
切换分支:git checkout
创建+切换分支:git checkout -b
合并某分支到当前分支:git merge
删除分支:git branch -d
解决冲突
合并的时候会遇到冲突,举例:
新建一个分支feature1
$ git checkout –b feature1
修改文件最后一行,加上ttttttt字符,并提交修改
切换回主分支,同样修改文件,在最后一行加上pppppppp字符,提交修改
现在两个分支各自有不同的提交:
现在合并,会有冲突:
提示存在冲突,必须解决后才再提交修改
Git用<<<<<<<,=======,>>>>>>>标记出不同分支的内容,我们手动修改后就提交了
我们用git log --graph --pretty=oneline
可以看到分支的合并情况:
分支管理策略
在前面介绍到的Fast forward模式,删除分支后就会丢失分支信息。
如果要强行禁用Fast forward模式,git会在merge时生成一个新的commit,这样从分支历史上就能看到分支信息了。
命令:git merge --no-ff –m “******” dev
因为这样合并是要创建新commit,所以要加上-m参数。这样合并后就能用log查看分支历史:git log --graph --pretty=oneline
不使用Fast forward模式,原理如图:
分支策略:
在实际开发中,我们应该按照几个基本原则进行分支管理:
首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;
那在哪干活呢?干活都在dev分支上,也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本;
你和你的小伙伴们每个人都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就可以了。
所以,团队合作的分支看起来就像这样:
Bug分支
软件开发经常需要修复bug,在git中,每个bug都可以通过一个新的临时分支开修复,修复后合并,然后将分支删除。
好了,当接到任务修复一个代号101的bug任务,很自然的,想创建一个分支issue-101来修复它,但是,等等,当前在dev上进行的工作还没完成不想提交,怎么办?
幸好git提供了一个stash功能,将工作现场暂时存储起来,需要的时候再恢复回来继续工作:
现在可以放心的来修复bug了,假如需要在master分支上修复这个bug,就从master创建分支:
等修复完之后,切换到master分支,完成合并,最后删除issue-101分支就完成了。
Bug修复完了,是时候回到dev分支继续工作了,现在就需要恢复之前存储起来的工作区:
两种方法:
首先git stash list命令查看一下:
方法一,git stash apply恢复,但是恢复后stash内容不删除,需要用git stash drop来删除
方法二,git stash pop,恢复的同时删除
当然也可以多次stash,恢复的时候用git stash list查看,然后恢复指定的stash:
$ git stash apply [email protected]{0}
小结:
修复bug时,我们会通过创建新的bug分支进行修复,然后合并,最后删除;
当手头工作没有完成时,先把工作现场git stash一下,然后去修复bug,修复后,再git stash pop,回到工作现场。
Feature分支
开发一个新feature,最好新建一个分支;
如果要丢弃一个没有被合并过的分支,可以通过git branch -D 强行删除。
git branch –d 这样删除一个没合并过的分支会提示你失败。
多人协作
多人协作的工作模式通常是这样:
首先,可以试图用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 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,如果有冲突,要先处理冲突。
标签管理
发布一个版本时,我们通常先在版本库中打一个标签(tag),这样,就唯一确定了打标签时刻的版本。
创建标签:
切换到需要打标签的分支上,然后,git tag v1.0 就可以打一个新标签,git tag查看已有标签:
若要对历史某个版本打标签,只需要git log查看历史commit的id就可以:
git show <tagname>
git show 可以查看标签的详细内容
还可以创建带有说明的标签,-a指定标签名,-m指定说明文字
$ git tag –a v0.1 –m “version 0.1 released” 9e93424
操作标签:
如果标签打错了,可以删除:
$ git tag –d v1.0
因为创建的标签存储在本地,还没有推送到远程
若要推送某个标签:$ git push origin <tagname>
若要推送全部尚未推送的标签:$ git push origin –tags
如果标签已经推送到了远程,要删除远程标签就麻烦点,点从本地开始删除:
$ git tag –d v0.9
然后远程删除:
$ git push origin :refs/tags/v0.9
自定义git
比如,让git显示颜色:
$ git config --global color.ui true
忽略特殊文件:
在工作区的一些文件不想提交到远程,但每次git status都会显示Untracked files…
要想让git忽略某些文件,需要在工作区创建一个特殊的.gitignore文件,然后把忽略的文件名填进去,注意.gitignore文件本身需要放到版本库里。
设置别名
比如,git status不好敲,可以设置成git st,具体命令:
$ git config --gobal alias.st status
现在敲git st效果等于git status
常用的:
$ git config --global alias.co checkout
$ git config --global alias.ci commit
$ git config --global alias.br branch
git reset HEAD file命令是将暂存区的修改撤销掉(unstage),重新放回工作区,可设置别名:
$ git config --global alias.unstage ‘reset HEAD‘
甚至还有人丧心病狂地把lg配置成了:
git config --global alias.lg "log --color --graph --pretty=format:‘%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset‘ --abbrev-commit"
效果很酷炫:
注:配置git时加上–global参数意思是针对当前用户起作用,如果不加,仅针对当前仓库
配置文件:
每个仓库都有一个配置文件,.git/config
每个用户也有一个配置文件,在用户主目录下.gitconfig
最后附上参考的两位大神的原文地址:
廖雪峰
http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000
阮一峰
http://www.ruanyifeng.com/blog/2014/06/git_remote.html