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 <sys/queue.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"
42 #define MAX(_a,_b) ((_a) > (_b) ? (_a) : (_b))
45 static const struct got_error *
46 add_line_metadata(struct got_diff_line **lines, size_t *nlines,
47 off_t off, uint8_t type)
49 struct got_diff_line *p;
51 p = reallocarray(*lines, *nlines + 1, sizeof(**lines));
53 return got_error_from_errno("reallocarray");
55 (*lines)[*nlines].offset = off;
56 (*lines)[*nlines].type = type;
62 static const struct got_error *
63 diff_blobs(struct got_diff_line **lines, size_t *nlines,
64 struct got_diffreg_result **resultp, struct got_blob_object *blob1,
65 struct got_blob_object *blob2, FILE *f1, FILE *f2,
66 const char *label1, const char *label2, mode_t mode1, mode_t mode2,
67 int diff_context, int ignore_whitespace, int force_text_diff, FILE *outfile,
68 enum got_diff_algorithm diff_algo)
70 const struct got_error *err = NULL, *free_err;
71 char hex1[SHA1_DIGEST_STRING_LENGTH];
72 char hex2[SHA1_DIGEST_STRING_LENGTH];
73 const char *idstr1 = NULL, *idstr2 = NULL;
75 struct got_diffreg_result *result = NULL;
79 if (lines && *lines && *nlines > 0)
80 outoff = (*lines)[*nlines - 1].offset;
82 err = add_line_metadata(lines, nlines, 0, GOT_DIFF_LINE_NONE);
91 err = got_opentemp_truncate(f1);
96 err = got_opentemp_truncate(f2);
103 idstr1 = got_object_blob_id_str(blob1, hex1, sizeof(hex1));
104 err = got_object_blob_dump_to_file(&size1, NULL, NULL, f1,
109 idstr1 = "/dev/null";
113 idstr2 = got_object_blob_id_str(blob2, hex2, sizeof(hex2));
114 err = got_object_blob_dump_to_file(&size2, NULL, NULL, f2,
119 idstr2 = "/dev/null";
122 char *modestr1 = NULL, *modestr2 = NULL;
124 if (mode1 && mode1 != mode2) {
128 modebits = (S_IRWXU | S_IRWXG | S_IRWXO);
129 if (asprintf(&modestr1, " (mode %o)",
130 mode1 & modebits) == -1) {
131 err = got_error_from_errno("asprintf");
135 if (mode2 && mode1 != mode2) {
139 modebits = (S_IRWXU | S_IRWXG | S_IRWXO);
140 if (asprintf(&modestr2, " (mode %o)",
141 mode2 & modebits) == -1) {
142 err = got_error_from_errno("asprintf");
146 n = fprintf(outfile, "blob - %s%s\n", idstr1,
147 modestr1 ? modestr1 : "");
152 err = add_line_metadata(lines, nlines, outoff,
153 GOT_DIFF_LINE_BLOB_MIN);
158 n = fprintf(outfile, "blob + %s%s\n", idstr2,
159 modestr2 ? modestr2 : "");
164 err = add_line_metadata(lines, nlines, outoff,
165 GOT_DIFF_LINE_BLOB_PLUS);
173 err = got_diffreg(&result, f1, f2, diff_algo, ignore_whitespace,
179 err = got_diffreg_output(lines, nlines, result,
180 blob1 != NULL, blob2 != NULL,
181 label1 ? label1 : idstr1,
182 label2 ? label2 : idstr2,
183 GOT_DIFF_OUTPUT_UNIDIFF, diff_context, outfile);
189 if (resultp && err == NULL)
192 free_err = got_diffreg_result_free(result);
193 if (free_err && err == NULL)
200 const struct got_error *
201 got_diff_blob_output_unidiff(void *arg, struct got_blob_object *blob1,
202 struct got_blob_object *blob2, FILE *f1, FILE *f2,
203 struct got_object_id *id1, struct got_object_id *id2,
204 const char *label1, const char *label2, mode_t mode1, mode_t mode2,
205 struct got_repository *repo)
207 struct got_diff_blob_output_unidiff_arg *a = arg;
209 return diff_blobs(&a->lines, &a->nlines, NULL,
210 blob1, blob2, f1, f2, label1, label2, mode1, mode2, a->diff_context,
211 a->ignore_whitespace, a->force_text_diff, a->outfile, a->diff_algo);
214 const struct got_error *
215 got_diff_blob(struct got_diff_line **lines, size_t*nlines,
216 struct got_blob_object *blob1, struct got_blob_object *blob2,
217 FILE *f1, FILE *f2, const char *label1, const char *label2,
218 enum got_diff_algorithm diff_algo, int diff_context,
219 int ignore_whitespace, int force_text_diff, FILE *outfile)
221 return diff_blobs(lines, nlines, NULL, blob1, blob2, f1, f2,
222 label1, label2, 0, 0, diff_context, ignore_whitespace,
223 force_text_diff, outfile, diff_algo);
226 static const struct got_error *
227 diff_blob_file(struct got_diffreg_result **resultp,
228 struct got_blob_object *blob1, FILE *f1, off_t size1, const char *label1,
229 FILE *f2, int f2_exists, struct stat *sb2, const char *label2,
230 enum got_diff_algorithm diff_algo, int diff_context, int ignore_whitespace,
231 int force_text_diff, FILE *outfile)
233 const struct got_error *err = NULL, *free_err;
234 char hex1[SHA1_DIGEST_STRING_LENGTH];
235 const char *idstr1 = NULL;
236 struct got_diffreg_result *result = NULL;
242 idstr1 = got_object_blob_id_str(blob1, hex1, sizeof(hex1));
244 idstr1 = "/dev/null";
249 /* display file mode for new added files only */
250 if (f2_exists && blob1 == NULL) {
251 int mmask = (S_IRWXU | S_IRWXG | S_IRWXO);
253 if (S_ISLNK(sb2->st_mode))
255 if (asprintf(&mode, " (mode %o)",
256 sb2->st_mode & mmask) == -1)
257 return got_error_from_errno("asprintf");
259 fprintf(outfile, "blob - %s\n", label1 ? label1 : idstr1);
260 fprintf(outfile, "file + %s%s\n",
261 f2_exists ? label2 : "/dev/null", mode ? mode : "");
265 err = got_diffreg(&result, f1, f2, diff_algo, ignore_whitespace,
271 err = got_diffreg_output(NULL, NULL, result,
272 blob1 != NULL, f2_exists,
273 label2, /* show local file's path, not a blob ID */
274 label2, GOT_DIFF_OUTPUT_UNIDIFF,
275 diff_context, outfile);
281 if (resultp && err == NULL)
284 free_err = got_diffreg_result_free(result);
285 if (free_err && err == NULL)
291 const struct got_error *
292 got_diff_blob_file(struct got_blob_object *blob1, FILE *f1, off_t size1,
293 const char *label1, FILE *f2, int f2_exists, struct stat *sb2,
294 const char *label2, enum got_diff_algorithm diff_algo, int diff_context,
295 int ignore_whitespace, int force_text_diff, FILE *outfile)
297 return diff_blob_file(NULL, blob1, f1, size1, label1, f2, f2_exists,
298 sb2, label2, diff_algo, diff_context, ignore_whitespace,
299 force_text_diff, outfile);
302 static const struct got_error *
303 diff_added_blob(struct got_object_id *id, FILE *f1, FILE *f2, int fd2,
304 const char *label, mode_t mode, struct got_repository *repo,
305 got_diff_blob_cb cb, void *cb_arg)
307 const struct got_error *err;
308 struct got_blob_object *blob = NULL;
309 struct got_object *obj = NULL;
311 err = got_object_open(&obj, repo, id);
315 err = got_object_blob_open(&blob, repo, obj, 8192, fd2);
318 err = cb(cb_arg, NULL, blob, f1, f2, NULL, id,
319 NULL, label, 0, mode, repo);
321 got_object_close(obj);
323 got_object_blob_close(blob);
327 static const struct got_error *
328 diff_modified_blob(struct got_object_id *id1, struct got_object_id *id2,
329 FILE *f1, FILE *f2, int fd1, int fd2,
330 const char *label1, const char *label2,
331 mode_t mode1, mode_t mode2, struct got_repository *repo,
332 got_diff_blob_cb cb, void *cb_arg)
334 const struct got_error *err;
335 struct got_object *obj1 = NULL;
336 struct got_object *obj2 = NULL;
337 struct got_blob_object *blob1 = NULL;
338 struct got_blob_object *blob2 = NULL;
340 err = got_object_open(&obj1, repo, id1);
344 if (obj1->type != GOT_OBJ_TYPE_BLOB) {
345 err = got_error(GOT_ERR_OBJ_TYPE);
349 err = got_object_open(&obj2, repo, id2);
352 if (obj2->type != GOT_OBJ_TYPE_BLOB) {
353 err = got_error(GOT_ERR_BAD_OBJ_DATA);
357 err = got_object_blob_open(&blob1, repo, obj1, 8192, fd1);
361 err = got_object_blob_open(&blob2, repo, obj2, 8192, fd2);
365 err = cb(cb_arg, blob1, blob2, f1, f2, id1, id2, label1, label2,
369 got_object_close(obj1);
371 got_object_close(obj2);
373 got_object_blob_close(blob1);
375 got_object_blob_close(blob2);
379 static const struct got_error *
380 diff_deleted_blob(struct got_object_id *id, FILE *f1, int fd1,
381 FILE *f2, const char *label, mode_t mode, struct got_repository *repo,
382 got_diff_blob_cb cb, void *cb_arg)
384 const struct got_error *err;
385 struct got_blob_object *blob = NULL;
386 struct got_object *obj = NULL;
388 err = got_object_open(&obj, repo, id);
392 err = got_object_blob_open(&blob, repo, obj, 8192, fd1);
395 err = cb(cb_arg, blob, NULL, f1, f2, id, NULL, label, NULL,
398 got_object_close(obj);
400 got_object_blob_close(blob);
404 static const struct got_error *
405 diff_added_tree(struct got_object_id *id, FILE *f1, FILE *f2, int fd2,
406 const char *label, struct got_repository *repo, got_diff_blob_cb cb,
407 void *cb_arg, int diff_content)
409 const struct got_error *err = NULL;
410 struct got_object *treeobj = NULL;
411 struct got_tree_object *tree = NULL;
413 err = got_object_open(&treeobj, repo, id);
417 if (treeobj->type != GOT_OBJ_TYPE_TREE) {
418 err = got_error(GOT_ERR_OBJ_TYPE);
422 err = got_object_tree_open(&tree, repo, treeobj);
426 err = got_diff_tree(NULL, tree, f1, f2, -1, fd2, NULL, label,
427 repo, cb, cb_arg, diff_content);
430 got_object_tree_close(tree);
432 got_object_close(treeobj);
436 static const struct got_error *
437 diff_modified_tree(struct got_object_id *id1, struct got_object_id *id2,
438 FILE *f1, FILE *f2, int fd1, int fd2,
439 const char *label1, const char *label2,
440 struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg,
443 const struct got_error *err;
444 struct got_object *treeobj1 = NULL;
445 struct got_object *treeobj2 = NULL;
446 struct got_tree_object *tree1 = NULL;
447 struct got_tree_object *tree2 = NULL;
449 err = got_object_open(&treeobj1, repo, id1);
453 if (treeobj1->type != GOT_OBJ_TYPE_TREE) {
454 err = got_error(GOT_ERR_OBJ_TYPE);
458 err = got_object_open(&treeobj2, repo, id2);
462 if (treeobj2->type != GOT_OBJ_TYPE_TREE) {
463 err = got_error(GOT_ERR_OBJ_TYPE);
467 err = got_object_tree_open(&tree1, repo, treeobj1);
471 err = got_object_tree_open(&tree2, repo, treeobj2);
475 err = got_diff_tree(tree1, tree2, f1, f2, fd1, fd2,
476 label1, label2, repo, cb, cb_arg, diff_content);
480 got_object_tree_close(tree1);
482 got_object_tree_close(tree2);
484 got_object_close(treeobj1);
486 got_object_close(treeobj2);
490 static const struct got_error *
491 diff_deleted_tree(struct got_object_id *id, FILE *f1, int fd1,
492 FILE *f2, const char *label, struct got_repository *repo,
493 got_diff_blob_cb cb, void *cb_arg, int diff_content)
495 const struct got_error *err;
496 struct got_object *treeobj = NULL;
497 struct got_tree_object *tree = NULL;
499 err = got_object_open(&treeobj, repo, id);
503 if (treeobj->type != GOT_OBJ_TYPE_TREE) {
504 err = got_error(GOT_ERR_OBJ_TYPE);
508 err = got_object_tree_open(&tree, repo, treeobj);
512 err = got_diff_tree(tree, NULL, f1, f2, fd1, -1, label, NULL,
513 repo, cb, cb_arg, diff_content);
516 got_object_tree_close(tree);
518 got_object_close(treeobj);
522 static const struct got_error *
523 diff_kind_mismatch(struct got_object_id *id1, struct got_object_id *id2,
524 const char *label1, const char *label2, struct got_repository *repo,
525 got_diff_blob_cb cb, void *cb_arg)
531 static const struct got_error *
532 diff_entry_old_new(struct got_tree_entry *te1, struct got_tree_entry *te2,
533 FILE *f1, FILE *f2, int fd1, int fd2,
534 const char *label1, const char *label2,
535 struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg,
538 const struct got_error *err = NULL;
541 if (got_object_tree_entry_is_submodule(te1))
545 if (S_ISDIR(te1->mode))
546 err = diff_deleted_tree(&te1->id, f1, fd1, f2,
547 label1, repo, cb, cb_arg, diff_content);
550 err = diff_deleted_blob(&te1->id, f1, fd1,
551 f2, label1, te1->mode, repo, cb, cb_arg);
553 err = cb(cb_arg, NULL, NULL, NULL, NULL,
554 &te1->id, NULL, label1, NULL,
558 } else if (got_object_tree_entry_is_submodule(te2))
561 id_match = (got_object_id_cmp(&te1->id, &te2->id) == 0);
562 if (S_ISDIR(te1->mode) && S_ISDIR(te2->mode)) {
564 return diff_modified_tree(&te1->id, &te2->id, f1, f2,
565 fd1, fd2, label1, label2, repo, cb, cb_arg,
567 } else if ((S_ISREG(te1->mode) || S_ISLNK(te1->mode)) &&
568 (S_ISREG(te2->mode) || S_ISLNK(te2->mode))) {
570 ((te1->mode & (S_IFLNK | S_IXUSR))) !=
571 (te2->mode & (S_IFLNK | S_IXUSR))) {
573 return diff_modified_blob(&te1->id, &te2->id,
574 f1, f2, fd1, fd2, label1, label2,
575 te1->mode, te2->mode, repo, cb, cb_arg);
577 return cb(cb_arg, NULL, NULL, NULL, NULL,
578 &te1->id, &te2->id, label1, label2,
579 te1->mode, te2->mode, repo);
586 return diff_kind_mismatch(&te1->id, &te2->id, label1, label2, repo,
590 static const struct got_error *
591 diff_entry_new_old(struct got_tree_entry *te2,
592 struct got_tree_entry *te1, FILE *f1, FILE *f2, int fd2, const char *label2,
593 struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg,
596 if (te1 != NULL) /* handled by diff_entry_old_new() */
599 if (got_object_tree_entry_is_submodule(te2))
602 if (S_ISDIR(te2->mode))
603 return diff_added_tree(&te2->id, f1, f2, fd2, label2,
604 repo, cb, cb_arg, diff_content);
607 return diff_added_blob(&te2->id, f1, f2, fd2,
608 label2, te2->mode, repo, cb, cb_arg);
610 return cb(cb_arg, NULL, NULL, NULL, NULL, NULL, &te2->id,
611 NULL, label2, 0, te2->mode, repo);
615 diffstat_field_width(size_t *maxlen, int *add_cols, int *rm_cols, size_t len,
616 uint32_t add, uint32_t rm)
620 *maxlen = MAX(*maxlen, len);
624 *add_cols = MAX(*add_cols, d1);
628 *rm_cols = MAX(*rm_cols, d2);
631 const struct got_error *
632 got_diff_tree_compute_diffstat(void *arg, struct got_blob_object *blob1,
633 struct got_blob_object *blob2, FILE *f1, FILE *f2,
634 struct got_object_id *id1, struct got_object_id *id2,
635 const char *label1, const char *label2,
636 mode_t mode1, mode_t mode2, struct got_repository *repo)
638 const struct got_error *err = NULL;
639 struct got_diffreg_result *result = NULL;
640 struct diff_result *r;
641 struct got_diff_changed_path *change = NULL;
642 struct got_diffstat_cb_arg *a = arg;
643 struct got_pathlist_entry *pe;
647 path = strdup(label2 ? label2 : label1);
649 return got_error_from_errno("malloc");
651 change = malloc(sizeof(*change));
652 if (change == NULL) {
653 err = got_error_from_errno("malloc");
659 change->status = GOT_STATUS_NO_CHANGE;
661 change->status = GOT_STATUS_ADD;
662 else if (id2 == NULL)
663 change->status = GOT_STATUS_DELETE;
665 if (got_object_id_cmp(id1, id2) != 0)
666 change->status = GOT_STATUS_MODIFY;
667 else if (mode1 != mode2)
668 change->status = GOT_STATUS_MODE_CHANGE;
672 err = got_opentemp_truncate(f1);
677 err = got_opentemp_truncate(f2);
683 err = got_object_blob_dump_to_file(NULL, NULL, NULL, f1,
689 err = got_object_blob_dump_to_file(NULL, NULL, NULL, f2,
695 err = got_diffreg(&result, f1, f2, a->diff_algo, a->ignore_ws,
700 for (i = 0, r = result->result; i < r->chunks.len; ++i) {
701 int flags = (r->left->atomizer_flags | r->right->atomizer_flags);
702 int isbin = (flags & DIFF_ATOMIZER_FOUND_BINARY_DATA);
704 if (!isbin || a->force_text) {
705 struct diff_chunk *c;
708 c = diff_chunk_get(r, i);
709 clc = diff_chunk_get_left_count(c);
710 crc = diff_chunk_get_right_count(c);
714 else if (crc && !clc)
719 err = got_pathlist_append(a->paths, path, change);
723 pe = TAILQ_LAST(a->paths, got_pathlist_head);
724 diffstat_field_width(&a->max_path_len, &a->add_cols, &a->rm_cols,
725 pe->path_len, change->add, change->rm);
726 a->ins += change->add;
727 a->del += change->rm;
732 const struct got_error *free_err;
734 free_err = got_diffreg_result_free(result);
735 if (free_err && err == NULL)
745 const struct got_error *
746 got_diff_tree_collect_changed_paths(void *arg, struct got_blob_object *blob1,
747 struct got_blob_object *blob2, FILE *f1, FILE *f2,
748 struct got_object_id *id1, struct got_object_id *id2,
749 const char *label1, const char *label2,
750 mode_t mode1, mode_t mode2, struct got_repository *repo)
752 const struct got_error *err = NULL;
753 struct got_pathlist_head *paths = arg;
754 struct got_diff_changed_path *change = NULL;
757 path = strdup(label2 ? label2 : label1);
759 return got_error_from_errno("malloc");
761 change = malloc(sizeof(*change));
762 if (change == NULL) {
763 err = got_error_from_errno("malloc");
767 change->status = GOT_STATUS_NO_CHANGE;
769 change->status = GOT_STATUS_ADD;
770 else if (id2 == NULL)
771 change->status = GOT_STATUS_DELETE;
773 if (got_object_id_cmp(id1, id2) != 0)
774 change->status = GOT_STATUS_MODIFY;
775 else if (mode1 != mode2)
776 change->status = GOT_STATUS_MODE_CHANGE;
779 err = got_pathlist_append(paths, path, change);
788 const struct got_error *
789 got_diff_tree(struct got_tree_object *tree1, struct got_tree_object *tree2,
790 FILE *f1, FILE *f2, int fd1, int fd2,
791 const char *label1, const char *label2,
792 struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg,
795 const struct got_error *err = NULL;
796 struct got_tree_entry *te1 = NULL;
797 struct got_tree_entry *te2 = NULL;
798 char *l1 = NULL, *l2 = NULL;
799 int tidx1 = 0, tidx2 = 0;
802 te1 = got_object_tree_get_entry(tree1, 0);
803 if (te1 && asprintf(&l1, "%s%s%s", label1, label1[0] ? "/" : "",
805 return got_error_from_errno("asprintf");
808 te2 = got_object_tree_get_entry(tree2, 0);
809 if (te2 && asprintf(&l2, "%s%s%s", label2, label2[0] ? "/" : "",
811 return got_error_from_errno("asprintf");
816 struct got_tree_entry *te = NULL;
818 te = got_object_tree_find_entry(tree2,
823 if (te && asprintf(&l2, "%s%s%s", label2,
824 label2[0] ? "/" : "", te->name) == -1)
826 got_error_from_errno("asprintf");
828 err = diff_entry_old_new(te1, te, f1, f2, fd1, fd2,
829 l1, l2, repo, cb, cb_arg, diff_content);
835 struct got_tree_entry *te = NULL;
837 te = got_object_tree_find_entry(tree1,
841 if (asprintf(&l2, "%s%s%s", label2,
842 label2[0] ? "/" : "", te->name) == -1)
844 got_error_from_errno("asprintf");
846 if (asprintf(&l2, "%s%s%s", label2,
847 label2[0] ? "/" : "", te2->name) == -1)
849 got_error_from_errno("asprintf");
851 err = diff_entry_new_old(te2, te, f1, f2, fd2, l2,
852 repo, cb, cb_arg, diff_content);
861 te1 = got_object_tree_get_entry(tree1, tidx1);
863 asprintf(&l1, "%s%s%s", label1,
864 label1[0] ? "/" : "", te1->name) == -1)
865 return got_error_from_errno("asprintf");
871 te2 = got_object_tree_get_entry(tree2, tidx2);
873 asprintf(&l2, "%s%s%s", label2,
874 label2[0] ? "/" : "", te2->name) == -1)
875 return got_error_from_errno("asprintf");
877 } while (te1 || te2);
882 const struct got_error *
883 got_diff_objects_as_blobs(struct got_diff_line **lines, size_t *nlines,
884 FILE *f1, FILE *f2, int fd1, int fd2,
885 struct got_object_id *id1, struct got_object_id *id2,
886 const char *label1, const char *label2,
887 enum got_diff_algorithm diff_algo, int diff_context,
888 int ignore_whitespace, int force_text_diff,
889 struct got_repository *repo, FILE *outfile)
891 const struct got_error *err;
892 struct got_blob_object *blob1 = NULL, *blob2 = NULL;
894 if (id1 == NULL && id2 == NULL)
895 return got_error(GOT_ERR_NO_OBJ);
898 err = got_object_open_as_blob(&blob1, repo, id1, 8192, fd1);
903 err = got_object_open_as_blob(&blob2, repo, id2, 8192, fd2);
907 err = got_diff_blob(lines, nlines, blob1, blob2, f1, f2, label1, label2,
908 diff_algo, diff_context, ignore_whitespace, force_text_diff,
912 got_object_blob_close(blob1);
914 got_object_blob_close(blob2);
918 static const struct got_error *
919 diff_paths(struct got_tree_object *tree1, struct got_tree_object *tree2,
920 FILE *f1, FILE *f2, int fd1, int fd2, struct got_pathlist_head *paths,
921 struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg)
923 const struct got_error *err = NULL;
924 struct got_pathlist_entry *pe;
925 struct got_object_id *id1 = NULL, *id2 = NULL;
926 struct got_tree_object *subtree1 = NULL, *subtree2 = NULL;
927 struct got_blob_object *blob1 = NULL, *blob2 = NULL;
929 TAILQ_FOREACH(pe, paths, entry) {
930 int type1 = GOT_OBJ_TYPE_ANY, type2 = GOT_OBJ_TYPE_ANY;
931 mode_t mode1 = 0, mode2 = 0;
938 got_object_tree_close(subtree1);
942 got_object_tree_close(subtree2);
946 got_object_blob_close(blob1);
950 got_object_blob_close(blob2);
954 err = got_object_tree_find_path(&id1, &mode1, repo, tree1,
956 if (err && err->code != GOT_ERR_NO_TREE_ENTRY)
958 err = got_object_tree_find_path(&id2, &mode2, repo, tree2,
960 if (err && err->code != GOT_ERR_NO_TREE_ENTRY)
962 if (id1 == NULL && id2 == NULL) {
963 err = got_error_path(pe->path, GOT_ERR_NO_TREE_ENTRY);
967 err = got_object_get_type(&type1, repo, id1);
972 err = got_object_get_type(&type2, repo, id2);
976 if (type1 == GOT_OBJ_TYPE_ANY &&
977 type2 == GOT_OBJ_TYPE_ANY) {
978 err = got_error_path(pe->path, GOT_ERR_NO_OBJ);
980 } else if (type1 != GOT_OBJ_TYPE_ANY &&
981 type2 != GOT_OBJ_TYPE_ANY && type1 != type2) {
982 err = got_error(GOT_ERR_OBJ_TYPE);
986 if (type1 == GOT_OBJ_TYPE_BLOB ||
987 type2 == GOT_OBJ_TYPE_BLOB) {
989 err = got_object_open_as_blob(&blob1, repo,
995 err = got_object_open_as_blob(&blob2, repo,
1000 err = cb(cb_arg, blob1, blob2, f1, f2, id1, id2,
1001 id1 ? pe->path : "/dev/null",
1002 id2 ? pe->path : "/dev/null",
1003 mode1, mode2, repo);
1006 } else if (type1 == GOT_OBJ_TYPE_TREE ||
1007 type2 == GOT_OBJ_TYPE_TREE) {
1009 err = got_object_open_as_tree(&subtree1, repo,
1015 err = got_object_open_as_tree(&subtree2, repo,
1020 err = got_diff_tree(subtree1, subtree2, f1, f2,
1022 id1 ? pe->path : "/dev/null",
1023 id2 ? pe->path : "/dev/null",
1024 repo, cb, cb_arg, 1);
1028 err = got_error(GOT_ERR_OBJ_TYPE);
1036 got_object_tree_close(subtree1);
1038 got_object_tree_close(subtree2);
1040 got_object_blob_close(blob1);
1042 got_object_blob_close(blob2);
1046 static const struct got_error *
1047 show_object_id(struct got_diff_line **lines, size_t *nlines,
1048 const char *obj_typestr, int ch, const char *id_str, FILE *outfile)
1050 const struct got_error *err;
1054 n = fprintf(outfile, "%s %c %s\n", obj_typestr, ch, id_str);
1056 return got_error_from_errno("fprintf");
1058 if (lines != NULL && *lines != NULL) {
1060 err = add_line_metadata(lines, nlines, 0,
1061 GOT_DIFF_LINE_META);
1065 outoff = (*lines)[*nlines - 1].offset;
1068 err = add_line_metadata(lines, nlines, outoff,
1069 GOT_DIFF_LINE_META);
1077 static const struct got_error *
1078 diff_objects_as_trees(struct got_diff_line **lines, size_t *nlines,
1079 FILE *f1, FILE *f2, int fd1, int fd2,
1080 struct got_object_id *id1, struct got_object_id *id2,
1081 struct got_pathlist_head *paths, const char *label1, const char *label2,
1082 int diff_context, int ignore_whitespace, int force_text_diff,
1083 struct got_repository *repo, FILE *outfile,
1084 enum got_diff_algorithm diff_algo)
1086 const struct got_error *err;
1087 struct got_tree_object *tree1 = NULL, *tree2 = NULL;
1088 struct got_diff_blob_output_unidiff_arg arg;
1089 int want_linemeta = (lines != NULL && *lines != NULL);
1091 if (id1 == NULL && id2 == NULL)
1092 return got_error(GOT_ERR_NO_OBJ);
1095 err = got_object_open_as_tree(&tree1, repo, id1);
1100 err = got_object_open_as_tree(&tree2, repo, id2);
1105 arg.diff_algo = diff_algo;
1106 arg.diff_context = diff_context;
1107 arg.ignore_whitespace = ignore_whitespace;
1108 arg.force_text_diff = force_text_diff;
1109 arg.outfile = outfile;
1110 if (want_linemeta) {
1112 arg.nlines = *nlines;
1117 if (paths == NULL || TAILQ_EMPTY(paths)) {
1118 err = got_diff_tree(tree1, tree2, f1, f2, fd1, fd2,
1119 label1, label2, repo,
1120 got_diff_blob_output_unidiff, &arg, 1);
1122 err = diff_paths(tree1, tree2, f1, f2, fd1, fd2, paths, repo,
1123 got_diff_blob_output_unidiff, &arg);
1125 if (want_linemeta) {
1126 *lines = arg.lines; /* was likely re-allocated */
1127 *nlines = arg.nlines;
1131 got_object_tree_close(tree1);
1133 got_object_tree_close(tree2);
1137 const struct got_error *
1138 got_diff_objects_as_trees(struct got_diff_line **lines, size_t *nlines,
1139 FILE *f1, FILE *f2, int fd1, int fd2,
1140 struct got_object_id *id1, struct got_object_id *id2,
1141 struct got_pathlist_head *paths, const char *label1, const char *label2,
1142 enum got_diff_algorithm diff_algo, int diff_context, int ignore_whitespace,
1143 int force_text_diff, struct got_repository *repo, FILE *outfile)
1145 const struct got_error *err;
1148 if (id1 == NULL && id2 == NULL)
1149 return got_error(GOT_ERR_NO_OBJ);
1152 err = got_object_id_str(&idstr, id1);
1155 err = show_object_id(lines, nlines, "tree", '-', idstr, outfile);
1161 err = show_object_id(lines, nlines, "tree", '-', "/dev/null",
1168 err = got_object_id_str(&idstr, id2);
1171 err = show_object_id(lines, nlines, "tree", '+', idstr, outfile);
1177 err = show_object_id(lines, nlines, "tree", '+', "/dev/null",
1183 err = diff_objects_as_trees(lines, nlines, f1, f2, fd1, fd2, id1, id2,
1184 paths, label1, label2, diff_context, ignore_whitespace,
1185 force_text_diff, repo, outfile, diff_algo);
1191 const struct got_error *
1192 got_diff_objects_as_commits(struct got_diff_line **lines, size_t *nlines,
1193 FILE *f1, FILE *f2, int fd1, int fd2,
1194 struct got_object_id *id1, struct got_object_id *id2,
1195 struct got_pathlist_head *paths, enum got_diff_algorithm diff_algo,
1196 int diff_context, int ignore_whitespace, int force_text_diff,
1197 struct got_repository *repo, FILE *outfile)
1199 const struct got_error *err;
1200 struct got_commit_object *commit1 = NULL, *commit2 = NULL;
1204 return got_error(GOT_ERR_NO_OBJ);
1207 err = got_object_open_as_commit(&commit1, repo, id1);
1210 err = got_object_id_str(&idstr, id1);
1213 err = show_object_id(lines, nlines, "commit", '-', idstr,
1220 err = show_object_id(lines, nlines, "commit", '-', "/dev/null",
1226 err = got_object_open_as_commit(&commit2, repo, id2);
1230 err = got_object_id_str(&idstr, id2);
1233 err = show_object_id(lines, nlines, "commit", '+', idstr, outfile);
1237 err = diff_objects_as_trees(lines, nlines, f1, f2, fd1, fd2,
1238 commit1 ? got_object_commit_get_tree_id(commit1) : NULL,
1239 got_object_commit_get_tree_id(commit2), paths, "", "",
1240 diff_context, ignore_whitespace, force_text_diff, repo, outfile,
1244 got_object_commit_close(commit1);
1246 got_object_commit_close(commit2);
1251 const struct got_error *
1252 got_diff_files(struct got_diffreg_result **resultp,
1253 FILE *f1, int f1_exists, const char *label1, FILE *f2, int f2_exists,
1254 const char *label2, int diff_context, int ignore_whitespace,
1255 int force_text_diff, FILE *outfile, enum got_diff_algorithm diff_algo)
1257 const struct got_error *err = NULL;
1258 struct got_diffreg_result *diffreg_result = NULL;
1264 fprintf(outfile, "file - %s\n",
1265 f1_exists ? label1 : "/dev/null");
1266 fprintf(outfile, "file + %s\n",
1267 f2_exists ? label2 : "/dev/null");
1270 err = got_diffreg(&diffreg_result, f1, f2, diff_algo,
1271 ignore_whitespace, force_text_diff);
1276 err = got_diffreg_output(NULL, NULL, diffreg_result,
1277 f1_exists, f2_exists, label1, label2,
1278 GOT_DIFF_OUTPUT_UNIDIFF, diff_context, outfile);
1284 if (resultp && err == NULL)
1285 *resultp = diffreg_result;
1286 else if (diffreg_result) {
1287 const struct got_error *free_err;
1288 free_err = got_diffreg_result_free(diffreg_result);
1289 if (free_err && err == NULL)