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.
17 #include "got_compat.h"
19 #include <sys/queue.h>
28 #include "got_object.h"
29 #include "got_repository.h"
30 #include "got_error.h"
33 #include "got_cancel.h"
34 #include "got_worktree.h"
35 #include "got_opentemp.h"
37 #include "got_lib_diff.h"
38 #include "got_lib_delta.h"
39 #include "got_lib_inflate.h"
40 #include "got_lib_object.h"
43 #define MAX(_a,_b) ((_a) > (_b) ? (_a) : (_b))
46 static const struct got_error
*
47 add_line_metadata(struct got_diff_line
**lines
, size_t *nlines
,
48 off_t off
, uint8_t type
)
50 struct got_diff_line
*p
;
52 p
= reallocarray(*lines
, *nlines
+ 1, sizeof(**lines
));
54 return got_error_from_errno("reallocarray");
56 (*lines
)[*nlines
].offset
= off
;
57 (*lines
)[*nlines
].type
= type
;
64 diffstat_field_width(size_t *maxlen
, int *add_cols
, int *rm_cols
, size_t len
,
65 uint32_t add
, uint32_t rm
)
70 *maxlen
= MAX(*maxlen
, len
);
74 *add_cols
= MAX(*add_cols
, d1
);
78 *rm_cols
= MAX(*rm_cols
, d2
);
81 static const struct got_error
*
82 get_diffstat(struct got_diffstat_cb_arg
*ds
, const char *path
,
83 struct diff_result
*r
, int force_text
, int status
)
85 const struct got_error
*err
;
86 struct got_pathlist_entry
*pe
;
87 struct got_diff_changed_path
*change
= NULL
;
88 int flags
= (r
->left
->atomizer_flags
| r
->right
->atomizer_flags
);
89 int isbin
= (flags
& DIFF_ATOMIZER_FOUND_BINARY_DATA
);
92 change
= calloc(1, sizeof(*change
));
94 return got_error_from_errno("calloc");
96 if (!isbin
|| force_text
) {
97 for (i
= 0; i
< r
->chunks
.len
; ++i
) {
101 c
= diff_chunk_get(r
, i
);
102 clc
= diff_chunk_get_left_count(c
);
103 crc
= diff_chunk_get_right_count(c
);
112 change
->status
= status
;
113 ds
->ins
+= change
->add
;
114 ds
->del
+= change
->rm
;
117 err
= got_pathlist_insert(&pe
, ds
->paths
, path
, change
);
118 if (err
|| pe
== NULL
) {
124 pe
= RB_MAX(got_pathlist_head
, ds
->paths
);
125 diffstat_field_width(&ds
->max_path_len
, &ds
->add_cols
, &ds
->rm_cols
,
126 pe
->path_len
, change
->add
, change
->rm
);
131 static const struct got_error
*
132 diff_blobs(struct got_diff_line
**lines
, size_t *nlines
,
133 struct got_diffreg_result
**resultp
, struct got_blob_object
*blob1
,
134 struct got_blob_object
*blob2
, FILE *f1
, FILE *f2
,
135 const char *label1
, const char *label2
, mode_t mode1
, mode_t mode2
,
136 int diff_context
, int ignore_whitespace
, int force_text_diff
,
137 struct got_diffstat_cb_arg
*diffstat
, FILE *outfile
,
138 enum got_diff_algorithm diff_algo
)
140 const struct got_error
*err
= NULL
, *free_err
;
141 char hex1
[GOT_OBJECT_ID_HEX_MAXLEN
];
142 char hex2
[GOT_OBJECT_ID_HEX_MAXLEN
];
143 const char *idstr1
= NULL
, *idstr2
= NULL
;
144 char *modestr1
= NULL
, *modestr2
= NULL
;
146 struct got_diffreg_result
*result
= NULL
;
150 if (lines
&& *lines
) {
152 outoff
= (*lines
)[*nlines
- 1].offset
;
154 err
= add_line_metadata(lines
, nlines
,
155 0, GOT_DIFF_LINE_NONE
);
165 err
= got_opentemp_truncate(f1
);
170 err
= got_opentemp_truncate(f2
);
177 idstr1
= got_object_blob_id_str(blob1
, hex1
, sizeof(hex1
));
178 err
= got_object_blob_dump_to_file(&size1
, NULL
, NULL
, f1
,
183 idstr1
= "/dev/null";
187 idstr2
= got_object_blob_id_str(blob2
, hex2
, sizeof(hex2
));
188 err
= got_object_blob_dump_to_file(&size2
, NULL
, NULL
, f2
,
193 idstr2
= "/dev/null";
198 if (mode1
&& mode1
!= mode2
) {
202 modebits
= (S_IRWXU
| S_IRWXG
| S_IRWXO
);
203 if (asprintf(&modestr1
, " (mode %o)",
204 mode1
& modebits
) == -1) {
205 err
= got_error_from_errno("asprintf");
209 if (mode2
&& mode1
!= mode2
) {
213 modebits
= (S_IRWXU
| S_IRWXG
| S_IRWXO
);
214 if (asprintf(&modestr2
, " (mode %o)",
215 mode2
& modebits
) == -1) {
216 err
= got_error_from_errno("asprintf");
220 n
= fprintf(outfile
, "blob - %s%s\n", idstr1
,
221 modestr1
? modestr1
: "");
223 err
= got_error_from_errno("fprintf");
227 if (lines
&& *lines
) {
228 err
= add_line_metadata(lines
, nlines
, outoff
,
229 GOT_DIFF_LINE_BLOB_MIN
);
234 n
= fprintf(outfile
, "blob + %s%s\n", idstr2
,
235 modestr2
? modestr2
: "");
237 err
= got_error_from_errno("fprintf");
241 if (lines
&& *lines
) {
242 err
= add_line_metadata(lines
, nlines
, outoff
,
243 GOT_DIFF_LINE_BLOB_PLUS
);
249 err
= got_diffreg(&result
, f1
, f2
, diff_algo
, ignore_whitespace
,
256 int status
= GOT_STATUS_NO_CHANGE
;
259 status
= GOT_STATUS_ADD
;
260 else if (blob2
== NULL
)
261 status
= GOT_STATUS_DELETE
;
263 if (strcmp(idstr1
, idstr2
) != 0)
264 status
= GOT_STATUS_MODIFY
;
265 else if (mode1
!= mode2
)
266 status
= GOT_STATUS_MODE_CHANGE
;
269 if (label1
== NULL
&& label2
== NULL
) {
270 /* diffstat of blobs, show hash instead of path */
271 if (asprintf(&path
, "%.10s -> %.10s",
272 idstr1
, idstr2
) == -1) {
273 err
= got_error_from_errno("asprintf");
277 if (label2
!= NULL
&&
278 (status
!= GOT_STATUS_DELETE
|| label1
== NULL
))
279 path
= strdup(label2
);
281 path
= strdup(label1
);
283 err
= got_error_from_errno("strdup");
288 err
= get_diffstat(diffstat
, path
, result
->result
,
289 force_text_diff
, status
);
297 err
= got_diffreg_output(lines
, nlines
, result
,
298 blob1
!= NULL
, blob2
!= NULL
,
299 label1
? label1
: idstr1
,
300 label2
? label2
: idstr2
,
301 GOT_DIFF_OUTPUT_UNIDIFF
, diff_context
, outfile
);
309 if (resultp
&& err
== NULL
)
312 free_err
= got_diffreg_result_free(result
);
313 if (free_err
&& err
== NULL
)
320 const struct got_error
*
321 got_diff_blob_output_unidiff(void *arg
, struct got_blob_object
*blob1
,
322 struct got_blob_object
*blob2
, FILE *f1
, FILE *f2
,
323 struct got_object_id
*id1
, struct got_object_id
*id2
,
324 const char *label1
, const char *label2
, mode_t mode1
, mode_t mode2
,
325 struct got_repository
*repo
)
327 struct got_diff_blob_output_unidiff_arg
*a
= arg
;
329 return diff_blobs(&a
->lines
, &a
->nlines
, NULL
,
330 blob1
, blob2
, f1
, f2
, label1
, label2
, mode1
, mode2
, a
->diff_context
,
331 a
->ignore_whitespace
, a
->force_text_diff
, a
->diffstat
, a
->outfile
,
335 const struct got_error
*
336 got_diff_blob(struct got_diff_line
**lines
, size_t*nlines
,
337 struct got_blob_object
*blob1
, struct got_blob_object
*blob2
,
338 FILE *f1
, FILE *f2
, const char *label1
, const char *label2
,
339 enum got_diff_algorithm diff_algo
, int diff_context
,
340 int ignore_whitespace
, int force_text_diff
,
341 struct got_diffstat_cb_arg
*ds
, FILE *outfile
)
343 return diff_blobs(lines
, nlines
, NULL
, blob1
, blob2
, f1
, f2
,
344 label1
, label2
, 0, 0, diff_context
, ignore_whitespace
,
345 force_text_diff
, ds
, outfile
, diff_algo
);
348 static const struct got_error
*
349 diff_blob_file(struct got_diff_line
**lines
, size_t *nlines
,
350 struct got_diffreg_result
**resultp
, struct got_blob_object
*blob1
,
351 FILE *f1
, off_t size1
, const char *label1
, FILE *f2
, int f2_exists
,
352 struct stat
*sb2
, const char *label2
, enum got_diff_algorithm diff_algo
,
353 int diff_context
, int ignore_whitespace
, int force_text_diff
,
354 struct got_diffstat_cb_arg
*diffstat
, FILE *outfile
)
356 const struct got_error
*err
= NULL
, *free_err
;
357 char hex1
[GOT_OBJECT_ID_HEX_MAXLEN
];
358 const char *idstr1
= NULL
;
359 struct got_diffreg_result
*result
= NULL
;
366 if (lines
!= NULL
&& *lines
!= NULL
) {
368 err
= add_line_metadata(lines
, nlines
, 0,
373 outoff
= (*lines
)[*nlines
- 1].offset
;
377 idstr1
= got_object_blob_id_str(blob1
, hex1
, sizeof(hex1
));
379 idstr1
= "/dev/null";
384 n
= fprintf(outfile
, "blob - %s\n", label1
? label1
: idstr1
);
386 return got_error_from_errno("fprintf");
387 if (lines
!= NULL
&& *lines
!= NULL
) {
389 err
= add_line_metadata(lines
, nlines
, outoff
,
390 GOT_DIFF_LINE_BLOB_MIN
);
395 /* display file mode for new added files only */
396 if (f2_exists
&& blob1
== NULL
) {
397 int mmask
= (S_IRWXU
| S_IRWXG
| S_IRWXO
);
399 if (S_ISLNK(sb2
->st_mode
))
401 if (asprintf(&mode
, " (mode %o)",
402 sb2
->st_mode
& mmask
) == -1)
403 return got_error_from_errno("asprintf");
405 n
= fprintf(outfile
, "file + %s%s\n",
406 f2_exists
? label2
: "/dev/null", mode
? mode
: "");
409 return got_error_from_errno("fprintf");
410 if (lines
!= NULL
&& *lines
!= NULL
) {
412 err
= add_line_metadata(lines
, nlines
, outoff
,
413 GOT_DIFF_LINE_BLOB_PLUS
);
419 err
= got_diffreg(&result
, f1
, f2
, diff_algo
, ignore_whitespace
,
422 char msg
[GOT_ERR_MAX_MSG_SIZE
];
424 if (snprintf(msg
, sizeof(msg
), "%s vs %s: %s",
425 label1
? label1
: idstr1
,
426 f2_exists
? label2
: "/dev/null", err
->msg
) >= 0) {
427 err
= got_error_msg(err
->code
, msg
);
433 err
= got_diffreg_output(lines
, nlines
, result
,
434 blob1
!= NULL
, f2_exists
,
435 label2
, /* show local file's path, not a blob ID */
436 label2
, GOT_DIFF_OUTPUT_UNIDIFF
,
437 diff_context
, outfile
);
444 int status
= GOT_STATUS_NO_CHANGE
;
447 * Ignore 'm'ode status change: if there's no accompanying
448 * content change, there'll be no diffstat, and if there
449 * are actual changes, 'M'odified takes precedence.
452 status
= GOT_STATUS_ADD
;
454 status
= GOT_STATUS_DELETE
;
456 status
= GOT_STATUS_MODIFY
;
458 if (label2
!= NULL
&&
459 (status
!= GOT_STATUS_DELETE
|| label1
== NULL
))
460 path
= strdup(label2
);
462 path
= strdup(label1
);
464 err
= got_error_from_errno("strdup");
468 err
= get_diffstat(diffstat
, path
, result
->result
,
469 force_text_diff
, status
);
477 if (resultp
&& err
== NULL
)
480 free_err
= got_diffreg_result_free(result
);
481 if (free_err
&& err
== NULL
)
487 const struct got_error
*
488 got_diff_blob_file(struct got_diff_line
**lines
, size_t *nlines
,
489 struct got_blob_object
*blob1
, FILE *f1
, off_t size1
, const char *label1
,
490 FILE *f2
, int f2_exists
, struct stat
*sb2
, const char *label2
,
491 enum got_diff_algorithm diff_algo
, int diff_context
, int ignore_whitespace
,
492 int force_text_diff
, struct got_diffstat_cb_arg
*ds
, FILE *outfile
)
494 return diff_blob_file(lines
, nlines
, NULL
, blob1
, f1
, size1
, label1
,
495 f2
, f2_exists
, sb2
, label2
, diff_algo
, diff_context
,
496 ignore_whitespace
, force_text_diff
, ds
, outfile
);
499 static const struct got_error
*
500 diff_added_blob(struct got_object_id
*id
, FILE *f1
, FILE *f2
, int fd2
,
501 const char *label
, mode_t mode
, struct got_repository
*repo
,
502 got_diff_blob_cb cb
, void *cb_arg
)
504 const struct got_error
*err
;
505 struct got_blob_object
*blob
= NULL
;
506 struct got_object
*obj
= NULL
;
508 err
= got_object_open(&obj
, repo
, id
);
512 err
= got_object_blob_open(&blob
, repo
, obj
, 8192, fd2
);
515 err
= cb(cb_arg
, NULL
, blob
, f1
, f2
, NULL
, id
,
516 NULL
, label
, 0, mode
, repo
);
518 got_object_close(obj
);
520 got_object_blob_close(blob
);
524 static const struct got_error
*
525 diff_modified_blob(struct got_object_id
*id1
, struct got_object_id
*id2
,
526 FILE *f1
, FILE *f2
, int fd1
, int fd2
,
527 const char *label1
, const char *label2
,
528 mode_t mode1
, mode_t mode2
, struct got_repository
*repo
,
529 got_diff_blob_cb cb
, void *cb_arg
)
531 const struct got_error
*err
;
532 struct got_object
*obj1
= NULL
;
533 struct got_object
*obj2
= NULL
;
534 struct got_blob_object
*blob1
= NULL
;
535 struct got_blob_object
*blob2
= NULL
;
537 err
= got_object_open(&obj1
, repo
, id1
);
541 if (obj1
->type
!= GOT_OBJ_TYPE_BLOB
) {
542 err
= got_error(GOT_ERR_OBJ_TYPE
);
546 err
= got_object_open(&obj2
, repo
, id2
);
549 if (obj2
->type
!= GOT_OBJ_TYPE_BLOB
) {
550 err
= got_error(GOT_ERR_OBJ_TYPE
);
554 err
= got_object_blob_open(&blob1
, repo
, obj1
, 8192, fd1
);
558 err
= got_object_blob_open(&blob2
, repo
, obj2
, 8192, fd2
);
562 err
= cb(cb_arg
, blob1
, blob2
, f1
, f2
, id1
, id2
, label1
, label2
,
566 got_object_close(obj1
);
568 got_object_close(obj2
);
570 got_object_blob_close(blob1
);
572 got_object_blob_close(blob2
);
576 static const struct got_error
*
577 diff_deleted_blob(struct got_object_id
*id
, FILE *f1
, int fd1
,
578 FILE *f2
, const char *label
, mode_t mode
, struct got_repository
*repo
,
579 got_diff_blob_cb cb
, void *cb_arg
)
581 const struct got_error
*err
;
582 struct got_blob_object
*blob
= NULL
;
583 struct got_object
*obj
= NULL
;
585 err
= got_object_open(&obj
, repo
, id
);
589 err
= got_object_blob_open(&blob
, repo
, obj
, 8192, fd1
);
592 err
= cb(cb_arg
, blob
, NULL
, f1
, f2
, id
, NULL
, label
, NULL
,
595 got_object_close(obj
);
597 got_object_blob_close(blob
);
601 static const struct got_error
*
602 diff_added_tree(struct got_object_id
*id
, FILE *f1
, FILE *f2
, int fd2
,
603 const char *label
, struct got_repository
*repo
, got_diff_blob_cb cb
,
604 void *cb_arg
, int diff_content
)
606 const struct got_error
*err
= NULL
;
607 struct got_object
*treeobj
= NULL
;
608 struct got_tree_object
*tree
= NULL
;
610 err
= got_object_open(&treeobj
, repo
, id
);
614 if (treeobj
->type
!= GOT_OBJ_TYPE_TREE
) {
615 err
= got_error(GOT_ERR_OBJ_TYPE
);
619 err
= got_object_tree_open(&tree
, repo
, treeobj
);
623 err
= got_diff_tree(NULL
, tree
, f1
, f2
, -1, fd2
, NULL
, label
,
624 repo
, cb
, cb_arg
, diff_content
);
627 got_object_tree_close(tree
);
629 got_object_close(treeobj
);
633 static const struct got_error
*
634 diff_modified_tree(struct got_object_id
*id1
, struct got_object_id
*id2
,
635 FILE *f1
, FILE *f2
, int fd1
, int fd2
,
636 const char *label1
, const char *label2
,
637 struct got_repository
*repo
, got_diff_blob_cb cb
, void *cb_arg
,
640 const struct got_error
*err
;
641 struct got_object
*treeobj1
= NULL
;
642 struct got_object
*treeobj2
= NULL
;
643 struct got_tree_object
*tree1
= NULL
;
644 struct got_tree_object
*tree2
= NULL
;
646 err
= got_object_open(&treeobj1
, repo
, id1
);
650 if (treeobj1
->type
!= GOT_OBJ_TYPE_TREE
) {
651 err
= got_error(GOT_ERR_OBJ_TYPE
);
655 err
= got_object_open(&treeobj2
, repo
, id2
);
659 if (treeobj2
->type
!= GOT_OBJ_TYPE_TREE
) {
660 err
= got_error(GOT_ERR_OBJ_TYPE
);
664 err
= got_object_tree_open(&tree1
, repo
, treeobj1
);
668 err
= got_object_tree_open(&tree2
, repo
, treeobj2
);
672 err
= got_diff_tree(tree1
, tree2
, f1
, f2
, fd1
, fd2
,
673 label1
, label2
, repo
, cb
, cb_arg
, diff_content
);
677 got_object_tree_close(tree1
);
679 got_object_tree_close(tree2
);
681 got_object_close(treeobj1
);
683 got_object_close(treeobj2
);
687 static const struct got_error
*
688 diff_deleted_tree(struct got_object_id
*id
, FILE *f1
, int fd1
,
689 FILE *f2
, const char *label
, struct got_repository
*repo
,
690 got_diff_blob_cb cb
, void *cb_arg
, int diff_content
)
692 const struct got_error
*err
;
693 struct got_object
*treeobj
= NULL
;
694 struct got_tree_object
*tree
= NULL
;
696 err
= got_object_open(&treeobj
, repo
, id
);
700 if (treeobj
->type
!= GOT_OBJ_TYPE_TREE
) {
701 err
= got_error(GOT_ERR_OBJ_TYPE
);
705 err
= got_object_tree_open(&tree
, repo
, treeobj
);
709 err
= got_diff_tree(tree
, NULL
, f1
, f2
, fd1
, -1, label
, NULL
,
710 repo
, cb
, cb_arg
, diff_content
);
713 got_object_tree_close(tree
);
715 got_object_close(treeobj
);
719 static const struct got_error
*
720 diff_kind_mismatch(struct got_tree_entry
*te1
, struct got_tree_entry
*te2
,
721 FILE *f1
, FILE *f2
, int fd1
, int fd2
,
722 const char *label1
, const char *label2
, struct got_repository
*repo
,
723 got_diff_blob_cb cb
, void *cb_arg
, int diff_content
)
725 const struct got_error
*err
= NULL
;
728 * Handle files changing into directories and vice-versa.
729 * Disregard edge cases with FIFOs, device nodes, etc for now.
731 if (!S_ISDIR(te1
->mode
) && S_ISDIR(te2
->mode
)) {
732 if (S_ISREG(te1
->mode
)) {
734 err
= diff_deleted_blob(&te1
->id
, f1
, fd1
,
735 f2
, label1
, te1
->mode
, repo
, cb
, cb_arg
);
737 err
= cb(cb_arg
, NULL
, NULL
, NULL
, NULL
,
738 &te1
->id
, NULL
, label1
, NULL
,
744 return diff_added_tree(&te2
->id
, f1
, f2
, fd2
, label2
,
745 repo
, cb
, cb_arg
, diff_content
);
746 } else if (S_ISDIR(te1
->mode
) && !S_ISDIR(te2
->mode
)) {
747 err
= diff_deleted_tree(&te1
->id
, f1
, fd1
, f2
,
748 label1
, repo
, cb
, cb_arg
, diff_content
);
751 if (S_ISREG(te2
->mode
)) {
753 err
= diff_added_blob(&te2
->id
, f1
, f2
, fd2
,
754 label2
, te2
->mode
, repo
, cb
, cb_arg
);
756 err
= cb(cb_arg
, NULL
, NULL
, NULL
, NULL
, NULL
,
757 &te2
->id
, NULL
, label2
, 0, te2
->mode
, repo
);
767 static const struct got_error
*
768 diff_entry_old_new(struct got_tree_entry
*te1
, struct got_tree_entry
*te2
,
769 FILE *f1
, FILE *f2
, int fd1
, int fd2
,
770 const char *label1
, const char *label2
,
771 struct got_repository
*repo
, got_diff_blob_cb cb
, void *cb_arg
,
774 const struct got_error
*err
= NULL
;
777 if (got_object_tree_entry_is_submodule(te1
))
781 if (S_ISDIR(te1
->mode
))
782 err
= diff_deleted_tree(&te1
->id
, f1
, fd1
, f2
,
783 label1
, repo
, cb
, cb_arg
, diff_content
);
786 err
= diff_deleted_blob(&te1
->id
, f1
, fd1
,
787 f2
, label1
, te1
->mode
, repo
, cb
, cb_arg
);
789 err
= cb(cb_arg
, NULL
, NULL
, NULL
, NULL
,
790 &te1
->id
, NULL
, label1
, NULL
,
794 } else if (got_object_tree_entry_is_submodule(te2
))
797 id_match
= (got_object_id_cmp(&te1
->id
, &te2
->id
) == 0);
798 if (S_ISDIR(te1
->mode
) && S_ISDIR(te2
->mode
)) {
800 return diff_modified_tree(&te1
->id
, &te2
->id
, f1
, f2
,
801 fd1
, fd2
, label1
, label2
, repo
, cb
, cb_arg
,
803 } else if ((S_ISREG(te1
->mode
) || S_ISLNK(te1
->mode
)) &&
804 (S_ISREG(te2
->mode
) || S_ISLNK(te2
->mode
))) {
806 ((te1
->mode
& (S_IFLNK
| S_IXUSR
))) !=
807 (te2
->mode
& (S_IFLNK
| S_IXUSR
))) {
809 return diff_modified_blob(&te1
->id
, &te2
->id
,
810 f1
, f2
, fd1
, fd2
, label1
, label2
,
811 te1
->mode
, te2
->mode
, repo
, cb
, cb_arg
);
813 return cb(cb_arg
, NULL
, NULL
, NULL
, NULL
,
814 &te1
->id
, &te2
->id
, label1
, label2
,
815 te1
->mode
, te2
->mode
, repo
);
822 return diff_kind_mismatch(te1
, te2
, f1
, f2
, fd1
, fd2
,
823 label1
, label2
, repo
, cb
, cb_arg
, diff_content
);
826 static const struct got_error
*
827 diff_entry_new_old(struct got_tree_entry
*te2
,
828 struct got_tree_entry
*te1
, FILE *f1
, FILE *f2
, int fd2
, const char *label2
,
829 struct got_repository
*repo
, got_diff_blob_cb cb
, void *cb_arg
,
832 if (te1
!= NULL
) /* handled by diff_entry_old_new() */
835 if (got_object_tree_entry_is_submodule(te2
))
838 if (S_ISDIR(te2
->mode
))
839 return diff_added_tree(&te2
->id
, f1
, f2
, fd2
, label2
,
840 repo
, cb
, cb_arg
, diff_content
);
843 return diff_added_blob(&te2
->id
, f1
, f2
, fd2
,
844 label2
, te2
->mode
, repo
, cb
, cb_arg
);
846 return cb(cb_arg
, NULL
, NULL
, NULL
, NULL
, NULL
, &te2
->id
,
847 NULL
, label2
, 0, te2
->mode
, repo
);
850 const struct got_error
*
851 got_diff_tree_compute_diffstat(void *arg
, struct got_blob_object
*blob1
,
852 struct got_blob_object
*blob2
, FILE *f1
, FILE *f2
,
853 struct got_object_id
*id1
, struct got_object_id
*id2
,
854 const char *label1
, const char *label2
,
855 mode_t mode1
, mode_t mode2
, struct got_repository
*repo
)
857 const struct got_error
*err
= NULL
;
858 struct got_diffreg_result
*result
= NULL
;
859 struct got_diffstat_cb_arg
*a
= arg
;
861 int status
= GOT_STATUS_NO_CHANGE
;
863 path
= strdup(label2
? label2
: label1
);
865 return got_error_from_errno("strdup");
868 status
= GOT_STATUS_ADD
;
869 else if (id2
== NULL
)
870 status
= GOT_STATUS_DELETE
;
872 if (got_object_id_cmp(id1
, id2
) != 0)
873 status
= GOT_STATUS_MODIFY
;
874 else if (mode1
!= mode2
)
875 status
= GOT_STATUS_MODE_CHANGE
;
879 err
= got_opentemp_truncate(f1
);
884 err
= got_opentemp_truncate(f2
);
890 err
= got_object_blob_dump_to_file(NULL
, NULL
, NULL
, f1
,
896 err
= got_object_blob_dump_to_file(NULL
, NULL
, NULL
, f2
,
902 err
= got_diffreg(&result
, f1
, f2
, a
->diff_algo
, a
->ignore_ws
,
907 err
= get_diffstat(a
, path
, result
->result
, a
->force_text
, status
);
911 const struct got_error
*free_err
;
913 free_err
= got_diffreg_result_free(result
);
914 if (free_err
&& err
== NULL
)
922 const struct got_error
*
923 got_diff_tree_collect_changed_paths(void *arg
, struct got_blob_object
*blob1
,
924 struct got_blob_object
*blob2
, FILE *f1
, FILE *f2
,
925 struct got_object_id
*id1
, struct got_object_id
*id2
,
926 const char *label1
, const char *label2
,
927 mode_t mode1
, mode_t mode2
, struct got_repository
*repo
)
929 const struct got_error
*err
= NULL
;
930 struct got_pathlist_head
*paths
= arg
;
931 struct got_pathlist_entry
*new;
932 struct got_diff_changed_path
*change
= NULL
;
935 path
= strdup(label2
? label2
: label1
);
937 return got_error_from_errno("strdup");
939 change
= malloc(sizeof(*change
));
940 if (change
== NULL
) {
941 err
= got_error_from_errno("malloc");
945 change
->status
= GOT_STATUS_NO_CHANGE
;
947 change
->status
= GOT_STATUS_ADD
;
948 else if (id2
== NULL
)
949 change
->status
= GOT_STATUS_DELETE
;
951 if (got_object_id_cmp(id1
, id2
) != 0)
952 change
->status
= GOT_STATUS_MODIFY
;
953 else if (mode1
!= mode2
)
954 change
->status
= GOT_STATUS_MODE_CHANGE
;
957 err
= got_pathlist_insert(&new, paths
, path
, change
);
959 if (err
|| new == NULL
) {
966 const struct got_error
*
967 got_diff_tree(struct got_tree_object
*tree1
, struct got_tree_object
*tree2
,
968 FILE *f1
, FILE *f2
, int fd1
, int fd2
,
969 const char *label1
, const char *label2
,
970 struct got_repository
*repo
, got_diff_blob_cb cb
, void *cb_arg
,
973 const struct got_error
*err
= NULL
;
974 struct got_tree_entry
*te1
= NULL
;
975 struct got_tree_entry
*te2
= NULL
;
976 char *l1
= NULL
, *l2
= NULL
;
977 int tidx1
= 0, tidx2
= 0;
980 te1
= got_object_tree_get_entry(tree1
, 0);
981 if (te1
&& asprintf(&l1
, "%s%s%s", label1
, label1
[0] ? "/" : "",
983 return got_error_from_errno("asprintf");
986 te2
= got_object_tree_get_entry(tree2
, 0);
987 if (te2
&& asprintf(&l2
, "%s%s%s", label2
, label2
[0] ? "/" : "",
989 err
= got_error_from_errno("asprintf");
996 struct got_tree_entry
*te
= NULL
;
999 te
= got_object_tree_find_entry(tree2
,
1004 if (te
&& asprintf(&l2
, "%s%s%s", label2
,
1005 label2
[0] ? "/" : "", te
->name
) == -1) {
1006 err
= got_error_from_errno("asprintf");
1011 err
= diff_entry_old_new(te1
, te
, f1
, f2
, fd1
, fd2
,
1012 l1
, l2
, repo
, cb
, cb_arg
, diff_content
);
1018 struct got_tree_entry
*te
= NULL
;
1021 te
= got_object_tree_find_entry(tree1
,
1027 if (asprintf(&l2
, "%s%s%s", label2
,
1028 label2
[0] ? "/" : "", te
->name
) == -1) {
1029 err
= got_error_from_errno("asprintf");
1033 if (asprintf(&l2
, "%s%s%s", label2
,
1034 label2
[0] ? "/" : "", te2
->name
) == -1) {
1035 err
= got_error_from_errno("asprintf");
1040 err
= diff_entry_new_old(te2
, te
, f1
, f2
, fd2
, l2
,
1041 repo
, cb
, cb_arg
, diff_content
);
1050 te1
= got_object_tree_get_entry(tree1
, tidx1
);
1052 asprintf(&l1
, "%s%s%s", label1
,
1053 label1
[0] ? "/" : "", te1
->name
) == -1) {
1054 err
= got_error_from_errno("asprintf");
1063 te2
= got_object_tree_get_entry(tree2
, tidx2
);
1065 asprintf(&l2
, "%s%s%s", label2
,
1066 label2
[0] ? "/" : "", te2
->name
) == -1) {
1067 err
= got_error_from_errno("asprintf");
1071 } while (te1
|| te2
);
1079 const struct got_error
*
1080 got_diff_objects_as_blobs(struct got_diff_line
**lines
, size_t *nlines
,
1081 FILE *f1
, FILE *f2
, int fd1
, int fd2
,
1082 struct got_object_id
*id1
, struct got_object_id
*id2
,
1083 const char *label1
, const char *label2
,
1084 enum got_diff_algorithm diff_algo
, int diff_context
,
1085 int ignore_whitespace
, int force_text_diff
, struct got_diffstat_cb_arg
*ds
,
1086 struct got_repository
*repo
, FILE *outfile
)
1088 const struct got_error
*err
;
1089 struct got_blob_object
*blob1
= NULL
, *blob2
= NULL
;
1091 if (id1
== NULL
&& id2
== NULL
)
1092 return got_error(GOT_ERR_NO_OBJ
);
1095 err
= got_object_open_as_blob(&blob1
, repo
, id1
, 8192, fd1
);
1100 err
= got_object_open_as_blob(&blob2
, repo
, id2
, 8192, fd2
);
1104 err
= got_diff_blob(lines
, nlines
, blob1
, blob2
, f1
, f2
, label1
, label2
,
1105 diff_algo
, diff_context
, ignore_whitespace
, force_text_diff
,
1109 got_object_blob_close(blob1
);
1111 got_object_blob_close(blob2
);
1115 static const struct got_error
*
1116 diff_paths(struct got_tree_object
*tree1
, struct got_tree_object
*tree2
,
1117 FILE *f1
, FILE *f2
, int fd1
, int fd2
, struct got_pathlist_head
*paths
,
1118 struct got_repository
*repo
, got_diff_blob_cb cb
, void *cb_arg
)
1120 const struct got_error
*err
= NULL
;
1121 struct got_pathlist_entry
*pe
;
1122 struct got_object_id
*id1
= NULL
, *id2
= NULL
;
1123 struct got_tree_object
*subtree1
= NULL
, *subtree2
= NULL
;
1124 struct got_blob_object
*blob1
= NULL
, *blob2
= NULL
;
1126 RB_FOREACH(pe
, got_pathlist_head
, paths
) {
1127 int type1
= GOT_OBJ_TYPE_ANY
, type2
= GOT_OBJ_TYPE_ANY
;
1128 mode_t mode1
= 0, mode2
= 0;
1135 got_object_tree_close(subtree1
);
1139 got_object_tree_close(subtree2
);
1143 got_object_blob_close(blob1
);
1147 got_object_blob_close(blob2
);
1151 err
= got_object_tree_find_path(&id1
, &mode1
, repo
,
1153 if (err
&& err
->code
!= GOT_ERR_NO_TREE_ENTRY
)
1157 err
= got_object_tree_find_path(&id2
, &mode2
, repo
,
1159 if (err
&& err
->code
!= GOT_ERR_NO_TREE_ENTRY
)
1162 if (id1
== NULL
&& id2
== NULL
) {
1163 err
= got_error_path(pe
->path
, GOT_ERR_NO_TREE_ENTRY
);
1167 err
= got_object_get_type(&type1
, repo
, id1
);
1172 err
= got_object_get_type(&type2
, repo
, id2
);
1176 if (type1
== GOT_OBJ_TYPE_ANY
&&
1177 type2
== GOT_OBJ_TYPE_ANY
) {
1178 err
= got_error_path(pe
->path
, GOT_ERR_NO_OBJ
);
1180 } else if (type1
!= GOT_OBJ_TYPE_ANY
&&
1181 type2
!= GOT_OBJ_TYPE_ANY
&& type1
!= type2
) {
1182 err
= got_error(GOT_ERR_OBJ_TYPE
);
1186 if (type1
== GOT_OBJ_TYPE_BLOB
||
1187 type2
== GOT_OBJ_TYPE_BLOB
) {
1189 err
= got_object_open_as_blob(&blob1
, repo
,
1195 err
= got_object_open_as_blob(&blob2
, repo
,
1200 err
= cb(cb_arg
, blob1
, blob2
, f1
, f2
, id1
, id2
,
1201 id1
? pe
->path
: "/dev/null",
1202 id2
? pe
->path
: "/dev/null",
1203 mode1
, mode2
, repo
);
1206 } else if (type1
== GOT_OBJ_TYPE_TREE
||
1207 type2
== GOT_OBJ_TYPE_TREE
) {
1209 err
= got_object_open_as_tree(&subtree1
, repo
,
1215 err
= got_object_open_as_tree(&subtree2
, repo
,
1220 err
= got_diff_tree(subtree1
, subtree2
, f1
, f2
,
1222 id1
? pe
->path
: "/dev/null",
1223 id2
? pe
->path
: "/dev/null",
1224 repo
, cb
, cb_arg
, 1);
1228 err
= got_error(GOT_ERR_OBJ_TYPE
);
1236 got_object_tree_close(subtree1
);
1238 got_object_tree_close(subtree2
);
1240 got_object_blob_close(blob1
);
1242 got_object_blob_close(blob2
);
1246 static const struct got_error
*
1247 show_object_id(struct got_diff_line
**lines
, size_t *nlines
,
1248 const char *obj_typestr
, int ch
, const char *id_str
, FILE *outfile
)
1250 const struct got_error
*err
;
1254 n
= fprintf(outfile
, "%s %c %s\n", obj_typestr
, ch
, id_str
);
1256 return got_error_from_errno("fprintf");
1258 if (lines
!= NULL
&& *lines
!= NULL
) {
1260 err
= add_line_metadata(lines
, nlines
, 0,
1261 GOT_DIFF_LINE_META
);
1265 outoff
= (*lines
)[*nlines
- 1].offset
;
1268 err
= add_line_metadata(lines
, nlines
, outoff
,
1269 GOT_DIFF_LINE_META
);
1277 static const struct got_error
*
1278 diff_objects_as_trees(struct got_diff_line
**lines
, size_t *nlines
,
1279 FILE *f1
, FILE *f2
, int fd1
, int fd2
,
1280 struct got_object_id
*id1
, struct got_object_id
*id2
,
1281 struct got_pathlist_head
*paths
, const char *label1
, const char *label2
,
1282 int diff_context
, int ignore_whitespace
, int force_text_diff
,
1283 struct got_diffstat_cb_arg
*dsa
, struct got_repository
*repo
,
1284 FILE *outfile
, enum got_diff_algorithm diff_algo
)
1286 const struct got_error
*err
;
1287 struct got_tree_object
*tree1
= NULL
, *tree2
= NULL
;
1288 struct got_diff_blob_output_unidiff_arg arg
;
1289 int want_linemeta
= (lines
!= NULL
&& *lines
!= NULL
);
1291 if (id1
== NULL
&& id2
== NULL
)
1292 return got_error(GOT_ERR_NO_OBJ
);
1295 err
= got_object_open_as_tree(&tree1
, repo
, id1
);
1300 err
= got_object_open_as_tree(&tree2
, repo
, id2
);
1305 arg
.diff_algo
= diff_algo
;
1306 arg
.diff_context
= diff_context
;
1307 arg
.ignore_whitespace
= ignore_whitespace
;
1308 arg
.force_text_diff
= force_text_diff
;
1310 arg
.outfile
= outfile
;
1311 if (want_linemeta
) {
1313 arg
.nlines
= *nlines
;
1318 if (paths
== NULL
|| RB_EMPTY(paths
))
1319 err
= got_diff_tree(tree1
, tree2
, f1
, f2
, fd1
, fd2
, label1
,
1320 label2
, repo
, got_diff_blob_output_unidiff
, &arg
, 1);
1322 err
= diff_paths(tree1
, tree2
, f1
, f2
, fd1
, fd2
, paths
, repo
,
1323 got_diff_blob_output_unidiff
, &arg
);
1324 if (want_linemeta
) {
1325 *lines
= arg
.lines
; /* was likely re-allocated */
1326 *nlines
= arg
.nlines
;
1330 got_object_tree_close(tree1
);
1332 got_object_tree_close(tree2
);
1336 const struct got_error
*
1337 got_diff_objects_as_trees(struct got_diff_line
**lines
, size_t *nlines
,
1338 FILE *f1
, FILE *f2
, int fd1
, int fd2
,
1339 struct got_object_id
*id1
, struct got_object_id
*id2
,
1340 struct got_pathlist_head
*paths
, const char *label1
, const char *label2
,
1341 enum got_diff_algorithm diff_algo
, int diff_context
, int ignore_whitespace
,
1342 int force_text_diff
, struct got_diffstat_cb_arg
*dsa
,
1343 struct got_repository
*repo
, FILE *outfile
)
1345 const struct got_error
*err
;
1348 if (id1
== NULL
&& id2
== NULL
)
1349 return got_error(GOT_ERR_NO_OBJ
);
1352 err
= got_object_id_str(&idstr
, id1
);
1355 err
= show_object_id(lines
, nlines
, "tree", '-', idstr
, outfile
);
1361 err
= show_object_id(lines
, nlines
, "tree", '-', "/dev/null",
1368 err
= got_object_id_str(&idstr
, id2
);
1371 err
= show_object_id(lines
, nlines
, "tree", '+', idstr
, outfile
);
1377 err
= show_object_id(lines
, nlines
, "tree", '+', "/dev/null",
1383 err
= diff_objects_as_trees(lines
, nlines
, f1
, f2
, fd1
, fd2
, id1
, id2
,
1384 paths
, label1
, label2
, diff_context
, ignore_whitespace
,
1385 force_text_diff
, dsa
, repo
, outfile
, diff_algo
);
1391 const struct got_error
*
1392 got_diff_objects_as_commits(struct got_diff_line
**lines
, size_t *nlines
,
1393 FILE *f1
, FILE *f2
, int fd1
, int fd2
,
1394 struct got_object_id
*id1
, struct got_object_id
*id2
,
1395 struct got_pathlist_head
*paths
, enum got_diff_algorithm diff_algo
,
1396 int diff_context
, int ignore_whitespace
, int force_text_diff
,
1397 struct got_diffstat_cb_arg
*dsa
, struct got_repository
*repo
, FILE *outfile
)
1399 const struct got_error
*err
;
1400 struct got_commit_object
*commit1
= NULL
, *commit2
= NULL
;
1404 return got_error(GOT_ERR_NO_OBJ
);
1407 err
= got_object_open_as_commit(&commit1
, repo
, id1
);
1410 err
= got_object_id_str(&idstr
, id1
);
1413 err
= show_object_id(lines
, nlines
, "commit", '-', idstr
,
1420 err
= show_object_id(lines
, nlines
, "commit", '-', "/dev/null",
1426 err
= got_object_open_as_commit(&commit2
, repo
, id2
);
1430 err
= got_object_id_str(&idstr
, id2
);
1433 err
= show_object_id(lines
, nlines
, "commit", '+', idstr
, outfile
);
1437 err
= diff_objects_as_trees(lines
, nlines
, f1
, f2
, fd1
, fd2
,
1438 commit1
? got_object_commit_get_tree_id(commit1
) : NULL
,
1439 got_object_commit_get_tree_id(commit2
), paths
, "", "",
1440 diff_context
, ignore_whitespace
, force_text_diff
, dsa
, repo
,
1441 outfile
, diff_algo
);
1444 got_object_commit_close(commit1
);
1446 got_object_commit_close(commit2
);
1451 const struct got_error
*
1452 got_diff_files(struct got_diffreg_result
**resultp
,
1453 FILE *f1
, int f1_exists
, const char *label1
, FILE *f2
, int f2_exists
,
1454 const char *label2
, int diff_context
, int ignore_whitespace
,
1455 int force_text_diff
, FILE *outfile
, enum got_diff_algorithm diff_algo
)
1457 const struct got_error
*err
= NULL
;
1458 struct got_diffreg_result
*diffreg_result
= NULL
;
1464 fprintf(outfile
, "file - %s\n",
1465 f1_exists
? label1
: "/dev/null");
1466 fprintf(outfile
, "file + %s\n",
1467 f2_exists
? label2
: "/dev/null");
1470 err
= got_diffreg(&diffreg_result
, f1
, f2
, diff_algo
,
1471 ignore_whitespace
, force_text_diff
);
1476 err
= got_diffreg_output(NULL
, NULL
, diffreg_result
,
1477 f1_exists
, f2_exists
, label1
, label2
,
1478 GOT_DIFF_OUTPUT_UNIDIFF
, diff_context
, outfile
);
1484 if (resultp
&& err
== NULL
)
1485 *resultp
= diffreg_result
;
1486 else if (diffreg_result
) {
1487 const struct got_error
*free_err
;
1489 free_err
= got_diffreg_result_free(diffreg_result
);
1490 if (free_err
&& err
== NULL
)