Git基本概念

来源:互联网 发布:springmvc国际化对js 编辑:程序博客网 时间:2024/06/07 21:14

$ sudo apt-get install git-core 学习git之前,先安装好git分布式版本控制软件

并且准备好“gitk”,这一可视化的工具

GIT对象模型

SHA

所有用来表示项目历史信息的文件,是通过一个40个字符的(40-digit)“对象名”来索引的,对象名看起来像这样:

6ff87c4664981e4397625791c8ea3bbb5f2279a3

你会在Git里到处看到这种“40个字符”字符串。每一个“对象名”都是对“对象”内容做SHA1哈希计算得来的,(SHA1是一种密码学的哈希算法)。这样就意味着两个不同内容的对象不可能有相同的“对象名”。

这样做会有几个好处:

  • Git只要比较对象名,就可以很快的判断两个对象是否相同。
  • 因为在每个仓库(repository)的“对象名”的计算方法都完全一样,如果同样的内容存在两个不同的仓库中,就会存在相同的“对象名”下。
  • Git还可以通过检查对象内容的SHA1的哈希值和“对象名”是否相同,来判断对象内容是否正确。

对象

每个对象(object) 包括三个部分:类型大小内容。大小就是指内容的大小,内容取决于对象的类型,有四种类型的对象:"blob"、"tree"、 "commit" 和"tag"。

  • “blob”用来存储文件数据,通常是一个文件。
  • “tree”有点像一个目录,它管理一些“tree”或是 “blob”(就像文件和子目录)
  • 一个“commit”只指向一个"tree",它用来标记项目某一个特定时间点的状态。它包括一些关于时间点的元数据,如时间戳、最近一次提交的作者、指向上次提交(commits)的指针等等。
  • 一个“tag”是来标记某一个提交(commit) 的方法。

几乎所有的Git功能都是使用这四个简单的对象类型来完成的。它就像是在你本机的文件系统之上构建一个小的文件系统。

安装与初始化

Git 配置

使用Git的第一件事就是设置你的名字和email,这些就是你在提交commit时的签名。

$ git config --global user.name "Scott Chacon"$ git config --global user.email "schacon@gmail.com"

执行了上面的命令后,会在你的主目录(home directory)建立一个叫 ~/.gitconfig 的文件.内容一般像下面这样:

[user]        name = Scott Chacon        email = schacon@gmail.com

译者注:这样的设置是全局设置,会影响此用户建立的每个项目.

如果你想使项目里的某个值与前面的全局设置有区别(例如把私人邮箱地址改为工作邮箱);你可以在项目中使用git config 命令不带 --global 选项来设置. 这会在你项目目录下的.git/config 文件增加一节[user]内容(如上所示).

日期标识符

The Ref Log that git keeps will allow you to do some relative stuff locally,such as:

Git的引用日志(Ref Log)可以让你做一些‘相对'查询操作:

master@{yesterday}master@{1 month ago}

上面的第一条命令是:'master分支的昨天状态(head)的缩写‘. 注意: 即使在两个有相同master分支指向的仓库上执行这条命令, 但是如果这个两个仓库在不同机器上, 那么执行结果也很可能会不一样.

译者注:因为两个不同机器上的仓库的历史一般很难相同.

顺序标识符

这种格式用来表达某点前面的第N个提交(ref).

master@{5}

上面的表达式代表着master前面的第5个提交(ref).

多个父对象

这能告诉你某个提交的第N个直接父提交(parent). 这种格式在合并提交(merge commits)时特别有用, 这样就可以使提交对象(commit object)有多于一个直接父对象(direct parent).

译者注:假设master是由a和b两个分支合并的,那么 master^1 是指分支a, master^2 就是指分支b.

master^2

波浪号

波浪号用来标识一个提交对象(commit object)的第N级嫡(祖)父对象(Nth grandparent). 例如:

master~2

就代表master所指向的提交对象的第一个父对象的第一个父对象(译者:你可以理解成是嫡系爷爷:)). 它和下面的这个表达式是等价的:

master^^

你也可以把这些‘标识符'(spec)叠加起来, 下面这个3个表达式都是指向同一个提交(commit):

master^^^^^^master~3^~2master~6

树对象指针

如果大家对第一章Git对象模型还有印象的话, 就记得提交对象(commit object)是指向一个树对象(tree object)的. 假如你要得到一个提交对象(commit object)指向的树对象(tree object)的sha串名, 你就可以在‘树名'的后面加上'{tree}'来得到它:

master^{tree}

二进制标识符

如果你要某个二次制对象(blob)的sha串名,你可以在'树名'(treeish)后添加二次制对象(blob)对应的文件路径来得到它.

master:/path/to/file

区间

最后, 你可以用".."来指两个提交(commit)之间的区间. 下面的命令会给出你在"7b593b5" 和"51bea1"之间除了"7b593b5外"的所有提交(commit)(注意:51bea1是最近的提交).

7b593b5..51bea1

这会包括所有 7b593b开始的提交(commit).译者注: 相当于 7b593b..HEAD

7b593b.. 


使用Git Grep进行搜索

用git grep 命令查找Git库里面的某段文字是很方便的. 当然, 你也可以用unix下的'grep'命令进行搜索, 但是'git grep'命令能让你不用签出(checkout)历史文件, 就能查找它们.

例如, 你要看 git.git 这个仓库里每个使用'xmmap'函数的地方, 你可以运行下面的命令:

$ git grep xmmapconfig.c:               contents = xmmap(NULL, contents_sz, PROT_READ,diff.c:         s->data = xmmap(NULL, s->size, PROT_READ, MAP_PRIVATE, fd, 0);git-compat-util.h:extern void *xmmap(void *start, size_t length, int prot, int flaread-cache.c:   mmap = xmmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_PRIVATE,refs.c: log_mapped = xmmap(NULL, mapsz, PROT_READ, MAP_PRIVATE, logfd, 0);sha1_file.c:    map = xmmap(NULL, mapsz, PROT_READ, MAP_PRIVATE, fd, 0);sha1_file.c:    idx_map = xmmap(NULL, idx_size, PROT_READ, MAP_PRIVATE, fd, 0);sha1_file.c:                    win->base = xmmap(NULL, win->len,sha1_file.c:                    map = xmmap(NULL, *size, PROT_READ, MAP_PRIVATE, fsha1_file.c:            buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);wrapper.c:void *xmmap(void *start, size_t length,

如果你要显示行号, 你可以添加'-n'选项:

$>git grep -n xmmapconfig.c:1016:          contents = xmmap(NULL, contents_sz, PROT_READ,diff.c:1833:            s->data = xmmap(NULL, s->size, PROT_READ, MAP_PRIVATE, fd,git-compat-util.h:291:extern void *xmmap(void *start, size_t length, int prot, intread-cache.c:1178:      mmap = xmmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_refs.c:1345:    log_mapped = xmmap(NULL, mapsz, PROT_READ, MAP_PRIVATE, logfd, 0);sha1_file.c:377:        map = xmmap(NULL, mapsz, PROT_READ, MAP_PRIVATE, fd, 0);sha1_file.c:479:        idx_map = xmmap(NULL, idx_size, PROT_READ, MAP_PRIVATE, fdsha1_file.c:780:                        win->base = xmmap(NULL, win->len,sha1_file.c:1076:                       map = xmmap(NULL, *size, PROT_READ, MAP_PRsha1_file.c:2393:               buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fdwrapper.c:89:void *xmmap(void *start, size_t length,

如果我们想只显示文件名, 我们可以使用'--name-onley'选项:

$>git grep --name-only xmmapconfig.cdiff.cgit-compat-util.hread-cache.crefs.csha1_file.cwrapper.c

我们可以用'-c'选项,可以查看每个文件里有多少行匹配内容(line matches):

$>git grep -c xmmapconfig.c:1diff.c:1git-compat-util.h:1read-cache.c:1refs.c:1sha1_file.c:5wrapper.c:1

现在, 如果我们要查找git仓库里某个特定版本里的内容, 我们可以像下面一样在命令行末尾加上标签名(tag reference):

$ git grep xmmap v1.5.0v1.5.0:config.c:                contents = xmmap(NULL, st.st_size, PROT_READ,v1.5.0:diff.c:          s->data = xmmap(NULL, s->size, PROT_READ, MAP_PRIVATE, fd,v1.5.0:git-compat-util.h:static inline void *xmmap(void *start, size_t length,v1.5.0:read-cache.c:                    cache_mmap = xmmap(NULL, cache_mmap_size, v1.5.0:refs.c:  log_mapped = xmmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, logfdv1.5.0:sha1_file.c:     map = xmmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, v1.5.0:sha1_file.c:     idx_map = xmmap(NULL, idx_size, PROT_READ, MAP_PRIVATE, fdv1.5.0:sha1_file.c:                     win->base = xmmap(NULL, win->len,v1.5.0:sha1_file.c:     map = xmmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, v1.5.0:sha1_file.c:             buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd

我可以看到"1.5.0版"和当前版本间一些区别: 在“1.5.0版"中, xmmap没有在wrapper.c中出现.

我们也可以组合一些搜索条件, 下面的命令就是查找我们在仓库的哪个地方定义了'SORT_DIRENT'.

$ git grep -e '#define' --and -e SORT_DIRENTbuiltin-fsck.c:#define SORT_DIRENT 0builtin-fsck.c:#define SORT_DIRENT 1

我不但可以进行“与"(both)条件搜索操作,也可以进行"或"(either)条件搜索操作.

$ git grep --all-match -e '#define' -e SORT_DIRENTbuiltin-fsck.c:#define REACHABLE 0x0001builtin-fsck.c:#define SEEN      0x0002builtin-fsck.c:#define ERROR_OBJECT 01builtin-fsck.c:#define ERROR_REACHABLE 02builtin-fsck.c:#define SORT_DIRENT 0builtin-fsck.c:#define DIRENT_SORT_HINT(de) 0builtin-fsck.c:#define SORT_DIRENT 1builtin-fsck.c:#define DIRENT_SORT_HINT(de) ((de)->d_ino)builtin-fsck.c:#define MAX_SHA1_ENTRIES (1024)builtin-fsck.c: if (SORT_DIRENT)

我们也可以查找出符合一个条件(term)且符合两个条件(terms)之一的文件行. 例如我们要找出名字中含有‘PATH'或是'MAX'的常量定义:

$ git grep -e '#define' --and \( -e PATH -e MAX \) abspath.c:#define MAXDEPTH 5builtin-blame.c:#define MORE_THAN_ONE_PATH      (1u<<13)builtin-blame.c:#define MAXSG 16builtin-describe.c:#define MAX_TAGS     (FLAG_BITS - 1)builtin-fetch-pack.c:#define MAX_IN_VAIN 256builtin-fsck.c:#define MAX_SHA1_ENTRIES (1024)...

译者注: 就是"与"条件搜索和"或"条件搜索可以组合使用.


Git的撤消操作 - 重置, 签出 和 撤消

Git提供了多种修复你开发过程中的错误的方法. 方法的选择取决于你的情况: 包含有错误的文件是否提交了(commited); 如果你把它已经提交了, 那么你是否把有错误的提交已与其它人共享这也很重要.

修复未提交文件中的错误(重置)

如果你现在的工作目录(work tree)里搞的一团乱麻, 但是你现在还没有把它们提交; 你可以通过下面的命令, 让工作目录回到上次提交时的状态(last committed state):

$ git reset --hard HEAD

这条件命令会把你工作目录中所有未提交的内容清空(当然这不包括未置于版控制下的文件 untracked files). 从另一种角度来说, 这会让"git diff" 和"git diff --cached"命令的显示法都变为空.

如果你只是要恢复一个文件,如"hello.rb", 你就要使用 git checkout

$ git checkout -- hello.rb

这条命令把hello.rb从HEAD中签出并且把它恢复成未修改时的样子.

译者:上面二行和原文有出入,经验证是原文有误,所以我据正确的重写了.

修复已提交文件中的错误

如果你已经做了一个提交(commit),但是你马上后悔了, 这里有两种截然不同的方法去处理这个问题:

  1. 创建一个新的提交(commit), 在新的提交里撤消老的提交所作的修改. 这种作法在你已经把代码发布的情况下十分正确.

2 你也可以去修改你的老提交(old commit). 但是如果你已经把代码发布了,那么千万别这么做; git不会处理项目的历史会改变的情况,如果一个分支的历史被改变了那以后就不能正常的合并.

创建新提交来修复错误

创建一个新的,撤消(revert)了前期修改的提交(commit)是很容易的; 只要把出错的提交(commit)的名字(reference)做为参数传给命令:git revert就可以了; 下面这条命令就演示了如何撤消最近的一个提交:

$ git revert HEAD

这样就创建了一个撤消了上次提交(HEAD)的新提交, 你就有机会来修改新提交(new commit)里的提交注释信息.

你也可撤消更早期的修改, 下面这条命令就是撤消“上上次”(next-to-last)的提交:

$ git revert HEAD^

在这种情况下,git尝试去撤消老的提交,然后留下完整的老提交前的版本. 如果你最近的修改和要撤消的修改有重叠(overlap),那么就会被要求手工解决冲突(conflicts), 就像解决合并(merge)时出现的冲突一样.

译者注: git revert 其实不会直接创建一个提交(commit), 把撤消后的文件内容放到索引(index)里,你需要再执行git commit命令,它们才会成为真正的提交(commit).

修改提交来修复错误

如果你刚刚做了某个提交(commit), 但是你又想马上修改这个提交; git commit 现在支持一个叫--amend的参数,它能让你修改刚才的这个提交(HEAD commit). 这项机制能让你在代码发布前,添加一些新的文件或是修改你的提交注释(commit message).

如果你在老提交(older commit)里发现一个错误, 但是现在还没有发布到代码服务器上. 你可以使用 git rebase命令的交互模式, "git rebase -i"会提示你在编辑中做相关的修改. 这样其实就是让你在rebase的过程来修改提交.

维护Git

保证良好的性能

在大的仓库中, git靠压缩历史信息来节约磁盘和内存空间.

压缩操作并不是自动进行的, 你需要手动执行 git gc:

$ git gc

压缩操作比较耗时, 你运行git gc命令最好是在你没有其它工作的时候.

保持可靠性

git fsck 运行一些仓库的一致性检查, 如果有任何问题就会报告. 这项操作也有点耗时, 通常报的警告就是“悬空对象"(dangling objects).

$ git fsckdangling commit 7281251ddd2a61e38657c827739c57015671a6b3dangling commit 2706a059f258c6b245f298dc4ff2ccd30ec21a63dangling commit 13472b7c4b80851a1bc551779171dcb03655e9b5dangling blob 218761f9d90712d37a9c5e36f406f92202db07ebdangling commit bf093535a34a4d35731aa2bd90fe6b176302f14fdangling commit 8e4bec7f2ddaa268bef999853c25755452100f8edangling tree d50bb86186bf27b681d25af89d3b5b68382e4085dangling tree b24c2473f1fd3d91352a624795be026d64c8841f...

“悬空对象"(dangling objects)并不是问题, 最坏的情况只是它们多占了一些磁盘空间. 有时候它们是找回丢失的工作的最后一丝希望.

其实这里最重要的一个命令就是:git fsck --lost-found,因为git中把commit删了后,并不是真正的删除,而是变成了悬空对象(dangling commit)。我们只要把把这悬空对象(dangling commit)找出来,用git rebase也好,用git merge也行就能把它们给恢复。



0 0
原创粉丝点击