git代码管理原理

做者:zccst SVN用得很熟了,可是git一直用得不习惯,看来有必要学习一下原理。 Git作为一个资源管理和跟踪系统,若是想要把本身的文件托管在Git上,那么首先你得让Git知道你须要管理的文件在哪。好比说如今我有一个项目,它在test文件夹里,我想让Git管理这个项目,这个时候你需进入到这个目录,而后运行“git init”命令。这个时候Git就会在该目录下生成一个.git的隐藏目录,Git用来进行版本控制和内容跟踪的全部文件都在该文件夹下。 处于git跟踪下的文件只具备三种状态: 1.Modified(working directory):被修改过的文件 2.Staged(staging area):经过git add添加到暂存区域的文件 3.Committed(git directory):经过git commit提交到仓库的文件 因此,通常的git工做流程多是这样:修改过某些文件,而后把这些文件添加都暂缓区,再提交到仓库中造成一个版本或快照,最后提交到git服务器上。而在中间,可能伴随着分支管理,分支切换,撤消与合并。 可能有些人会以为很奇怪,为何git会有暂存区域这个概念,直接提交到仓库中不就ok了。其实这是git为了作版本控制用的,试想若是没有暂存区域,每修改一个文件,就会造成一个版本,太过频繁,不易于管理。暂存区域其实就是下一个版本的文件清单,你能够自由控制该往仓库中提交什么文件,这也能够避免在一个版本中包含一些中间文件,好比编译后的文件。 [img]http://dl2.iteye.com/upload/attachment/0106/5951/57e24f0d-419c-3ed5-b6f8-c8178b0be289.png[/img] [img]http://dl2.iteye.com/upload/attachment/0106/5953/a24045c6-d7ff-3447-9295-91843c24a15b.png[/img] 1.Git是分布式的版本控制系统,它没有中心服务器的概念(虽然实际开发中能够建一个中心服务器),每一台开发机器上都保存完整的历史记录;可是它有本地代码仓库和远程代码仓库的概念(否则怎么多人协做?),并且能够追踪多个远程仓库; 2.Git可以很是快地创建分支和合并分支,并具备强大的跟踪分支和切换分支的能力。 Git的关键词:working directory; repository; stage; commit; remote; branch; merge   每个项目都应该有一个工做目录(Working directory),咱们能够本身建一个目录,而后把这个目录里面的代码用Git管理起来(使用git init命令和git add命令),也能够经过git clone命令从别的地方克隆一个项目过来自动生成一个工做目录。在工做目录中的文件就是当前编辑和修改的文件,若是是新创建的目录或新clone来的目录,工做目录中的文件就是该项目最新的状态。Git是在本地保存有全部的历史记录和分支记录的,这些内容都在工做目录的.git目录中,称之为本地仓库(local repository)。当切换分支或查看之前的历史版本时,工做目录中的文件自动改变(这才是重点,工做无需切换目录,目录中的文件会自动切换)。工做目录中的文件有三种状态:已修改、已暂存(stage)、已提交。修改后的文件能够先加入暂存区域,一次工做结束后一块儿提交。   Git是分布式的,没有中心服务器的概念,但实际工做中仍然能够把代码仓库放到一台你们均可以访问的服务器上,作实际的中心服务器使用(仅在小团队时使用此工做流程,缘由后面详述)。在本地机器上工做完后,使用git push命令把仓库推送到服务器上,换一个地方换一台机器后,只须要git clone一下,又能够得到全部的代码(包含全部的历史记录及分支)继续工做。服务器故障也没问题,由于每个工做的机器上都保存有完整的代码仓库,因此从不用担忧代码丢失。没有网络也没有关系,在本地机器上照样能够提交(git commit),由于整个仓库就在本身的机器上,当有网络时,push一下就能够了。   Git有远程仓库(remote repository)的概念,并且能够管理不少个远程仓库,远程仓库能够是服务器,也能够是别人的我的计算机(但通常没有人这么用),每个远程仓库都有一个简短的名字和一个地址,最开始clone代码的那个远程仓库别名每每默认为origin,本身添加的远程仓库能够随意指定别名,固然全部的远程仓库均可以随意修改别名。能够从远程仓库获取代码(git fetch 命令或 git pull命令),也能够把本身的代码推送到远程仓库(git push命令,须要写权限)。 既然Git便可以随便从远程仓库获取代码,又能够把本身的代码推送到远程仓库,那么当多人协做时,岂不会乱套吗?解决这个问题的,就是Git的必杀之技——建立分支及分支合并。下面要用图表来讲明问题了。下面一系列图片来自Git官方网站上的电子书《Pro Git》。   首先,随着一次次的提交,在本地代码库中造成一个主分支,以下图: [img]http://dl2.iteye.com/upload/attachment/0106/5956/97928e0f-7d07-3b3b-af7d-a2aab9a2abc9.png[/img] 有时为了开发新特性,随时能够开一个新分支,以下图: [img]http://dl2.iteye.com/upload/attachment/0106/5958/5e7fecc2-96f2-3ae9-aa63-b52462f1e75e.png[/img] 新分支和主分支之间能够随意切换,随着分支的发展,形式以下图: [img]http://dl2.iteye.com/upload/attachment/0106/5960/c98fa04d-aec4-306a-82a1-55dee587a178.png[/img] 主分支也能够向前发展,以下: [img]http://dl2.iteye.com/upload/attachment/0106/5962/9a966bb0-e954-3d6b-8200-73ccfc94c201.png[/img] 最终,当新分支代码很稳定之后,能够将其合并到主分支,以下图: [img]http://dl2.iteye.com/upload/attachment/0106/5964/e52691d2-01dc-3ba2-8022-f52111bf71b9.png[/img] 而可以防止多人协做时出现混乱的关键就在于,当从远程仓库clone代码库到本地或fetch代码库到本地时,远程分支的标记并不等于本地分支的标记。从远程clone一个代码库到本地后,其master分支有两个标记,一个标记为origin/master表示远程库中的master分支,一个标记为master,表示本地的master分支。以下图: [img]http://dl2.iteye.com/upload/attachment/0106/5968/71c16837-dfc1-3576-8a04-17c6be19378e.png[/img] 能够想象,因为别人的工做,远程仓库中的master分支确定会向前继续移动,可是在下次联网以前,该origin/master标记不会移动。而本地的master标记继续向前移动。 [img]http://dl2.iteye.com/upload/attachment/0106/5970/ae33c4fa-dabc-39b2-9cc0-abc273e4cd04.png[/img] 直到下次联网,使用git fetch命令将远程仓库的内容取回本地,origin/master标记才会改变位置,这时,看起来就像是两个分支,以下图: [img]http://dl2.iteye.com/upload/attachment/0106/5972/b4dde936-de51-36c2-bc32-254b70f118af.png[/img] 最后,将origin/master分支合并到master分支中(使用get merge命令),本地代码库又一次变成了一个单一的master分支,继续向前开发,并能够将它push到远程仓库,供别人使用。   Git冲突的处理彻底靠人工完成。(从逻辑上讲,机器也不可能完美处理冲突。)好比一个小型团队一块儿工做,他们能够设置一个服务器用于保存远程Git仓库,而后每一个人工做以前先从该远程仓库fetch代码,接着工做,工做完成后,先在本地提交,最后push到远程仓库。可是当一我的push的时候,已经有人在他以前push了,若是他们工做在同一个分支,就会出现冲突。解决冲突的办法就是先把别人push的内容再次fetch下来,合并分支,而后再push。   经过以前对git原理的了解,能够分析得出使用Git时有如下几种工做流程:   1.一我的单干,不须要考虑冲突,随时能够开分支、合并分支和切换分支,随时能够本地提交。若是为了防止代码丢失,能够开一个服务器,每次工做完成就push到服务器上;   2.小型团队合做,如前所述,开一个服务器保存代码仓库,而后全部的人把该服务器当成远程仓库,工做以前先fetch,工做以后再push。若是有冲突,则先fetch,合并分支解决冲突后再push。若是团队人数太多,每一个人都向该服务器push,那冲突该是有多少?有可能一个开发者第一次向服务器push的时候,有人在他以前已经push过了,他只好先fetch,手工合并解决冲突,可等他再次push的时候,发现又有人再他以前已经push了,因而他只好再作一次解决冲突的流程,但是若是在他工做的时候,又有人push了呢?这也是以前讲的该工做流程只适合小型开发团队的缘由。   以上流程通过适当修改也能够供大型团队使用,那就是将团队分组,每一个组的成员共用一个服务器当远程仓库,组长合并了该组的工做成果后,再push到另外一个服务器当总的远程仓库,这样就能够大大减小冲突的数量,减小工做量。   3.开源项目的合做,在这种状况下,每一个人都把本身的仓库暴露在互联网上。开源项目的组织者或负责人将全部人的仓库设为远程仓库,并把有意义的工做合并到主分支,而后发布官方的Git仓库。每一个开发者从官方仓库fetch代码后,完成本身的工做,而后再把它push到互联网上本身的仓库,等着项目负责人将本身的工做整合到官方仓库中。若是项目负责人不干了,改人了,只要还有人继续开发,该项目就能够继续下去。碰到团队比较大的状况,也能够进行分组。   服务器的建设也至关简单,由于Git支持以SSH、HTTP等协议传输数据,若是须要对服务器有写权限,就开通SSH服务吧,设一个帐户供全部人访问Git仓库便可。若是只须要只读权限,使用任何一个HTTP服务器都可。关于Git服务器的建设,请自行参考官方文档。若是是我的的、开源的项目,可使用Github网站提供的服务,直接存储在互联网上。(Github私人仓库是要收钱的。) 看来要把Git讲清楚并不容易,用了这么多篇幅。下面把Git经常使用的命令回顾一下:   git config 配置Git,通常使用不须要特别配置,但至少要设置开发者的名字和邮箱   git init和git add 建立一个新仓库,并跟踪工做目录下的文件   git clone 从远程克隆一个项目,包括工做目录和仓库   git add 将修改后的文件放入缓存区域(staging area),或这表示冲突已经解决   git status 显示文件状态,是已修改仍是已缓存仍是已提交   git commit 提交项目   git remote 管理远程仓库   git fetch和git pull 从远程仓库抓取数据   git push 向远程仓库推送数据   git branch和git merge 建立分支及合并分支   git checkout 切换分支 [size=large][b]第一次使用[/b][/size] 在第一次使用Git时,你须要告诉你的协同开发者,你是谁以及你的邮箱,在你提交的时候,Git须要这两个信息。具体经过如下命令设置: git config --global user.name “Test OSS” git config --global user.email oss.lzu.edu.cn@gmail.com 当你把Git设置好以后,若是你要和从Git服务器上得到仓库,或者向Git服务器提交你的代码(好比github),你可能须要生成你本身的ssh密钥对。Git支持4种与服务器端通讯的协议:git、http、ssh和https。其中git只是一个只读协议,也就是说你只能够从服务器端获取仓库,可是你不能提交你本身的代码。而http和https用的不多,大部分都只支持ssh协议和Git协议。 当你经过ssh协议与远端服务器进行通讯的时候,你能够经过如下命令生成ssh密钥对: ssh-keygen -t rsa Windows下 #ssh-keygen.exe #cat ~/.ssh/id_rsa.pub 若是你没有指定密钥名称和存放路径的话,它默认把两个不对称密钥放在你的家目录下的.ssh目录下,密钥文件默认名称为id_rsa和id_rsa.pub,前者是私钥,后者是公钥。中间可能会要你设定访问密钥密码,这个能够设,能够不设,但为了安全考虑,仍是建议你设一个访问密码。不然,意味着任何持有你密钥的人均可以使用该密钥。 而后把你的公钥发给Git仓库管理员,而后你就能够经过ssh协议来访问服务器端,期间程序会自动进行密钥对匹配,若是你设了访问密码,你可能须要输入密码。 进入某个存放代码的目录 git clone git://git2.kernel.org/pub/scm/git/git.git [size=large][b]GIT基本流程[/b][/size] 1.初始化仓库 初始化仓库有两种状况,一种是直接在一个空目录里创建一个项目,这时候你能够这样干: git init 另外一种是从其余机器复制一个仓库,好比这样: git clone git://git2.kernel.org/pub/scm/git/git.git (远程仓库) git clone https://github.com/jquery/jquery.git (远程仓库) git clone git@github.com:wengpingbo/MicroBlog.git (远程仓库) git clone /home/oss/test.git (本地仓库) 第一次从服务器上复制一个仓库,可能比较慢,由于git要把全部的历史记录和版本所有复制下来,这也算git的一个弊端吧! 复制完后,就会在当前目录下生成一个工做目录,名字以仓库名字命名。若是你不想指定目录,那就在上面的命令后加一个目录就ok了。好比我想把test仓库放到oss仓库中:git clone /home/oss/test.git oss 以后,你就能够开始你的工做啦! 2.添加文件 在编辑了几个文档以后,你可能忽然想起来,好像文件尚未让git跟踪。Git并不会实时的跟踪你的文件,只在你明确让它记录你的文件时,它才会把指定的文件的当前状态记录到仓库中去,而后又撒手无论了。我想这就是说git笨的缘由吧。这个时候,你须要手动添加你的文件当暂存区域: git add filename1 filename2 若是你懒得一个一个加,你能够试试这个: git add -A 它会把当前目录下全部的文件都添加到暂存区域。 3.添加一个版本 在添加完文件后,你可能以为应该建立一个commit了。 git commit 怎么样?是否是有点不对劲,好像这个命令并无按你想象的那样跳出一个提交成功的提示,而是直接跑到了你在配置中指定的编辑器中了。仔细看一下,原来是让你给这个版本作一些备注,随便写点什么,而后保存退出就ok了。若是你不想这么麻烦,能够这么干: git commit -m ‘initial version’ 可能你以为以前讲的太罗嗦了,提交一个commit还这么麻烦,其实有一个捷径可使你跳过添加文件这个过程: git commit -a -m ‘initial version’ 大功告成,这个命令会把以前全部的已经添加的文件都加入到这个版本中。 可能你又有疑问了,以前添加的文件不是自动会加入到下一个版本中吗,问什么还加这个-a参数? 其实git add命令只是把指定文件的当前状态添加到暂存区域,并不表明一个文件一旦添加,就会一直存在每一个版本中。若是你添加一个文件后对这个又进行了修改,在你commit时候,只会commit这个文件添加时的状态,不会把以后的修改也commit进去,除非你再次添加。 4.推送变动 在你commit完以后,你可能想把本身的代码提交到github或者其余git服务器上,与他人交流共享,这时候就须要和远程服务器打交道了。 若是你是在本地创建起的仓库,默认状况下是没有任何服务器地址的,若是你是从其余服务器复制过来的仓库,这个服务器地址会自动添加到你的仓库中,你能够这样查看: git remote -v 若是只输入”git remote”,就只会列出服务器端的别名,不会列出地址来。 一个仓库能够有多个服务器地址,这就意味着,你能够从不一样的人手中复制同一个仓库,但这并不会打乱你本身的分支,哪怕双方的分支名字都同样。假如你如今在和另外两我的作同一个项目中的同一个分支,你发现A的一个模块正是你想要的,你想把他的代码合并到你如今的版本中,这时候你能够这样作: git remote add code_a git://url/test.git //添加对方的地址,code_a是别名 git fetch code_a //复制对方的仓库到本地,但不合并,git pull会自动合并 git merge code_a/master //把对方master分支合并到本身当前版本下 合并完以后,你可能想提交你的代码到其余的服务器上,这时候你能够先把要提交的服务器地址添加进来,而后这样作: git push origin master 上面的命令就是把本身master的分支提交到名字为origin的服务器上 5.建立并管理分支 在作项目的时候,你可能会想写一些扩展性的功能,或者作一些小实验,可是你又不想影响你如今的项目。这时候,你能够建立一个分支,而后在这个分支里写东西,当以为很差的时候,你能够把这个分支删除掉,对你以前的主分支没有任何影响。或者你以为这个新特性超出了本身的预想,能够合并到主分支里,这时候你只要把工做转回主分支,而后合并分支,最后删除分支,而后就跟那个分支没建立同样。具体操做以下: git branch test //建立一个test分支 git checkout test //转到test分支 edit something...commit something... git checkout master //转到master分支 git merge test //合并test分支 git checkout -b test2 //建立test2分支,并转到test2分支 git branch -d test //删除test分支 git branch //列出分支列表 git branch -v //列出分支列表和当前commit Git merge的实质是把两个版本合在一块儿,而后在当前分支建立一个新的commit,若是你在两个分支的同一个文件的同一个地方都作了修改,这时候merge就会失败,git就不会自动建立一个新的commit,而是直接停住。你须要手动修改这些冲突的文件,选择这两个分支中的一个版本,或者本身重写这个部分,而后手动添加这些文件到暂存区域,再commit一下就ok了。要查看哪些文件冲突了,能够用”git status”查看。 6.撤消改动 是人就会犯错。当你执行某个命令以后,忽然发现,本身写错了,或者漏了一个文件,这时候怎么办? 若是你提交得太早,忘了添加某些文件,你能够这样作: git commit -m ‘add something ’ git add file1 git commit --amend 最后一个命令会把你当前暂存区域最为上一次的commit。若是你commit之后,立刻amend,这时候git会直接跳到编辑commit备注里面,这样你能够修改你上次commit的备注。 若是你添加了不应添加的文件,你能够这样挽回: git add . //把全部的文件都添加进去 git reset HEAD readme //把readme文件从暂存区域去除 若是你发现你编辑错了一个文件,你想把它恢复到上一个版本的状态,这时候你能够这样: git checkout -- filename1 //只撤消这一个文件 若是你以为这个版本糟糕透了,想彻底回滚到上一个版本,你能够干以下事情: git reset --hard HEAD^ HEAD是指向当前版本,^指当前版本的父版本,这个操做没法撤消。你能够把--hard换成--soft,这只会回退commit信息。还有一个--mixed默认选项,你们能够参考官方文档,查看这3个选项的具体区别。 参考资料: [url]http://www.uml.org.cn/pzgl/201501261.asp[/url] [url]http://www.blogjava.net/youxia/archive/2013/12/29/408182.html[/url] [url][/url] 若是您以为本文的内容对您的学习有所帮助,您能够微信: [img]http://dl2.iteye.com/upload/attachment/0109/0668/fb266dfa-95ca-3d09-b41e-5f04a19ba9a1.png[/img]