1+ ---
2+ ### DO NOT EDIT! Generated by script/update-book2.rb
3+ category: book
4+ section: documentation
5+ subsection: book
6+ sidebar: book
7+ book:
8+ language_code: zh
9+ chapter:
10+ title: Git 内部原理
11+ number: 10
12+ section:
13+ title: Git 引用
14+ number: 3
15+ cs_number: '10.3'
16+ previous: book/zh/v2/Git-内部原理-Git-对象
17+ next: book/zh/v2/Git-内部原理-包文件
18+ title: Git - Git 引用
19+ url: "/book/zh/v2/Git-内部原理-Git-引用.html"
20+ ---
21+ < h2 id ="_git_refs "> Git 引用</ h2 >
22+ < div class ="paragraph ">
23+ < p > 如果你对仓库中从一个提交(比如 < code > 1a410e</ code > )开始往前的历史感兴趣,那么可以运行
24+ < code > git log 1a410e</ code > 这样的命令来显示历史,不过你需要记得 < code > 1a410e</ code > 是你查看历史的起点提交。
25+ 如果我们有一个文件来保存 SHA-1 值,而该文件有一个简单的名字,
26+ 然后用这个名字指针来替代原始的 SHA-1 值的话会更加简单。</ p >
27+ </ div >
28+ < div class ="paragraph ">
29+ < p > 在 Git 中,这种简单的名字被称为“引用(references,或简写为 refs)”。
30+ 你可以在 < code > .git/refs</ code > 目录下找到这类含有 SHA-1 值的文件。
31+ 在目前的项目中,这个目录没有包含任何文件,但它包含了一个简单的目录结构:</ p >
32+ </ div >
33+ < div class ="listingblock ">
34+ < div class ="content ">
35+ < pre class ="highlight "> < code class ="language-console " data-lang ="console "> $ find .git/refs
36+ .git/refs
37+ .git/refs/heads
38+ .git/refs/tags
39+ $ find .git/refs -type f</ code > </ pre >
40+ </ div >
41+ </ div >
42+ < div class ="paragraph ">
43+ < p > 若要创建一个新引用来帮助记忆最新提交所在的位置,从技术上讲我们只需简单地做如下操作:</ p >
44+ </ div >
45+ < div class ="listingblock ">
46+ < div class ="content ">
47+ < pre class ="highlight "> < code class ="language-console " data-lang ="console "> $ echo 1a410efbd13591db07496601ebc7a059dd55cfe9 > .git/refs/heads/master</ code > </ pre >
48+ </ div >
49+ </ div >
50+ < div class ="paragraph ">
51+ < p > 现在,你就可以在 Git 命令中使用这个刚创建的新引用来代替 SHA-1 值了:</ p >
52+ </ div >
53+ < div class ="listingblock ">
54+ < div class ="content ">
55+ < pre class ="highlight "> < code class ="language-console " data-lang ="console "> $ git log --pretty=oneline master
56+ 1a410efbd13591db07496601ebc7a059dd55cfe9 third commit
57+ cac0cab538b970a37ea1e769cbbde608743bc96d second commit
58+ fdf4fc3344e67ab068f836878b6c4951e3b15f3d first commit</ code > </ pre >
59+ </ div >
60+ </ div >
61+ < div class ="paragraph ">
62+ < p > 我们不提倡直接编辑引用文件。
63+ 如果想更新某个引用,Git 提供了一个更加安全的命令 < code > update-ref</ code > 来完成此事:</ p >
64+ </ div >
65+ < div class ="listingblock ">
66+ < div class ="content ">
67+ < pre class ="highlight "> < code class ="language-console " data-lang ="console "> $ git update-ref refs/heads/master 1a410efbd13591db07496601ebc7a059dd55cfe9</ code > </ pre >
68+ </ div >
69+ </ div >
70+ < div class ="paragraph ">
71+ < p > 这基本就是 Git 分支的本质:一个指向某一系列提交之首的指针或引用。
72+ 若想在第二个提交上创建一个分支,可以这么做:</ p >
73+ </ div >
74+ < div class ="listingblock ">
75+ < div class ="content ">
76+ < pre class ="highlight "> < code class ="language-console " data-lang ="console "> $ git update-ref refs/heads/test cac0ca</ code > </ pre >
77+ </ div >
78+ </ div >
79+ < div class ="paragraph ">
80+ < p > 这个分支将只包含从第二个提交开始往前追溯的记录:</ p >
81+ </ div >
82+ < div class ="listingblock ">
83+ < div class ="content ">
84+ < pre class ="highlight "> < code class ="language-console " data-lang ="console "> $ git log --pretty=oneline test
85+ cac0cab538b970a37ea1e769cbbde608743bc96d second commit
86+ fdf4fc3344e67ab068f836878b6c4951e3b15f3d first commit</ code > </ pre >
87+ </ div >
88+ </ div >
89+ < div class ="paragraph ">
90+ < p > 至此,我们的 Git 数据库从概念上看起来像这样:</ p >
91+ </ div >
92+ < div class ="imageblock ">
93+ < div class ="content ">
94+ < img src ="{{< relurl "book /zh/v2/images/data-model-4.png" > }}" alt="包含分支引用的 Git 目录对象。">
95+ </ div >
96+ < div class ="title "> Figure 151. 包含分支引用的 Git 目录对象。</ div >
97+ </ div >
98+ < div class ="paragraph ">
99+ < p > 当运行类似于 < code > git branch <branch></ code > 这样的命令时,Git 实际上会运行 < code > update-ref</ code > 命令,
100+ 取得当前所在分支最新提交对应的 SHA-1 值,并将其加入你想要创建的任何新引用中。</ p >
101+ </ div >
102+ < div class ="sect3 ">
103+ < h3 id ="ref_the_ref "> HEAD 引用</ h3 >
104+ < div class ="paragraph ">
105+ < p > 现在的问题是,当你执行 < code > git branch <branch></ code > 时,Git 如何知道最新提交的 SHA-1 值呢?
106+ 答案是 HEAD 文件。</ p >
107+ </ div >
108+ < div class ="paragraph ">
109+ < p > HEAD 文件通常是一个符号引用(symbolic reference),指向目前所在的分支。
110+ 所谓符号引用,表示它是一个指向其他引用的指针。</ p >
111+ </ div >
112+ < div class ="paragraph ">
113+ < p > 然而在某些罕见的情况下,HEAD 文件可能会包含一个 git 对象的 SHA-1 值。
114+ 当你在检出一个标签、提交或远程分支,让你的仓库变成
115+ < a href ="https://git-scm.com/docs/git-checkout#_detached_head "> “分离 HEAD”</ a > 状态时,就会出现这种情况。</ p >
116+ </ div >
117+ < div class ="paragraph ">
118+ < p > 如果查看 HEAD 文件的内容,通常我们看到类似这样的内容:</ p >
119+ </ div >
120+ < div class ="listingblock ">
121+ < div class ="content ">
122+ < pre class ="highlight "> < code class ="language-console " data-lang ="console "> $ cat .git/HEAD
123+ ref: refs/heads/master</ code > </ pre >
124+ </ div >
125+ </ div >
126+ < div class ="paragraph ">
127+ < p > 如果执行 < code > git checkout test</ code > ,Git 会像这样更新 HEAD 文件:</ p >
128+ </ div >
129+ < div class ="listingblock ">
130+ < div class ="content ">
131+ < pre class ="highlight "> < code class ="language-console " data-lang ="console "> $ cat .git/HEAD
132+ ref: refs/heads/test</ code > </ pre >
133+ </ div >
134+ </ div >
135+ < div class ="paragraph ">
136+ < p > 当我们执行 < code > git commit</ code > 时,该命令会创建一个提交对象,并用 HEAD 文件中那个引用所指向的 SHA-1 值设置其父提交字段。</ p >
137+ </ div >
138+ < div class ="paragraph ">
139+ < p > 你也可以手动编辑该文件,然而同样存在一个更安全的命令来完成此事:< code > git symbolic-ref</ code > 。
140+ 可以借助此命令来查看 HEAD 引用对应的值:</ p >
141+ </ div >
142+ < div class ="listingblock ">
143+ < div class ="content ">
144+ < pre class ="highlight "> < code class ="language-console " data-lang ="console "> $ git symbolic-ref HEAD
145+ refs/heads/master</ code > </ pre >
146+ </ div >
147+ </ div >
148+ < div class ="paragraph ">
149+ < p > 同样可以设置 HEAD 引用的值:</ p >
150+ </ div >
151+ < div class ="listingblock ">
152+ < div class ="content ">
153+ < pre class ="highlight "> < code class ="language-console " data-lang ="console "> $ git symbolic-ref HEAD refs/heads/test
154+ $ cat .git/HEAD
155+ ref: refs/heads/test</ code > </ pre >
156+ </ div >
157+ </ div >
158+ < div class ="paragraph ">
159+ < p > 不能把符号引用设置为一个不符合引用规范的值:</ p >
160+ </ div >
161+ < div class ="listingblock ">
162+ < div class ="content ">
163+ < pre class ="highlight "> < code class ="language-console " data-lang ="console "> $ git symbolic-ref HEAD test
164+ fatal: Refusing to point HEAD outside of refs/</ code > </ pre >
165+ </ div >
166+ </ div >
167+ </ div >
168+ < div class ="sect3 ">
169+ < h3 id ="_标签引用 "> 标签引用</ h3 >
170+ < div class ="paragraph ">
171+ < p > 前面我们刚讨论过 Git 的三种主要的对象类型(< strong > 数据对象</ strong > 、< strong > 树对象</ strong > 和 < strong > 提交对象</ strong > ),然而实际上还有第四种。
172+ < strong > 标签对象(tag object)</ strong > 非常类似于一个提交对象——它包含一个标签创建者信息、一个日期、一段注释信息,以及一个指针。
173+ 主要的区别在于,标签对象通常指向一个提交对象,而不是一个树对象。
174+ 它像是一个永不移动的分支引用——永远指向同一个提交对象,只不过给这个提交对象加上一个更友好的名字罢了。</ p >
175+ </ div >
176+ < div class ="paragraph ">
177+ < p > 正如 < a href ="{{< relurl "book /zh/v2/ch00/ch02-git-basics-chapter" > }}"> Git 基础</ a > 中所讨论的那样,存在两种类型的标签:附注标签和轻量标签。
178+ 可以像这样创建一个轻量标签:</ p >
179+ </ div >
180+ < div class ="listingblock ">
181+ < div class ="content ">
182+ < pre class ="highlight "> < code class ="language-console " data-lang ="console "> $ git update-ref refs/tags/v1.0 cac0cab538b970a37ea1e769cbbde608743bc96d</ code > </ pre >
183+ </ div >
184+ </ div >
185+ < div class ="paragraph ">
186+ < p > 这就是轻量标签的全部内容——一个固定的引用。
187+ 然而,一个附注标签则更复杂一些。
188+ 若要创建一个附注标签,Git 会创建一个标签对象,并记录一个引用来指向该标签对象,而不是直接指向提交对象。
189+ 可以通过创建一个附注标签来验证这个过程(使用 < code > -a</ code > 选项):</ p >
190+ </ div >
191+ < div class ="listingblock ">
192+ < div class ="content ">
193+ < pre class ="highlight "> < code class ="language-console " data-lang ="console "> $ git tag -a v1.1 1a410efbd13591db07496601ebc7a059dd55cfe9 -m 'test tag'</ code > </ pre >
194+ </ div >
195+ </ div >
196+ < div class ="paragraph ">
197+ < p > 下面是上述过程所建标签对象的 SHA-1 值:</ p >
198+ </ div >
199+ < div class ="listingblock ">
200+ < div class ="content ">
201+ < pre class ="highlight "> < code class ="language-console " data-lang ="console "> $ cat .git/refs/tags/v1.1
202+ 9585191f37f7b0fb9444f35a9bf50de191beadc2</ code > </ pre >
203+ </ div >
204+ </ div >
205+ < div class ="paragraph ">
206+ < p > 现在对该 SHA-1 值运行 < code > git cat-file -p</ code > 命令:</ p >
207+ </ div >
208+ < div class ="listingblock ">
209+ < div class ="content ">
210+ < pre class ="highlight "> < code class ="language-console " data-lang ="console "> $ git cat-file -p 9585191f37f7b0fb9444f35a9bf50de191beadc2
211+ object 1a410efbd13591db07496601ebc7a059dd55cfe9
212+ type commit
213+ tag v1.1
214+ tagger Scott Chacon <
[email protected] > Sat May 23 16:48:58 2009 -0700
215+
216+ test tag</ code > </ pre >
217+ </ div >
218+ </ div >
219+ < div class ="paragraph ">
220+ < p > 我们注意到,object 条目指向我们打了标签的那个提交对象的 SHA-1 值。
221+ 另外要注意的是,标签对象并非必须指向某个提交对象;你可以对任意类型的 Git 对象打标签。
222+ 例如,在 Git 源码中,项目维护者将他们的 GPG 公钥添加为一个数据对象,然后对这个对象打了一个标签。
223+ 可以克隆一个 Git 版本库,然后通过执行下面的命令来在这个版本库中查看上述公钥:</ p >
224+ </ div >
225+ < div class ="listingblock ">
226+ < div class ="content ">
227+ < pre class ="highlight "> < code class ="language-console " data-lang ="console "> $ git cat-file blob junio-gpg-pub</ code > </ pre >
228+ </ div >
229+ </ div >
230+ < div class ="paragraph ">
231+ < p > Linux 内核版本库同样有一个不指向提交对象的标签对象——首个被创建的标签对象所指向的是最初被引入版本库的那份内核源码所对应的树对象。</ p >
232+ </ div >
233+ </ div >
234+ < div class ="sect3 ">
235+ < h3 id ="_远程引用 "> 远程引用</ h3 >
236+ < div class ="paragraph ">
237+ < p > 我们将看到的第三种引用类型是远程引用(remote reference)。
238+ 如果你添加了一个远程版本库并对其执行过推送操作,Git 会记录下最近一次推送操作时每一个分支所对应的值,并保存在 < code > refs/remotes</ code > 目录下。
239+ 例如,你可以添加一个叫做 < code > origin</ code > 的远程版本库,然后把 < code > master</ code > 分支推送上去:</ p >
240+ </ div >
241+ < div class ="listingblock ">
242+ < div class ="content ">
243+ < pre class ="
highlight "
> < code class ="
language-console "
data-lang ="
console "
> $ git remote add origin
[email protected] :schacon/simplegit-progit.git
244+ $ git push origin master
245+ Counting objects: 11, done.
246+ Compressing objects: 100% (5/5), done.
247+ Writing objects: 100% (7/7), 716 bytes, done.
248+ Total 7 (delta 2), reused 4 (delta 1)
249+ To
[email protected] :schacon/simplegit-progit.git
250+ a11bef0..ca82a6d master -> master</ code > </ pre >
251+ </ div >
252+ </ div >
253+ < div class ="paragraph ">
254+ < p > 此时,如果查看 < code > refs/remotes/origin/master</ code > 文件,可以发现 < code > origin</ code > 远程版本库的 < code > master</ code > 分支所对应的 SHA-1 值,就是最近一次与服务器通信时本地 < code > master</ code > 分支所对应的 SHA-1 值:</ p >
255+ </ div >
256+ < div class ="listingblock ">
257+ < div class ="content ">
258+ < pre class ="highlight "> < code class ="language-console " data-lang ="console "> $ cat .git/refs/remotes/origin/master
259+ ca82a6dff817ec66f44342007202690a93763949</ code > </ pre >
260+ </ div >
261+ </ div >
262+ < div class ="paragraph ">
263+ < p > 远程引用和分支(位于 < code > refs/heads</ code > 目录下的引用)之间最主要的区别在于,远程引用是只读的。
264+ 虽然可以 < code > git checkout</ code > 到某个远程引用,但是 Git 并不会将 HEAD 引用指向该远程引用。因此,你永远不能通过 < code > commit</ code > 命令来更新远程引用。
265+ Git 将这些远程引用作为记录远程服务器上各分支最后已知位置状态的书签来管理。</ p >
266+ </ div >
267+ </ div >
268+ < div id ="nav "> < a href ="{{< previous-section >}} "> prev</ a > | < a href ="{{< next-section >}} "> next</ a > </ div >
0 commit comments