+static void diff_flush_name(struct diff_filepair *p,
+ int inter_name_termination,
+ int line_termination)
+{
+ char *path = p->two->path;
+
+ if (line_termination)
+ path = quote_one(p->two->path);
+ else
+ path = p->two->path;
+ printf("%s%c", path, line_termination);
+ if (p->two->path != path)
+ free(path);
+}
+
+int diff_unmodified_pair(struct diff_filepair *p)
+{
+ /* This function is written stricter than necessary to support
+ * the currently implemented transformers, but the idea is to
+ * let transformers to produce diff_filepairs any way they want,
+ * and filter and clean them up here before producing the output.
+ */
+ struct diff_filespec *one, *two;
+
+ if (DIFF_PAIR_UNMERGED(p))
+ return 0; /* unmerged is interesting */
+
+ one = p->one;
+ two = p->two;
+
+ /* deletion, addition, mode or type change
+ * and rename are all interesting.
+ */
+ if (DIFF_FILE_VALID(one) != DIFF_FILE_VALID(two) ||
+ DIFF_PAIR_MODE_CHANGED(p) ||
+ strcmp(one->path, two->path))
+ return 0;
+
+ /* both are valid and point at the same path. that is, we are
+ * dealing with a change.
+ */
+ if (one->sha1_valid && two->sha1_valid &&
+ !memcmp(one->sha1, two->sha1, sizeof(one->sha1)))
+ return 1; /* no change */
+ if (!one->sha1_valid && !two->sha1_valid)
+ return 1; /* both look at the same file on the filesystem. */
+ return 0;
+}
+
+static void diff_flush_patch(struct diff_filepair *p)
+{
+ if (diff_unmodified_pair(p))
+ return;
+
+ if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) ||
+ (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode)))
+ return; /* no tree diffs in patch format */
+
+ run_diff(p);
+}
+
+int diff_queue_is_empty(void)
+{
+ struct diff_queue_struct *q = &diff_queued_diff;
+ int i;
+ for (i = 0; i < q->nr; i++)
+ if (!diff_unmodified_pair(q->queue[i]))
+ return 0;
+ return 1;
+}
+
+#if DIFF_DEBUG
+void diff_debug_filespec(struct diff_filespec *s, int x, const char *one)
+{
+ fprintf(stderr, "queue[%d] %s (%s) %s %06o %s\n",
+ x, one ? one : "",
+ s->path,
+ DIFF_FILE_VALID(s) ? "valid" : "invalid",
+ s->mode,
+ s->sha1_valid ? sha1_to_hex(s->sha1) : "");
+ fprintf(stderr, "queue[%d] %s size %lu flags %d\n",
+ x, one ? one : "",
+ s->size, s->xfrm_flags);
+}
+
+void diff_debug_filepair(const struct diff_filepair *p, int i)
+{
+ diff_debug_filespec(p->one, i, "one");
+ diff_debug_filespec(p->two, i, "two");
+ fprintf(stderr, "score %d, status %c stays %d broken %d\n",
+ p->score, p->status ? p->status : '?',
+ p->source_stays, p->broken_pair);
+}
+
+void diff_debug_queue(const char *msg, struct diff_queue_struct *q)
+{
+ int i;
+ if (msg)
+ fprintf(stderr, "%s\n", msg);
+ fprintf(stderr, "q->nr = %d\n", q->nr);
+ for (i = 0; i < q->nr; i++) {
+ struct diff_filepair *p = q->queue[i];
+ diff_debug_filepair(p, i);
+ }
+}
+#endif
+
+static void diff_resolve_rename_copy(void)
+{
+ int i, j;
+ struct diff_filepair *p, *pp;
+ struct diff_queue_struct *q = &diff_queued_diff;
+
+ diff_debug_queue("resolve-rename-copy", q);
+
+ for (i = 0; i < q->nr; i++) {
+ p = q->queue[i];
+ p->status = 0; /* undecided */
+ if (DIFF_PAIR_UNMERGED(p))
+ p->status = DIFF_STATUS_UNMERGED;
+ else if (!DIFF_FILE_VALID(p->one))
+ p->status = DIFF_STATUS_ADDED;
+ else if (!DIFF_FILE_VALID(p->two))
+ p->status = DIFF_STATUS_DELETED;
+ else if (DIFF_PAIR_TYPE_CHANGED(p))
+ p->status = DIFF_STATUS_TYPE_CHANGED;
+
+ /* from this point on, we are dealing with a pair
+ * whose both sides are valid and of the same type, i.e.
+ * either in-place edit or rename/copy edit.
+ */
+ else if (DIFF_PAIR_RENAME(p)) {
+ if (p->source_stays) {
+ p->status = DIFF_STATUS_COPIED;
+ continue;
+ }
+ /* See if there is some other filepair that
+ * copies from the same source as us. If so
+ * we are a copy. Otherwise we are either a
+ * copy if the path stays, or a rename if it
+ * does not, but we already handled "stays" case.
+ */
+ for (j = i + 1; j < q->nr; j++) {
+ pp = q->queue[j];
+ if (strcmp(pp->one->path, p->one->path))
+ continue; /* not us */
+ if (!DIFF_PAIR_RENAME(pp))
+ continue; /* not a rename/copy */
+ /* pp is a rename/copy from the same source */
+ p->status = DIFF_STATUS_COPIED;
+ break;
+ }
+ if (!p->status)
+ p->status = DIFF_STATUS_RENAMED;
+ }
+ else if (memcmp(p->one->sha1, p->two->sha1, 20) ||
+ p->one->mode != p->two->mode)
+ p->status = DIFF_STATUS_MODIFIED;
+ else {
+ /* This is a "no-change" entry and should not
+ * happen anymore, but prepare for broken callers.
+ */
+ error("feeding unmodified %s to diffcore",
+ p->one->path);
+ p->status = DIFF_STATUS_UNKNOWN;
+ }
+ }
+ diff_debug_queue("resolve-rename-copy done", q);
+}
+
+void diff_flush(struct diff_options *options)
+{
+ struct diff_queue_struct *q = &diff_queued_diff;
+ int i;
+ int inter_name_termination = '\t';
+ int diff_output_format = options->output_format;
+ int line_termination = options->line_termination;
+
+ if (!line_termination)
+ inter_name_termination = 0;
+
+ for (i = 0; i < q->nr; i++) {
+ struct diff_filepair *p = q->queue[i];
+ if ((diff_output_format == DIFF_FORMAT_NO_OUTPUT) ||
+ (p->status == DIFF_STATUS_UNKNOWN))
+ continue;
+ if (p->status == 0)
+ die("internal error in diff-resolve-rename-copy");
+ switch (diff_output_format) {
+ case DIFF_FORMAT_PATCH:
+ diff_flush_patch(p);
+ break;
+ case DIFF_FORMAT_RAW:
+ case DIFF_FORMAT_NAME_STATUS:
+ diff_flush_raw(p, line_termination,
+ inter_name_termination,
+ diff_output_format);
+ break;
+ case DIFF_FORMAT_NAME:
+ diff_flush_name(p,
+ inter_name_termination,
+ line_termination);
+ break;
+ }
+ diff_free_filepair(q->queue[i]);
+ }
+ free(q->queue);
+ q->queue = NULL;
+ q->nr = q->alloc = 0;
+}
+
+static void diffcore_apply_filter(const char *filter)
+{
+ int i;
+ struct diff_queue_struct *q = &diff_queued_diff;
+ struct diff_queue_struct outq;
+ outq.queue = NULL;
+ outq.nr = outq.alloc = 0;
+
+ if (!filter)
+ return;
+
+ if (strchr(filter, DIFF_STATUS_FILTER_AON)) {
+ int found;
+ for (i = found = 0; !found && i < q->nr; i++) {
+ struct diff_filepair *p = q->queue[i];
+ if (((p->status == DIFF_STATUS_MODIFIED) &&
+ ((p->score &&
+ strchr(filter, DIFF_STATUS_FILTER_BROKEN)) ||
+ (!p->score &&
+ strchr(filter, DIFF_STATUS_MODIFIED)))) ||
+ ((p->status != DIFF_STATUS_MODIFIED) &&
+ strchr(filter, p->status)))
+ found++;
+ }
+ if (found)
+ return;
+
+ /* otherwise we will clear the whole queue
+ * by copying the empty outq at the end of this
+ * function, but first clear the current entries
+ * in the queue.
+ */
+ for (i = 0; i < q->nr; i++)
+ diff_free_filepair(q->queue[i]);
+ }
+ else {
+ /* Only the matching ones */
+ for (i = 0; i < q->nr; i++) {
+ struct diff_filepair *p = q->queue[i];
+
+ if (((p->status == DIFF_STATUS_MODIFIED) &&
+ ((p->score &&
+ strchr(filter, DIFF_STATUS_FILTER_BROKEN)) ||
+ (!p->score &&
+ strchr(filter, DIFF_STATUS_MODIFIED)))) ||
+ ((p->status != DIFF_STATUS_MODIFIED) &&
+ strchr(filter, p->status)))
+ diff_q(&outq, p);
+ else
+ diff_free_filepair(p);
+ }
+ }
+ free(q->queue);
+ *q = outq;
+}
+
+void diffcore_std(struct diff_options *options)
+{
+ if (options->paths && options->paths[0])
+ diffcore_pathspec(options->paths);
+ if (options->break_opt != -1)
+ diffcore_break(options->break_opt);
+ if (options->detect_rename)
+ diffcore_rename(options);
+ if (options->break_opt != -1)
+ diffcore_merge_broken();
+ if (options->pickaxe)
+ diffcore_pickaxe(options->pickaxe, options->pickaxe_opts);
+ if (options->orderfile)
+ diffcore_order(options->orderfile);
+ diff_resolve_rename_copy();
+ diffcore_apply_filter(options->filter);
+}
+
+
+void diffcore_std_no_resolve(struct diff_options *options)
+{
+ if (options->pickaxe)
+ diffcore_pickaxe(options->pickaxe, options->pickaxe_opts);
+ if (options->orderfile)
+ diffcore_order(options->orderfile);
+ diffcore_apply_filter(options->filter);
+}
+
+void diff_addremove(struct diff_options *options,
+ int addremove, unsigned mode,
+ const unsigned char *sha1,
+ const char *base, const char *path)
+{
+ char concatpath[PATH_MAX];
+ struct diff_filespec *one, *two;
+
+ /* This may look odd, but it is a preparation for
+ * feeding "there are unchanged files which should
+ * not produce diffs, but when you are doing copy
+ * detection you would need them, so here they are"
+ * entries to the diff-core. They will be prefixed
+ * with something like '=' or '*' (I haven't decided
+ * which but should not make any difference).
+ * Feeding the same new and old to diff_change()
+ * also has the same effect.
+ * Before the final output happens, they are pruned after
+ * merged into rename/copy pairs as appropriate.
+ */
+ if (options->reverse_diff)
+ addremove = (addremove == '+' ? '-' :
+ addremove == '-' ? '+' : addremove);
+
+ if (!path) path = "";
+ sprintf(concatpath, "%s%s", base, path);
+ one = alloc_filespec(concatpath);
+ two = alloc_filespec(concatpath);
+
+ if (addremove != '+')
+ fill_filespec(one, sha1, mode);
+ if (addremove != '-')
+ fill_filespec(two, sha1, mode);
+
+ diff_queue(&diff_queued_diff, one, two);
+}
+
+void diff_change(struct diff_options *options,
+ unsigned old_mode, unsigned new_mode,
+ const unsigned char *old_sha1,
+ const unsigned char *new_sha1,
+ const char *base, const char *path)
+{
+ char concatpath[PATH_MAX];
+ struct diff_filespec *one, *two;
+
+ if (options->reverse_diff) {
+ unsigned tmp;
+ const unsigned char *tmp_c;
+ tmp = old_mode; old_mode = new_mode; new_mode = tmp;
+ tmp_c = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_c;
+ }
+ if (!path) path = "";
+ sprintf(concatpath, "%s%s", base, path);
+ one = alloc_filespec(concatpath);
+ two = alloc_filespec(concatpath);
+ fill_filespec(one, old_sha1, old_mode);
+ fill_filespec(two, new_sha1, new_mode);
+
+ diff_queue(&diff_queued_diff, one, two);
+}
+
+void diff_unmerge(struct diff_options *options,
+ const char *path)
+{
+ struct diff_filespec *one, *two;
+ one = alloc_filespec(path);
+ two = alloc_filespec(path);
+ diff_queue(&diff_queued_diff, one, two);