GitUserManualChinese – Robin Wiki

Git 使用者手冊(1.5.3 及後續版本適用)




  1. Preface 前言
  2. Chapter 1. Repositories and Branches 第一章. 版本庫與分支

    1. How to get a git repository 如何獲取一個版本庫
    2. How to check out a different version of a project 如何提取專案的不同版本
    3. Understanding History: Commits 理解歷史: 交付

      1. Understanding history: commits, parents, and reachability 交付,父交付與可及性
      2. Understanding history: History diagrams 歷史沿革示圖
      3. Understanding history: What is a branch? 理解歷史:什麼是分支
    4. Manipulating branches 操作分支
    5. Examining an old version without creating a new branch 不通過建立新分支來調查舊版本
    6. Examining branches from a remote repository 調查遠端版本庫上的分支
    7. Naming branches, tags, and other references 命名分支,標籤,與其他引用
    8. Updating a repository with git-fetch 用 git fetch 更新版本庫
    9. Fetching branches from other repositories 獲取其他版本庫的分支
  3. Chapter 2. Exploring git history 第二章. 檢索 git 歷史

    1. How to use bisect to find a regression 如何用平分來定位撤退
    2. Naming commits 交付的稱謂
    3. Creating tags 建立標籤
    4. Browsing revisions 瀏覽修訂
    5. Generating diffs 生成差異
    6. Viewing old file versions 檢視舊的檔案版本
    7. Examples 例項

      1. Counting the number of commits on a branch 統計一個分支中的交付數目
      2. Check whether two branches point at the same history 檢查兩分支是否在同一歷史時期
      3. Find first tagged version including a given fix 找到包含給定修正的第一個標籤版本
      4. Showing commits unique to a given branch 顯示僅屬於某個分支的交付
      5. Creating a changelog and tarball for a software release 為軟體的發行製作變更日誌和壓縮包
      6. Finding commits referencing a file with given content 尋找一個指向包含給定內容的檔案的交付
  4. Chapter 3. Developing with git 第三章. 用 git 進行研發

    1. Telling git your name 告訴 git 你的名字
    2. Creating a new repository 建立新的版本庫
    3. How to make a commit 如何製作一個交付
    4. Creating good commit messages 寫好交付資訊
    5. Ignoring files 忽略檔案
    6. How to merge 如何合併
    7. Resolving a merge 解決合併衝突

      1. Getting conflict-resolution help during a merge 在合併過程中取得衝突解決幫助
    8. Undoing a merge 撤銷合併
    9. Fast-forward merges 快進合併
    10. Fixing mistakes 修復失誤

      1. Fixing a mistake with a new commit 修復一個新的交付中的失誤
    11. Fixing a mistake by rewriting history 通過重寫歷史來修復失誤
    12. Checking out an old version of a file 提取一個檔案的舊版本
    13. Temporarily setting aside work in progress 臨時放下手頭上的工作
    14. Ensuring good performance 確保好的效能
    15. Ensuring reliability 確保伸縮性

      1. Checking the repository for corruption 檢查版本的損壞
      2. Recovering lost changes 恢復丟失的變更
  5. Chapter 4. Sharing development with others 第四章. 與他人協同研發

    1. Getting updates with git-pull 用 git-pull 取得更新
    2. Submitting patches to a project 向專案提交補丁
    3. Importing patches to a project 給專案匯入補丁
    4. Public git repositories 釋出 git 版本庫
    5. Setting up a public repository 建立一個公共版本庫
    6. Exporting a git repository via the git protocol 通過 git 協議公開版本庫
    7. Exporting a git repository via http 通過 http 協議公開版本庫
    8. Pushing changes to a public repository 將變更推入到公共版本庫
    9. What to do when a push fails 推入失敗之後該怎麼處理
    10. Setting up a shared repository 建立共享版本庫
    11. Allowing web browsing of a repository 容許 Web 瀏覽版本庫
    12. Examples 例子

      1. Maintaining topic branches for a Linux subsystem maintainer | Linux 子系統維護者如何維護主題分支
  6. Chapter 5. Rewriting history and maintaining patch series 第五章. 改寫歷史與維護補丁串

    1. Creating the perfect patch series 建立出色的補丁串
    2. Keeping a patch series up to date using git-rebase 使用 git-rebase 保持補丁串的新穎
    3. Rewriting a single commit 重寫單個交付
    4. Reordering or selecting from a patch series 在補丁串中選取與重新排序
    5. Other tools 第三方工具
    6. Problems with rewriting history 重寫歷史帶來的問題
    7. Why bisecting merge commits can be harder than bisecting linear history 為何定位合併交付中的問題要比線上性歷史中困難
  7. Chapter 6. Advanced branch management 第六章. 高階分支管理

    1. Fetching individual branches
    2. git fetch and fast-forwards 抓取與快進
    3. Configuring remote branches 配置遠端分支
  8. Chapter 7. Git concepts 第七章. Git 概念

    1. The Object Database 物件資料庫

      1. Commit Object 交付物件
      2. Tree Object 樹物件
      3. Blob Object 片物件
      4. Trust 信賴
      5. Tag Object 標籤物件
      6. How git stores objects efficiently: pack files | git 如何高效地儲存物件: 打包檔案
      7. Dangling objects 懸空物件
      8. Recovering from repository corruption 從損壞中恢復
    2. The index 索引
  9. Chapter 8. Submodules 子模組

      1. Pitfalls with submodules 子模組陷阱
  10. Chapter 9. Low-level git operations 第九章. 底層 git 操作

    1. Object access and manipulation 物件訪問與操作
    2. The Workflow 運作流程

      1. working directory -> index 工作樹 -> 索引
      2. index -> object database 索引 -> 物件資料庫
      3. object database -> index 物件資料庫 -> 索引
      4. index -> working directory 索引 -> 工作目錄
      5. Tying it all together 全盤瞭解
    3. Examining the data 檢驗資料
    4. Merging multiple trees 合併多個樹
    5. Merging multiple trees, continued 合併多個樹,續完
  11. Chapter 10. Hacking git 第十章. git 的開發

    1. Object storage format 物件的儲存格式
    2. A birds-eye view of Git’s source code 鳥瞰 git 原始碼
  12. Chapter 11. GIT Glossary 第十一章. GIT 字典
  13. Appendix A. Git Quick Reference 附錄 A. Git 快速參考

    1. Creating a new repository 建立一個新的版本庫
    2. Managing branches 管理分支
    3. Exploring history 勘查歷史
    4. Making changes 製作變更
    5. Merging 合併
    6. Sharing your changes 共享你的變更
    7. Repository maintenance 版本庫的維護
    8. Appendix B. Notes and todo list for this manual 附錄 B. 備忘與本手冊的工作計劃



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 知識的人設計的。

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 來獲取和研究一個專案,通過閱讀這些章節,我們學習如何建立和測試一個具體的軟體專案的版本,學習“撤退”等等。

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.


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 版本庫對我們閱讀這份手冊將非常有用。

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://
# the Linux kernel (approx. 150MB download):
$ git clone git://

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


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” 和
“.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)。

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.


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

一個 git 版本庫可以跟蹤多個分支的發展,它通過儲存一個分支頭列表的方式來做到這一點,每個分支頭都是一個引用(reference),它指向該分支最後的一個交付(commit); git-branch(1) 命令可以向你展示每個分支頭:


$ git branch
* master

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” (主分支),並且工作目錄已經被初始化為這個分支頭所指向的專案狀態。

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

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

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
* 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

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 理解歷史: 交付

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 <[email protected](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
@@ -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.


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

個交付都有一個40個16進位制字元的標識號,稱為 “物件名” 或者叫 “SHA1 id”,它顯示在 git-show
說它是全域性唯一的名字: 譬如你告訴其他人某個物件名(通過

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 交付,父交付與可及性

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


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)。表示合併的交付就等於有一個以上的父交付了,每個父交付表示每個開發線路發展到這裡的最貼近的交付。

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 是如何組織歷史的很有幫助。

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> 的分支,並且將它提取出來。

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 不通過建立新分支來調查舊版本

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
$ git branch
* (no branch)

In this case we say that the HEAD is “detached”.

這種情況,我們說 HEAD 是 “遊離的”。

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 調查遠端版本庫上的分支

“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):

分支是你克隆版本庫的時候建立的,它是你的克隆的那個遠端版本庫上的 HEAD
的復件。那麼遠端版本庫上可能還存在其他的分支,故而你的本地版本庫中也保留了那些遠端分支的蹤跡。你可以用 git branch(1) 命令加上
“-r” 選項來檢視。


$ git branch -r

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 命名分支,標籤,與其他引用

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.


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))

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 分支的快捷引用。

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 更新版本庫

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


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://
$ 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

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://
fetch =  refs/heads/*:refs/remotes/linux-nfs/*

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

這裡的配置就是 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 如何用平分來定位撤退

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]

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

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

現在 git 提取出了更舊的版本,繼續類似的步驟(編譯和測試),將 git 每次給你的版本的測試結果用 good
或者是 bad
告訴 git,並且注意每次提取出來用於測試的版本都大致是擷取一半的變更跨度。

  • 譯註:
    git 每次都抽取大致從 good 至 bad 之間這個專案發展區間一半的那個版本進行測試,如果測試的結果是 good,那麼就以當前被提取出來的這個版本到 bad 版本為區間再次進行平分提取,如此不斷迴圈,很快就定位到專案的漏洞。

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.


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

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”,不斷嘗試。

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

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 就無法得知在第一個跳過的交付到最後一個壞交付中那些交付是壞交付。

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個十六進位制字元的物件名
  • 分支名: 指向給定的分支頭的交付
  • 標籤名: 指向給定的標籤的交付的引用(我們已經瞭解過分支和標籤都是引用的特例);
  • 頭: 指向當前分支的引用

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

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,對於它還有一系列特殊的稱謂。

(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 操作,均是改變當前提取的交付,他們通常都是將命名執行之前的 HEAD 的值儲存到 ORIG_HEAD。

  • 譯註:
    這就等於是一服後悔藥,如果你對 git reset,git merge 和 git pull 之類的命令的結果不滿意,那麼用下面的命令倒退回去就了:

  •  $ git reset --hard ORIG_HEAD

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:// theirbranch

the fetched commits will still be available from FETCH_HEAD.

抓取的交付將總是在 FETCH_HEAD 中。

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,它指向被我們剛剛合併進當前分支的另外一個分支。

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


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。

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

這是建立一個 “輕量” 標籤。如果你希望建立一個帶註釋和加密簽註的標籤的話,那麼你應該建立一個標籤物件的例項,詳情請參考 git tag(1) 的手冊頁。


Browsing revisions 瀏覽修訂

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()'

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” 選項,會有更多的顯示選項。

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

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

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

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
$ git rev-list master

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 找到包含給定修正的第一個標籤版本

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

假如你知道交付 e05db0fd 已經修正了一個問題,你一定想知道包含這個交付的最早的標籤發行版本是什麼。

course, there may be more than one answer—if the history branched after
commit e05db0fd, then there could be multiple “earliest” tagged

當然,可能會存在不止一個的答案,如果歷史在交付 e05db0fd 之後發生了分叉的話,那麼就會存在多個“最早”的標籤版本。

You could just visually inspect the commits since e05db0fd:

你可能需要一個視覺化的方式來檢查交付 e05db0fd 之後的情況:


$ gitk e05db0fd..

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

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

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

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.

當且僅當 e05db0fd 是 v1.5.0-rc1 的源頭的時候,這個命令輸出為空, 這個命令只會輸出 v1.5.0-rc1 不可及的交付。

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(1) 根據引數之間的可及性關係向左列舉各個引數, 你可以試試


$ 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
! [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

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.

這個顯示錶明 e05db0fd 對於 v1.5.0-rc1, v1.5.0-rc2 以及它自身來說是可及的,而對於 v1.5.0-rc0 來說則不是。

  • 譯註:
    注意該行前面的加號 ” ” 與所對應的 v1.5.0-rc0, v1.5.0-rc1, v1.5.0-rc2 的列對齊關係。


Showing commits unique to a given branch 顯示僅屬於某個分支的交付

you would like to see all the commits reachable from the branch head
named “master” but not from any other head in your repository.

假如你想知道在你的版本庫中,那些交付僅僅是可及分支 “master”, 而對其他的分支沒有可及性。

We can list all the heads in this repository with git-show-ref(1):

我們可以用 git-show-ref 命令先列出版本庫中所有的分支頭。


$ git show-ref --heads
bf62196b5e363d73353a9dcf094c59595f3153b7 refs/heads/core-tutorial
db768d5504c1bb46f63ee9d6e1772bd047e05bf9 refs/heads/maint
a07157ac624b2524a059a3414e99f6f44bebc1e7 refs/heads/master
24dbc180ea14dc1aebe09f14c8ecf32010690627 refs/heads/tutorial-2
1e87486ae06626c2f31eaa63d26fc0fd646c8af2 refs/heads/tutorial-fixes

We can get just the branch-head names, and remove “master”, with the help of the standard utilities cut and grep:

我們可以用標準的輔助工具 cut 和 grep, 從分支列表中去掉 “master” 。


$ git show-ref --heads | cut -d' ' -f2 | grep -v '^refs/heads/master'

And then we can ask to see all the commits reachable from master but not from these other heads:

接著我們檢視那些交付對 “master” 是可及的, 而對其他的分支沒有可及性。


$ gitk master --not $( git show-ref --heads | cut -d' ' -f2 |
grep -v '^refs/heads/master' )

endless variations are possible; for example, to see all commits
reachable from some head but not from any tag in the repository:

顯然,上述命令是可以帶無限的引數的; 譬如, 檢視對於某些分支頭可及,但是對於任何的標籤都不可及的所有交付的命令如下:


$ gitk $( git show-ref --heads ) --not  $( git show-ref --tags )

(See git-rev-parse(1) for explanations of commit-selecting syntax such as —not.)

(參看 git-rev-parse(1), 那裡有關於交付選取的語法的解釋, 例如 –not)


Creating a changelog and tarball for a software release 為軟體的發行製作變更日誌和壓縮包

The git-archive(1) command can create a tar or zip archive from any version of a project; for example:

git archive(1) 命令可以為專案的任何版本建立壓縮包; 例如:


$ git archive --format=tar --prefix=project/ HEAD | gzip >latest.tar.gz

will use HEAD to produce a tar archive in which each filename is preceded by “project/”.

這樣將以 HEAD 為版本建立壓縮包,並且每個檔名前面都將加上字首。

you’re releasing a new version of a software project, you may want to
simultaneously make a changelog to include in the release announcement.

如果你要發行一個新的軟體版本, 你可能希望同時在發行宣告中包含軟體的變更日誌。

Linus Torvalds, for example, makes new kernel releases by tagging them, then running:

譬如, Linus Torvalds 製作打過版本標籤的新的核心版本時,就執行:


$ release-script 2.6.12 2.6.13-rc6 2.6.13-rc7

where release-script is a shell script that looks like:

這裡的 release-scritp 是一個 shell 指令碼, 它大概會象下面的樣子:


echo "# git tag v$new"
echo "git archive --prefix=linux-$new/ v$new | gzip -9 > ../linux-$new.tar.gz"
echo "git diff v$stable v$new | gzip -9 > ../patch-$new.gz"
echo "git log --no-merges v$new ^v$last > ../ChangeLog-$new"
echo "git shortlog --no-merges v$new ^v$last > ../ShortLog"
echo "git diff --stat --summary -M v$last v$new > ../diffstat-$new"

and then he just cut-and-pastes the output commands after verifying that they look OK.

並且他僅僅是在命令執行過之後, 驗證一下輸出的東西,再剪下-貼上一下。


Finding commits referencing a file with given content 尋找一個指向包含給定內容的檔案的交付

hands you a copy of a file, and asks which commits modified a file such
that it contained the given content either before or after the commit.
You can find out with this:

某人給了你一個檔案的拷貝, 並請求得到修改過這個檔案的那些個交付,不過,他甚至不知道這個檔案現在所包含的內容是否已經被提交過。 你可以這樣地查詢:


$  git log --raw --abbrev=40 --pretty=oneline |
grep -B 1 `git hash-object filename`

out why this works is left as an exercise to the (advanced) student.
The git-log(1), git-diff-tree(1), and git-hash-object(1) man pages may
prove helpful.

思考一下為什麼這個命令可以做到這樣的結果,並將這個問題留給(高階的)學生作為練習。 git-log(1), git-diff-tree(1), 與 git-hash-object(1) 的手冊頁可能會提供有用的幫助。


Chapter 3. Developing with git 第三章. 用 git 進行研發


Telling git your name 告訴 git 你的名字

creating any commits, you should introduce yourself to git. The easiest
way to do so is to make sure the following lines appear in a file named
.gitconfig in your home directory:

在建立任何交付之前,你應該先將你自己介紹給 git。最容易的方法就是在你的家目錄中建立一個叫 .gitconfig 的檔案,並寫入如下的配置項。


name = Your Name Comes Here
email = [email protected]

(See the “CONFIGURATION FILE” section of git-config(1) for details on the configuration file.)

(在 git-config(1) 中參考 “CONFIGURATION FILE” 那一節得到有關配置檔案的幫助)


Creating a new repository 建立新的版本庫

Creating a new repository from scratch is very easy:



$ mkdir project
$ cd project
$ git init

If you have some initial content (say, a tarball):

如果你已經有一個原始的內容的話 (意思是你已經有一個壓縮包了):


$ tar xzvf project.tar.gz
$ cd project
$ git init
$ git add . # include everything below ./ in the first commit:
$ git commit


How to make a commit 如何製作一個交付

Creating a new commit takes three steps:

  1. Making some changes to the working directory using your favorite editor.
  2. Telling git about your changes.
  3. Creating the commit using the content you told git about in step 2.


  1. 在你的工作目錄中用你喜歡的編輯器做了某些變更。
  2. 告訴 git 你所做的變更。
  3. 將第二步中你告訴 git 的變更內容建立一個交付。

practice, you can interleave and repeat steps 1 and 2 as many times as
you want: in order to keep track of what you want committed at step 3,
git maintains a snapshot of the tree’s contents in a special staging
area called “the index.”

事實上,第一步 和 第二步 在任何時候都可以交替和重複地進行: 為了保持著對你打算在第三步提交的內容進行跟蹤, git 在一個特殊的階段性的區域內保留了你的工作目錄樹的內容快照,這個區域叫 “索引”。

the beginning, the content of the index will be identical to that of
the HEAD. The command “git diff —cached”, which shows the difference
between the HEAD and the index, should therefore produce no output at
that point.

在開始的時候, 索引中的內容跟 HEAD 中的內容是一致的。對於命令 git diff --cached
,它是顯示 HEAD 與 索引之間的差異的,在這個時候,它應該沒有任何的輸出。

Modifying the index is easy:


To update the index with the new contents of a modified file, use



$ git add path/to/file

To add the contents of a new file to the index, use



$ git add path/to/file

To remove a file from the index and from the working tree,



$ git rm path/to/file

After each step you can verify that



$ git diff --cached

shows the difference between the HEAD and the index file—this is what
you’d commit if you created the commit now—and that

無論如何都應該顯示一下 HEAD 和索引檔案之間的差異 — 這其實就是你即將要提交的交付中的東西


$ git diff

shows the difference between the working tree and the index file.


that “git-add” always adds just the current contents of a file to the
index; further changes to the same file will be ignored unless you run
git-add on the file again.

注意, “git-add” 僅僅是將當前檔案的內容加入索引; 在此之後的同一個檔案的變更將會被忽略,除非你再次對該檔案應用 git add 命令。

When you’re ready, just run



$ git commit

git will prompt you for a commit message and then create the new
commit. Check to make sure it looks like what you expected with

git 將在建立新的交付時向你提示交付資訊。要檢查你要期望的東西的話,執行


$ git show

As a special shortcut,



$ git commit -a

will update the index with any files that you’ve modified or removed and create a commit, all in one step.


A number of commands are useful for keeping track of what you’re about to commit:



$ git diff --cached # difference between HEAD and the index; what
# would be committed if you ran "commit" now.
$ git diff          # difference between the index file and your
# working directory; changes that would not
# be included if you ran "commit" now.
$ git diff HEAD     # difference between HEAD and working tree; what
# would be committed if you ran "commit -a" now.
$ git status        # a brief per-file summary of the above.

can also use git-gui(1) to create commits, view changes in the index
and the working tree files, and individually select diff hunks for
inclusion in the index (by right-clicking on the diff hunk and choosing
“Stage Hunk For Commit”).

你還可以用 git-gui(1) 來建立交付,你將看到在索引和工作樹檔案中的變更,並可以個別地選取在索引中的變更塊進行提交(在變更塊上右擊滑鼠並選擇“Stage Hunk For Commit”)。


Creating good commit messages 寫好交付資訊

not required, it’s a good idea to begin the commit message with a
single short (less than 50 character) line summarizing the change,
followed by a blank line and then a more thorough description. Tools
that turn commits into email, for example, use the first line on the
Subject line and the rest of the commit in the body.


  • 譯註:
    工作樹中的 “.git/COMMIT_EDITMSG” 檔案是預設的交付資訊檔案,如果你的 git commit
    命令沒有帶 -m 引數,那麼 git 將會為你開啟系統預設編輯器編輯這個檔案作為交付資訊。


Ignoring files 忽略檔案

A project
will often generate files that you do not want to track with git. This
typically includes files generated by a build process or temporary
backup files made by your editor. Of course, not tracking files with
git is just a matter of not calling “git-add” on them. But it quickly
becomes annoying to have these untracked files lying around; e.g. they
make “git add .” practically useless, and they keep showing up in the
output of “git status”.

個專案總是經常產生一些你不想讓 git
來跟蹤的檔案。典型的就是編譯過程中的中間檔案,或者是編輯器的臨時備份檔案等等。當然了,不跟蹤某個檔案只不過是不用 “git add”
命令將它加入 git 而已。不過你很快會不勝其擾;譬如你不小心用 “git add .” 命令將它加入了 git,並且這些檔案總是在 “git
status” 命令的輸出中出現。

can tell git to ignore certain files by creating a file called
.gitignore in the top level of your working directory, with contents
such as:

你可以在工作樹的頂層目錄中建立一個叫 .gitignore 的檔案來告訴 git 那些檔案是要忽略的,檔案的內容大致如下:


# Lines starting with '#' are considered comments.
# Ignore any file named foo.txt.
# Ignore (generated) html files,
# except foo.html which is maintained by hand.
# Ignore objects and archives.

gitignore(5) for a detailed explanation of the syntax. You can also
place .gitignore files in other directories in your working tree, and
they will apply to those directories and their subdirectories. The
.gitignore files can be added to your repository like any other files
(just run git add .gitignore and git commit, as usual), which is
convenient when the exclude patterns (such as patterns matching build
output files) would also make sense for other users who clone your

考 gitignore(5) 得到更多的語法的解析。你還可以將 .gitignore
檔案放到工作樹的其他目錄下,這樣它將影響它所處的目錄以及下級目錄。.gitignore 檔案當然也可以像一般的檔案那樣納入為 git
的跟蹤中(只需要執行 git add .gitignore 和 git commit,就像處理一般的檔案那樣將行了)。如果存在 “排除匹配”

  • 譯註:
    關於 排除匹配
    可以參考 gitignore,排除匹配的表述檔案是與 git 一起安裝的,例如可能在 /usr/share/git-core/templates/info/exclude。

you wish the exclude patterns to affect only certain repositories
(instead of every repository for a given project), you may instead put
them in a file in your repository named .git/info/exclude, or in any
file specified by the core.excludesfile configuration variable. Some
git commands can also take exclude patterns directly on the command
line. See gitignore(5) for the details.

果你希望排除匹配只是影響某些指定的版本庫(而不是一個專案的每個版本庫),你可以用在你的版本庫中 .git/info/exclude
檔案來取代全域性的排除匹配,或者是針對任何一個檔案在 git 的配置檔案中用 core.excludesfile 配置引數來指定。某些 git
命令是可以做命令列引數中直接接受排除匹配的。 詳情參考 gitignore(5)。


How to merge 如何合併

You can rejoin two diverging branches of development using git-merge(1):

你可以將兩個研發的分支用 git-merge(1) 合併在一起:


$ git merge branchname

the development in the branch “branchname” into the current branch. If
there are conflicts—for example, if the same file is modified in two
different ways in the remote branch and the local branch—then you are
warned; the output may look something like this:

這是合併另外一個分支 “branchname” 到當前分支。如果同一個檔案無論是在遠端分支,還是在本地分支上的發展程序中被修改過,那麼將出現衝突,此時你會得到一個提示,提示的輸出大概是下面的樣子:


$ git merge next
100% (4/4) done
Auto-merged file.txt
CONFLICT (content): Merge conflict in file.txt
Automatic merge failed; fix conflicts and then commit the result.

markers are left in the problematic files, and after you resolve the
conflicts manually, you can update the index with the contents and run
git commit, as you normally would when creating a new file.

衝突標誌會被保留在檔案中,並且當你手動解決衝突之後,你可以用新的內容重新整理索引,並執行 git commit,這類似你建立一個新的檔案之後所做的那樣。

you examine the resulting commit using gitk, you will see that it has
two parents, one pointing to the top of the current branch, and one to
the top of the other branch.

如果你用 gitk 來解決衝突,你將會看到解決衝突的這個交付有兩個父交付,一個指向當前分支的頂部,另一個指向兩個另外一個分支的頂部。


Resolving a merge 解決合併衝突

When a
merge isn’t resolved automatically, git leaves the index and the
working tree in a special state that gives you all the information you
need to help resolve the merge.

當合並衝突不能自動解決時,git 將會讓索引和工作樹都保持在你可以得到所有資訊的特殊狀態,此時你需要協助 git 解決合併衝突。

with conflicts are marked specially in the index, so until you resolve
the problem and update the index, git-commit(1) will fail:

衝突檔案會在索引中特別被標記起來,直到你解決這些衝突並重新整理索引為止,否則 git commit(1) 將出錯:


$ git commit
file.txt: needs merge

git-status(1) will list those files as “unmerged”, and the files with
conflicts will have conflict markers added, like this:

並且,git-status(1) 將列舉出 “unmerged” 的那些檔案,檔案中也會被加入衝突的標誌,它看起來大致是這樣:


<<<<<<< HEAD:file.txt
Hello world
>>>>>>> 77976da35a11db4580b80ae27e8d65caf5208086:file.txt

All you need to do is edit the files to resolve the conflicts, and then



$ git add file.txt
$ git commit

that the commit message will already be filled in for you with some
information about the merge. Normally you can just use this default
message unchanged, but you may add additional commentary of your own if


The above is all you need to know to resolve a simple merge. But git also provides more information to help resolve conflicts:

以上你是必須知道的有關簡單合併的知識,不過 git 會提供更多的資訊幫助你解決衝突:


Getting conflict-resolution help during a merge 在合併過程中取得衝突解決幫助

All of
the changes that git was able to merge automatically are already added
to the index file, so git-diff(1) shows only the conflicts. It uses an
unusual syntax:

所有 git 可以自動合併的東西都會被加入到索引中,而且 git-diff(1) 只會顯示衝突,它使用一種特殊的語法來表示:


$ git diff
diff --cc file.txt
index 802992c,2b60207..0000000
--- a/file.txt
@@@ -1,1 -1,1  1,5 @@@
<<<<<<< HEAD:file.txt
Hello world
>>>>>>> 77976da35a11db4580b80ae27e8d65caf5208086:file.txt

that the commit which will be committed after we resolve this conflict
will have two parents instead of the usual one: one parent will be
HEAD, the tip of the current branch; the other will be the tip of the
other branch, which is stored temporarily in MERGE_HEAD.

回想一下,你解決了衝突之後提交的交付將有兩個父交付:一個是 HEAD,當前分支的頂端;另一個是另外一個分支的頂端,它臨時性地儲存為 MERGE_HEAD 。

the merge, the index holds three versions of each file. Each of these
three “file stages” represents a different version of the file:

合併的過程中,索引為每個衝突的檔案保持三個版本,這三個 “檔案階段(file stages)” 表示三個檔案的不同版本:


$ git show :1:file.txt  # the file in a common ancestor of both branches
$ git show :2:file.txt  # the version from HEAD.
$ git show :3:file.txt  # the version from MERGE_HEAD.

you ask git-diff(1) to show the conflicts, it runs a three-way diff
between the conflicted merge results in the work tree with stages 2 and
3 to show only hunks whose contents come from both sides, mixed (in
other words, when a hunk’s merge results come only from stage 2, that
part is not conflicting and is not shown. Same for stage 3).

你用 git diff(1) 來顯示衝突的時候,它將根據你當前工作目錄中的版本與檔案階段 2 和 3

diff above shows the differences between the working-tree version of
file.txt and the stage 2 and stage 3 versions. So instead of preceding
each line by a single ” ” or “-“, it now uses two columns: the first
column is used for differences between the first parent and the working
directory copy, and the second for differences between the second
parent and the working directory copy. (See the “COMBINED DIFF FORMAT”
section of git-diff-files(1) for a details of the format.)

述的輸出顯示了 file.txt 檔案在工作樹中的版本對階段2(stage 2)與階段3(stage 3)中的差異,並且在每行中加上 ” ”
或者 “-” 的字首, 分兩列排列: 第一列用於標記第一個父交付對工作樹的差異,第二列用於表示第一個父交付對工作樹的差異。(參考
git-diff-files(1) 中的 “COMBINED DIFF FORMAT” 段,取得詳細資訊)

After resolving the conflict in the obvious way (but before updating the index), the diff will look like:

解決了衝突之後(但未重新整理索引之前), 差異輸出類似這樣:


$ git diff
diff --cc file.txt
index 802992c,2b60207..0000000
--- a/file.txt
@@@ -1,1 -1,1  1,1 @@@
- Hello world
Goodbye world

shows that our resolved version deleted “Hello world” from the first
parent, deleted “Goodbye” from the second parent, and added “Goodbye
world”, which was previously absent from both.

這個輸出顯示我們的解決方案是刪除來自第一個父交付的 “Hello world”,刪除來自第二個父交付中的 “Goodbye”,加入了 “Goodbye world”,這是在兩個父交付中都從來沒有出現過的內容。

Some special diff options allow diffing the working directory against any of these stages:



$ git diff -1 file.txt          # diff against stage 1
$ git diff --base file.txt      # same as the above
$ git diff -2 file.txt          # diff against stage 2
$ git diff --ours file.txt      # same as the above
$ git diff -3 file.txt          # diff against stage 3
$ git diff --theirs file.txt    # same as the above.

The git-log(1) and gitk(1) commands also provide special help for merges:

git-log(1) 與 gitk(1) 命令會有關於合併的專題幫助:


$ git log --merge
$ gitk --merge

These will display all commits which exist only on HEAD or on MERGE_HEAD, and which touch an unmerged file.

如此將顯示所有僅存於 HEAD 或者是 MERGE_HEAD 中的所有涉及未合併的檔案的交付。

You may also use git-mergetool(1), which lets you merge the unmerged files using external tools such as Emacs or kdiff3.

你還可以用 git-mergetool(1) 命令,它容許你使用第三方合併工具進行合併操作,譬如 Emacs 或者是 kdiff3。

Each time you resolve the conflicts in a file and update the index:



$ git add file.txt

different stages of that file will be “collapsed”, after which git-diff
will (by default) no longer show diffs for that file.

之後,其他的差異階段檔案將被丟棄, git-diff 命令將顯示沒有任何差異。


Undoing a merge 撤銷合併

If you get stuck and decide to just give up and throw the whole mess away, you can always return to the pre-merge state with



$ git reset --hard HEAD

Or, if you’ve already committed the merge that you want to throw away,



$ git reset --hard ORIG_HEAD

this last command can be dangerous in some cases—never throw away a
commit you have already committed if that commit may itself have been
merged into another branch, as doing so may confuse further merges.



Fast-forward merges 快進合併

There is
one special case not mentioned above, which is treated differently.
Normally, a merge results in a merge commit, with two parents, one
pointing at each of the two lines of development that were merged.


if the current branch is a descendant of the other—so every commit
present in the one is already contained in the other—then git just
performs a “fast forward”; the head of the current branch is moved
forward to point at the head of the merged-in branch, without any new
commits being created.

不過,要是當前分支的東西都是另外一個分支中的東西的後裔的時候,git 只會進行 “快進” 操作;也就是說,它只會將當前的分支頭快進到合併過後的分支頭,而不會建立一個新的交付。


Fixing mistakes 修復失誤

If you’ve
messed up the working tree, but haven’t yet committed your mistake, you
can return the entire working tree to the last committed state with



$ git reset --hard HEAD

If you make a commit that you later wish you hadn’t, there are two fundamentally different ways to fix the problem:


  • 1.
    You can create a new commit that undoes whatever was done by the old
    commit. This is the correct thing if your mistake has already been made
  • 2.
    You can go back and modify the old commit. You should never do this if
    you have already made the history public; git does not normally expect
    the “history” of a project to change, and cannot correctly perform
    repeated merges from a branch that has had its history changed.
  • 1. 你可以建立一個新的交付來撤銷你舊的交付的東西。這個才是正確的方法,如果你的錯誤已經發布出去了。
  • 2. 你可以回到那個老的交付中並修改它。可是當你的版本庫歷史已經發布出去之後,你絕對不應該這樣做;通常地 git 不會設想專案的 “歷史” 是會出現改變的。同時也不能夠正確地對那些歷史發生過改變的分支合併進行重新整合。


Fixing a mistake with a new commit 修復一個新的交付中的失誤

a new commit that reverts an earlier change is very easy; just pass the
git-revert(1) command a reference to the bad commit; for example, to
revert the most recent commit:

建立一個新的交付去逆轉一個最近的交付的變更是很容易的;通過 git-revert(1) 命令指向一個壞交付就行;例如,逆轉一個最新的交付:


$ git revert HEAD

will create a new commit which undoes the change in HEAD. You will be
given a chance to edit the commit message for the new commit.

這將建立一個新交付去撤銷在 HEAD 中的變更。這樣就給了你一個機會編輯新交付中的註釋。

You can also revert an earlier change, for example, the next-to-last:



$ git revert HEAD^

this case git will attempt to undo the old change while leaving intact
any changes made since then. If more recent changes overlap with the
changes to be reverted, then you will be asked to fix conflicts
manually, just as in the case of resolving a merge.

在這種情況下,每當 git 離開過一個原封的變更時,它將試圖將撤銷的變更應用到其後的專案歷史中。如果有多個變更重疊,你可能會被要求解決衝突,正如你要解決合併衝突那樣。


Fixing a mistake by rewriting history 通過重寫歷史來修復失誤

If the
problematic commit is the most recent commit, and you have not yet made
that commit public, then you may just destroy it using git-reset.

如果一個有問題的交付是最新的交付,並且你還沒有將它釋出出去,那麼你可以用 git-reset 來銷燬它。

you can edit the working directory and update the index to fix your
mistake, just as if you were going to create a new commit, then run



$ git commit --amend

will replace the old commit by a new commit incorporating your changes,
giving you a chance to edit the old commit message first.


you should never do this to a commit that may already have been merged
into another branch; use git-revert(1) instead in that case.

重申一次,你絕對不應該在這個交付已經併合並經其他的分支的情況下這樣做; 此時你應該用 git-revert(1) 來處理失誤。

is also possible to replace commits further back in the history, but
this is an advanced topic to be left for another chapter.



Checking out an old version of a file 提取一個檔案的舊版本

In the
process of undoing a previous bad change, you may find it useful to
check out an older version of a particular file using git-checkout(1).
We’ve used git-checkout before to switch branches, but it has quite
different behavior if it is given a path name: the command

在處理撤銷以前壞變更的過程中,你可能已經認識到 git-checkout(1) 用於提取一個具體的檔案的舊版本的有用性。以前我們用 git-checkout 來在舊的版本之間切換,不過如果你給出路徑的名稱,那麼這個命令的表現會完全不同。


$ git checkout HEAD^ path/to/file

path/to/file by the contents it had in the commit HEAD^, and also
updates the index to match. It does not change branches.

它將以交付 HEAD^ 中的內容來覆蓋 path/to/file 檔案,並且同時恰當地重新整理索引。它不會改變分支。

you just want to look at an old version of the file, without modifying
the working directory, you can do that with git-show(1):

假如你只是想看看一個檔案的舊版本是什麼樣子,而不想觸動工作樹的話,你可以用 git-show(1) 命令這樣做:


$ git show HEAD^:path/to/file

which will display the given version of the file.



Temporarily setting aside work in progress 臨時放下手頭上的工作

While you
are in the middle of working on something complicated, you find an
unrelated but obvious and trivial bug. You would like to fix it before
continuing. You can use git-stash(1) to save the current state of your
work, and after fixing the bug (or, optionally after doing so on a
different branch and then coming back), unstash the work-in-progress



$ git stash save "work in progress for foo feature"

command will save your changes away to the stash, and reset your
working tree and the index to match the tip of your current branch.
Then you can make your fix as usual.



... edit and test ...
$ git commit -a -m "blorpl: typofix"

After that, you can go back to what you were working on with git stash apply:

之後,你就可以用 git stash apply 命令回到原來的工作狀態:


$ git stash apply


Ensuring good performance 確保好的效能

On large
repositories, git depends on compression to keep the history
information from taking up too much space on disk or in memory.

在大型專案中, git 依靠對歷史資訊進行壓縮來利用磁碟空間和記憶體空間。

This compression is not performed automatically. Therefore you should occasionally run git-gc(1):

不過壓縮不是自動的,你應該時不時執行一下 git-gc(1):


$ git gc

to recompress the archive. This can be very time-consuming, so you may prefer to run git-gc when you are not doing other work.



Ensuring reliability 確保伸縮性


Checking the repository for corruption 檢查版本的損壞

git-fsck(1) command runs a number of self-consistency checks on the
repository, and reports on any problems. This may take some time. The
most common warning by far is about “dangling” objects:

git-fsck(1) 命令在版本庫中執行一系列的一致性性檢查,並報告錯誤。這可能需要一些時間,最常見的警告就是關於“懸空”物件:


$ git fsck
dangling commit 7281251ddd2a61e38657c827739c57015671a6b3
dangling commit 2706a059f258c6b245f298dc4ff2ccd30ec21a63
dangling commit 13472b7c4b80851a1bc551779171dcb03655e9b5
dangling blob 218761f9d90712d37a9c5e36f406f92202db07eb
dangling commit bf093535a34a4d35731aa2bd90fe6b176302f14f
dangling commit 8e4bec7f2ddaa268bef999853c25755452100f8e
dangling tree d50bb86186bf27b681d25af89d3b5b68382e4085
dangling tree b24c2473f1fd3d91352a624795be026d64c8841f

objects are not a problem. At worst they may take up a little extra
disk space. They can sometimes provide a last-resort method for
recovering lost work—see the section called “Dangling objects” for

懸空物件不是一個問題。它們只是佔多了一點的磁碟空間。某些時候,它們是恢復丟失的工作的救命稻草。詳情請參考 “懸空物件” 一節。


Recovering lost changes 恢復丟失的變更


Reflogs 引用日誌

Say you
modify a branch with git-reset(1) —hard, and then realize that the
branch was the only reference you had to that point in history.

要是你用 git-reset(1) –hard 來恢復了你的分支,接著你才醒悟到那個分支是你在版本庫中所處的歷史時刻的唯一引用。

git also keeps a log, called a “reflog”, of all the previous values of
each branch. So in this case you can still find the old history using,
for example,

幸運的是,git 總是儲存一個日誌,叫“引用日誌”,它儲存了每個分支以前的值。在這種情況下,你仍然可以將舊歷史找回來,例如:


$ git log [email protected]{1}

lists the commits reachable from the previous version of the “master”
branch head. This syntax can be used with any git command that accepts
a commit, not just with git log. Some other examples:

他將列舉所有對於 “master” 分支頭具有可及性的交付。這種形式的語法可以用於任何的 git 命令而不僅僅是 git log。舉些例子:


$ git show [email protected]{2}           # See where the branch pointed 2,
$ git show [email protected]{3}           # 3, ... changes ago.
$ gitk [email protected]{yesterday}       # See where it pointed yesterday,
$ gitk [email protected]{"1 week ago"}    # ... or last week
$ git log --walk-reflogs master # show reflog entries for master

A separate reflog is kept for the HEAD, so

有關 HEAD 的引用日誌,則是


$ git show [email protected]{"1 week ago"}

show what HEAD pointed to one week ago, not what the current branch
pointed to one week ago. This allows you to see the history of what
you’ve checked out.

它將顯示 HEAD 一週之前指向什麼,而不是當前分支指向什麼。這就容許你看到如果你要提取歷史的話將得到什麼。

reflogs are kept by default for 30 days, after which they may be
pruned. See git-reflog(1) and git-gc(1) to learn how to control this
pruning, and see the “SPECIFYING REVISIONS” section of git-rev-parse(1)
for details.

應用日誌預設會被儲存 30 天,之後它們就會被剪裁掉。參考 git-reflog(1) 與 git-gc(1) 學習如何控制剪裁,詳情檢視 git-rev-parse(1) 中的 “SPECIFYING REVISION” 一節。

that the reflog history is very different from normal git history.
While normal history is shared by every repository that works on the
same project, the reflog history is not shared: it tells you only about
how the branches in your local repository have changed over time.

注意,引用日誌完全不同與正式的 git 歷史記錄。正式的歷史記錄在同一個專案中的任何一個版本庫中都被共享,而引用日誌是不共享的:它僅僅告訴你在某段時間內你的本地版本庫中的分支如何變化。


Examining dangling objects 檢驗懸空物件

In some
situations the reflog may not be able to save you. For example, suppose
you delete a branch, then realize you need the history it contained.
The reflog is also deleted; however, if you have not yet pruned the
repository, then you may still be able to find the lost commits in the
dangling objects that git-fsck reports. See the section called
“Dangling objects” for the details.

果你還沒有剪裁版本庫的話,你仍然可以在懸空物件中找回那些丟失的交付,git-fsck 會給你報告懸空物件的列表。詳情參考 “懸空物件” 一節。


$ git fsck
dangling commit 7281251ddd2a61e38657c827739c57015671a6b3
dangling commit 2706a059f258c6b245f298dc4ff2ccd30ec21a63
dangling commit 13472b7c4b80851a1bc551779171dcb03655e9b5

You can examine one of those dangling commits with, for example,



$ gitk 7281251ddd --not --all

does what it sounds like: it says that you want to see the commit
history that is described by the dangling commit(s), but not the
history that is described by all your existing branches and tags. Thus
you get exactly the history reachable from that commit that is lost.
(And notice that it might not be just one commit: we only report the
“tip of the line” as being dangling, but there might be a whole deep
and complex commit history that was dropped.)


If you decide you want the history back, you can always create a new reference pointing to it, for example, a new branch:



$ git branch recovered-branch 7281251ddd

Other types of dangling objects (blobs and trees) are also possible, and dangling objects can arise in other situations.



Chapter 4. Sharing development with others 第四章. 與他人協同研發


Getting updates with git-pull 用 git-pull 取得更新

After you
clone a repository and make a few changes of your own, you may wish to
check the original repository for updates and merge them into your own


have already seen how to keep remote tracking branches up to date with
git-fetch(1), and how to merge two branches. So you can merge in
changes from the original repository’s master branch with:

我們已經看過如何用 git-fetch(1) 命令來保持對遠端分支的更新跟蹤,如何合併兩個分支。那麼你可以這樣合併源頭版本庫上的主分支(master)中的變化:


$ git fetch
$ git merge origin/master

However, the git-pull(1) command provides a way to do this in one step:



$ git pull origin master

fact, if you have “master” checked out, then by default “git pull”
merges from the HEAD branch of the origin repository. So often you can
accomplish the above with just a simple

事實上,如果你已經切換到 “master” 分支的話,”git pull” 命令預設就是合併源頭版本庫中的 HEAD。所以你經常可以通過下面簡單的命令來完成上述的操作


$ git pull

generally, a branch that is created from a remote branch will pull by
default from that branch. See the descriptions of the
branch.<name>.remote and branch.<name>.merge options in
git-config(1), and the discussion of the —track option in
git-checkout(1), to learn how to control these defaults.

廣泛地,一個由遠端版本庫上建立的分支將作為本地對應分支的預設拉入分支。參考 git-config(1) 中有關
branch.<name>.remote 和 branch.<name>.merge 中的有關說明,以及
git-checkout(1) 中有關 –track 的討論,學習如何控制那些預設操作。

addition to saving you keystrokes, “git pull” also helps you by
producing a default commit message documenting the branch and
repository that you pulled from.

為了節省你打字的功夫,”git pull” 已經幫你自動產生了交付資訊來說明你拉入的版本庫和分支了。

note that no such commit will be created in the case of a fast forward;
instead, your branch will just be updated to point to the latest commit
from the upstream branch.)


git-pull command can also be given “.” as the “remote” repository, in
which case it just merges in a branch from the current repository; so
the commands

git-pull 命令可以用一個 “.” 作為 “遠端的” 版本庫,在這種情況下則是合併當前版本庫的另外一個分支;所以如下命令


$ git pull . branch
$ git merge branch

are roughly equivalent. The former is actually very commonly used.



Submitting patches to a project 向專案提交補丁

If you just have a few changes, the simplest way to submit them may just be to send them as patches in email:


First, use git-format-patch(1); for example:

首先,使用 git-format-patch(1); 例子:


$ git format-patch origin

produce a numbered series of files in the current directory, one for
each patch in the current branch but not in origin/HEAD.

這將會在當前目錄下產生一系列檔案,每個補丁都是針對當前分支,而不是 origin/HEAD。

can then import these into your mail client and send them by hand.
However, if you have a lot to send at once, you may prefer to use the
git-send-email(1) script to automate the process. Consult the mailing
list for your project first to determine how they prefer such patches
be handled.

你可以將這些檔案匯入你的郵件客戶端工具並手工傳送出去。不過,如果你一次性要傳送很多東西的話,你可以使用 git-send-email(1) 指令碼來自動處理。首先參考一下你的專案的郵件列表,瞭解一下他們是如何處理補丁的。


Importing patches to a project 給專案匯入補丁

Git also
provides a tool called git-am(1) (am stands for “apply mailbox”), for
importing such an emailed series of patches. Just save all of the
patch-containing messages, in order, into a single mailbox file, say
“patches.mbox”, then run

Git 還提供了專門的工具 git-am(1)(am 是 “apply mailbox” 的縮寫),它用於匯入郵件中的補丁串。補丁郵件中同時也包含了補丁資訊,郵件包的名字叫 “patches.mbox”,你可以執行


$ git am -3 patches.mbox

will apply each patch in order; if any conflicts are found, it will
stop, and you can fix the conflicts as described in “Resolving a
merge”. (The “-3” option tells git to perform a merge; if you would
prefer it just to abort and leave your tree and index untouched, you
may omit that option.)

Git 按順序將補丁應用到專案中;要是出現衝突,它將停下來,你可以修正衝突,就像我們在 “解決合併衝突” 中所闡述的那樣。(選項 “-3” 告訴執行合併操作;如果你想它不觸動你的工作樹和索引,你可以忽略這個選項。)

Once the index is updated with the results of the conflict resolution, instead of creating a new commit, just run



$ git am --resolved

and git will create the commit for you and continue applying the remaining patches from the mailbox.

這樣 git 將建立一個新的交付,並繼續匯入郵件包中的餘下補丁。0

final result will be a series of commits, one for each patch in the
original mailbox, with authorship and commit log message each taken
from the message containing each patch.



Public git repositories 釋出 git 版本庫

way to submit changes to a project is to tell the maintainer of that
project to pull the changes from your repository using git-pull(1). In
the section “Getting updates with git-pull” we described this as a way
to get updates from the “main” repository, but it works just as well in
the other direction.

外一個提交變更到專案的方法是告訴專案的維護者從你的版本庫中用 git-pull(1) 命令將你的變更拉入。在 “用 git-pull
取得更新” 一節中,我們已經闡述過怎麼用這個命令來從 “主” 版本庫中更新你自己的版本庫,現在只是這個命令的反向應用而已。

you and the maintainer both have accounts on the same machine, then you
can just pull changes from each other’s repositories directly; commands
that accept repository URLs as arguments will also accept a local
directory name:

如果你和專案的維護者都在同一部機器上有使用者帳號,那麼你就可以從其他的版本庫中直接拉入變更;git 命令同樣可以接受本地目錄名作為連線名稱。


$ git clone /path/to/repository
$ git pull /path/to/other/repository

or an ssh URL:

或者是 ssh 的連線名:


$ git clone ssh://yourhost/~you/repository

For projects with few developers, or for synchronizing a few private repositories, this may be all you need.


the more common way to do this is to maintain a separate public
repository (usually on a different host) for others to pull changes
from. This is usually more convenient, and allows you to cleanly
separate private work in progress from publicly visible work.


will continue to do your day-to-day work in your personal repository,
but periodically “push” changes from your personal repository into your
public repository, allowing other developers to pull from that
repository. So the flow of changes, in a situation where there is one
other developer with a public repository, looks like this:

你將在你的個人版本庫中一天接一天地開展你的工作,並階段性地 “push” 將變更從你的個人版本庫推入到你的公共版本庫,讓其他的開發者從可以從你的公共版本庫中拉入你的工作。變更的流程,就像你面向另外一個開發者,他也有自己的公共版本庫那樣,看起來如下:


                      you push
your personal repo ------------------> your public repo
^                                     |
|                                     |
| you pull                            | they pull
|                                     |
|                                     |
|               they push             V
their public repo <------------------- their repo

We explain how to do this in the following sections.



Setting up a public repository 建立一個公共版本庫

your personal repository is in the directory ~/proj. We first create a
new clone of the repository and tell git-daemon that it is meant to be

假設你的個人版本庫在目錄 ~/proj。我們首先克隆一個新的版本庫並告訴 git 守護程序(git-daemon)這個是一個公共版本庫:


$ git clone --bare ~/proj proj.git
$ touch proj.git/git-daemon-export-ok

resulting directory proj.git contains a “bare” git repository—it is
just the contents of the “.git” directory, without any files checked
out around it.

執行的結果是建立了一個 proj.git 的裸目錄 — 他僅僅包含了 “.git” 目錄,而不提取出版本庫中包含的檔案。

copy proj.git to the server where you plan to host the public
repository. You can use scp, rsync, or whatever is most convenient.

接著,拷貝 proj.git 到你計劃用來部署公共版本庫的主機上去。你可以用 scp, rsync,反正就是你用得最方便的工具。


Exporting a git repository via the git protocol 通過 git 協議公開版本庫

This is the preferred method.


someone else administers the server, they should tell you what
directory to put the repository in, and what git:// URL it will appear
at. You can then skip to the section “Pushing changes to a public
repository”, below.

如果伺服器維護者中的某人,他告訴了你在伺服器中版本庫應該放在什麼目錄和 git:// 的連線路徑。你可以跳到下面 “將變更推入公共版本庫” 一節。

all you need to do is start git-daemon(1); it will listen on port 9418.
By default, it will allow access to any directory that looks like a git
directory and contains the magic file git-daemon-export-ok. Passing
some directory paths as git-daemon arguments will further restrict the
exports to those paths.

否則,你就需要啟動 git-daemon(1);它將監聽 9418 埠。預設地,它容許訪問所有個的包含魔術檔案 git-daemon-export-ok 的 git 目錄。向 git-deamon 傳遞目錄引數可以有效限制輸出目錄。

can also run git-daemon as an inetd service; see the git-daemon(1) man
page for details. (See especially the examples section.)

你可以將 git-deamon 作為系統服務;詳情參考 git-daemon(1) 手冊頁(尤其是 example 一節)。


Exporting a git repository via http 通過 http 協議公開版本庫

The git
protocol gives better performance and reliability, but on a host with a
web server set up, http exports may be simpler to set up.

使用 git 協議會得到更好的效能和伸縮性,不過對於已經配置好網頁伺服器的主機來說,用 http 協議公開版本庫更簡單。

you need to do is place the newly created bare git repository in a
directory that is exported by the web server, and make some adjustments
to give web clients some extra information they need:

你需要做的就是在 Web 伺服器的目錄中放置新的裸版本庫,並根據 Web 服務的擴充套件資訊為 Web 客戶端作一些調整。


$ mv proj.git /home/you/public_html/proj.git
$ cd proj.git
$ git --bare update-server-info
$ mv hooks/post-update.sample hooks/post-update

(For an explanation of the last two lines, see git-update-server-info(1) and githooks(5).)

(關於上述命令的最後兩行的解釋,參考 git-update-server-info(1) 與 githooks(5)。)

the URL of proj.git. Anybody else should then be able to clone or pull
from that URL, for example with a command line like:

公佈 proj.git 的 URL,任何人都應該可以通過 URL 克隆和拉入版本庫的東西了,命令大致如下:


$ git clone

(See also setup-git-server-over-http for a slightly more sophisticated setup using WebDAV which also allows pushing over http.)

(還可以參考 setup-git-server-over-http,通過設定 WebDAV 也可以通過 http 來容許推入操作。)


Pushing changes to a public repository 將變更推入到公共版本庫

Note that
the two techniques outlined above (exporting via http or git) allow
other maintainers to fetch your latest changes, but they do not allow
write access, which you will need to update the public repository with
the latest changes created in your private repository.

注意,上述的兩個技術框架(通過 http 或者 git)容許其他的維護者抓取你的最新更新,但是他們是無法進行寫入操作的,因此你需要用你個人版本庫中的最新變更去更新公共版本庫。

simplest way to do this is using git-push(1) and ssh; to update the
remote branch named “master” with the latest state of your branch named
“master”, run

最簡單的方式是使用 git-push(1) 命令和 ssh, 將你個人版本庫中的 “master” 分支的最新狀態更新到遠端版本庫上 “master” 的分支,執行


$ git push ssh:// master:master

or just



$ git push ssh:// master

with git-fetch, git-push will complain if this does not result in a
fast forward; see the following section for details on handling this

正如 git-fetch 命令一樣,git-push 命令的執行結果如果不是 快進 操作的話,將會遇到一些狀況;下面的章節將詳細介紹處理這些狀況的方法。

that the target of a “push” is normally a bare repository. You can also
push to a repository that has a checked-out working tree, but the
working tree will not be updated by the push. This may lead to
unexpected results if the branch you push to is the currently
checked-out branch!


As with git-fetch, you may also set up configuration options to save typing; so, for example, after

與 git-fetch 命令一樣,你可以通過配置項來節省打字量;作為例子,在下面命令之後


$ cat >>.git/config <<EOF
[remote "public-repo"]
url = ssh://

you should be able to perform the above push with just



$ git push public-repo master

the explanations of the remote.<name>.url,
branch.<name>.remote, and remote.<name>.push options in
git-config(1) for details.

詳細說明情參考 git-config(1) 中有關 remote.<name>.url, branch.<name>.remote, 與 remote.<name>.push。


What to do when a push fails 推入失敗之後該怎麼處理

If a push would not result in a fast forward of the remote branch, then it will fail with an error like:



error: remote 'refs/heads/master' is not an ancestor of
local  'refs/heads/master'.
Maybe you are not up-to-date and need to pull first?
error: failed to push to 'ssh://'

This can happen, for example, if you:

  • use git-reset —hard to remove already-published commits, or
  • use
    git-commit —amend to replace already-published commits (as in the
    section called “Fixing a mistake by rewriting history”), or
  • use
    git-rebase to rebase any already-published commits (as in the section
    called “Keeping a patch series up to date using git-rebase”).


  • 使用 git-reset –hard 刪除了某些已經傳播出去的交付,或者
  • 使用 git-commit –amend 覆蓋了某些已經傳播出去的交付(在 “通過重寫歷史修復失誤” 一節中提到過),或者
  • 使用 git-rebase 對已經傳播出去的交付進行了重新定位(在 “使用 git-rebase 保持補丁串的新穎” 中說明)。

You may force git-push to perform the update anyway by preceding the branch name with a plus sign:

你可以用 git-push 命令在分支名前面加一個加號進行強制推入:


$ git push ssh://  master

whenever a branch head in a public repository is modified, it is
modified to point to a descendant of the commit that it pointed to
before. By forcing a push in this situation, you break that convention.
(See the section called “Problems with rewriting history”.)

正常情況下,推入操作就是修改公共版本庫的分支頭,將它更新到它原來指向的那個交付的後裔交付。在這種狀況下強行推入等如打破了這個規則。(參考 “重寫歷史帶來的問題” 一節。)

this is a common practice for people that need a simple way to publish
a work-in-progress patch series, and it is an acceptable compromise as
long as you warn other developers that this is how you intend to manage
the branch.


also possible for a push to fail in this way when other people have the
right to push to the same repository. In that case, the correct
solution is to retry the push after first updating your work: either by
a pull, or by a fetch followed by a rebase; see the next section and
gitcvs-migration(7) for more.

者是抓取(fetch)之後再進行重定位(rebase);詳情參考下一節和 gitcvs-migration(1)。


Setting up a shared repository 建立共享版本庫

way to collaborate is by using a model similar to that commonly used in
CVS, where several developers with special rights all push to and pull
from a single shared repository. See gitcvs-migration(7) for
instructions on how to set this up.

另外一個協同開發的策略就是使用類似 CVS 那樣的模式,就是各地的開發者都對單個公共的版本庫有特定的寫入許可權。參考 gitcvs-migration(7) 瞭解如何建立這種方式。

while there is nothing wrong with git’s support for shared
repositories, this mode of operation is not generally recommended,
simply because the mode of collaboration that git supports—by
exchanging patches and pulling from public repositories—has so many
advantages over the central shared repository:

無論如何,即使 git 對支援共享版本庫這種開發模式其實是沒有問題的,但是不建議採用這種模式,最簡單的理由就是 git 所支援的由公共版本庫進行補丁交換和拉入的模式,要比中央共享版本庫的模式要先進得多。

  • Git’s
    ability to quickly import and merge patches allows a single maintainer
    to process incoming changes even at very high rates. And when that
    becomes too much, git-pull provides an easy way for that maintainer to
    delegate this job to other maintainers while still allowing optional
    review of incoming changes.
  • Since
    every developer’s repository has the same complete copy of the project
    history, no repository is special, and it is trivial for another
    developer to take over maintenance of a project, either by mutual
    agreement, or because a maintainer becomes unresponsive or difficult to
    work with.
  • The lack of a central group of “committers” means there is less need for formal decisions about who is “in” and who is “out”.
  • Git 可以快速匯入和合並補丁,容許單個的維護者處理高頻率到達的變更。當情況實在是過分的時候,git-pull 容許該維護者將維護工作委託給其他的開發者的同時預覽這些到來的變更。
  • 每個開發者的版本庫始終擁有專案的完整的歷史記錄,沒有任何一個版本庫是特殊的,這為其他的開發者成為專案維護者提供了便利,不管大家一致同意的,還是由於原來的專案維護者已經無法承擔維護的責任或者是對承擔這個工作存在困難。
  • 中央版本庫模式的侷限性在於,至少你要決定“提交者”誰應該參與誰應該出局。


Allowing web browsing of a repository 容許 Web 瀏覽版本庫

gitweb cgi script provides users an easy way to browse your project’s
files and history without having to install git; see the file
gitweb/INSTALL in the git source tree for instructions on setting it

gitweb cgi 指令碼為你瀏覽專案的檔案和歷史提供了一個便利的方法,而無須安裝 git;參考 git 程式碼樹中的 gitweb/INSTALL 學習如何安裝。


Examples 例子


Maintaining topic branches for a Linux subsystem maintainer | Linux 子系統維護者如何維護主題分支

This describes how Tony Luck uses git in his role as maintainer of the IA64 architecture for the Linux kernel.

這裡介紹 Tony Luck 作為 Linux 核心 IA64 架構的維護者如何工作。

He uses two public branches:

  • A
    “test” tree into which patches are initially placed so that they can
    get some exposure when integrated with other ongoing development. This
    tree is available to Andrew for pulling into -mm whenever he wants.
  • A
    “release” tree into which tested patches are moved for final sanity
    checking, and as a vehicle to send them upstream to Linus (by sending
    him a “please pull” request.)


  • “測試”樹裡面放置的是那些整合過的正在進行的開發工作的補丁,這棵樹可以讓 Andrew 隨時執行拉入操作。
  • “釋出”樹裡面放置的是那些已經測試過,要移交做最後的健全性檢驗的補丁,它作為向上遊的 Linus 傳送這些補丁的載體(向他發出一個“拉入”的請求就行了。)

He also uses a set of temporary branches (“topic branches”), each containing a logical grouping of patches.


To set this up, first create your work tree by cloning Linus’s public tree:

首先通過克隆 Linus 的公共樹來建立你自己的工作樹。


$ git clone git:// work
$ cd work

tree will be stored in the remote branch named origin/master, and can
be updated using git-fetch(1); you can track other public trees using
git-remote(1) to set up a “remote” and git-fetch(1) to keep them
up-to-date; see Chapter 1, Repositories and Branches.

的樹將被儲存為一個遠端分支名叫 origin/master,並且可以用 git-fetch(1) 命令進行更新;你可以用
git-remote(1) 命令來跟蹤另外的遠端分支,並用 git-fetch(1) 保持他們的更新;參考 第一章,版本庫與分支。

create the branches in which you are going to work; these start out at
the current tip of origin/master branch, and should be set up (using
the —track option to git-branch(1)) to merge changes in from Linus by

現在建立你用來開展工作的分支;它將以 origin/master 分支的頂端作為起始點,並且設定它預設用來合併來自 Linus 的變更(在 git-branch(1) 命令中使用 –track 選項)。


$ git branch --track test origin/master
$ git branch --track release origin/master

These can be easily kept up to date using git-pull(1).

通過 git-pull(1) 很容易就保持他們的更新。


$ git checkout test && git pull
$ git checkout release && git pull

note! If you have any local changes in these branches, then this merge
will create a commit object in the history (with no local changes git
will simply do a “Fast forward” merge). Many people dislike the “noise”
that this creates in the Linux history, so you should avoid doing this
capriciously in the “release” branch, as these noisy commits will
become part of the permanent history when you ask Linus to pull from
the release branch.

則是簡單地進行快進合併)。許多人並不喜歡在 Linux 的發展歷史中“噪音”,而這些噪音交付當你要求 Linus
從發行分支中拉入的話,將永遠成為 Linux 歷史的一部分。

few configuration variables (see git-config(1)) can make it easy to
push both branches to your public tree. (See the section called
“Setting up a public repository”.)

通過配置變數(參考 git-config(1))可以很容易地將這個兩個分支推出到你的公共樹中。(參考 “建立公共版本庫” 一節。)


$ cat >> .git/config <<EOF
[remote "mytree"]
url =
push = release
push = test

Then you can push both the test and release trees using git-push(1):

接著你可以用 git-push(1) 退出這兩個分支了:


$ git push mytree

or push just one of the test and release branches using:



$ git push mytree test



$ git push mytree release

to apply some patches from the community. Think of a short snappy name
for a branch to hold this patch (or related group of patches), and
create a new branch from the current tip of Linus’s branch:

現在從社群中匯入一些補丁。給這個裝載這些補丁(或者是相關的補丁組)的分支起一個短而酷的名字吧,並且從 Linus 的分支頂端作為新分支的起點。


$ git checkout -b speed-up-spinlocks origin

you apply the patch(es), run some tests, and commit the change(s). If
the patch is a multi-part series, then you should apply each as a
separate commit to this branch.



$ ... patch ... test  ... commit [ ... patch ... test ... commit ]*

When you are happy with the state of this change, you can pull it into the “test” branch in preparation to make it public:

當你覺得這些變更的狀態還不錯,你可以將他們拉入到 “test” 分支中使他們釋出出去了。


$ git checkout test && git pull . speed-up-spinlocks

is unlikely that you would have any conflicts here … but you might if
you spent a while on this step and had also pulled new versions from


time later when enough time has passed and testing done, you can pull
the same branch into the “release” tree ready to go upstream. This is
where you see the value of keeping each patch (or patch series) in its
own branch. It means that the patches can be moved into the “release”
tree in any order.

當進行過充分的測試之後,你可以將這個分支的東西拉入到 “release” 分支中,並準備好向上遊版本傳遞了。到現在你應該體會到將每個補丁(或者是補丁串)保留在他們自己專有的分支中的好處了。它意味著補丁串可以以不同的順序加入到 “release” 樹中。


$ git checkout release && git pull . speed-up-spinlocks

a while, you will have a number of branches, and despite the well
chosen names you picked for each of them, you may forget what they are
for, or what status they are in. To get a reminder of what changes are
in a specific branch, use:



$ git log linux..branchname | git shortlog

To see whether it has already been merged into the test or release branches, use:

要檢視他們是否已經合併到 test 或者是 release 分支的情況,使用:


$ git log test..branchname




$ git log release..branchname

(If this branch has not yet been merged, you will see some log entries. If it has been merged, then there will be no output.)


a patch completes the great cycle (moving from test to release, then
pulled by Linus, and finally coming back into your local
“origin/master” branch), the branch for this change is no longer
needed. You detect this when the output from:

一旦補丁完成了建立的週期(從 test 轉移到了 release,並且被 Linus 拉入,最終成為你的本地分支 “origin/master” 的一部分),這個分支就沒有再存在的必要了。你可以通過以下的命令的輸出來判斷:


$ git log origin..branchname

is empty. At this point the branch can be deleted:



$ git branch -d branchname

changes are so trivial that it is not necessary to create a separate
branch and then merge into each of the test and release branches. For
these changes, just apply directly to the “release” branch, and then
merge that into the “test” branch.

某些變更是沒有必要為他們建立獨立的分支的,將他們直接合併到 test 和 release 中去就可以。對於這些變更,可以直接將他們匯入 release 分支,併合併到 test 分支中。

To create diffstat and shortlog summaries of changes to include in a “please pull” request to Linus you can use:

請求 Linus 進行拉入的時候,最好建立一個變更的短摘要:


$ git diff --stat origin..release




$ git log -p origin..release | git shortlog

Here are some of the scripts that simplify all this even further.



==== update script ====
# Update a branch in my GIT tree.  If the branch to be updated
# is origin, then pull from  Otherwise merge
# origin/master branch into test|release branch
case "$1" in
git checkout $1 && git pull . origin
before=$(git rev-parse refs/remotes/origin/master)
git fetch origin
after=$(git rev-parse refs/remotes/origin/master)
if [ $before != $after ]
git log $before..$after | git shortlog
echo "Usage: $0 origin|test|release" 1>&2
exit 1


==== merge script ====
# Merge a branch into either the test or release branch
echo "Usage: $pname branch test|release" 1>&2
exit 1
git show-ref -q --verify -- refs/heads/"$1" || {
echo "Can't see branch <$1>" 1>&2
case "$2" in
if [ $(git log $2..$1 | wc -c) -eq 0 ]
echo $1 already merged into $2 1>&2
exit 1
git checkout $2 && git pull . $1


==== status script ====
# report on status of my ia64 GIT tree
gb=$(tput setab 2)
rb=$(tput setab 1)
restore=$(tput setab 9)
if [ `git rev-list test..release | wc -c` -gt 0 ]
echo $rb Warning: commits in release that are not in test $restore
git log test..release
for branch in `git show-ref --heads | sed 's|^.*/||'`
if [ $branch = test -o $branch = release ]
echo -n $gb ======= $branch ====== $restore " "
for ref in test release origin/master
if [ `git rev-list $ref..$branch | wc -c` -gt 0 ]
case $status in
echo $rb Need to pull into test $restore
echo "In test"
echo "Waiting for linus"
echo $rb All done $restore
echo $rb "<$status>" $restore
git log origin/master..$branch | git shortlog


Chapter 5. Rewriting history and maintaining patch series 第五章. 改寫歷史與維護補丁串

commits are only added to a project, never taken away or replaced. Git
is designed with this assumption, and violating it will cause git’s
merge machinery (for example) to do the wrong thing.

通常來說,交付一旦被加入到專案中,就永遠不能被拋棄和覆蓋。Git 就是以這樣的假設來設計的,打破這個原則(只是假設)會導致 git 的合併機制發生錯誤。

However, there is a situation in which it can be useful to violate this assumption.



Creating the perfect patch series 建立出色的補丁串

you are a contributor to a large project, and you want to add a
complicated feature, and to present it to the other developers in a way
that makes it easy for them to read your changes, verify that they are
correct, and understand why you made each change.


If you present all of your changes as a single patch (or commit), they may find that it is too much to digest all at once.


you present them with the entire history of your work, complete with
mistakes, corrections, and dead ends, they may be overwhelmed.


So the ideal is usually to produce a series of patches such that:

  1. Each patch can be applied in order.
  2. Each patch includes a single logical change, together with a message explaining the change.
  3. No
    patch introduces a regression: after applying any initial part of the
    series, the resulting project still compiles and works, and has no bugs
    that it didn’t have before.
  4. The complete series produces the same end result as your own (probably much messier!) development process did.


  1. 每個補丁應用起來都是規整的。
  2. 每個補丁應該是一個獨立的邏輯過程,並帶有說明這個變遷的資訊。
  3. 補丁都不應該造成後退:專案應用了補丁串的任何初始部分之後,專案仍然是可以完整地工作的,並且不包含你原來工作中的漏洞。
  4. 整個補丁串應用完之後,專案所得到的結果應該和你本來的工作(可能是非常散亂的)一樣。

will introduce some tools that can help you do this, explain how to use
them, and then explain some of the problems that can arise because you
are rewriting history.



Keeping a patch series up to date using git-rebase 使用 git-rebase 保持補丁串的新穎

Suppose that you create a branch “mywork” on a remote-tracking branch “origin”, and create some commits on top of it:

假定你建立了一個針對 “origin” 的遠端跟蹤分支叫 “mywork”,並且在這個分支上做了一定的工作。


$ git checkout -b mywork origin
$ vi file.txt
$ git commit
$ vi otherfile.txt
$ git commit

You have performed no merges into mywork, so it is just a simple linear sequence of patches on top of “origin”:

你還沒有在 mywork 中做過任何的合併,所以目前它只是一個在 origin 的頂端上面一個很簡單的佇列。


 o--o--o <-- origin
o--o--o <-- mywork

Some more interesting work has been done in the upstream project, and “origin” has advanced:

某些有趣的工作已經在專案的上游版本庫中完成了,實際上 “origin” 已經向前邁進了:


 o--o--O--o--o--o <-- origin
a--b--c <-- mywork

At this point, you could use “pull” to merge your changes back in; the result would create a new merge commit, like this:

此時,你可以用 “拉入” 來合併這些後臺的變更,這會導致建立一個合併的交付,類似:


 o--o--O--o--o--o <-- origin
/        /
a--b--c--m <-- mywork

if you prefer to keep the history in mywork a simple series of commits
without any merges, you may instead choose to use git-rebase(1):

不過,如果你希望保持 mywork 中的簡單交付佇列而不進行合併的話,git-rebase(1)就是替代方案:


$ git checkout mywork
$ git rebase origin

will remove each of your commits from mywork, temporarily saving them
as patches (in a directory named “.git/rebase-apply”), update mywork to
point at the latest version of origin, then apply each of the saved
patches to the new mywork. The result will look like:

這將從 mywork 中刪除你的每個工作,臨時儲存為補丁集(在目錄 “.git/rebase-apply”中),將 mywork 指向 origin 的最新版本,再將剛才儲存過的補丁應用到 mywork 中。結果大致像下面這個樣子:


 o--o--O--o--o--o <-- origin
a'--b'--c' <-- mywork

the process, it may discover conflicts. In that case it will stop and
allow you to fix the conflicts; after fixing conflicts, use “git-add”
to update the index with those contents, and then, instead of running
git-commit, just run

在此過程中,它也許會發現衝突。並停下來讓你解決衝突;之後用 “git-add” 重新整理索引,但是不要執行 git-commit,而應該執行


$ git rebase --continue

and git will continue applying the rest of the patches.

這樣 git 會繼續應用補丁集中餘下的補丁。

any point you may use the —abort option to abort this process and
return mywork to the state it had before you started the rebase:

你任何時候都可以用 –abort 選項終止重新定位的過程,而讓 mywork 回到你開始執行 rebase 之前的狀態:


$ git rebase --abort


Rewriting a single commit 重寫單個交付

We saw in the section called “Fixing a mistake by rewriting history” that you can replace the most recent commit using

我們已經在 “通過重寫歷史修復失誤” 一節中學習過你是怎麼樣通過下面的命令來覆蓋當前的交付的


$ git commit --amend

will replace the old commit by a new commit incorporating your changes,
giving you a chance to edit the old commit message first.


can also use a combination of this and git-rebase(1) to replace a
commit further back in your history and recreate the intervening
changes on top of it. First, tag the problematic commit with

你同樣可以將這個方法結合 git-rebase(1) 來覆蓋在你的歷史程序中更久遠的交付,並在它的上面再生成一個調解性的變更。首先我們將有問題的交付打個標籤


$ git tag bad mywork~5

(Either gitk or git-log may be useful for finding the commit.)

(輸入 gitk 或者是 git-log 對查詢這個交付很有幫助)

check out that commit, edit it, and rebase the rest of the series on
top of it (note that we could check out the commit on a temporary
branch, but instead we’re using a detached head):



$ git checkout bad
$ # make changes here and update the index
$ git commit --amend
$ git rebase --onto HEAD bad mywork

you’re done, you’ll be left with mywork checked out, with the top
patches on mywork reapplied on top of your modified commit. You can
then clean up with

當你完成了之後,你可以離開,並將 mywork 提取出來,此時你所處的位置已經是在那個修改過的交付上再打了所有補丁的歷史程序的頂端。那樣你就可以進行清理工作了


$ git tag -d bad

that the immutable nature of git history means that you haven’t really
“modified” existing commits; instead, you have replaced the old commits
with new commits having new object names.

注意 git 的歷史不可變更的屬性意味著你不可以修改現存的交付;你要用一個具有新的物件名稱的交付去覆蓋舊的交付。


Reordering or selecting from a patch series 在補丁串中選取與重新排序

Given one
existing commit, the git-cherry-pick(1) command allows you to apply the
change introduced by that commit and create a new commit that records
it. So, for example, if “mywork” points to a series of patches on top
of “origin”, you might do something like:

給定一個現存的交付,git-cherry-pick(1) 命令可以讓你建立一個新交付對那個現存交付所包含的變更進行重新應用和排序。舉例,如果 “mywork” 是指向以 “origin” 為起點的補丁串的指標,你可以做類似以下的工作:


$ git checkout -b mywork-new origin
$ gitk origin..mywork &

browse through the list of patches in the mywork branch using gitk,
applying them (possibly in a different order) to mywork-new using
cherry-pick, and possibly modifying them as you go using commit —amend.
The git-gui(1) command may also help as it allows you to individually
select diff hunks for inclusion in the index (by right-clicking on the
diff hunk and choosing “Stage Hunk for Commit”).

可以用 gitk 來瀏覽在 mywork 中的補丁串列表,並用 cherry-pick 將他們重新應用到新的分支 mywork-new
上去(可能使用不同的順序),並且可以用 commit –amend 對它們進行重新修訂。git-gui(1)
命令可以幫助你獨立地選取不同的差異塊,包括在索引中的(滑鼠右擊該差異塊並點 “Stage Hunk for Commit”)。

Another technique is to use git-format-patch to create a series of patches, then reset the state to before the patches:

另外一個技巧就是用 git-formate-patch 建立一個補丁串,之後回到這些補丁形成之前的狀態:


$ git format-patch origin
$ git reset --hard origin

Then modify, reorder, or eliminate patches as preferred before applying them again with git-am(1).

接著編輯,重排順序,或者剔除補丁,如前面介紹過那樣用 git-am 將他們重新應用一次。


Other tools 第三方工具

There are
numerous other tools, such as StGIT, which exist for the purpose of
maintaining a patch series. These are outside of the scope of this

有許多的第三方工具,譬如 StGIT,專門用於補丁串的維護的。不過這已經超出了本文的覆蓋範圍。


Problems with rewriting history 重寫歷史帶來的問題

primary problem with rewriting the history of a branch has to do with
merging. Suppose somebody fetches your branch and merges it into their
branch, with a result something like this:



 o--o--O--o--o--o <-- origin
/        /
t--t--t--m <-- their branch:

Then suppose you modify the last three commits:



         o--o--o <-- new head of origin
o--o--O--o--o--o <-- old head of origin

If we examined all this history together in one repository, it will look like:



         o--o--o <-- new head of origin
o--o--O--o--o--o <-- old head of origin
/        /
t--t--t--m <-- their branch:

has no way of knowing that the new head is an updated version of the
old head; it treats this situation exactly the same as it would if two
developers had independently done the work on the old and new heads in
parallel. At this point, if someone attempts to merge the new head in
to their branch, git will attempt to merge together the two (old and
new) lines of development, instead of trying to replace the old by the
new. The results are likely to be unexpected.

中, git 將試圖將這兩個開發線路都合並進去(新的和老的),而不是用新分支去覆蓋老的分支。這樣一來就會出現可不預測的結果了。

may still choose to publish branches whose history is rewritten, and it
may be useful for others to be able to fetch those branches in order to
examine or test them, but they should not attempt to pull such branches
into their own work.


For true distributed development that supports proper merging, published branches should never be rewritten.



Why bisecting merge commits can be harder than bisecting linear history 為何定位合併交付中的問題要比線上性歷史中困難

git-bisect(1) command correctly handles history that includes merge
commits. However, when the commit that it finds is a merge commit, the
user may need to work harder than usual to figure out why that commit
introduced a problem.

git-bisect(1) 命令可以正確地處理包含合併交付的歷史程序。不過,當一個合併交付在切分過程中出現的時候,使用者要推理出合併交付為何帶來了問題要比正常的情況下困難。

Imagine this history:



/                       /

that on the upper line of development, the meaning of one of the
functions that exists at Z is changed at commit X. The commits from Z
leading to A change both the function’s implementation and all calling
sites that exist at Z, as well as new calling sites they add, to be
consistent. There is no bug at A.

假設上面的那條開發線路,某個函式在 Z 位置出現並在 X 處被修改過。交付從 Z 發展到 A 的變更中其實是函式的不同的實現,並且函式的呼叫已經在 Z 位置就開始出現了,以及新的函式呼叫還會被加入,並且呼叫會持續地被加入。在 A 處函式已經沒有漏洞了。

that in the meantime on the lower line of development somebody adds a
new calling site for that function at commit Y. The commits from Z
leading to B all assume the old semantics of that function and the
callers and the callee are consistent with each other. There is no bug
at B, either.

再來設想下面的那條開發路線,某人在 Y 處加入了對那個函式新的呼叫。從 Z 到 B 的所有交付中,無論是函式的呼叫者還是被呼叫者,都會假設函式是舊的語義形式,並且它們每個都會持續使用這樣的形式。專案發展到 B 處就沒有漏洞了。

Suppose further that the two development lines merge cleanly at C, so no conflict resolution is required.

假設兩條開發線路發展到 C 處進行了乾淨的合併,也就是說沒有衝突需要處理。

the code at C is broken, because the callers added on the lower line of
development have not been converted to the new semantics introduced on
the upper line of development. So if all you know is that D is bad,
that Z is good, and that git-bisect(1) identifies C as the culprit, how
will you figure out that the problem is due to this change in

過程式碼在 C 出就崩潰了,原因是下面的開發線路中的函式呼叫並沒有針對在上面的研發線路中的函式的新的語義作出相應的調整。那麼假設你在 D
處知道程式出事了,而且 Z 處是穩定的。 git-bisect(1) 將 C

the result of a git-bisect is a non-merge commit, you should normally
be able to discover the problem by examining just that commit.
Developers can make this easy by breaking their changes into small
self-contained commits. That won’t help in the case above, however,
because the problem isn’t obvious from examination of any single
commit; instead, a global view of the development is required. To make
matters worse, the change in semantics in the problematic function may
be just one small part of the changes in the upper line of development.


the other hand, if instead of merging at C you had rebased the history
between Z to B on top of A, you would have gotten this linear history:

另一個方面,要是你不在 C 處進行合併操作,而是將你從 Z 到 B 的發展歷史重新定位到 A 點這個歷史頂端的話,你很容易就可以在一個線性歷史中發現問題。



Bisecting between Z and D* would hit a single culprit commit Y*, and understanding why Y* was broken would probably be easier.

切分 Z 到 D* 的歷史,馬上就可以捕捉到 Y* 是肇事者,並且很容易就理解了 Y* 為什麼會崩潰。

for this reason, many experienced git users, even when working on an
otherwise merge-heavy project, keep the history linear by rebasing
against the latest upstream version before publishing.

基於這樣的理由,許多經驗豐富的 git使用者,即使他們工作在一個需要大規模合併的專案中時,在他們向上遊版本釋出工作的之前,總是用重定位來保持歷史的線性發展。


Chapter 6. Advanced branch management 第六章. 高階分支管理


Fetching individual branches

of using git-remote(1), you can also choose just to update one branch
at a time, and to store it locally under an arbitrary name:

作為 git-remote 命令的替代品,你聽樣可以選擇一次過更新一個分支,並將它儲存為一個任意名稱的本地分支:


$ git fetch origin todo:my-todo-work

first argument, “origin”, just tells git to fetch from the repository
you originally cloned from. The second argument tells git to fetch the
branch named “todo” from the remote repository, and to store it locally
under the name refs/heads/my-todo-work.

第一個引數 “origin”,只是告訴 git 從你的版本庫的克隆源頭上抓取東西。第二個引數告訴 git 抓取遠端分版本庫中的 “todo” 分支,並儲存為 refs/heads/my-todo-work 本地分支。

You can also fetch branches from other repositories; so



$ git fetch git:// master:example-master

create a new branch named “example-master” and store in it the branch
named “master” from the repository at the given URL. If you already
have a branch named example-master, it will attempt to fast-forward to
the commit given by’s master branch. In more detail:

將建立一個新的分支名叫 “example-master” 並將指定的 URL 遠端版本庫中的 “master”
分支儲存在這個分支中。如果你已經有一個 example-master 的分支,它將嘗試快針對 上的 master


git fetch and fast-forwards 抓取與快進

In the
previous example, when updating an existing branch, “git-fetch” checks
to make sure that the most recent commit on the remote branch is a
descendant of the most recent commit on your copy of the branch before
updating your copy of the branch to point at the new commit. Git calls
this process a fast forward.

在前面的範例中,當要更新的分支已經存在時, “git-fetch” 將檢驗並確認遠端分支上最新的交付是你打算更新的這個分支的最新交付的後裔,並將這個本地分支的頭指向拷貝下來的最新交付。Git 叫這個處理過程叫快進。


A fast forward looks something like this:
o--o--o--o <-- old head of the branch
o--o--o <-- new head of the branch

some cases it is possible that the new head will not actually be a
descendant of the old head. For example, the developer may have
realized she made a serious mistake, and decided to backtrack,
resulting in a situation like:



 o--o--o--o--a--b <-- old head of the branch
o--o--o <-- new head of the branch

In this case, “git-fetch” will fail, and print out a warning.

這樣的話,”git-fetch” 將失敗,並列印警告資訊。

that case, you can still force git to update to the new head, as
described in the following section. However, note that in the situation
above this may mean losing the commits labeled “a” and “b”, unless
you’ve already created a reference of your own pointing to them.

在這種情況下,你仍然可以強制 git 更新到新的頭,我們將在接著的章節說明。不過需要注意在上面的情況中,你將丟失交付 “a” 和 “b”,除非你已經建立了自己的引用指向他們。


Configuring remote branches 配置遠端分支

We saw
above that “origin” is just a shortcut to refer to the repository that
you originally cloned from. This information is stored in git
configuration variables, which you can see using git-config(1):

我們做前面已經知道 “origin” 是指向你的版本庫的克隆源頭的引用。這些配置資訊儲存做 git 的配置變數中,我們可以用 git-config(1) 來檢視。


$ git config -l
remote.origin.fetch= refs/heads/*:refs/remotes/origin/*

there are other repositories that you also use frequently, you can
create similar configuration options to save typing; for example, after



$ git config remote.example.url git://

then the following two commands will do the same thing:



$ git fetch git:// master:refs/remotes/example/master
$ git fetch example master:refs/remotes/example/master

Even better, if you add one more option:



$ git config remote.example.fetch master:refs/remotes/example/master

then the following commands will all do the same thing:



$ git fetch git:// master:refs/remotes/example/master
$ git fetch example master:refs/remotes/example/master
$ git fetch example

You can also add a ” ” to force the update each time:

你還可以加入一個 ” ” 號,讓每次都進行強制更新:


$ git config remote.example.fetch  master:ref/remotes/example/master

Don’t do this unless you’re sure you won’t mind “git fetch” possibly throwing away commits on example/master.

不過,除非你確定你自己並不在乎 “git fetch” 可能會在 example/master 中拋棄任何交付,否則不要這樣做。

note that all of the above configuration can be performed by directly
editing the file .git/config instead of using git-config(1).

所有上面的配置項你都可以直接編輯 .git/config 檔案來實現,而不用 git-config(1)命令。

See git-config(1) for more details on the configuration options mentioned above.

詳情請參考 git-config(1) 中的有關上述配置項的介紹。


Chapter 7. Git concepts 第七章. Git 概念

Git is
built on a small number of simple but powerful ideas. While it is
possible to get things done without understanding them, you will find
git much more intuitive if you do.

Git 是以精簡強大這樣的指導思想來開發的。即使你還沒有完全理解它,但是你仍然可以發現 git 是很直觀的。

We start with the most important, the object database and the index.



The Object Database 物件資料庫

already saw in the section called “Understanding History: Commits” that
all commits are stored under a 40-digit “object name”. In fact, all the
information needed to represent the history of a project is stored in
objects with such names. In each case the name is calculated by taking
the SHA1 hash of the contents of the object. The SHA1 hash is a
cryptographic hash function. What that means to us is that it is
impossible to find two different objects with the same name. This has a
number of advantages; among others:

們已經做 “理解歷史:交付” 一節中看到,所有的交付都以一個 40
個十六進位制字元組成的名字作為物件名來儲存。事實上,所有需要展現專案歷史的物件都是以這樣的名字來儲存的。這個名字是根據物件中的內容用 SHA1
雜湊演算法計算出來的。SHA1 是一個加密雜湊函式。這意味著我們不可能用同一名字找到兩個不同的物件,這有很多的好處;其中:

  • Git can quickly determine whether two objects are identical or not, just by comparing names.
  • Since
    object names are computed the same way in every repository, the same
    content stored in two repositories will always be stored under the same
  • Git can detect errors when it reads an object, by checking that the object’s name is still the SHA1 hash of its contents.
  • Git 可以僅僅通過比較他們的名字就能快速地測定兩個物件是否相同。
  • 只要計算物件名的演算法在每個版本庫中都是一樣的,那麼在不同的版本庫中,相同的內容他們的名字是一樣的。
  • Git 在讀取一個物件的時候,可以通過檢查物件名是否符合物件內容的 SHA1 雜湊特徵值的方式來測定錯誤。

(See the section called “Object storage format” for the details of the object formatting and SHA1 calculation.)

(參考 “物件的儲存格式” 一節,有物件的格式和 SHA1 演算法的詳細介紹)

There are four different types of objects: “blob”, “tree”, “commit”, and “tag”.

有四種不同的物件形式: “片”,“樹”,“交付”,和“標籤”。

  • A “blob” object is used to store file data.
  • A
    “tree” object ties one or more “blob” objects into a directory
    structure. In addition, a tree object can refer to other tree objects,
    thus creating a directory hierarchy.
  • A
    “commit” object ties such directory hierarchies together into a
    directed acyclic graph of revisions—each commit contains the object
    name of exactly one tree designating the directory hierarchy at the
    time of the commit. In addition, a commit refers to “parent” commit
    objects that describe the history of how we arrived at that directory
  • A “tag”
    object symbolically identifies and can be used to sign other objects.
    It contains the object name and type of another object, a symbolic name
    (of course!) and, optionally, a signature.
  • “片” 物件是儲存檔案的資料的。
  • “樹” 物件將 “片” 物件以目錄結構的形式繫結起來,還有,樹物件可以引用到其他的樹物件,從而形成了目錄層次關係。
  • “交付” 物件將每次所提交的修訂中所包含的物件名,以一個定向擴充套件的檢視為目錄結構形式繫結起來,這個樹形結構準確地構建出每次提交的目錄層次。另外,一個交付還指向他的父交付,這就是我們所看到的歷史發展的層次關係。
  • “標籤” 物件用於標記其他的物件,它包含該物件的名稱,型別,以及這個標記自己的名稱,可選的還有簽名。

The object types in some more detail:



Commit Object 交付物件

“commit” object links a physical state of a tree with a description of
how we got there and why. Use the —pretty=raw option to git-show(1) or
git-log(1) to examine your favorite commit:

“交付” 物件連線某個樹物件的物理狀態,並附帶有如何與為什麼到達該處的說明。在 git-show(1) 中使用 –pretty=raw 選項,或 git-log(1) 檢查你需要的交付:


$ git show -s --pretty=raw 2be7fcb476
commit 2be7fcb4764f2dbcee52635b91fedb1b3dcf7ab4
tree fb3a8bdd0ceddd019615af4d57a53f43d8cee2bf
parent 257a84d9d02e90447b149af58b271c19405edb6a
author Dave Watson <[email protected]> 1187576872 -0400
committer Junio C Hamano <[email protected]> 1187591163 -0700
Fix misspelling of 'suppress' in docs
Signed-off-by: Junio C Hamano <[email protected]>

As you can see, a commit is defined by:


  • a tree: The SHA1 name of a tree object (as defined below), representing the contents of a directory at a certain point in time.
  • parent(s):
    The SHA1 name of some number of commits which represent the immediately
    previous step(s) in the history of the project. The example above has
    one parent; merge commits may have more than one. A commit with no
    parents is called a “root” commit, and represents the initial revision
    of a project. Each project must have at least one root. A project can
    also have multiple roots, though that isn’t common (or necessarily a
    good idea).
  • an author: The name of the person responsible for this change, together with its date.
  • a
    committer: The name of the person who actually created the commit, with
    the date it was done. This may be different from the author, for
    example, if the author was someone who wrote a patch and emailed it to
    the person who used it to create the commit.
  • a comment describing this commit.
  • 一個樹物件: 一個樹物件的 SHA1 名稱 (在交付物件名的下面),他表達的是一個確定的時間點上的目錄內容。

  • 交付: 一個或多個交付的 SHA1
  • 作者: 表明是誰在什麼時間建立的這個交付,不過可能存在不止一個作者的情況,譬如,某甲寫了一個補丁用電子郵件發給了某乙,某乙用它來建立了這個交付。
  • 交付的註釋。

that a commit does not itself contain any information about what
actually changed; all changes are calculated by comparing the contents
of the tree referred to by this commit with the trees associated with
its parents. In particular, git does not attempt to record file renames
explicitly, though it can identify cases where the existence of the
same file data at changing paths suggests a rename. (See, for example,
the -M option to git-diff(1)).

實上,git 不會檢視真正地紀錄檔案的重新命名,儘管它可以將現存的檔案資料的路徑變更看成是檔案的重新命名。(參考例子, 在 git-diff(1)
中使用 -M 選項)。

commit is usually created by git-commit(1), which creates a commit
whose parent is normally the current HEAD, and whose tree is taken from
the content currently stored in the index.

一個交付正常是由 git-commit(1) 建立,通常情況下以當前的 HEAD 作為它的父交付,他所引用的樹物件則來自當前索引中所儲存的內容。


Tree Object 樹物件

The ever-versatile git-show(1) command can also be used to examine tree objects, but git-ls-tree(1) will give you more details:

git-show(1) 命令是個多面手,它同樣可以用來檢驗樹物件,不過 git-ls-tree(1) 會給出更多的細節:


$ git ls-tree fb3a8bdd0ce
100644 blob 63c918c667fa005ff12ad89437f2fdc80926e21c    .gitignore
100644 blob 5529b198e8d14decbe4ad99db3f7fb632de0439d    .mailmap
100644 blob 6ff87c4664981e4397625791c8ea3bbb5f2279a3    COPYING
040000 tree 2fb783e477100ce076f6bf57e4a6f026013dc745    Documentation
100755 blob 3c0032cec592a765692234f1cba47dfdcc3a9200    GIT-VERSION-GEN
100644 blob 289b046a443c0647624607d471289b2c7dcd470b    INSTALL
100644 blob 4eb463797adc693dc168b926b6932ff53f17d0b1    Makefile
100644 blob 548142c327a6790ff8821d67c2ee1eff7a656b52    README

you can see, a tree object contains a list of entries, each with a
mode, object type, SHA1 name, and name, sorted by name. It represents
the contents of a single directory tree.


object type may be a blob, representing the contents of a file, or
another tree, representing the contents of a subdirectory. Since trees
and blobs, like all other objects, are named by the SHA1 hash of their
contents, two trees have the same SHA1 name if and only if their
contents (including, recursively, the contents of all subdirectories)
are identical. This allows git to quickly determine the differences
between two related tree objects, since it can ignore any entries with
identical object names.

象的型別可能是一個片物件,表示一個檔案的內容,或者是一個樹物件,表示一個子目錄。即使是樹物件與片物件,也和其他的物件一樣,也是用 它們的內容的
SHA1 雜湊特徵值來命名,當且僅當它們內容是一樣(包括所有的子目錄的遞迴內容)的時候兩個樹物件的名字是一樣的。這就可以令 git

(Note: in the presence of submodules, trees may also have commits as entries. See Chapter 8, Submodules for documentation.)

(注意: 當出現子模組時,樹物件可能還包含交付物件的條目。參考 第八章,文件子模組)

Note that the files all have mode 644 or 755: git actually only pays attention to the executable bit.

注意當檔案有 644 或 755 模式時: git 才會真正地將該檔案作為可執行的二進位制檔案來對待。


Blob Object 片物件

You can
use git-show(1) to examine the contents of a blob; take, for example,
the blob in the entry for “COPYING” from the tree above:

你可以用 git-show(1) 來檢驗片物件的內容;譬如,上面的樹物件中的 “COPYING” 條目。


$ git show 6ff87c4664
Note that the only valid version of the GPL as far as this project
is concerned is _this_ particular version of the license (ie v2, not
v2.2 or v3.x or whatever), unless explicitly otherwise stated.

A “blob” object is nothing but a binary blob of data. It doesn’t refer to anything else or have attributes of any kind.

一個 “片”物件除了一個二進位制的資料片之外沒有任何東西。它不引用任何東西,也沒有任何型別屬性。

the blob is entirely defined by its data, if two files in a directory
tree (or in multiple different versions of the repository) have the
same contents, they will share the same blob object. The object is
totally independent of its location in the directory tree, and renaming
a file does not change the object that file is associated with.


that any tree or blob object can be examined using git-show(1) with the
<revision>:<path> syntax. This can sometimes be useful for
browsing the contents of a tree that is not currently checked out.

注意任何樹和片都可以用 git-show(1) 命令帶上 <revision>:<path> 語法進行檢驗。這個方法有時對於瀏覽並不是當前提取的樹物件的內容非常有用。


Trust 信賴

If you
receive the SHA1 name of a blob from one source, and its contents from
another (possibly untrusted) source, you can still trust that those
contents are correct as long as the SHA1 name agrees. This is because
the SHA1 is designed so that it is infeasible to find different
contents that produce the same hash.

如果你從一個來源收到一個片物件的名稱,並且從另一個來源收到它的內容(可能是不被信賴的),你仍然可以信賴它的內容,只要它能通過 SHA1 名稱的許可。這是因為 SHA1 演算法就設計成對於不同的內容不可能產生相同的雜湊特徵值。

you need only trust the SHA1 name of a top-level tree object to trust
the contents of the entire directory that it refers to, and if you
receive the SHA1 name of a commit from a trusted source, then you can
easily verify the entire history of commits reachable through parents
of that commit, and all of those contents of the trees referred to by
those commits.

似地,你應該以只信賴頂層樹物件的 SHA1 名稱的方式來信賴它所指向的整個目錄的內容,並且如果你收到從一個可信的來源得到的交付物件的 SHA1

to introduce some real trust in the system, the only thing you need to
do is to digitally sign just one special note, which includes the name
of a top-level commit. Your digital signature shows others that you
trust that commit, and the immutability of the history of commits tells
others that they can trust the whole history.


other words, you can easily validate a whole archive by just sending
out a single email that tells the people the name (SHA1 hash) of the
top commit, and digitally sign that email using something like GPG/PGP.

換言之,你可以容易地確認整個檔案是有效的,只需要發出一個頂層交付的名稱(SHA1 雜湊特徵值)給其他的人,並且以 GPG/PGP 等加密郵件。

To assist in this, git also provides the tag object…

作為這個機制的輔佐,git 還提供了標籤物件…


Tag Object 標籤物件

A tag
object contains an object, object type, tag name, the name of the
person (“tagger”) who created the tag, and a message, which may contain
a signature, as can be seen using git-cat-file(1):

一個標籤物件包含一個物件,物件型別,標籤名稱,標籤作者(“標記者”)的名稱,以及一個註釋,可能還包含一個密匙,這個密匙可以用 git-cat-file(1) 來檢視。


$ git cat-file tag v1.5.0
object 437b1b20df4b356c9342dac8d38849f24ef44f27
type commit
tag v1.5.0
tagger Junio C Hamano <[email protected]> 1171411200  0000
GIT 1.5.0
Version: GnuPG v1.4.6 (GNU/Linux)
=2E 0

the git-tag(1) command to learn how to create and verify tag objects.
(Note that git-tag(1) can also be used to create “lightweight tags”,
which are not tag objects at all, but just simple references whose
names begin with “refs/tags/”).

參考 git-tag(1) 命令學習如何建立一個核實標籤物件。(注意,git-tag(1) 同樣可以用來建立一個 “輕量標籤”,其實它並不是一個真實的標籤物件,只是在 “refs/tags/” 中的簡單引用)。


How git stores objects efficiently: pack files | git 如何高效地儲存物件: 打包檔案

Newly created objects are initially created in a file named after the object’s SHA1 hash (stored in .git/objects).

建立一個新的物件就是以一個雜湊特徵值作為檔名建立一個新的檔案(儲存在 .git/objects 中)。

Unfortunately this system becomes inefficient once a project has a lot of objects. Try this on an old project:



$ git count-objects
6930 objects, 47620 kilobytes

first number is the number of objects which are kept in individual
files. The second is the amount of space taken up by those “loose”

第一個數字是有多少個獨立的檔案被儲存了。第二個數字是那些 “鬆散” 物件所佔的磁碟空間。

can save space and make git faster by moving these loose objects in to
a “pack file”, which stores a group of objects in an efficient
compressed format; the details of how pack files are formatted can be
found in technical/pack-format.txt.

你可以通過將那些鬆散物件打包到 “包檔案” 的方式來節省磁碟空間和提高 git 的速度,這種發放是以高效壓縮的方式將物件分組儲存;關於包檔案的格式可以在 techincal/pack-formate.txt 檔案中找到。

To put the loose objects into a pack, just run git repack:

要將鬆散物件打包,只需要執行 git repack:


$ git repack
Generating pack...
Done counting 6020 objects.
Deltifying 6020 objects.
100% (6020/6020) done
Writing 6020 objects.
100% (6020/6020) done
Total 6020, written 6020 (delta 4070), reused 0 (delta 0)
Pack pack-3e54ad29d5b2e05838c75df582c65257b8d08e1c created.

You can then run



$ git prune

remove any of the “loose” objects that are now contained in the pack.
This will also remove any unreferenced objects (which may be created
when, for example, you use “git-reset” to remove a commit). You can
verify that the loose objects are gone by looking at the .git/objects
directory or by running

就將已經打包的鬆散物件刪除。這將會刪除掉那些未被引用的物件(譬如,它們可能是由於你執行 “git-reset” 時遺棄了某些交付而產生的)。你可以通過檢視 .git/objects 或者執行下面的命令來檢視鬆散物件是否已經被清除了。


$ git count-objects
0 objects, 0 kilobytes

Although the object files are gone, any commands that refer to those objects will work exactly as they did before.


The git-gc(1) command performs packing, pruning, and more for you, so is normally the only high-level command you need.

git-gc(1) 命令一次完成你上面的打包,剪除工作。它只是提供方便的高層命令。


Dangling objects 懸空物件

The git-fsck(1) command will sometimes complain about dangling objects. They are not a problem.

有時 git-fsck(1) 命令會因為懸空物件帶來點煩惱。不過這個不是一個問題。

most common cause of dangling objects is that you’ve rebased a branch,
or you have pulled from somebody else who rebased a branch—see Chapter
5, Rewriting history and maintaining patch series. In that case, the
old head of the original branch still exists, as does everything it
pointed to. The branch pointer itself just doesn’t, since you replaced
it with another one.


are also other situations that cause dangling objects. For example, a
“dangling blob” may arise because you did a “git-add” of a file, but
then, before you actually committed it and made it part of the bigger
picture, you changed something else in that file and committed that
updated thing—the old state that you added originally ends up not being
pointed to by any commit or tree, so it’s now a dangling blob object.

那些更新了的內容 – 你在最後加入的狀態就已經沒有任何交付和樹物件指向它了,所以它現在成了懸空的片物件。

when the “recursive” merge strategy runs, and finds that there are
criss-cross merges and thus more than one merge base (which is fairly
unusual, but it does happen), it will generate one temporary midway
tree (or possibly even more, if you had lots of criss-crossing merges
and more than two merge bases) as a temporary internal merge base, and
again, those are real objects, but the end result will not end up
pointing to them, so they end up “dangling” in your repository.

似地,要是在 “遞迴”

dangling objects aren’t anything to worry about. They can even be very
useful: if you screw something up, the dangling objects can be how you
recover your old tree (say, you did a rebase, and realized that you
really didn’t want to—you can look at what dangling objects you have,
and decide to reset your head to some old dangling state).


For commits, you can just use:



$ gitk <dangling-commit-sha-goes-here> --not --all

asks for all the history reachable from the given commit but not from
any branch, tag, or other reference. If you decide it’s something you
want, you can always create a new reference to it, e.g.,



$ git branch recovered-branch <dangling-commit-sha-goes-here>

For blobs and trees, you can’t do the same, but you can still examine them. You can just do



$ git show <dangling-blob/tree-sha-goes-here>

show what the contents of the blob were (or, for a tree, basically what
the “ls” for that directory was), and that may give you some idea of
what the operation was that left that dangling object.

這樣可以看到片物件中的內容(或者,對於樹物件,就像對一個目錄執行 ‘ls’ 命令一樣),通過這樣也許會提示到你在懸空物件中留下了什麼東西。

dangling blobs and trees aren’t very interesting. They’re almost always
the result of either being a half-way mergebase (the blob will often
even have the conflict markers from a merge in it, if you have had
conflicting merges that you fixed up by hand), or simply because you
interrupted a “git-fetch” with ^C or something like that, leaving some
of the new objects in the object database, but just dangling and

話,並且你要手動處理它),或者是你在執行 “git-fetch” 命令的時候按了 Ctrl C

Anyway, once you are sure that you’re not interested in any dangling state, you can just prune all unreachable objects:



$ git prune

they’ll be gone. But you should only run “git prune” on a quiescent
repository—it’s kind of like doing a filesystem fsck recovery: you
don’t want to do that while the filesystem is mounted.

於是懸空物件就被清除了,不過你之應該在一個沒有執行其他操作的版本庫中執行 “git prune”,這個就類似用 fsck 來進行檔案系統的修復一樣:你不應該在一個已經掛載了的檔案系統中執行 fsck 。

(The same is true of “git-fsck” itself, btw, but since git-fsck never actually changes
the repository, it just reports on what it found, git-fsck itself is
never “dangerous” to run. Running it while somebody is actually
changing the repository can cause confusing and scary messages, but it
won’t actually do anything bad. In contrast, running “git prune” while
somebody is actively changing the repository is a BAD

(”git-fsck”命令本身是可以信賴的,事實上 git-fsck 並不會改變

本庫的任何東西,它僅僅會報告它檢查到了什麼,執行 git-fsck
版本庫的時候,你同時執行 “git prune” 那就是


Recovering from repository corruption 從損壞中恢復

design, git treats data trusted to it with caution. However, even in
the absence of bugs in git itself, it is still possible that hardware
or operating system errors could corrupt data.

就設計而言,git 對待資料是很謹慎小心的。不過,git 自身的漏洞,硬體和作業系統的錯誤等都可能造成資料的損壞。

first defense against such problems is backups. You can back up a git
directory using clone, or just using cp, tar, or any other backup

首要的預防措施就是備份,你可以通過克隆來備份 git 的目錄,或者是用 cp, tar, 等任何可能的備份方法。

a last resort, you can search for the corrupted objects and attempt to
replace them by hand. Back up your repository before attempting this in
case you corrupt things even more in the process.


assume that the problem is a single missing or corrupted blob, which is
sometimes a solvable problem. (Recovering missing trees and especially
commits is much harder).


Before starting, verify that there is corruption, and figure out where it is with git-fsck(1); this may be time-consuming.


Assume the output looks like this:



$ git fsck --full
broken link from    tree 2d9263c6d23595e7cb2a21e5ebbb53655278dff8
to    blob 4b9458b3786228369c63936db65827de3cc06200
missing blob 4b9458b3786228369c63936db65827de3cc06200

(Typically there will be some “dangling object” messages too, but they aren’t interesting.)

(典型的情況是還存在一些 “懸空物件” 的資訊,不過這不是我們感興趣的東西。)

you know that blob 4b9458b3 is missing, and that the tree 2d9263c6
points to it. If you could find just one copy of that missing blob
object, possibly in some other repository, you could move it into
.git/objects/4b/9458b3… and be done. Suppose you can’t. You can still
examine the tree that pointed to it with git-ls-tree(1), which might
output something like:

在你知道片 4b9458b3 丟失了,並且樹 2d9263c6
.git/objects/4b/9458b3… 中,這樣就算是修復它了。假如你找不到,你仍然可以用 git-ls-tree


$ git ls-tree 2d9263c6d23595e7cb2a21e5ebbb53655278dff8
100644 blob 8d14531846b95bfa3564b58ccfb7913a034323b8    .gitignore
100644 blob ebf9bf84da0aab5ed944264a5db2a65fe3a3e883    .mailmap
100644 blob ca442d313d86dc67e0a2e5d584b465bd382cbf5c    COPYING
100644 blob 4b9458b3786228369c63936db65827de3cc06200    myfile

now you know that the missing blob was the data for a file named
“myfile”. And chances are you can also identify the directory—let’s say
it’s in “somedirectory”. If you’re lucky the missing copy might be the
same as the copy you have checked out in your working tree at
“somedirectory/myfile”; you can test whether that’s right with

樣你就知道了丟失的片是檔案 “myfile”
“somedirectory/myfile” 的位置上;你可以通過 git-hash-object(1) 命令來確定一下檔案的內容是否正確。


$ git hash-object -w somedirectory/myfile

will create and store a blob object with the contents of
somedirectory/myfile, and output the sha1 of that object. if you’re
extremely lucky it might be 4b9458b3786228369c63936db65827de3cc06200,
in which case you’ve guessed right, and the corruption is fixed!

樣會建立一個新的物件,這個片物件以 somedirectory/myfile 的資料為內容,並且會輸出這個物件的

Otherwise, you need more information. How do you tell which version of the file has been lost?


The easiest way to do this is with:



$ git log --raw --all --full-history -- somedirectory/myfile

Because you’re asking for raw output, you’ll now get something like



commit abc
:100644 100644 4b9458b... newsha... M somedirectory/myfile
commit xyz
:100644 100644 oldsha... 4b9458b... M somedirectory/myfile

tells you that the immediately preceding version of the file was
“newsha”, and that the immediately following version was “oldsha”. You
also know the commit messages that went with the change from oldsha to
4b9458b and with the change from 4b9458b to newsha.

這會告訴你當前處理的檔案版本是 “newsha”,並且緊接著的版本叫 “oldsha”。你還可以看到從 oldsha 到 469458b 以及從 4b9458交付的資訊。

you’ve been committing small enough changes, you may now have a good
shot at reconstructing the contents of the in-between state 4b9458b.

如果你的每個交付都足夠短的話,你現在最好的辦法是重新將檔案的內容彌補至 4b9458b 的狀態。

If you can do that, you can now recreate the missing object with



$ git hash-object -w <recreated-file>

and your repository is good again!


(Btw, you could have ignored the fsck, and started with doing a

(順便說一下,你完全可以跳過 fsck,並且直接用下面的命令開始


$ git log --raw --all

just looked for the sha of the missing object (4b9458b..) in that whole
thing. It’s up to you – git does have a lot of information, it is just
missing one particular blob version.

查詢丟失的物件的 sha 雜湊特徵值。這完全取決於你 - 要是你不在乎僅僅是為了一個丟失的片版本,而檢查 git 生成的一大堆的資訊的話。


The index 索引

The index
is a binary file (generally kept in .git/index) containing a sorted
list of path names, each with permissions and the SHA1 of a blob
object; git-ls-files(1) can show you the contents of the index:

索引是一個二進位制檔案(總是儲存在為 .git/index)儲存著一個有序的檔案路徑名稱列表,以及每個檔案的許可權許可權和每個片物件的 SHA1 雜湊特徵值;git-ls-files 可以向你展示索引中的內容:


$ git ls-files --stage
100644 63c918c667fa005ff12ad89437f2fdc80926e21c 0       .gitignore
100644 5529b198e8d14decbe4ad99db3f7fb632de0439d 0       .mailmap
100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0       COPYING
100644 a37b2152bd26be2c2289e1f57a292534a51a93c7 0       Documentation/.gitignore
100644 fbefe9a45b00a54b58d94d06eca48b03d40a50e0 0       Documentation/Makefile
100644 2511aef8d89ab52be5ec6a5e46236b4b6bcd07ea 0       xdiff/xtypes.h
100644 2ade97b2574a9f77e7ae4002a4e07a6a38e46d07 0       xdiff/xutils.c
100644 d5de8292e05e7c36c4b68857c1cf9855e3d2f70a 0       xdiff/xutils.h

that in older documentation you may see the index called the “current
directory cache” or just the “cache”. It has three important

注意,在舊的的 git 文件中,你可能會看到索引被叫做 “當前目錄快取” 或者只是叫 “cache”。他有三個主要的特性:

  • The index contains all the information necessary to generate a single (uniquely determined) tree object.
  • 索引儲存著所有生成(唯一確定的)樹物件的所有必要資訊。
  • For
    example, running git-commit(1) generates this tree object from the
    index, stores it in the object database, and uses it as the tree object
    associated with the new commit.
  • 例如,執行 git-commit(1) 從索引中產生一個樹物件,將它儲存到物件資料庫中,並且用它來作為樹物件和新的交付的關聯紐帶。
  • The index enables fast comparisons between the tree object it defines and the working tree.
  • 索引可以加快樹物件和工作目錄樹之間的差異比較。
  • It
    does this by storing some additional data for each entry (such as the
    last modified time). This data is not displayed above, and is not
    stored in the created tree object, but it can be used to determine
    quickly which files in the working directory differ from what was
    stored in the index, and thus save git from having to read all of the
    data from such files to look for changes.
  • 它還儲存每個條目的其他的一些附帶資訊(譬如最後的修改日期)。這些資料沒有在上面的輸出中顯示,也不會被儲存到建立的樹物件中,不過它有利於快速比較工作樹和索引中儲存的資料的差異,避免了 git 要查詢變更的時候去讀取每個檔案的內容。
  • It
    can efficiently represent information about merge conflicts between
    different tree objects, allowing each pathname to be associated with
    sufficient information about the trees involved that you can create a
    three-way merge between them.
  • 他可以高效地描述不同的樹物件之間的合併衝突,容許樹物件所涉及的每個檔名都可以關聯到充分的資訊,令你可以以三路合併的方式來合併它們。
  • We
    saw in the section called “Getting conflict-resolution help during a
    merge” that during a merge the index can store multiple versions of a
    single file (called “stages”). The third column in the git-ls-files(1)
    output above is the stage number, and will take on values other than 0
    for files with merge conflicts.
  • 我們看過了 “在合併中取得衝突解決幫助” 一節,我們看到了在合併過程中,索引儲存了一個單一檔案的多個不同版本(稱為 “階段”)。git-ls-files(1) 輸出中的第三列就是階段的號碼,並且當合並出現衝突時會有多於 0 的其他編號。

The index is thus a sort of temporary staging area, which is filled with a tree which you are in the process of working on.


you blow the index away entirely, you generally haven’t lost any
information as long as you have the name of the tree that it described.



Chapter 8. Submodules 子模組

projects are often composed of smaller, self-contained modules. For
example, an embedded Linux distribution’s source tree would include
every piece of software in the distribution with some local
modifications; a movie player might need to build against a specific,
known-working version of a decompression library; several independent
programs might all share the same build scripts.

型專案經常是由更小的,自包含的模組組成。譬如,一個嵌入式 Linux

centralized revision control systems this is often accomplished by
including every module in one single repository. Developers can check
out all modules or only the modules they need to work with. They can
even modify files across several modules in a single commit while
moving things around or updating APIs and translations.

對於中心型的修訂控制系統,經常是將所有的模組都紡織在單個的版本庫中。開發者可以提取所有的模組或者是單個的模組。當事情的變化涉及到 APIs 和翻譯的時候,他們甚至可以在一個交付中包含跨越多個模組的檔案修改。

does not allow partial checkouts, so duplicating this approach in Git
would force developers to keep a local copy of modules they are not
interested in touching. Commits in an enormous checkout would be slower
than you’d expect as Git would have to scan every directory for
changes. If modules have a lot of local history, clones would take

Git 是不允許部分地提取的,這種副本式的實現方式,強制開發者在本地保持即使是他不感興趣的模組的拷貝。巨大的提出操作可能比你預期的還要慢,當 Git 需要掃描每個目錄的變動的時候。要是模組有一個相當長的歷史發展過程的話,克隆操作會將這些歷史始終保持。

the plus side, distributed revision control systems can much better
integrate with external sources. In a centralized model, a single
arbitrary snapshot of the external project is exported from its own
revision control and then imported into the local revision control on a
vendor branch. All the history is hidden. With distributed revision
control you can clone the entire external history and much more easily
follow development and re-merge local changes.


submodule support allows a repository to contain, as a subdirectory, a
checkout of an external project. Submodules maintain their own
identity; the submodule support just stores the submodule repository
location and commit ID, so other developers who clone the containing
project (“superproject”) can easily clone all the submodules at the
same revision. Partial checkouts of the superproject are possible: you
can tell Git to clone none, some or all of the submodules.

ID,那麼那些克隆上一級專案的開發者可以容易地克隆所有的子模組:你可以不必告訴 git 克隆某些或者是全部子模組。

git-submodule(1) command is available since Git 1.5.3. Users with Git
1.5.2 can look up the submodule commits in the repository and manually
check them out; earlier versions won’t recognize the submodules at all.

git-submodule(1)命令從 1.5.3 版本開始支援。對於 Git 1.5.2 的使用者可以手動查詢子模組中的交付和提取它們。更早版本的 git 將無法識別子模組了。

To see how submodule support works, create (for example) four example repositories that can be used later as a submodule:



$ mkdir ~/git
$ cd ~/git
$ for i in a b c d
mkdir $i
cd $i
git init
echo "module $i" > $i.txt
git add $i.txt
git commit -m "Initial commit, submodule $i"
cd ..

Now create the superproject and add all the submodules:



$ mkdir super
$ cd super
$ git init
$ for i in a b c d
git submodule add ~/git/$i $i
  • Note 注意

  • Do not use local URLs here if you plan to publish your superproject!
  • 如果你打算髮布這個上級版本庫就不要用本地路徑作為 URLs 了!

See what files git-submodule created:

該檢視一下 git-submodule 都建立了什麼了:


$ ls -a
.  ..  .git  .gitmodules  a  b  c  d

The git-submodule add <repo> <path> command does a couple of things:

git-submodule add <repo> <path> 命令做了如下的事情:

  • It
    clones the submodule from <repo> to the given <path> under
    the current directory and by default checks out the master branch.

  • It adds the submodule’s clone path to the gitmodules(5) file and adds this file to the index, ready to be committed.
  • It adds the submodule’s current commit ID to the index, ready to be committed.
  • 它從 <repo> 子模組克隆到 <path> 指定的目錄,並預設地提取 master 分支。

  • 它將子模組的克隆路徑加入到 .gitmodules(5) 檔案中,並將該檔案加入索引,也已經提交了該檔案。
  • 它將子模組的當前交付 ID 加入到索引中,並被提交。

Commit the superproject:



$ git commit -m "Add submodules a, b, c and d."

Now clone the superproject:



$ cd ..
$ git clone super cloned
$ cd cloned

The submodule directories are there, but they’re empty:



$ ls -a a
.  ..
$ git submodule status
-d266b9873ad50488163457f025db7cdd9683d88b a
-e81d457da15309b4fef4249aba9b50187999670d b
-c1536a972b9affea0f16e0680ba87332dc059146 c
-d96249ff5d57de5de093e6baff9e0aafa5276a74 d
  • Note 注意

  • The
    commit object names shown above would be different for you, but they
    should match the HEAD commit object names of your repositories. You can
    check it by running git ls-remote ../a.
  • 上面顯示的物件名對於你來說應該是不同的,不過他們應該匹配你的版本庫中的 HEAD 交付的物件名。你可以通過 git ls-remote ../a 命令將他們展示出來。

down the submodules is a two-step process. First run git submodule init
to add the submodule repository URLs to .git/config:

拉入子模組的操作由兩步組成,首先執行 git submodule init 將子模組的版本庫 URLs 加入到 .git/config:


$ git submodule init

Now use git-submodule update to clone the repositories and check out the commits specified in the superproject:

接著執行 git submodule update 克隆並提取交付到特定的上級專案:


$ git submodule update
$ cd a
$ ls -a
.  ..  .git  a.txt

major difference between git-submodule update and git-submodule add is
that git-submodule update checks out a specific commit, rather than the
tip of a branch. It’s like checking out a tag: the head is detached, so
you’re not working on a branch.

git submodule update 和 git submodule add 的主要區別是 git submodule update 提取特定的交付,而不是分支的頂端。它類似提取一個標籤:頭被剝離,如此你並不是工作在分支上。


$ git branch
* (no branch)

you want to make a change within a submodule and you have a detached
head, then you should create or checkout a branch, make your changes,
publish the change within the submodule, and then update the
superproject to reference the new commit:



$ git checkout master




$ git checkout -b fix-up




$ echo "adding a line again" >> a.txt
$ git commit -a -m "Updated the submodule from within the superproject."
$ git push
$ cd ..
$ git diff
diff --git a/a b/a
index d266b98..261dfac 160000
--- a/a
@@ -1  1 @@
-Subproject commit d266b9873ad50488163457f025db7cdd9683d88b
Subproject commit 261dfac35cb99d380eb966e102c1197139f7fa24
$ git add a
$ git commit -m "Updated submodule a."
$ git push

You have to run git submodule update after git pull if you want to update submodules, too.

你必須在執行 git pull 之後執行 git submodule update,如果你也想更新子模組的話。


Pitfalls with submodules 子模組陷阱

publish the submodule change before publishing the change to the
superproject that references it. If you forget to publish the submodule
change, others won’t be able to clone the repository:



$ cd ~/git/super/a
$ echo i added another line to this file >> a.txt
$ git commit -a -m "doing it wrong this time"
$ cd ..
$ git add a
$ git commit -m "Updated submodule a again."
$ git push
$ cd ~/git/cloned
$ git pull
$ git submodule update
error: pathspec '261dfac35cb99d380eb966e102c1197139f7fa24' did not match any file(s) known to git.
Did you forget to 'git add'?
Unable to checkout '261dfac35cb99d380eb966e102c1197139f7fa24' in submodule path 'a'

You also should not rewind branches in a submodule beyond commits that were ever recorded in any superproject.


not safe to run git submodule update if you’ve made and committed
changes within a submodule without checking out a branch first. They
will be silently overwritten:

如果你並不是在首先提取一個分支的情況下執行 git submodule update 是不安全的。他們會被無聲地覆蓋:


$ cat a.txt
module a
$ echo line added from private2 >> a.txt
$ git commit -a -m "line added inside private2"
$ cd ..
$ git submodule update
Submodule path 'a': checked out 'd266b9873ad50488163457f025db7cdd9683d88b'
$ cd a
$ cat a.txt
module a
  • Note 注意

  • The changes are still visible in the submodule’s reflog.
  • 變更仍然在子模組的引用日誌中可見。

This is not the case if you did not commit your changes.



Chapter 9. Low-level git operations 第九章. 底層 git 操作

Many of
the higher-level commands were originally implemented as shell scripts
using a smaller core of low-level git commands. These can still be
useful when doing unusual things with git, or just as a way to
understand its inner workings.

很多的高層的命令通過執行更小的底層 git 命令的 shell 指令碼的方式實現的。底層命令在日常的 git 事務中也是有用的,或者僅是作為理解 git 的內部運作也是很有幫助的。


Object access and manipulation 物件訪問與操作

The git-cat-file(1) command can show the contents of any object, though the higher-level git-show(1) is usually more useful.

git cat-file(1) 命令可以展示任何物件的內容,儘管高層命令 git show(1)通常更有力。

The git-commit-tree(1) command allows constructing commits with arbitrary parents and trees.

git commit-tree(1)命令容許構建任意的交付為父交付的交付以及樹物件。

tree can be created with git-write-tree(1) and its data can be accessed
by git-ls-tree(1). Two trees can be compared with git-diff-tree(1).

一個樹物件可以由 git write-tree(10) 建立並且它的資料可以用 git ls-tree(1)命令訪問。兩個樹可以用 git diff-tree(1)來進行差異比較。

tag is created with git-mktag(1), and the signature can be verified by
git-verify-tag(1), though it is normally simpler to use git-tag(1) for

git mktage(1)命令用於建立標籤,並且用 git verifgy-tage(1)來驗證其加密。不過通常用 git tag(1) 命令來同時完成這兩個步驟。


The Workflow 運作流程

operations such as git-commit(1), git-checkout(1) and git-reset(1) work
by moving data between the working tree, the index, and the object
database. Git provides low-level operations which perform each of these
steps individually.

高層操作,例如 git commit(1), git checkout(1) 以及 git reset(1) 用於在工作樹,索引,與物件資料庫中進行資料轉移,實際上 git 提供的是更細分的底層操作。

all “git” operations work on the index file. Some operations work
purely on the index file (showing the current state of the index), but
most operations move data between the index file and either the
database or the working directory. Thus there are four main

總地來說,所有 “git” 對索引檔案的操作,都是純粹操作索引檔案(展示索引的當前狀態),不過大部分的命令都是在索引檔案,資料庫或者是工作樹之間遷移資料。存在四個主要的組合:


working directory -> index 工作樹 -> 索引

git-update-index(1) command updates the index with information from the
working directory. You generally update the index information by just
specifying the filename you want to update, like so:

git update-index(1) 命令用來自工作樹的資訊重新整理索引。你通常以指定檔名的方式來重新整理索引資訊,例如:


$ git update-index filename

to avoid common mistakes with filename globbing etc, the command will
not normally add totally new entries or remove old entries, i.e. it
will normally just update existing cache entries.


tell git that yes, you really do realize that certain files no longer
exist, or that new files should be added, you should use the —remove
and —add flags respectively.

正確告訴 git 增加或者刪除檔案的方法是,當你的確認為某些現存的檔案已經沒有再存在的必要,或者是需要加入新的檔案的話,你應該分別地使用 –remove 和 –add 引數。

A —remove flag does not mean that subsequent filenames will necessarily
be removed: if the files still exist in your directory structure, the
index will be updated with their new status, not removed. The only
thing —remove means is that update-index will be considering a removed
file to be a valid thing, and if the file really does not exist any
more, it will update the index accordingly.

–remove 引數的唯一意義是表示刪除那個檔案是合法的。並且當那個檔案的確已經不再存在的時候,索引會相應地更新自己的狀態。

a special case, you can also do git update-index —refresh, which will
refresh the “stat” information of each index to match the current stat
information. It will not update the object status itself, and it will
only update the fields that are used to quickly test whether an object
still matches its old backing store object.

作為特例,你可以執行一下 git update-index –refresh,這將會更新每個索引所匹配的當前狀態資訊。它不會更新物件的狀態,並且它只會重新整理那些用於快速檢查一個物件是否仍然匹配它舊的後臺儲存的物件的那些索引域。

The previously introduced git-add(1) is just a wrapper for git-update-index(1).

前面介紹過的 git-add(1) 只是 git-update-index(1) 命令的一個封裝。


index -> object database 索引 -> 物件資料庫

You write your current index file to a “tree” object with the program

你要將當前索引的檔案寫入一個 “樹” 物件用這個命令


$ git write-tree

doesn’t come with any options—it will just write out the current index
into the set of tree objects that describe that state, and it will
return the name of the resulting top-level tree. You can use that tree
to re-generate the index at any time by going in the other direction:



object database -> index 物件資料庫 -> 索引

You read
a “tree” file from the object database, and use that to populate (and
overwrite—don’t do this if your index contains any unsaved state that
you might want to restore later!) your current index. Normal operation
is just

你可以從物件資料庫中讀取一個 “樹” 檔案,並用它來建立你當前的索引(如果你當前的索引中包含你希望儲存但又未儲存的內容的時候,就別這樣做了)。標準的做法就是


$ git read-tree <sha1 of tree>

your index file will now be equivalent to the tree that you saved
earlier. However, that is only your index file: your working directory
contents have not been modified.



index -> working directory 索引 -> 工作目錄

update your working directory from the index by “checking out” files.
This is not a very common operation, since normally you’d just keep
your files updated, and rather than write to your working directory,
you’d tell the index files about the changes in your working directory
(i.e. git-update-index).

從索引中重新整理你的工作目錄是通過 “提取出” 檔案的方法。這是非常不常見的操作,正常來說,你當前所保持的就是檔案的最新狀態的情況多過你把你曾經告訴過索引的檔案更動(譬如,git-update-index),再從索引中寫回到你的工作目錄。

if you decide to jump to a new version, or check out somebody else’s
version, or just restore a previous tree, you’d populate your index
file with read-tree, and then you need to check out the result with

無論如何,如果你打算跳過一個新的版本,或者是提取某人的版本,或者是恢復之前的某個樹,你就需要用 read-tree 來生成索引檔案,並且提取出結果


$ git checkout-index filename

or, if you want to check out all of the index, use -a.

或者,如果你要提取所有的索引的話,用 -a 選項。

git-checkout-index normally refuses to overwrite old files, so if you
have an old version of the tree already checked out, you will need to
use the “-f” flag (before the “-a” flag or the filename) to force the

注意! git-checkout 預設地拒絕覆蓋舊的檔案,如果你的工作目錄中有一個就的樹的版本,你需要用 -f 引數(在引數 -a 或者是要提取的檔名前面)強制提取。

Finally, there are a few odds and ends which are not purely moving from one representation to the other:



Tying it all together 全盤瞭解

To commit
a tree you have instantiated with “git write-tree”, you’d create a
“commit” object that refers to that tree and the history behind it—most
notably the “parent” commits that preceded it in history.

你用 “git write-tree” 提交一個樹物件的例項的同時,你應該也建立了一個 “交付” 物件,它包含那個樹物件的引用,以及這個交付物件本身在歷史中最接近的父交付物件的引用,以此來保持歷史的延續。

a “commit” has one parent: the previous state of the tree before a
certain change was made. However, sometimes it can have two or more
parent commits, in which case we call it a “merge”, due to the fact
that such a commit brings together (“merges”) two or more previous
states represented by other commits.

正常來說,一個 “交付” 會有一個父輩:在本次變動產生之前的樹的狀態。不過,某些時候會有兩個父輩,這種情況稱之為 “合併”,就原理來說,那樣的交付物件整合(合併)了兩個或者是兩個以上的描述之前的歷史狀態的交付物件。

other words, while a “tree” represents a particular directory state of
a working directory, a “commit” represents that state in “time”, and
explains how we got there.

換言之,如果一個 “樹” 是描述工作目錄的具體狀態的話,一個 “交付” 描述的就是 “時間” 狀態,它說明了我們是怎麼到達這裡的。

You create a commit object by giving it the tree that describes the state at the time of the commit, and a list of parents:



$ git commit-tree <tree> -p <parent> [-p <parent2> ..]

then giving the reason for the commit on stdin (either through
redirection from a pipe or file, or by just typing it at the tty).

並且我們可以用 stdin 標準輸入的方式向交付命令提供引數依據(可以通過管道或者是檔案,或者是直接在命令列終端輸入)。

will return the name of the object that represents that commit, and you
should save it away for later use. Normally, you’d commit a new HEAD
state, and while git doesn’t care where you save the note about that
state, in practice we tend to just write the result to the file pointed
at by .git/HEAD, so that we can always see what the last committed
state was.

git-commit-tree 將返回一個描述該交付的物件名,並且你應該記下它以備以後用到(譯註:

層命令 git commit-tree 並不會像 git commit 那樣將這個返回值寫入 .git/HEAD)。正常地,你提交的是一個新的
HEAD 狀態,其實 git 並不在乎你在哪裡儲存這個狀態的記錄,我們之所以關心將這個返回的結果寫入到 .git/HEAD

Here is an ASCII art by Jon Loeliger that illustrates how various pieces fit together.

這裡有一個 Jon Loeliger 用字元畫的圖,將一系列的操作都準確地描述了。


commit obj
|    |
|    |
V    V
| Object DB |
|  Backing  |
|   Store   |
write-tree  |     |
tree obj  |     |
|     |  read-tree
|     |  tree obj
|   Index   |
|  "cache"  |
update-index  ^
blob obj  |     |
|     |
checkout-index -u  |     |  checkout-index
stat      |     |  blob obj
|  Working  |
| Directory |


Examining the data 檢驗資料

You can
examine the data represented in the object database and the index with
various helper tools. For every object, you can use git-cat-file(1) to
examine details about the object:

你可以用一系列的輔助工具來檢驗物件資料庫和索引。對於所有的物件,你可以用 git-cat-file(1) 檢驗物件的細節:


$ git cat-file -t <objectname>

shows the type of the object, and once you have the type (which is usually implicit in where you find the object), you can use



$ git cat-file blob|tree|commit|tag <objectname>

show its contents. NOTE! Trees have binary content, and as a result
there is a special helper for showing that content, called git-ls-tree,
which turns the binary content into a more easily readable form.

對於顯示內容,注意!樹物件有二進位制的內容的,有專門的輔助工具來顯示這些內容,稱做 git-ls-tree,它將二進位制內容轉換為更加可讀的形式。

especially instructive to look at “commit” objects, since those tend to
be small and fairly self-explanatory. In particular, if you follow the
convention of having the top commit name in .git/HEAD, you can do

“交付” 物件的結構獨特,首先它很少,而且很明瞭。事實上,如果你遵守將頂端交付物件名寫入 .git/HEAD 中的約定,你就可以


$ git cat-file commit HEAD

to see what the top commit was.



Merging multiple trees 合併多個樹

Git helps
you do a three-way merge, which you can expand to n-way by repeating
the merge procedure arbitrary times until you finally “commit” the
state. The normal situation is that you’d only do one three-way merge
(two parents), and commit it, but if you like to, you can do multiple
parents in one go.

Git 幫助你完成三路合併,其實你可以展開 N-路 合併進行任意的合併,最終得到你所需要提交的狀態。正常的狀態下,你只需要進行一個 三-路 合併(兩個父輩)並提交結果,不過你願意的話,你可以一次過進行多父輩的合併。

do a three-way merge, you need the two sets of “commit” objects that
you want to merge, use those to find the closest common parent (a third
“commit” object), and then use those commit objects to find the state
of the directory (“tree” object) at these points.


To get the “base” for the merge, you first look up the common parent of two commits with

取得合併 “基點”,你首先需要查詢需要合併的兩個交付的共同的父輩


$ git merge-base <commit1> <commit2>

will return you the commit they are both based on. You should now look
up the “tree” objects of those commits, which you can easily do with
(for example)



$ git cat-file commit <commitname> | head -1

since the tree object information is always the first line in a commit object.


you know the three trees you are going to merge (the one “original”
tree, aka the common tree, and the two “result” trees, aka the branches
you want to merge), you do a “merge” read into the index. This will
complain if it has to throw away your old index contents, so you should
make sure that you’ve committed those—in fact you would normally always
do a merge against your last commit (which should thus match what you
have in your current index anyway).

為這樣會拋棄掉你的舊的索引內容,所以你應該確保你舊的索引狀態已經被提交過 -

To do the merge, do



$ git read-tree -m -u <origtree> <yourtree> <targettree>

will do all trivial merge operations for you directly in the index
file, and you can just write the result out with git write-tree.

它為你直接在索引檔案中執行所有無干涉合併,並且你僅需要將合併的結果用 git write-tree 命令產生一個樹物件。


Merging multiple trees, continued 合併多個樹,續完

many merges aren’t trivial. If there are files that have been added,
moved or removed, or if both branches have modified the same file, you
will be left with an index tree that contains “merge entries” in it.
Such an index tree can NOT be written out to a tree object, and you
will have to resolve any such merge clashes using other tools before
you can write out the result.

不幸的是許多的合併都不是互不干涉的。如果存在檔案被加入,移動或者是刪除,或者是兩個分之都修改過同一個檔案的話,你會在索引中留下包含 “合併條目” 的樹。這是一個不能被寫成樹物件的索引樹,在你將結果寫為樹物件之前,你必要用其他的工具解決合併衝突。

You can examine such index state with git ls-files —unmerged command. An example:

你可以用 git ls-files –unmerged 命令來檢驗索引狀態,例如:


$ git read-tree -m $orig HEAD $target
$ git ls-files --unmerged
100644 263414f423d0e4d70dae8fe53fa34614ff3e2860 1       hello.c
100644 06fa6a24256dc7e560efa5687fa84b51f0263c3a 2       hello.c
100644 cc44c73eb783565da5831b4d820c962954019b69 3       hello.c

line of the git ls-files —unmerged output begins with the blob mode
bits, blob SHA1, stage number, and the filename. The stage number is
git’s way to say which tree it came from: stage 1 corresponds to $orig
tree, stage 2 HEAD tree, and stage3 $target tree.

一行的 git ls-files –unmerged 輸出的開始是片物件的模式,接著就是片的 SHA1,階段編號,以及檔名。階段編號是
git 用來說明樹來源的:stage1 表示 源頭樹,stage2 表示 HEAD 樹,以及 stage3 表示目標樹。

we said that trivial merges are done inside git-read-tree -m. For
example, if the file did not change from $orig to HEAD nor $target, or
if the file changed from $orig to HEAD and $orig to $target the same
way, obviously the final outcome is what is in HEAD. What the above
example shows is that file hello.c was changed from $orig to HEAD and
$orig to $target in a different way. You could resolve this by running
your favorite 3-way merge program, e.g. diff3, merge, or git’s own
merge-file, on the blob objects from these three stages yourself, like

前我們講過,無干涉的合併會在 git-read-tree -m
命令內部完成。例如,如果檔案從源頭到HEAD或者是目標這兩路發展過程中,其中一路沒有修改過檔案 ,也或者是檔案從源頭到 HEAD
以及從源頭到目標的發展過程中,修改都來自同一路,顯然,最終的結果都是 HEAD 中的東西。上面的例子中,顯示檔案 hello.c 從源頭到
HEAD 以及從源頭到目標的變化來自不同的路徑。你可以執行你喜歡的3-路合併工具來解決衝突,例如 diff3,merge,或者是 git
自帶的 merge-file,用這些工具來處理這三個階段檔案,好像這樣:

  • 譯註:
    我就喜歡用 vi 直接開啟並修改 hello.c,並儲存提交就可以了。


$ git cat-file blob 263414f... >hello.c~1
$ git cat-file blob 06fa6a2... >hello.c~2
$ git cat-file blob cc44c73... >hello.c~3
$ git merge-file hello.c~2 hello.c~1 hello.c~3

would leave the merge result in hello.c~2 file, along with conflict
markers if there are conflicts. After verifying the merge result makes
sense, you can tell git what the final merge result for this file is

這樣合併的結果會出現在 hello.c~2 檔案中,如果有合併衝突的話,檔案中會有衝突標記。處理好衝突之後,告訴 git 最終的合併結果:


$ mv -f hello.c~2 hello.c
$ git update-index hello.c

When a path is in the “unmerged” state, running git-update-index for that path tells git to mark the path resolved.

如果出現路徑的“未合併”狀態,對路徑執行 git-update-index 告訴 git 路徑合併的問題解決了。

above is the description of a git merge at the lowest level, to help
you understand what conceptually happens under the hood. In practice,
nobody, not even git itself, runs git-cat-file three times for this.
There is a git-merge-index program that extracts the stages to
temporary files and calls a “merge” script on it:

面的陳述是對 git 合併的底層介紹,幫助你從概念上理解合併的表面之下發生了什麼,事實上,沒有人,甚至是 git 自身都不會執行
git-cat-files 三次來處理這樣的情況。git-merge-index 程式提取各個階段到臨時檔案,稱為“合併”指令碼。


$ git merge-index git-merge-one-file hello.c

and that is what higher level git-merge -s resolve is implemented with.

並有一個高層命令 git-merge -s resolve 來實現。


Chapter 10. Hacking git 第十章. git 的開發

This chapter covers internal details of the git implementation which probably only git developers need to understand.

本章涵蓋 git 的實現的內部細節,它只適合 git 的開發者學習理解。


Object storage format 物件的儲存格式

objects have a statically determined “type” which identifies the format
of the object (i.e. how it is used, and how it can refer to other
objects). There are currently four different object types: “blob”,
“tree”, “commit”, and “tag”.

所有的物件都有一個固定的“型別”,它用於識別物件的格式(譬如,他如何使用,以及它如何引用到其他的型別)。現在有四種不同的物件型別:“片”,“樹”,“交付”,以及 “標籤”。

of object type, all objects share the following characteristics: they
are all deflated with zlib, and have a header that not only specifies
their type, but also provides size information about the data in the
object. It’s worth noting that the SHA1 hash that is used to name the
object is the hash of the original data plus this header, so sha1sum
does not match the object name for file
. (Historical note: in the dawn of the age of git the hash was the sha1 of the compressed object.)

與物件的型別無關,所有物件都具備以下的這些性質:他們都使用 zlib
庫的 deflated
壓縮演算法,並且都帶一個頭,它不但指明瞭物件的型別,同時還提供了物件中的資料大小資訊。這對於計算在原始資料中加入了物件頭的物件的 SHA1 雜湊特徵值來說是很有價值的,這個雜湊特徵值用來命名該物件。所以,sha1sum file
這樣的命令是不能匹配到一個 file
檔案的物件名的。(歷史提示:在 git 專案的初期是使用壓縮之後的物件的雜湊特徵值的。)

a result, the general consistency of an object can always be tested
independently of the contents or the type of the object: all objects
can be validated by verifying that (a) their hashes match the content
of the file and (b) the object successfully inflates to a stream of
bytes that forms a sequence of <ascii type without space>
<space> <ascii decimal size> <byte/0>
<binary object data>.

配他們的檔案的內容,以及(b)物件可以有效地以一個位元組串的佇列形式展開 <無空格的 ascii 編碼型別標記>
<空格> <ascii 編碼的十進位制大小標記> <位元組 /0>

structured objects can further have their structure and connectivity to
other objects verified. This is generally done with the git-fsck
program, which generates a full dependency graph of all objects, and
verifies their internal consistency (in addition to just verifying
their superficial consistency through the hash).

構建好的物件會擁有自己的結構,以及對已經驗證過的其他物件的連線。這可以用 git-fsck 程式來檢驗,它檢驗所有物件的關聯檢視,以及驗證他們的內部一致性(驗證他們的表層一致性只需要通過雜湊演算法)。


A birds-eye view of Git’s source code 鳥瞰 git 原始碼

It is not
always easy for new developers to find their way through Git’s source
code. This section gives you a little guidance to show where to start.

對於一個新手來說,找到他們自己的理解 git 原始碼的途徑不是一件容易的事情。這一節給你一個從那裡開始的小小指導。

A good place to start is with the contents of the initial commit, with:



$ git checkout e83c5163

The initial revision lays the foundation for almost everything git has today, but is small enough to read in one sitting.

初始的修訂版本已經為今天的 git 的打下了基礎,而且它對於閱讀來說足夠地小。

that terminology has changed since that revision. For example, the
README in that revision uses the word “changeset” to describe what we
now call a commit.

注意版本中的術語的變遷,譬如,在 README 中,我們現在叫的“交付”(commit)那時叫“變更集”(changeset)。

we do not call it “cache” any more, but rather “index”; however, the
file is still called cache.h. Remark: Not much reason to change it now,
especially since there is no good single name for it anyway, because it
is basically the header file which is included by all of Git’s C

還有,我們現在已經不再將“索引”(index)叫做“快取”(cache)了,不過檔案明還是叫 cache.h 。備註:現在還沒有足夠的理由改掉這個名字,尤其是在沒有一個好的名字來替代它,因為它是基礎性的標頭檔案,被包含在所有 Git 的 C 原始碼中。

you grasp the ideas in that initial commit, you should check out a more
recent version and skim cache.h, object.h and commit.h.

當你已經領悟了初始交付中的內容,你應該提取更新的版本來閱讀,並且跳過 cache.h, object.h 和 commit.h 。

the early days, Git (in the tradition of UNIX) was a bunch of programs
which were extremely simple, and which you used in scripts, piping the
output of one into another. This turned out to be good for initial
development, since it was easier to test new things. However, recently
many of these parts have become builtins, and some of the core has been
“libified”, i.e. put into libgit.a for performance, portability
reasons, and to avoid code duplication.

避免程式碼的重複,這些核心部分放到了 libgit.a 庫中。

now, you know what the index is (and find the corresponding data
structures in cache.h), and that there are just a couple of object
types (blobs, trees, commits and tags) which inherit their common
structure from struct object, which is their first member (and thus,
you can cast e.g. (struct object *)commit to achieve the same as
&commit->object, i.e. get at the object name and flags).

在,你知道什麼是索引了(並且你可以在 cache.h
變數(因此,你可以進行類似這樣的指標變換 (struct object *)commit,等同由於
&commit->object, 也就可以得到物件的名稱和標誌)。

Now is a good point to take a break to let this information sink in.


step: get familiar with the object naming. Read the section called
“Naming commits”. There are quite a few ways to name an object (and not
only revisions!). All of these are handled in sha1_name.c. Just have a
quick look at the function get_sha1(). A lot of the special handling is
done by functions like get_sha1_basic() or the likes.

一步:熟悉物件名稱。參看“命名交付”那一節。有若干的途徑來命名一個物件(並非只是一個修訂版物件)。所有這些處理在 sha1_name.c
檔案中實現。首先快速閱讀一下函式 get_sha1()。很多特殊的處理由類似 get_sha1_basice() 之類的函式來處理。

This is just to get you into the groove for the most libified part of Git: the revision walker.

這個只是帶你進入大量的 Git 庫化部分的切入點:修訂版步行者。

Basically, the initial version of git-log was a shell script:

基本上,初期的 git-log 命令是一個 shell 指令碼:


$ git-rev-list --pretty $(git-rev-parse --default HEAD "[email protected]") | /
LESS=-S ${PAGER:-less}

What does this mean?


is the original version of the revision walker, which always printed a
list of revisions to stdout. It is still functional, and needs to,
since most new Git programs start out as scripts using git-rev-list.

git-rev-list 是修訂版步行者的起源版本,它總是將修訂版的列表列印到標準輸出。它仍然是有用和有需要的,直至最新的 Git 程式中仍然是一個以 git-rev-list 為開始的指令碼。

is not as important any more; it was only used to filter out options
that were relevant for the different plumbing commands that were called
by the script.

git-rev-parse 就並不是那麼重要了;它只是用作Git指令碼呼叫不同的關聯命令的選項過濾器。

of what git-rev-list did is contained in revision.c and revision.h. It
wraps the options in a struct named rev_info, which controls how and
what revisions are walked, and more.

大部分 git-rev-list 所做的事情是在 revision.c 和 revision.h 中實現的。那裡封裝了結構 rev_info,用於控制如何進行版本步行和處理等等。

original job of git-rev-parse is now taken by the function
setup_revisions(), which parses the revisions and the common command
line options for the revision walker. This information is stored in the
struct rev_info for later consumption. You can do your own command line
option parsing after calling setup_revisions(). After that, you have to
call prepare_revision_walk() for initialization, and then you can get
the commits one by one with the function get_revision().

的本份的任務是呼叫函式 setup_revision(),它解析修訂版併為修訂版步行解析命令列選項。解析出來的資訊儲存在結構 rev_info
中,以備將來呼叫。呼叫函式 setup_revision()
對命令列選項進行過解析之後,你將來將可以使用你自己的命令列選項了。之後,你必須呼叫 prepare_revision_walk()
進行初始化,之後,你就可以通過 get_revision() 一個接一個地取得各個交付(commit)。

you are interested in more details of the revision walking process,
just have a look at the first implementation of cmd_log(); call git
show v1.3.0~155^2~4 and scroll down to that function (note that you no
longer need to call setup_pager() directly).

如果你對修訂版步行的更多細節感興趣,只要看看 cmd_log() 的第一個實現;在版本庫中呼叫 git show v1.3.0~155^2~4 並下滾到該函式(注意你已經不必再直至呼叫 setup_pager() 函式)。

Nowadays, git-log is a builtin, which means that it is contained in the command git. The source side of a builtin is

現在,git-log 已經是一個內建命令,這意味著它包含做 git 命令中。從原始碼的角度說,內建就是

  • a function called cmd_<bla>, typically defined in builtin-<bla>.c, and declared in builtin.h,

  • an entry in the commands[] array in git.c, and
  • an entry in BUILTIN_OBJECTS in the Makefile.
  • 函式名為 cmd_<bla>,典型地它在 builtin-<bla>.c 中實現,並在 builtin.h 中宣告,

  • 它將成為在 git.c 中的 commands[] 陣列的一個條目,並且
  • 成為 Makefile 檔案中的 BUILTIN_OBJECTS 的一個條目。

more than one builtin is contained in one source file. For example,
cmd_whatchanged() and cmd_log() both reside in builtin-log.c, since
they share quite a bit of code. In that case, the commands which are
not named like the .c file in which they live have to be listed in
BUILT_INS in the Makefile.

些時候內建命令被包裝在一個原始檔中。例如,com_whatchanged() 和 com_log() 兩個都位於 builtin-log.c
中,如此他們就可以共享二進位制程式碼。這樣命令就不能像它是位於 .c 檔案中那樣來命名了,它必須在 Makefile 檔案中的

looks more complicated in C than it does in the original script, but
that allows for a much greater flexibility and performance.

git-log 在 C 中的實現,看起來要比原來用 shell 指令碼實現的時候複雜多了,不過這樣也得到了更好的效能和更大的伸縮性。

Here again it is a good point to take a pause.


three is: study the code. Really, it is the best way to learn about the
organization of Git (after you know the basic concepts).

第三課是: 研究原始碼。的確,這是學習 Git 的專案組織的最佳途徑(當你知道了基本的概念之後)。

think about something which you are interested in, say, “how can I
access a blob just knowing the object name of it?”. The first step is
to find a Git command with which you can do it. In this example, it is
either git-show or git-cat-file.

於是,想象一下某些你感興趣的事情,比方說,“我是如何通過物件名來訪問一個片物件的呢?”。首先查詢有什麼 Git 命令可以做到這個。在這個例子中,git-show 或者是 git-cat-file 任何一個都可以。

For the sake of clarity, let’s stay with git-cat-file, because it

為更為清晰的緣故,讓我們先談談 git-cat-file, 因為它

  • is plumbing, and
  • was
    around even in the initial commit (it literally went only through some
    20 revisions as cat-file.c, was renamed to builtin-cat-file.c when made
    a builtin, and then saw less than 10 versions).
  • 它是一截水管(譯註:
    極客們對 git 核心命令的戲稱。),並且

  • 它涉及到交付物件的初始化(cat-file.c 檔案大約經歷了20多個修訂之後,當它成為內建命令之後,被重新命名為 builtin-cat-file.c,並且我們至少可以看到又經歷了10個版本的修訂)。

So, look into builtin-cat-file.c, search for cmd_cat_file() and look what it does.

看看 builtin-cat-file.c 的內部,找到 cmd_cat_file() 函式並且看看它做了什麼。


"git-cat-file [-t|-s|-e|-p|<type>] <sha1>"
"Not a valid object name %s"

skip over the obvious details; the only really interesting part here is
the call to get_sha1(). It tries to interpret argv[2] as an object
name, and if it refers to an object which is present in the current
repository, it writes the resulting SHA-1 into the variable sha1.

讓我們跳過一些明顯的細節,只關注它呼叫 get_sha1() 函式的地方。它嘗試解析 argv[2] 作為物件名,當它可以發現它的確指向當前版本庫中的一個物件時,它將 SHA-1 結果寫入變數 sha1。

Two things are interesting here:


  • get_sha1()
    returns 0 on success. This might surprise some new Git hackers, but
    there is a long tradition in UNIX to return different negative numbers
    in case of different errors—and 0 on success.
  • the
    variable sha1 in the function signature of get_sha1() is unsigned char
    *, but is actually expected to be a pointer to unsigned char[20]. This
    variable will contain the 160-bit SHA-1 of the given commit. Note that
    whenever a SHA-1 is passed as unsigned char *, it is the binary
    representation, as opposed to the ASCII representation in hex
    characters, which is passed as char *.
  • get_sha1() 成功時返回 0。這對於一些新的 Git 黑客來說可能會感到奇怪,不過這個是 Unix 系統的長期傳統,不同的錯誤返回不同的負數值,而成功則返回 0 。

  • 數 get_sha1() 的引數變數 sha1 是無符號字元指標 (unsigned char *),不過實際上被當成是指向一個無符號字元陣列
    (unsigned char[20]) 的指標看待。這個變數包含一個 160 位二進位制的 SHA-1
    給指定的交付使用。注意,任何時候它作為二進位制 SHA-1 的表述時,都應作為無符號字元指標 (unsigned char *)
    來傳遞,當作為十六進位制的 ASCII 編碼表述時,它就要作為字元指標傳遞 (char *)。

You will see both of these things throughout the code.


Now, for the meat:




is how you read a blob (actually, not only a blob, but any type of
object). To know how the function read_object_with_reference() actually
works, find the source code for it (something like git grep
read_object_with | grep “:[a-z]” in the git repository), and read the

裡就是如何讀取一個片(準確地說,不但是片,而是所有的物件型別)。要知道函式 read_object_with_referencee()
是如何工作的,那就查詢一下這個函式(例如在 git 版本庫中執行命令 git grep read_object_with | grep

  • 譯註:
    瀏覽和分析程式碼,還是 vim ctags cscope 這樣的工具組合方便得多,不過這已經超出了本文的範圍了。

To find out how the result can be used, just read on in cmd_cat_file():

查詢返回的結果如何使用,就看看函式 cmd_cat_file():



you do not know where to look for a feature. In many such cases, it
helps to search through the output of git log, and then git-show the
corresponding commit.

有些時候,你並不知道在哪裡找到一個功能特性。大多數情況下, git log 的輸出可以幫助你發現你需要的東西,接著 git-show 也能找到響應的交付。

If you know that there was some test case for git-bundle, but do not
remember where it was (yes, you could git grep bundle t/, but that does
not illustrate the point!):

據個例子: 如果你知道某些針對 git-bundle 測試工作,不過不記得它是在什麼地方了(對,你可以用 git grep bundle t/ ,不過那樣似乎不得要領!):


$ git log --no-merges t/

the pager (less), just search for “bundle”, go a few lines back, and
see that it is in commit 18449ab0… Now just copy this object name, and
paste it into the command line

在輸出的頁面中(輸出被管道定向到 less 工具),查詢 “bundle”,回滾幾行,就能看到關聯交付物件名 18449ab0…,並將它複製到命令列


$ git show 18449ab0



Another example: Find out what to do in order to make some script a builtin:

另外一個例子: 查詢從指令碼方式到內建命令的方式的開發過程中做過什麼:


$ git log --no-merges --diff-filter=A builtin-*.c

You see, Git is actually the best tool to find out about the source of Git itself!

你可以看到,Git 自身事實上就是非常好的原始碼搜尋工具。


Chapter 11. GIT Glossary 第十一章. GIT 字典

alternate object database 輪替物件資料庫

  • Via
    the alternates mechanism, a repository can inherit part of its object
    database from another object database, which is called “alternate”.
  • 通過輪替機制,一個版本庫可以將自己的某些部分作為另外的版本庫的傳承,所以叫“輪替”。

bare repository 裸庫

  • A
    bare repository is normally an appropriately named directory with a
    .git suffix that does not have a locally checked-out copy of any of the
    files under revision control. That is, all of the git administrative
    and control files that would normally be present in the hidden .git
    sub-directory are directly present in the repository.git directory
    instead, and no other files are present and checked out. Usually
    publishers of public repositories make bare repositories available.
  • 一個裸庫就是正常版本庫的那個叫 .git 的目錄,並且不會提取出任何在版本庫控制下的檔案。所有 git 管理和控制的檔案正常情況下是在隱藏的 .git 子目錄中呈現的,對於裸庫來說,這些被直接呈現在 repository.git 目錄中(譯註:
    譬如叫 u-boot.git,linux-2.6.git 等),並且不會呈現和提取出任何的專案檔案。通常來說,專案釋出者會以裸庫的形式釋出專案。

blob object 片物件

  • Untyped object, e.g. the contents of a file.
  • 無型別的物件,只是一個檔案的內容。

branch 分支

  • A
    “branch” is an active line of development. The most recent commit on a
    branch is referred to as the tip of that branch. The tip of the branch
    is referenced by a branch head, which moves forward as additional
    development is done on the branch. A single git repository can track an
    arbitrary number of branches, but your working tree is associated with
    just one of them (the “current” or “checked out” branch), and HEAD
    points to that branch.

  • 個“分支”就是一條研發的線路。在分支上的最新交付物件就是作為分支的頂端。分支的頂端也是分支頭的引用,它將隨專案的研發不斷推進。一個單獨的版本庫可
    以跟中任意多的分支數目,不過你的工作樹就只能跟它們其中一個關聯對應了(即 “當前” 或者是 “提取” 的分支),HEAD

cache 快取

  • Obsolete for: index.
  • 過時的概念了,參考: 索引

chain 鏈

  • A
    list of objects, where each object in the list contains a reference to
    its successor (for example, the successor of a commit could be one of
    its parents).
  • 一個物件連結串列,其中的每個物件都包含一個指向其被承繼者的引用(例如,一個交付物件的被承繼者,就是它的其中一個父交付)。

changeset 變更集

  • BitKeeper/cvsps
    speak for “commit”. Since git does not store changes, but states, it
    really does not make sense to use the term “changesets” with git.
  • 在 BitKeeper/cvsps 中叫 “交付”。然而 git 是不儲存專案的變遷的,它儲存的是專案的狀態,對於 git 來說,就不必為 “變更集” 這個概念費神了。

checkout 提取

  • The
    action of updating all or part of the working tree with a tree object
    or blob from the object database, and updating the index and HEAD if
    the whole working tree has been pointed at a new branch.
  • 以物件資料庫中的樹物件和片物件來更新工作目錄中的所有東西,如果工作樹被指向了一個新的分支的話,同時重新整理索引和 HEAD。

cherry-picking 櫻桃摘取

  • In
    SCM jargon, “cherry pick” means to choose a subset of changes out of a
    series of changes (typically commits) and record them as a new series
    of changes on top of a different codebase. In GIT, this is performed by
    the “git cherry-pick” command to extract the change introduced by an
    existing commit and to record it based on the tip of the current branch
    as a new commit.

  • 原始碼管理的行話中,“櫻桃摘取”意味著在一個變更(交付就是一個典型)序列中選擇一個子集並將它儲存為一個不同的程式碼起點的變更集。在 git
    中,執行這個操作的是 git cherry-pick

clean 乾淨的

  • A working tree is clean, if it corresponds to the revision referenced by the current head. Also see “dirty”.
  • 如果工作樹完全對應於當前的分支頭所引用的狀態,我們稱它是乾淨的。對應地,參看 “汙濁”。

commit 交付

  • As
    a noun: A single point in the git history; the entire history of a
    project is represented as a set of interrelated commits. The word
    “commit” is often used by git in the same places other revision control
    systems use the words “revision” or “version”. Also used as a short
    hand for commit object.
  • As
    a verb: The action of storing a new snapshot of the project’s state in
    the git history, by creating a new commit representing the current
    state of the index and advancing HEAD to point at the new commit.
  • 作為名詞: 是指 git 中的一個歷史瞬間;整個專案的發展歷史表現為一個相互關聯的交付集。在 git 中,“交付”這個詞所表達的意思,在其他的版本控制系統上用“修訂”或者是“版本”。它還經常被用作交付物件的縮略寫法。
  • 作為動詞: 是指在 git 歷史中儲存專案狀態快照的動作,它描述索引的當前狀態並將 HEAD 推進至當前新的交付。

commit object 交付物件

  • An
    object which contains the information about a particular revision, such
    as parents, committer, author, date and the tree object which
    corresponds to the top directory of the stored revision.
  • 一個物件,它包含了具體的修訂資訊,譬如它的父輩,提交人,作者,日期和樹物件,該樹物件儲存了對應的專案目錄的修訂。

core git 核心 git

  • Fundamental data structures and utilities of git. Exposes only limited source code management tools.
  • Git 的基本資料結構和工具。只表現為最基本的原始碼管理工具。


  • Directed
    acyclic graph. The commit objects form a directed acyclic graph,
    because they have parents (directed), and the graph of commit objects
    is acyclic (there is no chain which begins and ends with the same
  • 單向非環路檢視。交付物件是按一個單向非環路是圖的形式來組織的,因為他們有父輩(方向),以及非環路檢視(不存在開始和結尾都是同一個物件的情況)。

dangling object

  • An
    unreachable object which is not reachable even from other unreachable
    objects; a dangling object has no references to it from any reference
    or object in the repository.

detached HEAD 遊離 HEAD

  • Normally
    the HEAD stores the name of a branch. However, git also allows you to
    check out an arbitrary commit that isn’t necessarily the tip of any
    particular branch. In this case HEAD is said to be “detached”.
  • 正常情況下,HEAD 儲存的是一個分支,不過 git 還容許你提取任意的交付,它不必是一個具體的分支的頂端。這種情況下,我們成 HEAD 是“遊離的”。

dircache 目錄快取

  • You are waaaaay behind. See index.
  • 你有點暈了吧,看“索引”吧。

directory 目錄

  • The list you get with “ls” :-)

  • 那當然就是你執行 “ls” 命令看到的東西了 :-)

dirty 汙濁的

  • A working tree is said to be “dirty” if it contains modifications which have not been committed to the current branch.
  • 當一個工作樹的內容已經被修改過,但是仍然沒有被提交到當前分支,則成為“汙濁的”。


evil merge 惡劣合併

  • An evil merge is a merge that introduces changes that do not appear in any parent.
  • 惡劣合併就是合併了一個由沒有父輩的東西所匯入的變更。

fast forward 快進

  • A
    fast-forward is a special type of merge where you have a revision and
    you are “merging” another branch’s changes that happen to be a
    descendant of what you have. In such these cases, you do not make a new
    merge commit but instead just update to his revision. This will happen
    frequently on a tracking branch of a remote repository.
  • 快進是一個特殊型別的合併,你要合併的來自其他分支的東西是你的合併的目標分支的後裔。這種情況下,你不用生成一個新的合併交付,取而代之的是將你的目標分支更新到他的修訂上。這在一個跟蹤遠端版本庫的分支上是經常發生的。

fetch 抓取

  • Fetching
    a branch means to get the branch’s head ref from a remote repository,
    to find out which objects are missing from the local object database,
    and to get them, too. See also git-fetch(1).
  • 抓取一個分支,意味著取得一個遠端版本庫的頭的引用,並找出本地版本庫的物件資料庫中有什麼物件缺失了,並將缺失的物件抓取過來。參考 git-fetch(1)。

file system 檔案系統

  • Linus
    Torvalds originally designed git to be a user space file system, i.e.
    the infrastructure to hold files and directories. That ensured the
    efficiency and speed of git.
  • Linus Torvalds 開始是就將 git 設計成一個使用者空間的檔案系統,也就是基礎架構包含了檔案和目錄。這保證了 git 的效率和速度。

git archive | git 檔案

  • Synonym for repository (for arch people).
  • 版本庫的同義詞(對於 arch 使用者來說)。 譯註:

    Gnu Arch

grafts 嫁接

  • Grafts
    enables two otherwise different lines of development to be joined
    together by recording fake ancestry information for commits. This way
    you can make git pretend the set of parents a commit has is different
    from what was recorded when the commit was created. Configured via the
    .git/info/grafts file.
  • 嫁接可以使兩個不同的開發路線,通過偽造交付的祖先資訊的方式接合到一起。通過這個方法你可以令 git 在一個交付被建立的時候,偽造一份與其真正來源不同的父輩集。它在 .git/info/grafts 檔案中配置。

hash 雜湊

  • In git’s context, synonym to object name.
  • 在 git 的背景下,就是物件名的同義詞。

head 頭

  • A
    named reference to the commit at the tip of a branch. Heads are stored
    in $GIT_DIR/refs/heads/, except when using packed refs. (See
  • 一個分支頂端交付的引用稱謂。頭都儲存在 $GIT_DIR/refs/heads 中,當使用打包過的引用時除外(參看 git-pack-refs(1).)。


  • The
    current branch. In more detail: Your working tree is normally derived
    from the state of the tree referred to by HEAD. HEAD is a reference to
    one of the heads in your repository, except when using a detached HEAD,
    in which case it may reference an arbitrary commit.
  • 當前分支。更詳細點: 你的工作樹正常情況下是產生自 HEAD 所引用的樹物件所描述的狀態。HEAD 指向你的版本庫中的其中一個頭,除非你正在使用一個遊離的 HEAD,這是一種指向一個任意的交付的情況。

head ref 頭引用

  • A synonym for head.
  • 頭的同義詞。

hook 鉤子

  • During
    the normal execution of several git commands, call-outs are made to
    optional scripts that allow a developer to add functionality or
    checking. Typically, the hooks allow for a command to be pre-verified
    and potentially aborted, and allow for a post-notification after the
    operation is done. The hook scripts are found in the $GIT_DIR/hooks/
    directory, and are enabled by simply removing the .sample suffix from
    the filename. In earlier versions of git you had to make them

  • 一系列的 git
    在命令執行完成之後進行後期提示。鉤子指令碼可以在 $GIT_DIR/hooks/ 目錄下找到,並可以簡單地通過將檔名的 .sample
    後續名去掉來使能它。在早期的 git 版本中,你還必要給他們加上可執行屬性。

index 索引

  • A
    collection of files with stat information, whose contents are stored as
    objects. The index is a stored version of your working tree. Truth be
    told, it can also contain a second, and even a third version of a
    working tree, which are used when merging.
  • 一個檔案狀態資訊的集合,它的內容會被儲存為物件。索引儲存了你的工作目錄的版本。告訴你一個真相,它是可以包含第二個,甚至是第三個你的工作樹的版本的,那是當你進行合併操作的時候。

index entry 索引條目

  • The
    information regarding a particular file, stored in the index. An index
    entry can be unmerged, if a merge was started, but not yet finished
    (i.e. if the index contains multiple versions of that file).
  • 針對每個具體的檔案的資訊,它儲存在索引中。一個索引條目可以是未合併的,如果合併操作已經開始,但還沒有完成時(例如,索引包含檔案的多個版本)。

master 主分支

  • The
    default development branch. Whenever you create a git repository, a
    branch named “master” is created, and becomes the active branch. In
    most cases, this contains the local development, though that is purely
    by convention and is not required.
  • 預設的開發分支,每當你建立一個 git 版本庫的時候,名為 “master” 的分支會同時被建立,併成為活動的分支。大多數情況下,版本庫會包含本地的開發分支,但是那純粹是一種約定,並不是必須這樣做的。

merge 合併

  • As
    a verb: To bring the contents of another branch (possibly from an
    external repository) into the current branch. In the case where the
    merged-in branch is from a different repository, this is done by first
    fetching the remote branch and then merging the result into the current
    branch. This combination of fetch and merge operations is called a
    pull. Merging is performed by an automatic process that identifies
    changes made since the branches diverged, and then applies all those
    changes together. In cases where changes conflict, manual intervention
    may be required to complete the merge.
  • As
    a noun: unless it is a fast forward, a successful merge results in the
    creation of a new commit representing the result of the merge, and
    having as parents the tips of the merged branches. This commit is
    referred to as a “merge commit”, or sometimes just a “merge”.

  • 為動詞:
  • 作為名詞: 除非合併是一個快進,一個成功的合併將建立一個新的交付物件,該物件描述了合併的結果,並有一個合併分支的頂端交付作為父輩的集合。這個交付歸類為“合併交付”,或者有時乾脆就叫“合併”。

object 物件

  • The unit of storage in git. It is uniquely identified by the SHA1 of its contents. Consequently, an object can not be changed.
  • git 中的儲存單元。它以 SHA1 (譯註:


object database 物件資料庫

  • Stores
    a set of “objects”, and an individual object is identified by its
    object name. The objects usually live in $GIT_DIR/objects/.
  • 儲存物件們的場所,不同的物件由它們自身的物件名識別。物件通常是儲存在 $GIT_DIR/objects/ 目錄中。

object identifier 物件標識

  • Synonym for object name.
  • 物件名的同義詞。

object name 物件名

  • The
    unique identifier of an object. The hash of the object’s contents using
    the Secure Hash Algorithm 1 and usually represented by the 40 character
    hexadecimal encoding of the hash of the object.
  • 物件的唯一標識。它是以物件的內容由安全雜湊演算法1計算出來的雜湊特徵值,通常表達為一串40位的十六進位制編碼的字串。

object type 物件型別

  • One of the identifiers “commit”, “tree”, “tag” or “blob” describing the type of an object.
  • 一個標記,表明物件是屬於 “交付”,“樹”,“標籤”或者是“片”中的那個型別。

octopus 章魚

  • To merge more than two branches. Also denotes an intelligent predator.
  • 合併多於兩個分支。也表示一個只聰明的食肉動物,呵呵。

origin 源頭

  • The
    default upstream repository. Most projects have at least one upstream
    project which they track. By default origin is used for that purpose.
    New upstream updates will be fetched into remote tracking branches
    named origin/name-of-upstream-branch, which you can see using “git
    branch -r”.
  • 預設的上游版本庫。大部分的專案都有至少一個上游版本庫需要跟蹤。預設源頭就是為這個目的來使用的。新的上游版本庫跟蹤分支將被抓取進 origin/name-of-upstream-branch 中,它們可以用 “git branch -r” 命令列舉出來。

pack 打包

  • A set of objects which have been compressed into one file (to save space or to transmit them efficiently).
  • 物件集被壓縮到一個檔案當中(有利於節省磁碟空間和傳輸效率)。

pack index

  • The
    list of identifiers, and other information, of the objects in a pack,
    to assist in efficiently accessing the contents of a pack.
  • 已經被打包的物件的標識,以及其他資訊的連結串列,輔助高效地在打包檔案中訪問物件的內容。

parent 父輩

  • A commit object contains a (possibly empty) list of the logical predecessor(s) in the line of development, i.e. its parents.
  • 一個交付包含一個(可能是空)的連結串列,裡面紀錄了該物件在開發過程當中的邏輯前輩,例如,它的父交付。

pickaxe 手鎬

  • The
    term pickaxe refers to an option to the diffcore routines that help
    select changes that add or delete a given text string. With the
    —pickaxe-all option, it can be used to view the full changeset that
    introduced or removed, say, a particular line of text. See git-diff(1).
  • 手鎬遞交一個選項給差異比較規程,幫助決定一個指定的文字串是加入或者是刪除。結合 –packaxe-all 選項,它可以用於參看全部的變更是引入還是刪除,對每個具體的文字行,參看 git-diff(1)。

plumbing 水管

  • Cute name for core git.
  • git 核心的戲稱。

porcelain 瓷器

  • Cute
    name for programs and program suites depending on core git, presenting
    a high level access to core git. Porcelains expose more of a SCM
    interface than the plumbing.
  • 依賴於 git 核心的程式以及程式包的戲稱,表現為規 git 核心的封裝和呼叫。瓷器相對於水管來說,表現出更標準的 SCM 的介面。

pull 拉入

  • Pulling a branch means to fetch it and merge it. See also git-pull(1).
  • 拉入一個分支,意味著抓取併合並它。參看 git-pull(1)。

push 推出

  • Pushing
    a branch means to get the branch’s head ref from a remote repository,
    find out if it is a direct ancestor to the branch’s local head ref, and
    in that case, putting all objects, which are reachable from the local
    head ref, and which are missing from the remote repository, into the
    remote object database, and updating the remote head ref. If the remote
    head is not an ancestor to the local head, the push fails.
  • 推出一個分支,意味著獲取一個遠端版本庫的分之頭,並檢視它是否是本地分之頭的祖先,如果是,則將本地分之頭的所有可及的,而遠端分支上缺少的物件傳輸至遠端物件資料庫,並更新遠端分之頭。如果遠端分之頭不是本地分之頭的前輩,推入將失敗。

reachable 可及性

  • All
    of the ancestors of a given commit are said to be “reachable” from that
    commit. More generally, one object is reachable from another if we can
    reach the one from the other by a chain that follows tags to whatever
    they tag, commits to their parents or trees, and trees to the trees or
    blobs that they contain.

  • 個給定的交付的所有先輩對於這個交付來說是可及的。廣義地,如果一個物件對於另外一個物件來說是可及的,那麼我們可以從在一個鏈條中從一個追索到另外一

rebase 重定基點

  • To reapply a series of changes from a branch to a different base, and reset the head of that branch to the result.
  • 將一系列的變動從一個分之應用到另外一個不同的基點,並將分之頭重新放置到應用之後的結果上。

ref 引用

  • A 40-byte hex representation of a SHA1 or a name that denotes a particular object. These may be stored in $GIT_DIR/refs/.
  • 一個40個十六進位制字元的雜湊特徵值,或者是表明具體物件的名稱。他一般儲存在 $GIT_DIR/refs/ 目錄中。

reflog 引用日誌

  • A
    reflog shows the local “history” of a ref. In other words, it can tell
    you what the 3rd last revision in this repository was, and what was the
    current state in this repository, yesterday 9:14pm. See git-reflog(1)
    for details.
  • 一個引用日誌展示了一個引用的本地歷史。換言之,它可以告訴你最後的第三次修訂是什麼,以及昨天 9:14pm 時的版本庫的狀態等諸如此類的資訊。詳情參看 git-reflog(1)。

refspec 引用規則

  • A
    “refspec” is used by fetch and push to describe the mapping between
    remote ref and local ref. They are combined with a colon in the format
    <src>:<dst>, preceded by an optional plus sign, . For
    example: git fetch $URL refs/heads/master:refs/heads/origin means “grab
    the master branch head from the $URL and store it as my origin branch
    head”. And git push $URL refs/heads/master:refs/heads/to-upstream means
    “publish my master branch head as to-upstream branch at $URL”. See also

  • 引用規則” 用於在抓取和推出操作中對映遠端引用和本地引用的。它們以冒號作為組合格式
    <源>:<目標>,還可以在前面加上一個 號作為選項。例如:git fetch $URL
    refs/heads/master:refs/heads/origin 意味著“從遠端版本庫 $URL 中獲取 master
    分之頭並將其儲存為我的本地 origin 的分支頭”。以及 git push $URL
    refs/heads/master:refs/heads/to-upstream 意味著“推出我的 master 分支頭作為 $URL
    遠端版本庫中的 to-upstream 分支”。參看 git-push(1)。

repository 版本庫

  • A
    collection of refs together with an object database containing all
    objects which are reachable from the refs, possibly accompanied by meta
    data from one or more porcelains. A repository can share an object
    database with other repositories via alternates mechanism.
  • 一個引用自帶物件資料庫的引用集合,所有物件都有它們可及的引用,可能還伴隨帶有來自“瓷器”的後設資料。一個版本庫可以通過替換機制與另外一個版本庫共享它的物件資料庫。

resolve 解決

  • The action of fixing up manually what a failed automatic merge left behind.
  • 手工修復自動合併失敗所遺留的問題。

revision 修訂

  • A particular state of files and directories which was stored in the object database. It is referenced by a commit object.
  • 檔案和目錄的具體狀態,它被儲存在物件資料庫中。該物件被一個交付物件引用。

rewind 逆轉

  • To throw away part of the development, i.e. to assign the head to an earlier revision.
  • 拋棄部分的開發工作,例如,將頭設定到較早時的修訂。


  • Source code management (tool).
  • 原始碼管理(工具)。


  • Synonym for object name.
  • 物件名的同義詞。

shallow repository 淺庫

  • A
    shallow repository has an incomplete history some of whose commits have
    parents cauterized away (in other words, git is told to pretend that
    these commits do not have the parents, even though they are recorded in
    the commit object). This is sometimes useful when you are interested
    only in the recent history of a project even though the real history
    recorded in the upstream is much larger. A shallow repository is
    created by giving the —depth option to git-clone(1), and its history
    can be later deepened with git-fetch(1).

  • 個淺庫是指一個版本庫擁有一個不完整的專案歷史,某些交付物件的先輩被剔除了(換言之,git
    常龐大的時候。建立一個淺庫只需在 git-clone(1) 命令中帶上 –depth 選項,並且它的歷史深度會被 git-fetch(1)

symref 代號引用

  • Symbolic
    reference: instead of containing the SHA1 id itself, it is of the
    format ref: refs/some/thing and when referenced, it recursively
    dereferences to this reference. HEAD is a prime example of a symref.
    Symbolic references are manipulated with the git-symbolic-ref(1)
  • 代號引用:代替物件自己的 SHA1 識別號,當需要引用某個物件時,它的格式: refs/some/thing,git 會遞迴定址該引用。HEAD 就是一個代號引號的好例子。代號引用由 git-synboblic-ref(1) 命令來維護。

tag 標籤

  • A
    ref pointing to a tag or commit object. In contrast to a head, a tag is
    not changed by a commit. Tags (not tag objects) are stored in
    $GIT_DIR/refs/tags/. A git tag has nothing to do with a Lisp tag (which
    would be called an object type in git’s context). A tag is most
    typically used to mark a particular point in the commit ancestry chain.
  • 一個指向一個標記或者是交付物件的引用。相對於頭,一個標籤是不會被一個交付物件改變的。標籤(不是標籤物件)儲存在 $GIT_DIR/refs/tags 中。一個標籤大多數情況下被用作標記交付物件的祖先鏈中的某個具體點。

tag object 標籤物件

  • An
    object containing a ref pointing to another object, which can contain a
    message just like a commit object. It can also contain a (PGP)
    signature, in which case it is called a “signed tag object”.
  • 一個包含指向其他物件的引用的物件,它包含一個資訊,就如交付物件那樣。它還可能包含一個(PGP)加密簽註,在這中情況下,稱它為“已簽註標籤物件”。

topic branch 主題分支

  • A
    regular git branch that is used by a developer to identify a conceptual
    line of development. Since branches are very easy and inexpensive, it
    is often desirable to have several small branches that each contain
    very well defined concepts or small incremental yet related changes.
  • 一個常規的 git 分支,開發者用來分辨某個概念性的開發線路。建立分支是非常容易和開銷很低的,時常用一系列的小分支來裝載各個概念的實現,或者是一組關聯的變更。

tracking branch 跟隨分支

  • A
    regular git branch that is used to follow changes from another
    repository. A tracking branch should not contain direct modifications
    or have local commits made to it. A tracking branch can usually be
    identified as the right-hand-side ref in a Pull: refspec.
  • 一個常規的分支,用於跟蹤其他版本庫的變更。一個跟隨分支不應該被直接修改或者是向它提交本地的交付物件。一個跟隨在拉入 (pull) 操作時,通常是在引用規則 (refspec) 的冒號右側。

tree 樹

  • Either
    a working tree, or a tree object together with the dependent blob and
    tree objects (i.e. a stored representation of a working tree).
  • 要麼是指你的工作目錄樹,要麼是指一個樹物件它彙集了有關的片物件和樹物件(一個對工作樹的狀態描述)。

tree object 樹物件

  • An
    object containing a list of file names and modes along with refs to the
    associated blob and/or tree objects. A tree is equivalent to a
  • 一個物件,它包含一個檔名稱和屬性的列表,以及它所關聯的片物件和樹物件的引用。一個樹就如同一個目錄。


  • A ref pointing to either a commit object, a tree object, or a tag object pointing to a tag or commit or tree object.
  • 一個引用,要麼是指向一個交付物件,樹物件,要麼是一個標籤物件,它指向一個標籤或者是交付物件,或者是樹物件。


Appendix A. Git Quick Reference 附錄 A. Git 快速參考

This is a quick summary of the major commands; the previous chapters explain how these work in more detail.



Creating a new repository 建立一個新的版本庫

From a tarball:



$ tar xzf project.tar.gz
$ cd project
$ git init
Initialized empty Git repository in .git/
$ git add .
$ git commit

From a remote repository:



$ git clone git://
$ cd project


Managing branches 管理分支


$ git branch         # list all local branches in this repo
$ git checkout test  # switch working directory to branch "test"
$ git branch new     # create branch "new" starting at current HEAD
$ git branch -d new  # delete branch "new"

Instead of basing a new branch on current HEAD (the default), use:

建立一個不以當前的 HEAD 為起點的分支,用:


$ git branch new test    # branch named "test"
$ git branch new v2.6.15 # tag named v2.6.15
$ git branch new HEAD^   # commit before the most recent
$ git branch new HEAD^^  # commit before that
$ git branch new test~10 # ten commits before tip of branch "test"

Create and switch to a new branch at the same time:



$ git checkout -b new v2.6.15

Update and examine branches from the repository you cloned from:



$ git fetch             # update
$ git branch -r         # list
$ git checkout -b masterwork origin/master

Fetch a branch from a different repository, and give it a new name in your repository:



$ git fetch git:// theirbranch:mybranch
$ git fetch git:// v2.6.15:mybranch

Keep a list of repositories you work with regularly:



$ git remote add example git://
$ git remote                    # list remote repositories
$ git remote show example       # get details
* remote example
URL: git://
Tracked remote branches
$ git fetch example             # update branches from example
$ git branch -r                 # list all remote branches


Exploring history 勘查歷史


$ gitk                      # visualize and browse history
$ git log                   # list all commits
$ git log src/              # ...modifying src/
$ git log v2.6.15..v2.6.16  # v2.6.16, not in v2.6.15
$ git log master..test      # branch test, not in branch master
$ git log test..master      # branch master, but not in test
$ git log test...master     # one branch, not in both
$ git log -S'foo()'         # ...where difference contain "foo()"
$ git log --since="2 weeks ago"
$ git log -p                # show patches as well
$ git show                  # most recent commit
$ git diff v2.6.15..v2.6.16 # diff between two tagged versions
$ git diff v2.6.15..HEAD    # diff with current head
$ git grep "foo()"          # search working directory for "foo()"
$ git grep v2.6.15 "foo()"  # search old tree for "foo()"
$ git show v2.6.15:a.txt    # look at old version of a.txt

Search for regressions:



$ git bisect start
$ git bisect bad                # current version is bad
$ git bisect good v2.6.13-rc2   # last known good revision
Bisecting: 675 revisions left to test after this
# test here, then:
$ git bisect good               # if this revision is good, or
$ git bisect bad                # if this revision is bad.
# repeat until done.


Making changes 製作變更

Make sure git knows who to blame:

告訴 git 責任該由誰負:


$ cat >>~/.gitconfig <</EOF
name = Your Name Comes Here
email = [email protected]

Select file contents to include in the next commit, then make the commit:



$ git add a.txt    # updated file
$ git add b.txt    # new file
$ git rm c.txt     # old file
$ git commit

Or, prepare and create the commit in one step:



$ git commit d.txt # use latest content only of d.txt
$ git commit -a    # use latest content of all tracked files


Merging 合併


$ git merge test   # merge branch "test" into the current branch
$ git pull git:// master
# fetch and merge in remote branch
$ git pull . test  # equivalent to git merge test


Sharing your changes 共享你的變更

Importing or exporting patches:



$ git format-patch origin..HEAD # format a patch for each commit
# in HEAD but not in origin
$ git am mbox # import patches from the mailbox "mbox"

Fetch a branch in a different git repository, then merge into the current branch:

抓取一個不同的 git 版本庫的分支,併合並進當前分支:


$ git pull git:// theirbranch

Store the fetched branch into a local branch before merging into the current branch:



$ git pull git:// theirbranch:mybranch

After creating commits on a local branch, update the remote branch with your commits:



$ git push ssh:// mybranch:theirbranch

When remote and local branch are both named “test”:

當本地和遠端分支都是叫 “test” 時:


$ git push ssh:// test

Shortcut version for a frequently used remote repository:



$ git remote add example ssh://
$ git push example test


Repository maintenance 版本庫的維護

Check for corruption:



$ git fsck

Recompress, remove unused cruft:



$ git gc


Appendix B. Notes and todo list for this manual 附錄 B. 備忘與本手冊的工作計劃

This is a work in progress.


The basic requirements:


  • It
    must be readable in order, from beginning to end, by someone
    intelligent with a basic grasp of the UNIX command line, but without
    any special knowledge of git. If necessary, any other prerequisites
    should be specifically mentioned as they arise.
  • Whenever
    possible, section headings should clearly describe the task they
    explain how to do, in language that requires no more knowledge than
    necessary: for example, “importing patches into a project” rather than
    “the git-am command”
  • 它必須是從頭到尾按順序閱讀的話是易於理解的,讀者的物件是那些有基本的 UNIX 命令列知識,但是對 git 沒有什麼特別認識的人。若有必要的話,其他的背景知識就提示他們注意。
  • 儘可能地在每個章節的開頭就闡明他們準備說明什麼東西,千言萬語都不及動手操作實在:例如,”importing patches into a project” 就不如 “the git-am command”。

about how to create a clear chapter dependency graph that will allow
people to get to important topics without necessarily reading
everything in between.


Scan Documentation/ for other stuff left out; in particular:

瀏覽 git 原始碼中的 Documentation/ 目錄,看看本文還有什麼遺漏的;具體地:

  • howto’s
  • some of technical/?
  • hooks
  • list of commands in git(1)
  • 如何操作
  • 某些技巧
  • 鉤子
  • git 命令的列表

Scan email archives for other stuff left out


Scan man pages to see if any assume more background than this manual provides.


Simplify beginning by suggesting disconnected head instead of temporary branch creation?


more good examples. Entire sections of just cookbook examples might be
a good idea; maybe make an “advanced examples” section a standard
end-of-chapter section?


Include cross-references to the glossary, where appropriate.


Document shallow clones? See draft 1.5.0 release notes for some documentation.

更詳細的淺克隆文件,可以參考 1.5.0 版本的草案發布註解,那裡有一些文件。

a section on working with other version control systems, including CVS,
Subversion, and just imports of series of release tarballs.

加入與其他修訂控制系統的協調工作的章節,包括 CVS, Subversion,以及匯入發行壓縮包串。

More details on gitweb?

需要詳細介紹 gitweb 嗎?

Write a chapter on using plumbing and writing scripts.


Alternates, clone -reference, etc.

還有對 clone -reference 的介紹,等等。

More on recovery from repository corruption. See:


GitUserManualChinese (last edited 2009-03-30 22:03:21 by RobinSteven