summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRussell Belfer <rb@github.com>2012-06-08 11:56:24 -0700
committerRussell Belfer <rb@github.com>2012-06-08 12:11:13 -0700
commit145e696b498a046762e4df9045c9b71440308486 (patch)
treeb37682dece97e69e2be1056d656dc8d6fc5d3155 /src
parent0abd724454078f2089701b54be94df7306dcfb8e (diff)
downloadlibgit2-145e696b498a046762e4df9045c9b71440308486.tar.gz
Minor fixes, cleanups, and clarifications
There are three actual changes in this commit: 1. When the trailing newline of a file is removed in a diff, the change will now be reported with `GIT_DIFF_LINE_DEL_EOFNL` passed to the callback. Previously, the `ADD_EOFNL` constant was given which was just an error in my understanding of when the various circumstances arose. `GIT_DIFF_LINE_ADD_EOFNL` is deprecated and should never be generated. A new newline is simply an `ADD`. 2. Rewrote the `diff_delta__merge_like_cgit` function that contains the core logic of the `git_diff_merge` implementation. The new version doesn't actually have significantly different behavior, but the logic should be much more obvious, I think. 3. Fixed a bug in `git_diff_merge` where it freed a string pool while some of the string data was still in use. This led to `git_diff_print_patch` accessing memory that had been freed. The rest of this commit contains improved documentation in `diff.h` to make the behavior and the equivalencies with core git clearer, and a bunch of new tests to cover the various cases, oh and a minor simplification of `examples/diff.c`.
Diffstat (limited to 'src')
-rw-r--r--src/diff.c63
-rw-r--r--src/diff_output.c11
2 files changed, 47 insertions, 27 deletions
diff --git a/src/diff.c b/src/diff.c
index e3167b90..02b89b46 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -130,37 +130,50 @@ fail:
static git_diff_delta *diff_delta__merge_like_cgit(
const git_diff_delta *a, const git_diff_delta *b, git_pool *pool)
{
- git_diff_delta *dup = diff_delta__dup(a, pool);
- if (!dup)
- return NULL;
-
- if (git_oid_cmp(&dup->new_file.oid, &b->new_file.oid) == 0)
- return dup;
-
- git_oid_cpy(&dup->new_file.oid, &b->new_file.oid);
-
- dup->new_file.mode = b->new_file.mode;
- dup->new_file.size = b->new_file.size;
- dup->new_file.flags = b->new_file.flags;
+ git_diff_delta *dup;
/* Emulate C git for merging two diffs (a la 'git diff <sha>').
*
* When C git does a diff between the work dir and a tree, it actually
* diffs with the index but uses the workdir contents. This emulates
* those choices so we can emulate the type of diff.
+ *
+ * We have three file descriptions here, let's call them:
+ * f1 = a->old_file
+ * f2 = a->new_file AND b->old_file
+ * f3 = b->new_file
*/
- if (git_oid_cmp(&dup->old_file.oid, &dup->new_file.oid) == 0) {
- if (dup->status == GIT_DELTA_DELETED)
- /* preserve pending delete info */;
- else if (b->status == GIT_DELTA_UNTRACKED ||
- b->status == GIT_DELTA_IGNORED)
- dup->status = b->status;
- else
+
+ /* if f2 == f3 or f2 is deleted, then just dup the 'a' diff */
+ if (b->status == GIT_DELTA_UNMODIFIED || a->status == GIT_DELTA_DELETED)
+ return diff_delta__dup(a, pool);
+
+ /* otherwise, base this diff on the 'b' diff */
+ if ((dup = diff_delta__dup(b, pool)) == NULL)
+ return NULL;
+
+ /* If 'a' status is uninteresting, then we're done */
+ if (a->status == GIT_DELTA_UNMODIFIED)
+ return dup;
+
+ assert(a->status != GIT_DELTA_UNMODIFIED);
+ assert(b->status != GIT_DELTA_UNMODIFIED);
+
+ /* A cgit exception is that the diff of a file that is only in the
+ * index (i.e. not in HEAD nor workdir) is given as empty.
+ */
+ if (dup->status == GIT_DELTA_DELETED) {
+ if (a->status == GIT_DELTA_ADDED)
dup->status = GIT_DELTA_UNMODIFIED;
+ /* else don't overwrite DELETE status */
+ } else {
+ dup->status = a->status;
}
- else if (dup->status == GIT_DELTA_UNMODIFIED ||
- b->status == GIT_DELTA_DELETED)
- dup->status = b->status;
+
+ git_oid_cpy(&dup->old_file.oid, &a->old_file.oid);
+ dup->old_file.mode = a->old_file.mode;
+ dup->old_file.size = a->old_file.size;
+ dup->old_file.flags = a->old_file.flags;
return dup;
}
@@ -783,6 +796,12 @@ int git_diff_merge(
git_vector_swap(&onto->deltas, &onto_new);
git_pool_swap(&onto->pool, &onto_pool);
onto->new_src = from->new_src;
+
+ /* prefix strings also come from old pool, so recreate those.*/
+ onto->opts.old_prefix =
+ git_pool_strdup(&onto->pool, onto->opts.old_prefix);
+ onto->opts.new_prefix =
+ git_pool_strdup(&onto->pool, onto->opts.new_prefix);
}
git_vector_foreach(&onto_new, i, delta)
diff --git a/src/diff_output.c b/src/diff_output.c
index d1aa910b..92f7f8f2 100644
--- a/src/diff_output.c
+++ b/src/diff_output.c
@@ -83,12 +83,13 @@ static int diff_output_cb(void *priv, mmbuffer_t *bufs, int len)
info->cb_data, info->delta, &info->range, origin, bufs[1].ptr, bufs[1].size) < 0)
return -1;
- /* deal with adding and removing newline at EOF */
+ /* This should only happen if we are adding a line that does not
+ * have a newline at the end and the old code did. In that case,
+ * we have a ADD with a DEL_EOFNL as a pair.
+ */
if (len == 3) {
- if (origin == GIT_DIFF_LINE_ADDITION)
- origin = GIT_DIFF_LINE_ADD_EOFNL;
- else
- origin = GIT_DIFF_LINE_DEL_EOFNL;
+ origin = (origin == GIT_DIFF_LINE_ADDITION) ?
+ GIT_DIFF_LINE_DEL_EOFNL : GIT_DIFF_LINE_ADD_EOFNL;
return info->line_cb(
info->cb_data, info->delta, &info->range, origin, bufs[2].ptr, bufs[2].size);