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"
32 #include "got_cancel.h"
33 #include "got_worktree.h"
34 #include "got_opentemp.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
, FILE *f1
, FILE *f2
,
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
,
61 enum got_diff_algorithm diff_algo
)
63 const struct got_error
*err
= NULL
, *free_err
;
64 char hex1
[SHA1_DIGEST_STRING_LENGTH
];
65 char hex2
[SHA1_DIGEST_STRING_LENGTH
];
66 const 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);
84 err
= got_opentemp_truncate(f1
);
89 err
= got_opentemp_truncate(f2
);
96 idstr1
= got_object_blob_id_str(blob1
, hex1
, sizeof(hex1
));
97 err
= got_object_blob_dump_to_file(&size1
, NULL
, NULL
, f1
,
102 idstr1
= "/dev/null";
106 idstr2
= got_object_blob_id_str(blob2
, hex2
, sizeof(hex2
));
107 err
= got_object_blob_dump_to_file(&size2
, NULL
, NULL
, f2
,
112 idstr2
= "/dev/null";
115 char *modestr1
= NULL
, *modestr2
= NULL
;
117 if (mode1
&& mode1
!= mode2
) {
121 modebits
= (S_IRWXU
| S_IRWXG
| S_IRWXO
);
122 if (asprintf(&modestr1
, " (mode %o)",
123 mode1
& modebits
) == -1) {
124 err
= got_error_from_errno("asprintf");
128 if (mode2
&& mode1
!= mode2
) {
132 modebits
= (S_IRWXU
| S_IRWXG
| S_IRWXO
);
133 if (asprintf(&modestr2
, " (mode %o)",
134 mode2
& modebits
) == -1) {
135 err
= got_error_from_errno("asprintf");
139 n
= fprintf(outfile
, "blob - %s%s\n", idstr1
,
140 modestr1
? modestr1
: "");
145 err
= add_line_offset(line_offsets
, nlines
, outoff
);
150 n
= fprintf(outfile
, "blob + %s%s\n", idstr2
,
151 modestr2
? modestr2
: "");
156 err
= add_line_offset(line_offsets
, nlines
, outoff
);
164 err
= got_diffreg(&result
, f1
, f2
, diff_algo
, ignore_whitespace
,
170 err
= got_diffreg_output(line_offsets
, nlines
, result
,
171 blob1
!= NULL
, blob2
!= NULL
,
172 label1
? label1
: idstr1
,
173 label2
? label2
: idstr2
,
174 GOT_DIFF_OUTPUT_UNIDIFF
, diff_context
, outfile
);
179 if (resultp
&& err
== NULL
)
182 free_err
= got_diffreg_result_free(result
);
183 if (free_err
&& err
== NULL
)
190 const struct got_error
*
191 got_diff_blob_output_unidiff(void *arg
, struct got_blob_object
*blob1
,
192 struct got_blob_object
*blob2
, FILE *f1
, FILE *f2
,
193 struct got_object_id
*id1
, struct got_object_id
*id2
,
194 const char *label1
, const char *label2
, mode_t mode1
, mode_t mode2
,
195 struct got_repository
*repo
)
197 struct got_diff_blob_output_unidiff_arg
*a
= arg
;
199 return diff_blobs(&a
->line_offsets
, &a
->nlines
, NULL
,
200 blob1
, blob2
, f1
, f2
, label1
, label2
, mode1
, mode2
, a
->diff_context
,
201 a
->ignore_whitespace
, a
->force_text_diff
, a
->outfile
, a
->diff_algo
);
204 const struct got_error
*
205 got_diff_blob(off_t
**line_offsets
, size_t *nlines
,
206 struct got_blob_object
*blob1
, struct got_blob_object
*blob2
,
207 FILE *f1
, FILE *f2
, const char *label1
, const char *label2
,
208 enum got_diff_algorithm diff_algo
, int diff_context
,
209 int ignore_whitespace
, int force_text_diff
, FILE *outfile
)
211 return diff_blobs(line_offsets
, nlines
, NULL
, blob1
, blob2
, f1
, f2
,
212 label1
, label2
, 0, 0, diff_context
, ignore_whitespace
,
213 force_text_diff
, outfile
, diff_algo
);
216 static const struct got_error
*
217 diff_blob_file(struct got_diffreg_result
**resultp
,
218 struct got_blob_object
*blob1
, FILE *f1
, off_t size1
, const char *label1
,
219 FILE *f2
, int f2_exists
, size_t size2
, const char *label2
,
220 enum got_diff_algorithm diff_algo
, int diff_context
, int ignore_whitespace
,
221 int force_text_diff
, FILE *outfile
)
223 const struct got_error
*err
= NULL
, *free_err
;
224 char hex1
[SHA1_DIGEST_STRING_LENGTH
];
225 const char *idstr1
= NULL
;
226 struct got_diffreg_result
*result
= NULL
;
232 idstr1
= got_object_blob_id_str(blob1
, hex1
, sizeof(hex1
));
234 idstr1
= "/dev/null";
237 fprintf(outfile
, "blob - %s\n", label1
? label1
: idstr1
);
238 fprintf(outfile
, "file + %s\n",
239 f2_exists
? label2
: "/dev/null");
242 err
= got_diffreg(&result
, f1
, f2
, diff_algo
, ignore_whitespace
,
248 err
= got_diffreg_output(NULL
, NULL
, result
,
249 blob1
!= NULL
, f2_exists
,
250 label2
, /* show local file's path, not a blob ID */
251 label2
, GOT_DIFF_OUTPUT_UNIDIFF
,
252 diff_context
, outfile
);
257 if (resultp
&& err
== NULL
)
260 free_err
= got_diffreg_result_free(result
);
261 if (free_err
&& err
== NULL
)
268 const struct got_error
*
269 got_diff_blob_file(struct got_blob_object
*blob1
, FILE *f1
, off_t size1
,
270 const char *label1
, FILE *f2
, int f2_exists
, size_t size2
,
271 const char *label2
, enum got_diff_algorithm diff_algo
, int diff_context
,
272 int ignore_whitespace
, int force_text_diff
, FILE *outfile
)
274 return diff_blob_file(NULL
, blob1
, f1
, size1
, label1
, f2
, f2_exists
,
275 size2
, label2
, diff_algo
, diff_context
, ignore_whitespace
,
276 force_text_diff
, outfile
);
279 static const struct got_error
*
280 diff_added_blob(struct got_object_id
*id
, FILE *f1
, FILE *f2
, int fd2
,
281 const char *label
, mode_t mode
, struct got_repository
*repo
,
282 got_diff_blob_cb cb
, void *cb_arg
)
284 const struct got_error
*err
;
285 struct got_blob_object
*blob
= NULL
;
286 struct got_object
*obj
= NULL
;
288 err
= got_object_open(&obj
, repo
, id
);
292 err
= got_object_blob_open(&blob
, repo
, obj
, 8192, fd2
);
295 err
= cb(cb_arg
, NULL
, blob
, f1
, f2
, NULL
, id
,
296 NULL
, label
, 0, mode
, repo
);
298 got_object_close(obj
);
300 got_object_blob_close(blob
);
304 static const struct got_error
*
305 diff_modified_blob(struct got_object_id
*id1
, struct got_object_id
*id2
,
306 FILE *f1
, FILE *f2
, int fd1
, int fd2
,
307 const char *label1
, const char *label2
,
308 mode_t mode1
, mode_t mode2
, struct got_repository
*repo
,
309 got_diff_blob_cb cb
, void *cb_arg
)
311 const struct got_error
*err
;
312 struct got_object
*obj1
= NULL
;
313 struct got_object
*obj2
= NULL
;
314 struct got_blob_object
*blob1
= NULL
;
315 struct got_blob_object
*blob2
= NULL
;
317 err
= got_object_open(&obj1
, repo
, id1
);
321 if (obj1
->type
!= GOT_OBJ_TYPE_BLOB
) {
322 err
= got_error(GOT_ERR_OBJ_TYPE
);
326 err
= got_object_open(&obj2
, repo
, id2
);
329 if (obj2
->type
!= GOT_OBJ_TYPE_BLOB
) {
330 err
= got_error(GOT_ERR_BAD_OBJ_DATA
);
334 err
= got_object_blob_open(&blob1
, repo
, obj1
, 8192, fd1
);
338 err
= got_object_blob_open(&blob2
, repo
, obj2
, 8192, fd2
);
342 err
= cb(cb_arg
, blob1
, blob2
, f1
, f2
, id1
, id2
, label1
, label2
,
346 got_object_close(obj1
);
348 got_object_close(obj2
);
350 got_object_blob_close(blob1
);
352 got_object_blob_close(blob2
);
356 static const struct got_error
*
357 diff_deleted_blob(struct got_object_id
*id
, FILE *f1
, int fd1
,
358 FILE *f2
, const char *label
, mode_t mode
, struct got_repository
*repo
,
359 got_diff_blob_cb cb
, void *cb_arg
)
361 const struct got_error
*err
;
362 struct got_blob_object
*blob
= NULL
;
363 struct got_object
*obj
= NULL
;
365 err
= got_object_open(&obj
, repo
, id
);
369 err
= got_object_blob_open(&blob
, repo
, obj
, 8192, fd1
);
372 err
= cb(cb_arg
, blob
, NULL
, f1
, f2
, id
, NULL
, label
, NULL
,
375 got_object_close(obj
);
377 got_object_blob_close(blob
);
381 static const struct got_error
*
382 diff_added_tree(struct got_object_id
*id
, FILE *f1
, FILE *f2
, int fd2
,
383 const char *label
, struct got_repository
*repo
, got_diff_blob_cb cb
,
384 void *cb_arg
, int diff_content
)
386 const struct got_error
*err
= NULL
;
387 struct got_object
*treeobj
= NULL
;
388 struct got_tree_object
*tree
= NULL
;
390 err
= got_object_open(&treeobj
, repo
, id
);
394 if (treeobj
->type
!= GOT_OBJ_TYPE_TREE
) {
395 err
= got_error(GOT_ERR_OBJ_TYPE
);
399 err
= got_object_tree_open(&tree
, repo
, treeobj
);
403 err
= got_diff_tree(NULL
, tree
, f1
, f2
, -1, fd2
, NULL
, label
,
404 repo
, cb
, cb_arg
, diff_content
);
407 got_object_tree_close(tree
);
409 got_object_close(treeobj
);
413 static const struct got_error
*
414 diff_modified_tree(struct got_object_id
*id1
, struct got_object_id
*id2
,
415 FILE *f1
, FILE *f2
, int fd1
, int fd2
,
416 const char *label1
, const char *label2
,
417 struct got_repository
*repo
, got_diff_blob_cb cb
, void *cb_arg
,
420 const struct got_error
*err
;
421 struct got_object
*treeobj1
= NULL
;
422 struct got_object
*treeobj2
= NULL
;
423 struct got_tree_object
*tree1
= NULL
;
424 struct got_tree_object
*tree2
= NULL
;
426 err
= got_object_open(&treeobj1
, repo
, id1
);
430 if (treeobj1
->type
!= GOT_OBJ_TYPE_TREE
) {
431 err
= got_error(GOT_ERR_OBJ_TYPE
);
435 err
= got_object_open(&treeobj2
, repo
, id2
);
439 if (treeobj2
->type
!= GOT_OBJ_TYPE_TREE
) {
440 err
= got_error(GOT_ERR_OBJ_TYPE
);
444 err
= got_object_tree_open(&tree1
, repo
, treeobj1
);
448 err
= got_object_tree_open(&tree2
, repo
, treeobj2
);
452 err
= got_diff_tree(tree1
, tree2
, f1
, f2
, fd1
, fd2
,
453 label1
, label2
, repo
, cb
, cb_arg
, diff_content
);
457 got_object_tree_close(tree1
);
459 got_object_tree_close(tree2
);
461 got_object_close(treeobj1
);
463 got_object_close(treeobj2
);
467 static const struct got_error
*
468 diff_deleted_tree(struct got_object_id
*id
, FILE *f1
, int fd1
,
469 FILE *f2
, const char *label
, struct got_repository
*repo
,
470 got_diff_blob_cb cb
, void *cb_arg
, int diff_content
)
472 const struct got_error
*err
;
473 struct got_object
*treeobj
= NULL
;
474 struct got_tree_object
*tree
= NULL
;
476 err
= got_object_open(&treeobj
, repo
, id
);
480 if (treeobj
->type
!= GOT_OBJ_TYPE_TREE
) {
481 err
= got_error(GOT_ERR_OBJ_TYPE
);
485 err
= got_object_tree_open(&tree
, repo
, treeobj
);
489 err
= got_diff_tree(tree
, NULL
, f1
, f2
, fd1
, -1, label
, NULL
,
490 repo
, cb
, cb_arg
, diff_content
);
493 got_object_tree_close(tree
);
495 got_object_close(treeobj
);
499 static const struct got_error
*
500 diff_kind_mismatch(struct got_object_id
*id1
, struct got_object_id
*id2
,
501 const char *label1
, const char *label2
, struct got_repository
*repo
,
502 got_diff_blob_cb cb
, void *cb_arg
)
508 static const struct got_error
*
509 diff_entry_old_new(struct got_tree_entry
*te1
, struct got_tree_entry
*te2
,
510 FILE *f1
, FILE *f2
, int fd1
, int fd2
,
511 const char *label1
, const char *label2
,
512 struct got_repository
*repo
, got_diff_blob_cb cb
, void *cb_arg
,
515 const struct got_error
*err
= NULL
;
518 if (got_object_tree_entry_is_submodule(te1
))
522 if (S_ISDIR(te1
->mode
))
523 err
= diff_deleted_tree(&te1
->id
, f1
, fd1
, f2
,
524 label1
, repo
, cb
, cb_arg
, diff_content
);
527 err
= diff_deleted_blob(&te1
->id
, f1
, fd1
,
528 f2
, label1
, te1
->mode
, repo
, cb
, cb_arg
);
530 err
= cb(cb_arg
, NULL
, NULL
, NULL
, NULL
,
531 &te1
->id
, NULL
, label1
, NULL
,
535 } else if (got_object_tree_entry_is_submodule(te2
))
538 id_match
= (got_object_id_cmp(&te1
->id
, &te2
->id
) == 0);
539 if (S_ISDIR(te1
->mode
) && S_ISDIR(te2
->mode
)) {
541 return diff_modified_tree(&te1
->id
, &te2
->id
, f1
, f2
,
542 fd1
, fd2
, label1
, label2
, repo
, cb
, cb_arg
,
544 } else if ((S_ISREG(te1
->mode
) || S_ISLNK(te1
->mode
)) &&
545 (S_ISREG(te2
->mode
) || S_ISLNK(te2
->mode
))) {
547 ((te1
->mode
& (S_IFLNK
| S_IXUSR
))) !=
548 (te2
->mode
& (S_IFLNK
| S_IXUSR
))) {
550 return diff_modified_blob(&te1
->id
, &te2
->id
,
551 f1
, f2
, fd1
, fd2
, label1
, label2
,
552 te1
->mode
, te2
->mode
, repo
, cb
, cb_arg
);
554 return cb(cb_arg
, NULL
, NULL
, NULL
, NULL
,
555 &te1
->id
, &te2
->id
, label1
, label2
,
556 te1
->mode
, te2
->mode
, repo
);
563 return diff_kind_mismatch(&te1
->id
, &te2
->id
, label1
, label2
, repo
,
567 static const struct got_error
*
568 diff_entry_new_old(struct got_tree_entry
*te2
,
569 struct got_tree_entry
*te1
, FILE *f1
, FILE *f2
, int fd2
, const char *label2
,
570 struct got_repository
*repo
, got_diff_blob_cb cb
, void *cb_arg
,
573 if (te1
!= NULL
) /* handled by diff_entry_old_new() */
576 if (got_object_tree_entry_is_submodule(te2
))
579 if (S_ISDIR(te2
->mode
))
580 return diff_added_tree(&te2
->id
, f1
, f2
, fd2
, label2
,
581 repo
, cb
, cb_arg
, diff_content
);
584 return diff_added_blob(&te2
->id
, f1
, f2
, fd2
,
585 label2
, te2
->mode
, repo
, cb
, cb_arg
);
587 return cb(cb_arg
, NULL
, NULL
, NULL
, NULL
, NULL
, &te2
->id
,
588 NULL
, label2
, 0, te2
->mode
, repo
);
591 const struct got_error
*
592 got_diff_tree_collect_changed_paths(void *arg
, struct got_blob_object
*blob1
,
593 struct got_blob_object
*blob2
, FILE *f1
, FILE *f2
,
594 struct got_object_id
*id1
, struct got_object_id
*id2
,
595 const char *label1
, const char *label2
,
596 mode_t mode1
, mode_t mode2
, struct got_repository
*repo
)
598 const struct got_error
*err
= NULL
;
599 struct got_pathlist_head
*paths
= arg
;
600 struct got_diff_changed_path
*change
= NULL
;
603 path
= strdup(label2
? label2
: label1
);
605 return got_error_from_errno("malloc");
607 change
= malloc(sizeof(*change
));
608 if (change
== NULL
) {
609 err
= got_error_from_errno("malloc");
613 change
->status
= GOT_STATUS_NO_CHANGE
;
615 change
->status
= GOT_STATUS_ADD
;
616 else if (id2
== NULL
)
617 change
->status
= GOT_STATUS_DELETE
;
619 if (got_object_id_cmp(id1
, id2
) != 0)
620 change
->status
= GOT_STATUS_MODIFY
;
621 else if (mode1
!= mode2
)
622 change
->status
= GOT_STATUS_MODE_CHANGE
;
625 err
= got_pathlist_append(paths
, path
, change
);
634 const struct got_error
*
635 got_diff_tree(struct got_tree_object
*tree1
, struct got_tree_object
*tree2
,
636 FILE *f1
, FILE *f2
, int fd1
, int fd2
,
637 const char *label1
, const char *label2
,
638 struct got_repository
*repo
, got_diff_blob_cb cb
, void *cb_arg
,
641 const struct got_error
*err
= NULL
;
642 struct got_tree_entry
*te1
= NULL
;
643 struct got_tree_entry
*te2
= NULL
;
644 char *l1
= NULL
, *l2
= NULL
;
645 int tidx1
= 0, tidx2
= 0;
648 te1
= got_object_tree_get_entry(tree1
, 0);
649 if (te1
&& asprintf(&l1
, "%s%s%s", label1
, label1
[0] ? "/" : "",
651 return got_error_from_errno("asprintf");
654 te2
= got_object_tree_get_entry(tree2
, 0);
655 if (te2
&& asprintf(&l2
, "%s%s%s", label2
, label2
[0] ? "/" : "",
657 return got_error_from_errno("asprintf");
662 struct got_tree_entry
*te
= NULL
;
664 te
= got_object_tree_find_entry(tree2
,
669 if (te
&& asprintf(&l2
, "%s%s%s", label2
,
670 label2
[0] ? "/" : "", te
->name
) == -1)
672 got_error_from_errno("asprintf");
674 err
= diff_entry_old_new(te1
, te
, f1
, f2
, fd1
, fd2
,
675 l1
, l2
, repo
, cb
, cb_arg
, diff_content
);
681 struct got_tree_entry
*te
= NULL
;
683 te
= got_object_tree_find_entry(tree1
,
687 if (asprintf(&l2
, "%s%s%s", label2
,
688 label2
[0] ? "/" : "", te
->name
) == -1)
690 got_error_from_errno("asprintf");
692 if (asprintf(&l2
, "%s%s%s", label2
,
693 label2
[0] ? "/" : "", te2
->name
) == -1)
695 got_error_from_errno("asprintf");
697 err
= diff_entry_new_old(te2
, te
, f1
, f2
, fd2
, l2
,
698 repo
, cb
, cb_arg
, diff_content
);
707 te1
= got_object_tree_get_entry(tree1
, tidx1
);
709 asprintf(&l1
, "%s%s%s", label1
,
710 label1
[0] ? "/" : "", te1
->name
) == -1)
711 return got_error_from_errno("asprintf");
717 te2
= got_object_tree_get_entry(tree2
, tidx2
);
719 asprintf(&l2
, "%s%s%s", label2
,
720 label2
[0] ? "/" : "", te2
->name
) == -1)
721 return got_error_from_errno("asprintf");
723 } while (te1
|| te2
);
728 const struct got_error
*
729 got_diff_objects_as_blobs(off_t
**line_offsets
, size_t *nlines
,
730 FILE *f1
, FILE *f2
, int fd1
, int fd2
,
731 struct got_object_id
*id1
, struct got_object_id
*id2
,
732 const char *label1
, const char *label2
,
733 enum got_diff_algorithm diff_algo
, 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, fd1
);
749 err
= got_object_open_as_blob(&blob2
, repo
, id2
, 8192, fd2
);
753 err
= got_diff_blob(line_offsets
, nlines
, blob1
, blob2
, f1
, f2
,
754 label1
, label2
, diff_algo
, diff_context
, ignore_whitespace
,
755 force_text_diff
, outfile
);
758 got_object_blob_close(blob1
);
760 got_object_blob_close(blob2
);
764 static const struct got_error
*
765 diff_paths(struct got_tree_object
*tree1
, struct got_tree_object
*tree2
,
766 FILE *f1
, FILE *f2
, int fd1
, int fd2
, struct got_pathlist_head
*paths
,
767 struct got_repository
*repo
, got_diff_blob_cb cb
, void *cb_arg
)
769 const struct got_error
*err
= NULL
;
770 struct got_pathlist_entry
*pe
;
771 struct got_object_id
*id1
= NULL
, *id2
= NULL
;
772 struct got_tree_object
*subtree1
= NULL
, *subtree2
= NULL
;
773 struct got_blob_object
*blob1
= NULL
, *blob2
= NULL
;
775 TAILQ_FOREACH(pe
, paths
, entry
) {
776 int type1
= GOT_OBJ_TYPE_ANY
, type2
= GOT_OBJ_TYPE_ANY
;
777 mode_t mode1
= 0, mode2
= 0;
784 got_object_tree_close(subtree1
);
788 got_object_tree_close(subtree2
);
792 got_object_blob_close(blob1
);
796 got_object_blob_close(blob2
);
800 err
= got_object_tree_find_path(&id1
, &mode1
, repo
, tree1
,
802 if (err
&& err
->code
!= GOT_ERR_NO_TREE_ENTRY
)
804 err
= got_object_tree_find_path(&id2
, &mode2
, repo
, tree2
,
806 if (err
&& err
->code
!= GOT_ERR_NO_TREE_ENTRY
)
808 if (id1
== NULL
&& id2
== NULL
) {
809 err
= got_error_path(pe
->path
, GOT_ERR_NO_TREE_ENTRY
);
813 err
= got_object_get_type(&type1
, repo
, id1
);
818 err
= got_object_get_type(&type2
, repo
, id2
);
822 if (type1
== GOT_OBJ_TYPE_ANY
&&
823 type2
== GOT_OBJ_TYPE_ANY
) {
824 err
= got_error_path(pe
->path
, GOT_ERR_NO_OBJ
);
826 } else if (type1
!= GOT_OBJ_TYPE_ANY
&&
827 type2
!= GOT_OBJ_TYPE_ANY
&& type1
!= type2
) {
828 err
= got_error(GOT_ERR_OBJ_TYPE
);
832 if (type1
== GOT_OBJ_TYPE_BLOB
||
833 type2
== GOT_OBJ_TYPE_BLOB
) {
835 err
= got_object_open_as_blob(&blob1
, repo
,
841 err
= got_object_open_as_blob(&blob2
, repo
,
846 err
= cb(cb_arg
, blob1
, blob2
, f1
, f2
, id1
, id2
,
847 id1
? pe
->path
: "/dev/null",
848 id2
? pe
->path
: "/dev/null",
852 } else if (type1
== GOT_OBJ_TYPE_TREE
||
853 type2
== GOT_OBJ_TYPE_TREE
) {
855 err
= got_object_open_as_tree(&subtree1
, repo
,
861 err
= got_object_open_as_tree(&subtree2
, repo
,
866 err
= got_diff_tree(subtree1
, subtree2
, f1
, f2
,
868 id1
? pe
->path
: "/dev/null",
869 id2
? pe
->path
: "/dev/null",
870 repo
, cb
, cb_arg
, 1);
874 err
= got_error(GOT_ERR_OBJ_TYPE
);
882 got_object_tree_close(subtree1
);
884 got_object_tree_close(subtree2
);
886 got_object_blob_close(blob1
);
888 got_object_blob_close(blob2
);
892 static const struct got_error
*
893 show_object_id(off_t
**line_offsets
, size_t *nlines
, const char *obj_typestr
,
894 int ch
, const char *id_str
, FILE *outfile
)
896 const struct got_error
*err
;
900 n
= fprintf(outfile
, "%s %c %s\n", obj_typestr
, ch
, id_str
);
901 if (line_offsets
!= NULL
&& *line_offsets
!= NULL
) {
903 err
= add_line_offset(line_offsets
, nlines
, 0);
907 outoff
= (*line_offsets
)[*nlines
- 1];
910 err
= add_line_offset(line_offsets
, nlines
, outoff
);
918 static const struct got_error
*
919 diff_objects_as_trees(off_t
**line_offsets
, size_t *nlines
,
920 FILE *f1
, FILE *f2
, int fd1
, int fd2
,
921 struct got_object_id
*id1
, struct got_object_id
*id2
,
922 struct got_pathlist_head
*paths
, const char *label1
, const char *label2
,
923 int diff_context
, int ignore_whitespace
, int force_text_diff
,
924 struct got_repository
*repo
, FILE *outfile
,
925 enum got_diff_algorithm diff_algo
)
927 const struct got_error
*err
;
928 struct got_tree_object
*tree1
= NULL
, *tree2
= NULL
;
929 struct got_diff_blob_output_unidiff_arg arg
;
930 int want_lineoffsets
= (line_offsets
!= NULL
&& *line_offsets
!= NULL
);
932 if (id1
== NULL
&& id2
== NULL
)
933 return got_error(GOT_ERR_NO_OBJ
);
936 err
= got_object_open_as_tree(&tree1
, repo
, id1
);
941 err
= got_object_open_as_tree(&tree2
, repo
, id2
);
946 arg
.diff_algo
= diff_algo
;
947 arg
.diff_context
= diff_context
;
948 arg
.ignore_whitespace
= ignore_whitespace
;
949 arg
.force_text_diff
= force_text_diff
;
950 arg
.outfile
= outfile
;
951 if (want_lineoffsets
) {
952 arg
.line_offsets
= *line_offsets
;
953 arg
.nlines
= *nlines
;
955 arg
.line_offsets
= NULL
;
958 if (paths
== NULL
|| TAILQ_EMPTY(paths
)) {
959 err
= got_diff_tree(tree1
, tree2
, f1
, f2
, fd1
, fd2
,
960 label1
, label2
, repo
,
961 got_diff_blob_output_unidiff
, &arg
, 1);
963 err
= diff_paths(tree1
, tree2
, f1
, f2
, fd1
, fd2
, paths
, repo
,
964 got_diff_blob_output_unidiff
, &arg
);
966 if (want_lineoffsets
) {
967 *line_offsets
= arg
.line_offsets
; /* was likely re-allocated */
968 *nlines
= arg
.nlines
;
972 got_object_tree_close(tree1
);
974 got_object_tree_close(tree2
);
978 const struct got_error
*
979 got_diff_objects_as_trees(off_t
**line_offsets
, size_t *nlines
,
980 FILE *f1
, FILE *f2
, int fd1
, int fd2
,
981 struct got_object_id
*id1
, struct got_object_id
*id2
,
982 struct got_pathlist_head
*paths
, const char *label1
, const char *label2
,
983 enum got_diff_algorithm diff_algo
, int diff_context
, int ignore_whitespace
,
984 int force_text_diff
, struct got_repository
*repo
, FILE *outfile
)
986 const struct got_error
*err
;
989 if (id1
== NULL
&& id2
== NULL
)
990 return got_error(GOT_ERR_NO_OBJ
);
993 err
= got_object_id_str(&idstr
, id1
);
996 err
= show_object_id(line_offsets
, nlines
, "tree", '-',
1003 err
= show_object_id(line_offsets
, nlines
, "tree", '-',
1004 "/dev/null", outfile
);
1010 err
= got_object_id_str(&idstr
, id2
);
1013 err
= show_object_id(line_offsets
, nlines
, "tree", '+',
1020 err
= show_object_id(line_offsets
, nlines
, "tree", '+',
1021 "/dev/null", outfile
);
1026 err
= diff_objects_as_trees(line_offsets
, nlines
, f1
, f2
, fd1
, fd2
,
1027 id1
, id2
, paths
, label1
, label2
, diff_context
, ignore_whitespace
,
1028 force_text_diff
, repo
, outfile
, diff_algo
);
1034 const struct got_error
*
1035 got_diff_objects_as_commits(off_t
**line_offsets
, size_t *nlines
,
1036 FILE *f1
, FILE *f2
, int fd1
, int fd2
,
1037 struct got_object_id
*id1
, struct got_object_id
*id2
,
1038 struct got_pathlist_head
*paths
, enum got_diff_algorithm diff_algo
,
1039 int diff_context
, int ignore_whitespace
, int force_text_diff
,
1040 struct got_repository
*repo
, FILE *outfile
)
1042 const struct got_error
*err
;
1043 struct got_commit_object
*commit1
= NULL
, *commit2
= NULL
;
1047 return got_error(GOT_ERR_NO_OBJ
);
1050 err
= got_object_open_as_commit(&commit1
, repo
, id1
);
1053 err
= got_object_id_str(&idstr
, id1
);
1056 err
= show_object_id(line_offsets
, nlines
, "commit", '-',
1063 err
= show_object_id(line_offsets
, nlines
, "commit", '-',
1064 "/dev/null", outfile
);
1069 err
= got_object_open_as_commit(&commit2
, repo
, id2
);
1073 err
= got_object_id_str(&idstr
, id2
);
1076 err
= show_object_id(line_offsets
, nlines
, "commit", '+',
1081 err
= diff_objects_as_trees(line_offsets
, nlines
, f1
, f2
, fd1
, fd2
,
1082 commit1
? got_object_commit_get_tree_id(commit1
) : NULL
,
1083 got_object_commit_get_tree_id(commit2
), paths
, "", "",
1084 diff_context
, ignore_whitespace
, force_text_diff
, repo
, outfile
,
1088 got_object_commit_close(commit1
);
1090 got_object_commit_close(commit2
);
1095 const struct got_error
*
1096 got_diff_files(struct got_diffreg_result
**resultp
,
1097 FILE *f1
, int f1_exists
, const char *label1
, FILE *f2
, int f2_exists
,
1098 const char *label2
, int diff_context
, int ignore_whitespace
,
1099 int force_text_diff
, FILE *outfile
, enum got_diff_algorithm diff_algo
)
1101 const struct got_error
*err
= NULL
;
1102 struct got_diffreg_result
*diffreg_result
= NULL
;
1108 fprintf(outfile
, "file - %s\n",
1109 f1_exists
? label1
: "/dev/null");
1110 fprintf(outfile
, "file + %s\n",
1111 f2_exists
? label2
: "/dev/null");
1114 err
= got_diffreg(&diffreg_result
, f1
, f2
, diff_algo
,
1115 ignore_whitespace
, force_text_diff
);
1120 err
= got_diffreg_output(NULL
, NULL
, diffreg_result
,
1121 f1_exists
, f2_exists
, label1
, label2
,
1122 GOT_DIFF_OUTPUT_UNIDIFF
, diff_context
, outfile
);
1128 if (resultp
&& err
== NULL
)
1129 *resultp
= diffreg_result
;
1130 else if (diffreg_result
) {
1131 const struct got_error
*free_err
;
1132 free_err
= got_diffreg_result_free(diffreg_result
);
1133 if (free_err
&& err
== NULL
)