"GOT", but the "O" is a cute, smiling pufferfish. Index | Thread | Search

From:
Stefan Sperling <stsp@stsp.name>
Subject:
Re: move creation of tempfiles out of lib/diff.c
To:
gameoftrees@openbsd.org
Date:
Tue, 31 May 2022 21:07:41 +0200

Download raw body.

Thread
On Tue, May 31, 2022 at 05:18:37PM +0200, Stefan Sperling wrote:
> This patch moves got_opentemp() calls from lib/diff.c into callers.
> 
> ok?

Previous version of this patch broke the gotweb build. Fixed here.

diff f1cdc1932167666c113e55d67b377d9e1c920c6f 6c756902fd7a43df1c39ce796b34c48863348907
blob - cf96009500ac33bb3b660dd7e99902801b0cb975
blob + d88f23ce5b63a9d80862b5fac05c212d054a0b74
--- got/got.c
+++ got/got.c
@@ -3481,25 +3481,41 @@ diff_blobs(struct got_object_id *blob_id1, struct got_
 {
 	const struct got_error *err = NULL;
 	struct got_blob_object *blob1 = NULL, *blob2 = NULL;
+	FILE *f1 = NULL, *f2 = NULL;
 
 	if (blob_id1) {
 		err = got_object_open_as_blob(&blob1, repo, blob_id1, 8192);
 		if (err)
 			goto done;
+		f1 = got_opentemp();
+		if (f1 == NULL) {
+			err = got_error_from_errno("got_opentemp");
+			goto done;
+		}
 	}
 
 	err = got_object_open_as_blob(&blob2, repo, blob_id2, 8192);
 	if (err)
 		goto done;
 
+	f2 = got_opentemp();
+	if (f2 == NULL) {
+		err = got_error_from_errno("got_opentemp");
+		goto done;
+	}
+
 	while (path[0] == '/')
 		path++;
-	err = got_diff_blob(NULL, NULL, blob1, blob2, path, path,
+	err = got_diff_blob(NULL, NULL, blob1, blob2, f1, f2, path, path,
 	    diff_context, ignore_whitespace, force_text_diff, stdout);
 done:
 	if (blob1)
 		got_object_blob_close(blob1);
 	got_object_blob_close(blob2);
+	if (f1 && fclose(f1) == EOF && err == NULL)
+		err = got_error_from_errno("fclose");
+	if (f2 && fclose(f2) == EOF && err == NULL)
+		err = got_error_from_errno("fclose");
 	return err;
 }
 
@@ -3511,17 +3527,29 @@ diff_trees(struct got_object_id *tree_id1, struct got_
 	const struct got_error *err = NULL;
 	struct got_tree_object *tree1 = NULL, *tree2 = NULL;
 	struct got_diff_blob_output_unidiff_arg arg;
+	FILE *f1 = NULL, *f2 = NULL;
 
 	if (tree_id1) {
 		err = got_object_open_as_tree(&tree1, repo, tree_id1);
 		if (err)
 			goto done;
+		f1 = got_opentemp();
+		if (f1 == NULL) {
+			err = got_error_from_errno("got_opentemp");
+			goto done;
+		}
 	}
 
 	err = got_object_open_as_tree(&tree2, repo, tree_id2);
 	if (err)
 		goto done;
 
+	f2 = got_opentemp();
+	if (f2 == NULL) {
+		err = got_error_from_errno("got_opentemp");
+		goto done;
+	}
+
 	arg.diff_context = diff_context;
 	arg.ignore_whitespace = ignore_whitespace;
 	arg.force_text_diff = force_text_diff;
@@ -3530,13 +3558,17 @@ diff_trees(struct got_object_id *tree_id1, struct got_
 	arg.nlines = 0;
 	while (path[0] == '/')
 		path++;
-	err = got_diff_tree(tree1, tree2, path, path, repo,
+	err = got_diff_tree(tree1, tree2, f1, f2, path, path, repo,
 	    got_diff_blob_output_unidiff, &arg, 1);
 done:
 	if (tree1)
 		got_object_tree_close(tree1);
 	if (tree2)
 		got_object_tree_close(tree2);
+	if (f1 && fclose(f1) == EOF && err == NULL)
+		err = got_error_from_errno("fclose");
+	if (f2 && fclose(f2) == EOF && err == NULL)
+		err = got_error_from_errno("fclose");
 	return err;
 }
 
@@ -3578,7 +3610,7 @@ get_changed_paths(struct got_pathlist_head *paths,
 	if (err)
 		goto done;
 
-	err = got_diff_tree(tree1, tree2, "", "", repo,
+	err = got_diff_tree(tree1, tree2, NULL, NULL, "", "", repo,
 	    got_diff_tree_collect_changed_paths, paths, 0);
 done:
 	if (tree1)
@@ -4369,9 +4401,10 @@ print_diff(void *arg, unsigned char status, unsigned c
 	const struct got_error *err = NULL;
 	struct got_blob_object *blob1 = NULL;
 	int fd = -1;
-	FILE *f2 = NULL;
+	FILE *f1 = NULL, *f2 = NULL;
 	char *abspath = NULL, *label1 = NULL;
 	struct stat sb;
+	off_t size1 = 0;
 
 	if (a->diff_staged) {
 		if (staged_status != GOT_STATUS_MODIFY &&
@@ -4413,9 +4446,20 @@ print_diff(void *arg, unsigned char status, unsigned c
 		default:
 			return got_error(GOT_ERR_FILE_STATUS);
 		}
-		return got_diff_objects_as_blobs(NULL, NULL, blob_id,
-		    staged_blob_id, label1, label2, a->diff_context,
+		f1 = got_opentemp();
+		if (f1 == NULL) {
+			err = got_error_from_errno("got_opentemp");
+			goto done;
+		}
+		f2 = got_opentemp();
+		if (f2 == NULL) {
+			err = got_error_from_errno("got_opentemp");
+			goto done;
+		}
+		err = got_diff_objects_as_blobs(NULL, NULL, f1, f2,
+		    blob_id, staged_blob_id, label1, label2, a->diff_context,
 		    a->ignore_whitespace, a->force_text_diff, a->repo, stdout);
+		goto done;
 	}
 
 	if (staged_status == GOT_STATUS_ADD ||
@@ -4488,11 +4532,26 @@ print_diff(void *arg, unsigned char status, unsigned c
 	} else
 		sb.st_size = 0;
 
-	err = got_diff_blob_file(blob1, label1, f2, sb.st_size, path,
-	    a->diff_context, a->ignore_whitespace, a->force_text_diff, stdout);
+	if (blob1) {
+		f1 = got_opentemp();
+		if (f1 == NULL) {
+			err = got_error_from_errno("got_opentemp");
+			goto done;
+		}
+		err = got_object_blob_dump_to_file(&size1, NULL, NULL, f1,
+		    blob1);
+		if (err)
+			goto done;
+	}	
+
+	err = got_diff_blob_file(blob1, f1, size1, label1, f2, sb.st_size,
+	    path, a->diff_context, a->ignore_whitespace, a->force_text_diff,
+	    stdout);
 done:
 	if (blob1)
 		got_object_blob_close(blob1);
+	if (f1 && fclose(f1) == EOF && err == NULL)
+		err = got_error_from_errno("fclose");
 	if (f2 && fclose(f2) == EOF && err == NULL)
 		err = got_error_from_errno("fclose");
 	if (fd != -1 && close(fd) == -1 && err == NULL)
@@ -4519,6 +4578,7 @@ cmd_diff(int argc, char *argv[])
 	struct got_reflist_head refs;
 	struct got_pathlist_head paths;
 	struct got_pathlist_entry *pe;
+	FILE *f1 = NULL, *f2 = NULL;
 
 	TAILQ_INIT(&refs);
 	TAILQ_INIT(&paths);
@@ -4811,22 +4871,34 @@ cmd_diff(int argc, char *argv[])
 		worktree = NULL;
 	}
 
+	f1 = got_opentemp();
+	if (f1 == NULL) {
+		error = got_error_from_errno("got_opentemp");
+		goto done;
+	}
+
+	f2 = got_opentemp();
+	if (f2 == NULL) {
+		error = got_error_from_errno("got_opentemp");
+		goto done;
+	}
+
 	switch (type1 == GOT_OBJ_TYPE_ANY ? type2 : type1) {
 	case GOT_OBJ_TYPE_BLOB:
-		error = got_diff_objects_as_blobs(NULL, NULL, ids[0], ids[1],
-		    NULL, NULL, diff_context, ignore_whitespace,
-		    force_text_diff, repo, stdout);
+		error = got_diff_objects_as_blobs(NULL, NULL, f1, f2,
+		    ids[0], ids[1], 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, ids[0], ids[1],
-		    &paths, "", "", diff_context, ignore_whitespace,
-		    force_text_diff, repo, stdout);
+		error = got_diff_objects_as_trees(NULL, NULL, f1, f2,
+		    ids[0], ids[1], &paths, "", "", diff_context,
+		    ignore_whitespace, force_text_diff, repo, stdout);
 		break;
 	case GOT_OBJ_TYPE_COMMIT:
 		printf("diff %s %s\n", labels[0], labels[1]);
-		error = got_diff_objects_as_commits(NULL, NULL, ids[0], ids[1],
-		    &paths, diff_context, ignore_whitespace, force_text_diff,
-		    repo, stdout);
+		error = got_diff_objects_as_commits(NULL, NULL, f1, f2,
+		    ids[0], ids[1], &paths, diff_context, ignore_whitespace,
+		    force_text_diff, repo, stdout);
 		break;
 	default:
 		error = got_error(GOT_ERR_OBJ_TYPE);
@@ -4847,6 +4919,10 @@ done:
 		free((char *)pe->path);
 	got_pathlist_free(&paths);
 	got_ref_list_free(&refs);
+	if (f1 && fclose(f1) == EOF && error == NULL)
+		error = got_error_from_errno("fclose");
+	if (f2 && fclose(f2) == EOF && error == NULL)
+		error = got_error_from_errno("fclose");
 	return error;
 }
 
@@ -8827,8 +8903,9 @@ struct check_path_prefix_arg {
 
 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_blob_object *blob2, FILE *f1, FILE *f2,
+    struct got_object_id *id1, struct got_object_id *id2,
+    const char *path1, const char *path2,
     mode_t mode1, mode_t mode2, struct got_repository *repo)
 {
 	struct check_path_prefix_arg *a = arg;
@@ -8876,7 +8953,7 @@ check_path_prefix(struct got_object_id *parent_id,
 		cpp_arg.path_prefix++;
 	cpp_arg.len = strlen(cpp_arg.path_prefix);
 	cpp_arg.errcode = errcode;
-	err = got_diff_tree(tree1, tree2, "", "", repo,
+	err = got_diff_tree(tree1, tree2, NULL, NULL, "", "", repo,
 	    check_path_prefix_in_diff, &cpp_arg, 0);
 done:
 	if (tree1)
blob - 3d8c00182e1858eae16c7d8308e2673b82997bad
blob + 613976ee0e8a3f55e862ceb0aa730fd85f3e46e3
--- gotweb/gotweb.c
+++ gotweb/gotweb.c
@@ -2851,7 +2851,7 @@ static const struct got_error *
 gw_output_diff(struct gw_trans *gw_trans, struct gw_header *header)
 {
 	const struct got_error *error;
-	FILE *f = NULL;
+	FILE *f = NULL, *f1 = NULL, *f2 = NULL;
 	struct got_object_id *id1 = NULL, *id2 = NULL;
 	char *label1 = NULL, *label2 = NULL, *line = NULL;
 	int obj_type;
@@ -2863,6 +2863,18 @@ gw_output_diff(struct gw_trans *gw_trans, struct gw_he
 	if (f == NULL)
 		return NULL;
 
+	f1 = got_opentemp();
+	if (f1 == NULL) {
+		error = got_error_from_errno("got_opentemp");
+		goto done;
+	}
+
+	f2 = got_opentemp();
+	if (f2 == NULL) {
+		error = got_error_from_errno("got_opentemp");
+		goto done;
+	}
+
 	if (header->parent_id != NULL &&
 	    strncmp(header->parent_id, "/dev/null", 9) != 0) {
 		error = got_repo_match_object_id(&id1, &label1,
@@ -2883,16 +2895,16 @@ gw_output_diff(struct gw_trans *gw_trans, struct gw_he
 		goto done;
 	switch (obj_type) {
 	case GOT_OBJ_TYPE_BLOB:
-		error = got_diff_objects_as_blobs(NULL, NULL, id1, id2,
-		    NULL, NULL, 3, 0, 0, gw_trans->repo, f);
+		error = got_diff_objects_as_blobs(NULL, NULL, f1, f2,
+		    id1, id2, NULL, NULL, 3, 0, 0, gw_trans->repo, f);
 		break;
 	case GOT_OBJ_TYPE_TREE:
-		error = got_diff_objects_as_trees(NULL, NULL, id1, id2,
-		    NULL, "", "", 3, 0, 0, gw_trans->repo, f);
+		error = got_diff_objects_as_trees(NULL, NULL, f1, f2,
+		    id1, id2, NULL, "", "", 3, 0, 0, gw_trans->repo, f);
 		break;
 	case GOT_OBJ_TYPE_COMMIT:
-		error = got_diff_objects_as_commits(NULL, NULL, id1, id2,
-		    NULL, 3, 0, 0, gw_trans->repo, f);
+		error = got_diff_objects_as_commits(NULL, NULL, f1, f2,
+		    id1, id2, NULL, 3, 0, 0, gw_trans->repo, f);
 		break;
 	default:
 		error = got_error(GOT_ERR_OBJ_TYPE);
@@ -2922,6 +2934,10 @@ gw_output_diff(struct gw_trans *gw_trans, struct gw_he
 done:
 	if (f && fclose(f) == EOF && error == NULL)
 		error = got_error_from_errno("fclose");
+	if (f1 && fclose(f1) == EOF && error == NULL)
+		error = got_error_from_errno("fclose");
+	if (f2 && fclose(f2) == EOF && error == NULL)
+		error = got_error_from_errno("fclose");
 	free(line);
 	free(label1);
 	free(label2);
blob - 47d14eddfbf3c27bb83d461d7330e04389cd0700
blob + 5dbe18b856a53a58bee722d773249a93e91993a8
--- include/got_diff.h
+++ include/got_diff.h
@@ -16,8 +16,15 @@
 
 /*
  * Compute the differences between two blobs 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.
+ * to the provided output file. Two open temporary files must be provided
+ * for internal use; these files can be obtained from got_opentemp() and
+ * must be closed by the caller.
+ * If one of the blobs being diffed does not exist, all corresponding
+ * blob object and temporary file arguments should be set to NULL.
+ * Two const char * diff header labels may be provided which will be used
+ * to identify each blob in the diff output.
+ * 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.
  * 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.
  * Whitespace differences may optionally be ignored.
@@ -25,19 +32,20 @@
  * array of line offsets for, and the number of lines in, the unidiff text.
  */
 const struct got_error *got_diff_blob(off_t **, size_t *,
-    struct got_blob_object *, struct got_blob_object *,
+    struct got_blob_object *, struct got_blob_object *, FILE *, FILE *,
     const char *, const char *, int, int, int, FILE *);
 
 /*
  * Compute the differences between a blob and a file and write unified diff
- * text to the provided output file. The file's size must be provided, as
- * well as a const char * diff header label which identifies the file.
+ * text to the provided output file. The blob object, its content, and its
+ * size must be provided.The file's size must be provided, as well as a
+ * const char * diff header label which identifies the file.
  * An optional const char * diff header label for the blob may be provided, too.
  * The number of context lines to show in the diff must be specified as well.
  * 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, int, FILE *);
+const struct got_error *got_diff_blob_file(struct got_blob_object *, FILE *,
+    off_t, const char *, FILE *, size_t, const char *, int, int, int, FILE *);
 
 /*
  * A callback function invoked to handle the differences between two blobs
@@ -45,13 +53,15 @@ const struct got_error *got_diff_blob_file(struct got_
  * 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.
+ * Two open temporary files must be provided for internal use; these files
+ * can be obtained from got_opentemp() and must be closed by the caller.
  * 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_blob_object *, struct got_blob_object *, FILE *, FILE *,
     struct got_object_id *, struct got_object_id *,
     const char *, const char *, mode_t, mode_t, struct got_repository *);
 
@@ -82,7 +92,7 @@ struct got_diff_blob_output_unidiff_arg {
 	off_t *line_offsets;	/* Dispose of with free(3) when done. */
 };
 const struct got_error *got_diff_blob_output_unidiff(void *,
-    struct got_blob_object *, struct got_blob_object *,
+    struct got_blob_object *, struct got_blob_object *, FILE *, FILE *,
     struct got_object_id *, struct got_object_id *,
     const char *, const char *, mode_t, mode_t, struct got_repository *);
 
@@ -92,9 +102,14 @@ const struct got_error *got_diff_blob_output_unidiff(v
  * Diffing of blob content can be suppressed by passing zero for the
  * 'diff_content' parameter. The callback will then only receive blob
  * object IDs and diff labels, but NULL pointers instead of blob objects.
+ * If 'diff_content' is set, two open temporary files must be provided
+ * for internal use; these files can be obtained from got_opentemp()
+ * and must be closed by the caller. Otherwise the files can be NULL.
+ * The set of arguments relating to either tree may be NULL to indicate
+ * that no content is present on its respective side of the diff.
  */
 const struct got_error *got_diff_tree(struct got_tree_object *,
-    struct got_tree_object *, const char *, const char *,
+    struct got_tree_object *, FILE *, FILE *, const char *, const char *,
     struct got_repository *, got_diff_blob_cb cb, void *cb_arg, int);
 
 /*
@@ -114,7 +129,7 @@ struct got_diff_changed_path {
 	int status;
 };
 const struct got_error *got_diff_tree_collect_changed_paths(void *,
-    struct got_blob_object *, struct got_blob_object *,
+    struct got_blob_object *, struct got_blob_object *, FILE *, FILE *,
     struct got_object_id *, struct got_object_id *,
     const char *, const char *, mode_t, mode_t, struct got_repository *);
 
@@ -122,13 +137,17 @@ const struct got_error *got_diff_tree_collect_changed_
  * Diff two objects, assuming both objects are blobs. 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.
+ * Two open temporary files must be provided for internal use; these files
+ * can be obtained from got_opentemp() and must be closed by the caller.
+ * 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.
  * The number of context lines to show in the diff must be specified as well.
  * Write unified diff text to the provided output FILE.
  * If not NULL, the two initial output arguments will be populated with an
  * array of line offsets for, and the number of lines in, the unidiff text.
  */
 const struct got_error *got_diff_objects_as_blobs(off_t **, size_t *,
-    struct got_object_id *, struct got_object_id *,
+    FILE *, FILE *, struct got_object_id *, struct got_object_id *,
     const char *, const char *, int, int, int,
     struct got_repository *, FILE *);
 
@@ -137,23 +156,32 @@ const struct got_error *got_diff_objects_as_blobs(off_
  * header labels may be provided which will be used to identify each blob in
  * the trees. If a label is NULL, use the blob's SHA1 checksum instead.
  * The number of context lines to show in diffs must be specified.
+ * Two open temporary files must be provided for internal use; these files
+ * can be obtained from got_opentemp() and must be closed by the caller.
+ * The set of arguments relating to either tree may be NULL to indicate
+ * that no content is present on its respective side of the diff.
  * Write unified diff text to the provided output FILE.
  * If not NULL, the two initial output arguments will be populated with an
  * array of line offsets for, and the number of lines in, the unidiff text.
  */
 const struct got_error *got_diff_objects_as_trees(off_t **, size_t *,
-    struct got_object_id *, struct got_object_id *, struct got_pathlist_head *,
-    char *, char *, int, int, int, struct got_repository *, FILE *);
+    FILE *, FILE *, struct got_object_id *, struct got_object_id *,
+    struct got_pathlist_head *, char *, char *, int, int, int,
+    struct got_repository *, FILE *);
 
 /*
  * Diff two objects, assuming both objects are commits.
  * The number of context lines to show in diffs must be specified.
+ * Two open temporary files must be provided for internal use; these files
+ * can be obtained from got_opentemp() and must be closed by the caller.
+ * The set of arguments relating to either commit may be NULL to indicate
+ * that no content is present on its respective side of the diff.
  * Write unified diff text to the provided output FILE.
  * If not NULL, the two initial output arguments will be populated with an
  * 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 *, struct got_pathlist_head *,
-    int, int, int, struct got_repository *, FILE *);
+    FILE *, FILE *, struct got_object_id *, struct got_object_id *,
+    struct got_pathlist_head *, int, int, int, struct got_repository *, FILE *);
 
 #define GOT_DIFF_MAX_CONTEXT	64
blob - da3c26b1d829a76ecc75775909353f514f618f42
blob + 1c12d8425dd6f7aa30fb805c6ff288d3b84161c0
--- lib/diff.c
+++ lib/diff.c
@@ -28,7 +28,6 @@
 #include "got_repository.h"
 #include "got_error.h"
 #include "got_diff.h"
-#include "got_opentemp.h"
 #include "got_path.h"
 #include "got_cancel.h"
 #include "got_worktree.h"
@@ -53,14 +52,25 @@ add_line_offset(off_t **line_offsets, size_t *nlines, 
 }
 
 static const struct got_error *
+reset_file(FILE *f)
+{
+	if (fpurge(f) == EOF)
+		return got_error_from_errno("fpurge");
+	if (ftruncate(fileno(f), 0L) == -1)
+		return got_error_from_errno("ftruncate");
+	if (fseeko(f, 0L, SEEK_SET) == -1)
+		return got_error_from_errno("fseeko");
+	return NULL;
+}
+
+static const struct got_error *
 diff_blobs(off_t **line_offsets, size_t *nlines,
     struct got_diffreg_result **resultp, struct got_blob_object *blob1,
-    struct got_blob_object *blob2,
+    struct got_blob_object *blob2, FILE *f1, FILE *f2,
     const char *label1, const char *label2, mode_t mode1, mode_t mode2,
     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;
 	char hex1[SHA1_DIGEST_STRING_LENGTH];
 	char hex2[SHA1_DIGEST_STRING_LENGTH];
 	char *idstr1 = NULL, *idstr2 = NULL;
@@ -80,20 +90,15 @@ diff_blobs(off_t **line_offsets, size_t *nlines,
 	if (resultp)
 		*resultp = NULL;
 
-	if (blob1) {
-		f1 = got_opentemp();
-		if (f1 == NULL)
-			return got_error_from_errno("got_opentemp");
+	if (f1) {
+		err = reset_file(f1);
+		if (err)
+			goto done;
 	}
-
-	if (blob2) {
-		f2 = got_opentemp();
-		if (f2 == NULL) {
-			err = got_error_from_errno("got_opentemp");
-			if (f1)
-				fclose(f1);
-			return err;
-		}
+	if (f2) {
+		err = reset_file(f2);
+		if (err)
+			goto done;
 	}
 
 	size1 = 0;
@@ -189,66 +194,53 @@ diff_blobs(off_t **line_offsets, size_t *nlines,
 			err = free_err;
 	}
 done:
-	if (f1 && fclose(f1) == EOF && err == NULL)
-		err = got_error_from_errno("fclose");
-	if (f2 && fclose(f2) == EOF && err == NULL)
-		err = got_error_from_errno("fclose");
 	return err;
 }
 
 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,
-    mode_t mode1, mode_t mode2, struct got_repository *repo)
+    struct got_blob_object *blob2, FILE *f1, FILE *f2,
+    struct got_object_id *id1, struct got_object_id *id2,
+    const char *label1, const char *label2, mode_t mode1, mode_t mode2,
+    struct got_repository *repo)
 {
 	struct got_diff_blob_output_unidiff_arg *a = arg;
 
 	return diff_blobs(&a->line_offsets, &a->nlines, NULL,
-	    blob1, blob2, label1, label2, mode1, mode2, a->diff_context,
+	    blob1, blob2, f1, f2, label1, label2, mode1, mode2, a->diff_context,
 	    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, int force_text_diff, FILE *outfile)
+    FILE *f1, FILE *f2, const char *label1, const char *label2,
+    int diff_context, int ignore_whitespace, int force_text_diff,
+    FILE *outfile)
 {
-	return diff_blobs(line_offsets, nlines, NULL, blob1, blob2,
+	return diff_blobs(line_offsets, nlines, NULL, blob1, blob2, f1, f2,
 	    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,
-    int force_text_diff, FILE *outfile)
+    struct got_blob_object *blob1, FILE *f1, off_t size1, const char *label1,
+    FILE *f2, size_t size2, 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;
 	char hex1[SHA1_DIGEST_STRING_LENGTH];
 	char *idstr1 = NULL;
-	off_t size1;
 	struct got_diffreg_result *result = NULL;
 
 	if (resultp)
 		*resultp = NULL;
 
-	size1 = 0;
-	if (blob1) {
-		f1 = got_opentemp();
-		if (f1 == NULL)
-			return got_error_from_errno("got_opentemp");
+	if (blob1)
 		idstr1 = got_object_blob_id_str(blob1, hex1, sizeof(hex1));
-		err = got_object_blob_dump_to_file(&size1, NULL, NULL, f1,
-		    blob1);
-		if (err)
-			goto done;
-	} else {
+	else
 		idstr1 = "/dev/null";
-	}
 
 	if (outfile) {
 		fprintf(outfile, "blob - %s\n", label1 ? label1 : idstr1);
@@ -263,7 +255,7 @@ diff_blob_file(struct got_diffreg_result **resultp,
 
 	if (outfile) {
 		err = got_diffreg_output(NULL, NULL, result,
-		    blob1 != NULL, f2 != NULL,
+		    f1 != NULL, f2 != NULL,
 		    label2, /* show local file's path, not a blob ID */
 		    label2, GOT_DIFF_OUTPUT_UNIDIFF,
 		    diff_context, outfile);
@@ -279,23 +271,22 @@ diff_blob_file(struct got_diffreg_result **resultp,
 			err = free_err;
 	}
 done:
-	if (f1 && fclose(f1) == EOF && err == NULL)
-		err = got_error_from_errno("fclose");
 	return err;
 }
 
 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, int force_text_diff, FILE *outfile)
+got_diff_blob_file(struct got_blob_object *blob1, FILE *f1, off_t size1,
+    const char *label1, FILE *f2, size_t size2, const char *label2,
+    int diff_context, int ignore_whitespace, int force_text_diff, FILE *outfile)
 {
-	return diff_blob_file(NULL, blob1, label1, f2, size2, label2,
+	return diff_blob_file(NULL, blob1, f1, size1, label1, f2, size2, label2,
 	    diff_context, ignore_whitespace, force_text_diff, outfile);
 }
 
 static const struct got_error *
-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)
+diff_added_blob(struct got_object_id *id, FILE *f, const char *label,
+    mode_t mode, struct got_repository *repo,
+    got_diff_blob_cb cb, void *cb_arg)
 {
 	const struct got_error *err;
 	struct got_blob_object  *blob = NULL;
@@ -308,7 +299,8 @@ diff_added_blob(struct got_object_id *id, const char *
 	err = got_object_blob_open(&blob, repo, obj, 8192);
 	if (err)
 		goto done;
-	err = cb(cb_arg, NULL, blob, NULL, id, NULL, label, 0, mode, repo);
+	err = cb(cb_arg, NULL, blob, NULL, f, NULL, id,
+	    NULL, label, 0, mode, repo);
 done:
 	got_object_close(obj);
 	if (blob)
@@ -318,8 +310,9 @@ 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, mode_t mode1, mode_t mode2,
-    struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg)
+    FILE *f1, FILE *f2, 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;
@@ -351,8 +344,8 @@ diff_modified_blob(struct got_object_id *id1, struct g
 	if (err)
 		goto done;
 
-	err = cb(cb_arg, blob1, blob2, id1, id2, label1, label2, mode1, mode2,
-	    repo);
+	err = cb(cb_arg, blob1, blob2, f1, f2, id1, id2, label1, label2,
+	    mode1, mode2, repo);
 done:
 	if (obj1)
 		got_object_close(obj1);
@@ -366,8 +359,8 @@ done:
 }
 
 static const struct got_error *
-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)
+diff_deleted_blob(struct got_object_id *id, FILE *f, const char *label,
+    mode_t mode, struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg)
 {
 	const struct got_error *err;
 	struct got_blob_object  *blob = NULL;
@@ -380,7 +373,8 @@ diff_deleted_blob(struct got_object_id *id, const char
 	err = got_object_blob_open(&blob, repo, obj, 8192);
 	if (err)
 		goto done;
-	err = cb(cb_arg, blob, NULL, id, NULL, label, NULL, mode, 0, repo);
+	err = cb(cb_arg, blob, NULL, f, NULL, id, NULL, label, NULL,
+	    mode, 0, repo);
 done:
 	got_object_close(obj);
 	if (blob)
@@ -389,7 +383,7 @@ done:
 }
 
 static const struct got_error *
-diff_added_tree(struct got_object_id *id, const char *label,
+diff_added_tree(struct got_object_id *id, FILE *f, const char *label,
     struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg,
     int diff_content)
 {
@@ -410,8 +404,8 @@ diff_added_tree(struct got_object_id *id, const char *
 	if (err)
 		goto done;
 
-	err = got_diff_tree(NULL, tree, NULL, label, repo, cb, cb_arg,
-	    diff_content);
+	err = got_diff_tree(NULL, tree, NULL, f, NULL, label,
+	    repo, cb, cb_arg, diff_content);
 done:
 	if (tree)
 		got_object_tree_close(tree);
@@ -422,8 +416,9 @@ 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, struct got_repository *repo,
-    got_diff_blob_cb cb, void *cb_arg, int diff_content)
+    FILE *f1, FILE *f2, const char *label1, const char *label2,
+    struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg,
+    int diff_content)
 {
 	const struct got_error *err;
 	struct got_object *treeobj1 = NULL;
@@ -457,8 +452,8 @@ diff_modified_tree(struct got_object_id *id1, struct g
 	if (err)
 		goto done;
 
-	err = got_diff_tree(tree1, tree2, label1, label2, repo, cb, cb_arg,
-	    diff_content);
+	err = got_diff_tree(tree1, tree2, f1, f2, label1, label2,
+	    repo, cb, cb_arg, diff_content);
 
 done:
 	if (tree1)
@@ -473,7 +468,7 @@ done:
 }
 
 static const struct got_error *
-diff_deleted_tree(struct got_object_id *id, const char *label,
+diff_deleted_tree(struct got_object_id *id, FILE *f, const char *label,
     struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg,
     int diff_content)
 {
@@ -494,8 +489,8 @@ diff_deleted_tree(struct got_object_id *id, const char
 	if (err)
 		goto done;
 
-	err = got_diff_tree(tree, NULL, label, NULL, repo, cb, cb_arg,
-	    diff_content);
+	err = got_diff_tree(tree, NULL, f, NULL, label, NULL,
+	    repo, cb, cb_arg, diff_content);
 done:
 	if (tree)
 		got_object_tree_close(tree);
@@ -514,8 +509,8 @@ diff_kind_mismatch(struct got_object_id *id1, struct g
 }
 
 static const struct got_error *
-diff_entry_old_new(struct got_tree_entry *te1,
-    struct got_tree_entry *te2, const char *label1, const char *label2,
+diff_entry_old_new(struct got_tree_entry *te1, struct got_tree_entry *te2,
+    FILE *f1, FILE *f2, const char *label1, const char *label2,
     struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg,
     int diff_content)
 {
@@ -527,15 +522,16 @@ diff_entry_old_new(struct got_tree_entry *te1,
 
 	if (te2 == NULL) {
 		if (S_ISDIR(te1->mode))
-			err = diff_deleted_tree(&te1->id, label1, repo,
+			err = diff_deleted_tree(&te1->id, f1, label1, repo,
 			    cb, cb_arg, diff_content);
 		else {
 			if (diff_content)
-				err = diff_deleted_blob(&te1->id, label1,
+				err = diff_deleted_blob(&te1->id, f1, label1,
 				    te1->mode, repo, cb, cb_arg);
 			else
-				err = cb(cb_arg, NULL, NULL, &te1->id, NULL,
-				    label1, NULL, te1->mode, 0, repo);
+				err = cb(cb_arg, NULL, NULL, NULL, NULL,
+				    &te1->id, NULL, label1, NULL,
+				    te1->mode, 0, repo);
 		}
 		return err;
 	} else if (got_object_tree_entry_is_submodule(te2))
@@ -544,7 +540,7 @@ diff_entry_old_new(struct got_tree_entry *te1,
 	id_match = (got_object_id_cmp(&te1->id, &te2->id) == 0);
 	if (S_ISDIR(te1->mode) && S_ISDIR(te2->mode)) {
 		if (!id_match)
-			return diff_modified_tree(&te1->id, &te2->id,
+			return diff_modified_tree(&te1->id, &te2->id, f1, f2,
 			    label1, label2, repo, cb, cb_arg, diff_content);
 	} else if ((S_ISREG(te1->mode) || S_ISLNK(te1->mode)) &&
 	    (S_ISREG(te2->mode) || S_ISLNK(te2->mode))) {
@@ -553,12 +549,12 @@ diff_entry_old_new(struct got_tree_entry *te1,
 		    (te2->mode & (S_IFLNK | S_IXUSR))) {
 			if (diff_content)
 				return diff_modified_blob(&te1->id, &te2->id,
-				    label1, label2, te1->mode, te2->mode,
-				    repo, cb, cb_arg);
+				    f1, f2, label1, label2, te1->mode,
+				    te2->mode, repo, cb, cb_arg);
 			else
-				return cb(cb_arg, NULL, NULL, &te1->id,
-				    &te2->id, label1, label2, te1->mode,
-				    te2->mode, repo);
+				return cb(cb_arg, NULL, NULL, NULL, NULL,
+				    &te1->id, &te2->id, label1, label2,
+				    te1->mode, te2->mode, repo);
 		}
 	}
 
@@ -571,7 +567,7 @@ diff_entry_old_new(struct got_tree_entry *te1,
 
 static const struct got_error *
 diff_entry_new_old(struct got_tree_entry *te2,
-    struct got_tree_entry *te1, const char *label2,
+    struct got_tree_entry *te1, FILE *f2, const char *label2,
     struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg,
     int diff_content)
 {
@@ -582,21 +578,22 @@ diff_entry_new_old(struct got_tree_entry *te2,
 		return NULL;
 
 	if (S_ISDIR(te2->mode))
-		return diff_added_tree(&te2->id, label2, repo, cb, cb_arg,
-		    diff_content);
+		return diff_added_tree(&te2->id, f2, label2,
+		    repo, cb, cb_arg, diff_content);
 
 	if (diff_content)
-		return diff_added_blob(&te2->id, label2, te2->mode, repo, cb,
-		    cb_arg);
+		return diff_added_blob(&te2->id, f2, label2, te2->mode,
+		    repo, cb, cb_arg);
 
-	return cb(cb_arg, NULL, NULL, NULL, &te2->id, NULL, label2, 0,
-	    te2->mode, repo);
+	return cb(cb_arg, NULL, NULL, NULL, NULL, NULL, &te2->id,
+	    NULL, label2, 0, te2->mode, repo);
 }
 
 const struct got_error *
 got_diff_tree_collect_changed_paths(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_blob_object *blob2, FILE *f1, FILE *f2,
+    struct got_object_id *id1, struct got_object_id *id2,
+    const char *label1, const char *label2,
     mode_t mode1, mode_t mode2, struct got_repository *repo)
 {
 	const struct got_error *err = NULL;
@@ -637,8 +634,9 @@ done:
 
 const struct got_error *
 got_diff_tree(struct got_tree_object *tree1, struct got_tree_object *tree2,
-    const char *label1, const char *label2, struct got_repository *repo,
-    got_diff_blob_cb cb, void *cb_arg, int diff_content)
+    FILE *f1, FILE *f2, const char *label1, const char *label2,
+    struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg,
+    int diff_content)
 {
 	const struct got_error *err = NULL;
 	struct got_tree_entry *te1 = NULL;
@@ -673,8 +671,8 @@ got_diff_tree(struct got_tree_object *tree1, struct go
 					return
 					    got_error_from_errno("asprintf");
 			}
-			err = diff_entry_old_new(te1, te, l1, l2, repo, cb,
-			    cb_arg, diff_content);
+			err = diff_entry_old_new(te1, te, f1, f2, l1, l2,
+			    repo, cb, cb_arg, diff_content);
 			if (err)
 				break;
 		}
@@ -696,7 +694,7 @@ got_diff_tree(struct got_tree_object *tree1, struct go
 					return
 					    got_error_from_errno("asprintf");
 			}
-			err = diff_entry_new_old(te2, te, l2, repo,
+			err = diff_entry_new_old(te2, te, f2, l2, repo,
 			    cb, cb_arg, diff_content);
 			if (err)
 				break;
@@ -729,7 +727,7 @@ got_diff_tree(struct got_tree_object *tree1, struct go
 
 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,
+    FILE *f1, FILE *f2, struct got_object_id *id1, struct got_object_id *id2,
     const char *label1, const char *label2, int diff_context,
     int ignore_whitespace, int force_text_diff,
     struct got_repository *repo, FILE *outfile)
@@ -750,7 +748,7 @@ got_diff_objects_as_blobs(off_t **line_offsets, size_t
 		if (err)
 			goto done;
 	}
-	err = got_diff_blob(line_offsets, nlines, blob1, blob2,
+	err = got_diff_blob(line_offsets, nlines, blob1, blob2, f1, f2,
 	    label1, label2, diff_context, ignore_whitespace, force_text_diff,
 	    outfile);
 done:
@@ -763,8 +761,8 @@ done:
 
 static const struct got_error *
 diff_paths(struct got_tree_object *tree1, struct got_tree_object *tree2,
-    struct got_pathlist_head *paths, struct got_repository *repo,
-    got_diff_blob_cb cb, void *cb_arg)
+    FILE *f1, FILE *f2, struct got_pathlist_head *paths,
+    struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg)
 {
 	const struct got_error *err = NULL;
 	struct got_pathlist_entry *pe;
@@ -843,7 +841,7 @@ diff_paths(struct got_tree_object *tree1, struct got_t
 				if (err)
 					goto done;
 			}
-			err = cb(cb_arg, blob1, blob2, id1, id2,
+			err = cb(cb_arg, blob1, blob2, f1, f2, id1, id2,
 			    id1 ? pe->path : "/dev/null",
 			    id2 ? pe->path : "/dev/null",
 			    mode1, mode2, repo);
@@ -863,7 +861,7 @@ diff_paths(struct got_tree_object *tree1, struct got_t
 				if (err)
 					goto done;
 			}
-			err = got_diff_tree(subtree1, subtree2,
+			err = got_diff_tree(subtree1, subtree2, f1, f2,
 			    id1 ? pe->path : "/dev/null",
 			    id2 ? pe->path : "/dev/null",
 			    repo, cb, cb_arg, 1);
@@ -890,7 +888,7 @@ done:
 
 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,
+    FILE *f1, FILE *f2, struct got_object_id *id1, struct got_object_id *id2,
     struct got_pathlist_head *paths,
     char *label1, char *label2, int diff_context, int ignore_whitespace,
     int force_text_diff, struct got_repository *repo, FILE *outfile)
@@ -926,10 +924,10 @@ got_diff_objects_as_trees(off_t **line_offsets, size_t
 		arg.nlines = 0;
 	}
 	if (paths == NULL || TAILQ_EMPTY(paths)) {
-		err = got_diff_tree(tree1, tree2, label1, label2, repo,
-		    got_diff_blob_output_unidiff, &arg, 1);
+		err = got_diff_tree(tree1, tree2, f1, f2, label1, label2,
+		    repo, got_diff_blob_output_unidiff, &arg, 1);
 	} else {
-		err = diff_paths(tree1, tree2, paths, repo,
+		err = diff_paths(tree1, tree2, f1, f2, paths, repo,
 		    got_diff_blob_output_unidiff, &arg);
 	}
 	if (want_lineoffsets) {
@@ -946,7 +944,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,
+    FILE *f1, FILE *f2, struct got_object_id *id1, struct got_object_id *id2,
     struct got_pathlist_head *paths,
     int diff_context, int ignore_whitespace, int force_text_diff,
     struct got_repository *repo, FILE *outfile)
@@ -967,7 +965,7 @@ got_diff_objects_as_commits(off_t **line_offsets, size
 	if (err)
 		goto done;
 
-	err = got_diff_objects_as_trees(line_offsets, nlines,
+	err = got_diff_objects_as_trees(line_offsets, nlines, f1, f2,
 	    commit1 ? got_object_commit_get_tree_id(commit1) : NULL,
 	    got_object_commit_get_tree_id(commit2), paths, "", "",
 	    diff_context, ignore_whitespace, force_text_diff, repo, outfile);
blob - ca8e4d6a6ba63c5f3e16487196a7a3bf16fa888b
blob + c8e98cc3b117b9e02d199a6a46a60f02d73c2b83
--- lib/worktree.c
+++ lib/worktree.c
@@ -2749,8 +2749,9 @@ struct merge_file_cb_arg {
 
 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_blob_object *blob2, FILE *f1, FILE *f2,
+    struct got_object_id *id1, struct got_object_id *id2,
+    const char *path1, const char *path2,
     mode_t mode1, mode_t mode2, struct got_repository *repo)
 {
 	static const struct got_error *err = NULL;
@@ -3056,8 +3057,9 @@ struct check_merge_conflicts_arg {
 
 static const struct got_error *
 check_merge_conflicts(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_blob_object *blob2, FILE *f1, FILE *f2,
+    struct got_object_id *id1, struct got_object_id *id2,
+    const char *path1, const char *path2,
     mode_t mode1, mode_t mode2, struct got_repository *repo)
 {
 	const struct got_error *err = NULL;
@@ -3105,6 +3107,7 @@ merge_files(struct got_worktree *worktree, struct got_
 	struct check_merge_conflicts_arg cmc_arg;
 	struct merge_file_cb_arg arg;
 	char *label_orig = NULL;
+	FILE *f1 = NULL, *f2 = NULL;
 
 	if (commit_id1) {
 		err = got_object_open_as_commit(&commit1, repo, commit_id1);
@@ -3133,6 +3136,12 @@ merge_files(struct got_worktree *worktree, struct got_
 			goto done;
 		}
 		free(id_str);
+
+		f1 = got_opentemp();
+		if (f1 == NULL) {
+			err = got_error_from_errno("got_opentemp");
+			goto done;
+		}
 	}
 
 	err = got_object_open_as_commit(&commit2, repo, commit_id2);
@@ -3148,10 +3157,16 @@ merge_files(struct got_worktree *worktree, struct got_
 	if (err)
 		goto done;
 
+	f2 = got_opentemp();
+	if (f2 == NULL) {
+		err = got_error_from_errno("got_opentemp");
+		goto done;
+	}
+
 	cmc_arg.worktree = worktree;
 	cmc_arg.fileindex = fileindex;
 	cmc_arg.repo = repo;
-	err = got_diff_tree(tree1, tree2, "", "", repo,
+	err = got_diff_tree(tree1, tree2, f1, f2, "", "", repo,
 	    check_merge_conflicts, &cmc_arg, 0);
 	if (err)
 		goto done;
@@ -3165,7 +3180,8 @@ merge_files(struct got_worktree *worktree, struct got_
 	arg.label_orig = label_orig;
 	arg.commit_id2 = commit_id2;
 	arg.allow_bad_symlinks = 1; /* preserve bad symlinks across merges */
-	err = got_diff_tree(tree1, tree2, "", "", repo, merge_file_cb, &arg, 1);
+	err = got_diff_tree(tree1, tree2, f1, f2, "", "", repo,
+	    merge_file_cb, &arg, 1);
 	sync_err = sync_fileindex(fileindex, fileindex_path);
 	if (sync_err && err == NULL)
 		err = sync_err;
@@ -3178,6 +3194,10 @@ done:
 		got_object_tree_close(tree1);
 	if (tree2)
 		got_object_tree_close(tree2);
+	if (f1 && fclose(f1) == EOF && err == NULL)
+		err = got_error_from_errno("fclose");
+	if (f2 && fclose(f2) == EOF && err == NULL)
+		err = got_error_from_errno("fclose");
 	free(label_orig);
 	return err;
 }
blob - a4221a9f756b84c1412ef1f4983cccd6aa233c2d
blob + 2e5a6b392b55da86649c73d7f8efeabff5ff6cab
--- tog/tog.c
+++ tog/tog.c
@@ -308,7 +308,7 @@ get_color_value(const char *envvar)
 struct tog_diff_view_state {
 	struct got_object_id *id1, *id2;
 	const char *label1, *label2;
-	FILE *f;
+	FILE *f, *f1, *f2;
 	int first_displayed_line;
 	int last_displayed_line;
 	int eof;
@@ -3144,7 +3144,7 @@ get_changed_paths(struct got_pathlist_head *paths,
 	if (err)
 		goto done;
 
-	err = got_diff_tree(tree1, tree2, "", "", repo,
+	err = got_diff_tree(tree1, tree2, NULL, NULL, "", "", repo,
 	    got_diff_tree_collect_changed_paths, paths, 0);
 done:
 	if (tree1)
@@ -3364,12 +3364,13 @@ create_diff(struct tog_diff_view_state *s)
 	switch (obj_type) {
 	case GOT_OBJ_TYPE_BLOB:
 		err = got_diff_objects_as_blobs(&s->line_offsets, &s->nlines,
-		    s->id1, s->id2, s->label1, s->label2, s->diff_context,
-		    s->ignore_whitespace, s->force_text_diff, s->repo, s->f);
+		    s->f1, s->f2, s->id1, s->id2, s->label1, s->label2,
+		    s->diff_context, s->ignore_whitespace, 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, NULL, "", "", s->diff_context,
+		    s->f1, s->f2, s->id1, s->id2, NULL, "", "", s->diff_context,
 		    s->ignore_whitespace, s->force_text_diff, s->repo, s->f);
 		break;
 	case GOT_OBJ_TYPE_COMMIT: {
@@ -3404,8 +3405,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, NULL, s->diff_context, s->ignore_whitespace,
-		    s->force_text_diff, s->repo, s->f);
+		    s->f1, s->f2, s->id1, s->id2, NULL, s->diff_context,
+		    s->ignore_whitespace, s->force_text_diff, s->repo, s->f);
 		break;
 	}
 	default:
@@ -3502,6 +3503,32 @@ search_next_diff_view(struct tog_view *view)
 }
 
 static const struct got_error *
+close_diff_view(struct tog_view *view)
+{
+	const struct got_error *err = NULL;
+	struct tog_diff_view_state *s = &view->state.diff;
+
+	free(s->id1);
+	s->id1 = NULL;
+	free(s->id2);
+	s->id2 = NULL;
+	if (s->f && fclose(s->f) == EOF)
+		err = got_error_from_errno("fclose");
+	s->f = NULL;
+	if (s->f1 && fclose(s->f1) == EOF)
+		err = got_error_from_errno("fclose");
+	s->f1 = NULL;
+	if (s->f2 && fclose(s->f2) == EOF)
+		err = got_error_from_errno("fclose");
+	s->f2 = NULL;
+	free_colors(&s->colors);
+	free(s->line_offsets);
+	s->line_offsets = NULL;
+	s->nlines = 0;
+	return err;
+}
+
+static const struct got_error *
 open_diff_view(struct tog_view *view, struct got_object_id *id1,
     struct got_object_id *id2, const char *label1, const char *label2,
     int diff_context, int ignore_whitespace, int force_text_diff,
@@ -3510,6 +3537,8 @@ open_diff_view(struct tog_view *view, struct got_objec
 	const struct got_error *err;
 	struct tog_diff_view_state *s = &view->state.diff;
 
+	memset(s, 0, sizeof(*s));
+
 	if (id1 != NULL && id2 != NULL) {
 	    int type1, type2;
 	    err = got_object_get_type(&type1, repo, id1);
@@ -3535,16 +3564,26 @@ open_diff_view(struct tog_view *view, struct got_objec
 		s->id1 = got_object_id_dup(id1);
 		if (s->id1 == NULL)
 			return got_error_from_errno("got_object_id_dup");
+		s->f1 = got_opentemp();
+		if (s->f1 == NULL) {
+			err = got_error_from_errno("got_opentemp");
+			goto done;
+		}
 	} else
 		s->id1 = NULL;
 
 	s->id2 = got_object_id_dup(id2);
 	if (s->id2 == NULL) {
-		free(s->id1);
-		s->id1 = NULL;
-		return got_error_from_errno("got_object_id_dup");
+		err = got_error_from_errno("got_object_id_dup");
+		goto done;
 	}
-	s->f = NULL;
+
+	s->f2 = got_opentemp();
+	if (s->f2 == NULL) {
+		err = got_error_from_errno("got_opentemp");
+		goto done;
+	}
+
 	s->first_displayed_line = 1;
 	s->last_displayed_line = view->nlines;
 	s->diff_context = diff_context;
@@ -3559,89 +3598,52 @@ open_diff_view(struct tog_view *view, struct got_objec
 		    "^-", TOG_COLOR_DIFF_MINUS,
 		    get_color_value("TOG_COLOR_DIFF_MINUS"));
 		if (err)
-			return err;
+			goto done;
 		err = add_color(&s->colors, "^\\+",
 		    TOG_COLOR_DIFF_PLUS,
 		    get_color_value("TOG_COLOR_DIFF_PLUS"));
-		if (err) {
-			free_colors(&s->colors);
-			return err;
-		}
+		if (err)
+			goto done;
 		err = add_color(&s->colors,
 		    "^@@", TOG_COLOR_DIFF_CHUNK_HEADER,
 		    get_color_value("TOG_COLOR_DIFF_CHUNK_HEADER"));
-		if (err) {
-			free_colors(&s->colors);
-			return err;
-		}
+		if (err)
+			goto done;
 
 		err = add_color(&s->colors,
 		    "^(commit [0-9a-f]|parent [0-9]|(blob|file) [-+] |"
 		    "[MDmA]  [^ ])", TOG_COLOR_DIFF_META,
 		    get_color_value("TOG_COLOR_DIFF_META"));
-		if (err) {
-			free_colors(&s->colors);
-			return err;
-		}
+		if (err)
+			goto done;
 
 		err = add_color(&s->colors,
 		    "^(from|via): ", TOG_COLOR_AUTHOR,
 		    get_color_value("TOG_COLOR_AUTHOR"));
-		if (err) {
-			free_colors(&s->colors);
-			return err;
-		}
+		if (err)
+			goto done;
 
 		err = add_color(&s->colors,
 		    "^date: ", TOG_COLOR_DATE,
 		    get_color_value("TOG_COLOR_DATE"));
-		if (err) {
-			free_colors(&s->colors);
-			return err;
-		}
+		if (err)
+			goto done;
 	}
 
 	if (log_view && view_is_splitscreen(view))
 		show_log_view(log_view); /* draw vborder */
 	diff_view_indicate_progress(view);
 
-	s->line_offsets = NULL;
-	s->nlines = 0;
 	err = create_diff(s);
-	if (err) {
-		free(s->id1);
-		s->id1 = NULL;
-		free(s->id2);
-		s->id2 = NULL;
-		free_colors(&s->colors);
-		return err;
-	}
 
 	view->show = show_diff_view;
 	view->input = input_diff_view;
 	view->close = close_diff_view;
 	view->search_start = search_start_diff_view;
 	view->search_next = search_next_diff_view;
-
-	return NULL;
-}
-
-static const struct got_error *
-close_diff_view(struct tog_view *view)
-{
-	const struct got_error *err = NULL;
-	struct tog_diff_view_state *s = &view->state.diff;
-
-	free(s->id1);
-	s->id1 = NULL;
-	free(s->id2);
-	s->id2 = NULL;
-	if (s->f && fclose(s->f) == EOF)
-		err = got_error_from_errno("fclose");
-	free_colors(&s->colors);
-	free(s->line_offsets);
-	s->line_offsets = NULL;
-	s->nlines = 0;
+done:
+	if (err)
+		close_diff_view(view);
 	return err;
 }