Subject: [PATCH WIP 2/3] tree-diff: teach it to understand exclude patterns

From: Nguyễn Thái Ngọc Duy <[email-masked]>
Date: Wed, 5 Oct 2011 18:18:07 +1100

We introduce a new set of API, diff_tree_*_with_exclude, that also
exclude entries based on .gitignore patterns.

Signed-off-by: Nguyễn Thái Ngọc Duy <[email-masked]>
---
 diff.h      |   11 +++++++++++
 tree-diff.c |   58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 65 insertions(+), 4 deletions(-)

diff --git a/diff.h b/diff.h
index 8c66b59..51c8a5f 100644
--- a/diff.h
+++ b/diff.h
@@ -12,6 +12,7 @@ struct diff_queue_struct;
 struct strbuf;
 struct diff_filespec;
 struct userdiff_driver;
+struct exclude_list;
 
 typedef void (*change_fn_t)(struct diff_options *options,
 		 unsigned old_mode, unsigned new_mode,
@@ -170,8 +171,18 @@ extern void diff_tree_setup_paths(const char **paths, struct diff_options *);
 extern void diff_tree_release_paths(struct diff_options *);
 extern int diff_tree(struct tree_desc *t1, struct tree_desc *t2,
 		     const char *base, struct diff_options *opt);
+extern int diff_tree_with_exclude(struct tree_desc *t1, struct tree_desc *t2,
+				  const char *base, struct diff_options *opt,
+				  struct exclude_list *el,
+				  int def_excl1, int def_excl2);
 extern int diff_tree_sha1(const unsigned char *old, const unsigned char *new,
 			  const char *base, struct diff_options *opt);
+extern int diff_tree_sha1_with_exclude(const unsigned char *old,
+				       const unsigned char *new,
+				       const char *base,
+				       struct diff_options *opt,
+				       struct exclude_list *el,
+				       int def_excl1, int def_excl2);
 extern int diff_root_tree_sha1(const unsigned char *new, const char *base,
                                struct diff_options *opt);
 
diff --git a/tree-diff.c b/tree-diff.c
index b3cc2e4..9938ccf 100644
--- a/tree-diff.c
+++ b/tree-diff.c
@@ -5,12 +5,14 @@
 #include "diff.h"
 #include "diffcore.h"
 #include "tree.h"
+#include "dir.h"
 
 static void show_entry(struct diff_options *opt, const char *prefix,
 		       struct tree_desc *desc, struct strbuf *base);
 
 static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2,
-			      struct strbuf *base, struct diff_options *opt)
+			      struct strbuf *base, struct diff_options *opt,
+			      struct exclude_list *el, int def_excl1, int def_excl2)
 {
 	unsigned mode1, mode2;
 	const char *path1, *path2;
@@ -52,7 +54,8 @@ static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2,
 				    sha1, sha2, base->buf, 0, 0);
 		}
 		strbuf_addch(base, '/');
-		diff_tree_sha1(sha1, sha2, base->buf, opt);
+		diff_tree_sha1_with_exclude(sha1, sha2, base->buf, opt,
+					    el, def_excl1, def_excl2);
 	} else {
 		opt->change(opt, mode1, mode2, sha1, sha2, base->buf, 0, 0);
 	}
@@ -113,6 +116,31 @@ static void show_entry(struct diff_options *opt, const char *prefix,
 	strbuf_setlen(base, old_baselen);
 }
 
+static int skip_excludes(struct tree_desc *t, struct strbuf *base,
+			 struct exclude_list *el, int defval)
+{
+	for (; t->size; update_tree_entry(t)) {
+		/*
+		 * excluded_from_list only cares whether dtype is
+		 * DT_DIR or something else (except DT_UNKNOWN). Any
+		 * other value would do
+		 */
+		int dtype = S_ISDIR(t->entry.mode) ? DT_DIR : DT_REG;
+		int ret = excluded_from_list(base->buf, base->len, t->entry.path,
+					     &dtype, el);
+
+		/* If undecided, use matching result of parent dir in defval */
+		if (ret < 0)
+			ret = defval;
+
+		if (ret == 1 && dtype == DT_REG)
+			;
+		else
+			return ret;
+	}
+	return defval;
+}
+
 static void skip_uninteresting(struct tree_desc *t, struct strbuf *base,
 			       struct diff_options *opt, int *match)
 {
@@ -130,9 +158,17 @@ static void skip_uninteresting(struct tree_desc *t, struct strbuf *base,
 int diff_tree(struct tree_desc *t1, struct tree_desc *t2,
 	      const char *base_str, struct diff_options *opt)
 {
+	return diff_tree_with_exclude(t1, t2, base_str, opt, NULL, 0, 0);
+}
+
+int diff_tree_with_exclude(struct tree_desc *t1, struct tree_desc *t2,
+			   const char *base_str, struct diff_options *opt,
+			   struct exclude_list *el, int def_excl1, int def_excl2)
+{
 	struct strbuf base;
 	int baselen = strlen(base_str);
 	int t1_match = 0, t2_match = 0;
+	int excl1 = 0, excl2 = 0;
 
 	/* Enable recursion indefinitely */
 	opt->pathspec.recursive = DIFF_OPT_TST(opt, RECURSIVE);
@@ -148,6 +184,10 @@ int diff_tree(struct tree_desc *t1, struct tree_desc *t2,
 			skip_uninteresting(t1, &base, opt, &t1_match);
 			skip_uninteresting(t2, &base, opt, &t2_match);
 		}
+		if (el && el->nr) {
+			excl1 = skip_excludes(t1, &base, el, def_excl1);
+			excl2 = skip_excludes(t2, &base, el, def_excl2);
+		}
 		if (!t1->size) {
 			if (!t2->size)
 				break;
@@ -160,7 +200,7 @@ int diff_tree(struct tree_desc *t1, struct tree_desc *t2,
 			update_tree_entry(t1);
 			continue;
 		}
-		switch (compare_tree_entry(t1, t2, &base, opt)) {
+		switch (compare_tree_entry(t1, t2, &base, opt, el, excl1, excl2)) {
 		case -1:
 			update_tree_entry(t1);
 			continue;
@@ -267,6 +307,16 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co
 
 int diff_tree_sha1(const unsigned char *old, const unsigned char *new, const char *base, struct diff_options *opt)
 {
+	return diff_tree_sha1_with_exclude(old, new, base, opt, NULL, 0, 0);
+}
+
+int diff_tree_sha1_with_exclude(const unsigned char *old,
+				const unsigned char *new,
+				const char *base,
+				struct diff_options *opt,
+				struct exclude_list *el,
+				int def_excl1, int def_excl2)
+{
 	void *tree1, *tree2;
 	struct tree_desc t1, t2;
 	unsigned long size1, size2;
@@ -280,7 +330,7 @@ int diff_tree_sha1(const unsigned char *old, const unsigned char *new, const cha
 		die("unable to read destination tree (%s)", sha1_to_hex(new));
 	init_tree_desc(&t1, tree1, size1);
 	init_tree_desc(&t2, tree2, size2);
-	retval = diff_tree(&t1, &t2, base, opt);
+	retval = diff_tree_with_exclude(&t1, &t2, base, opt, el, def_excl1, def_excl2);
 	if (!*base && DIFF_OPT_TST(opt, FOLLOW_RENAMES) && diff_might_be_rename()) {
 		init_tree_desc(&t1, tree1, size1);
 		init_tree_desc(&t2, tree2, size2);
-- 
1.7.3.1.256.g2539c.dirty