RobinSteven/GitUserManualChinese

From Git SCM Wiki
Jump to: navigation, search

OBSOLETE CONTENT

This wiki has been archived and the content is no longer updated. Please visit git-scm.com/doc for up-to-date documentation.

<tablestyle="width:95%; text-align:center; font-size:2.2em; font-weight:bold; border:hidden">Git 用户手册中文版

Contents


Preface 前言

Git is a fast distributed revision control system.

Git 是一个快速的分布式版本控制系统

This manual is designed to be readable by someone with basic UNIX command-line skills, but no previous knowledge of git.

这个手册是面向那些具有基本的 Unix 命令行使用技能,但是没有 Git 知识的人设计的。

Chapter 1, Repositories and Branches and Chapter 2, Exploring git history explain how to fetch and study a project using git—read these chapters to learn how to build and test a particular version of a software project, search for regressions, and so on.

第一章 版本库与分支 和 第二章 考查 git 历史 将展示如何用 git 来获取和研究一个项目,通过阅读这些章节,我们学习如何建立和测试一个具体的软件项目的版本,学习“撤退”等等。

People needing to do actual development will also want to read Chapter 3, Developing with git and Chapter 4, Sharing development with others.

人们是需要开展真正的研发工作的,那么就学习 第三章, 用 git 进行开发 和 第四章,与他人共享研发成果。

Further chapters cover more specialized topics.

更多的一些章节会涉及到许多的专题话题。

Comprehensive reference documentation is available through the man pages, or git-help(1) command. For example, for the command "git clone <repo>", you can either use:

参考文档可以通过系统的手册页命令,或者是 git-help(1) 命令来查看。譬如,你想参考 "git clone <repo>", 你可以用下面的两种方式:

$ man git-clone

or: 或者:

$ git help clone

With the latter, you can use the manual viewer of your choice; see git-help(1) for more information.

晚一点你就有机会用到这些手册查看器的;看 git-help(1) 会得到比较多的信息。

See also Appendix A, Git Quick Reference for a brief overview of git commands, without any explanation.

阅读 附录A,那里是一个 git 命令的快速纵览,但是它不带任何的解说。

Finally, see Appendix B, Notes and todo list for this manual for ways that you can help make this manual more complete.

最后,看看 附录B,这份手册的笔记和工作表,通过它你可以帮助这份文档变得更完善。

Chapter 1. Repositories and Branches 第一章,版本库与分支

How to get a git repository 如何获取一个版本库

It will be useful to have a git repository to experiment with as you read this manual.

有一个实验性的 git 版本库对我们阅读这份手册将非常有用。

The best way to get one is by using the git-clone(1) command to download a copy of an existing repository. If you don't already have a project in mind, here are some interesting examples:

获取一个已经存在的版本库,最佳的方法是用 git-clone 命令,如果你还没有什么心目中的项目的话,那么这里有些有趣的例子:

        # git itself (approx. 10MB download):
$ git clone git://git.kernel.org/pub/scm/git/git.git
        # the Linux kernel (approx. 150MB download):
$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git

The initial clone may be time-consuming for a large project, but you will only need to clone once.

对于一个大型项目来说,初始性的克隆是挺费时的,不过克隆只需要做一次。

The clone command creates a new directory named after the project ("git" or "linux-2.6" in the examples above). After you cd into this directory, you will see that it contains a copy of the project files, called the working tree, together with a special top-level directory named ".git", which contains all the information about the history of the project.

克隆命令会创建一个新的目录,并根据项目的名称来命名这个项目(譬如上说例子中的 “git” 和 “linux-2.6”)。当你进入这个目录的时候,你可以看到它已经包含了项目中的所有文件,我们称之为工作树,在顶层目录中还连带了一个叫 ".git" 的特殊的目录,里面包含了项目的发展历史的所有信息。

How to check out a different version of a project 如何提取项目的不同版本

Git is best thought of as a tool for storing the history of a collection of files. It stores the history as a compressed collection of interrelated snapshots of the project's contents. In git each such version is called a commit.

最好将 git 当作是文件发展的历史纪录的收集工具,它压缩并保存了项目的发展的关联性快照。在 git 中,每个这些变更称作交付(commit)。

Those snapshots aren't necessarily all arranged in a single line from oldest to newest; instead, work may simultaneously proceed along parallel lines of development, called branches, which may merge and diverge.

这些快照并不需要按从旧到新单线索地发展;它可以是同步并行地发展的,称之为分支,它们是可以合并和分割的。

A single git repository can track development on multiple branches. It does this by keeping a list of heads which reference the latest commit on each branch; the git-branch(1) command shows you the list of branch heads:

一个 git 版本库可以跟踪多个分支的发展,它通过保存一个分支头列表的方式来做到这一点,每个分支头都是一个引用(reference),它指向该分支最后的一个交付(commit); git-branch(1) 命令可以向你展示每个分支头:

$ git branch
* master

A freshly cloned repository contains a single branch head, by default named "master", with the working directory initialized to the state of the project referred to by that branch head.

一个刚刚克隆的版本库只包含一个分支头,默认叫 “master” (主分支),并且工作目录已经被初始化为这个分支头所指向的项目状态。

Most projects also use tags. Tags, like heads, are references into the project's history, and can be listed using the git-tag(1) command:

大部分的项目还用到标签(tags)。标签(Tags)就好像头(heads),它指向项目的某个历史场面,它们可以通过 git-tag(1) 命令列举出来:

$ git tag -l
v2.6.11
v2.6.11-tree
v2.6.12
v2.6.12-rc2
v2.6.12-rc3
v2.6.12-rc4
v2.6.12-rc5
v2.6.12-rc6
v2.6.13
...

Tags are expected to always point at the same version of a project, while heads are expected to advance as development progresses.

Tags 被当做是项目统一的版本来对待,而 heads 则是项目前进的每一个步伐。

Create a new branch head pointing to one of these versions and check it out using git-checkout(1):

下面创建一个新的分支头,使其指向其中的某个版本,同时将它提取出来,可以用 git-checkout(1) 命令:

$ git checkout -b new v2.6.13

The working directory then reflects the contents that the project had when it was tagged v2.6.13, and git-branch(1) shows two branches, with an asterisk marking the currently checked-out branch:

工作目录将被镜像为项目中标记为 v2.6.13 的版本的内容,用 git-branch(1) 命令展示这个两个分支,前面带星号(*)的就是当前抽取的分支。

$ git branch
  master
* new

If you decide that you'd rather see version 2.6.17, you can modify the current branch to point at v2.6.17 instead, with

如果你打算看看 2.6.17 的版本,你可以迁移你当前的分支,让它指向 2.6.17, 使用一下命令:

$ git reset --hard v2.6.17

Note that if the current branch head was your only reference to a particular point in history, then resetting that branch may leave you with no way to find the history it used to point to; so use this command carefully.

注意,如果当前的分支头是你唯一的指向具体的历史场面的引用的话,那么复位 (resetting) 这个分支将令你无法找回这个分支以前的所有历史纪录,所以这个命令要慎用。

Understanding History: Commits 理解历史: 交付

Every change in the history of a project is represented by a commit. The git-show(1) command shows the most recent commit on the current branch:

项目的每一个历史变更体现为每一个交付 (commit)。git-show(1) 命令展示当前分支的最新交付:

$ git show
commit 17cf781661e6d38f737f15f53ab552f1e95960d7
Author: Linus Torvalds <torvalds@ppc970.osdl.org.(none)>
Date:   Tue Apr 19 14:11:06 2005 -0700

    Remove duplicate getenv(DB_ENVIRONMENT) call

    Noted by Tony Luck.

diff --git a/init-db.c b/init-db.c
index 65898fa..b002dc6 100644
--- a/init-db.c
+++ b/init-db.c
@@ -7,7 +7,7 @@

 int main(int argc, char **argv)
 {
-       char *sha1_dir = getenv(DB_ENVIRONMENT), *path;
+       char *sha1_dir, *path;
        int len, i;

        if (mkdir(".git", 0755) < 0) {

As you can see, a commit shows who made the latest change, what they did, and why.

正如你看到的那样,交付表明了谁做的最后的更改,改了什么,为什么改。

Every commit has a 40-hexdigit id, sometimes called the "object name" or the "SHA1 id", shown on the first line of the "git-show" output. You can usually refer to a commit by a shorter name, such as a tag or a branch name, but this longer name can also be useful. Most importantly, it is a globally unique name for this commit: so if you tell somebody else the object name (for example in email), then you are guaranteed that name will refer to the same commit in their repository that it does in yours (assuming their repository has that commit at all). Since the object name is computed as a hash over the contents of the commit, you are guaranteed that the commit can never change without its name also changing.

每个交付都有一个40个16进制字符的标识号,称为 “对象名” 或者叫 “SHA1 id”,它显示在 git-show 命令的输出的第一行中。你通常可以用较简短的名字来指明一个交付,譬如标签和分支的名称等等,但是这个长长的名字是很有用的。最重要的是,对于某个交付来说它是全局唯一的名字: 譬如你告诉其他人某个对象名(通过 email等方式),那么你需要确保这个名字不管是在你的版本库中,还是在他们的版本库中都是指向同一个交付的(假设他们的版本库也被交付了很多东西)。当对象名根据每个交付的内容通过哈希算法(hash)算出之后,你就可以确保每个交付中的内容的修改,都不可能脱离它的名字。

In fact, in Chapter 7, Git concepts we shall see that everything stored in git history, including file data and directory contents, is stored in an object with a name that is a hash of its contents.

事实上,在 第七章,Git 的概念 中,我们可以看到保存在 git 历史中的所有东西,包括文件数据与目录内容都会被保存为对象,对象名就是他们的内容的哈希特征值。

Understanding history: commits, parents, and reachability 交付,父交付与可及性

Every commit (except the very first commit in a project) also has a parent commit which shows what happened before this commit. Following the chain of parents will eventually take you back to the beginning of the project.

每个交付(除非是项目的第一个交付)总是有他的父交付,这样就说明了到当前的交付为止到底发生过什么。追索父交付链,可以将我们带回项目的起始点。

However, the commits do not form a simple list; git allows lines of development to diverge and then reconverge, and the point where two lines of development reconverge is called a "merge". The commit representing a merge can therefore have more than one parent, with each parent representing the most recent commit on one of the lines of development leading to that point.

无论如何,这些交付的组织形式都不会是简单的;git 容许开发路线可以分道扬镳,也可以殊途同归,两条开发线路的结合点我们叫“合并”(merge)。表示合并的交付就等于有一个以上的父交付了,每个父交付表示每个开发线路发展到这里的最贴近的交付。

The best way to see how this works is using the gitk(1) command; running gitk now on a git repository and looking for merge commits will help understand how the git organizes history.

最好的查看这个机制的方法是用 gitk(1) 命令;现在在版本库中运行 gitk 命令,并查看一下那些合并交付会对你理解 git 是如何组织历史的会有帮助。

In the following, we say that commit X is "reachable" from commit Y if commit X is an ancestor of commit Y. Equivalently, you could say that Y is a descendant of X, or that there is a chain of parents leading from commit Y to commit X.

接着,我们说 交付X 对于 交付Y 来说是“可及的”, 如果 交付X 是 交付Y 的祖先的话。同样地,你可以说 Y 是 X 的一个后裔, 或者说存在一个从 Y 追索到 X 的世系族谱。

Understanding history: History diagrams 历史沿革示图

We will sometimes represent git history using diagrams like the one below. Commits are shown as "o", and the links between them with lines drawn with - / and \. Time goes left to right:

某些时候,我们会用下面的示图来描述 git 的历史。所有的交付用 "o" 表示, 联系他们各个发展线路之间画上 / 和 \。时间的推移是由左至右。

         o--o--o <-- Branch A
        /
 o--o--o <-- master
        \
         o--o--o <-- Branch B

If we need to talk about a particular commit, the character "o" may be replaced with another letter or number.

如果需要具体地谈论某个交付,那么就用其他的字母或者是数字来替代 "o" 。

Understanding history: What is a branch? 什么是分支

When we need to be precise, we will use the word "branch" to mean a line of development, and "branch head" (or just "head") to mean a reference to the most recent commit on a branch. In the example above, the branch head named "A" is a pointer to one particular commit, but we refer to the line of three commits leading up to that point as all being part of "branch A".

为准确起见,我们用 “分支” 这个词来表达开发的线路, 并且用 “分支头”(或者是 “头”)来表达一个分支中最晚的一个交付。在上面的例子中,那个叫 “A” 的分支头是一个指向一个具体的交付的指针。但是我们指出,该线路上发展到这个点的三个交付全都是 “分支A” 的组成部分。

However, when no confusion will result, we often just use the term "branch" both for branches and for branch heads.

可是, 在不至于产生混淆的前提下,我们常常只是用 “分支” 这个术语来表示分支和分支头。

Manipulating branches 操作分支

Creating, deleting, and modifying branches is quick and easy; here's a summary of the commands:

创建,删除,和更改分支都是很快和容易的;这里是这个命令的摘要:

  • git branch
    • list all branches
    • 列举所有的分支
  • git branch <branch>
    • create a new branch named <branch>, referencing the same point in history as the current branch
    • 创建一个新的分支,并引用当前分支作为同一历史沿革
  • git branch <branch> <start-point>
    • create a new branch named <branch>, referencing <start-point>, which may be specified any way you like, including using a branch name or a tag name
    • 创建一个名叫 <branch> 的新分支,引用 <start-point>,它是可以任意指定的,可以是现存的分支的名称或者是标签的名称
  • git branch -d <branch>
    • delete the branch <branch>; if the branch you are deleting points to a commit which is not reachable from the current branch, this command will fail with a warning.
    • 删除一个叫 <branch> 的分支;如果你要删除的分支指向当前分支中一个不可及的交付的话,那么命令将返回失败并作出提示
  • git branch -D <branch>
    • even if the branch points to a commit not reachable from the current branch, you may know that that commit is still reachable from some other branch or tag. In that case it is safe to use this command to force git to delete the branch.
    • 尽管需要删除一个当前分支不可及的交付,但是你知道那个交付仍然可有其他的分支或者是标签可及。在这种情况下,用这个命令强制删除一个分支是安全的。
  • git checkout <branch>
    • make the current branch <branch>, updating the working directory to reflect the version referenced by <branch>
    • 提取分支,也即是引用 <branch> 版本状态更新工作目录的内容
  • git checkout -b <new> <start-point>
    • create a new branch <new> referencing <start-point>, and check it out.
    • 引用 <start-point> 创建一个叫 <new> 的分支,并且将它提取出来。

The special symbol "HEAD" can always be used to refer to the current branch. In fact, git uses a file named "HEAD" in the .git directory to remember which branch is current:

特殊的标号 "HEAD" 总是被用作引用,指向当前分支。事实上,git 是用 .git 目录中的名叫 "HEAD" 的文件来记住那个是当前分支。

$ cat .git/HEAD
ref: refs/heads/master

Examining an old version without creating a new branch 不通过创建新分支来调查旧版本

The git-checkout command normally expects a branch head, but will also accept an arbitrary commit; for example, you can check out the commit referenced by a tag:

git-checkout 命令按常规是抽取分支头的,但是也可以接受任意的交付;例如,你可以引用一个标签来进行提取。

$ git checkout v2.6.17
Note: moving to "v2.6.17" which isn't a local branch
If you want to create a new branch from this checkout, you may do so
(now or later) by using -b with the checkout command again. Example:
  git checkout -b <new_branch_name>
HEAD is now at 427abfa... Linux v2.6.17

The HEAD then refers to the SHA1 of the commit instead of to a branch, and git branch shows that you are no longer on a branch:

此时,HEAD 将指向交付的 SHA1 来代替分支名称, git branch 命令表明你现在的项目状态不从属于任何一个分支:

$ cat .git/HEAD
427abfa28afedffadfca9dd8b067eb6d36bac53f
$ git branch
* (no branch)
  master

In this case we say that the HEAD is "detached".

这种情况,我们说 HEAD 是 “游离的”。

This is an easy way to check out a particular version without having to make up a name for the new branch. You can still create a new branch (or tag) for this version later if you decide to.

这是一个方便的途径,既可以提取某个版本,又避免了创建与命名一个新的分支。如果你愿意,你当然还可以以这个版本为标本来创建一个新分支(或者是标签)。

Examining branches from a remote repository 调查远程版本库上的分支

The "master" branch that was created at the time you cloned is a copy of the HEAD in the repository that you cloned from. That repository may also have had other branches, though, and your local repository keeps branches which track each of those remote branches, which you can view using the "-r" option to git-branch(1):

"master" 分支是你克隆版本库的时候创建的,它是你的克隆的那个远程版本库上的 HEAD 的复件。那么远程版本库上可能还存在其他的分支,故而你的本地版本库中也保留了那些远程分支的踪迹。你可以用 git branch(1) 命令加上 "-r" 选项来查看。

$ git branch -r
  origin/HEAD
  origin/html
  origin/maint
  origin/man
  origin/master
  origin/next
  origin/pu
  origin/todo

You cannot check out these remote-tracking branches, but you can examine them on a branch of your own, just as you would a tag:

你不可能将这些远程分支提取出来,但是你可以用一个你自己的分支来调查他们,你当他们是一个标签好了。

$ git checkout -b my-todo-copy origin/todo

Note that the name "origin" is just the name that git uses by default to refer to the repository that you cloned from.

注意一下,"origin" 是 git 用来指向你的版本库的克隆来源的默认名称。

Naming branches, tags, and other references 命名分支,标签,与其他引用

Branches, remote-tracking branches, and tags are all references to commits. All references are named with a slash-separated path name starting with "refs"; the names we've been using so far are actually shorthand:

分支,远程踪迹分支,与标签所有这些都是交付的引用。这些因为被命名为以 "refs" 起头的带斜杠的路径名;实际上我们一直以来都用他们的速记名。

  • The branch "test" is short for "refs/heads/test".
  • The tag "v2.6.18" is short for "refs/tags/v2.6.18".
  • "origin/master" is short for "refs/remotes/origin/master".
  • 分支 "branch" 作为 "refs/heads/test" 的简称。
  • 标签 "v2.6.18" 作为 "refs/tags/v2.6.18" 的简称
  • "origin/master" 作为 "refs/remotes/origin/master" 的简称

The full name is occasionally useful if, for example, there ever exists a tag and a branch with the same name.

全名偶尔也会很有用,例如存在一个同名的分支和标签的时候。

(Newly created refs are actually stored in the .git/refs directory, under the path given by their name. However, for efficiency reasons they may also be packed together in a single file; see git-pack-refs(1)).

(新创建的引用实际是保存在 .git/refs 目录中,在他们的名字表明的路径下面。不过,由于效率的原因,它们几乎总是被打包到一个单独的文件中了;参考 git-pack-refs(1))

As another useful shortcut, the "HEAD" of a repository can be referred to just using the name of that repository. So, for example, "origin" is usually a shortcut for the HEAD branch in the repository "origin".

还有一个很有用的捷径,版本库中的 "HEAD" 可以被引用为一个版本库的名称。作为例子,"origin" 常常作为 "origin" 版本库中的 HEAD 分支的快捷引用。

For the complete list of paths which git checks for references, and the order it uses to decide which to choose when there are multiple references with the same shorthand name, see the "SPECIFYING REVISIONS" section of git-rev-parse(1).

完整的路径令 git 可以查验引用,并在速记名相同的情况下决定准确的引用。参考 git rev-parse(1) 中 "SPECIFYING REVISIONS" 一段。

Updating a repository with git-fetch 用 git fetch 更新版本库

Eventually the developer cloned from will do additional work in her repository, creating new commits and advancing the branches to point at the new commits.

你向她克隆版本库的那个开发者,总归是要将新的工作加入到版本库中的,如创建了新的交付,将分支推进到新的交付点上。

The command "git fetch", with no arguments, will update all of the remote-tracking branches to the latest version found in her repository. It will not touch any of your own branches—not even the "master" branch that was created for you on clone.

命令 "git fetch",不用带任何参数,本地版本库中所有远程关联分支都会被更新到远程版本库中对应的分支的最新版本。它不会触动你的专属分支的任何东西,甚至是克隆的时候为你创建的 "master" 分支。

  • (!) 译者注: 你可以直接修改 .git/config 文件将 master 分支的远程跟踪属性去掉。

Fetching branches from other repositories 获取其他版本库的分支

You can also track branches from repositories other than the one you cloned from, using git-remote(1):

你同样可以跟踪一个并不是你的克隆源头的版本库上的分支。使用 git remote(1):

$ git remote add linux-nfs git://linux-nfs.org/pub/nfs-2.6.git
$ git fetch linux-nfs
* refs/remotes/linux-nfs/master: storing branch 'master' ...
  commit: bf81b46

New remote-tracking branches will be stored under the shorthand name that you gave "git-remote add", in this case linux-nfs:

新的远程跟踪分支将以速记名的形式保存,这个名称是有命令 "git remote add" 给定的,在这里是 linux-nfs:

$ git branch -r
linux-nfs/master
origin/master

If you run "git fetch <remote>" later, the tracking branches for the named <remote> will be updated.

如果在晚一点的时候再你运行 "git fetch <remote>",那么这个跟踪分支就会被更新。

If you examine the file .git/config, you will see that git has added a new stanza:

如果你检查一下 .git/config 文件,你发现 git 增加了一个新的配置段。

$ cat .git/config
...
[remote "linux-nfs"]
        url = git://linux-nfs.org/pub/nfs-2.6.git
        fetch = +refs/heads/*:refs/remotes/linux-nfs/*
...

This is what causes git to track the remote's branches; you may modify or delete these configuration options by editing .git/config with a text editor. (See the "CONFIGURATION FILE" section of git-config(1) for details.)

这里的配置就是 git 进行远端分支跟踪的机制,你可以用文本编辑器修改或者是删除 .git/config 文件中的配置。(详情请参考 git config(1) 中的 "CONFIGURATION FILE")

Chapter 2. Exploring git history 检索 git 历史

Git is best thought of as a tool for storing the history of a collection of files. It does this by storing compressed snapshots of the contents of a file hierarchy, together with "commits" which show the relationships between these snapshots.

最好将 Git 当做是纪录文件历史变更集合的工具。它压缩并逐层地保存文件的快照,收集这些 "交付",就展示了文件快照之间的关系。

Git provides extremely flexible and fast tools for exploring the history of a project.

Git 在项目的历史搜索上提供了很强的检索弹性和快速工具。

We start with one specialized tool that is useful for finding the commit that introduced a bug into a project.

我们用一个特别点的工具作为开始,它对于如何发现一个将 bug 引入到项目中的交付很有用。

How to use bisect to find a regression 如何用平分来定位撤退

Suppose version 2.6.18 of your project worked, but the version at "master" crashes. Sometimes the best way to find the cause of such a regression is to perform a brute-force search through the project's history to find the particular commit that caused the problem. The git-bisect(1) command can help you do this:

假设项目中的 2.6.18 版本工作良好,但是 "master" 分支不稳定。某些时候,通过对项目历史进行暴力搜索,确定是那个交付造成的问题,这样我们就可以知道我们应该撤退到什么地方。git-bisect(1) 命令可以帮你做到这点:

$ git bisect start
$ git bisect good v2.6.18
$ git bisect bad master 
Bisecting: 3537 revisions left to test after this
[65934a9a028b88e83e2b0f8b36618fe503349f8e] BLOCK: Make USB storage depend on SCSI rather than selecting it [try #6]

If you run "git branch" at this point, you'll see that git has temporarily moved you in "(no branch)". HEAD is now detached from any branch and points directly to a commit (with commit id 65934…) that is reachable from "master" but not from v2.6.18. Compile and test it, and see whether it crashes. Assume it does crash. Then:

如果这个时候你运行 "git branch",你会发现 git 已经将你移到 “无分支的状态”。HEAD 目前是处于游离的状态的,他不从属于任何的分支,并直接指向一个交付(id 为 65934...),这个交付对于 "master" 分支来说是可及的,但对于 v2.6.18 来说就不是。现在可以编译和测试一下,看它是否会崩溃,如果是,那么:

$ git bisect bad
Bisecting: 1769 revisions left to test after this
[7eff82c8b1511017ae605f0c99ac275a7e21b867] i2c-core: Drop useless bitmaskings

checks out an older version. Continue like this, telling git at each stage whether the version it gives you is good or bad, and notice that the number of revisions left to test is cut approximately in half each time.

现在 git 提取出了更旧的版本,继续类似的步骤(编译和测试),将 git 每次给你的版本的测试结果用 good 或者是 bad 告诉 git,并且注意每次提取出来用于测试的版本都大致是截取一半的时段。

  • (!) 译注: git 每次都抽取大致从 good 至 bad 之间这个项目发展区间一半的那个版本进行测试,如果测试的结果是 good,那么就以当前被提出出来的这个版本到 bad 版本为区间再次进行平分提取,如此不断循环,很快就定位到项目的漏洞。

After about 13 tests (in this case), it will output the commit id of the guilty commit. You can then examine the commit with git-show(1), find out who wrote it, and mail them your bug report with the commit id. Finally, run

在这个例子中,经过13个测试之后,它终于定位到了那个出现问题的交付的 id 。你可以通过 git show(1) 命令来检查是谁写的这个东西,并将缺陷报告通过邮件告诉他们,记得写上那个交付的 id 。最后运行:

$ git bisect reset

to return you to the branch you were on before.

这样就返回到你原来所在的分支了。

Note that the version which git-bisect checks out for you at each point is just a suggestion, and you're free to try a different version if you think it would be a good idea. For example, occasionally you may land on a commit that broke something unrelated; run

提醒一下,git bisect 为你提取的版本只是一个建议性的东西,其实你可以尝试任何的版本,如果你认为这样是个好主意的话,偶尔可能会空降到某个造成问题的交付上去;运行:

$ git bisect visualize

which will run gitk and label the commit it chose with a marker that says "bisect". Choose a safe-looking commit nearby, note its commit id, and check it out with:

这个命令会运行 gitk 并将它选取的那个交付标贴上一个叫 "bisect" 的记号,就近这个标记选择一个看起来是安全的交付,纪录下它的 id,并将它提取出来:

$ git reset --hard fb47ddb2db...

then test, run "bisect good" or "bisect bad" as appropriate, and continue.

进行测试,并恰当地运行 "bisect good" 或 "bisect bad",不断尝试。

Instead of "git bisect visualize" and then "git reset —hard fb47ddb2db…", you might just want to tell git that you want to skip the current commit:

要是不想去做 "git bisect visualize" 和 "git reset --hard fb47ddb2db" 。你只是想跳过 git 为你选择的那个交付的话:

$ git bisect skip

In this case, though, git may not eventually be able to tell the first bad one between some first skipped commits and a later bad commit.

如此一来,git 就无法得知在第一个跳过的交付到最后一个坏交付中那些交付是坏交付。

There are also ways to automate the bisecting process if you have a test script that can tell a good from a bad commit. See git-bisect(1) for more information about this and other "git bisect" features.

其实存在自动进行平分操作的方法,不过你需要一个脚本来告诉 git 好和坏的交付,参考 git bisect(1) 有更多的信息,这是 git bisect 的一个特性。

Naming commits 交付的称谓

We have seen several ways of naming commits already:

我们有一系列的方法来称呼交付:

  • 40-hexdigit object name
  • branch name: refers to the commit at the head of the given branch
  • tag name: refers to the commit pointed to by the given tag (we've seen branches and tags are special cases of references).
  • HEAD: refers to the head of the current branch
  • 40个十六进制字符的对象名
  • 分支名: 指向给定的分支头的交付
  • 标签名: 指向给定的标签的交付的引用(我们已经了解过分支和标签都是引用的特例);
  • 头: 指向当前分支的引用

There are many more; see the "" section of the git-rev-parse(1) man page for the complete list of ways to name revisions. Some examples:

还有更多的称谓; 参考 git rev-parse(1) 手册页中的 "SPECIFYING REVISIONS" 的章节,那里有有“关世系名称”的介绍,例如:

$ git show fb47ddb2 # the first few characters of the object name
                    # are usually enough to specify it uniquely
$ git show HEAD^    # the parent of the HEAD commit
$ git show HEAD^^   # the grandparent
$ git show HEAD~4   # the great-great-grandparent

Recall that merge commits may have more than one parent; by default, ^ and ~ follow the first parent listed in the commit, but you can also choose:

回想一下,一个合并的交付可能有一个以上的父交付;默认地,^ 和 ~ 是他们的父辈列表中的一个父交付,当然你也可以显式地指出他们,譬如:

$ git show HEAD^1   # show the first parent of HEAD
$ git show HEAD^2   # show the second parent of HEAD

In addition to HEAD, there are several other special names for commits:

对于 HEAD,交付有某种特殊的称谓。

Merges (to be discussed later), as well as operations such as git-reset, which change the currently checked-out commit, generally set ORIG_HEAD to the value HEAD had before the current operation.

合并(将会在后面讨论到),有如 git reset 操作,均是改变当前提取的交付,通常是将 ORIG_HEAD 赋值为 HEAD 在进行这些操作之前所保存的值。

The git-fetch operation always stores the head of the last fetched branch in FETCH_HEAD. For example, if you run git fetch without specifying a local branch as the target of the operation

git fetch 操作总是将最后抓取的那个分支的头 (head) 保存到 FETCH_HEAD 中,如果你在运行 git fetch 的时候没有给定本地分支作为操作的目标的话。

$ git fetch git://example.com/proj.git theirbranch

the fetched commits will still be available from FETCH_HEAD.

抓取的交付将总是在 FETCH_HEAD 中。

When we discuss merges we'll also see the special name MERGE_HEAD, which refers to the other branch that we're merging in to the current branch.

当我们要讨论合并的时候,还将看到一个特殊称谓 MERGE_HEAD,它指向被我们刚刚合并进当前分支的另外一个分支。

The git-rev-parse(1) command is a low-level command that is occasionally useful for translating some name for a commit to the object name for that commit:

git rev-parse 命名是一个底层命令,我们偶尔要将某些称谓翻译成对象名的时候非常有用。

$ git rev-parse origin
e05db0fd4f31dde7005f075a84f96b360d05984b

Creating tags 创建标签

We can also create a tag to refer to a particular commit; after running

我们可以创建一个标签指向某个具体的交付,运行下面命令之后

$ git tag stable-1 1b2e1d63ff

You can use stable-1 to refer to the commit 1b2e1d63ff.

你可以看到 stable-1 指向交付 1b2e1d62ff。

This creates a "lightweight" tag. If you would also like to include a comment with the tag, and possibly sign it cryptographically, then you should create a tag object instead; see the git-tag(1) man page for details.

这是创建一个 “轻量” 标签。如果你希望创建一个带注释和加密签注的标签的话,那么你应该创建一个标签对象的实例,详情请参考 git tag(1) 的手册页。

Browsing revisions 浏览修订

The git-log(1) command can show lists of commits. On its own, it shows all commits reachable from the parent commit; but you can also make more specific requests:

git log(1) 命令列举交付列表。它将列举所有可及的父交付;并且你还可以做更多指定的查询:

$ git log v2.5..        # commits since (not reachable from) v2.5
$ git log test..master  # commits reachable from master but not test
$ git log master..test  # ...reachable from test but not master
$ git log master...test # ...reachable from either test or master,
                        #    but not both
$ git log --since="2 weeks ago" # commits from the last 2 weeks
$ git log Makefile      # commits which modify Makefile
$ git log fs/           # ... which modify any file under fs/
$ git log -S'foo()'     # commits which add or remove any file data
                        # matching the string 'foo()'

And of course you can combine all of these; the following finds commits since v2.5 which touch the Makefile or any file under fs:

你还可以组合这些查询请求;下面的命令就是查询 v2.6 版本一来有关 Makefile 和 fs/ 目录下的修订的交付。

$ git log v2.5.. Makefile fs/

You can also ask git log to show patches:

你还可以让 git log 出示补丁:

$ git log -p

See the "—pretty" option in the git-log(1) man page for more display options.

参考 git log(1) 手册页中的 "-pretty" 选项,会有更多的显示选项。

Note that git log starts with the most recent commit and works backwards through the parents; however, since git history can contain multiple independent lines of development, the particular order that commits are listed in may be somewhat arbitrary.

需要注意的是, git log 从最新的交付开始向后回溯父交付;然而,git 的历史中包含了许多并行的相互独立的开发线路,具体的列举顺序可能需要我们作出一定的仲裁。

Generating diffs 生成差异

You can generate diffs between any two versions using git-diff(1):

你可以用 git diff(1) 命令生成两个版本之间的差异:

$ git diff master..test

That will produce the diff between the tips of the two branches. If you'd prefer to find the diff from their common ancestor to test, you can use three dots instead of two:

这将产生两个分支之间的差异提示。如果你期望发现他们两个的共同的祖先的差异,那你可以用三个点号代替两个点号。

$ git diff master...test

Sometimes what you want instead is a set of patches; for this you can use git-format-patch(1):

有时候,你希望取得一个实际的补丁集;你可以用 git format-patch(1):

$ git format-patch master..test

will generate a file with a patch for each commit reachable from test but not from master.

这样会产生一个补丁文件,该文件是所有 test 可及的交付, 而不是 master。

Viewing old file versions 查看旧的文件版本

You can always view an old version of a file by just checking out the correct revision first. But sometimes it is more convenient to be able to view an old version of a single file without checking anything out; this command does that:

你当然可以通过提取文件的恰当的旧版本的方式来查看文件。不过有些时候查看单个文件可以有比提取出来更方便的办法,这个命令就是:

$ git show v2.5:fs/locks.c

Before the colon may be anything that names a commit, and after it may be any path to a file tracked by git.

冒号之前可以是任意一个交付的名称,冒号之后跟着已经纳入 git 跟踪的文件的路径。

Examples 实例

Counting the number of commits on a branch 统计一个分支中的交付数目

Suppose you want to know how many commits you've made on "mybranch" since it diverged from "origin":

假如你想知道你自己那个派生自 "origin" 的,叫 "mybranch" 的分支中有多少个交付:

$ git log --pretty=oneline origin..mybranch | wc -l

Alternatively, you may often see this sort of thing done with the lower-level command git-rev-list(1), which just lists the SHA1's of all the given commits:

另外一个途径是你可以通过底层命令 git rev-list(1) 来列举这些东西,不过它列举的只是那些交付的 SHA1 的 id 好。

$ git rev-list origin..mybranch | wc -l

Check whether two branches point at the same history 检查两分支是否在同一历史时期

Suppose you want to check whether two branches point at the same point in history.

若然你想检查两个分支是否在同一历史时期。

$ git diff origin..master

will tell you whether the contents of the project are the same at the two branches; in theory, however, it's possible that the same project contents could have been arrived at by two different historical routes. You could compare the object names:

这样就得知一个项目的内容在两个分支中是否是相同的;理论上,项目可以经由不同的历史进程抵达相同的历史场面。你可以比较一下他们对应的对象名就知道了。

$ git rev-list origin
e05db0fd4f31dde7005f075a84f96b360d05984b
$ git rev-list master
e05db0fd4f31dde7005f075a84f96b360d05984b

Or you could recall that the … operator selects all commits contained reachable from either one reference or the other but not both: so

或者你可以回顾一下,选取他们两者之中任何一个,而不是两者共同的可及的交付的操作: 如

$ git log origin...master

will return no commits when the two branches are equal.

当两个分支是相等的情况下,将不返回任何的交付。

Find first tagged version including a given fix 找到包含给定修正的第一个标签版本

Suppose you know that the commit e05db0fd fixed a certain problem. You'd like to find the earliest tagged release that contains that fix.

假如你知道交付 e05db0fd 已经修正了一个问题,你一定想知道包含这个交付的最早的标签发行版本是什么。

Of course, there may be more than one answer—if the history branched after commit e05db0fd, then there could be multiple "earliest" tagged releases.

当然,可能会存在不止一个的答案,如果历史在交付 e05db0fd 之后发生了分叉的话,那么就会存在多个“最早”的标签版本。

You could just visually inspect the commits since e05db0fd:

你可能需要一个可视化的方式来检查交付 e05db0fd 之后的情况:

$ gitk e05db0fd..

Or you can use git-name-rev(1), which will give the commit a name based on any tag it finds pointing to one of the commit's descendants:

又或者你可以用命令 git name-rev(1), 它可以给出你指定的那个交付的后裔中已经打过标签的那个交付。

$ git name-rev --tags e05db0fd
e05db0fd tags/v1.5.0-rc1^0~23

The git-describe(1) command does the opposite, naming the revision using a tag on which the given commit is based:

git describe(1) 命令则是逆操作,找到以给定的交付为根源的那个标签的名字。

$ git describe e05db0fd
v1.5.0-rc0-260-ge05db0f

but that may sometimes help you guess which tags might come after the given commit.

它对你要推断在给定的交付之后有那个标签会出现很有帮助。

If you just want to verify whether a given tagged version contains a given commit, you could use git-merge-base(1):

要是你只是想核实一下某个标签是否包含某个交付的话,你可以用 git merge-base(1):

$ git merge-base e05db0fd v1.5.0-rc1
e05db0fd4f31dde7005f075a84f96b360d05984b

The merge-base command finds a common ancestor of the given commits, and always returns one or the other in the case where one is a descendant of the other; so the above output shows that e05db0fd actually is an ancestor of v1.5.0-rc1.

merge-base 命令查找给定的交付的共同的祖先,它总是返回一个结果,或者是返回他们两个之中的其中一个,而这一个是另外一个的祖先;从输出的结果来看,e05db0fd 的确是 v1.5.0-rc1 的祖先。

Alternatively, note that

$ git log v1.5.0-rc1..e05db0fd

will produce empty output if and only if v1.5.0-rc1 includes e05db0fd, because it outputs only commits that are not reachable from v1.5.0-rc1.

As yet another alternative, the git-show-branch(1) command lists the commits reachable from its arguments with a display on the left-hand side that indicates which arguments that commit is reachable from. So, you can run something like

$ git show-branch e05db0fd v1.5.0-rc0 v1.5.0-rc1 v1.5.0-rc2 ! [e05db0fd] Fix warnings in sha1_file.c - use C99 printf format if available

! [v1.5.0-rc0] GIT v1.5.0 preview
 ! [v1.5.0-rc1] GIT v1.5.0-rc1
  ! [v1.5.0-rc2] GIT v1.5.0-rc2

...

then search for a line that looks like

+ ++ [e05db0fd] Fix warnings in sha1_file.c - use C99 printf format if available

Which shows that e05db0fd is reachable from itself, from v1.5.0-rc1, and from v1.5.0-rc2, but not from v1.5.0-rc0.


Personal tools