#include "diffcore.h"
#include "quote.h"
-struct path_list {
- struct path_list *next;
- int len;
- char *path;
- unsigned char sha1[20];
- unsigned char parent_sha1[FLEX_ARRAY][20];
-};
-
static int uninteresting(struct diff_filepair *p)
{
if (diff_unmodified_pair(p))
return 0;
}
-static struct path_list *intersect_paths(struct path_list *curr,
- int n, int num_parent)
+static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr, int n, int num_parent)
{
struct diff_queue_struct *q = &diff_queued_diff;
- struct path_list *p;
+ struct combine_diff_path *p;
int i;
if (!n) {
- struct path_list *list = NULL, **tail = &list;
+ struct combine_diff_path *list = NULL, **tail = &list;
for (i = 0; i < q->nr; i++) {
int len;
const char *path;
return different;
}
-static int make_hunks(struct sline *sline, unsigned long cnt,
- int num_parent, int dense)
+static unsigned long adjust_hunk_tail(struct sline *sline,
+ unsigned long all_mask,
+ unsigned long hunk_begin,
+ unsigned long i)
+{
+ /* i points at the first uninteresting line.
+ * If the last line of the hunk was interesting
+ * only because it has some deletion, then
+ * it is not all that interesting for the
+ * purpose of giving trailing context lines.
+ */
+ if ((hunk_begin + 1 <= i) &&
+ ((sline[i-1].flag & all_mask) == all_mask))
+ i--;
+ return i;
+}
+
+static unsigned long next_interesting(struct sline *sline,
+ unsigned long mark,
+ unsigned long i,
+ unsigned long cnt,
+ int uninteresting)
+{
+ while (i < cnt)
+ if (uninteresting ?
+ !(sline[i].flag & mark) :
+ (sline[i].flag & mark))
+ return i;
+ else
+ i++;
+ return cnt;
+}
+
+static int give_context(struct sline *sline, unsigned long cnt, int num_parent)
{
unsigned long all_mask = (1UL<<num_parent) - 1;
unsigned long mark = (1UL<<num_parent);
unsigned long i;
- int has_interesting = 0;
- i = 0;
+ i = next_interesting(sline, mark, 0, cnt, 0);
+ if (cnt <= i)
+ return 0;
+
while (i < cnt) {
- if (interesting(&sline[i], all_mask)) {
- unsigned long j = (context < i) ? i - context : 0;
- while (j <= i)
+ unsigned long j = (context < i) ? (i - context) : 0;
+ unsigned long k;
+ while (j < i)
+ sline[j++].flag |= mark;
+
+ again:
+ j = next_interesting(sline, mark, i, cnt, 1);
+ if (cnt <= j)
+ break; /* the rest are all interesting */
+
+ /* lookahead context lines */
+ k = next_interesting(sline, mark, j, cnt, 0);
+ j = adjust_hunk_tail(sline, all_mask, i, j);
+
+ if (k < j + context) {
+ /* k is interesting and [j,k) are not, but
+ * paint them interesting because the gap is small.
+ */
+ while (j < k)
sline[j++].flag |= mark;
- while (++i < cnt) {
- if (!interesting(&sline[i], all_mask))
- break;
- sline[i].flag |= mark;
- }
- j = (i + context < cnt) ? i + context : cnt;
- while (i < j)
- sline[i++].flag |= mark;
- has_interesting = 1;
- continue;
+ i = k;
+ goto again;
}
- i++;
+
+ /* j is the first uninteresting line and there is
+ * no overlap beyond it within context lines.
+ */
+ i = k;
+ k = (j + context < cnt) ? j + context : cnt;
+ while (j < k)
+ sline[j++].flag |= mark;
+ }
+ return 1;
+}
+
+static int make_hunks(struct sline *sline, unsigned long cnt,
+ int num_parent, int dense)
+{
+ unsigned long all_mask = (1UL<<num_parent) - 1;
+ unsigned long mark = (1UL<<num_parent);
+ unsigned long i;
+ int has_interesting = 0;
+
+ for (i = 0; i < cnt; i++) {
+ if (interesting(&sline[i], all_mask))
+ sline[i].flag |= mark;
+ else
+ sline[i].flag &= ~mark;
}
if (!dense)
- return has_interesting;
+ return give_context(sline, cnt, num_parent);
/* Look at each hunk, and if we have changes from only one
* parent, or the changes are the same from all but one
* parent, mark that uninteresting.
*/
- has_interesting = 0;
i = 0;
while (i < cnt) {
- int j, hunk_end, same, diff;
+ unsigned long j, hunk_begin, hunk_end;
+ int same, diff;
unsigned long same_diff, all_diff;
while (i < cnt && !(sline[i].flag & mark))
i++;
if (cnt <= i)
break; /* No more interesting hunks */
- for (hunk_end = i + 1; hunk_end < cnt; hunk_end++)
- if (!(sline[hunk_end].flag & mark))
- break;
+ hunk_begin = i;
+ for (j = i + 1; j < cnt; j++) {
+ if (!(sline[j].flag & mark)) {
+ /* Look beyond the end to see if there
+ * is an interesting line after this
+ * hunk within context span.
+ */
+ unsigned long la; /* lookahead */
+ int contin = 0;
+ la = adjust_hunk_tail(sline, all_mask,
+ hunk_begin, j);
+ la = (la + context < cnt) ?
+ (la + context) : cnt;
+ while (j <= --la) {
+ if (sline[la].flag & mark) {
+ contin = 1;
+ break;
+ }
+ }
+ if (!contin)
+ break;
+ j = la;
+ }
+ }
+ hunk_end = j;
+
/* [i..hunk_end) are interesting. Now does it have
* the same change with all but one parent?
*/
}
if ((num_parent - 1 <= same) || (diff == 1)) {
/* This hunk is not that interesting after all */
- for (j = i; j < hunk_end; j++)
+ for (j = hunk_begin; j < hunk_end; j++)
sline[j].flag &= ~mark;
}
- else
- has_interesting = 1;
i = hunk_end;
}
+
+ has_interesting = give_context(sline, cnt, num_parent);
return has_interesting;
}
}
}
-static int show_combined_diff(struct path_list *elem, int num_parent,
- int dense, const char *header, int show_empty)
+int show_combined_diff(struct combine_diff_path *elem, int num_parent,
+ int dense, const char *header, int show_empty)
{
unsigned long size, cnt, lno;
char *result, *cp, *ep;
struct sline *sline; /* survived lines */
int i, show_hunks, shown_header = 0;
- char ourtmp[TMPPATHLEN];
+ char ourtmp_buf[TMPPATHLEN];
+ char *ourtmp = ourtmp_buf;
/* Read the result of merge first */
- result = grab_blob(elem->sha1, &size);
- write_to_temp_file(ourtmp, result, size);
+ if (memcmp(elem->sha1, null_sha1, 20)) {
+ result = grab_blob(elem->sha1, &size);
+ write_to_temp_file(ourtmp, result, size);
+ }
+ else {
+ struct stat st;
+ int fd;
+ ourtmp = elem->path;
+ if (0 <= (fd = open(ourtmp, O_RDONLY)) &&
+ !fstat(fd, &st)) {
+ int len = st.st_size;
+ int cnt = 0;
+
+ size = len;
+ result = xmalloc(len + 1);
+ while (cnt < len) {
+ int done = xread(fd, result+cnt, len-cnt);
+ if (done == 0)
+ break;
+ if (done < 0)
+ die("read error '%s'", ourtmp);
+ cnt += done;
+ }
+ result[len] = 0;
+ }
+ else {
+ /* deleted file */
+ size = 0;
+ result = xmalloc(1);
+ result[0] = 0;
+ ourtmp = "/dev/null";
+ }
+ if (0 <= fd)
+ close(fd);
+ }
for (cnt = 0, cp = result; cp - result < size; cp++) {
if (*cp == '\n')
putchar('\n');
dump_sline(sline, cnt, num_parent);
}
- unlink(ourtmp);
+ if (ourtmp == ourtmp_buf)
+ unlink(ourtmp);
free(result);
for (i = 0; i < cnt; i++) {
struct commit *commit = lookup_commit(sha1);
struct diff_options diffopts;
struct commit_list *parents;
- struct path_list *p, *paths = NULL;
+ struct combine_diff_path *p, *paths = NULL;
int num_parent, i, num_paths;
diff_setup(&diffopts);
/* Clean things up */
while (paths) {
- struct path_list *tmp = paths;
+ struct combine_diff_path *tmp = paths;
paths = paths->next;
free(tmp);
}