epubjs
Version:
Render ePub documents in the browser, across many devices
158 lines (108 loc) • 9.4 kB
HTML
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Pro Git - professional version control</title>
<meta content="http://www.w3.org/1999/xhtml; charset=utf-8" http-equiv="Content-Type"/>
<link href="stylesheet.css" type="text/css" rel="stylesheet"/>
<style type="text/css">
@page { margin-bottom: 5.000000pt; margin-top: 5.000000pt; }</style>
</head>
<body class="calibre">
<h2 class="calibre4" id="calibre_pb_73">Git References</h2>
<p class="calibre3">You can run something like <code class="calibre10">git log 1a410e</code> to look through your whole history, but you still have to remember that <code class="calibre10">1a410e</code> is the last commit in order to walk that history to find all those objects. You need a file in which you can store the SHA-1 value under a simple name so you can use that pointer rather than the raw SHA-1 value.</p>
<p class="calibre3">In Git, these are called "references" or "refs"; you can find the files that contain the SHA-1 values in the <code class="calibre10">.git/refs</code> directory. In the current project, this directory contains no files, but it does contain a simple structure:</p>
<pre class="calibre9"><code class="calibre10">$ find .git/refs
.git/refs
.git/refs/heads
.git/refs/tags
$ find .git/refs -type f
$
</code></pre>
<p class="calibre3">To create a new reference that will help you remember where your latest commit is, you can technically do something as simple as this:</p>
<pre class="calibre9"><code class="calibre10">$ echo "1a410efbd13591db07496601ebc7a059dd55cfe9" > .git/refs/heads/master
</code></pre>
<p class="calibre3">Now, you can use the head reference you just created instead of the SHA-1 value in your Git commands:</p>
<pre class="calibre9"><code class="calibre10">$ git log --pretty=oneline master
1a410efbd13591db07496601ebc7a059dd55cfe9 third commit
cac0cab538b970a37ea1e769cbbde608743bc96d second commit
fdf4fc3344e67ab068f836878b6c4951e3b15f3d first commit
</code></pre>
<p class="calibre3">You aren't encouraged to directly edit the reference files. Git provides a safer command to do this if you want to update a reference called <code class="calibre10">update-ref</code>:</p>
<pre class="calibre9"><code class="calibre10">$ git update-ref refs/heads/master 1a410efbd13591db07496601ebc7a059dd55cfe9
</code></pre>
<p class="calibre3">That's basically what a branch in Git is: a simple pointer or reference to the head of a line of work. To create a branch back at the second commit, you can do this:</p>
<pre class="calibre9"><code class="calibre10">$ git update-ref refs/heads/test cac0ca
</code></pre>
<p class="calibre3">Your branch will contain only work from that commit down:</p>
<pre class="calibre9"><code class="calibre10">$ git log --pretty=oneline test
cac0cab538b970a37ea1e769cbbde608743bc96d second commit
fdf4fc3344e67ab068f836878b6c4951e3b15f3d first commit
</code></pre>
<p class="calibre3">Now, your Git database conceptually looks something like Figure 9-4.</p>
<p class="calibre3"><img src="18333fig0904-tn.png" alt="Figure 9-4. Git directory objects with branch head references included." title="Figure 9-4. Git directory objects with branch head references included." class="calibre6"/></p>
<p class="calibre3">When you run commands like <code class="calibre10">git branch (branchname)</code>, Git basically runs that <code class="calibre10">update-ref</code> command to add the SHA-1 of the last commit of the branch you're on into whatever new reference you want to create.</p>
<h3 class="calibre5">The HEAD</h3>
<p class="calibre3">The question now is, when you run <code class="calibre10">git branch (branchname)</code>, how does Git know the SHA-1 of the last commit? The answer is the HEAD file. The HEAD file is a symbolic reference to the branch you're currently on. By symbolic reference, I mean that unlike a normal reference, it doesn't generally contain a SHA-1 value but rather a pointer to another reference. If you look at the file, you'll normally see something like this:</p>
<pre class="calibre9"><code class="calibre10">$ cat .git/HEAD
ref: refs/heads/master
</code></pre>
<p class="calibre3">If you run <code class="calibre10">git checkout test</code>, Git updates the file to look like this:</p>
<pre class="calibre9"><code class="calibre10">$ cat .git/HEAD
ref: refs/heads/test
</code></pre>
<p class="calibre3">When you run <code class="calibre10">git commit</code>, it creates the commit object, specifying the parent of that commit object to be whatever SHA-1 value the reference in HEAD points to.</p>
<p class="calibre3">You can also manually edit this file, but again a safer command exists to do so: <code class="calibre10">symbolic-ref</code>. You can read the value of your HEAD via this command:</p>
<pre class="calibre9"><code class="calibre10">$ git symbolic-ref HEAD
refs/heads/master
</code></pre>
<p class="calibre3">You can also set the value of HEAD:</p>
<pre class="calibre9"><code class="calibre10">$ git symbolic-ref HEAD refs/heads/test
$ cat .git/HEAD
ref: refs/heads/test
</code></pre>
<p class="calibre3">You can't set a symbolic reference outside of the refs style:</p>
<pre class="calibre9"><code class="calibre10">$ git symbolic-ref HEAD test
fatal: Refusing to point HEAD outside of refs/
</code></pre>
<h3 class="calibre5">Tags</h3>
<p class="calibre3">You've just gone over Git's three main object types, but there is a fourth. The tag object is very much like a commit object - it contains a tagger, a date, a message, and a pointer. The main difference is that a tag object points to a commit rather than a tree. It's like a branch reference, but it never moves - it always points to the same commit but gives it a friendlier name.</p>
<p class="calibre3">As discussed in Chapter 2, there are two types of tags: annotated and lightweight. You can make a lightweight tag by running something like this:</p>
<pre class="calibre9"><code class="calibre10">$ git update-ref refs/tags/v1.0 cac0cab538b970a37ea1e769cbbde608743bc96d
</code></pre>
<p class="calibre3">That is all a lightweight tag is - a branch that never moves. An annotated tag is more complex, however. If you create an annotated tag, Git creates a tag object and then writes a reference to point to it rather than directly to the commit. You can see this by creating an annotated tag (<code class="calibre10">-a</code> specifies that it's an annotated tag):</p>
<pre class="calibre9"><code class="calibre10">$ git tag -a v1.1 1a410efbd13591db07496601ebc7a059dd55cfe9 -m 'test tag'
</code></pre>
<p class="calibre3">Here's the object SHA-1 value it created:</p>
<pre class="calibre9"><code class="calibre10">$ cat .git/refs/tags/v1.1
9585191f37f7b0fb9444f35a9bf50de191beadc2
</code></pre>
<p class="calibre3">Now, run the <code class="calibre10">cat-file</code> command on that SHA-1 value:</p>
<pre class="calibre9"><code class="calibre10">$ git cat-file -p 9585191f37f7b0fb9444f35a9bf50de191beadc2
object 1a410efbd13591db07496601ebc7a059dd55cfe9
type commit
tag v1.1
tagger Scott Chacon <schacon@gmail.com> Sat May 23 16:48:58 2009 -0700
test tag
</code></pre>
<p class="calibre3">Notice that the object entry points to the commit SHA-1 value that you tagged. Also notice that it doesn't need to point to a commit; you can tag any Git object. In the Git source code, for example, the maintainer has added their GPG public key as a blob object and then tagged it. You can view the public key by running</p>
<pre class="calibre9"><code class="calibre10">$ git cat-file blob junio-gpg-pub
</code></pre>
<p class="calibre3">in the Git source code repository. The Linux kernel repository also has a non-commit-pointing tag object - the first tag created points to the initial tree of the import of the source code.</p>
<h3 class="calibre5">Remotes</h3>
<p class="calibre3">The third type of reference that you'll see is a remote reference. If you add a remote and push to it, Git stores the value you last pushed to that remote for each branch in the <code class="calibre10">refs/remotes</code> directory. For instance, you can add a remote called <code class="calibre10">origin</code> and push your <code class="calibre10">master</code> branch to it:</p>
<pre class="calibre9"><code class="calibre10">$ git remote add origin git@github.com:schacon/simplegit-progit.git
$ git push origin master
Counting objects: 11, done.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (7/7), 716 bytes, done.
Total 7 (delta 2), reused 4 (delta 1)
To git@github.com:schacon/simplegit-progit.git
a11bef0..ca82a6d master -> master
</code></pre>
<p class="calibre3">Then, you can see what the <code class="calibre10">master</code> branch on the <code class="calibre10">origin</code> remote was the last time you communicated with the server, by checking the <code class="calibre10">refs/remotes/origin/master</code> file:</p>
<pre class="calibre9"><code class="calibre10">$ cat .git/refs/remotes/origin/master
ca82a6dff817ec66f44342007202690a93763949
</code></pre>
<p class="calibre3">Remote references differ from branches (<code class="calibre10">refs/heads</code> references) mainly in that they can't be checked out. Git moves them around as bookmarks to the last known state of where those branches were on those servers.</p>
</body>
</html>