Commit 46f68b205b4ab514846c5352c6d952720b9da365

Stefan Sperling 2019-10-19T11:06:56

show file mode differences when diffing trees

diff --git a/got/got.c b/got/got.c
index 5423c33..1500258 100644
--- a/got/got.c
+++ b/got/got.c
@@ -5024,7 +5024,7 @@ static const struct got_error *
 check_path_prefix_in_diff(void *arg, struct got_blob_object *blob1,
     struct got_blob_object *blob2, struct got_object_id *id1,
     struct got_object_id *id2, const char *path1, const char *path2,
-    struct got_repository *repo)
+    mode_t mode1, mode_t mode2, struct got_repository *repo)
 {
 	struct check_path_prefix_arg *a = arg;
 
diff --git a/include/got_diff.h b/include/got_diff.h
index 2cbdb36..d7f3479 100644
--- a/include/got_diff.h
+++ b/include/got_diff.h
@@ -44,11 +44,13 @@ const struct got_error *got_diff_blob_file(struct got_blob_object *,
  * 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.
+ * File modes from relevant tree objects which contain the blobs may
+ * also be passed. These will be zero if not available.
  */
 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 *);
+    const char *, const char *, mode_t, mode_t, struct got_repository *);
 
 /*
  * A pre-defined implementation of got_diff_blob_cb() which appends unidiff
@@ -63,7 +65,7 @@ struct got_diff_blob_output_unidiff_arg {
 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 *);
+    const char *, const char *, mode_t, mode_t, struct got_repository *);
 
 /*
  * Compute the differences between two trees and invoke the provided
diff --git a/lib/diff.c b/lib/diff.c
index c100c66..c98502c 100644
--- a/lib/diff.c
+++ b/lib/diff.c
@@ -38,8 +38,9 @@
 
 static const struct got_error *
 diff_blobs(struct got_blob_object *blob1, struct got_blob_object *blob2,
-    const char *label1, const char *label2, int diff_context,
-    int ignore_whitespace, FILE *outfile, struct got_diff_changes *changes)
+    const char *label1, const char *label2, mode_t mode1, mode_t mode2,
+    int diff_context, int ignore_whitespace, FILE *outfile,
+    struct got_diff_changes *changes)
 {
 	struct got_diff_state ds;
 	struct got_diff_args args;
@@ -110,8 +111,27 @@ diff_blobs(struct got_blob_object *blob1, struct got_blob_object *blob2,
 		flags |= D_IGNOREBLANKS;
 
 	if (outfile) {
-		fprintf(outfile, "blob - %s\n", idstr1);
-		fprintf(outfile, "blob + %s\n", idstr2);
+		char *modestr1 = NULL, *modestr2 = NULL;
+		if (mode1 && mode1 != mode2) {
+			if (asprintf(&modestr1, " (mode %o)",
+			    mode1 & (S_IRWXU | S_IRWXG | S_IRWXO)) == -1) {
+				err = got_error_from_errno("asprintf");
+				goto done;
+			}
+		}
+		if (mode2 && mode1 != mode2) {
+			if (asprintf(&modestr2, " (mode %o)",
+			    mode2 & (S_IRWXU | S_IRWXG | S_IRWXO)) == -1) {
+				err = got_error_from_errno("asprintf");
+				goto done;
+			}
+		}
+		fprintf(outfile, "blob - %s%s\n", idstr1,
+		    modestr1 ? modestr1 : "");
+		fprintf(outfile, "blob + %s%s\n", idstr2,
+		    modestr2 ? modestr2 : "");
+		free(modestr1);
+		free(modestr2);
 	}
 	err = got_diffreg(&res, f1, f2, flags, &args, &ds, outfile, changes);
 	got_diff_state_free(&ds);
@@ -127,12 +147,12 @@ 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)
+    mode_t mode1, mode_t mode2, struct got_repository *repo)
 {
 	struct got_diff_blob_output_unidiff_arg *a = arg;
 
-	return diff_blobs(blob1, blob2, label1, label2, a->diff_context,
-	    a->ignore_whitespace, a->outfile, NULL);
+	return diff_blobs(blob1, blob2, label1, label2, mode1, mode2,
+	    a->diff_context, a->ignore_whitespace, a->outfile, NULL);
 }
 
 const struct got_error *
@@ -140,7 +160,7 @@ got_diff_blob(struct got_blob_object *blob1, struct got_blob_object *blob2,
     const char *label1, const char *label2, int diff_context,
     int ignore_whitespace, FILE *outfile)
 {
-	return diff_blobs(blob1, blob2, label1, label2, diff_context,
+	return diff_blobs(blob1, blob2, label1, label2, 0, 0, diff_context,
 	    ignore_whitespace, outfile, NULL);
 }
 
@@ -255,7 +275,7 @@ got_diff_blob_lines_changed(struct got_diff_changes **changes,
 	if (err)
 		return err;
 
-	err = diff_blobs(blob1, blob2, NULL, NULL, 3, 0, NULL, *changes);
+	err = diff_blobs(blob1, blob2, NULL, NULL, 0, 0, 3, 0, NULL, *changes);
 	if (err) {
 		got_diff_free_changes(*changes);
 		*changes = NULL;
@@ -276,7 +296,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,
+diff_added_blob(struct got_object_id *id, const char *label, mode_t mode,
     struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg)
 {
 	const struct got_error *err;
@@ -290,7 +310,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 = cb(cb_arg, NULL, blob, NULL, id, NULL, label, repo);
+	err = cb(cb_arg, NULL, blob, NULL, id, NULL, label, 0, mode, repo);
 done:
 	got_object_close(obj);
 	if (blob)
@@ -300,8 +320,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, struct got_repository *repo,
-    got_diff_blob_cb cb, void *cb_arg)
+    const char *label1, const char *label2, mode_t mode1, mode_t mode2,
+    struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg)
 {
 	const struct got_error *err;
 	struct got_object *obj1 = NULL;
@@ -333,7 +353,8 @@ diff_modified_blob(struct got_object_id *id1, struct got_object_id *id2,
 	if (err)
 		goto done;
 
-	err = cb(cb_arg, blob1, blob2, id1, id2, label1, label2, repo);
+	err = cb(cb_arg, blob1, blob2, id1, id2, label1, label2, mode1, mode2,
+	    repo);
 done:
 	if (obj1)
 		got_object_close(obj1);
@@ -347,7 +368,7 @@ done:
 }
 
 static const struct got_error *
-diff_deleted_blob(struct got_object_id *id, const char *label,
+diff_deleted_blob(struct got_object_id *id, const char *label, mode_t mode,
     struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg)
 {
 	const struct got_error *err;
@@ -361,7 +382,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 = cb(cb_arg, blob, NULL, id, NULL, label, NULL, repo);
+	err = cb(cb_arg, blob, NULL, id, NULL, label, NULL, mode, 0, repo);
 done:
 	got_object_close(obj);
 	if (blob)
@@ -512,11 +533,11 @@ diff_entry_old_new(const struct got_tree_entry *te1,
 			    cb, cb_arg, diff_content);
 		else {
 			if (diff_content)
-				err = diff_deleted_blob(te1->id, label1, repo,
-				    cb, cb_arg);
+				err = diff_deleted_blob(te1->id, label1,
+				    te1->mode, repo, cb, cb_arg);
 			else
 				err = cb(cb_arg, NULL, NULL, te1->id, NULL,
-				    label1, NULL, repo);
+				    label1, NULL, te1->mode, 0, repo);
 		}
 		return err;
 	} else if (got_object_tree_entry_is_submodule(te2))
@@ -528,13 +549,16 @@ diff_entry_old_new(const struct got_tree_entry *te1,
 			return diff_modified_tree(te1->id, te2->id,
 			    label1, label2, repo, cb, cb_arg, diff_content);
 	} else if (S_ISREG(te1->mode) && S_ISREG(te2->mode)) {
-		if (!id_match) {
+		if (!id_match ||
+		    (te1->mode & S_IXUSR) != (te2->mode & S_IXUSR)) {
 			if (diff_content)
 				return diff_modified_blob(te1->id, te2->id,
-				    label1, label2, repo, cb, cb_arg);
+				    label1, label2, te1->mode, te2->mode,
+				    repo, cb, cb_arg);
 			else
 				return cb(cb_arg, NULL, NULL, te1->id,
-				    te2->id, label1, label2, repo);
+				    te2->id, label1, label2, te1->mode,
+				    te2->mode, repo);
 		}
 	}
 
@@ -562,9 +586,11 @@ diff_entry_new_old(const struct got_tree_entry *te2,
 		    diff_content);
 
 	if (diff_content)
-		return diff_added_blob(te2->id, label2, repo, cb, cb_arg);
+		return diff_added_blob(te2->id, label2, te2->mode, repo, cb,
+		    cb_arg);
 
-	return cb(cb_arg, NULL, NULL, NULL, te2->id, NULL, label2, repo);
+	return cb(cb_arg, NULL, NULL, NULL, te2->id, NULL, label2, 0,
+	    te2->mode, repo);
 }
 
 const struct got_error *
diff --git a/lib/worktree.c b/lib/worktree.c
index edb81fa..e289542 100644
--- a/lib/worktree.c
+++ b/lib/worktree.c
@@ -1986,7 +1986,7 @@ static const struct got_error *
 merge_file_cb(void *arg, struct got_blob_object *blob1,
     struct got_blob_object *blob2, struct got_object_id *id1,
     struct got_object_id *id2, const char *path1, const char *path2,
-    struct got_repository *repo)
+    mode_t mode1, mode_t mode2, struct got_repository *repo)
 {
 	static const struct got_error *err = NULL;
 	struct merge_file_cb_arg *a = arg;
diff --git a/regress/cmdline/diff.sh b/regress/cmdline/diff.sh
old mode 100744
new mode 100755
index 0454b55..31587c1
--- a/regress/cmdline/diff.sh
+++ b/regress/cmdline/diff.sh
@@ -217,7 +217,8 @@ function test_diff_tag {
 	echo "blob - /dev/null" >> $testroot/stdout.expected
 	echo -n 'blob + ' >> $testroot/stdout.expected
 	got tree -r $testroot/repo -i -c $commit_id2 | grep 'new$' | \
-		cut -d' ' -f 1 >> $testroot/stdout.expected
+		cut -d' ' -f 1 | tr -d '\n' >> $testroot/stdout.expected
+	echo " (mode 644)" >> $testroot/stdout.expected
 	echo '--- /dev/null' >> $testroot/stdout.expected
 	echo '+++ new' >> $testroot/stdout.expected
 	echo '@@ -0,0 +1 @@' >> $testroot/stdout.expected
diff --git a/regress/cmdline/import.sh b/regress/cmdline/import.sh
index 42ff367..a7b1c1d 100755
--- a/regress/cmdline/import.sh
+++ b/regress/cmdline/import.sh
@@ -67,25 +67,25 @@ function test_import_basic {
 	echo " " >> $testroot/stdout.expected
 	echo "diff /dev/null $tree_id" >> $testroot/stdout.expected
 	echo "blob - /dev/null" >> $testroot/stdout.expected
-	echo "blob + $id_alpha" >> $testroot/stdout.expected
+	echo "blob + $id_alpha (mode 644)" >> $testroot/stdout.expected
 	echo "--- /dev/null" >> $testroot/stdout.expected
 	echo "+++ alpha" >> $testroot/stdout.expected
 	echo "@@ -0,0 +1 @@" >> $testroot/stdout.expected
 	echo "+alpha" >> $testroot/stdout.expected
 	echo "blob - /dev/null" >> $testroot/stdout.expected
-	echo "blob + $id_beta" >> $testroot/stdout.expected
+	echo "blob + $id_beta (mode 644)" >> $testroot/stdout.expected
 	echo "--- /dev/null" >> $testroot/stdout.expected
 	echo "+++ beta" >> $testroot/stdout.expected
 	echo "@@ -0,0 +1 @@" >> $testroot/stdout.expected
 	echo "+beta" >> $testroot/stdout.expected
 	echo "blob - /dev/null" >> $testroot/stdout.expected
-	echo "blob + $id_zeta" >> $testroot/stdout.expected
+	echo "blob + $id_zeta (mode 644)" >> $testroot/stdout.expected
 	echo "--- /dev/null" >> $testroot/stdout.expected
 	echo "+++ epsilon/zeta" >> $testroot/stdout.expected
 	echo "@@ -0,0 +1 @@" >> $testroot/stdout.expected
 	echo "+zeta" >> $testroot/stdout.expected
 	echo "blob - /dev/null" >> $testroot/stdout.expected
-	echo "blob + $id_delta" >> $testroot/stdout.expected
+	echo "blob + $id_delta (mode 644)" >> $testroot/stdout.expected
 	echo "--- /dev/null" >> $testroot/stdout.expected
 	echo "+++ gamma/delta" >> $testroot/stdout.expected
 	echo "@@ -0,0 +1 @@" >> $testroot/stdout.expected
diff --git a/regress/cmdline/stage.sh b/regress/cmdline/stage.sh
index c847136..ba1b5ea 100755
--- a/regress/cmdline/stage.sh
+++ b/regress/cmdline/stage.sh
@@ -1359,8 +1359,9 @@ function test_stage_commit {
 	echo '+modified file' >> $testroot/stdout.expected
 	echo -n 'blob - ' >> $testroot/stdout.expected
 	got tree -r $testroot/repo -i -c $first_commit \
-		| grep 'beta$' | cut -d' ' -f 1 \
+		| grep 'beta$' | cut -d' ' -f 1 | tr -d '\n' \
 		>> $testroot/stdout.expected
+	echo " (mode 644)" >> $testroot/stdout.expected
 	echo 'blob + /dev/null' >> $testroot/stdout.expected
 	echo '--- beta' >> $testroot/stdout.expected
 	echo '+++ /dev/null' >> $testroot/stdout.expected
@@ -1368,7 +1369,8 @@ function test_stage_commit {
 	echo '-beta' >> $testroot/stdout.expected
 	echo 'blob - /dev/null' >> $testroot/stdout.expected
 	echo -n 'blob + ' >> $testroot/stdout.expected
-	cat $testroot/blob_id_foo >> $testroot/stdout.expected
+	cat $testroot/blob_id_foo | tr -d '\n' >> $testroot/stdout.expected
+	echo " (mode 644)" >> $testroot/stdout.expected
 	echo '--- /dev/null' >> $testroot/stdout.expected
 	echo '+++ foo' >> $testroot/stdout.expected
 	echo '@@ -0,0 +1 @@' >> $testroot/stdout.expected