You are previewing Git Pocket Guide.

Git Pocket Guide

Cover of Git Pocket Guide by Richard E. Silverman Published by O'Reilly Media, Inc.
  1. Special Upgrade Offer
  2. Preface
    1. What Is Git?
    2. Goals of This Book
    3. Conventions Used in This Book
      1. Unix
      2. Shell
      3. Command Syntax
      4. Typography
    4. Using Code Examples
    5. Safari® Books Online
    6. How to Contact Us
    7. Acknowledgments
  3. 1. Understanding Git
    1. Overview
      1. Terminology
      2. Branches
      3. Sharing Work
    2. The Object Store
      1. Blob
      2. Tree
      3. Commit
      4. Tag
    3. Object IDs and SHA-1
      1. Security
    4. Where Objects Live
    5. The Commit Graph
    6. Refs
      1. Related Commands
    7. Branches
    8. The Index
    9. Merging
      1. Merging Content
      2. Merging History
    10. Push and Pull
      1. Notes
  4. 2. Getting Started
    1. Basic Configuration
      1. Personal Identification
      2. Text Editor
      3. Commit ID Abbreviation
      4. Pagination
      5. Color
      6. Cryptographic Keys
      7. Command Aliases
      8. Getting Help
      9. References
    2. Creating a New, Empty Repository
      1. Selected Options
    3. Importing an Existing Project
    4. Ignoring Files
      1. Syntax of “Ignore Patterns”
  5. 3. Making Commits
    1. Changing the Index
      1. Adding a New File
      2. Adding the Changes to an Existing File
      3. Adding Partial Changes
      4. Shortcuts
      5. Removing a File
      6. Renaming a File
      7. Unstaging Changes
    2. Making a Commit
      1. Commit Messages
      2. What Makes a Good Commit?
      3. Shortcuts
      4. Empty Directories
      5. A Commit Workflow
  6. 4. Undoing and Editing Commits
    1. Changing the Last Commit
      1. Double Oops!
    2. Discarding the Last Commit
      1. Discarding Any Number of Commits
    3. Undoing a Commit
      1. Partial Undo
    4. Editing a Series of Commits
      1. Conflicts
      2. The exec Action
  7. 5. Branching
    1. The Default Branch, master
    2. Making a New Branch
    3. Switching Branches
      1. Uncommitted Changes
      2. Untracked Files
      3. Losing Your Head
    4. Deleting a Branch
    5. Renaming a Branch
  8. 6. Tracking Other Repositories
    1. Cloning a Repository
      1. Clones and Hard Links
      2. Bare Repositories
      3. Reference Repositories
    2. Local, Remote, and Tracking Branches
    3. Synchronization: Push and Pull
      1. Pulling
      2. Pushing
      3. Push Defaults
      4. Pull with Rebase
      5. Notes
    4. Access Control
  9. 7. Merging
    1. Merge Conflicts
      1. Resolving Merge Conflicts
      2. Notes
    2. Details on Merging
    3. Merge Tools
      1. Notes
    4. Custom Merge Tools
    5. Merge Strategies
    6. Why the Octopus?
    7. Reusing Previous Merge Decisions
  10. 8. Naming Commits
    1. Naming Individual Commits
      1. Commit ID
      2. Ref Name
      3. Names Relative to a Given Commit
      4. Names Relative to the Reflog
      5. The Upstream Branch
      6. Matching a Commit Message
      7. Following Chains
      8. Addressing Pathnames
    2. Naming Sets of Commits
  11. 9. Viewing History
    1. Command Format
    2. Output Formats
    3. Defining Your Own Formats
      1. Notes
    4. Limiting Commits to Be Shown
    5. Regular Expressions
    6. Reflog
    7. Decoration
    8. Date Style
    9. Listing Changed Files
    10. Showing and Following Renames or Copies
      1. Detecting Copies
    11. Rewriting Names and Addresses: The “mailmap”
      1. Shortening Names
    12. Searching for Changes: The “pickaxe”
    13. Showing Diffs
      1. Color
      2. Word Diff
    14. Comparing Branches
      1. Displaying Sides
    15. Showing Notes
    16. Commit Ordering
    17. History Simplification
    18. Related Commands
      1. git cherry
      2. git shortlog
  12. 10. Editing History
    1. Rebasing
      1. Undoing a Rebase
    2. Importing from One Repository to Another
      1. Importing Disconnected History
      2. Importing Linear History
      3. Importing Nonlinear History
    3. Commit Surgery: git replace
      1. Keeping It Real
    4. The Big Hammer: git filter-branch
      1. Examples
    5. Notes
  13. 11. Understanding Patches
    1. Applying Plain Diffs
    2. Patches with Commit Information
  14. 12. Remote Access
    1. SSH
    2. HTTP
    3. Storing Your Username
    4. Storing Your Password
    5. References
  15. 13. Miscellaneous
    1. git cherry-pick
    2. git notes
      1. git notes Subcommands
    3. git grep
      1. Combining Regular Expressions
      2. What to Search
      3. What to Show
      4. How to Match
    4. git rev-parse
    5. git clean
    6. git stash
      1. Subcommands
    7. git show
    8. git tag
      1. Deleting a Tag from a Remote
      2. Following Tags
      3. Backdating Tags
    9. git diff
      1. git diff
      2. git diff --staged
      3. git diff <commit>
      4. git diff <A> <B>
      5. Options and Arguments
    10. git instaweb
    11. Git Hooks
    12. Visual Tools
    13. Submodules
  16. 14. How Do I…?
    1. …Make and Use a Central Repository?
    2. …Fix the Last Commit I Made?
    3. …Edit the Previous n Commits?
    4. …Undo My Last n Commits?
    5. …Reuse the Message from an Existing Commit?
    6. …Reapply an Existing Commit from Another Branch?
    7. …List Files with Conflicts when Merging?
    8. …Get a Summary of My Branches?
    9. …Get a Summary of My Working Tree and Index State?
    10. …Stage All the Current Changes to My Working Files?
    11. …Show the Changes to My Working Files?
    12. …Save and Restore My Working Tree and Index Changes?
    13. …Add a Downstream Branch Without Checking It Out?
    14. …List the Files in a Specific Commit?
    15. …Show the Changes Made by a Commit?
    16. …Get Tab Completion of Branch Names, Tags, and So On?
    17. …List All Remotes?
    18. …Change the URL for a Remote?
    19. …Remove Old Remote-Tracking Branches?
    20. …Have git log:
      1. Find Commits I Made but Lost?
      2. Not Show the diffs for Root Commits?
      3. Show the Changes for Each Commit?
      4. Show the Committer as well as the Author?
  17. Index
  18. About the Author
  19. Special Upgrade Offer
  20. Copyright
O'Reilly logo

Chapter 11. Understanding Patches

A “patch” is a compact representation of the differences between two files, intended for use with line-oriented text files. It describes how to turn one file into another, and is asymmetric: the patch from file1 to file2 is not the same as the patch for the other direction (it would say to delete and add opposite lines, as we will see). The patch format uses context as well as line numbers to locate differing file regions, so that a patch can often be applied to a somewhat earlier or later version of the first file than the one from which it was derived, as long as the applying program can still locate the context of the change.

The terms “patch” and “diff” are often used interchangeably, although there is a distinction, at least historically. A diff only need show the differences between two files, and can be quite minimal in doing so. A patch is an extension of a diff, augmented with further information such as context lines and filenames, which allow it to be applied more widely. These days, the Unix diff program can produce patches of various kinds.

Here’s a simple patch, generated by git diff:

diff --git a/foo.c b/foo.c
index 30cfd169..8de130c2 100644
--- a/foo.c
+++ b/foo.c
@@ -1,5 +1,5 @@
 #include <string.h>

 int check (char *string) {
-    return !strcmp(string, "ok");
+    return (string != NULL) && !strcmp(string, "ok");
 }

Breaking this into sections:

diff --git a/foo.c b/foo.c

This is the Git diff header; diff --git isn’t a literal command, but rather just suggests the notion of a Git-specific diff in Unix command style. a/foo.c and b/foo.c are the files being compared, with added leading directory names a and b to distinguish them in case they are the same (as they are here; this patch shows the changes from one version to another of the same file). To generate this patch, I changed the file foo.c and ran git diff, which shows the unstaged changes between the working tree and the index. There are in fact no directories named a and b in the repository; they are just convention:

index 30cfd169..8de130c2 100644

This is an extended header line, one of several possible forms, though there is only one in this patch. This line gives information from the Git index regarding this file: 30cfd169 and 8de130c2 are the blob IDs of the A and B versions of the file contents being compared, and 100644 are the “mode bits,” indicating that this is a regular file: not executable and not a symbolic link (the use of .. here between the blob IDs is just as a separator and has nothing to do with its use in naming either sets of revs or for git diff). Other header lines might indicate the old and new modes if that had changed, old and new filenames if the file were being renamed, etc.

The blob IDs are helpful if this patch is later applied by Git to the same project and there are conflicts while applying it. If those blobs are in the object database, then Git can use them to perform a three-way merge with those two versions and the working copy, to help you resolve the conflicts. The patch still makes sense to other tools besides Git; they will just ignore this line and not be able to use the extra information:

--- a/foo.c
+++ b/foo.c

This is the traditional “unified diff” header, again showing the files being compared and the direction of the changes, which will be shown later: minus signs will show lines in the A version but missing from the B version; and plus signs, lines missing in A but present in B. If the patch were of this file being added or deleted in its entirety, one of these would be /dev/null to signal that:

@@ -1,5 +1,5 @@
 #include <string.h>

 int check (char *string) {
-    return !strcmp(string, "ok");
+    return (string != NULL) && !strcmp(string, "ok");
 }

This is a difference section, or “hunk,” of which there is just one in this diff. The line beginning with @@ indicates by line number and length the positions of this hunk in the A and B versions; here, the hunk starts at line 1 and extends for 5 lines in both versions. The subsequent lines beginning with a space are context: they appear as shown in both versions of the file. The lines beginning with minus and plus signs have the meanings just mentioned: this patch replaces a single line, fixing a common C bug whereby the program would crash if this function were passed a null pointer as its string argument.

A single patch file can contain the differences for any number of files, and git diff produces diffs for all altered files in the repository in a single patch (unlike the usual Unix diff command, which requires extra options to recursively process whole directory trees).

Applying Plain Diffs

If you save the output of git diff to a file (e.g., with git diff > foo.patch), you can apply it to the same or a similar version of the file elsewhere with git apply, or with other common tools that handle diff format, such as patch (although they won’t be able to use any extra Git-specific information in the diff). This is useful for saving a set of uncommitted changes to apply to a different set of files, or for transmitting any set of changes to someone else who is not using Git.

You can use the output of git show commit as a patch representing the changes for a given nonmerge commit, as a shortcut for git diff commit~ commit (explicitly comparing a commit and its parent).

Patches with Commit Information

There is another patch format, specific to Git, that contains not only patches for some number of files, but also commit metadata: the author, timestamp, and message. This carries all the information needed to reapply the changes from one commit as a new commit elsewhere, and is useful for transmitting a commit when it is not possible or convenient to do so with the usual Git push/pull mechanism.

You produce this patch format with git format-patch, and apply it with git am. The patch itself is actually in the venerable Unix mailbox format, using the email “from,” “date,” and “subject” headers as the author, timestamp, and commit message subject, and the email body as the rest of the message. A commit patch for the previous example might look like this:

From ccadc07f2e22ed56c546951… Mon Sep 17 00:00:00 2001
From: "Richard E. Silverman" <res@oreilly.com>
Date: Mon, 11 Feb 2013 00:42:41 -0500
Subject: [PATCH] fix null-pointer bug in check()

It is truly a wonder that we continue to write
high-level application software in what is essentially
assembly language. We deserve all the segfaults we
get.

---
foo.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/foo.c b/foo.c
...

The initial line contains the original commit ID, and a fixed timestamp meant to signal that this “email” was produced by git format-patch. The [PATCH] prefix in the subject is not part of the commit message, but rather intended to distinguish patches among other email messages. A diffstat summary of the patch comes next (which you can suppress with --no-stat (-p)), followed by the patch itself in the format shown earlier.

You run git format-patch like so:

$ git format-patch [options] [revisions]

The revisions argument can be any expression specifying a set of commits to format, as described in Chapter 8. As an exception, however, a single commit C means C..HEAD, i.e., the commits on the current branch not contained in C (if C is on this branch, these are the commits made since C). To get the other meaning instead—that is, all commits reachable from C—use the --root option.

By default, Git writes patches for the selected commits into sequentially numbered files in the current directory, with names reflecting the commit message subject lines like so:

0001-work-around-bug-with-DNS-KDC-location.patch
0002-use-DNS-realm-mapping-even-for-local-host.patch
0003-fix-AP_REQ-authenticator-bug.patch
0004-add-key-extraction-to-kadmin.patch

The leading numbers in the filenames makes it easy to apply these patches in order with git am *.patch, since the shell will sort them lexicographically when expanding the wildcard. You can give a different output directory with --output-directory (-o), or write the patches all to standard output with --stdout; git am reads from standard input if given no file arguments (or a single hyphen as the only argument, git am -).

git format-patch takes a number of other options for controlling the resulting email format, such as adding other mail headers, as well as many options taken by git diff to affect the diff itself; see git-format-patch(1) and git-diff(1) for more detail. Also see Importing Linear History for some examples.

The best content for your career. Discover unlimited learning on demand for around $1/day.