2 * Copyright (c) 2017 Stefan Sperling <stsp@openbsd.org>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25 #include "got_compat.h"
27 #include "got_object.h"
28 #include "got_repository.h"
29 #include "got_error.h"
31 #include "got_opentemp.h"
33 #include "got_cancel.h"
34 #include "got_worktree.h"
36 #include "got_lib_diff.h"
37 #include "got_lib_delta.h"
38 #include "got_lib_inflate.h"
39 #include "got_lib_object.h"
41 static const struct got_error
*
42 add_line_offset(off_t
**line_offsets
, size_t *nlines
, off_t off
)
46 p
= reallocarray(*line_offsets
, *nlines
+ 1, sizeof(off_t
));
48 return got_error_from_errno("reallocarray");
50 (*line_offsets
)[*nlines
] = off
;
55 static const struct got_error
*
56 diff_blobs(off_t
**line_offsets
, size_t *nlines
,
57 struct got_diffreg_result
**resultp
, struct got_blob_object
*blob1
,
58 struct got_blob_object
*blob2
,
59 const char *label1
, const char *label2
, mode_t mode1
, mode_t mode2
,
60 int diff_context
, int ignore_whitespace
, int force_text_diff
, FILE *outfile
)
62 const struct got_error
*err
= NULL
, *free_err
;
63 FILE *f1
= NULL
, *f2
= NULL
;
64 char hex1
[SHA1_DIGEST_STRING_LENGTH
];
65 char hex2
[SHA1_DIGEST_STRING_LENGTH
];
66 char *idstr1
= NULL
, *idstr2
= NULL
;
68 struct got_diffreg_result
*result
;
72 if (line_offsets
&& *line_offsets
&& *nlines
> 0)
73 outoff
= (*line_offsets
)[*nlines
- 1];
74 else if (line_offsets
) {
75 err
= add_line_offset(line_offsets
, nlines
, 0);
86 return got_error_from_errno("got_opentemp");
92 err
= got_error_from_errno("got_opentemp");
101 idstr1
= got_object_blob_id_str(blob1
, hex1
, sizeof(hex1
));
102 err
= got_object_blob_dump_to_file(&size1
, NULL
, NULL
, f1
,
107 idstr1
= "/dev/null";
111 idstr2
= got_object_blob_id_str(blob2
, hex2
, sizeof(hex2
));
112 err
= got_object_blob_dump_to_file(&size2
, NULL
, NULL
, f2
,
117 idstr2
= "/dev/null";
120 char *modestr1
= NULL
, *modestr2
= NULL
;
122 if (mode1
&& mode1
!= mode2
) {
126 modebits
= (S_IRWXU
| S_IRWXG
| S_IRWXO
);
127 if (asprintf(&modestr1
, " (mode %o)",
128 mode1
& modebits
) == -1) {
129 err
= got_error_from_errno("asprintf");
133 if (mode2
&& mode1
!= mode2
) {
137 modebits
= (S_IRWXU
| S_IRWXG
| S_IRWXO
);
138 if (asprintf(&modestr2
, " (mode %o)",
139 mode2
& modebits
) == -1) {
140 err
= got_error_from_errno("asprintf");
144 n
= fprintf(outfile
, "blob - %s%s\n", idstr1
,
145 modestr1
? modestr1
: "");
150 err
= add_line_offset(line_offsets
, nlines
, outoff
);
155 n
= fprintf(outfile
, "blob + %s%s\n", idstr2
,
156 modestr2
? modestr2
: "");
161 err
= add_line_offset(line_offsets
, nlines
, outoff
);
169 err
= got_diffreg(&result
, f1
, f2
, GOT_DIFF_ALGORITHM_PATIENCE
,
170 ignore_whitespace
, force_text_diff
);
175 err
= got_diffreg_output(line_offsets
, nlines
, result
,
176 blob1
!= NULL
, blob2
!= NULL
,
177 label1
? label1
: idstr1
,
178 label2
? label2
: idstr2
,
179 GOT_DIFF_OUTPUT_UNIDIFF
, diff_context
, outfile
);
184 if (resultp
&& err
== NULL
)
187 free_err
= got_diffreg_result_free(result
);
188 if (free_err
&& err
== NULL
)
192 if (f1
&& fclose(f1
) == EOF
&& err
== NULL
)
193 err
= got_error_from_errno("fclose");
194 if (f2
&& fclose(f2
) == EOF
&& err
== NULL
)
195 err
= got_error_from_errno("fclose");
199 const struct got_error
*
200 got_diff_blob_output_unidiff(void *arg
, struct got_blob_object
*blob1
,
201 struct got_blob_object
*blob2
, struct got_object_id
*id1
,
202 struct got_object_id
*id2
, const char *label1
, const char *label2
,
203 mode_t mode1
, mode_t mode2
, struct got_repository
*repo
)
205 struct got_diff_blob_output_unidiff_arg
*a
= arg
;
207 return diff_blobs(&a
->line_offsets
, &a
->nlines
, NULL
,
208 blob1
, blob2
, label1
, label2
, mode1
, mode2
, a
->diff_context
,
209 a
->ignore_whitespace
, a
->force_text_diff
, a
->outfile
);
212 const struct got_error
*
213 got_diff_blob(off_t
**line_offsets
, size_t *nlines
,
214 struct got_blob_object
*blob1
, struct got_blob_object
*blob2
,
215 const char *label1
, const char *label2
, int diff_context
,
216 int ignore_whitespace
, int force_text_diff
, FILE *outfile
)
218 return diff_blobs(line_offsets
, nlines
, NULL
, blob1
, blob2
,
219 label1
, label2
, 0, 0, diff_context
, ignore_whitespace
,
220 force_text_diff
, outfile
);
223 static const struct got_error
*
224 diff_blob_file(struct got_diffreg_result
**resultp
,
225 struct got_blob_object
*blob1
, const char *label1
, FILE *f2
, size_t size2
,
226 const char *label2
, int diff_context
, int ignore_whitespace
,
227 int force_text_diff
, FILE *outfile
)
229 const struct got_error
*err
= NULL
, *free_err
;
231 char hex1
[SHA1_DIGEST_STRING_LENGTH
];
234 struct got_diffreg_result
*result
= NULL
;
243 return got_error_from_errno("got_opentemp");
244 idstr1
= got_object_blob_id_str(blob1
, hex1
, sizeof(hex1
));
245 err
= got_object_blob_dump_to_file(&size1
, NULL
, NULL
, f1
,
250 idstr1
= "/dev/null";
254 fprintf(outfile
, "blob - %s\n", label1
? label1
: idstr1
);
255 fprintf(outfile
, "file + %s\n",
256 f2
== NULL
? "/dev/null" : label2
);
259 err
= got_diffreg(&result
, f1
, f2
, GOT_DIFF_ALGORITHM_PATIENCE
,
260 ignore_whitespace
, force_text_diff
);
265 err
= got_diffreg_output(NULL
, NULL
, result
,
266 blob1
!= NULL
, f2
!= NULL
,
267 label2
, /* show local file's path, not a blob ID */
268 label2
, GOT_DIFF_OUTPUT_UNIDIFF
,
269 diff_context
, outfile
);
274 if (resultp
&& err
== NULL
)
277 free_err
= got_diffreg_result_free(result
);
278 if (free_err
&& err
== NULL
)
282 if (f1
&& fclose(f1
) == EOF
&& err
== NULL
)
283 err
= got_error_from_errno("fclose");
287 const struct got_error
*
288 got_diff_blob_file(struct got_blob_object
*blob1
, const char *label1
,
289 FILE *f2
, size_t size2
, const char *label2
, int diff_context
,
290 int ignore_whitespace
, int force_text_diff
, FILE *outfile
)
292 return diff_blob_file(NULL
, blob1
, label1
, f2
, size2
, label2
,
293 diff_context
, ignore_whitespace
, force_text_diff
, outfile
);
296 static const struct got_error
*
297 diff_added_blob(struct got_object_id
*id
, const char *label
, mode_t mode
,
298 struct got_repository
*repo
, got_diff_blob_cb cb
, void *cb_arg
)
300 const struct got_error
*err
;
301 struct got_blob_object
*blob
= NULL
;
302 struct got_object
*obj
= NULL
;
304 err
= got_object_open(&obj
, repo
, id
);
308 err
= got_object_blob_open(&blob
, repo
, obj
, 8192);
311 err
= cb(cb_arg
, NULL
, blob
, NULL
, id
, NULL
, label
, 0, mode
, repo
);
313 got_object_close(obj
);
315 got_object_blob_close(blob
);
319 static const struct got_error
*
320 diff_modified_blob(struct got_object_id
*id1
, struct got_object_id
*id2
,
321 const char *label1
, const char *label2
, mode_t mode1
, mode_t mode2
,
322 struct got_repository
*repo
, got_diff_blob_cb cb
, void *cb_arg
)
324 const struct got_error
*err
;
325 struct got_object
*obj1
= NULL
;
326 struct got_object
*obj2
= NULL
;
327 struct got_blob_object
*blob1
= NULL
;
328 struct got_blob_object
*blob2
= NULL
;
330 err
= got_object_open(&obj1
, repo
, id1
);
333 if (obj1
->type
!= GOT_OBJ_TYPE_BLOB
) {
334 err
= got_error(GOT_ERR_OBJ_TYPE
);
338 err
= got_object_open(&obj2
, repo
, id2
);
341 if (obj2
->type
!= GOT_OBJ_TYPE_BLOB
) {
342 err
= got_error(GOT_ERR_BAD_OBJ_DATA
);
346 err
= got_object_blob_open(&blob1
, repo
, obj1
, 8192);
350 err
= got_object_blob_open(&blob2
, repo
, obj2
, 8192);
354 err
= cb(cb_arg
, blob1
, blob2
, id1
, id2
, label1
, label2
, mode1
, mode2
,
358 got_object_close(obj1
);
360 got_object_close(obj2
);
362 got_object_blob_close(blob1
);
364 got_object_blob_close(blob2
);
368 static const struct got_error
*
369 diff_deleted_blob(struct got_object_id
*id
, const char *label
, mode_t mode
,
370 struct got_repository
*repo
, got_diff_blob_cb cb
, void *cb_arg
)
372 const struct got_error
*err
;
373 struct got_blob_object
*blob
= NULL
;
374 struct got_object
*obj
= NULL
;
376 err
= got_object_open(&obj
, repo
, id
);
380 err
= got_object_blob_open(&blob
, repo
, obj
, 8192);
383 err
= cb(cb_arg
, blob
, NULL
, id
, NULL
, label
, NULL
, mode
, 0, repo
);
385 got_object_close(obj
);
387 got_object_blob_close(blob
);
391 static const struct got_error
*
392 diff_added_tree(struct got_object_id
*id
, const char *label
,
393 struct got_repository
*repo
, got_diff_blob_cb cb
, void *cb_arg
,
396 const struct got_error
*err
= NULL
;
397 struct got_object
*treeobj
= NULL
;
398 struct got_tree_object
*tree
= NULL
;
400 err
= got_object_open(&treeobj
, repo
, id
);
404 if (treeobj
->type
!= GOT_OBJ_TYPE_TREE
) {
405 err
= got_error(GOT_ERR_OBJ_TYPE
);
409 err
= got_object_tree_open(&tree
, repo
, treeobj
);
413 err
= got_diff_tree(NULL
, tree
, NULL
, label
, repo
, cb
, cb_arg
,
417 got_object_tree_close(tree
);
419 got_object_close(treeobj
);
423 static const struct got_error
*
424 diff_modified_tree(struct got_object_id
*id1
, struct got_object_id
*id2
,
425 const char *label1
, const char *label2
, struct got_repository
*repo
,
426 got_diff_blob_cb cb
, void *cb_arg
, int diff_content
)
428 const struct got_error
*err
;
429 struct got_object
*treeobj1
= NULL
;
430 struct got_object
*treeobj2
= NULL
;
431 struct got_tree_object
*tree1
= NULL
;
432 struct got_tree_object
*tree2
= NULL
;
434 err
= got_object_open(&treeobj1
, repo
, id1
);
438 if (treeobj1
->type
!= GOT_OBJ_TYPE_TREE
) {
439 err
= got_error(GOT_ERR_OBJ_TYPE
);
443 err
= got_object_open(&treeobj2
, repo
, id2
);
447 if (treeobj2
->type
!= GOT_OBJ_TYPE_TREE
) {
448 err
= got_error(GOT_ERR_OBJ_TYPE
);
452 err
= got_object_tree_open(&tree1
, repo
, treeobj1
);
456 err
= got_object_tree_open(&tree2
, repo
, treeobj2
);
460 err
= got_diff_tree(tree1
, tree2
, label1
, label2
, repo
, cb
, cb_arg
,
465 got_object_tree_close(tree1
);
467 got_object_tree_close(tree2
);
469 got_object_close(treeobj1
);
471 got_object_close(treeobj2
);
475 static const struct got_error
*
476 diff_deleted_tree(struct got_object_id
*id
, const char *label
,
477 struct got_repository
*repo
, got_diff_blob_cb cb
, void *cb_arg
,
480 const struct got_error
*err
;
481 struct got_object
*treeobj
= NULL
;
482 struct got_tree_object
*tree
= NULL
;
484 err
= got_object_open(&treeobj
, repo
, id
);
488 if (treeobj
->type
!= GOT_OBJ_TYPE_TREE
) {
489 err
= got_error(GOT_ERR_OBJ_TYPE
);
493 err
= got_object_tree_open(&tree
, repo
, treeobj
);
497 err
= got_diff_tree(tree
, NULL
, label
, NULL
, repo
, cb
, cb_arg
,
501 got_object_tree_close(tree
);
503 got_object_close(treeobj
);
507 static const struct got_error
*
508 diff_kind_mismatch(struct got_object_id
*id1
, struct got_object_id
*id2
,
509 const char *label1
, const char *label2
, struct got_repository
*repo
,
510 got_diff_blob_cb cb
, void *cb_arg
)
516 static const struct got_error
*
517 diff_entry_old_new(struct got_tree_entry
*te1
,
518 struct got_tree_entry
*te2
, const char *label1
, const char *label2
,
519 struct got_repository
*repo
, got_diff_blob_cb cb
, void *cb_arg
,
522 const struct got_error
*err
= NULL
;
525 if (got_object_tree_entry_is_submodule(te1
))
529 if (S_ISDIR(te1
->mode
))
530 err
= diff_deleted_tree(&te1
->id
, label1
, repo
,
531 cb
, cb_arg
, diff_content
);
534 err
= diff_deleted_blob(&te1
->id
, label1
,
535 te1
->mode
, repo
, cb
, cb_arg
);
537 err
= cb(cb_arg
, NULL
, NULL
, &te1
->id
, NULL
,
538 label1
, NULL
, te1
->mode
, 0, repo
);
541 } else if (got_object_tree_entry_is_submodule(te2
))
544 id_match
= (got_object_id_cmp(&te1
->id
, &te2
->id
) == 0);
545 if (S_ISDIR(te1
->mode
) && S_ISDIR(te2
->mode
)) {
547 return diff_modified_tree(&te1
->id
, &te2
->id
,
548 label1
, label2
, repo
, cb
, cb_arg
, diff_content
);
549 } else if ((S_ISREG(te1
->mode
) || S_ISLNK(te1
->mode
)) &&
550 (S_ISREG(te2
->mode
) || S_ISLNK(te2
->mode
))) {
552 ((te1
->mode
& (S_IFLNK
| S_IXUSR
))) !=
553 (te2
->mode
& (S_IFLNK
| S_IXUSR
))) {
555 return diff_modified_blob(&te1
->id
, &te2
->id
,
556 label1
, label2
, te1
->mode
, te2
->mode
,
559 return cb(cb_arg
, NULL
, NULL
, &te1
->id
,
560 &te2
->id
, label1
, label2
, te1
->mode
,
568 return diff_kind_mismatch(&te1
->id
, &te2
->id
, label1
, label2
, repo
,
572 static const struct got_error
*
573 diff_entry_new_old(struct got_tree_entry
*te2
,
574 struct got_tree_entry
*te1
, const char *label2
,
575 struct got_repository
*repo
, got_diff_blob_cb cb
, void *cb_arg
,
578 if (te1
!= NULL
) /* handled by diff_entry_old_new() */
581 if (got_object_tree_entry_is_submodule(te2
))
584 if (S_ISDIR(te2
->mode
))
585 return diff_added_tree(&te2
->id
, label2
, repo
, cb
, cb_arg
,
589 return diff_added_blob(&te2
->id
, label2
, te2
->mode
, repo
, cb
,
592 return cb(cb_arg
, NULL
, NULL
, NULL
, &te2
->id
, NULL
, label2
, 0,
596 const struct got_error
*
597 got_diff_tree_collect_changed_paths(void *arg
, struct got_blob_object
*blob1
,
598 struct got_blob_object
*blob2
, struct got_object_id
*id1
,
599 struct got_object_id
*id2
, const char *label1
, const char *label2
,
600 mode_t mode1
, mode_t mode2
, struct got_repository
*repo
)
602 const struct got_error
*err
= NULL
;
603 struct got_pathlist_head
*paths
= arg
;
604 struct got_diff_changed_path
*change
= NULL
;
607 path
= strdup(label2
? label2
: label1
);
609 return got_error_from_errno("malloc");
611 change
= malloc(sizeof(*change
));
612 if (change
== NULL
) {
613 err
= got_error_from_errno("malloc");
617 change
->status
= GOT_STATUS_NO_CHANGE
;
619 change
->status
= GOT_STATUS_ADD
;
620 else if (id2
== NULL
)
621 change
->status
= GOT_STATUS_DELETE
;
623 if (got_object_id_cmp(id1
, id2
) != 0)
624 change
->status
= GOT_STATUS_MODIFY
;
625 else if (mode1
!= mode2
)
626 change
->status
= GOT_STATUS_MODE_CHANGE
;
629 err
= got_pathlist_insert(NULL
, paths
, path
, change
);
638 const struct got_error
*
639 got_diff_tree(struct got_tree_object
*tree1
, struct got_tree_object
*tree2
,
640 const char *label1
, const char *label2
, struct got_repository
*repo
,
641 got_diff_blob_cb cb
, void *cb_arg
, int diff_content
)
643 const struct got_error
*err
= NULL
;
644 struct got_tree_entry
*te1
= NULL
;
645 struct got_tree_entry
*te2
= NULL
;
646 char *l1
= NULL
, *l2
= NULL
;
647 int tidx1
= 0, tidx2
= 0;
650 te1
= got_object_tree_get_entry(tree1
, 0);
651 if (te1
&& asprintf(&l1
, "%s%s%s", label1
, label1
[0] ? "/" : "",
653 return got_error_from_errno("asprintf");
656 te2
= got_object_tree_get_entry(tree2
, 0);
657 if (te2
&& asprintf(&l2
, "%s%s%s", label2
, label2
[0] ? "/" : "",
659 return got_error_from_errno("asprintf");
664 struct got_tree_entry
*te
= NULL
;
666 te
= got_object_tree_find_entry(tree2
,
671 if (te
&& asprintf(&l2
, "%s%s%s", label2
,
672 label2
[0] ? "/" : "", te
->name
) == -1)
674 got_error_from_errno("asprintf");
676 err
= diff_entry_old_new(te1
, te
, l1
, l2
, repo
, cb
,
677 cb_arg
, diff_content
);
683 struct got_tree_entry
*te
= NULL
;
685 te
= got_object_tree_find_entry(tree1
,
689 if (asprintf(&l2
, "%s%s%s", label2
,
690 label2
[0] ? "/" : "", te
->name
) == -1)
692 got_error_from_errno("asprintf");
694 if (asprintf(&l2
, "%s%s%s", label2
,
695 label2
[0] ? "/" : "", te2
->name
) == -1)
697 got_error_from_errno("asprintf");
699 err
= diff_entry_new_old(te2
, te
, l2
, repo
,
700 cb
, cb_arg
, diff_content
);
709 te1
= got_object_tree_get_entry(tree1
, tidx1
);
711 asprintf(&l1
, "%s%s%s", label1
,
712 label1
[0] ? "/" : "", te1
->name
) == -1)
713 return got_error_from_errno("asprintf");
719 te2
= got_object_tree_get_entry(tree2
, tidx2
);
721 asprintf(&l2
, "%s%s%s", label2
,
722 label2
[0] ? "/" : "", te2
->name
) == -1)
723 return got_error_from_errno("asprintf");
725 } while (te1
|| te2
);
730 const struct got_error
*
731 got_diff_objects_as_blobs(off_t
**line_offsets
, size_t *nlines
,
732 struct got_object_id
*id1
, struct got_object_id
*id2
,
733 const char *label1
, const char *label2
, int diff_context
,
734 int ignore_whitespace
, int force_text_diff
,
735 struct got_repository
*repo
, FILE *outfile
)
737 const struct got_error
*err
;
738 struct got_blob_object
*blob1
= NULL
, *blob2
= NULL
;
740 if (id1
== NULL
&& id2
== NULL
)
741 return got_error(GOT_ERR_NO_OBJ
);
744 err
= got_object_open_as_blob(&blob1
, repo
, id1
, 8192);
749 err
= got_object_open_as_blob(&blob2
, repo
, id2
, 8192);
753 err
= got_diff_blob(line_offsets
, nlines
, blob1
, blob2
,
754 label1
, label2
, diff_context
, ignore_whitespace
, force_text_diff
,
758 got_object_blob_close(blob1
);
760 got_object_blob_close(blob2
);
764 const struct got_error
*
765 got_diff_objects_as_trees(off_t
**line_offsets
, size_t *nlines
,
766 struct got_object_id
*id1
, struct got_object_id
*id2
,
767 char *label1
, char *label2
, int diff_context
, int ignore_whitespace
,
768 int force_text_diff
, struct got_repository
*repo
, FILE *outfile
)
770 const struct got_error
*err
;
771 struct got_tree_object
*tree1
= NULL
, *tree2
= NULL
;
772 struct got_diff_blob_output_unidiff_arg arg
;
773 int want_lineoffsets
= (line_offsets
!= NULL
&& *line_offsets
!= NULL
);
775 if (id1
== NULL
&& id2
== NULL
)
776 return got_error(GOT_ERR_NO_OBJ
);
779 err
= got_object_open_as_tree(&tree1
, repo
, id1
);
784 err
= got_object_open_as_tree(&tree2
, repo
, id2
);
788 arg
.diff_context
= diff_context
;
789 arg
.ignore_whitespace
= ignore_whitespace
;
790 arg
.force_text_diff
= force_text_diff
;
791 arg
.outfile
= outfile
;
792 if (want_lineoffsets
) {
793 arg
.line_offsets
= *line_offsets
;
794 arg
.nlines
= *nlines
;
796 arg
.line_offsets
= NULL
;
799 err
= got_diff_tree(tree1
, tree2
, label1
, label2
, repo
,
800 got_diff_blob_output_unidiff
, &arg
, 1);
802 if (want_lineoffsets
) {
803 *line_offsets
= arg
.line_offsets
; /* was likely re-allocated */
804 *nlines
= arg
.nlines
;
808 got_object_tree_close(tree1
);
810 got_object_tree_close(tree2
);
814 const struct got_error
*
815 got_diff_objects_as_commits(off_t
**line_offsets
, size_t *nlines
,
816 struct got_object_id
*id1
, struct got_object_id
*id2
,
817 int diff_context
, int ignore_whitespace
, int force_text_diff
,
818 struct got_repository
*repo
, FILE *outfile
)
820 const struct got_error
*err
;
821 struct got_commit_object
*commit1
= NULL
, *commit2
= NULL
;
824 return got_error(GOT_ERR_NO_OBJ
);
827 err
= got_object_open_as_commit(&commit1
, repo
, id1
);
832 err
= got_object_open_as_commit(&commit2
, repo
, id2
);
836 err
= got_diff_objects_as_trees(line_offsets
, nlines
,
837 commit1
? got_object_commit_get_tree_id(commit1
) : NULL
,
838 got_object_commit_get_tree_id(commit2
), "", "", diff_context
,
839 ignore_whitespace
, force_text_diff
, repo
, outfile
);
842 got_object_commit_close(commit1
);
844 got_object_commit_close(commit2
);
848 const struct got_error
*
849 got_diff_files(struct got_diffreg_result
**resultp
,
850 FILE *f1
, const char *label1
, FILE *f2
, const char *label2
,
851 int diff_context
, int ignore_whitespace
, int force_text_diff
,
854 const struct got_error
*err
= NULL
;
855 struct got_diffreg_result
*diffreg_result
= NULL
;
861 fprintf(outfile
, "file - %s\n",
862 f1
== NULL
? "/dev/null" : label1
);
863 fprintf(outfile
, "file + %s\n",
864 f2
== NULL
? "/dev/null" : label2
);
867 err
= got_diffreg(&diffreg_result
, f1
, f2
, GOT_DIFF_ALGORITHM_PATIENCE
,
868 ignore_whitespace
, force_text_diff
);
873 err
= got_diffreg_output(NULL
, NULL
, diffreg_result
,
874 f1
!= NULL
, f2
!= NULL
, label1
, label2
,
875 GOT_DIFF_OUTPUT_UNIDIFF
, diff_context
, outfile
);
881 if (resultp
&& err
== NULL
)
882 *resultp
= diffreg_result
;
883 else if (diffreg_result
) {
884 const struct got_error
*free_err
;
885 free_err
= got_diffreg_result_free(diffreg_result
);
886 if (free_err
&& err
== NULL
)