I've always wanted to explore the topic of diff in depth, but so far haven't been able to. I have some links that I've stashed; hopefully they will be useful.
For binary/executable code, I believe Colin Percival's bsdiff is the best: http://www.daemonology.net/bsdiff/ although he hints that his thesis contains a better algorithm.
For people just wanting to get their feet wet and understand one of the simpler algorithms, I implemented a simple diff algorithm in Python a decade ago with code readability in mind and it's since been translated to a handful of languages.
Quick question, is this the sort of technique that used for general text diffing? I see you effectively tokenize the string using whitespace to split and then pass in 2 lists of tokens. Is that the way it's generally done, and you just choose a suitable tokenizer for your specific use-case?
Does anyone know much about prose diff? Very few diff apps work well with prose, and I wonder to what extent this is driven at the level of the algorithm.
in the same way that one-sentence-per-line
gives you much more fine-grained diffs than
one-paragraph-per-line, notching it down to
one-phrase-per-line gives the best results.
a simple javascript routine can do that split,
and then rejoin the lines after you do a diff.
i've written this up extensively, to no notice.
http://zenmagiclove.com/simple/breaker.html
https://github.com/bbirdiman/breakerbreaker
***
i've since found that a dozen changes suffice:
replace ". " -- with -- ". \n"
replace ", " -- with -- ", \n"
replace "? " -- with -- "? \n"
replace "! " -- with -- "! \n"
replace ": " -- with -- ": \n"
replace "; " -- with -- "; \n"
replace ") " -- with -- ") \n"
replace "] " -- with -- "] \n"
replace "} " -- with -- "} \n"
replace "-- " -- with -- "-- \n"
replace "' " -- with -- "' \n"
replace '" ' -- with -- '" \n'
***
and, of course, to revert those changes,
you simply change " \n" to " " and boom.
> I wonder to what extent this is driven at the level of the algorithm.
Almost entirely.
Most programming diff algorithms operate on a line-by-line basis. That means that a single character change in a line marks the whole line as changed. For prose, a single 'line' is usually a paragraph, so it's pretty obvious why they don't work well. You might check out gnu `wdiff` for what a word-by-word diff could look like. I haven't really looked into the area deeply, so I don't know what the state of the art is.
This makes me wonder if anyone has implemented diff by comparing the Abstract Syntax Trees of the two code-bases. I guess it's probably easier than regular diff (assuming you've already got the ASTs from somewhere).
I would think diffing trees would be necessarily more complex than diffing lines - you can think of the line-by-line description of a file as a tree with at most one child per node, so any algorithm for diffing trees should also automatically be able to diff lines.
Tree diff algorithms definitely exist (I know React.js uses diffs on a virtual DOM to minimize operations), but I'm not sure what the state of the art is for those.
Tree diff varies between O(N^2) and O(N^4) for simple solutions of varying complexity of matches possible, with a complex fully general algorithms coming in at O(N^2 log^2 N).
Tree diff is harder because the range of operations is bigger.
React.js cheats by insisting on keys for arrays so matching is much easier.
I've maintained prose in git with the default diff algorithm using one sentence per line, and it's worked pretty well.
My specific use case was a document tweaked for two different audiences, and I've been able to make changes in one version and rebase the other version on top of it relatively easily.
From the simple perspective, using a fixed width line in a prose markup language that is mostly whitespace agnostic like Markdown creates okay diffs in a line-based diff tool.
That particular tool/experiment uses the Pygments tokenizer used for syntax highlighting and produces interesting somewhat semantically meaningful code diffs. I think the same principles would apply if you used something like a part of speech tagger on prose.
From a different approach, I put some effort into better line-based and file-based diffs of Inform 7 which is a prose format that is less whitespace agnostic than Markdown and also built as a single monolithic file, by converting it to an intermediate format.
That works by splitting the file at things that resembles headers, converting newlines to pilcrows (the paragraph symbol), and essentially reformatting to more of a fixed width format. (All of which is trivially reversible.)
You can see the commits in that repository as an example of what the intermediate format looks and diffs like.
I wrote that to better source control zip files as the contents of their zip rather than a binary blob, by letting the zip/unzip operations be automatable as a part of source control operations (pre- and post-commit hooks). This I've used for some of the modern file formats like .docx which are built as zip files of XML files and other assets. For instance, you want write the document in Word, interacting with the .docx, and source control its XML contents. That too gives more useful diffs than source controlling the .docx on its own.
Courgette used to only work on Windows (PE/COFF) executables. It looks like it now also works on ELF x86 and ELF arm executables. But it doesn't seem to support ELF 64-bit, nor does it support mach-o (Mac) executables.
In addition to the algorithms you mentioned git also has --diff-algorithm=minimal. This is just the XDF_NEED_MINIMAL flag to xdiff, so I'm unsure if it counts as a separate algorithm.
I believe the classic for typical textual diff is this article by Myers, whose algorithm is still the default in git: http://link.springer.com/article/10.1007/BF01840446
Git has two other diff algorithms, patience and histogram: http://alfedenzo.livejournal.com/170301.html https://github.com/git/git/commit/8c912eea94a2138e8bc608f7c3...
For binary/executable code, I believe Colin Percival's bsdiff is the best: http://www.daemonology.net/bsdiff/ although he hints that his thesis contains a better algorithm.
For just executables, however, I think Google Chrome uses Courgette, which actually performs disassembly first: https://www.chromium.org/developers/design-documents/softwar...
Also useful is libxdiff, which is a C library offering various diff utilities: http://www.xmailserver.org/xdiff-lib.html