Computing and the Command Line

Staging and Committing Well

Good history is made of small commits, each one logical change, with messages that say what it does. Stage part of a file with git add -p, check for whitespace mistakes with git diff --check, write a summary line of about 50 characters in the imperative with a wrapped body, and move or delete tracked files with git mv and git rm.

  • 5 min
  • 7 steps
  • 2 questions
  • Lesson 28 of 80

In this lesson

  1. Small, logical commits
  2. Staging part of a file
  3. Writing the message
  4. Checking before you commit
  5. Moving and deleting files
  6. Your turn
  7. So

Small, logical commits

The history you build is something you’ll read later, to find when something changed, to undo one change without losing others, or to understand why. That works best when each commit holds one logical change: a new feature, a fix, a rearrangement, but not all three at once 1. If you’ve done several things since your last commit, commit them separately.

The staging area is what makes that possible: you choose exactly what goes into each commit.

Staging part of a file

git add -p (“patch”) walks through your changes one hunk (a block of changed lines) at a time and asks about each 1:

me@linuxbox:~/garden$ git add -p
diff --git a/README.md b/README.md
@@ -1 +1,3 @@
 # Garden plan
+
+Four raised beds, south side.
(1/1) Stage this hunk [y,n,q,a,d,e,p,P,?]? y
diff --git a/beds.txt b/beds.txt
@@ -2,3 +2,4 @@ tomatoes
 beans
 carrots
 peas
+lettuce
(1/1) Stage this hunk [y,n,q,a,d,e,p,P,?]? n
me@linuxbox:~/garden$ git status -s
M  README.md
 M beds.txt

The answers you’ll use: y stage this hunk, n skip it, s split it into smaller hunks, e edit it by hand, q quit, and ? for the rest 1. Now git commit saves only the README change; the lettuce waits for its own commit.

git add -p is also a good review: you read every change before it’s committed. Many people use it instead of git add all the time.

Left, a git add -p session: a hunk adding a paragraph to README.md, answered y to stage it, then a hunk adding lettuce to beds.txt, answered n to skip it; git status -s then shows M in the first column for README.md, staged, and M in the second column for beds.txt, not staged. Keys: y stage, n skip, s split, e edit, q quit, ? help. Right, a good commit message: the summary line Describe the beds in the README; a blank line; a body explaining the four raised beds and where the sizes come from; and two bullet points. Notes: summary about 50 characters or fewer, capitalized, imperative; the blank line matters; body says what and why, wrapped at about 72. Fix bug, not Fixed bug or Fixes bug. One logical change per commit.
Stage exactly one change, then describe it in the imperative. Credit: StudyCorner diagram · CC BY 4.0 · Source

Quick check

You fixed a typo and added a new section in the same file. How do you commit them separately?

Writing the message

Pro Git’s guidelines, adapted from a widely used template 1:

  • A summary line of about 50 characters or fewer, capitalized, with no period.
  • Written in the imperative: “Fix bug,” not “Fixed bug” or “Fixes bug.” That matches the messages git itself writes, like “Merge branch…” and “Revert…”.
  • A blank line, then, if it’s needed, a body wrapped at about 72 characters, saying what changed and why. Bullet points are fine.
Describe the beds in the README

Four raised beds on the south side, so the plan says where each crop
goes before we dig. Sizes come from the lumber list.

- beds are 4 x 8 feet
- paths are 3 feet wide

For a message with a body, leave off -m: git commit opens your editor with the staged changes listed as comments at the bottom, a last chance to check. A good test of the summary: does it finish the sentence “If applied, this commit will…”?

Quick check

Which summary line follows Pro Git’s guidelines?

Checking before you commit

Before committing, two quick checks 1:

me@linuxbox:~/garden$ git diff --staged          # exactly what will be committed
me@linuxbox:~/garden$ git diff --staged --check   # trailing spaces and other whitespace errors

--check flags stray spaces at line ends and similar problems that make later diffs noisy; plain git diff --check checks your unstaged changes instead 1.

Moving and deleting files

To delete a tracked file, git rm file removes it and stages the removal in one step. To rename or move one, git mv old new 1:

me@linuxbox:~/garden$ git mv fence.txt fence-plan.txt
me@linuxbox:~/garden$ git status -s
R  fence.txt -> fence-plan.txt

Git doesn’t actually record renames; it notices them, because the new file’s contents match the old one’s. So deleting and adding by hand works too, but git mv is one step instead of three 1.

Your turn

Exercises

  1. In your garden repository, make two unrelated changes to the same file. Commit them as two separate commits with git add -p.
  2. Try the s answer on a hunk with two separate changed lines close together. What happens?
  3. Write a commit with a body: run git commit without -m, write a summary, a blank line, and two lines of explanation. Check it with git log -1.
  4. Add a line with trailing spaces and run git diff --check. Then stage it and run git diff --staged --check.
  5. Rename a file with git mv, commit, and look at git log --stat -1.
Answers
  1. If the hunk has unchanged lines between the changes, s splits it into smaller hunks you can answer one by one. If the changes are touching, it can’t split, and e lets you edit the hunk to pick lines.
  2. git log -1 shows the summary and body separately; git log --oneline shows only the summary, which is why the summary must stand on its own.
  3. Both print the file and line number with trailing whitespace.
  4. The stat line shows fence.txt => fence-plan.txt, a rename.

So

Commit one logical change at a time, using git add -p to stage exactly the hunks that belong together. Write a short, capitalized, imperative summary line, a blank line, and a body that explains why. Check with git diff --staged and git diff --check, and use git mv and git rm for renames and deletions.

Lesson complete

Nice work.

1day streak
0/1today's goal
–correct

Up next · 5 min

Ignoring Files and Reading History

Next lesson
Sources for this lesson
  1. 1
    Scott Chacon, Ben Straub. Pro Git, 2nd edition. Apress; free online at git-scm.com. 2014. verifiedFree CC BY-NC-SA 3.0 book, maintained online. Ch. 1: version control; Git's 2005 origin when the Linux kernel lost free use of BitKeeper; snapshots, not differences (unchanged files stored once); nearly every operation local; integrity through 40-character SHA-1 checksums; the three states (modified, staged, committed) and three areas (working tree, staging area or index, .git directory); first-time setup with system/global/local config levels, user.name and user.email baked into commits, core.editor, git config --list --show-origin. Ch. 2: git init, status (and -s), add, diff and diff --staged, commit (-m, -a), .gitignore, log options, amending, undoing, remotes, tags, aliases. Ch. 3: branches as movable pointers, HEAD, merging and conflicts, remote branches, rebasing and its rule. Ch. 7: reset demystified, stashing, revision selection. Ch. 8: core.autocrlf true on Windows, input on Linux and macOS. Ch. 10: objects (blob, tree, commit) and references.