I've been thinking about "diff -B -M" for quite a while, and I am
finding a few interesting problems we have in our codebase. Here is
a snapshot, with a few action items.
In this write-up of my current thinking, I'll use these terms:
patchset::
Output from a single "git diff" invocation, which may contain
one or more "patch".
patch::
Part of a "patchset" from a line that begins with "diff --git"
up to (but excluding) the next line that begins with "diff --git".
tree::
"diff" compares two collections of files; each collection is a
"tree". Left and right sides of the comparison are called "old
tree" and "new tree", respectively. The "tree" we attempt to
apply a patchset is the "target tree". The "tree" we would get
after successful application of a patchset is the "resulting
tree". A "tree" does not have to be a tree object--we may be
comparing the index and the files in the working tree, for
example.
preimage::
postimage::
Lines in a "patch" that are prefixed by "-" or " " but not "+"
that denote what the patched file ought to have for the patchset
to apply ("preimage"). Lines with "+" or " " denote what the
patch result ought to look like ("postimage").
1. Basics
First the basics. Let's think about a patchset with a single patch.
What does this patchset tell us?
diff --git a/major-08.txt b/major-08.txt
index 680c5f6..5de90cb 100644
--- a/major-08.txt
+++ b/major-08.txt
@@ -1,3 +1,3 @@
-8. Fortitude.
+8. Strength.
This is one of the cardinal virtues, of which I shall speak later.
It obviously tells us that the new tree changed "Fortitude", that
used to be in the old tree, to "Strength", but it actually tells us
a bit more about the old tree. For this patch to apply, the target
tree must have a file named "major-08.txt" that begins with lines we
see as the preimage in the patch.
2. Renaming a file
Now let's get a bit fancier and study a patchset with a rename
patch, which I invented very early in the Git's life (it was done
while Linus was still running the project).
What does this patchset tell us?
diff --git rws/major-08.txt marseille/major-11.txt
similarity index 97%
rename from major-08.txt
rename to major-11.txt
index 680c5f6..2ab22a0 100644
--- rws/major-08.txt
+++ marseille/major-11.txt
@@ -1,3 +1,3 @@
-8. Fortitude.
+11. Fortitude.
This is one of the cardinal virtues, of which I shall speak later.
We can see that this is going from the same old tree as the previous
one's old tree, renames major-08.txt to major-11.txt with slight
modification. It tells us a lot more about the trees, compared to
the previous example. For this patchset to apply, the target tree
must satisfy the same preconditon as the previous one about
"major-08.txt", and in addition it must lack "major-11.txt";
otherwise we wouldn't be renaming a new file to it. Also, it tells
us that the resulting tree must lack "major-08.txt".
So far, these are straight-forward.
In summary:
rule 1. a patch from file A to file A means file A exists in the
target tree with contents that match the preimage of the
patch. file A will be in the resulting tree.
rule 2. a patch renaming file A to file B requires that file A
exists the target tree with contents that match the
preimage of the patch, and file B does not exist in the
target tree. file A will not exist in the resulting tree.
rule 3. a patch that creates file A requires that file A does not exist
in the target tree.
rule 4. a patch that deletes file A requires that file A exists in
the target tree with contents that match the preimage of
the patch.
The latter two I didn't illustrate with examples, but they should be
obvious.
3. First twist: cross renaming
Now, here is the first twist. What does this patchset mean?
diff --git rws/major-11.txt marseille/major-08.txt
similarity index 99%
rename from major-11.txt
rename to major-08.txt
index 517d9f8..44e8d3a 100644
--- rws/major-11.txt
+++ marseille/major-08.txt
@@ -1,3 +1,3 @@
-11. Justice.
+8. La Justice
That the Tarot, though it is of all reasonable antiquity, is not of
diff --git rws/major-08.txt marseille/major-11.txt
similarity index 97%
rename from major-08.txt
rename to major-11.txt
index 5de90cb..a101d5f 100644
--- rws/major-08.txt
+++ marseille/major-11.txt
@@ -1,3 +1,3 @@
-8. Strength.
+11. La Force
This is one of the cardinal virtues, of which I shall speak later.
This is a "swap" patchset, that swaps major-08.txt and major-11.txt
with small edit. You would have done something like this to prepare
it:
$ mv major-11.txt tmp
$ mv major-08.txt major-11.txt
$ mv tmp major-11.txt
$ edit major-08.txt major-11.txt ;# just a little
$ git commit -m swap major-08.txt major-11.txt