diff options
author | Linus Torvalds <torvalds@osdl.org> | 2006-03-24 20:13:22 -0800 |
---|---|---|
committer | Junio C Hamano <junkio@cox.net> | 2006-03-25 16:49:58 -0800 |
commit | 3443546f6ef57fe28ea5cca232df8e400bfc3883 (patch) | |
tree | 01b252a45c73d900a97fe305d696f386658e5d7d /diff.c | |
parent | c150462824008957f568ca7aa05a65b35d860eb9 (diff) | |
download | git-3443546f6ef57fe28ea5cca232df8e400bfc3883.tar.gz git-3443546f6ef57fe28ea5cca232df8e400bfc3883.tar.xz |
Use a *real* built-in diff generator
This uses a simplified libxdiff setup to generate unified diffs _without_
doing fork/execve of GNU "diff".
This has several huge advantages, for example:
Before:
[torvalds@g5 linux]$ time git diff v2.6.16.. > /dev/null
real 0m24.818s
user 0m13.332s
sys 0m8.664s
After:
[torvalds@g5 linux]$ time git diff v2.6.16.. > /dev/null
real 0m4.563s
user 0m2.944s
sys 0m1.580s
and the fact that this should be a lot more portable (ie we can ignore all
the issues with doing fork/execve under Windows).
Perhaps even more importantly, this allows us to do diffs without actually
ever writing out the git file contents to a temporary file (and without
any of the shell quoting issues on filenames etc etc).
NOTE! THIS PATCH DOES NOT DO THAT OPTIMIZATION YET! I was lazy, and the
current "diff-core" code actually will always write the temp-files,
because it used to be something that you simply had to do. So this current
one actually writes a temp-file like before, and then reads it into memory
again just to do the diff. Stupid.
But if this basic infrastructure is accepted, we can start switching over
diff-core to not write temp-files, which should speed things up even
further, especially when doing big tree-to-tree diffs.
Now, in the interest of full disclosure, I should also point out a few
downsides:
- the libxdiff algorithm is different, and I bet GNU diff has gotten a
lot more testing. And the thing is, generating a diff is not an exact
science - you can get two different diffs (and you will), and they can
both be perfectly valid. So it's not possible to "validate" the
libxdiff output by just comparing it against GNU diff.
- GNU diff does some nice eye-candy, like trying to figure out what the
last function was, and adding that information to the "@@ .." line.
libxdiff doesn't do that.
- The libxdiff thing has some known deficiencies. In particular, it gets
the "\No newline at end of file" case wrong. So this is currently for
the experimental branch only. I hope Davide will help fix it.
That said, I think the huge performance advantage, and the fact that it
integrates better is definitely worth it. But it should go into a
development branch at least due to the missing newline issue.
Technical note: this is based on libxdiff-0.17, but I did some surgery to
get rid of the extraneous fat - stuff that git doesn't need, and seriously
cutting down on mmfile_t, which had much more capabilities than the diff
algorithm either needed or used. In this version, "mmfile_t" is just a
trivial <pointer,length> tuple.
That said, I tried to keep the differences to simple removals, so that you
can do a diff between this and the libxdiff origin, and you'll basically
see just things getting deleted. Even the mmfile_t simplifications are
left in a state where the diffs should be readable.
Apologies to Davide, whom I'd love to get feedback on this all from (I
wrote my own "fill_mmfile()" for the new simpler mmfile_t format: the old
complex format had a helper function for that, but I did my surgery with
the goal in mind that eventually we _should_ just do
mmfile_t mf;
buf = read_sha1_file(sha1, type, &size);
mf->ptr = buf;
mf->size = size;
.. use "mf" directly ..
which was really a nightmare with the old "helpful" mmfile_t, and really
is that easy with the new cut-down interfaces).
[ Btw, as any hawk-eye can see from the diff, this was actually generated
with itself, so it is "self-hosting". That's about all the testing it
has gotten, along with the above kernel diff, which eye-balls correctly,
but shows the newline issue when you double-check it with "git-apply" ]
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
Diffstat (limited to 'diff.c')
-rw-r--r-- | diff.c | 79 |
1 files changed, 73 insertions, 6 deletions
@@ -8,6 +8,7 @@ #include "quote.h" #include "diff.h" #include "diffcore.h" +#include "xdiff/xdiff.h" static const char *diff_opts = "-pu"; @@ -178,6 +179,49 @@ static void emit_rewrite_diff(const char *name_a, copy_file('+', temp[1].name); } +static int fill_mmfile(mmfile_t *mf, const char *file) +{ + int fd = open(file, O_RDONLY); + struct stat st; + char *buf; + unsigned long size; + + mf->ptr = NULL; + mf->size = 0; + if (fd < 0) + return 0; + fstat(fd, &st); + size = st.st_size; + buf = xmalloc(size); + mf->ptr = buf; + mf->size = size; + while (size) { + int retval = read(fd, buf, size); + if (retval < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + break; + } + if (!retval) + break; + buf += retval; + size -= retval; + } + mf->size -= size; + close(fd); + return 0; +} + +static int fn_out(void *priv, mmbuffer_t *mb, int nbuf) +{ + int i; + + for (i = 0; i < nbuf; i++) + if (!fwrite(mb[i].ptr, mb[i].size, 1, stdout)) + return -1; + return 0; +} + static const char *builtin_diff(const char *name_a, const char *name_b, struct diff_tempfile *temp, @@ -186,6 +230,7 @@ static const char *builtin_diff(const char *name_a, const char **args) { int i, next_at, cmd_size; + mmfile_t mf1, mf2; const char *const diff_cmd = "diff -L%s -L%s"; const char *const diff_arg = "-- %s %s||:"; /* "||:" is to return 0 */ const char *input_name_sq[2]; @@ -255,12 +300,34 @@ static const char *builtin_diff(const char *name_a, } } - /* This is disgusting */ - *args++ = "sh"; - *args++ = "-c"; - *args++ = cmd; - *args = NULL; - return "/bin/sh"; + /* Un-quote the paths */ + if (label_path[0][0] != '/') + label_path[0] = quote_two("a/", name_a); + if (label_path[1][0] != '/') + label_path[1] = quote_two("b/", name_b); + + printf("--- %s\n", label_path[0]); + printf("+++ %s\n", label_path[1]); + + if (fill_mmfile(&mf1, temp[0].name) < 0 || + fill_mmfile(&mf2, temp[1].name) < 0) + die("unable to read files to diff"); + + /* Crazy xdl interfaces.. */ + { + xpparam_t xpp; + xdemitconf_t xecfg; + xdemitcb_t ecb; + + xpp.flags = XDF_NEED_MINIMAL; + xecfg.ctxlen = 3; + ecb.outf = fn_out; + xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); + } + + free(mf1.ptr); + free(mf2.ptr); + return NULL; } struct diff_filespec *alloc_filespec(const char *path) |