Git做为一个资源管理和跟踪系统,如果想要把自己的文件托管在Git上,那么首先你得让Git知道你需要管理的文件在哪。比如说现在我有一个项目,它在test文件夹里,我想让Git管理这个项目,这个时候你需进入到这个目录,然后运行“git init”命令。这个时候Git就会在该目录下生成一个.git的隐藏目录,Git用来进行版本控制和内容跟踪的所有文件都在该文件夹下。
处于git跟踪下的文件只具有三种状态:
- Modified(working directory):被修改过的文件
- Staged(staging area):通过git add添加到暂存区域的文件
-
Committed(git directory):通过git commit提交到仓库的文件
所以,一般的git工作流程可能是这样:修改过某些文件,然后把这些文件添加都暂缓区,再提交到仓库中形成一个版本或快照,最后提交到git服务器上。而在中间,可能伴随着分支管理,分支切换,撤消与合并。 可能有些人会觉得很奇怪,为什么git会有暂存区域这个概念,直接提交到仓库中不就ok了。其实这是git为了做版本控制用的,试想如果没有暂存区域,每修改一个文件,就会形成一个版本,太过频繁,不易于管理。暂存区域其实就是下一个版本的文件清单,你可以自由控制该往仓库中提交什么文件,这也可以避免在一个版本中包含一些中间文件,比如编译后的文件。
Git是分布式的版本控制系统,它没有中心服务器的概念(虽然实际开发中可以建一个中心服务器),每一台开发机器上都保存完整的历史记录;但是它有本地代码仓库和远程代码仓库的概念(不然怎么多人协作?),而且可以追踪多个远程仓库;
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》。
有时为了开发新特性,随时可以开一个新分支,如下图:
新分支和主分支之间可以随意切换,随着分支的发展,形式如下图:
主分支也可以向前发展,如下:
最终,当新分支代码很稳定以后,可以将其合并到主分支,如下图:
而能够防止多人协作时出现混乱的关键就在于,当从远程仓库clone代码库到本地或fetch代码库到本地时,远程分支的标记并不等于本地分支的标记。从远程clone一个代码库到本地后,其master分支有两个标记,一个标记为origin/master表示远程库中的master分支,一个标记为master,表示本地的master分支。如下图:
可以想象,由于别人的工作,远程仓库中的master分支肯定会向前继续移动,但是在下次联网之前,该origin/master标记不会移动。而本地的master标记继续向前移动。
直到下次联网,使用git fetch命令将远程仓库的内容取回本地,origin/master标记才会改变位置,这时,看起来就像是两个分支,如下图:
最后,将origin/master分支合并到master分支中(使用get merge命令),本地代码库又一次变成了一个单一的master分支,继续向前开发,并可以将它push到远程仓库,供别人使用。
Git冲突的处理完全靠人工完成。(从逻辑上讲,机器也不可能完美处理冲突。)比如一个小型团队一起工作,他们可以设置一个服务器用于保存远程Git仓库,然后每个人工作之前先从该远程仓库fetch代码,接着工作,工作完成后,先在本地提交,最后push到远程仓库。但是当一个人push的时候,已经有人在他之前push了,如果他们工作在同一个分支,就会出现冲突。解决冲突的办法就是先把别人push的内容再次fetch下来,合并分支,然后再push。
通过之前对git原理的了解,可以分析得出使用Git时有以下几种工作流程:
- 一个人单干,不需要考虑冲突,随时可以开分支、合并分支和切换分支,随时可以本地提交。如果为了防止代码丢失,可以开一个服务器,每次工作完成就push到服务器上;
- 小型团队合作,如前所述,开一个服务器保存代码仓库,然后所有的人把该服务器当成远程仓库,工作之前先fetch,工作之后再push。如果有冲突,则先fetch,合并分支解决冲突后再push。如果团队人数太多,每个人都向该服务器push,那冲突该是有多少?有可能一个开发者第一次向服务器push的时候,有人在他之前已经push过了,他只好先fetch,手工合并解决冲突,可等他再次push的时候,发现又有人再他之前已经push了,于是他只好再做一次解决冲突的流程,可是如果在他工作的时候,又有人push了呢?这也是之前讲的该工作流程只适合小型开发团队的原因。
以上流程经过适当修改也可以供大型团队使用,那就是将团队分组,每个组的成员共用一个服务器当远程仓库,组长合并了该组的工作成果后,再push到另一个服务器当总的远程仓库,这样就可以大大减少冲突的数量,减少工作量。 - 开源项目的合作,在这种情况下,每个人都把自己的仓库暴露在互联网上。开源项目的组织者或负责人将所有人的仓库设为远程仓库,并把有意义的工作合并到主分支,然后发布官方的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 切换分支