Git — The stupid content tracker,傻瓜内容跟踪器。它是由 Linus Torvalds 开发的一个分布式版本控制/软件配置管理软件。
所谓版本控制,说白了就好像游戏存档一样。我们在玩游戏的时候总希望将当前进度存档,而且每到一些关键时刻(比如关键的选择),我们都希望另存一个新档,这样当我们发现选择错误了,我们可以读取那个关键的存档,而放弃在这之后的进度。版本控制做的就是这样的事情,当我们在写代码的时候,我们总希望能够备份当前的代码,并且当我们面临一些重要的更改的时候,为了当发现需要推倒重做的时候能够顺利回滚到更改之前,我们往往需要另外保存一份。版本控制系统就是帮我们处理这类工作的,它可以监视我们的代码,当代码发生更改,就提示我们将这个更改提交,作为一个版本。这样当我们后悔对代码所做的修改时,可以通过它回滚到我们之前所提交的任何一个版本。
早期Linux的开发人员是使用BitKeeper来管理版本控制和维护程式码。2005年的时候,开发BitKeeper的公司同Linux内核开源社区结束合作关系,并收回使用BitKeeper的权利。Torvalds开始着手开发Git来替代BitKeeper。Git的优点包括:
实际上内核开发团队决定开始开发和使用Git来作为内核开发的版本控制系统的时候,世界开源社群的反对声音不少,最大的理由是Git太艰涩难懂,从Git的内部工作机制来说,的确是这样。但是随着开发的深入,Git的正常使用都由一些友好的脚本命令来执行,使Git变得非常好用,即使是用来管理我们自己的开发项目,Git都是一个友好,有力的工具。现在,越来越多的著名项目采用Git来管理项目开发,例如:wine、U-boot等。
Git 已经集成在大部分 Linux 系统的源里,Ubuntu用户直接 apt-get
,Arch用户直接 pacman -S
即可。
如果想要从源码安装,或者使用的是 Windows操作系统 ,可以访问 Git 的官方主页 查看更多安装信息。
1 | git config --global user.name "John Doe" |
--global
选项更改的配置文件就是位于你用户主目录下的那个,以后你所有的项目都会默认使用这里配置的用户信息。如果要在某个特定的项目中使用其他名字或者电邮,只要去掉 --global
选项重新配置即可,新的设定保存在当前项目的 .git/config
文件里。
1 | git config --global core.editor emacs |
例如改用 vimdiff:
1 | git config --global merge.tool vimdiff |
Git 可以理解 kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge 和 opendiff 等合并工具的输出信息。
1 | git config --list |
把特定的名字跟在后面即可,像这样:
1 | git config user.name |
三个命令任选一:
1 | git help <verb> |
1 | git init |
1 | git add *.c |
1 | git clone git://github.com/schacon/grit.git |
这会在当前目录下创建一个名为 grit
的目录,其中包含一个 .git 的目录,用于保存下载下来的所有版本记录,然后从中取出最新版本的文件拷贝。
如果希望在克隆的时候,自己定义要新建的项目目录名称,可以在上面的命令末尾指定新的名字:
1 | git clone git://github.com/schacon/grit.git mygrit |
1 | $ git status |
1 | $ git add <file> ... |
可以创建一个名为 .gitignore
的文件,列出要忽略的文件模式。来看一个实际的例子:
1 | cat .gitignore |
文件 .gitignore
的格式规范如下:
git status 的显示比较简单,仅仅是列出了修改过的文件。
如果要查看具体修改了什么地方,可以用 git diff 命令:
1 | $ git diff |
此命令比较的是 工作目录中当前文件和暂存区域快照之间的差异 ,也就是修改之后 还没有暂存 起来的变化内容。
若要看 已经暂存 起来的文件和 上次提交 时的快照之间的差异,可以用 git diff --cached
命令 或 git diff --staged
命令,两条命令等效,但后者只在 Git 1.6.1 及更高版本才有。)
1 | git diff --cached |
或
1 | git diff --staged |
应该养成的习惯:每次准备提交前,先用 git status 看下,是不是都已暂存起来了,然后再运行提交命令 git commit
:
1 | git commit |
几个有用的参数:
-v
: 将修改差异的每一行都包含到注释中来;-m <提交说明>
: 在一行命令中提交更新;-a
: 自动把所有已经跟踪过的文件暂存起来一并提交,从而跳过 git add 步骤。1 | git rm <file> ... |
如果删除之前修改过并且已经放到暂存区域的话,则必须要用强制删除选项 -f (译注:即 force 的首字母),以防误删除文件后丢失修改的内容。
1 | git rm \*~ |
会递归删除当前目录及其子目录中所有 ~ 结尾的文件,注意 * 号前必须得加上反斜杠,否则不会递归匹配。
把文件从 Git 仓库中删除(亦即从暂存区域移除),但仍然希望保留在当前工作目录中。换句话说,仅是从跟踪清单中删除。比如一些大型日志文件或者一堆 .a 编译文件,不小心纳入仓库后,要移除跟踪但不删除文件,以便稍后在 .gitignore 文件中补上,用 –cached 选项即可
1 | git rm --cached <file> ... |
1 | git mv file_from file_to |
其实,运行 git mv
就相当于运行了下面三条命令:
1 | mv README.txt README |
1 | git log |
选项 | 说明 |
---|---|
-p |
按补丁格式显示每个更新之间的差异。 |
--stat |
显示每次更新的文件修改统计信息。 |
--shortstat |
只显示 --stat 中最后的行数修改添加移除统计。 |
--name-only |
仅在提交信息后显示已修改的文件清单。 |
--name-status |
显示新增、修改、删除的文件清单。 |
--abbrev-commit |
仅显示 SHA-1 的前几个字符,而非所有的40个字符。 |
--relative-date |
使用较短的相对时间显示(比如,“2 weeks ago" )。 |
--graph |
显示ASCII图形表示的分支合并历史。 |
--pretty |
使用其他格式显示历史提交信息。 |
使用 --pretty
选项,可以指定使用完全不同于默认格式的方式展示提交历史。比如用 oneline 将每个提交放在一行显示,这在提交数很大时非常有用。可选值包括:
选项 | 说明 |
---|---|
%H | 提交对象(commit)的完整哈希字串 |
%h | 提交对象的简短哈希字串 |
%T | 树对象(tree)的完整哈希字串 |
%t | 树对象的简短哈希字串 |
%P | 父对象(parent)的完整哈希字串 |
%p | 父对象的简短哈希字串 |
%an | 作者(author)的名字 |
%ae | 作者的电子邮件地址 |
%ad | 作者修订日期(可以用 -date=选项定制格式) |
%ar | 作者修订日期,按多久以前的方式显示 |
%cn | 提交者(committer)的名字 |
%ce | 提交者的电子邮件地址 |
%cd | 提交日期 |
%cr | 提交日期,按多久以前的方式显示 |
%s | 提交说明 |
git log
还有许多非常实用的限制输出长度的选项:
选项 | 说明 |
---|---|
-(n) |
仅显示最近的n条提交 |
--since ,--after |
仅显示指定时间之后的提交。 |
--until ,--before |
仅显示指定时间之前的提交。 |
--author |
仅显示指定作者相关的提交。 |
--committer |
仅显示指定提交者相关的提交。 |
示例 查看 Git 仓库中,2008 年 10 月期间,Junio Hamano 提交的但未合并的测试脚本(位于项目的 t/ 目录下的文件):
1 | git log --pretty="%h - %s" --author=gitster --since="2008-10-01" --before="2008-11-01" --no-merges -- t/ |
1 | $ git commit --amend |
1 | $ git checkout <file> ... |
reset是指将当前head的内容重置,不会留任何痕迹。
1 | git reset --hard HEAD~3 |
会将最新的3次提交全部重置,就像没有提交过一样。
1 | git remote |
如果加上 -v
选项(译注:此为 --verbose
的简写,取首字母),还可以显示对应的克隆地址:
1 | $ git remote -v |
1 | $ git remote add <shortname> <url> |
1 | $ git fetch [remote-name] |
此命令会到远程仓库中拉取所有你本地仓库中还没有的数据。运行完成后,你就可以在本地访问该远程仓库中的所有分支,将其中某个分支合并到本地,或者只是取出某个分支,一探究竟。
如果是克隆了一个仓库,此命令会自动将远程仓库归于 origin 名下。所以,git fetch origin 会抓取从你上次克隆以来别人上传到此远程仓库中的所有更新(或是上次 fetch 以来别人提交的更新)。有一点很重要,需要记住,fetch 命令只是将远端的数据拉到本地仓库,并不自动合并到当前工作分支,只有当你确实准备好了,才能手工合并。
如果设置了某个分支用于跟踪某个远端仓库的分支(参见下节及第三章的内容),可以使用 git pull 命令自动抓取数据下来,然后将远端分支自动合并到本地仓库中当前分支。在日常工作中我们经常这么用,既快且好。
1 | $ git push [remote-name] [branch-name] |
1 | $ git remote show [remote-name] |
1 | $ git remote rename <remote-name-old> <remote-name-new> |
Linux使用git的时候,如果添加的文件是中文名字,会显示为转义符,虽然不影响上传的结果,但是看着很不方便。这个时候可以使用
1 | $ git config --global core.quotepath false |
这个命令,来禁用对于大于0x80的字符进行转义的功能,这样就可以显示出中文文件名了。
所谓的 glob 模式是指 shell 所使用的简化了的正则表达式。
如果在方括号中使用短划线分隔两个字符,表示所有在这两个字符范围内的都可以匹配(比如 [0-9] 表示匹配所有 0 到 9 的数字)。 ↩︎