您的位置:首页 > 其它

Git的基本概念与使用

ling091 2014-10-14 16:51 62 查看

1 关于Git

Git是一个免费的分布式版本控制工具。它与传统的中心式管理系统最大的不同就是每个用户都拥有一个完整的repository,都可以成为一个服务器。由于代码在本地,所以几乎所有的动作都可以在本地进行,所以速度非常快。只有在你和别人交换信息时才需要网络。这使得你可以在何时何地都提交你的代码、查看代码版本、创建许许多多的分支来灵活管理自己的代码,而且创建分支和合并分支在git上是非常容易和高效的[basic, concept]。(而传统的中心式管理系统则非常排斥离线工作,很多工作都得在服务器上进行)。

Git之所以能够提供方便的本地分支等特性,是与它的文件存储机制有关的。

在git中,每次对一个文件的修改都会创建一个文件对象,该对象是不可修改的。git通过一个Tree对象来指向文件对象或 tree。一个顶级目录的tree就代表了一个代码库的版本,所以每一次提交都会生成一个新的顶层tree。所以一个分支或提交都是一些指向这些对象的引用,引用会经常修改。Git通过存储上的牺牲实现了控制上的高效和灵活,这对于开发者是至关重要的,[theory]。举个例子来说,比如你在开发某个任务期间,来了一个紧急任务要去修改一个bug,在svn上你只好将本地的所有修改diff,并输出成为一个patch文件,然后回滚有关当前任务的所有代码,再开始修改Bug的任务,等到修改好后,在将patch应用回来。前前后后要完成多个繁琐的步骤,这还不计中间代码发生冲突所要进行的工作量。可是如果使用Git,我们只需要开一个分支或者转回到主分支上,就可以随时开始Bug修改的任务,完成之后,只要切换到原来的分支就可以优雅的继续以前的任务,参见临时任务。[basic]

2 Git的使用

2.1 获取代码

如果远程主服务器上已经存在了一个Git版本库,那么只需要在本地克隆一份,执行下面的命令:

git clone git://github.com/someone/some_project.gitsome_project

“git://github.com/someone/some_project.git”是远程服务器的URL地址。这样就将远程的代码库复制到本地的some_project目录下。

2.2 提交代码

默认情况下总会有一个分支叫master,这就是你默认所在的分支。但是我们最好创建一个自己的分支,然后在这个分支上工作。

1. 更新本地代码,同步到最新版

git pull git://github.com/someone/some_project.git

Note:如果有未提交的更改,同步代码时则不会覆盖掉这些修改,所以需要先执行commit或恢复到一个以前的状态,后面会提到。

2. 创建分支, 到所在工程的根目录执行:

git branch my_branch # create a branch named mybranch

3. 切换到该分支:

git checkout my_branch

Note: git checkout –b my_branch 可以代替上面两条命令,即创建一个分支并切换到该分支上

4. 直接修改代码

5. 将修改添加到要要执行的提交中,切换到工程的根目录执行:

git add .

“.”代表当前目录 该命令将当前目录及子目录的修改都添加到要执行的提交中。

6. 查看所做的修改,以防改错文件:

git status

git会列出所有修改的文件。

7. 执行本地提交:

git commit

此时会弹出一个文件可以填写commit的一些信息。

现在我们的代码就提交到了本地的仓库历史中,如果要提交到远程的服务器上,还要执行下面的命令。

8. 上传到主服务器:

git push git://github.com/someone/some_project.gitHEAD

HEAD 代表本地的最后一次提交

Note: git commit –a 命令相当于执行 gitadd + git commit

2.3 修改最后一次提交

如果代码提交后,发现有错误,只要直接修改后执行:

git add .

git commit --amend

如果需要上传,就可以继续执行git push命令。

只要发现提交还有错误,就可以一直这样做,直到提交正确为止。

2.4 回退代码(可以用来撤销提交)

如果你需要把某个时间点之后的所有改动都回滚掉,因为这些的改动是错误的,可以使用下面的命令:

git log

git log列出最近的提交列表,以及他们的SHA1哈希值。

恢复到一个指定的提交状态,并从记录里永久去掉这次提交之后的所有提交:

git reset --hard SHA1_HASH

SHA1_HASH 是要回滚到的某次提交的哈希值,相当于ID。

要回滚到C,这相当于做了以下动作:



如果想要撤销掉最后一次提交,就可以这样做:

git reset--hard HEAD~

HEAD代表最后一次提交。如果撤销的提交是某次历史提交,参见2.5

丢弃未提交的修改:

git reset --hard HEAD

也可以用git reset --hard

2.5 还原某次历史提交

如果发现某次历史提交的代码是错误的,想要丢弃这次提交,但后面的提交内容要保留。可以使用下面的命令:

git revert SHA1_HASH

同样的如果有未提及的修改需要commit或reset.

如果撤销掉第二次提交,这条命令的效果如下:



2.6 撤销 git add

如果已经执行了git add . 但是发现不正确,可以通过下面的命令进行撤销:

git reset

2.7 切换到历史状态

如果只是简单地切换到旧状态,而不抹掉之后的提交,可以使用下面的命令:

git checkout SHA1_HASH

如果在执行上面的命令之前有未提交的修改,需要先使用commit命令执行该提交或reset丢弃掉这些修改。

只想恢复某个文件:

git checkout HEAD hello.c

Note: 切换到旧状态后,已经不在之前所在的branch上,因为那个branch指向的最后一次提交。如果此时提交代码,并没有分支引用到该commit,如果不创建一个分支指向该commit,会导致该commit丢失,尽管git会保留一段时间。如果要保存该commit,可以使用git branch new_branch_name命令使得该commit位于新创建的branch上,另外创建一个tag也是可以的。

2.8 管理分支

列出所有分支:

git branch

前面带*的分支,代表当前所在的分支

创建分支:

git branch banch_name

切换分支:

git checkout branch_name

删除分支:

git branch –D branch_name

2.9 临时任务

回到本文一开始提到的一个问题,在某个任务的开发过程中来了紧急任务,你可以这样做:

假设你当前在branch_a分支上,首先提交当前的改动:

git commit –a

执行新任务,首先恢复代码到一个旧状态,比如这次提交之前的历史版本:

git checkout HEAD~ #如果需要,也可以同步到最新服务器上的代码再执行下面的命令

修改代码并执行:

git commit –a

git branch branch_b

git pull git://github.com/someone/some_project.gitHEAD #上传该提交

回到之前的任务:

git checkout branch_a

2.10 查看文件每一行的修改信息

在跟踪一些代码时,如果能够知道每一行的最后修改时间和作者是很重要的,git提供了这样的指令:

git blame file_name

2.11 提交太大

如果一次改动过大,而且其中只有有用的才需要提交,可以在添加提交内容时使用下面的命令:

git add –p

该命令会列出每个文件前后修改的不同,然后逐个询问你是否作为提交内容。

2.12 比较修改

在提交代码时,需要比较当前代码跟最近的一次提交之间的不同,可以运行:

git diff HEAD

当前代码与某一次提交的不同,则使用

git diff SHA1_HASH

比较两次commit之间的不同, 给出两次commit的哈希值:

git diff SHA1_HASH SHA2_HASH

2.13 后移branch

我们可能在一个分支上工作了太久,如为了开发一个新的feature,而主干上已经做了很多更新,为了使得我们的分支也能够拥有主干上的更新,可以使用rebase命令。

以下图为例,如当前在一个分支commit E上,想要拥有F和G的改动,可以运行:

git rebase master



2.14 查找bug所在commit

在进行了一系列提交后,发现出现了bug,需要定位该bug所在的commit。可以这样做:

$ git bisect start

$ git bisect bad SHA1_OF_BAD_VERSION

$ git bisect good SHA1_OF_GOOD_VERSION

Git从历史记录中检出一个中间的状态。在这个状态上测试功能,如果还是错误的:

$ git bisect bad

如果可以工作了,则把"bad"替换成"good"。Git会再次帮你找到一个以确定的好版本和坏版本之间的状态,通过这种方式缩小范围。经过一系列的迭代,这种二进制查询会帮你找到导致这个错误的那次提交。一旦完成了问题定位的调查,你可以返回到原始状态,键入:

$ git bisect reset

不需要手工测试每一次改动,执行如下命令可以自动的完成上面的搜索:

$ git bisect run COMMAND

Git使用指定命令(通常是一个一次性的脚本)的返回值来决定一次改动是否是正确的:命令退出时的代码0代表改动是正确的,125代表要跳过对这次改动的检查,1到127之间的其他数值代表改动是错误的。返回负数将会中断整个bisect的检查。

2.15 临时分支

很快你会发现你经常会因为一些相同的原因创建短期的分支。或许有些时候你创建分支只是为了保存当前状态,同时你方便在上一个保存状态执行一些操作,诸如修复高优先级的臭虫之类。

可以和电视的换台做类比,临时切到别的频道,来看看那正演什么,但并不是简单地按几个按钮,你必须创建,检出,删除并提交临时分支。幸运的是,Git已经有了和电视机遥控器一样方便的功能:

$ git stash

这个命令保存当前状态到一个临时的地方(一个隐藏的地方)并且恢复之前状态。你的工作目录看起来和你开始编辑之前一样,并且你可以修复臭虫,引入之前变更等。当你想回到隐藏状态的时候,键入:

$ git stash apply # 你可能需要解决一些冲突

你可以有多个隐藏,并用不同的方式来操作他们。参见git help slash

3 Git的基本概念

仅仅知道一些常用命令,而不清楚这些命令具体干了些什么往往是不够的。如果对git的原理有一些了解,就可以驾轻就熟的灵活处理各种状况。

3.1 git 的三个部分

通过git clone命令获取一份代码仓库后,就可以看到程序的所有文件,这就是我们的工作目录。除此之外,还会有一个.git文件夹,这里面是git的一些对象,保存了整个仓库的历史。如果需要查看历史版本,我们需要从git历史中将代码check out到工作目录。提交代码时,就是从我们当前的工作目录将代码提交到git的仓库历史中。

在git 历史和工作目录之间还有一个暂存目录叫stage (也叫做索引)。

我们在提交前,通过git add命令把修改添加到提交的内容时,就是放到stage中。然后git commit将stage中的内容提交到git 历史。从git历史获取代码时,stage和工作目录都会被更新。



所以repository, 仓库, 包含了所有的代码, 分支, 代码历史等信息. Git只需要代码根目录下的这一个.git目录就可以记录完整的版本控制信息[basic]

workspace, 工作区, 从repository check out出来的代码。

3.2 Git概念

3.2.1 Blob

Blob对象就是用来存储文件数据的一些文件,它是不可改变的。当我们修改了某些文件提交后,就会为这些修改的文件创建Blob对象。

3.3 Tree

Tree相当于目录,它指向Blog或其他Tree。Git将文件内容跟文件名分开,文件内容存储在Blob中,文件名则存储在Tree中。一个顶级目录的Tree对象就包含了一次提交的所有代码。

3.4 Commit

每次执行git commit都会生成一个commit对象,它包含的最重要的信息就是本次提交的tree和parent tree的哈希值。Git使用哈希值来唯一标识这些对象。git历史就是一系列commit。而一个branch实际上就指向了该分支的最后一次commit。

执行commit时,Git如何得知我们当前所在的提交呢,有一个HEAD对象,它通常指向一个已命名的branch,这个branch会指向最后的提交,所以这时Head间接的指向了最新的提交。如在下图中,有一个主干master和一个分支maint,他们分别指向该分支的最后一次提交即f0cec和a47c3。HEAD则指向当前的分支master。



上图中当前在master分支上,当执行一个新提交时,Git利用stage中的文件创建一个新的commit对象f0cec,如下图所示,并将Parent commit设为当前的commit,然后将branch指向这个最新的提交。即master指向f0cec,Head仍然指向master。



4 Git命令帮助

如查看branch命令: git help branch 或者使用 man git-branch

5 参考

[user] Git User’s Manual (for version 1.5.3or newer)

http://www.kernel.org/pub/software/scm/git/docs/user-manual.html

[magic] Git Magic

https://docs.google.com/document/pub?id=1NLgr9EGSCQdpG20eWcY2bO7RB8mD7NvzTElU-z_2JIg译自http://www-cs-students.stanford.edu/~blynn/gitmagic

[basic] 活灵活现用Git-基础. http://phoenixtoday.blogbus.com/logs/33458940.html
[theory] Git简介之工作原理杂谈. http://lib.open-open.com/view/open1331165995202.html
[concept] Git的几个重要概念.http://blog.csdn.net/hb188753472/article/details/7201278

[visual] 图解Git. http://marklodato.github.com/visual-git-guide/index-en.html
Gie内部原理 http://sishuok.com/forum/blogPost/list/5991.html
标签: