8

I really like git diff's output format, and I use it a lot as git diff --no-index to compare arbitrary non-git-related files. But as far as I can tell when using --no-index you can provide only a single pair of files. I'd really like to be able to pass it multiple pairs, like:

git diff --no-index a.new a.old b.new b.old

And get the kind of output you'd get by running git diff in a repo:

diff --git a/a b/a
index e965047..ce01362 100644
--- a/a
+++ b/a
@@ -1 +1 @@
-Hello
+hello
diff --git a/b b/b
index 2b60207..dd7e1c6 100644
--- a/b
+++ b/b
@@ -1 +1 @@
-Goodbye
+goodbye

Is there any way to do this with git diff, or a similar tool?

alecbz
  • 338
  • 1
  • 2
  • 11

2 Answers2

4

Since git diff --no-index accepts also two directories and compare these, you could always write a wrapper script around that, copying your files into two temporary directories.

Other than that, I don't know of a diff tool that accepts multiple pairs of files to compare. Even diff only accepts a single pair of files, or a directory and a list of files.

Jaap Eldering
  • 9,425
  • 2
  • 18
  • 26
  • Ooh, I didn't know it accepted directories. That's probably good enough for me. I would prefer a solution where the file names didn't necessarily have to match (ie, in the two directories), but I can probably live with that. – alecbz Dec 24 '15 at 05:24
  • @Alec With the advent (years later in v2.15) of `diff.colorMoved` in the config options, the filenames matching should matter less. There will still be `+` and `-` lines, but they'll be colored to show that they came from somewhere else in the same diff. – Michael May 30 '18 at 13:30
  • @Michael I've tried to find it but I couldn't find how to do what OP wants with regular GNU diff(1) either. I have version v3.6 (far newer than the v2.15 you mention). I can only seem to pass it FILE1 FILE2, not FILE1 FILE2 FILE3 ... (in pairs). – Aktau Jul 23 '18 at 15:57
  • @Aktau The version I mentioned was for Git (and its associated `git diff`). See [my answer](https://superuser.com/a/1343927/23156) for an extended explanation. – Michael Jul 27 '18 at 20:45
  • Thanks for the answer @Michael. My problem (and the OPs problem, I guess) is that our files aren't tracked in Git. We just like using git-diff(1) for its output. – Aktau Jul 30 '18 at 07:39
  • @Aktau, Oh, me too. I aliased `gdiff` to `git diff --no-index` long ago. Git doesn't have the pairwise compare, but its rename detection (even with `--no-index`) is good enough that a folder of old files and a folder of new files pretty much works. – Michael Sep 01 '23 at 15:22
4

This is basically two context-dependent versions of Jaap's answer for git diff --no-index, with an extra color suggestion.

Special coloring for moved lines

Before running git diff --no-index, you may want to set diff.colorMoved in the Git config. Alternatively, you can include a --color-moved option instead of setting it globally.

diff.colorMoved settings use a different line color when lines removed from one place show up elsewhere in the diff. This works, whether they moved within the file or they moved to another file, like in a refactor.

I have been quite pleased with the dimmed-zebra color option.

Two circumstances for diffing

Complete or almost complete sets

You have some files in a directory, current-version, that you want to compare to some other files. If the set of files you want to compare them to in new-version is nearly identical in content, just use Jaap's suggestion:

git diff --no-index current-version/ new-version/

Specific files to compare

If there are only a handful of files in one version and lots of files in the other, like so…

┌ current-project
| ├ (various folders)
| └ src
|   ├ (various files)
|   ├ foo.c
|   └ bar.c
└ proposed-replacements
  ├ new-foo.c
  └ new-bar.c

…you can try to let Git's rename detection1 do the work.

You don't care to see that the majority of the files are "missing" in proposed-replacements. You don't want to painstakingly give proposed-replacements' files the same name/path as they appear in current-project, so you leave them where they were and add a filter that includes only renamed and modified files.

git diff --no-index --diff-filter=RM current-project/ proposed-replacements/
diff --git a/current-project/src/foo.c b/proposed-replacements/new-foo.c
similarity index 65%
rename from current-project/src/foo.c
rename to proposed-replacements/new-foo.c
index dd51990..64445b1 100644
--- a/current-project/src/foo.c
+++ b/proposed-replacements/new-foo.c
@@ -1,4 +1,4 @@
 octopus
 cow
-dog
 tiger
+flamingo

diff --git a/current-project/src/bar.c b/proposed-replacements/new-bar.c
similarity index 87%
rename from current-project/src/bar.c
rename to proposed-replacements/new-bar.c
...

The default rename detection is when the file is 50+% the same as the old version. You can tweak that with --find-renames=40% or similar. See the documentation for more.

1 I don't know what version of Git adds the degree of rename tracking that is necessary to do this. I'm using v2.18, but some of the options date back to at least 2005. Also, the testing I did was on a pretty trivial set of files.

Michael
  • 999
  • 10
  • 16