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

From:
Tracey Emery <tracey@traceyemery.net>
Subject:
Re: move creation of tempfiles out of lib/diff.c
To:
gameoftrees@openbsd.org
Date:
Tue, 31 May 2022 14:28:33 -0600

Download raw body.

Thread
On Tue, May 31, 2022 at 09:07:41PM +0200, Stefan Sperling wrote:
> 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?
> 

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;
>  }
>  
> 

-- 

Tracey Emery