Commit aaa1358905e35eaa19a177bd11797d1a38d6cc03

Stefan Sperling 2019-06-01T15:25:13

generalize got_diff_tree() by adding a diff output callback

diff --git a/got/got.c b/got/got.c
index d428a78..0cb4b58 100644
--- a/got/got.c
+++ b/got/got.c
@@ -759,6 +759,7 @@ print_patch(struct got_commit_object *commit, struct got_object_id *id,
 	struct got_tree_object *tree1 = NULL, *tree2;
 	struct got_object_qid *qid;
 	char *id_str1 = NULL, *id_str2;
+	struct got_diff_blob_output_unidiff_arg arg;
 
 	err = got_object_open_as_tree(&tree2, repo,
 	    got_object_commit_get_tree_id(commit));
@@ -789,7 +790,10 @@ print_patch(struct got_commit_object *commit, struct got_object_id *id,
 		goto done;
 
 	printf("diff %s %s\n", id_str1 ? id_str1 : "/dev/null", id_str2);
-	err = got_diff_tree(tree1, tree2, "", "", diff_context, repo, stdout);
+	arg.diff_context = diff_context;
+	arg.outfile = stdout;
+	err = got_diff_tree(tree1, tree2, "", "", repo,
+	    got_diff_blob_output_unidiff, &arg);
 done:
 	if (tree1)
 		got_object_tree_close(tree1);
diff --git a/include/got_diff.h b/include/got_diff.h
index 79cfc0f..8ab2399 100644
--- a/include/got_diff.h
+++ b/include/got_diff.h
@@ -34,15 +34,40 @@ const struct got_error *got_diff_blob_file(struct got_blob_object *, FILE *,
     size_t, const char *, int, FILE *);
 
 /*
- * Compute the differences between two trees and write unified diff text
- * to the provided output FILE. Two const char * diff header labels may
- * be provided which will be used to identify each blob in the diff output.
- * If a label is NULL, use the blob's SHA1 checksum instead.
- * The number of context lines to show in the diff must be specified as well.
+ * A callback function invoked to handle the differences between two blobs
+ * when diffing trees with got_diff_tree(). This callback receives two blobs,
+ * their respective IDs, and two corresponding paths within the diffed trees.
+ * The first blob contains content from the old side of the diff, and
+ * the second blob contains content on the new side of the diff.
+ * The set of arguments relating to either blob may be NULL to indicate
+ * that no content is present on its respective side of the diff.
+ */
+typedef const struct got_error *(*got_diff_blob_cb)(void *,
+    struct got_blob_object *, struct got_blob_object *,
+    struct got_object_id *, struct got_object_id *,
+    const char *, const char *, struct got_repository *);
+
+/*
+ * A pre-defined implementation of got_diff_blob_cb() which appends unidiff
+ * output to a file. The caller must allocate and fill in the argument
+ * structure.
+ */
+struct got_diff_blob_output_unidiff_arg {
+	FILE *outfile;		/* Unidiff text will be written here. */
+	int diff_context;	/* Sets the number of context lines. */
+};
+const struct got_error *got_diff_blob_output_unidiff(void *,
+    struct got_blob_object *, struct got_blob_object *,
+    struct got_object_id *, struct got_object_id *,
+    const char *, const char *, struct got_repository *);
+
+/*
+ * Compute the differences between two trees and invoke the provided
+ * got_diff_blob_cb() callback when content differs.
  */
 const struct got_error *got_diff_tree(struct got_tree_object *,
-    struct got_tree_object *, const char *label1, const char *label2,
-    int, struct got_repository *, FILE *);
+    struct got_tree_object *, const char *, const char *,
+    struct got_repository *, got_diff_blob_cb cb, void *cb_arg);
 
 /*
  * Diff two objects, assuming both objects are blobs. Two const char * diff
diff --git a/lib/diff.c b/lib/diff.c
index 1c51bb9..4da542a 100644
--- a/lib/diff.c
+++ b/lib/diff.c
@@ -119,6 +119,18 @@ done:
 }
 
 const struct got_error *
+got_diff_blob_output_unidiff(void *arg, struct got_blob_object *blob1,
+    struct got_blob_object *blob2, struct got_object_id *id1,
+    struct got_object_id *id2, const char *label1, const char *label2,
+    struct got_repository *repo)
+{
+	struct got_diff_blob_output_unidiff_arg *a = arg;
+
+	return diff_blobs(blob1, blob2, label1, label2, a->diff_context,
+	    a->outfile, NULL);
+}
+
+const struct got_error *
 got_diff_blob(struct got_blob_object *blob1, struct got_blob_object *blob2,
     const char *label1, const char *label2, int diff_context, FILE *outfile)
 {
@@ -216,7 +228,7 @@ got_diff_free_changes(struct got_diff_changes *changes)
 
 static const struct got_error *
 diff_added_blob(struct got_object_id *id, const char *label,
-    int diff_context, struct got_repository *repo, FILE *outfile)
+    struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg)
 {
 	const struct got_error *err;
 	struct got_blob_object  *blob = NULL;
@@ -229,7 +241,7 @@ diff_added_blob(struct got_object_id *id, const char *label,
 	err = got_object_blob_open(&blob, repo, obj, 8192);
 	if (err)
 		goto done;
-	err = got_diff_blob(NULL, blob, NULL, label, diff_context, outfile);
+	err = cb(cb_arg, NULL, blob, NULL, id, NULL, label, repo);
 done:
 	got_object_close(obj);
 	if (blob)
@@ -239,8 +251,8 @@ done:
 
 static const struct got_error *
 diff_modified_blob(struct got_object_id *id1, struct got_object_id *id2,
-    const char *label1, const char *label2, int diff_context,
-    struct got_repository *repo, FILE *outfile)
+    const char *label1, const char *label2, struct got_repository *repo,
+    got_diff_blob_cb cb, void *cb_arg)
 {
 	const struct got_error *err;
 	struct got_object *obj1 = NULL;
@@ -272,9 +284,7 @@ diff_modified_blob(struct got_object_id *id1, struct got_object_id *id2,
 	if (err)
 		goto done;
 
-	err = got_diff_blob(blob1, blob2, label1, label2, diff_context,
-	    outfile);
-
+	err = cb(cb_arg, blob1, blob2, id1, id2, label1, label2, repo);
 done:
 	if (obj1)
 		got_object_close(obj1);
@@ -289,7 +299,7 @@ done:
 
 static const struct got_error *
 diff_deleted_blob(struct got_object_id *id, const char *label,
-    int diff_context, struct got_repository *repo, FILE *outfile)
+    struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg)
 {
 	const struct got_error *err;
 	struct got_blob_object  *blob = NULL;
@@ -302,7 +312,7 @@ diff_deleted_blob(struct got_object_id *id, const char *label,
 	err = got_object_blob_open(&blob, repo, obj, 8192);
 	if (err)
 		goto done;
-	err = got_diff_blob(blob, NULL, label, NULL, diff_context, outfile);
+	err = cb(cb_arg, blob, NULL, id, NULL, label, NULL, repo);
 done:
 	got_object_close(obj);
 	if (blob)
@@ -312,7 +322,7 @@ done:
 
 static const struct got_error *
 diff_added_tree(struct got_object_id *id, const char *label,
-    int diff_context, struct got_repository *repo, FILE *outfile)
+    struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg)
 {
 	const struct got_error *err = NULL;
 	struct got_object *treeobj = NULL;
@@ -331,9 +341,7 @@ diff_added_tree(struct got_object_id *id, const char *label,
 	if (err)
 		goto done;
 
-	err = got_diff_tree(NULL, tree, NULL, label, diff_context, repo,
-	    outfile);
-
+	err = got_diff_tree(NULL, tree, NULL, label, repo, cb, cb_arg);
 done:
 	if (tree)
 		got_object_tree_close(tree);
@@ -344,8 +352,8 @@ done:
 
 static const struct got_error *
 diff_modified_tree(struct got_object_id *id1, struct got_object_id *id2,
-    const char *label1, const char *label2, int diff_context,
-    struct got_repository *repo, FILE *outfile)
+    const char *label1, const char *label2, struct got_repository *repo,
+    got_diff_blob_cb cb, void *cb_arg)
 {
 	const struct got_error *err;
 	struct got_object *treeobj1 = NULL;
@@ -379,8 +387,7 @@ diff_modified_tree(struct got_object_id *id1, struct got_object_id *id2,
 	if (err)
 		goto done;
 
-	err = got_diff_tree(tree1, tree2, label1, label2, diff_context, repo,
-	    outfile);
+	err = got_diff_tree(tree1, tree2, label1, label2, repo, cb, cb_arg);
 
 done:
 	if (tree1)
@@ -396,7 +403,7 @@ done:
 
 static const struct got_error *
 diff_deleted_tree(struct got_object_id *id, const char *label,
-    int diff_context, struct got_repository *repo, FILE *outfile)
+    struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg)
 {
 	const struct got_error *err;
 	struct got_object *treeobj = NULL;
@@ -415,8 +422,7 @@ diff_deleted_tree(struct got_object_id *id, const char *label,
 	if (err)
 		goto done;
 
-	err = got_diff_tree(tree, NULL, label, NULL, diff_context, repo,
-	    outfile);
+	err = got_diff_tree(tree, NULL, label, NULL, repo, cb, cb_arg);
 done:
 	if (tree)
 		got_object_tree_close(tree);
@@ -427,7 +433,8 @@ done:
 
 static const struct got_error *
 diff_kind_mismatch(struct got_object_id *id1, struct got_object_id *id2,
-    const char *label1, const char *label2, FILE *outfile)
+    const char *label1, const char *label2, struct got_repository *repo,
+    got_diff_blob_cb cb, void *cb_arg)
 {
 	/* XXX TODO */
 	return NULL;
@@ -436,18 +443,18 @@ diff_kind_mismatch(struct got_object_id *id1, struct got_object_id *id2,
 static const struct got_error *
 diff_entry_old_new(const struct got_tree_entry *te1,
     const struct got_tree_entry *te2, const char *label1, const char *label2,
-    int diff_context, struct got_repository *repo, FILE *outfile)
+    struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg)
 {
 	const struct got_error *err = NULL;
 	int id_match;
 
 	if (te2 == NULL) {
 		if (S_ISDIR(te1->mode))
-			err = diff_deleted_tree(te1->id, label1, diff_context,
-			    repo, outfile);
+			err = diff_deleted_tree(te1->id, label1, repo,
+			    cb, cb_arg);
 		else
-			err = diff_deleted_blob(te1->id, label1, diff_context,
-			    repo, outfile);
+			err = diff_deleted_blob(te1->id, label1, repo,
+			    cb, cb_arg);
 		return err;
 	}
 
@@ -455,38 +462,38 @@ diff_entry_old_new(const struct got_tree_entry *te1,
 	if (S_ISDIR(te1->mode) && S_ISDIR(te2->mode)) {
 		if (!id_match)
 			return diff_modified_tree(te1->id, te2->id,
-			    label1, label2, diff_context, repo, outfile);
+			    label1, label2, repo, cb, cb_arg);
 	} else if (S_ISREG(te1->mode) && S_ISREG(te2->mode)) {
 		if (!id_match)
 			return diff_modified_blob(te1->id, te2->id,
-			    label1, label2, diff_context, repo, outfile);
+			    label1, label2, repo, cb, cb_arg);
 	}
 
 	if (id_match)
 		return NULL;
 
-	return diff_kind_mismatch(te1->id, te2->id, label1, label2, outfile);
+	return diff_kind_mismatch(te1->id, te2->id, label1, label2, repo,
+	    cb, cb_arg);
 }
 
 static const struct got_error *
 diff_entry_new_old(const struct got_tree_entry *te2,
-    const struct got_tree_entry *te1, const char *label2, int diff_context,
-    struct got_repository *repo, FILE *outfile)
+    const struct got_tree_entry *te1, const char *label2,
+    struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg)
 {
 	if (te1 != NULL) /* handled by diff_entry_old_new() */
 		return NULL;
 
 	if (S_ISDIR(te2->mode))
-		return diff_added_tree(te2->id, label2, diff_context, repo,
-		    outfile);
+		return diff_added_tree(te2->id, label2, repo, cb, cb_arg);
 
-	return diff_added_blob(te2->id, label2, diff_context, repo, outfile);
+	return diff_added_blob(te2->id, label2, repo, cb, cb_arg);
 }
 
 const struct got_error *
 got_diff_tree(struct got_tree_object *tree1, struct got_tree_object *tree2,
-    const char *label1, const char *label2, int diff_context,
-    struct got_repository *repo, FILE *outfile)
+    const char *label1, const char *label2, struct got_repository *repo,
+    got_diff_blob_cb cb, void *cb_arg)
 {
 	const struct got_error *err = NULL;
 	struct got_tree_entry *te1 = NULL;
@@ -524,8 +531,8 @@ got_diff_tree(struct got_tree_object *tree1, struct got_tree_object *tree2,
 					return
 					    got_error_from_errno("asprintf");
 			}
-			err = diff_entry_old_new(te1, te, l1, l2, diff_context,
-			    repo, outfile);
+			err = diff_entry_old_new(te1, te, l1, l2, repo, cb,
+			    cb_arg);
 			if (err)
 				break;
 		}
@@ -547,8 +554,7 @@ got_diff_tree(struct got_tree_object *tree1, struct got_tree_object *tree2,
 					return
 					    got_error_from_errno("asprintf");
 			}
-			err = diff_entry_new_old(te2, te, l2, diff_context,
-			    repo, outfile);
+			err = diff_entry_new_old(te2, te, l2, repo, cb, cb_arg);
 			if (err)
 				break;
 		}
@@ -614,6 +620,7 @@ got_diff_objects_as_trees(struct got_object_id *id1, struct got_object_id *id2,
 {
 	const struct got_error *err;
 	struct got_tree_object *tree1 = NULL, *tree2 = NULL;
+	struct got_diff_blob_output_unidiff_arg arg;
 
 	if (id1 == NULL && id2 == NULL)
 		return got_error(GOT_ERR_NO_OBJ);
@@ -628,8 +635,10 @@ got_diff_objects_as_trees(struct got_object_id *id1, struct got_object_id *id2,
 		if (err)
 			goto done;
 	}
-	err = got_diff_tree(tree1, tree2, label1, label2, diff_context,
-	   repo, outfile);
+	arg.diff_context = diff_context;
+	arg.outfile = outfile;
+	err = got_diff_tree(tree1, tree2, label1, label2, repo,
+	    got_diff_blob_output_unidiff, &arg);
 done:
 	if (tree1)
 		got_object_tree_close(tree1);
diff --git a/regress/repository/repository_test.c b/regress/repository/repository_test.c
index a556201..52d956f 100644
--- a/regress/repository/repository_test.c
+++ b/regress/repository/repository_test.c
@@ -365,6 +365,7 @@ repo_diff_tree(const char *repo_path)
 	struct got_tree_object *tree1;
 	struct got_tree_object *tree2;
 	FILE *outfile;
+	struct got_diff_blob_output_unidiff_arg arg;
 
 	err = got_repo_open(&repo, repo_path);
 	if (err != NULL || repo == NULL)
@@ -392,7 +393,10 @@ repo_diff_tree(const char *repo_path)
 	} else
 		outfile = stdout;
 	test_printf("\n");
-	got_diff_tree(tree1, tree2, "", "", 3, repo, outfile);
+	arg.diff_context = 3;
+	arg.outfile = outfile;
+	got_diff_tree(tree1, tree2, "", "", repo,
+	    got_diff_blob_output_unidiff, &arg);
 	test_printf("\n");
 
 	got_object_tree_close(tree1);