Commit Diff


commit - b67f3bcb5237084aa51e21704775e3df1b159ad0
commit + 64453f7e55c4c69b6cd08229929ed8227b789ff8
blob - 2af328db5bc16371a9ab63961a5e8cdffad041e8
blob + c44524c54ba55850265ed0fc7be1f083914d1309
--- got/got.1
+++ got/got.1
@@ -785,7 +785,7 @@ This option has no effect if the specified
 .Ar commit
 is never traversed.
 .El
-.It Cm diff Oo Fl C Ar number Oc Oo Fl r Ar repository-path Oc Oo Fl s Oc Oo Fl w Oc Op Ar object1 Ar object2 | Ar path
+.It Cm diff Oo Fl a Oc Oo Fl C Ar number Oc Oo Fl r Ar repository-path Oc Oo Fl s Oc Oo Fl w Oc Op Ar object1 Ar object2 | Ar path
 When invoked within a work tree with less than two arguments, display
 local changes in the work tree.
 If a
@@ -803,6 +803,8 @@ The options for
 .Cm got diff
 are as follows:
 .Bl -tag -width Ds
+.It Fl a
+Treat file contents as ASCII text even if binary data is detected.
 .It Fl C Ar number
 Set the number of context lines shown in the diff.
 By default, 3 lines of context are shown.
blob - d471800beafe3df5995d9268f91ecb6e56f38953
blob + 3b8ba655c02b9df7b4088a73f765a46b9f598e97
--- got/got.c
+++ got/got.c
@@ -3124,7 +3124,7 @@ done:
 static const struct got_error *
 diff_blobs(struct got_object_id *blob_id1, struct got_object_id *blob_id2,
     const char *path, int diff_context, int ignore_whitespace,
-    struct got_repository *repo)
+    int force_text_diff, struct got_repository *repo)
 {
 	const struct got_error *err = NULL;
 	struct got_blob_object *blob1 = NULL, *blob2 = NULL;
@@ -3142,7 +3142,7 @@ diff_blobs(struct got_object_id *blob_id1, struct got_
 	while (path[0] == '/')
 		path++;
 	err = got_diff_blob(NULL, NULL, blob1, blob2, path, path,
-	    diff_context, ignore_whitespace, stdout);
+	    diff_context, ignore_whitespace, force_text_diff, stdout);
 done:
 	if (blob1)
 		got_object_blob_close(blob1);
@@ -3153,7 +3153,7 @@ done:
 static const struct got_error *
 diff_trees(struct got_object_id *tree_id1, struct got_object_id *tree_id2,
     const char *path, int diff_context, int ignore_whitespace,
-    struct got_repository *repo)
+    int force_text_diff, struct got_repository *repo)
 {
 	const struct got_error *err = NULL;
 	struct got_tree_object *tree1 = NULL, *tree2 = NULL;
@@ -3171,6 +3171,7 @@ diff_trees(struct got_object_id *tree_id1, struct got_
 
 	arg.diff_context = diff_context;
 	arg.ignore_whitespace = ignore_whitespace;
+	arg.force_text_diff = force_text_diff;
 	arg.outfile = stdout;
 	arg.line_offsets = NULL;
 	arg.nlines = 0;
@@ -3282,11 +3283,11 @@ print_patch(struct got_commit_object *commit, struct g
 		switch (obj_type) {
 		case GOT_OBJ_TYPE_BLOB:
 			err = diff_blobs(obj_id1, obj_id2, path, diff_context,
-			    0, repo);
+			    0, 0, repo);
 			break;
 		case GOT_OBJ_TYPE_TREE:
 			err = diff_trees(obj_id1, obj_id2, path, diff_context,
-			    0, repo);
+			    0, 0, repo);
 			break;
 		default:
 			err = got_error(GOT_ERR_OBJ_TYPE);
@@ -3305,8 +3306,10 @@ print_patch(struct got_commit_object *commit, struct g
 			if (err)
 				goto done;
 		}
-		printf("diff %s %s\n", id_str1 ? id_str1 : "/dev/null", id_str2);
-		err = diff_trees(obj_id1, obj_id2, "", diff_context, 0, repo);
+		printf("diff %s %s\n", id_str1 ? id_str1 : "/dev/null",
+		    id_str2);
+		err = diff_trees(obj_id1, obj_id2, "", diff_context, 0, 0,
+		    repo);
 	}
 done:
 	free(id_str1);
@@ -3887,8 +3890,8 @@ done:
 __dead static void
 usage_diff(void)
 {
-	fprintf(stderr, "usage: %s diff [-C number] [-r repository-path] [-s] "
-	    "[-w] [object1 object2 | path]\n", getprogname());
+	fprintf(stderr, "usage: %s diff [-a] [-C number] [-r repository-path] "
+	    "[-s] [-w] [object1 object2 | path]\n", getprogname());
 	exit(1);
 }
 
@@ -3900,6 +3903,7 @@ struct print_diff_arg {
 	int header_shown;
 	int diff_staged;
 	int ignore_whitespace;
+	int force_text_diff;
 };
 
 /*
@@ -4004,7 +4008,7 @@ print_diff(void *arg, unsigned char status, unsigned c
 		}
 		return got_diff_objects_as_blobs(NULL, NULL, blob_id,
 		    staged_blob_id, label1, label2, a->diff_context,
-		    a->ignore_whitespace, a->repo, stdout);
+		    a->ignore_whitespace, a->force_text_diff, a->repo, stdout);
 	}
 
 	if (staged_status == GOT_STATUS_ADD ||
@@ -4077,7 +4081,7 @@ print_diff(void *arg, unsigned char status, unsigned c
 		sb.st_size = 0;
 
 	err = got_diff_blob_file(blob1, label1, f2, sb.st_size, path,
-	    a->diff_context, a->ignore_whitespace, stdout);
+	    a->diff_context, a->ignore_whitespace, a->force_text_diff, stdout);
 done:
 	if (blob1)
 		got_object_blob_close(blob1);
@@ -4101,6 +4105,7 @@ cmd_diff(int argc, char *argv[])
 	char *label1 = NULL, *label2 = NULL;
 	int type1, type2;
 	int diff_context = 3, diff_staged = 0, ignore_whitespace = 0, ch;
+	int force_text_diff = 0;
 	const char *errstr;
 	char *path = NULL;
 
@@ -4110,8 +4115,11 @@ cmd_diff(int argc, char *argv[])
 		err(1, "pledge");
 #endif
 
-	while ((ch = getopt(argc, argv, "C:r:sw")) != -1) {
+	while ((ch = getopt(argc, argv, "aC:r:sw")) != -1) {
 		switch (ch) {
+		case 'a':
+			force_text_diff = 1;
+			break;
 		case 'C':
 			diff_context = strtonum(optarg, 0, GOT_DIFF_MAX_CONTEXT,
 			    &errstr);
@@ -4229,6 +4237,7 @@ cmd_diff(int argc, char *argv[])
 		arg.header_shown = 0;
 		arg.diff_staged = diff_staged;
 		arg.ignore_whitespace = ignore_whitespace;
+		arg.force_text_diff = force_text_diff;
 
 		error = got_pathlist_append(&paths, path, NULL);
 		if (error)
@@ -4267,17 +4276,19 @@ cmd_diff(int argc, char *argv[])
 	switch (type1) {
 	case GOT_OBJ_TYPE_BLOB:
 		error = got_diff_objects_as_blobs(NULL, NULL, id1, id2,
-		    NULL, NULL, diff_context, ignore_whitespace, repo,
-		    stdout);
+		    NULL, NULL, diff_context, ignore_whitespace,
+		    force_text_diff, repo, stdout);
 		break;
 	case GOT_OBJ_TYPE_TREE:
 		error = got_diff_objects_as_trees(NULL, NULL, id1, id2,
-		    "", "", diff_context, ignore_whitespace, repo, stdout);
+		    "", "", diff_context, ignore_whitespace, force_text_diff,
+		    repo, stdout);
 		break;
 	case GOT_OBJ_TYPE_COMMIT:
 		printf("diff %s %s\n", label1, label2);
 		error = got_diff_objects_as_commits(NULL, NULL, id1, id2,
-		    diff_context, ignore_whitespace, repo, stdout);
+		    diff_context, ignore_whitespace, force_text_diff, repo,
+		    stdout);
 		break;
 	default:
 		error = got_error(GOT_ERR_OBJ_TYPE);
blob - 6e5a0c492ea1eaa3cc496e6131fd71978725ad78
blob + 2e56321573d28e2eeb0cb21dadfcb6110292501c
--- include/got_diff.h
+++ include/got_diff.h
@@ -26,7 +26,7 @@
  */
 const struct got_error *got_diff_blob(off_t **, size_t *,
     struct got_blob_object *, struct got_blob_object *,
-    const char *, const char *, int, int, FILE *);
+    const char *, const char *, int, int, int, FILE *);
 
 /*
  * Compute the differences between a blob and a file and write unified diff
@@ -37,7 +37,7 @@ const struct got_error *got_diff_blob(off_t **, size_t
  * Whitespace differences may optionally be ignored.
  */
 const struct got_error *got_diff_blob_file(struct got_blob_object *,
-    const char *, FILE *, size_t, const char *, int, int, FILE *);
+    const char *, FILE *, size_t, const char *, int, int, int, FILE *);
 
 /*
  * A callback function invoked to handle the differences between two blobs
@@ -64,6 +64,7 @@ struct got_diff_blob_output_unidiff_arg {
 	FILE *outfile;		/* Unidiff text will be written here. */
 	int diff_context;	/* Sets the number of context lines. */
 	int ignore_whitespace;	/* Ignore whitespace differences. */
+	int force_text_diff;	/* Assume text even if binary data detected. */
 
 	/*
 	 * The number of lines contained in produced unidiff text output,
@@ -128,7 +129,7 @@ const struct got_error *got_diff_tree_collect_changed_
  */
 const struct got_error *got_diff_objects_as_blobs(off_t **, size_t *,
     struct got_object_id *, struct got_object_id *,
-    const char *, const char *, int, int,
+    const char *, const char *, int, int, int,
     struct got_repository *, FILE *);
 
 /*
@@ -142,7 +143,7 @@ const struct got_error *got_diff_objects_as_blobs(off_
  */
 const struct got_error *got_diff_objects_as_trees(off_t **, size_t *,
     struct got_object_id *, struct got_object_id *, char *, char *,
-    int, int, struct got_repository *, FILE *);
+    int, int, int, struct got_repository *, FILE *);
 
 /*
  * Diff two objects, assuming both objects are commits.
@@ -152,7 +153,7 @@ const struct got_error *got_diff_objects_as_trees(off_
  * array of line offsets for, and the number of lines in, the unidiff text.
  */
 const struct got_error *got_diff_objects_as_commits(off_t **, size_t *,
-    struct got_object_id *, struct got_object_id *, int, int,
+    struct got_object_id *, struct got_object_id *, int, int, int,
     struct got_repository *, FILE *);
 
 #define GOT_DIFF_MAX_CONTEXT	64
blob - 1b319f38e537a78f703878f6a497ceef443622fd
blob + f48f80f88a4377e0ddd59fea3e98da4649a1d494
--- lib/diff.c
+++ lib/diff.c
@@ -57,7 +57,7 @@ diff_blobs(off_t **line_offsets, size_t *nlines,
     struct got_diffreg_result **resultp, struct got_blob_object *blob1,
     struct got_blob_object *blob2,
     const char *label1, const char *label2, mode_t mode1, mode_t mode2,
-    int diff_context, int ignore_whitespace, FILE *outfile)
+    int diff_context, int ignore_whitespace, int force_text_diff, FILE *outfile)
 {
 	const struct got_error *err = NULL, *free_err;
 	FILE *f1 = NULL, *f2 = NULL;
@@ -161,7 +161,7 @@ diff_blobs(off_t **line_offsets, size_t *nlines,
 		free(modestr2);
 	}
 	err = got_diffreg(&result, f1, f2, GOT_DIFF_ALGORITHM_PATIENCE,
-	    ignore_whitespace);
+	    ignore_whitespace, force_text_diff);
 	if (err)
 		goto done;
 
@@ -199,23 +199,25 @@ got_diff_blob_output_unidiff(void *arg, struct got_blo
 
 	return diff_blobs(&a->line_offsets, &a->nlines, NULL,
 	    blob1, blob2, label1, label2, mode1, mode2, a->diff_context,
-	    a->ignore_whitespace, a->outfile);
+	    a->ignore_whitespace, a->force_text_diff, a->outfile);
 }
 
 const struct got_error *
 got_diff_blob(off_t **line_offsets, size_t *nlines,
     struct got_blob_object *blob1, struct got_blob_object *blob2,
     const char *label1, const char *label2, int diff_context,
-    int ignore_whitespace, FILE *outfile)
+    int ignore_whitespace, int force_text_diff, FILE *outfile)
 {
 	return diff_blobs(line_offsets, nlines, NULL, blob1, blob2,
-	    label1, label2, 0, 0, diff_context, ignore_whitespace, outfile);
+	    label1, label2, 0, 0, diff_context, ignore_whitespace,
+	    force_text_diff, outfile);
 }
 
 static const struct got_error *
 diff_blob_file(struct got_diffreg_result **resultp,
     struct got_blob_object *blob1, const char *label1, FILE *f2, size_t size2,
-    const char *label2, int diff_context, int ignore_whitespace, FILE *outfile)
+    const char *label2, int diff_context, int ignore_whitespace,
+    int force_text_diff, FILE *outfile)
 {
 	const struct got_error *err = NULL, *free_err;
 	FILE *f1 = NULL;
@@ -248,7 +250,7 @@ diff_blob_file(struct got_diffreg_result **resultp,
 	}
 
 	err = got_diffreg(&result, f1, f2, GOT_DIFF_ALGORITHM_PATIENCE, 
-	    ignore_whitespace);
+	    ignore_whitespace, force_text_diff);
 	if (err)
 		goto done;
 
@@ -276,10 +278,10 @@ done:
 const struct got_error *
 got_diff_blob_file(struct got_blob_object *blob1, const char *label1,
     FILE *f2, size_t size2, const char *label2, int diff_context,
-    int ignore_whitespace, FILE *outfile)
+    int ignore_whitespace, int force_text_diff, FILE *outfile)
 {
 	return diff_blob_file(NULL, blob1, label1, f2, size2, label2,
-	    diff_context, ignore_whitespace, outfile);
+	    diff_context, ignore_whitespace, force_text_diff, outfile);
 }
 
 static const struct got_error *
@@ -720,7 +722,8 @@ const struct got_error *
 got_diff_objects_as_blobs(off_t **line_offsets, size_t *nlines,
     struct got_object_id *id1, struct got_object_id *id2,
     const char *label1, const char *label2, int diff_context,
-    int ignore_whitespace, struct got_repository *repo, FILE *outfile)
+    int ignore_whitespace, int force_text_diff,
+    struct got_repository *repo, FILE *outfile)
 {
 	const struct got_error *err;
 	struct got_blob_object *blob1 = NULL, *blob2 = NULL;
@@ -739,7 +742,8 @@ got_diff_objects_as_blobs(off_t **line_offsets, size_t
 			goto done;
 	}
 	err = got_diff_blob(line_offsets, nlines, blob1, blob2,
-	    label1, label2, diff_context, ignore_whitespace, outfile);
+	    label1, label2, diff_context, ignore_whitespace, force_text_diff,
+	    outfile);
 done:
 	if (blob1)
 		got_object_blob_close(blob1);
@@ -752,7 +756,7 @@ const struct got_error *
 got_diff_objects_as_trees(off_t **line_offsets, size_t *nlines,
     struct got_object_id *id1, struct got_object_id *id2,
     char *label1, char *label2, int diff_context, int ignore_whitespace,
-    struct got_repository *repo, FILE *outfile)
+    int force_text_diff, struct got_repository *repo, FILE *outfile)
 {
 	const struct got_error *err;
 	struct got_tree_object *tree1 = NULL, *tree2 = NULL;
@@ -774,6 +778,7 @@ got_diff_objects_as_trees(off_t **line_offsets, size_t
 	}
 	arg.diff_context = diff_context;
 	arg.ignore_whitespace = ignore_whitespace;
+	arg.force_text_diff = force_text_diff;
 	arg.outfile = outfile;
 	if (want_lineoffsets) {
 		arg.line_offsets = *line_offsets;
@@ -800,7 +805,7 @@ done:
 const struct got_error *
 got_diff_objects_as_commits(off_t **line_offsets, size_t *nlines,
     struct got_object_id *id1, struct got_object_id *id2,
-    int diff_context, int ignore_whitespace,
+    int diff_context, int ignore_whitespace, int force_text_diff,
     struct got_repository *repo, FILE *outfile)
 {
 	const struct got_error *err;
@@ -822,7 +827,7 @@ got_diff_objects_as_commits(off_t **line_offsets, size
 	err = got_diff_objects_as_trees(line_offsets, nlines,
 	    commit1 ? got_object_commit_get_tree_id(commit1) : NULL,
 	    got_object_commit_get_tree_id(commit2), "", "", diff_context,
-	    ignore_whitespace, repo, outfile);
+	    ignore_whitespace, force_text_diff, repo, outfile);
 done:
 	if (commit1)
 		got_object_commit_close(commit1);
@@ -834,7 +839,8 @@ done:
 const struct got_error *
 got_diff_files(struct got_diffreg_result **resultp,
     FILE *f1, const char *label1, FILE *f2, const char *label2,
-    int diff_context, int ignore_whitespace, FILE *outfile)
+    int diff_context, int ignore_whitespace, int force_text_diff,
+    FILE *outfile)
 {
 	const struct got_error *err = NULL;
 	struct got_diffreg_result *diffreg_result = NULL;
@@ -850,7 +856,7 @@ got_diff_files(struct got_diffreg_result **resultp,
 	}
 
 	err = got_diffreg(&diffreg_result, f1, f2, GOT_DIFF_ALGORITHM_PATIENCE,
-	    ignore_whitespace);
+	    ignore_whitespace, force_text_diff);
 	if (err)
 		goto done;
 
blob - d685f9e10bd4f51dc68c4cb81c4adbec7998b4fa
blob + 7b8449a42173543736e1f5d0836bb64451ab832f
--- lib/diff3.c
+++ lib/diff3.c
@@ -223,7 +223,7 @@ diffreg(BUF **d, const char *path1, const char *path2)
 		goto done;
 
 	err = got_diffreg(&diffreg_result, f1, f2,
-	    GOT_DIFF_ALGORITHM_PATIENCE, 0);
+	    GOT_DIFF_ALGORITHM_PATIENCE, 0, 1);
 	if (err)
 		goto done;
 
blob - e22e81b5097ae6ae7772a348e7f8f31f7b2c6421
blob + 8683b991a4378f8e61d26b25a83570f7cf3a3351
--- lib/diffreg.c
+++ lib/diffreg.c
@@ -139,7 +139,7 @@ got_diff_get_config(struct diff_config **cfg,
 const struct got_error *
 got_diff_prepare_file(FILE *f, char **p, size_t *size,
     struct diff_data *diff_data, const struct diff_config *cfg,
-    int ignore_whitespace)
+    int ignore_whitespace, int force_text_diff)
 {
 	const struct got_error *err = NULL;
 	struct stat st;
@@ -150,6 +150,8 @@ got_diff_prepare_file(FILE *f, char **p, size_t *size,
 	diff_flags |= DIFF_FLAG_SHOW_PROTOTYPES;
 	if (ignore_whitespace)
 		diff_flags |= DIFF_FLAG_IGNORE_WHITESPACE;
+	if (force_text_diff)
+		diff_flags |= DIFF_FLAG_FORCE_TEXT_DATA;
 
 	if (fstat(fileno(f), &st) == -1) {
 		err = got_error_from_errno("fstat");
@@ -177,7 +179,8 @@ done:
 
 const struct got_error *
 got_diffreg(struct got_diffreg_result **diffreg_result, FILE *f1, FILE *f2,
-    enum got_diff_algorithm algorithm, int ignore_whitespace)
+    enum got_diff_algorithm algorithm, int ignore_whitespace,
+    int force_text_diff)
 {
 	const struct got_error *err = NULL;
 	struct diff_config *cfg = NULL;
@@ -223,12 +226,12 @@ got_diffreg(struct got_diffreg_result **diffreg_result
 	}
 
 	err = got_diff_prepare_file(f1, &p1, &size1, left, cfg,
-	    ignore_whitespace);
+	    ignore_whitespace, force_text_diff);
 	if (err)
 		goto done;
 
 	err = got_diff_prepare_file(f2, &p2, &size2, right, cfg,
-	    ignore_whitespace);
+	    ignore_whitespace, force_text_diff);
 	if (err)
 		goto done;
 
blob - 3a995c48e676b6b3ef159bad68c8c69a070427d5
blob + f3e58cc505cffa4e06ca6a0ec16396a3e5307747
--- lib/got_lib_diff.h
+++ lib/got_lib_diff.h
@@ -48,9 +48,9 @@ struct got_diffreg_result {
 const struct got_error *got_diff_get_config(struct diff_config **,
     enum got_diff_algorithm, diff_atomize_func_t, void *);
 const struct got_error *got_diff_prepare_file(FILE *, char **, size_t *,
-    struct diff_data *, const struct diff_config *, int); 
-const struct got_error *got_diffreg(struct got_diffreg_result **, FILE *, FILE *,
-    enum got_diff_algorithm, int);
+    struct diff_data *, const struct diff_config *, int, int); 
+const struct got_error *got_diffreg(struct got_diffreg_result **, FILE *,
+    FILE *, enum got_diff_algorithm, int, int);
 const struct got_error *got_diffreg_output(off_t **, size_t *,
     struct got_diffreg_result *, FILE *, FILE *, const char *, const char *,
     enum got_diff_output_format, int, FILE *);
@@ -66,4 +66,4 @@ const struct got_error *got_merge_diff3(int *, int, co
     const char *, const char *, const char *, const char *);
 
 const struct got_error *got_diff_files(struct got_diffreg_result **, FILE *,
-    const char *, FILE *, const char *, int, int, FILE *);
+    const char *, FILE *, const char *, int, int, int, FILE *);
blob - b012446a6b55a2c0ce73c346d370b6a2b7d964b7
blob + 971955fbafa7d712d0e5a61dfe60def65321b0f0
--- lib/worktree.c
+++ lib/worktree.c
@@ -4324,7 +4324,7 @@ create_patched_content(char **path_outfile, int revers
 	if (err)
 		goto done;
 
-	err = got_diff_files(&diffreg_result, f1, id_str, f2, path2, 3, 0,
+	err = got_diff_files(&diffreg_result, f1, id_str, f2, path2, 3, 0, 1,
 	    NULL);
 	if (err)
 		goto done;
@@ -7450,7 +7450,7 @@ create_unstaged_content(char **path_unstaged_content,
 		goto done;
 
 	err = got_diff_files(&diffreg_result, f1, label1, f2,
-	    path2, 3, 0, NULL);
+	    path2, 3, 0, 1, NULL);
 	if (err)
 		goto done;
 
blob - 40f48b0786ea4b80be8d254a65d81296572408a1
blob + 92cd8f39cbdc7a59932a05599b57b808a8814472
--- regress/cmdline/diff.sh
+++ regress/cmdline/diff.sh
@@ -596,6 +596,20 @@ test_diff_binary_files() {
 
 	printf '\377\377\0\0\377\377\0\0' > $testroot/wt/foo
 	(cd $testroot/wt && got add foo >/dev/null)
+
+	echo "diff $head_rev $testroot/wt" > $testroot/stdout.expected
+	echo 'blob - /dev/null' >> $testroot/stdout.expected
+	echo 'file + foo' >> $testroot/stdout.expected
+	echo "Binary files foo and foo differ" >> $testroot/stdout.expected
+
+	(cd $testroot/wt && got diff > $testroot/stdout)
+	cmp -s $testroot/stdout.expected $testroot/stdout
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		diff -a -u $testroot/stdout.expected $testroot/stdout
+		test_done "$testroot" "$ret"
+		return 1
+	fi
 
 	echo "diff $head_rev $testroot/wt" > $testroot/stdout.expected
 	echo 'blob - /dev/null' >> $testroot/stdout.expected
@@ -606,7 +620,7 @@ test_diff_binary_files() {
 	printf '+\377\377\0\0\377\377\0\0\n' >> $testroot/stdout.expected
 	echo '\\ No newline at end of file' >> $testroot/stdout.expected
 
-	(cd $testroot/wt && got diff > $testroot/stdout)
+	(cd $testroot/wt && got diff -a > $testroot/stdout)
 	cmp -s $testroot/stdout.expected $testroot/stdout
 	ret="$?"
 	if [ "$ret" != "0" ]; then
@@ -633,7 +647,7 @@ test_diff_binary_files() {
 	printf '+\377\200\0\0\377\200\0\0\n' >> $testroot/stdout.expected
 	echo '\\ No newline at end of file' >> $testroot/stdout.expected
 
-	(cd $testroot/wt && got diff > $testroot/stdout)
+	(cd $testroot/wt && got diff -a > $testroot/stdout)
 	cmp -s $testroot/stdout.expected $testroot/stdout
 	ret="$?"
 	if [ "$ret" != "0" ]; then
blob - c6cc6c9fe1d1bc5c155e3f4b780dabe521ee8157
blob + 2998a0230cfb9957985eaba7c922178ec4868278
--- tog/tog.1
+++ tog/tog.1
@@ -175,7 +175,7 @@ If this directory is a
 .Xr got 1
 work tree, use the repository path associated with this work tree.
 .El
-.It Cm diff Oo Fl r Ar repository-path Oc Ar object1 object2
+.It Cm diff Oo Fl a Oc Oo Fl r Ar repository-path Oc Ar object1 object2
 Display the differences between two objects in the repository.
 Each
 .Ar object
@@ -188,6 +188,9 @@ The key bindings for
 .Cm tog diff
 are as follows:
 .Bl -tag -width Ds
+.It Cm a
+Toggle treatment of file contents as ASCII text even if binary data was
+detected.
 .It Cm Down-arrow, j
 Scroll down.
 .It Cm Up-arrow, k
@@ -220,6 +223,8 @@ The options for
 .Cm tog diff
 are as follows:
 .Bl -tag -width Ds
+.It Fl a
+Treat file contents as ASCII text even if binary data is detected.
 .It Fl r Ar repository-path
 Use the repository at the specified path.
 If not specified, assume the repository is located at or above the current
blob - 54c37e9784d2d00193462315edb6f7cfc5bd6479
blob + e4b6922ea70550bd65bf25da483b6f095898670d
--- tog/tog.c
+++ tog/tog.c
@@ -249,6 +249,7 @@ struct tog_diff_view_state {
 	int last_displayed_line;
 	int eof;
 	int diff_context;
+	int force_text_diff;
 	struct got_repository *repo;
 	struct got_reflist_head *refs;
 	struct tog_colors colors;
@@ -445,7 +446,7 @@ struct tog_view {
 };
 
 static const struct got_error *open_diff_view(struct tog_view *,
-    struct got_object_id *, struct got_object_id *, struct tog_view *,
+    struct got_object_id *, struct got_object_id *, int, struct tog_view *,
     struct got_reflist_head *, struct got_repository *);
 static const struct got_error *show_diff_view(struct tog_view *);
 static const struct got_error *input_diff_view(struct tog_view **,
@@ -1768,7 +1769,7 @@ open_diff_view_for_commit(struct tog_view **new_view, 
 
 	parent_id = SIMPLEQ_FIRST(got_object_commit_get_parent_ids(commit));
 	err = open_diff_view(diff_view, parent_id ? parent_id->id : NULL,
-	    commit_id, log_view, refs, repo);
+	    commit_id, 0, log_view, refs, repo);
 	if (err == NULL)
 		*new_view = diff_view;
 	return err;
@@ -2757,8 +2758,8 @@ __dead static void
 usage_diff(void)
 {
 	endwin();
-	fprintf(stderr, "usage: %s diff [-r repository-path] object1 object2\n",
-	    getprogname());
+	fprintf(stderr, "usage: %s diff [-a] [-r repository-path] "
+	    "object1 object2\n", getprogname());
 	exit(1);
 }
 
@@ -3217,11 +3218,12 @@ create_diff(struct tog_diff_view_state *s)
 	case GOT_OBJ_TYPE_BLOB:
 		err = got_diff_objects_as_blobs(&s->line_offsets, &s->nlines,
 		    s->id1, s->id2, NULL, NULL, s->diff_context, 0,
-		    s->repo, s->f);
+		    s->force_text_diff, s->repo, s->f);
 		break;
 	case GOT_OBJ_TYPE_TREE:
 		err = got_diff_objects_as_trees(&s->line_offsets, &s->nlines,
-		    s->id1, s->id2, "", "", s->diff_context, 0, s->repo, s->f);
+		    s->id1, s->id2, "", "", s->diff_context, 0,
+		    s->force_text_diff, s->repo, s->f);
 		break;
 	case GOT_OBJ_TYPE_COMMIT: {
 		const struct got_object_id_queue *parent_ids;
@@ -3253,7 +3255,8 @@ create_diff(struct tog_diff_view_state *s)
 		got_object_commit_close(commit2);
 
 		err = got_diff_objects_as_commits(&s->line_offsets, &s->nlines,
-		    s->id1, s->id2, s->diff_context, 0, s->repo, s->f);
+		    s->id1, s->id2, s->diff_context, 0, s->force_text_diff,
+		    s->repo, s->f);
 		break;
 	}
 	default:
@@ -3357,7 +3360,7 @@ search_next_diff_view(struct tog_view *view)
 
 static const struct got_error *
 open_diff_view(struct tog_view *view, struct got_object_id *id1,
-    struct got_object_id *id2, struct tog_view *log_view,
+    struct got_object_id *id2, int force_text_diff, struct tog_view *log_view,
     struct got_reflist_head *refs, struct got_repository *repo)
 {
 	const struct got_error *err;
@@ -3400,6 +3403,7 @@ open_diff_view(struct tog_view *view, struct got_objec
 	s->first_displayed_line = 1;
 	s->last_displayed_line = view->nlines;
 	s->diff_context = 3;
+	s->force_text_diff = force_text_diff;
 	s->log_view = log_view;
 	s->repo = repo;
 	s->refs = refs;
@@ -3563,6 +3567,14 @@ input_diff_view(struct tog_view **new_view, struct tog
 	int i;
 
 	switch (ch) {
+	case 'a':
+		s->force_text_diff = !s->force_text_diff;
+		wclear(view->window);
+		s->first_displayed_line = 1;
+		s->last_displayed_line = view->nlines;
+		diff_view_indicate_progress(view);
+		err = create_diff(s);
+		break;
 	case 'k':
 	case KEY_UP:
 		if (s->first_displayed_line > 1)
@@ -3687,7 +3699,7 @@ cmd_diff(int argc, char *argv[])
 	struct got_object_id *id1 = NULL, *id2 = NULL;
 	char *repo_path = NULL, *cwd = NULL;
 	char *id_str1 = NULL, *id_str2 = NULL;
-	int ch;
+	int ch, force_text_diff = 0;
 	struct tog_view *view;
 
 	SIMPLEQ_INIT(&refs);
@@ -3698,8 +3710,11 @@ cmd_diff(int argc, char *argv[])
 		err(1, "pledge");
 #endif
 
-	while ((ch = getopt(argc, argv, "r:")) != -1) {
+	while ((ch = getopt(argc, argv, "ar:")) != -1) {
 		switch (ch) {
+		case 'a':
+			force_text_diff = 1;
+			break;
 		case 'r':
 			repo_path = realpath(optarg, NULL);
 			if (repo_path == NULL)
@@ -3772,7 +3787,8 @@ cmd_diff(int argc, char *argv[])
 		error = got_error_from_errno("view_open");
 		goto done;
 	}
-	error = open_diff_view(view, id1, id2, NULL, &refs, repo);
+	error = open_diff_view(view, id1, id2, force_text_diff, NULL, &refs,
+	    repo);
 	if (error)
 		goto done;
 	error = view_loop(view);
@@ -4533,7 +4549,7 @@ input_blame_view(struct tog_view **new_view, struct to
 			break;
 		}
 		err = open_diff_view(diff_view, pid ? pid->id : NULL,
-		    id, NULL, s->refs, s->repo);
+		    id, 0, NULL, s->refs, s->repo);
 		got_object_commit_close(commit);
 		if (err) {
 			view_close(diff_view);