summaryrefslogtreecommitdiff
path: root/src/diff_xdiff.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/diff_xdiff.c')
-rw-r--r--src/diff_xdiff.c135
1 files changed, 105 insertions, 30 deletions
diff --git a/src/diff_xdiff.c b/src/diff_xdiff.c
index 7694fb996..e0bc11f7f 100644
--- a/src/diff_xdiff.c
+++ b/src/diff_xdiff.c
@@ -24,26 +24,26 @@ static int git_xdiff_scan_int(const char **str, int *value)
return (digits > 0) ? 0 : -1;
}
-static int git_xdiff_parse_hunk(git_diff_range *range, const char *header)
+static int git_xdiff_parse_hunk(git_diff_hunk *hunk, const char *header)
{
/* expect something of the form "@@ -%d[,%d] +%d[,%d] @@" */
if (*header != '@')
return -1;
- if (git_xdiff_scan_int(&header, &range->old_start) < 0)
+ if (git_xdiff_scan_int(&header, &hunk->old_start) < 0)
return -1;
if (*header == ',') {
- if (git_xdiff_scan_int(&header, &range->old_lines) < 0)
+ if (git_xdiff_scan_int(&header, &hunk->old_lines) < 0)
return -1;
} else
- range->old_lines = 1;
- if (git_xdiff_scan_int(&header, &range->new_start) < 0)
+ hunk->old_lines = 1;
+ if (git_xdiff_scan_int(&header, &hunk->new_start) < 0)
return -1;
if (*header == ',') {
- if (git_xdiff_scan_int(&header, &range->new_lines) < 0)
+ if (git_xdiff_scan_int(&header, &hunk->new_lines) < 0)
return -1;
} else
- range->new_lines = 1;
- if (range->old_start < 0 || range->new_start < 0)
+ hunk->new_lines = 1;
+ if (hunk->old_start < 0 || hunk->new_start < 0)
return -1;
return 0;
@@ -51,38 +51,104 @@ static int git_xdiff_parse_hunk(git_diff_range *range, const char *header)
typedef struct {
git_xdiff_output *xo;
- git_diff_patch *patch;
- git_diff_range range;
+ git_patch *patch;
+ git_diff_hunk hunk;
+ int old_lineno, new_lineno;
+ mmfile_t xd_old_data, xd_new_data;
} git_xdiff_info;
+static int diff_update_lines(
+ git_xdiff_info *info,
+ git_diff_line *line,
+ const char *content,
+ size_t content_len)
+{
+ const char *scan = content, *scan_end = content + content_len;
+
+ for (line->num_lines = 0; scan < scan_end; ++scan)
+ if (*scan == '\n')
+ ++line->num_lines;
+
+ line->content = content;
+ line->content_len = content_len;
+
+ /* expect " "/"-"/"+", then data */
+ switch (line->origin) {
+ case GIT_DIFF_LINE_ADDITION:
+ case GIT_DIFF_LINE_DEL_EOFNL:
+ line->old_lineno = -1;
+ line->new_lineno = info->new_lineno;
+ info->new_lineno += (int)line->num_lines;
+ break;
+ case GIT_DIFF_LINE_DELETION:
+ case GIT_DIFF_LINE_ADD_EOFNL:
+ line->old_lineno = info->old_lineno;
+ line->new_lineno = -1;
+ info->old_lineno += (int)line->num_lines;
+ break;
+ case GIT_DIFF_LINE_CONTEXT:
+ case GIT_DIFF_LINE_CONTEXT_EOFNL:
+ line->old_lineno = info->old_lineno;
+ line->new_lineno = info->new_lineno;
+ info->old_lineno += (int)line->num_lines;
+ info->new_lineno += (int)line->num_lines;
+ break;
+ default:
+ giterr_set(GITERR_INVALID, "Unknown diff line origin %02x",
+ (unsigned int)line->origin);
+ return -1;
+ }
+
+ return 0;
+}
+
static int git_xdiff_cb(void *priv, mmbuffer_t *bufs, int len)
{
git_xdiff_info *info = priv;
- git_diff_patch *patch = info->patch;
- const git_diff_delta *delta = git_diff_patch_delta(patch);
+ git_patch *patch = info->patch;
+ const git_diff_delta *delta = git_patch_get_delta(patch);
git_diff_output *output = &info->xo->output;
+ git_diff_line line;
if (len == 1) {
- output->error = git_xdiff_parse_hunk(&info->range, bufs[0].ptr);
+ output->error = git_xdiff_parse_hunk(&info->hunk, bufs[0].ptr);
if (output->error < 0)
return output->error;
+ info->hunk.header_len = bufs[0].size;
+ if (info->hunk.header_len >= sizeof(info->hunk.header))
+ info->hunk.header_len = sizeof(info->hunk.header) - 1;
+ memcpy(info->hunk.header, bufs[0].ptr, info->hunk.header_len);
+ info->hunk.header[info->hunk.header_len] = '\0';
+
if (output->hunk_cb != NULL &&
- output->hunk_cb(delta, &info->range,
- bufs[0].ptr, bufs[0].size, output->payload))
+ output->hunk_cb(delta, &info->hunk, output->payload))
output->error = GIT_EUSER;
+
+ info->old_lineno = info->hunk.old_start;
+ info->new_lineno = info->hunk.new_start;
}
if (len == 2 || len == 3) {
/* expect " "/"-"/"+", then data */
- char origin =
+ line.origin =
(*bufs[0].ptr == '+') ? GIT_DIFF_LINE_ADDITION :
(*bufs[0].ptr == '-') ? GIT_DIFF_LINE_DELETION :
GIT_DIFF_LINE_CONTEXT;
- if (output->data_cb != NULL &&
- output->data_cb(delta, &info->range,
- origin, bufs[1].ptr, bufs[1].size, output->payload))
+ if (line.origin == GIT_DIFF_LINE_ADDITION)
+ line.content_offset = bufs[1].ptr - info->xd_new_data.ptr;
+ else if (line.origin == GIT_DIFF_LINE_DELETION)
+ line.content_offset = bufs[1].ptr - info->xd_old_data.ptr;
+ else
+ line.content_offset = -1;
+
+ output->error = diff_update_lines(
+ info, &line, bufs[1].ptr, bufs[1].size);
+
+ if (!output->error &&
+ output->data_cb != NULL &&
+ output->data_cb(delta, &info->hunk, &line, output->payload))
output->error = GIT_EUSER;
}
@@ -92,26 +158,30 @@ static int git_xdiff_cb(void *priv, mmbuffer_t *bufs, int len)
* If we have a '-' and a third buf, then we have removed a line
* with out a newline but added a blank line, so ADD_EOFNL.
*/
- char origin =
+ line.origin =
(*bufs[0].ptr == '+') ? GIT_DIFF_LINE_DEL_EOFNL :
(*bufs[0].ptr == '-') ? GIT_DIFF_LINE_ADD_EOFNL :
GIT_DIFF_LINE_CONTEXT_EOFNL;
- if (output->data_cb != NULL &&
- output->data_cb(delta, &info->range,
- origin, bufs[2].ptr, bufs[2].size, output->payload))
+ line.content_offset = -1;
+
+ output->error = diff_update_lines(
+ info, &line, bufs[2].ptr, bufs[2].size);
+
+ if (!output->error &&
+ output->data_cb != NULL &&
+ output->data_cb(delta, &info->hunk, &line, output->payload))
output->error = GIT_EUSER;
}
return output->error;
}
-static int git_xdiff(git_diff_output *output, git_diff_patch *patch)
+static int git_xdiff(git_diff_output *output, git_patch *patch)
{
git_xdiff_output *xo = (git_xdiff_output *)output;
git_xdiff_info info;
git_diff_find_context_payload findctxt;
- mmfile_t xd_old_data, xd_new_data;
memset(&info, 0, sizeof(info));
info.patch = patch;
@@ -120,7 +190,7 @@ static int git_xdiff(git_diff_output *output, git_diff_patch *patch)
xo->callback.priv = &info;
git_diff_find_context_init(
- &xo->config.find_func, &findctxt, git_diff_patch__driver(patch));
+ &xo->config.find_func, &findctxt, git_patch__driver(patch));
xo->config.find_func_priv = &findctxt;
if (xo->config.find_func != NULL)
@@ -132,10 +202,10 @@ static int git_xdiff(git_diff_output *output, git_diff_patch *patch)
* updates are needed to xo->params.flags
*/
- git_diff_patch__old_data(&xd_old_data.ptr, &xd_old_data.size, patch);
- git_diff_patch__new_data(&xd_new_data.ptr, &xd_new_data.size, patch);
+ git_patch__old_data(&info.xd_old_data.ptr, &info.xd_old_data.size, patch);
+ git_patch__new_data(&info.xd_new_data.ptr, &info.xd_new_data.size, patch);
- xdl_diff(&xd_old_data, &xd_new_data,
+ xdl_diff(&info.xd_old_data, &info.xd_new_data,
&xo->params, &xo->config, &xo->callback);
git_diff_find_context_clear(&findctxt);
@@ -145,7 +215,7 @@ static int git_xdiff(git_diff_output *output, git_diff_patch *patch)
void git_xdiff_init(git_xdiff_output *xo, const git_diff_options *opts)
{
- uint32_t flags = opts ? opts->flags : GIT_DIFF_NORMAL;
+ uint32_t flags = opts ? opts->flags : 0;
xo->output.diff_cb = git_xdiff;
@@ -161,6 +231,11 @@ void git_xdiff_init(git_xdiff_output *xo, const git_diff_options *opts)
if (flags & GIT_DIFF_IGNORE_WHITESPACE_EOL)
xo->params.flags |= XDF_IGNORE_WHITESPACE_AT_EOL;
+ if (flags & GIT_DIFF_PATIENCE)
+ xo->params.flags |= XDF_PATIENCE_DIFF;
+ if (flags & GIT_DIFF_MINIMAL)
+ xo->params.flags |= XDF_NEED_MINIMAL;
+
memset(&xo->callback, 0, sizeof(xo->callback));
xo->callback.outf = git_xdiff_cb;
}