If I make a simple diff in git with e.g.:
git diff origin/master
where can I find good references on how to read the output of git. The man page on git-diff is rather long, and explains many cases which are maybe not necessary for a start.
If I make a simple diff in git with e.g.:
git diff origin/master
where can I find good references on how to read the output of git. The man page on git-diff is rather long, and explains many cases which are maybe not necessary for a start.
On my mac:
info diff
then select: Output formats
-> Context
-> Unified format
-> Detailed Unified
:
Or online man diff on gnu following the same path to the same section:
File: diff.info, Node: Detailed Unified, Next: Example Unified, Up: Unified Format
Detailed Description of Unified Format ......................................
The unified output format starts with a two-line header, which looks like this:
--- FROM-FILE FROM-FILE-MODIFICATION-TIME +++ TO-FILE TO-FILE-MODIFICATION-TIME
The time stamp looks like `2002-02-21 23:30:39.942229878 -0800' to indicate the date, time with fractional seconds, and time zone.
You can change the header's content with the `--label=LABEL' option; see *Note Alternate Names::.
Next come one or more hunks of differences; each hunk shows one area where the files differ. Unified format hunks look like this:
@@ FROM-FILE-RANGE TO-FILE-RANGE @@ LINE-FROM-EITHER-FILE LINE-FROM-EITHER-FILE...
The lines common to both files begin with a space character. The lines that actually differ between the two files have one of the following indicator characters in the left print column:
`+' A line was added here to the first file.
`-' A line was removed here from the first file.
The default output format (which originally comes from a program known as diff
if you want to look for more info) is known as a “unified diff”. It contains essentially 4 different types of lines:
+
,-
, andI advise that you practice reading diffs between two versions of a file where you know exactly what you changed. Like that you'll recognize just what is going on when you see it.
It's unclear from your question which part of the diffs you find confusing: the actually diff, or the extra header information git prints. Just in case, here's a quick overview of the header.
The first line is something like diff --git a/path/to/file b/path/to/file
- obviously it's just telling you what file this section of the diff is for. If you set the boolean config variable diff.mnemonic prefix
, the a
and b
will be changed to more descriptive letters like c
and w
(commit and work tree).
Next, there are "mode lines" - lines giving you a description of any changes that don't involve changing the content of the file. This includes new/deleted files, renamed/copied files, and permissions changes.
Finally, there's a line like index 789bd4..0afb621 100644
. You'll probably never care about it, but those 6-digit hex numbers are the abbreviated SHA1 hashes of the old and new blobs for this file (a blob is a git object storing raw data like a file's contents). And of course, the 100644
is the file's mode - the last three digits are obviously permissions; the first three give extra file metadata information (SO post describing that).
After that, you're on to standard unified diff output (just like the classic diff -U
). It's split up into hunks - a hunk is a section of the file containing changes and their context. Each hunk is preceded by a pair of ---
and +++
lines denoting the file in question, then the actual diff is (by default) three lines of context on either side of the -
and +
lines showing the removed/added lines.
Lets take a look at example advanced diff from git history (in commit 1088261f in git.git repository):
diff --git a/builtin-http-fetch.c b/http-fetch.c
similarity index 95%
rename from builtin-http-fetch.c
rename to http-fetch.c
index f3e63d7..e8f44ba 100644
--- a/builtin-http-fetch.c
+++ b/http-fetch.c
@@ -1,8 +1,9 @@
#include "cache.h"
#include "walker.h"
-int cmd_http_fetch(int argc, const char **argv, const char *prefix)
+int main(int argc, const char **argv)
{
+ const char *prefix;
struct walker *walker;
int commits_on_stdin = 0;
int commits;
@@ -18,6 +19,8 @@ int cmd_http_fetch(int argc, const char **argv, const char *prefix)
int get_verbosely = 0;
int get_recover = 0;
+ prefix = setup_git_directory();
+
git_config(git_default_config, NULL);
while (arg < argc && argv[arg][0] == '-') {
Lets analize this patch line after line.
The first line
diff --git a/builtin-http-fetch.c b/http-fetch.cis a "git diff" header in the form
diff --git a/file1 b/file2
. The a/
and b/
filenames are the same unless rename/copy is involved (like in our case). The --git
is to mean that diff is in the "git" diff format.Next are one or more extended header lines. The first three
similarity index 95% rename from builtin-http-fetch.c rename to http-fetch.ctell us that the file was renamed from
builtin-http-fetch.c
to http-fetch.c
and that those two files are 95% identical (which was used to detect this rename).index f3e63d7..e8f44ba 100644tell us about mode of given file (
100644
means that it is ordinary file and not e.g. symlink, and that it doesn't have executable permission bit), and about shortened hash of preimage (the version of file before given change) and postimage (the version of file after change). This line is used by git am --3way
to try to do a 3-way merge if patch cannot be applied itself.Next is two-line unified diff header
--- a/builtin-http-fetch.c +++ b/http-fetch.cCompared to
diff -U
result it doesn't have from-file-modification-time nor to-file-modification-time after source (preimage) and destination (postimage) file names. If file was created the source is /dev/null
; if file was deleted, the target is /dev/null
.diff.mnemonicPrefix
configuration variable to true, in place of a/
and b/
prefixes in this two-line header you can have instead c/
, i/
, w/
and o/
as prefixes, respectively to what you compare; see git-config(1)Next come one or more hunks of differences; each hunk shows one area where the files differ. Unified format hunks starts with line like
@@ -1,8 +1,9 @@or
@@ -18,6 +19,8 @@ int cmd_http_fetch(int argc, const char **argv, .... It is in the format
@@ from-file-range to-file-range @@ [header]
. The from-file-range is in the form -<start line>,<number of lines>
, and to-file-range is +<start line>,<number of lines>
. Both start-line and number-of-lines refer to position and length of hunk in preimage and postimage, respectively. If number-of-lines not shown it means that it is 0.
The optional header shows for C files which C function each change is in (like -p
option in GNU diff), or equivalent if any for other types of files.
Next comes the description of are whare files differ. The lines common to both files begin with a space character. The lines that actually differ between the two files have one of the following indicator characters in the left print column:
So for example first chunk
#include "cache.h"
#include "walker.h"
-int cmd_http_fetch(int argc, const char **argv, const char *prefix)
+int main(int argc, const char **argv)
{
+ const char *prefix;
struct walker *walker;
int commits_on_stdin = 0;
int commits;
means that cmd_http_fetch
was replaced by main
, and that const char *prefix;
line was added.
In other words before the change the appropriate fragment of then 'builtin-http-fetch.c' file looked like this:
#include "cache.h"
#include "walker.h"
int cmd_http_fetch(int argc, const char **argv, const char *prefix)
{
struct walker *walker;
int commits_on_stdin = 0;
int commits;
After the change this fragment of now 'http-fetch.c' file looks like this instead:
#include "cache.h"
#include "walker.h"
int main(int argc, const char **argv)
{
const char *prefix;
struct walker *walker;
int commits_on_stdin = 0;
int commits;
There might be
\ No newline at end of fileline present (it is not in example diff).
As Donal Fellows said it is best to practice reading diffs on real-life examples, where you know what you have changed.
References: