gittutorial-2.7 (16120B)
- '\" t
- .\" Title: gittutorial-2
- .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author]
- .\" Generator: DocBook XSL Stylesheets v1.79.2 <http://docbook.sf.net/>
- .\" Date: 2025-03-14
- .\" Manual: Git Manual
- .\" Source: Git 2.49.0
- .\" Language: English
- .\"
- .TH "GITTUTORIAL\-2" "7" "2025-03-14" "Git 2\&.49\&.0" "Git Manual"
- .\" -----------------------------------------------------------------
- .\" * Define some portability stuff
- .\" -----------------------------------------------------------------
- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- .\" http://bugs.debian.org/507673
- .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- .ie \n(.g .ds Aq \(aq
- .el .ds Aq '
- .\" -----------------------------------------------------------------
- .\" * set default formatting
- .\" -----------------------------------------------------------------
- .\" disable hyphenation
- .nh
- .\" disable justification (adjust text to left margin only)
- .ad l
- .\" -----------------------------------------------------------------
- .\" * MAIN CONTENT STARTS HERE *
- .\" -----------------------------------------------------------------
- .SH "NAME"
- gittutorial-2 \- A tutorial introduction to Git: part two
- .SH "SYNOPSIS"
- .sp
- .nf
- git *
- .fi
- .SH "DESCRIPTION"
- .sp
- You should work through \fBgittutorial\fR(7) before reading this tutorial\&.
- .sp
- The goal of this tutorial is to introduce two fundamental pieces of Git\(cqs architecture\(em\:the object database and the index file\(em\:and to provide the reader with everything necessary to understand the rest of the Git documentation\&.
- .SH "THE GIT OBJECT DATABASE"
- .sp
- Let\(cqs start a new project and create a small amount of history:
- .sp
- .if n \{\
- .RS 4
- .\}
- .nf
- $ mkdir test\-project
- $ cd test\-project
- $ git init
- Initialized empty Git repository in \&.git/
- $ echo \*(Aqhello world\*(Aq > file\&.txt
- $ git add \&.
- $ git commit \-a \-m "initial commit"
- [master (root\-commit) 54196cc] initial commit
- 1 file changed, 1 insertion(+)
- create mode 100644 file\&.txt
- $ echo \*(Aqhello world!\*(Aq >file\&.txt
- $ git commit \-a \-m "add emphasis"
- [master c4d59f3] add emphasis
- 1 file changed, 1 insertion(+), 1 deletion(\-)
- .fi
- .if n \{\
- .RE
- .\}
- .sp
- What are the 7 digits of hex that Git responded to the commit with?
- .sp
- We saw in part one of the tutorial that commits have names like this\&. It turns out that every object in the Git history is stored under a 40\-digit hex name\&. That name is the SHA\-1 hash of the object\(cqs contents; among other things, this ensures that Git will never store the same data twice (since identical data is given an identical SHA\-1 name), and that the contents of a Git object will never change (since that would change the object\(cqs name as well)\&. The 7 char hex strings here are simply the abbreviation of such 40 character long strings\&. Abbreviations can be used everywhere where the 40 character strings can be used, so long as they are unambiguous\&.
- .sp
- It is expected that the content of the commit object you created while following the example above generates a different SHA\-1 hash than the one shown above because the commit object records the time when it was created and the name of the person performing the commit\&.
- .sp
- We can ask Git about this particular object with the \fBcat\-file\fR command\&. Don\(cqt copy the 40 hex digits from this example but use those from your own version\&. Note that you can shorten it to only a few characters to save yourself typing all 40 hex digits:
- .sp
- .if n \{\
- .RS 4
- .\}
- .nf
- $ git cat\-file \-t 54196cc2
- commit
- $ git cat\-file commit 54196cc2
- tree 92b8b694ffb1675e5975148e1121810081dbdffe
- author J\&. Bruce Fields <bfields@puzzle\&.fieldses\&.org> 1143414668 \-0500
- committer J\&. Bruce Fields <bfields@puzzle\&.fieldses\&.org> 1143414668 \-0500
- initial commit
- .fi
- .if n \{\
- .RE
- .\}
- .sp
- A tree can refer to one or more "blob" objects, each corresponding to a file\&. In addition, a tree can also refer to other tree objects, thus creating a directory hierarchy\&. You can examine the contents of any tree using ls\-tree (remember that a long enough initial portion of the SHA\-1 will also work):
- .sp
- .if n \{\
- .RS 4
- .\}
- .nf
- $ git ls\-tree 92b8b694
- 100644 blob 3b18e512dba79e4c8300dd08aeb37f8e728b8dad file\&.txt
- .fi
- .if n \{\
- .RE
- .\}
- .sp
- Thus we see that this tree has one file in it\&. The SHA\-1 hash is a reference to that file\(cqs data:
- .sp
- .if n \{\
- .RS 4
- .\}
- .nf
- $ git cat\-file \-t 3b18e512
- blob
- .fi
- .if n \{\
- .RE
- .\}
- .sp
- A "blob" is just file data, which we can also examine with cat\-file:
- .sp
- .if n \{\
- .RS 4
- .\}
- .nf
- $ git cat\-file blob 3b18e512
- hello world
- .fi
- .if n \{\
- .RE
- .\}
- .sp
- Note that this is the old file data; so the object that Git named in its response to the initial tree was a tree with a snapshot of the directory state that was recorded by the first commit\&.
- .sp
- All of these objects are stored under their SHA\-1 names inside the Git directory:
- .sp
- .if n \{\
- .RS 4
- .\}
- .nf
- $ find \&.git/objects/
- \&.git/objects/
- \&.git/objects/pack
- \&.git/objects/info
- \&.git/objects/3b
- \&.git/objects/3b/18e512dba79e4c8300dd08aeb37f8e728b8dad
- \&.git/objects/92
- \&.git/objects/92/b8b694ffb1675e5975148e1121810081dbdffe
- \&.git/objects/54
- \&.git/objects/54/196cc2703dc165cbd373a65a4dcf22d50ae7f7
- \&.git/objects/a0
- \&.git/objects/a0/423896973644771497bdc03eb99d5281615b51
- \&.git/objects/d0
- \&.git/objects/d0/492b368b66bdabf2ac1fd8c92b39d3db916e59
- \&.git/objects/c4
- \&.git/objects/c4/d59f390b9cfd4318117afde11d601c1085f241
- .fi
- .if n \{\
- .RE
- .\}
- .sp
- and the contents of these files is just the compressed data plus a header identifying their length and their type\&. The type is either a blob, a tree, a commit, or a tag\&.
- .sp
- The simplest commit to find is the HEAD commit, which we can find from \&.git/HEAD:
- .sp
- .if n \{\
- .RS 4
- .\}
- .nf
- $ cat \&.git/HEAD
- ref: refs/heads/master
- .fi
- .if n \{\
- .RE
- .\}
- .sp
- As you can see, this tells us which branch we\(cqre currently on, and it tells us this by naming a file under the \&.git directory, which itself contains a SHA\-1 name referring to a commit object, which we can examine with cat\-file:
- .sp
- .if n \{\
- .RS 4
- .\}
- .nf
- $ cat \&.git/refs/heads/master
- c4d59f390b9cfd4318117afde11d601c1085f241
- $ git cat\-file \-t c4d59f39
- commit
- $ git cat\-file commit c4d59f39
- tree d0492b368b66bdabf2ac1fd8c92b39d3db916e59
- parent 54196cc2703dc165cbd373a65a4dcf22d50ae7f7
- author J\&. Bruce Fields <bfields@puzzle\&.fieldses\&.org> 1143418702 \-0500
- committer J\&. Bruce Fields <bfields@puzzle\&.fieldses\&.org> 1143418702 \-0500
- add emphasis
- .fi
- .if n \{\
- .RE
- .\}
- .sp
- The "tree" object here refers to the new state of the tree:
- .sp
- .if n \{\
- .RS 4
- .\}
- .nf
- $ git ls\-tree d0492b36
- 100644 blob a0423896973644771497bdc03eb99d5281615b51 file\&.txt
- $ git cat\-file blob a0423896
- hello world!
- .fi
- .if n \{\
- .RE
- .\}
- .sp
- and the "parent" object refers to the previous commit:
- .sp
- .if n \{\
- .RS 4
- .\}
- .nf
- $ git cat\-file commit 54196cc2
- tree 92b8b694ffb1675e5975148e1121810081dbdffe
- author J\&. Bruce Fields <bfields@puzzle\&.fieldses\&.org> 1143414668 \-0500
- committer J\&. Bruce Fields <bfields@puzzle\&.fieldses\&.org> 1143414668 \-0500
- initial commit
- .fi
- .if n \{\
- .RE
- .\}
- .sp
- The tree object is the tree we examined first, and this commit is unusual in that it lacks any parent\&.
- .sp
- Most commits have only one parent, but it is also common for a commit to have multiple parents\&. In that case the commit represents a merge, with the parent references pointing to the heads of the merged branches\&.
- .sp
- Besides blobs, trees, and commits, the only remaining type of object is a "tag", which we won\(cqt discuss here; refer to \fBgit-tag\fR(1) for details\&.
- .sp
- So now we know how Git uses the object database to represent a project\(cqs history:
- .sp
- .RS 4
- .ie n \{\
- \h'-04'\(bu\h'+03'\c
- .\}
- .el \{\
- .sp -1
- .IP \(bu 2.3
- .\}
- "commit" objects refer to "tree" objects representing the snapshot of a directory tree at a particular point in the history, and refer to "parent" commits to show how they\(cqre connected into the project history\&.
- .RE
- .sp
- .RS 4
- .ie n \{\
- \h'-04'\(bu\h'+03'\c
- .\}
- .el \{\
- .sp -1
- .IP \(bu 2.3
- .\}
- "tree" objects represent the state of a single directory, associating directory names to "blob" objects containing file data and "tree" objects containing subdirectory information\&.
- .RE
- .sp
- .RS 4
- .ie n \{\
- \h'-04'\(bu\h'+03'\c
- .\}
- .el \{\
- .sp -1
- .IP \(bu 2.3
- .\}
- "blob" objects contain file data without any other structure\&.
- .RE
- .sp
- .RS 4
- .ie n \{\
- \h'-04'\(bu\h'+03'\c
- .\}
- .el \{\
- .sp -1
- .IP \(bu 2.3
- .\}
- References to commit objects at the head of each branch are stored in files under \&.git/refs/heads/\&.
- .RE
- .sp
- .RS 4
- .ie n \{\
- \h'-04'\(bu\h'+03'\c
- .\}
- .el \{\
- .sp -1
- .IP \(bu 2.3
- .\}
- The name of the current branch is stored in \&.git/HEAD\&.
- .RE
- .sp
- Note, by the way, that lots of commands take a tree as an argument\&. But as we can see above, a tree can be referred to in many different ways\(em\:by the SHA\-1 name for that tree, by the name of a commit that refers to the tree, by the name of a branch whose head refers to that tree, etc\&.\-\-and most such commands can accept any of these names\&.
- .sp
- In command synopses, the word "tree\-ish" is sometimes used to designate such an argument\&.
- .SH "THE INDEX FILE"
- .sp
- The primary tool we\(cqve been using to create commits is \fBgit\-commit\fR \fB\-a\fR, which creates a commit including every change you\(cqve made to your working tree\&. But what if you want to commit changes only to certain files? Or only certain changes to certain files?
- .sp
- If we look at the way commits are created under the cover, we\(cqll see that there are more flexible ways creating commits\&.
- .sp
- Continuing with our test\-project, let\(cqs modify file\&.txt again:
- .sp
- .if n \{\
- .RS 4
- .\}
- .nf
- $ echo "hello world, again" >>file\&.txt
- .fi
- .if n \{\
- .RE
- .\}
- .sp
- but this time instead of immediately making the commit, let\(cqs take an intermediate step, and ask for diffs along the way to keep track of what\(cqs happening:
- .sp
- .if n \{\
- .RS 4
- .\}
- .nf
- $ git diff
- \-\-\- a/file\&.txt
- +++ b/file\&.txt
- @@ \-1 +1,2 @@
- hello world!
- +hello world, again
- $ git add file\&.txt
- $ git diff
- .fi
- .if n \{\
- .RE
- .\}
- .sp
- The last diff is empty, but no new commits have been made, and the head still doesn\(cqt contain the new line:
- .sp
- .if n \{\
- .RS 4
- .\}
- .nf
- $ git diff HEAD
- diff \-\-git a/file\&.txt b/file\&.txt
- index a042389\&.\&.513feba 100644
- \-\-\- a/file\&.txt
- +++ b/file\&.txt
- @@ \-1 +1,2 @@
- hello world!
- +hello world, again
- .fi
- .if n \{\
- .RE
- .\}
- .sp
- So \fIgit diff\fR is comparing against something other than the head\&. The thing that it\(cqs comparing against is actually the index file, which is stored in \&.git/index in a binary format, but whose contents we can examine with ls\-files:
- .sp
- .if n \{\
- .RS 4
- .\}
- .nf
- $ git ls\-files \-\-stage
- 100644 513feba2e53ebbd2532419ded848ba19de88ba00 0 file\&.txt
- $ git cat\-file \-t 513feba2
- blob
- $ git cat\-file blob 513feba2
- hello world!
- hello world, again
- .fi
- .if n \{\
- .RE
- .\}
- .sp
- So what our \fIgit add\fR did was store a new blob and then put a reference to it in the index file\&. If we modify the file again, we\(cqll see that the new modifications are reflected in the \fIgit diff\fR output:
- .sp
- .if n \{\
- .RS 4
- .\}
- .nf
- $ echo \*(Aqagain?\*(Aq >>file\&.txt
- $ git diff
- index 513feba\&.\&.ba3da7b 100644
- \-\-\- a/file\&.txt
- +++ b/file\&.txt
- @@ \-1,2 +1,3 @@
- hello world!
- hello world, again
- +again?
- .fi
- .if n \{\
- .RE
- .\}
- .sp
- With the right arguments, \fIgit diff\fR can also show us the difference between the working directory and the last commit, or between the index and the last commit:
- .sp
- .if n \{\
- .RS 4
- .\}
- .nf
- $ git diff HEAD
- diff \-\-git a/file\&.txt b/file\&.txt
- index a042389\&.\&.ba3da7b 100644
- \-\-\- a/file\&.txt
- +++ b/file\&.txt
- @@ \-1 +1,3 @@
- hello world!
- +hello world, again
- +again?
- $ git diff \-\-cached
- diff \-\-git a/file\&.txt b/file\&.txt
- index a042389\&.\&.513feba 100644
- \-\-\- a/file\&.txt
- +++ b/file\&.txt
- @@ \-1 +1,2 @@
- hello world!
- +hello world, again
- .fi
- .if n \{\
- .RE
- .\}
- .sp
- At any time, we can create a new commit using \fIgit commit\fR (without the "\-a" option), and verify that the state committed only includes the changes stored in the index file, not the additional change that is still only in our working tree:
- .sp
- .if n \{\
- .RS 4
- .\}
- .nf
- $ git commit \-m "repeat"
- $ git diff HEAD
- diff \-\-git a/file\&.txt b/file\&.txt
- index 513feba\&.\&.ba3da7b 100644
- \-\-\- a/file\&.txt
- +++ b/file\&.txt
- @@ \-1,2 +1,3 @@
- hello world!
- hello world, again
- +again?
- .fi
- .if n \{\
- .RE
- .\}
- .sp
- So by default \fIgit commit\fR uses the index to create the commit, not the working tree; the "\-a" option to commit tells it to first update the index with all changes in the working tree\&.
- .sp
- Finally, it\(cqs worth looking at the effect of \fIgit add\fR on the index file:
- .sp
- .if n \{\
- .RS 4
- .\}
- .nf
- $ echo "goodbye, world" >closing\&.txt
- $ git add closing\&.txt
- .fi
- .if n \{\
- .RE
- .\}
- .sp
- The effect of the \fIgit add\fR was to add one entry to the index file:
- .sp
- .if n \{\
- .RS 4
- .\}
- .nf
- $ git ls\-files \-\-stage
- 100644 8b9743b20d4b15be3955fc8d5cd2b09cd2336138 0 closing\&.txt
- 100644 513feba2e53ebbd2532419ded848ba19de88ba00 0 file\&.txt
- .fi
- .if n \{\
- .RE
- .\}
- .sp
- And, as you can see with cat\-file, this new entry refers to the current contents of the file:
- .sp
- .if n \{\
- .RS 4
- .\}
- .nf
- $ git cat\-file blob 8b9743b2
- goodbye, world
- .fi
- .if n \{\
- .RE
- .\}
- .sp
- The "status" command is a useful way to get a quick summary of the situation:
- .sp
- .if n \{\
- .RS 4
- .\}
- .nf
- $ git status
- On branch master
- Changes to be committed:
- (use "git restore \-\-staged <file>\&.\&.\&." to unstage)
- new file: closing\&.txt
- Changes not staged for commit:
- (use "git add <file>\&.\&.\&." to update what will be committed)
- (use "git restore <file>\&.\&.\&." to discard changes in working directory)
- modified: file\&.txt
- .fi
- .if n \{\
- .RE
- .\}
- .sp
- Since the current state of closing\&.txt is cached in the index file, it is listed as "Changes to be committed"\&. Since file\&.txt has changes in the working directory that aren\(cqt reflected in the index, it is marked "changed but not updated"\&. At this point, running "git commit" would create a commit that added closing\&.txt (with its new contents), but that didn\(cqt modify file\&.txt\&.
- .sp
- Also, note that a bare \fBgit\fR \fBdiff\fR shows the changes to file\&.txt, but not the addition of closing\&.txt, because the version of closing\&.txt in the index file is identical to the one in the working directory\&.
- .sp
- In addition to being the staging area for new commits, the index file is also populated from the object database when checking out a branch, and is used to hold the trees involved in a merge operation\&. See \fBgitcore-tutorial\fR(7) and the relevant man pages for details\&.
- .SH "WHAT NEXT?"
- .sp
- At this point you should know everything necessary to read the man pages for any of the git commands; one good place to start would be with the commands mentioned in \fBgiteveryday\fR(7)\&. You should be able to find any unknown jargon in \fBgitglossary\fR(7)\&.
- .sp
- The \m[blue]\fBGit User\(cqs Manual\fR\m[]\&\s-2\u[1]\d\s+2 provides a more comprehensive introduction to Git\&.
- .sp
- \fBgitcvs-migration\fR(7) explains how to import a CVS repository into Git, and shows how to use Git in a CVS\-like way\&.
- .sp
- For some interesting examples of Git use, see the \m[blue]\fBhowtos\fR\m[]\&\s-2\u[2]\d\s+2\&.
- .sp
- For Git developers, \fBgitcore-tutorial\fR(7) goes into detail on the lower\-level Git mechanisms involved in, for example, creating a new commit\&.
- .SH "SEE ALSO"
- .sp
- \fBgittutorial\fR(7), \fBgitcvs-migration\fR(7), \fBgitcore-tutorial\fR(7), \fBgitglossary\fR(7), \fBgit-help\fR(1), \fBgiteveryday\fR(7), \m[blue]\fBThe Git User\(cqs Manual\fR\m[]\&\s-2\u[1]\d\s+2
- .SH "GIT"
- .sp
- Part of the \fBgit\fR(1) suite
- .SH "NOTES"
- .IP " 1." 4
- Git User\(cqs Manual
- .RS 4
- \%git-htmldocs/user-manual.html
- .RE
- .IP " 2." 4
- howtos
- .RS 4
- \%git-htmldocs/howto-index.html
- .RE