
Introduction
The RGDiff project parses the output from a standard command line Diff program and presents the results in
a graphical manner. It re-uses the GNU Diff program by "shelling out" using the WIN32 CreateProcess()
function.
The techniques used could easily be incorporated into other programs requiring Diff functionality.
This article explains how the above is done in more detail.
Running RGDiff
RGDiff needs the GNU WIN32 diff.exe program to be in the same directory as
RGDiff.exe.
The directory should be writeable as RGDiff needs to create some temporary files.
RGDiff is statically linked with MFC because it is a standalone tool and MFC70.dll is
probably not as widely distributed as MFC42.dll. I know this is probably not true among
CodeProject members, but just to be on the safe side...
Background
Whenever I have used Microsoft's WinDiff, I could not help but be frustrated by its "Zebra Stripes".
I have used other GUI Diff tools, and found that a side by side approach helps me understand the
differences between the files much better. I had a look around the WWW and couldn't find anything
that was free and did what I wanted, I therefore decided to have a go at writing my own.
My first decision was whether to write my own Diff algorithm or rip off somebody else's.
I looked at Diff algorithms, the WinDiff code, and the GNU Diff code, and found them all
impenetrable in the time I had available :-(
I therefore decided that the only realistic approach for me was to use an existing command line
Diff utility as a spawned process, which is a common design pattern on *nix.
The GNU Diff utility seemed like a good start, and with programmers like Richard Stallman behind it,
I felt that it might take me a while to improve on their algorithms! (A man's gotta know his limitations).
The GNU WIN32 Project
provides a compiled diff.exe (and a lot of other useful stuff), and I have included
diff.exe as part of both downloads.
The above page provides details of how to get the source code if the reader should require that.
Techniques Used
This project uses standard WIN32/MFC techniques as follows:
- Spawn a process and capture the results
- SDI application with multiple windows
- Splitter windows
- Owner draw list boxes
- Specialized list boxes
- Registry access
Comparing Files
This is where Diff is used to compare 2 files. The command and output will be something like the following:
C:\Code\DIFF>diff f1.cpp f2.cpp
1,2d0
<
<
8c6,7
< return x + y;
---
> int res(x+y)
> return res;
15a15,20
>
>
>
>
>
>
The lines we are interested in are the ones without < or > signs at the start, as
these are simply to help a human see which lines come from which file. The other lines tell us what the
differences between the files are, or put another way, what we need to do to either file to make it the same
as the other. It is these lines that we parse out and use to build the visual display.
The results above are interpreted as follows:
1,2d0
means delete lines 1-2 of file 1; or, if changing file 2 into file 1,
append lines 1-2 of file 1 after line 0 of file 2.
8c6,7
means change line 8 of file 1 to read as lines 6-7 of file 2; or, if changing file 2 into file 1,
change lines 6-7 of file 2 to read as line 8 of file 1.
15a15,20
means append lines 15-20 of file 2 after line 15 of file 1; or, if changing
file 2 into file 1, delete lines 15-20 of file 2.
The GNU Diff documentation
fully describes the way the results should be interpreted.
Comparing Directories
Comparing 2 files is good, but being able to recurse sub-directories like WinDiff is better. Luckily
diff.exe
provides command line options to allow this. The command and output will be something like the following:
C:\Code\DIFF>diff --recursive --report-identical-files --brief dir1 dir2
Files dir1\file1.cpp and dir2\file1.cpp are identical
Files dir1\file2.cpp and dir2\file2.cpp differ
Only in dir1: file3.cpp
Only in dir2: file4.cpp
The command line options are mostly self explanatory, but --brief
tells Diff to only summarize each file.
If this was omitted, a full Diff report for each file would be produced, taking longer. The above command executes very quickly,
although I have only tried it on medium size projects of 200 or so files, with 20 or so sub-directories.
The results are parsed to produce a summary report just like with WinDiff. Double clicking a file then invokes Diff
again to compare those 2 files, and a detailed report is displayed the same, as if the user had just chosen to compare those 2 files.
Note that because file names can contain the sequence " and "
,
some care is required when parsing the results.
The Scrolling Views
I have chosen to implement each view as an owner-draw list box. This allows me to
customize the display
to a great extent and I think they look OK. One problem is that when 2 files are displayed side by side in detail mode,
I want them to keep in step when either one is scrolled. I achieve this by capturing all messages and when a scroll
message is received, I first send it to the other list box. This works fine but can look a little odd sometimes as one
view moves slightly after the other.
Future Direction
I am fairly pleased that RGDiff provides a useful, yet minimal implementation without too much effort expended.
However the following are a few things that I think could improve RGDiff:
- Birds eye view in the left margin, like WinDiff
- Merge functionality (one at a time, or en-masse)
- Better synchronized scrolling between views, in detail view
- Better remembering of recently used files, like CodeProject front end to WinDiff
History
1.0.0.3 - 3rd March 2003 - Initial Version