Learn Enough Git to Be Dangerous
An introduction to version control with Git
Michael Hartl
Contents
About the author
Michael Hartl is the creator of the Ruby on Rails Tutorial, one of the leading introductions to web development, and is cofounder and principal author at Learn Enough. Previously, he was a physics instructor at the California Institute of Technology (Caltech), where he received a Lifetime Achievement Award for Excellence in Teaching. He is a graduate of Harvard College, has a Ph.D. in Physics from Caltech, and is an alumnus of the Y Combinator entrepreneur program.
Chapter 1 Getting started with Git
Learn Enough Git to Be Dangerous is the final installment in a trilogy of tutorials on Developer Fundamentals designed to teach three skills essential for software developers and those who work with them. Its only prerequisites are the first two tutorials in the trilogy, Learn Enough Command Line to Be Dangerous (covering the Unix command line) and Learn Enough Text Editor to Be Dangerous (covering text editors).
This tutorial covers a third essential skill: version control. As with its two predecessors, Learn Enough Git to Be Dangerous doesn’t even assume you’re familiar with the category of application, so if you’re unsure about what “version control” is, you’re in the right place. Even if you are already familiar with the subject, it’s likely you’ll still learn a lot from this tutorial. Either way, learning the material in Learn Enough Git to Be Dangerous prepares you for the other Learn Enough tutorials while enabling an astonishing variety of applications—including a special surprise bonus at the end (Box 1.1).
As legendary Apple cofounder Steve Jobs once said: Real artists ship. What he meant was that, as tempting as it is to privately polish in perpetuity, makers must ship their work—that is, actually finish it and get it out into the world. This can be scary, because shipping means exposing your work not only to fans but also to critics. “What if people don’t like what I’ve made?” Real artists ship.
It’s important to understand that shipping is a separate skill from making. Many makers get good at making things but never learn to ship. To keep this from happening to us, starting in Learn Enough Git to Be Dangerous we’re going to ship at least one thing in every Learn Enough tutorial. In fact, in this tutorial we’ll actually ship two things—a public Git repository and a surprise bonus that will give you bragging rights with all of your friends.
Version control solves a problem that might look familiar if you’ve ever seen Word documents or Excel spreadsheets with names like Report_2014_1.doc
, Report_2014_2.doc
, Report_2014_3.doc
, or budget-v7.xls
. These cumbersome names indicate how annoying it can be to track different versions of documents. Nowadays, applications like Word do sometimes offer built-in version tracking, but such features are tightly coupled to the underlying application, and aren’t useful for any other document types. Many technical applications (including most websites and programming projects) require a general solution to the problem of versions.
A version control system, or VCS, provides an automatic way to track changes in software projects, giving creators the power to view previous versions of files and directories, develop speculative features without disrupting the main development, securely back up the project and its history, and collaborate easily and conveniently with others. In addition, using version control makes deploying production websites and web applications much easier. As a result, fluency in at least one version control system is an essential component of technical sophistication (Box 1.2), a useful skill for developer, designer, and manager alike. This applies especially to the version control system covered in this tutorial, called Git.
A principal theme of the Learn Enough tutorials is the development of technical sophistication, the combination of hard and soft skills that make it seem like you can magically solve any technical problem (as illustrated in “Tech Support Cheat Sheet” from xkcd). Learn Enough Git to Be Dangerous is important for developing these skills because being able to use at least one modern version control system is an essential component of technical sophistication.
In the context of Git, technical sophistication includes several things. Many Git commands print various details to the terminal screen; technical sophistication lets you figure out which ones to pay attention to and which to ignore. There are also many Git-related resources on the Web, which among other things means that Google searches are often useful for figuring out the exact command you need at a particular time. Technical sophistication lets you figure out the best search terms for finding the answer you’re looking for; e.g., if you need to delete a remote branch (Section 4.3.1), Googling for “git delete remote branch” is a good bet to turn up something useful. Finally, repository hosting sites like GitHub, GitLab, and Bitbucket typically include commands to help guide you through various setup tasks, and technical sophistication gives you the confidence to follow the steps even if you don’t understand every detail.
One helpful command for learning Git is git help
, which by itself gives general guidelines on Git usage, and when applied to a specific command gives further information on that command. For example, git help add
shows details about the git add
command. The output of git help
is similar to the man pages covered in Learn Enough Command Line to Be Dangerous: full of useful but often obscure information. As always, use your technical sophistication to help make sense of it.
Version control has evolved considerably over the years. The family line leading to Git includes programs called RCS, CVS, and Subversion, and there are many current alternatives as well, including Perforce, Bazaar, and Mercurial. I mention these examples not because you need to know what they are, but only to show what a bewildering variety there is. What’s worse, when you choose a version control system, you really commit to it,1 and it is often difficult to switch from one to another. Happily, in the last few years an undisputed winner has emerged in the open-source VCS wars: Git. This victory is the main reason this tutorial is called Learn Enough Git to Be Dangerous rather than Learn Enough Version Control to Be Dangerous. Nevertheless, many of the ideas here are quite general, and if by some chance you need to use a different VCS, this tutorial will still provide a useful introduction to the subject.
Originally developed by Linux creator Linus Torvalds2 to host the Linux kernel, Git is a command-line program that is designed in the Unix tradition (which is why a familiarity with the Unix command line is an important prerequisite). Git has a combination of power, speed, and community adoption that leave it few rivals, but it can be tricky to learn, and other Git tutorials have a tendency to introduce lots of heavy theory, which can be interesting to learn but in practice is really only understood by a tiny handful of Git users (as illustrated in “Git” via the webcomic xkcd). The good news is that the set of Git commands needed to be productive is relatively small; there are some pointers to more advanced and theory-oriented resources listed in Section 4.7, but in this tutorial we focus on the essential commands needed to be dangerous.
Note: If you’re using macOS, you should follow the instructions in Box 1.3 at this time.
If you’re using macOS, at this point you should make sure you’re using the right shell program for this tutorial. The default shell as of macOS Catalina is Z shell (Zsh), but to get results consistent with this tutorial you should switch to the shell known as Bash.
The first step is to determine which shell your system is running, which you can do using the echo
command:
$ echo $SHELL /bin/bash
This prints out the $SHELL
environment variable. If you see the result shown above, indicating that you’re already using Bash, you’re done and can proceed with the rest of the tutorial. (In rare cases, $SHELL
may differ from the current shell, but the procedure below will still correctly change from one shell to another.) For more information, including how to switch to and use Z shell with this tutorial, see the Learn Enough blog post “Using Z Shell on Macs with the Learn Enough Tutorials”.
The other possible result of echo
is this:
$ echo $SHELL /bin/zsh
If that’s the result you get, you should use the chsh
(“change shell”) command as follows:
$ chsh -s /bin/bash
You’ll almost certainly be prompted to type your system password at this point, which you should do. Then completely exit your shell program using Command-Q and relaunch it.
You can confirm that the change succeeded using echo
:
$ echo $SHELL /bin/bash
At this point, you will probably start seeing the following alert, which you should ignore:
The default interactive shell is now zsh. To update your account to use zsh, please run `chsh -s /bin/zsh`. For more details, please visit https://support.apple.com/kb/HT208050. [~]$
Note that the procedure above is entirely reversible, so there is no need to be concerned about damaging your system. See “Using Z Shell on Macs with the Learn Enough Tutorials” for more information.
1.1 Installation and setup
The most common way to use Git is via a command-line program called git
, which lets us transform an ordinary Unix directory into a repository (or repo for short) that enables us to track changes to our project.3 In this section, we’ll begin by installing Git (if necessary) and doing some one-time configuration.
Before doing anything else, we first need to check to see if Git is installed on the current system. As a reminder, we’re working in the Unix tradition, so it is strongly recommended that you use macOS or Linux. Microsoft Windows users are encouraged to set up a Linux-compatible development environment by following the Windows steps in Learn Enough Dev Environment to Be Dangerous or use a Linux-based cloud IDE (which is also covered in Learn Enough Dev Environment to Be Dangerous).
The easiest way to check for Git is to start a terminal window and use which
4 at the command line to see if the git
executable is already present:
$ which git
/usr/local/bin/git
If the result is empty or if it says the command is not found, it means you have to install Git manually. To do this, follow the instructions at “Getting Started – Installing Git” in the official Git documentation. (This will likely give you an opportunity to apply some technical sophistication (Box 1.2).)
The next step is to ensure that you have a sufficiently recent version of Git, which you can check as follows:
$ git --version
git version 2.31.1 # should be at least 2.28.0
If the version isn’t recent enough, then you should either install Git via the instructions at “Getting Started – Installing Git” or follow one of the suggestions below:
- Cloud IDE: If using the cloud IDE recommended in Learn Enough Dev Environment to Be Dangerous, run the command in Listing 1.1.
- macOS: If using macOS, run the command in Listing 1.2. (If you don’t have Homebrew installed, first follow the instructions to install Homebrew.)
$ source <(curl -sL https://cdn.learnenough.com/upgrade_git)
$ brew upgrade git
After installing Git but before starting a project, we need to perform a few one-time setup steps, as shown in Listing 1.3. These are global setups, meaning you only have to do them once per computer. (Don’t worry about the meaning or structure of these commands at this stage.)
$ git config --global user.name "Your Name"
$ git config --global user.email your.email@example.com
$ git config --global init.defaultBranch main
The first two configuration settings allow Git to identify changes in your projects by name and email address, which is especially helpful when collaborating with others (Chapter 4). Note that the name and email you use in Listing 1.3 will be viewable in any projects you make public, so don’t expose any information you’d rather keep private.
The third line in Listing 1.3 sets the default Git branch name to main
, which is the current recommended default. You should be aware that the default branch name for the first 15+ years of Git’s existence was master
, so you will invariably encounter many Git repositories that use master
instead of main
.5 (Being able to deal with this sort of situation is a hallmark of technical sophistication (Box 1.2).) See the Learn Enough blog post “Default Git Branch Name with Learn Enough and the Rails Tutorial” for more information.
In addition to the configuration in Listing 1.3, Learn Enough Git to Be Dangerous includes some optional advanced setup (Section 4.6) that I recommend you complete at some point. If you already have some familiarity with Git, or if you’re an experienced user of the Unix command line, I recommend completing the steps in Section 4.6 at this time, but otherwise I recommend deferring the advanced setup until later.
1.1.1 Exercises
- Run
git help
at the command line. What is the first command listed? - There’s a chance that the full output of
git help
was too big to fit in your terminal, with most of it just scrolling by. What’s the command to let us navigate the output ofgit help
interactively? (On some systems, you can use the mouse to scroll back in the terminal window, but it’s unwise to rely on this fact.) Hint: Pipe the output toless
. - Git stores global configuration settings in a hidden text file located in your home directory. By inspecting the file
~/.gitconfig
with a tool of your choice (cat
,less
, a text editor, etc.), confirm that the configuration set up by Listing 1.3 corresponds to simple text entries in this file.
1.2 Initializing the repo
Now it’s time to start creating a project and put it under version control with Git. To see how Git works and what benefits it brings, it helps to have a concrete application in mind, so we’ll be tracking changes in a simple project consisting of a small website consisting of two pages, a Home page and an About page.6 We’ll begin by making a directory with the generic name website
inside a repositories directory called repos
:
[~]$ mkdir -p repos/website
Here we’ve used the “make directory” command mkdir
covered in Learn Enough Command Line to Be Dangerous, together with the -p
option, which arranges for mkdir
to create intermediate directories as required (in this case, repos
). Note also that I’ve included the current directory in the prompt (in this case, [~]
) as arranged by the configuration in Listing 4.15.
After making the directory, we can cd
into it as follows:
[~]$ cd repos/website/
[website]$
(Recall that you can use tab completion when changing directories, so in real life I would probably type something like cd re⇥w⇥
.)
Even though the website
directory is empty, we can already convert it to a repository, which you can think of as a sort of enhanced Unix directory with the additional ability to track changes to every file and subdirectory. The way to create a new repository with Git is with the init
command (short for “initialize”), which creates a special hidden directory called .git
where Git stores the information it needs to track our project’s changes. (It’s the presence of a properly configured .git
directory that distinguishes a Git repository from a regular directory.)
All Git commands consist of the command-line program git
followed by the name of the command, so the full command to initialize a repository is git init
, as shown in Listing 1.4.
[website]$ git init
Initialized empty Git repository in /Users/mhartl/repos/website/.git/
[website (main)]$
The prompt shown in Listing 1.4 reflects both the Bash customization from Learn Enough Text Editor to Be Dangerous and the advanced setup in Section 4.6.2, so your prompt may differ.7 In particular, the prompt in the highlighted line in Listing 1.4 shows the name of the current Git branch, called main
. Don’t worry about what this means now; we’ll discuss branches starting in Section 3.3.
1.2.1 Exercises
- By running
ls -a
(discussed in Learn Enough Command Line to Be Dangerous), list all the files and directories in yourwebsite
directory. What is the name of the hidden directory used by the Git repository? (There is one such hidden directory per project.) - Using the result of the previous exercise, run
ls
on the hidden directory and guess the name of the main Git configuration file. Usecat
to dump its contents to the screen.
1.3 Our first commit
Git won’t let us complete the initialization of the repository while it’s empty, so we need to make a change to the current directory. We’ll make a more substantive change in a moment, but for now we’ll follow a common convention and simply use touch
to create an empty file (as mentioned in Learn Enough Command Line to Be Dangerous). In this case, we’re making a simple website, and the near-universal convention is to call the main page index.html
:
[website (main)]$ touch index.html
Having created this first file, we can use the git status
command to see the result:
[website (main)]$ git status
On branch main
No commits yet
Untracked files:
(use "git add <file>..." to include in what will be committed)
index.html
nothing added to commit but untracked files present (use "git add" to track)
We see here that the index.html
file is “untracked”, which means Git doesn’t yet know about it. We can add it using the git add
command:
[website (main)]$ git add -A
Here the -A
option tells Git to add all untracked files, even though in this case there’s only one. In my experience, 99% of the time you add files you’ll want to add them all, so this is a good habit to cultivate, and learning how to add individual files is left as an exercise (Section 1.3.1). (By the way, the nearly equivalent command git add .
, where the dot refers to the current directory, is also common.)8
We can see the result of git add -A
by running git status
again:
[website (main)]$ git status
On branch main
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: index.html
As implied by the word “unstage”, the status of the file has been promoted from untracked to staged, which means the file is ready to be added to the repository. Untracked/unstaged and staged are two of the four states commonly used by Git, as shown in Figure 1.1. (Technically, untracked and unstaged are different states, but the distinction is rarely important because git add
tracks and stages files at the same time.)

As shown in Figure 1.1, after putting changes in the staging area we can make them part of the local repository by committing them using git commit
. (We’ll cover the final step from Figure 1.1, git push
, in Section 2.3.) Most uses of git commit
use the command-line option -m
to include a message indicating the purpose of the commit (Box 1.4). In this case, the purpose is to initialize the new repository, which we can indicate as follows:
[website (main)]$ git commit -m "Initialize repository"
[main (root-commit) 44c52d4] Initialize repository
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 index.html
(I’ve shown my output here for completeness, but your details will vary.)
By design, Git requires every commit to include a commit message describing the purpose of the commit. Typically, this takes the form of a single line, usually limited to around 72 characters, with an optional longer message if desired (Section 4.2.3). Although conventions for commit messages vary (as humorously depicted in the xkcd comic strip “Git Commit”), the style adopted in this tutorial is to write commit messages in the present tense using the imperative mood, as in “Initialize repository” rather than “Initializes repository” or “Initialized repository”. The reason for this convention is that Git models commits as a series of text transformations, and in this context it makes sense to describe what each commit does instead of what it did. Moreover, this usage agrees with the convention followed by the commit messages generated by Git commands themselves (e.g., “merge” rather than “merges” or “merged”). For more information, see the GitHub article “Shiny new commit styles”.
At this point, we can use git log
to see a record of our commit:
[website (main)]$ git log
commit 44c52d432d294ef52bae5535dc6dcb0993175a04 (HEAD -> main)
Author: Michael Hartl <michael@michaelhartl.com>
Date: Thu Apr 1 10:30:38 2021 -0700
Initialize repository
The commit is identified by a hash, which is a unique string of letters and numbers that Git uses to label the commit and which lets Git retrieve the commit’s changes. In my case, the hash appears as
44c52d432d294ef52bae5535dc6dcb0993175a04
but since each hash is unique your result will differ. The hash is often referred to as a “SHA” (pronounced shah) because of the acronym for the Secure Hash Algorithm used to generate it. We’ll put these SHAs to use in Section 3.4, and several more advanced Git operations require them as well.
1.3.1 Exercises
- Using the
touch
command, create empty files calledfoo
andbar
in your repository directory. - By using
git add foo
, addfoo
to the staging area. Confirm withgit status
that it worked. - Using
git commit -m
and an appropriate message, addfoo
to the repository. - By using
git add bar
, addbar
to the staging area. Confirm withgit status
that it worked. - Now run
git commit
without the-m
option. Use your Vim knowledge to add the message “Add bar”, save, and quit. - Using
git log
, confirm that the commits made in the previous exercises worked correctly.
1.4 Viewing the diff
It’s often useful to be able to view the changes represented by a potential commit before making it. To see how this works, let’s add a little bit of content to index.html
by redirecting the output of echo
to make a “hello, world” page:
[website (main)]$ echo "hello, world" > index.html
Recall from Learn Enough Command Line to Be Dangerous that the Unix diff
utility lets us compare two files foo
and bar
by typing
$ diff foo bar
Git has a similar function, git diff
, which by default just shows the difference between the last commit and unstaged changes in the current project:9
[website (main)]$ git diff
diff --git a/index.html b/index.html
index e69de29..4b5fa63 100644
--- a/index.html
+++ b/index.html
@@ -0,0 +1 @@
+hello, world
Because the content added in Section 1.3 was empty, here the diff appears simply as an addition:
+hello, world
We can commit this change by passing the -a
option (for “all”) to git commit
, which arranges to commit all the changes in currently existing files (Listing 1.5).
[website (main)]$ git commit -a -m "Add content to index.html"
[main 64f6529] Add content to index.html
1 file changed, 1 insertion(+)
Note that the -a
option includes changes only to files already added to the repository, so when there are new files it’s important to run git add -A
as in Section 1.3 to make sure they’re added properly. It’s easy to get in the habit of running git commit -a
and forget to add new files explicitly; learning how to deal with this situation is left as an exercise (Section 1.4.1).
Having added and committed the changes, there’s now no diff:
[website (main)]$ git diff
[website (main)]$
(In fact, simply adding the changes is sufficient; running git add -A
would also lead to there being no diff. To see the difference between staged changes and the previous version of the repo, use git diff --staged
.)
We can confirm that the change went through by running git log
:
[website (main)]$ git log
commit 64f6529494cb0e193f05b0da75702feef854e176
Author: Michael Hartl <michael@michaelhartl.com>
Date: Thu Apr 1 10:33:24 2021 -0700
Add content to index.html
commit 44c52d432d294ef52bae5535dc6dcb0993175a04
Author: Michael Hartl <michael@michaelhartl.com>
Date: Thu Apr 1 10:30:38 2021 -0700
Initialize repository
1.4.1 Exercises
- Use
touch
to create an empty file calledbaz
. What happens if you rungit commit -am "Add baz"
? - Add
baz
to the staging area usinggit add -A
, then commit with the message"Add bazz"
. - Realizing there’s a typo in your commit message, change
bazz
tobaz
usinggit commit --amend
. - Run
git log
to get the SHA of the last commit, then view the diff usinggit show <SHA>
to verify that the message was amended properly.
1.5 Adding an HTML tag
We’ve now seen all of the major elements involved in the simplest Git workflow, so in this section and the next we’ll review what we’ve done and see how everything fits together. We’ll err on the side of making more frequent commits, representing relatively modest changes, but this isn’t necessarily how you should work in real life (Box 1.5). Still, it’s an excellent foundation, and it will give you a solid base on which to build your own workflow and development practices.
One common issue when learning Git involves figuring out when to make a commit. Unfortunately, there’s no simple answer, and real-life usage varies considerably (as illustrated in the xkcd comic strip “Git Commit”). My best advice is to make a commit whenever you’ve reached a natural stopping point, or when you’ve made enough changes that you’re starting to worry about losing them. In practice, this can lead to inconsistent results, and it’s common to work for a while and make a large commit and then make a minor unrelated change with a small commit. This mismatch between commit sizes can seem a little weird, but it’s a difficult situation to avoid.
Many teams (including most open-source projects) have their own conventions for commits, including the practice of squashing commits to combine them all into one commit for convenience. (Per Box 1.2, this is exactly the kind of thing you can learn about by Googling for it.) In these circumstances, I recommend following the conventions adopted by the project in question.
More than anything, don’t worry about it too much. “Git Commit” is a only slight exaggeration, and in any case deciding when to commit is the kind of thing that you’ll invariably get better at with time and experience.
As in previous sections, we’ll be working on the main index.html
file. Let’s start by opening this file in both a text editor and a web browser. My preferred method for doing this is at the command line using the atom
and open
commands (though the latter works only on macOS):
[website (main)]$ atom index.html
[website (main)]$ open index.html # only on macOS; otherwise, use a GUI
If you’re not on a Mac (or even if you are), you can open the directory using a graphical file browser and double-clicking the file to open it in the default browser (Figure 1.2). However you open the file, the results should appear approximately as shown in Figure 1.3 and Figure 1.4.

index.html
in a filesystem browser.


At this point, we’re ready to make a change, which is to promote “hello, world” from ordinary text to a top-level (Level 1) heading. In HTML, the language of the World Wide Web, the way to do this is with a tag—in this case, the Level 1 header tag h1
. Most browsers set h1
tags in a large font, so the text hello, world
should look bigger when we’re done. To make the change, replace the current contents of index.html
with the contents shown in Listing 1.6. (In this and all other examples of editing text, you’ll learn more if you type in everything by hand instead of copying and pasting.)
<h1>hello, world</h1>
Listing 1.6 shows the basic structure used by most HTML tags. First, there’s an opening tag that looks like <h1>
, where the angle brackets <
and >
surround the tag name (in this case, h1
). After the content, there’s a closing tag that’s the same as the opening tag, except with an extra slash after the opening angle bracket: </h1>
. (Note that, as with addresses on the World Wide Web, this is a slash, not a backslash (a common confusion humorously referenced in the xkcd comic strip “Trade expert”).)
Upon refreshing the web browser, the index page should appear something like Figure 1.5. As promised, the font size of the text for the top-level heading is bigger (and bolder, too).

h1
tag.
As before, we’ll run git status
and git diff
to learn more about what we’re going to commit to Git, though with experience you’ll come to run these commands only when necessary. The status simply indicates that index.html
has been modified:
[website (main)]$ git status
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: index.html
no changes added to commit (use "git add" and/or "git commit -a")
Meanwhile, the diff shows that one line has been deleted (indicated with -
) and another added (indicated with +
):
[website (main)]$ git diff
diff --git a/index.html b/index.html
index 4b5fa63..45d754a 100644
--- a/index.html
+++ b/index.html
@@ -1 +1 @@
-hello, world
+<h1>hello, world</h1>
As with the Unix diff
utility, modified sections of code or markup are shown as close to each other as possible so that it’s clear at a glance what changed.10
At this point, we’re ready to commit our changes. In Listing 1.5 we used both the -a
and -m
options to commit all pending changes while adding a commit message, but in fact the two can be combined as -am
(Listing 1.7).
-am
.
[website (main)]$ git commit -am "Add an h1 tag"
Using the -am
combination as in Listing 1.7 is common in idiomatic Git usage.
1.5.1 Exercises
- The
git log
command shows only the commit messages, which makes for a compact display but isn’t particularly detailed. Verify by runninggit log -p
that the-p
option shows the full diffs represented by each commit. - Under the
h1
tag in Listing 1.6, use thep
tag to add a paragraph consisting of the line “Call me Ishmael.” The result should appear as in Figure 1.6. (Don’t worry if you get stuck; we’ll incorporate the answer to this exercise in Section 1.6 (Listing 1.8).)

1.6 Adding HTML structure
Although the web browser correctly rendered the h1
tag in Figure 1.5, properly formatted HTML pages have more structure than just bare h1
or p
tags. In particular, each page should have an html
tag consisting of a head and a body (identified with head
and body
tags, respectively), as well as a “doctype” identifying the document type, which in this case is a particular version of HTML called HTML5. (Don’t worry about these details now; we’ll cover them in more depth in Learn Enough HTML to Be Dangerous.)
Applying these general considerations to index.html
leads to the full HTML structure shown in Listing 1.8. This includes the h1
tag from Listing 1.6 and the paragraph tag from Figure 1.6. (The title
tag, included inside the head
tag, is empty, but in general every page should have a title, and adding one for index.html
is left as an exercise (Section 1.6.1).)
1<!DOCTYPE html>
2<html>
3 <head>
4 <title></title>
5 </head>
6 <body>
7 <h1>hello, world</h1>
8 <p>Call me Ishmael.</p>
9 </body>
10</html>
Because this is a lot more content than our previous iteration (Listing 1.6), it’s a good idea to go through it line by line:
- The document type declaration
- Opening
html
tag - Opening
head
tag - Opening and closing
title
tags - Closing
head
tag - Opening
body
tag - Top-level heading
- Paragraph from the exercises (Section 1.5.1)
- Closing
body
tag - Closing
html
tag
As usual, we can see the changes represented by our addition using git diff
(Listing 1.9).
[website (main)]$ git diff
diff --git a/index.html b/index.html
index 4b5fa63..afcd202 100644
--- a/index.html
+++ b/index.html
@@ -1 +1,10 @@
-<h1>hello, world</h1>
+<!DOCTYPE html>
+<html>
+ <head>
+ <title></title>
+ </head>
+ <body>
+ <h1>hello, world</h1>
+ <p>Call me Ishmael.</p>
+ </body>
+</html>
Despite the extensive diffs in Listing 1.9, there are hardly any user-visible differences (Figure 1.7); the only change from Figure 1.6 is a small amount of space above the top-level heading. The structure is much better, though, and brings our page nearly into compliance with the HTML5 standard. (It’s not quite valid, because a nonblank page title is required by the standard; fixing this issue is left as an exercise (Section 1.6.1).)

Since we haven’t added any files, using git commit -am
suffices to commit all the changes (Listing 1.10).
[website (main)]$ git commit -am "Add some HTML structure"
1.6.1 Exercises
- Add the title “A whale of a greeting” to
index.html
. Browsers differ in how they display titles; the result in Safari is shown in Figure 1.8. (As of this writing, Safari doesn’t display the title unless there are at least two tabs, which is why there’s a second tab in Figure 1.8.) - Commit the new title with a commit message of your choice. Verify using
git log -p
that the change was committed as expected. - By pasting the contents of Listing 1.8 into an HTML validator, verify that it is not (quite) a valid web page.
- Using the validator, verify that the current
index.html
(with nonblank page title) is valid.

1.7 Summary
Important commands from this section are summarized in Table 1.1.
Command | Description | Example |
git help |
Get help on a command | $ git help push |
git config |
Configure Git | $ git config --global … |
mkdir -p |
Make intermediate directories as necessary | $ mkdir -p repos/website |
git status |
Show the status of the repository | $ git status |
touch <name> |
Create empty file | $ touch foo |
git add -A |
Add all files or directories to staging area | $ git add -A |
git add <name> |
Add given file or directory to staging area | $ git add foo |
git commit -m |
Commit staged changes with a message | $ git commit -m "Add thing" |
git commit -am |
Stage and commit changes with a message | $ git commit -am "Add thing" |
git diff |
Show diffs between commits, branches, etc. | $ git diff |
git commit --amend |
Amend the last commit | $ git commit --amend |
git show <SHA> |
Show diff vs. the SHA | $ git show fb738e… |