最近实习中上手了一个简单的TMC项目,实际开发中使用SVN,可是忽然有了想对比下Git和SVN的想法,因而就上网看了下教程。GitHub一直在用,可是基本都是clone别人的项目,因此对Git的使用一直没有跟进,因此如今算是亡羊补牢。
就实际工做而言,只须要掌握常见的Git提交文件以及管理分支的几个指令,学会怎样进行版本回退等(Git的命令清单)就能够知足平常团队写做需求。为了进一步了解经常使用指令,本文对Git作一个详尽的介绍,主要内容是对廖雪峰的官方网站内容的总结,尊重版权,从我作起。linux
集中式版本控制系统(SVN):版本库存放在服务器中,工做的时候须要先从服务器拷贝最新版本到本地,修改完成后推送给服务器。注意:单个非服务器的电脑之间是没法相互通讯的。集中式版本控制的最大问题在于须要时刻联网,对局域网来讲,带宽大,速度快,可是对于互联网,速度就会很慢。
分布式版本控制系统(Git):每一个用户的电脑上都有一个完整的版本库,工做的时候不须要联网,多用户之间交换修改只须要将修改互相推送给对方。虽然也有一个形式上的中央服务器,可是该服务器做用是用来方便交换用户之间的修改。优势是安全性较好,一方的电脑故障,在另外一个电脑上依旧保存有完整的版本库。并且,减小了对服务器的依赖。git
Windows上安装linux/Unix须要Cygwin工具,配置较为复杂。msysgit是Windows上的Git,从https://git-for-windows.githu...下载,而后按默认选项安装便可。安装完成后,在开始菜单里找到“Git”->“Git Bash”,蹦出一个相似命令行窗口的东西,就说明Git安装成功!
安装完成后,还须要最后一步设置,在命令行输入:
$ git config --global user.name "Your Name"
$ git config --global user.email "email@example.com"
由于Git是分布式版本控制系统,因此,每一个机器都必须自报家门:你的名字和Email地址。
注意git config命令的--global参数,用了这个参数,表示你这台机器上全部的Git仓库都会使用这个配置,固然也能够对某个仓库指定不一样的用户名和Email地址。github
工做区(working directory)就是在电脑里可以看到的目录,工做区中有一个隐藏目录.git,这是Git的版本库,里面存储了名叫stage的暂存区,还有Git为用户自动建立的一个分支master,以及指向master的一个指针叫HEAD。windows
在将修改过的文件提交到Git版本库的时候,须要git add将文件添加进去,实际上就是添加到暂存区,而后经过git commit提交文件,实质上就是将暂存区的全部文件一次性提交。一旦提交后,再使用git status命令,显示工做区是干净的。安全
版本库即一个仓库(repository),这个目录里的全部文件均可以被Git管理起来,每一个文件的修改、删除,Git都能跟踪,以便于任什么时候刻均可以追踪历史。建立版本库的代码以下:服务器
pwd用于显示当前目录,若是你使用Windows系统,为了不遇到各类莫名其妙的问题,请确保目录名(包括父目录)不包含中文。
而后经过git init将版本库变成Git能够管理的仓库:网络
在learngit目录下建立文件index.js,而后将其添加到Git仓库,须要两步,用git add <fileName>添加文件,用git commit提交到仓库:app
-m后的字符串是提交说明,提交后输出版本库的改动信息。add和commit的区别在于,add能够屡次添加不一样的文件,commit能够一次提交多个文件。ssh
对index.js文件作修改,而后经过git status 查询状态:分布式
Git指出那些文件被修改了,并给出建议提交更改的命令。“no changes added to commit”代表改变的部分并未被提交。甚至能够经过git diff <filename>查询更改发生的具体内容。
更改完以后就是提交文件,这和在版本库中增长文件操做相同,先add再commit。
提交完成后再观察版本库的目前状态,能够发现,没有须要提交的更改,工做树是干净的。
总之:git status 查看是否有须要提交的文件;git diff <fileName>输出更改的具体部分。
事实上我对index.js作了不止一次修改和提交,所以能够经过 git log 查看最近屡次修改的内容,这时能够看出为何在git commit命令后加-m “修改说明”了,这些修改说明会在log时显示出来,依次显式:版本号、修改日期、提交的修改说明。
回退到先前版本:Git用HEAD表示当前版本,HEAD^表示上一版本,HEAD^^表示再上一个版本。若是回溯的版本比价多,能够用HEAD~100表示回溯到100版本。回溯命令是git reset --hard <回溯版本>,例如想回溯到modified index.js,使用命令:
git reset --hard HEAD^
Git显示已经恢复到上一版本,经过cat <fileName>命令查看文件内容,能够看到,文件已经恢复。
Git的版本回退速度很是快,由于Git在内部有个指向当前版本的HEAD指针,当你回退版本的时候,Git仅仅是把HEAD从当前版本指向旧版本,而后顺便把工做区的文件更新了。
一种更直接的版本回退方式是经过 git reset --hard <version-id>,并且能够经过 git reflog显式全部先前输入的命令来帮助得到版本号。
总结:
HEAD指向的版本就是当前版本,所以,Git容许咱们在版本的历史之间穿梭,使用命令git reset --hard commit_id。
穿梭前,用git log能够查看提交历史,以便肯定要回退到哪一个版本。
要重返将来,用git reflog查看命令历史,以便肯定要回到将来的哪一个版本。
为何Git比其余版本控制系统设计得优秀,由于Git跟踪并管理的是修改,而非文件。假设有这样一个操做过程:第一次修改 -> git add -> 第二次修改 -> git commit,此时经过git status显示第二次修改并无被提交,这是由于只有经过git add才能将文件的修改提交到暂存区,而后git commit提交的是这部分修改,不在暂存区的修改是没办法提交的。总之,若是不add到暂存区,就不能commit到版本库中。
假设工做区有一份index.js文件,将它add到暂存区中,
而后,在文件中加入 and tall,此时cat index.js显示以下,更改已经生效,可是没有add。
此时经过git checkout撤销更改,此处的撤销是返回到上一次add以后的文件:
命令git checkout -- file意思就是,把index.js文件在工做区的修改所有撤销,这里有两种状况:
一种是index.js自修改后尚未被放到暂存区,如今,撤销修改就回到和版本库如出一辙的状态;
一种是index.js已经添加到暂存区后,又做了修改,如今,撤销修改就回到添加到暂存区后的状态。
总之,就是让这个文件回到最近一次git commit或git add时的状态。git checkout -- file命令中的--很重要,没有--,就变成了“切换到另外一个分支”的命令。
若是已经将修改add到暂存区,怎么撤回呢?
此时经过git reset HEAD <fileName>将暂存区的修改撤销,从新回到工做区:
此时内容修改依旧存在,可是git status代表changes not staged for commit,即修改没有add到暂存区,这时就可使用上述git checkout撤销工做区的更改:
cat输出原来内容,上传到暂存区的更改被撤销。
场景1:当你改乱了工做区某个文件的内容,想直接丢弃工做区的修改时,用命令git checkout -- file。可是这仅仅返回到上一次add或commit时的版本。
场景2:当你不但改乱了工做区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令git reset HEAD fileName,就回到了场景1,第二步按场景1操做。
场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,参考版本回退一节,不过前提是没有推送到远程库。
若是须要删除文件,使用rm <fileName>便可,删除以后,工做区和版本库中的文件不一致,若是是确认删除,再commit一下便可,文件就会从版本库删除;若是是误删,版本库里依旧有文件,所以可使用git checkout用版本库中的文件恢复到工做区,git checkout 其实就是用版本库中版本替换工做区的版本。
上述关于Git管理仓库文件的功能和集中式版本管理系统SVN没有区别,如下介绍Git的分布式特性:远程仓库。GitHub网站提供了远程仓库的简易实现,可是因为由于本地Git仓库和GitHub之间是经过SSH加密的,因此须要进行设置SSH。
建立ssh密匙
命令: ssh-keygen -t rsa -C “emailAdress”
登陆GitHub,打开SSH Keys页面,点击 add SSH Key,粘贴id-rsa.pub的内容。而后在GitHub网站建立本身的仓库learngit,创建本地库和远程库的链接:
而后将本地版本库push到远程github,本地库的内容推送到远程,用git push命令,其实是把当前分支master推送到远程。因为远程库是空的,咱们第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在之后的推送或者拉取时就能够简化命令。
因为远程库是空的,咱们第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在之后的推送或者拉取时就能够简化命令。
推送成功后,能够马上在GitHub页面中看到远程库的内容已经和本地如出一辙。从如今起,只要本地做了提交,就能够经过命令:
$ git push origin master
把本地master分支的最新修改推送至GitHub,如今,你就拥有了真正的分布式版本库!
总之,要记住如下几点:
要关联一个远程库,使用命令git remote add origin git@server-name:path/repo-name.git;
关联后,使用命令git push -u origin master第一次推送master分支的全部内容;
此后,每次本地提交后,只要有必要,就可使用命令git push origin master推送最新修改;
分布式版本系统的最大好处之一是在本地工做彻底不须要考虑远程库的存在,也就是有没有联网均可以正常工做,而SVN在没有联网的时候是拒绝干活的!当有网络的时候,再把本地提交推送一下就完成了同步,真是太方便了!
2.4 克隆仓库
要克隆一个仓库,首先必须知道仓库的地址,而后使用git clone命令克隆。Git支持多种协议,包括https,但经过ssh支持的原生git协议速度最快。
若是在Git中仅有一条时间线,也就意味着仅有一条分支:master分支,HEAD指针指向master分支,master指向最新的提交,因此HEAD指向的就是当前分支。
每次提交,master就会向前移动一步,这样随着不断提交,master分支的线也愈来愈长。当咱们建立新的分支dev时,Git新建了一个指针叫作dev,指向master相同的提交,再把HEAD指向dev,就表示当前分支在dev上。
Git建立一个分支很快,由于除了增长一个dev指针,改变HEAD的指向,工做区的文件没有变化。指向dev后,全部的修改和提交就是针对dev了。
当分支上的工做完成以后,就能够把dev合并到master之上,合并的方式就是把master指向dev的当前提交。
合并分支后,就能够把dev分支删除,删除分支就是删除dev指针。
建立dev分支,而后切换到dev分支,git checkout 加上-b至关于建立而且换分支,其中git branch <branchName>是建立分支、git checkout <branchName>是转换分支;git branch命令列出全部分支,当前分支前会有*号。
随后对index.js进行修改并add和commit到分支,在dev分支上正常提交,随后切换到master分支:git checkout master,此时查看index.js发现,内容没有修改,这很正常,由于主分支没有修改。
如今将dev分支合并到master分支上,git merge命令用于合并指定分支到当前分支,合并后查看index.js的内容,发现修改已经反应到master分支上。
合并后就能够删除dev分支,删除以后分支就只有master了。
由于建立、合并和删除分支很是快,因此Git鼓励你使用分支完成某个任务,合并后再删掉分支,这和直接在master分支上工做效果是同样的,但过程更安全。
注意,以上合并基于fast-forward模式,这种模式下,删除分以后,会丢掉分支信息,对此可使用—no-ff方式进行合并:
git merge --no-ff -m "merge with no-ff" dev
Git鼓励大量使用分支:
查看分支:git branch
建立分支:git branch <name>
切换分支:git checkout <name>
建立+切换分支:git checkout -b <name>
合并某分支到当前分支:git merge <name>
删除分支:git branch -d <name>
首先,master分支应该是很是稳定的,也就是仅用来发布新版本,平时不能在上面干活;那在哪干活呢?干活都在dev分支上,也就是说,dev分支是不稳定的,到某个时候,好比1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本;你和你的小伙伴们每一个人都在dev分支上干活,每一个人都有本身的分支,时不时地往dev分支上合并就能够了。
若是工做过程当中须要解决一个bug,此时最好可以将当前工做现场保存起来,而后建立bug分支,并转换到须要修复bug的分支进行修复。在bug分支修复bug以后,将其合并到目标分支上,而后删除bug分支。
Git提供了一个stash功能,能够将当前的工做现场保存起来,等之后恢复现场后继续工做:
git stash
bug修复以后,切换回到原来分支,经过 git stash list查看工做现场列表,而后进行恢复:采用git stash apply恢复,恢复以后,stash内容并不删除,用git stash drop删除便可;采用git stash pop恢复,同时stash的内容也被删除,这是针对只有一个stash的状况。固然,若是进行了屡次stash,恢复的时候须要先用git stash list查看须要回复的stash的名称,经过git stash apply <stashName>恢复:
git stash apply stash@{0}
软件开发过程当中,会有新功能不断加进来,添加新功能时,为了保证主分支不被弄乱,最好是新建一个feature分支,在上面开发,完成后再进行合并,并删除feature分支。也可能最后设计好的新功能不须要了,也就是须要删除未合并的分支,这时候删除分支就须要强制删除,也就是 git branch -D feature命令。
当你从远程仓库克隆时,实际上Git自动把本地的master分支和远程的master分支对应起来了,而且,远程仓库的默认名称是origin。要查看远程库的信息,用git remote:
$ git remote
或者,用git remote -v显示更详细的信息:
$ git remote -v origin git@github.com:michaelliao/learngit.git (fetch) origin git@github.com:michaelliao/learngit.git (push)
上面显示了能够抓取和推送的origin的地址。若是没有推送权限,就看不到push的地址。
推送分支,就是把该分支上的全部本地提交推送到远程库。推送时,要指定本地分支,这样,Git就会把该分支推送到远程库对应的远程分支上:
$ git push origin master
并非必定要把本地分支往远程推送,那么,哪些分支须要推送,哪些不须要呢?
master分支是主分支,所以要时刻与远程同步;
dev分支是开发分支,团队全部成员都须要在上面工做,因此也须要与远程同步;
bug分支只用于在本地修复bug,就不必推到远程了,除非老板要看看你每周到底修复了几个bug;
feature分支是否推到远程,取决于你是否和你的小伙伴合做在上面开发。
该文件指出哪些文件不该该加到版本库中,已经添加过的文件不受影响,因此最好在项目一开始就配置.gitignore文件,不然就没啥做用了。.gitignore文件自己要放到版本库里,而且能够对.gitignore作版本管理!文件中的规则包含两类:过滤规则和添加规则,区别在于添加规则多了开头的感叹号。过滤规则:/表示.gitignore所在的目录,通常是主目录。 以星号“*”通配多个字符; 以问号“?”通配单个字符 以方括号“[]”包含单个字符的匹配列表; 以叹号“!”表示不忽略(跟踪)匹配到的文件或目录;git 对于 .ignore 配置文件是按行从上到下进行规则匹配的,意味着若是前面的规则匹配的范围更大,则后面的规则将不会生效。(1)规则:fd1/* 说明:忽略目录 fd1 下的所有内容;注意,无论是根目录下的 /fd1/ 目录,仍是某个子目录 /child/fd1/ 目录,都会被忽略;(2)规则:/fd1/* 说明:忽略根目录下的 /fd1/ 目录的所有内容;(3)规则:/*!.gitignore!/fw/bin/!/fw/sf/说明:忽略所有内容,可是不忽略 .gitignore 文件、根目录下的 /fw/bin/ 和 /fw/sf/ 目录;