Git Diffs of Untracked Files

December 17th, 2021

Diffs

TL;DR: I like the nice output from git diff better than the native diff command, but how do I convince git to let me do this for files which aren't source controlled?

I like to wrap this in a shell function I call fdiff.

fdiff () {
    if (( $# < 2 ))
    then
        echo "fdiff usage: pass two Files to see their DIFFerence"
        echo "\x1b[100m"
        echo "\t                           "
        echo "\t  > fdiff old.txt new.txt  "
        echo "\t  \x1b[1m--- old = old.txt        "
        echo "\t  \x1b[1m+++ new = new.txt\x1b[0m\x1b[100m        "
        echo "\t  \x1b[36m@@ -1 +1 @@\x1b[39m              "
        echo "\t  \x1b[31m-This is the old file    "
        echo "\t  \x1b[32m+This is the new file    "
        echo "\t                           "
        echo "\x1b[0m"
        echo "accepts all options that git diff does, e.g. --word-diff -U0"
        echo "\x1b[100m"
        echo "\t                                           "
        echo "\t  > fdiff old.txt new.txt --word-diff -U0  "
        echo "\t  \x1b[1m--- old = old.txt                        "
        echo "\t  \x1b[1m+++ new = new.txt\x1b[0m\x1b[100m                        "
        echo "\t  \x1b[36m@@ -1 +1 @@\x1b[39m                              "
        echo "\t  This is the \x1b[31m[-old-]\x1b[32m{+new+}\x1b[39m file          "
        echo "\t                                           "
        echo "\x1b[0m"
    else
        git --no-pager diff --no-index --color=always --src-prefix="old = " --dst-prefix="new = " ${@:3} -- $1 $2
    fi
}

Here's a more minimal version

fdiff () {
    git --no-pager diff --no-index ${@:3} -- $1 $2
}

Explanation

Breaking this down, the most important pieces are

git
The git command.
diff
The diff command for git.
--no-index
This flag is the magic sauce that lets us use git diff with files that are not source controlled.
-- $1 $2
These are just passing the filepaths down to git so it knows where to look.

Nice extra features.

--no-pager
Tells git to print the output to stdout instead of opening a paging program. Note: this flag must come before diff
--color=always
Preserves the diff colors when you pipe to other programs, e.g. grep which you may not want!
--src-prefix="old = "
--dst-prefix="new = "
Tells git to display the file names as "old" and "new" instead of "a" and "b". Very much a personal preference.
${@:3}
Allows you to pass further arguments to git diff. See below for some examples.

You can read about all of the possible flags that can be passed to git diff by consulting git help diff.

Example Usage

Here we'll use the following two files, old.txt and new.txt.

This is the old text.
This is the new text.

Basic Usage

Running fdiff on these files produces output like this.

 > fdiff old.txt new.txt
--- old = old.txt
+++ new = new.txt
@@ -1 +1 @@
-This is the old text.
+This is the new text.

Diffs on words

For day-to-day usage, I find --word-diff or --color-words the most useful.

 > fdiff old.txt new.txt --word-diff
--- old = old.txt
+++ new = new.txt
@@ -1 +1 @@
This is the [-old-]{+new+} text.
 > fdiff old.txt new.txt --color-words
--- old = old.txt
+++ new = new.txt
@@ -1 +1 @@
This is the oldnew text.

Summarizing Diffs

If you just want an overview of diffs, you can use flags like --compact-summary or --numstat

 > fdiff old.txt new.txt --compact-summary
old.txt => new.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
 > fdiff old.txt new.txt --numstat
1    1    old.txt => new.txt

Seeing Where Text Moved

If you're writing a longer document and you want to see where pieces have moved, you can use --color-moved=plain. Here I'm no longer using our previous example files.

 > fdiff old.txt new.txt --color-moved=plain
--- old = old.txt
+++ new = new.txt
@@ -1,7 +1,7 @@
-Lorem ipsum dolor sit amet, consectetur adipiscing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ac orci phasellus egestas tellus rutrum tellus pellentesque eu tincidunt.
Eget magna fermentum iaculis eu non diam phasellus.
+Lorem ipsum dolor sit amet, consectetur adipiscing elit,
Feugiat nibh sed pulvinar proin gravida hendrerit lectus a.
Vulputate eu scelerisque felis imperdiet.
Amet volutpat consequat mauris nunc congue.

Context

You can specify the number of lines of context (lines before and after) you want with the -U flag. So for 0 lines of context, use -U0. For 10 lines, -U10, etc. I often use this in combination with --word-diff.

 > fdiff old.txt new.txt --word-diff -U1
--- old = old.txt
+++ new = new.txt
@@ -1,3 +1,3 @@
Lorem
[-ipsum-]{+magna+}
dolor
@@ -6,3 +6,3 @@ amet,
consectetur
[-adipiscing-]{+magna+}
elit,