关于版本控制系统(即VCS:Version Control Systems)
历程:什么是版本控制系统(VCS)?
墙荐:《Pro Git》中文版(实际上这本书已经够了)
阮老师的:Git远程操作详解
Git之删除本地仓库
廖雪峰老师:Git教程
一些命令:
git init
创建一个空git仓库或重新初始化现有的git仓库
1.需要在本地内建仓库,push到Git时,使用git init初始化,就可以变为.git的仓库了。初始化后,在当前目录下会出现一个名为 .git 的目录,所有 Git 需要的数据和资源都存放在这个目录中。不过目前,仅仅是按照既有的结构框架初始化好了里边所有的文件和目录,但我们还没有开始跟踪管理项目中的任何一个文件。
git clone
将仓库存储到新目录中
如果想对某个开源项目出一份力,可以先把该项目的 Git 仓库复制一份出来,这就需要用到 git clone 命令。如果你熟悉其他的 VCS 比如 Subversion,你可能已经注意到这里使用的是 clone 而不是 checkout。这是个非常重要的差别,Git 收取的是项目历史的所有数据(每一个文件的每一个版本),服务器上有的数据克隆之后本地也都有了。实际上,即便服务器的磁盘发生故障,用任何一个克隆出来的客户端都可以重建服务器上的仓库,回到当初克隆时的状态
git status
检查当前文件状态
git add .
跟踪新文件
git diff
git commit
提交更新
还有很多命令,这里就不不一一例举了。
开始使用Git
对于任何一个文件,在 Git 内都只有三种状态:已提交(committed),已修改(modified)和已暂存(staged)。已提交表示该文件已经被安全地保存在本地数据库中了;已修改表示修改了某个文件,但还没有提交保存;已暂存表示把已修改的文件放在下次提交时要保存的清单中。
由此我们看到 Git 管理项目时,文件流转的三个工作区域:Git 的工作目录,暂存区域,以及本地仓库。
每个项目都有一个 Git 目录(译注:如果 git clone 出来的话,就是其中 .git 的目录;如果 git clone --bare 的话,新建的目录本身就是 Git 目录。),它是 Git 用来保存元数据和对象数据库的地方。该目录非常重要,每次克隆镜像仓库的时候,实际拷贝的就是这个目录里面的数据。
从项目中取出某个版本的所有文件和目录,用以开始后续工作的叫做工作目录。这些文件实际上都是从 Git 目录中的压缩对象数据库中提取出来的,接下来就可以在工作目录中对这些文件进行编辑。
所谓的暂存区域只不过是个简单的文件,一般都放在 Git 目录中。有时候人们会把这个文件叫做索引文件,不过标准说法还是叫暂存区域。
基本的 Git 工作流程如下:
在工作目录中修改某些文件。
对修改后的文件进行快照,然后保存到暂存区域。
提交更新,将保存在暂存区域的文件快照永久转储到 Git 目录中。
所以,我们可以从文件所处的位置来判断状态:如果是 Git 目录中保存着的特定版本文件,就属于已提交状态;如果作了修改并已放入暂存区域,就属于已暂存状态;如果自上次取出后,作了修改但还没有放到暂存区域,就是已修改状态。到第二章的时候,我们会进一步了解其中细节,并学会如何根据文件状态实施后续操作,以及怎样跳过暂存直接提交。
配置用户信息
第一个要配置的是你个人的用户名称和电子邮件地址。这两条配置很重要,每次 Git 提交时都会引用这两条信息,说明是谁提交了更新,所以会随更新内容一起被永久纳入历史记录:
$ git config --global user name "user name"
$ git config --global user email user@example.com
检查已有的配置信息,可以使用 git config --list
命令:
当不清楚命令时,可order help
接下来我们在Github上新建一个Repository
一般有两种方法可以生成,一种是直接在远端生成,然后clone到本地(working directory)。另一种是在本地初始化好,最后上传到远端(git directory)
远端生成:
当你勾上Initialize this repository with a README选项时,仓库自动生成
.git
文件,此时才算是一个真正版本控制。
clone到本地
可以使用SSH方式,也可以使用HTTPS,这里使用HTTPS。
克隆仓库的命令格式为
git clone [url]
这会在当前目录下创建一个名为
Test
的目录,其中包含一个 .git
的目录,用于保存下载下来的所有版本记录,然后从中取出最新版本的文件拷贝。如果进入这个新建的 Test
目录,你会看到项目中的所有文件已经在里边了,准备好后续的开发和使用。如果希望在克隆的时候,自己定义要新建的项目目录名称,可以在上面的命令末尾指定新的名字:
这样
clone
下来的就不是叫Test
了,而是Mytest
检查当前文件状态
git status
说明现在的工作目录相当干净。换句话说,所有已跟踪文件在上次提交后都未被更改过。此外,上面的信息还表明,当前目录下没有出现任何处于未跟踪的新文件,否则 Git 会在这里列出来。最后,该命令还显示了当前所在的分支是 master,这是默认的分支名称,实际是可以修改的,现在先不用考虑。
现在我们更改一下README文件
vim README.md
后,再次git status
此时,
Git
发现了我们修改的文件,但是,此时README
并没被跟踪,所以需要手动跟踪文件,Git也提示:(use "git add" and/or "git commit -a")
跟踪新文件:git add
使用命令 git add 开始跟踪一个新文件。所以,要跟踪 README 文件,运行:git add README
,也可以:git add .
加点表示,Git会递归地将你执行命令时所在的目录中的所有文件添加上去,所以如果你将当前的工作目录作为参数,它就会追踪那儿的所有文件。
现在,再次重申一下那幅图,我修改了一点。
Git中的文件只有两种状态中的其中一种,即不是已跟踪就是未跟踪,已跟踪文件就是图中蓝色区域部分,可能是已修改、未更新,或已放入暂存区。
git add所做的就是跟踪文件,并放入暂存区:
此时我们git commit,就会把暂存区内所有文件的更新情况,版本信息上传至.git版本库中了,这次版本更新就完成了。
但是,假设我们在commit之前修改了已跟踪的README文件,会发生什么呢?
查看状态发现, 文件出现了两次!一次算未暂存,一次算已暂存。
实际上 Git 只不过暂存了你运行 git add 命令时的版本,如果现在提交,那么提交的是添加注释前的版本,而非当前工作目录中的版本。所以,运行了 git add 之后又作了修订的文件,需要重新运行 git add 把最新版本重新暂存起来。
查看已暂存和未暂存的更新 git diff
实际上 git status 的显示比较简单,仅仅是列出了修改过的文件,如果要查看具体修改了什么地方,可以用 git diff 命令。稍后我们会详细介绍 git diff,不过现在,它已经能回答我们的两个问题了:当前做的哪些更新还没有暂存?有哪些更新已经暂存起来准备好了下次提交? git diff 会使用文件补丁的格式显示具体添加和删除的行。
要查看尚未暂存的文件更新了哪些部分,不加参数直接输入 git diff:
请注意,单单 git diff 不过是显示还没有暂存起来的改动,而不是这次工作和上次提交之间的差异。所以有时候你一下子暂存(git add)了所有更新过的文件后,运行 git diff 后却什么也没有,就是这个原因。
提交更新git commit
现在的暂存区域已经准备妥当可以提交了。在此之前,请一定要确认还有什么修改过的或新建的文件还没有 git add 过,否则提交的时候不会记录这些还没暂存起来的变化。所以,每次准备提交前,先用 git status 看下,是不是都已暂存起来了,然后再运行提交命令 git commit:
git commit
种方式会启动文本编辑器以便输入本次提交的说明。(默认会启用 shell 的环境变量 $EDITOR 所指定的软件,一般都是 vim 或 emacs。当然也可以按照第一章介绍的方式,使用git config --global core.editor
命令设定你喜欢的编辑软件。)
编辑器会显示类似下面的文本信息(本例选用 Vim 的屏显方式展示):
或者也可以使用git commit -m "XXX"
,-m 参数后跟提交说明的方式,在一行命令中提交更新:
好,现在你已经创建了第一个提交!可以看到,提交后它会告诉你,当前是在哪个分支(master)提交的,本次提交的完整 SHA-1 校验码是什么(fbee2c0),以及在本次提交中,有多少文件修订过,多少行添改和删改过。
跳过使用暂存区域
尽管使用暂存区域的方式可以精心准备要提交的细节,但有时候这么做略显繁琐。Git 提供了一个跳过使用暂存区域的方式,只要在提交的时候,给 git commit 加上 -a 选项,Git 就会自动把所有已经跟踪过的文件暂存起来一并提交,从而跳过 git add 步骤。
查看提交历史:
在提交了若干更新之后,又或者克隆了某个项目,想回顾下提交历史,可以使用 git log 命令查看。
默认不用任何参数的话,git log 会按提交时间列出所有的更新,最近的更新排在最上面。看到了吗,每次更新都有一个 SHA-1 校验和、作者的名字和电子邮件地址、提交时间,最后缩进一个段落显示提交说明。
关于远程仓库
查看当前的远程库
git remote
要查看当前配置有哪些远程仓库,可以用 git remote 命令,它会列出每个远程库的简短名字。在克隆完某个项目后,至少可以看到一个名为 origin 的远程库,Git 默认使用这个名字来标识你所克隆的原始仓库:
推送数据到远程仓库
项目进行到一个阶段,要同别人分享目前的成果,可以将本地仓库中的数据推送到远程仓库。实现这个任务的命令很简单: git push [remote-name] [branch-name]。如果要把本地的 master 分支推送到 origin 服务器上(再次说明下,克隆操作会自动使用默认的 master 和 origin 名字),可以运行下面的命令:git push origin master
如果,在你推送到远程之前有人在你的远程仓库上进行了修改,会出现什么呢?
现在我们将本地更新的数据跟踪暂存,push到Remote Repository
可以看到,有两个操作未被跟踪,未被暂存,也没有进入本地版本库中。现在我们将它们add并commit。
Updates were rejected because the remote contains work that you do
:大概意思(原谅我英语不好)就是远程仓库中包含了一些本地没有的文件(更新)。
此时,我们需要抓取远程仓库中的数据,和本地同步之后,再进行修改上传。
从远程仓库抓取数据
git pull origin master
使用 git pull 命令自动抓取数据下来,然后将远端分支自动合并到本地仓库中当前分支
pull回来之后,用vim编辑器打开,就可以看到,远程仓库的文件和本地文件更新的区别。
稍作改动后,我们保存,就可以commit,push到远程了。
本地创建仓库并初始化
有两种取得 Git 项目仓库的方法。第一种是在现存的目录下,通过导入所有文件来创建新的 Git 仓库。第二种是从已有的 Git 仓库克隆出一个新的镜像仓库来。
在工作目录中初始化新仓库
git init
关于简单的分支概念
当多人协同工作,来开发、维护一个项目时,包含ABC三个模块(功能),程序员No.1—A,程序员No.2—B,程序员No.3—C。一号程序员就可以从初始项目上拉一个新分支开发A模块,同样的2、3号程序员也可以拉取属于自己的开发分支,此时,谁先完成了自己的开发任务,就可以直接合并到旧的项目中,不断迭代......
或者当一个线上项目出现问题,开发者可以返回服务器所在的分支,并开辟一个新的修复分支,当修复好以后,再把修复分支合并进来,再推送到服务器上.......
不是很准确的描述,但这正是Git最强大的地方。
查看所有分支git branch -a
蓝色:本地分支
红色:远程分支
* :当前所处分支
新建分支git branch
切换分支:
查看Dev分支内部文件,可以发现是和master分支是一样的。
现在我们模拟一下,有人在新分支内进行更新:
加入了新文件,并跟踪暂存,提交。
在切回原分支master上(想象成一个树干和树枝会更形象):
可以看到,master分支上并没有index.html文件,这可是同一个文件夹下哦。
这就是分支简单概念。
现在需要推送这个新分支了。
有时候可能需要你更改上传仓库URL。
可以想象一下,master是线上版本,拉一个Dev分支后,进行开发,测试。无误后,我们需要将Dev分支合并到原master上,如何合并呢?
首先切换到需要合并的分支上,这里是master分支,然后使用git merge
命令
此时,master分支内,也有index.html文件了。
再git push一次,此时就可以发布上线了,依旧是master分支,这个版本。
PS:之前对origin、master这两个概念搞不清,现在倒是清晰了一些,origin就是这个repository默认名字,默认远端库的名字。可以理解为一个指针,当然也可以更改,而master就是这个代码库的一个分支。
来自知友更正确的回答
冲突
有时候合并操作并不会如此顺利。如果在不同的分支中都修改了同一个文件的同一部分(例举问题,实际可能没那么简单),Git 就无法干净地把两者合到一起(译注:逻辑上说,这种问题只能由人来裁决。)
现在有人修改了master分支上的README。
这时,我们在本地更改了Dev分支,重新add,commit,并push。
本地下,更改Dev分支README文件,并add,commit。切换到master分支后,将Dev分支合并到master里,使得两个分支内容一样,最后add,commit,push。
出现了问题,远程包含了一些本地没有的内容,Git需要你手动解决这些矛盾。
那就git pull
呗
Git 作了合并,但没有提交,它会停下来等你解决冲突。要看看哪些文件在合并时发生冲突,可以用 git status 查阅:
任何包含未解决冲突的文件都会以未合并(unmerged)的状态列出。Git 会在有冲突的文件里加入标准的冲突解决标记,可以通过它们来手工定位并解决这些冲突。可以看到此文件包含类似下面这样的部分:
可以看到 ======= 隔开的上半部分,解决冲突的办法无非是二者选其一或者由你亲自整合到一起。
最后整理好后,记得
git add .
push后,远端仓库也做了更新。
PS:注意到,之前的冲突时的push,蓝色字体时MERGING,表明这实际上做了merge处理?(求大神告知)。
可以自己做更多的尝试,本地修改,远端修改,不同分支处理,就当是自己制造麻烦,然后寻找方法,这是最好的尝试了。
在《Pro Git》文档中也有这样的说明:
遇到冲突时的分支合并
引发冲突的场景还有很多,遇见更多的问题,并解决,才能更好的理解这一概念,使用好这一工具。
这篇文章只是很简单的入门了一下Git,当做自己的学习点滴纪录,Keep moving吧~!