Blob


1 /*
2 * Copyright (c) 2017 Stefan Sperling <stsp@openbsd.org>
3 *
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.
7 *
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.
15 */
17 #include <sys/queue.h>
18 #include <sys/stat.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <limits.h>
24 #include <sha1.h>
25 #include <zlib.h>
27 #include "got_object.h"
28 #include "got_repository.h"
29 #include "got_error.h"
30 #include "got_diff.h"
31 #include "got_path.h"
32 #include "got_cancel.h"
33 #include "got_worktree.h"
35 #include "got_lib_diff.h"
36 #include "got_lib_delta.h"
37 #include "got_lib_inflate.h"
38 #include "got_lib_object.h"
40 static const struct got_error *
41 add_line_offset(off_t **line_offsets, size_t *nlines, off_t off)
42 {
43 off_t *p;
45 p = reallocarray(*line_offsets, *nlines + 1, sizeof(off_t));
46 if (p == NULL)
47 return got_error_from_errno("reallocarray");
48 *line_offsets = p;
49 (*line_offsets)[*nlines] = off;
50 (*nlines)++;
51 return NULL;
52 }
54 static const struct got_error *
55 reset_file(FILE *f)
56 {
57 if (fpurge(f) == EOF)
58 return got_error_from_errno("fpurge");
59 if (ftruncate(fileno(f), 0L) == -1)
60 return got_error_from_errno("ftruncate");
61 if (fseeko(f, 0L, SEEK_SET) == -1)
62 return got_error_from_errno("fseeko");
63 return NULL;
64 }
66 static const struct got_error *
67 diff_blobs(off_t **line_offsets, size_t *nlines,
68 struct got_diffreg_result **resultp, struct got_blob_object *blob1,
69 struct got_blob_object *blob2, FILE *f1, FILE *f2,
70 const char *label1, const char *label2, mode_t mode1, mode_t mode2,
71 int diff_context, int ignore_whitespace, int force_text_diff, FILE *outfile)
72 {
73 const struct got_error *err = NULL, *free_err;
74 char hex1[SHA1_DIGEST_STRING_LENGTH];
75 char hex2[SHA1_DIGEST_STRING_LENGTH];
76 char *idstr1 = NULL, *idstr2 = NULL;
77 off_t size1, size2;
78 struct got_diffreg_result *result;
79 off_t outoff = 0;
80 int n;
82 if (line_offsets && *line_offsets && *nlines > 0)
83 outoff = (*line_offsets)[*nlines - 1];
84 else if (line_offsets) {
85 err = add_line_offset(line_offsets, nlines, 0);
86 if (err)
87 goto done;
88 }
90 if (resultp)
91 *resultp = NULL;
93 if (f1) {
94 err = reset_file(f1);
95 if (err)
96 goto done;
97 }
98 if (f2) {
99 err = reset_file(f2);
100 if (err)
101 goto done;
104 size1 = 0;
105 if (blob1) {
106 idstr1 = got_object_blob_id_str(blob1, hex1, sizeof(hex1));
107 err = got_object_blob_dump_to_file(&size1, NULL, NULL, f1,
108 blob1);
109 if (err)
110 goto done;
111 } else
112 idstr1 = "/dev/null";
114 size2 = 0;
115 if (blob2) {
116 idstr2 = got_object_blob_id_str(blob2, hex2, sizeof(hex2));
117 err = got_object_blob_dump_to_file(&size2, NULL, NULL, f2,
118 blob2);
119 if (err)
120 goto done;
121 } else
122 idstr2 = "/dev/null";
124 if (outfile) {
125 char *modestr1 = NULL, *modestr2 = NULL;
126 int modebits;
127 if (mode1 && mode1 != mode2) {
128 if (S_ISLNK(mode1))
129 modebits = S_IFLNK;
130 else
131 modebits = (S_IRWXU | S_IRWXG | S_IRWXO);
132 if (asprintf(&modestr1, " (mode %o)",
133 mode1 & modebits) == -1) {
134 err = got_error_from_errno("asprintf");
135 goto done;
138 if (mode2 && mode1 != mode2) {
139 if (S_ISLNK(mode2))
140 modebits = S_IFLNK;
141 else
142 modebits = (S_IRWXU | S_IRWXG | S_IRWXO);
143 if (asprintf(&modestr2, " (mode %o)",
144 mode2 & modebits) == -1) {
145 err = got_error_from_errno("asprintf");
146 goto done;
149 n = fprintf(outfile, "blob - %s%s\n", idstr1,
150 modestr1 ? modestr1 : "");
151 if (n < 0)
152 goto done;
153 outoff += n;
154 if (line_offsets) {
155 err = add_line_offset(line_offsets, nlines, outoff);
156 if (err)
157 goto done;
160 n = fprintf(outfile, "blob + %s%s\n", idstr2,
161 modestr2 ? modestr2 : "");
162 if (n < 0)
163 goto done;
164 outoff += n;
165 if (line_offsets) {
166 err = add_line_offset(line_offsets, nlines, outoff);
167 if (err)
168 goto done;
171 free(modestr1);
172 free(modestr2);
174 err = got_diffreg(&result, f1, f2, GOT_DIFF_ALGORITHM_PATIENCE,
175 ignore_whitespace, force_text_diff);
176 if (err)
177 goto done;
179 if (outfile) {
180 err = got_diffreg_output(line_offsets, nlines, result,
181 blob1 != NULL, blob2 != NULL,
182 label1 ? label1 : idstr1,
183 label2 ? label2 : idstr2,
184 GOT_DIFF_OUTPUT_UNIDIFF, diff_context, outfile);
185 if (err)
186 goto done;
189 if (resultp && err == NULL)
190 *resultp = result;
191 else {
192 free_err = got_diffreg_result_free(result);
193 if (free_err && err == NULL)
194 err = free_err;
196 done:
197 return err;
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->line_offsets, &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);
214 const struct got_error *
215 got_diff_blob(off_t **line_offsets, 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 int diff_context, int ignore_whitespace, int force_text_diff,
219 FILE *outfile)
221 return diff_blobs(line_offsets, nlines, NULL, blob1, blob2, f1, f2,
222 label1, label2, 0, 0, diff_context, ignore_whitespace,
223 force_text_diff, outfile);
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, size_t size2, const char *label2, int diff_context,
230 int ignore_whitespace, int force_text_diff, FILE *outfile)
232 const struct got_error *err = NULL, *free_err;
233 char hex1[SHA1_DIGEST_STRING_LENGTH];
234 char *idstr1 = NULL;
235 struct got_diffreg_result *result = NULL;
237 if (resultp)
238 *resultp = NULL;
240 if (blob1)
241 idstr1 = got_object_blob_id_str(blob1, hex1, sizeof(hex1));
242 else
243 idstr1 = "/dev/null";
245 if (outfile) {
246 fprintf(outfile, "blob - %s\n", label1 ? label1 : idstr1);
247 fprintf(outfile, "file + %s\n",
248 f2 == NULL ? "/dev/null" : label2);
251 err = got_diffreg(&result, f1, f2, GOT_DIFF_ALGORITHM_PATIENCE,
252 ignore_whitespace, force_text_diff);
253 if (err)
254 goto done;
256 if (outfile) {
257 err = got_diffreg_output(NULL, NULL, result,
258 f1 != NULL, f2 != NULL,
259 label2, /* show local file's path, not a blob ID */
260 label2, GOT_DIFF_OUTPUT_UNIDIFF,
261 diff_context, outfile);
262 if (err)
263 goto done;
266 if (resultp && err == NULL)
267 *resultp = result;
268 else if (result) {
269 free_err = got_diffreg_result_free(result);
270 if (free_err && err == NULL)
271 err = free_err;
273 done:
274 return err;
277 const struct got_error *
278 got_diff_blob_file(struct got_blob_object *blob1, FILE *f1, off_t size1,
279 const char *label1, FILE *f2, size_t size2, const char *label2,
280 int diff_context, int ignore_whitespace, int force_text_diff, FILE *outfile)
282 return diff_blob_file(NULL, blob1, f1, size1, label1, f2, size2, label2,
283 diff_context, ignore_whitespace, force_text_diff, outfile);
286 static const struct got_error *
287 diff_added_blob(struct got_object_id *id, FILE *f, const char *label,
288 mode_t mode, struct got_repository *repo,
289 got_diff_blob_cb cb, void *cb_arg)
291 const struct got_error *err;
292 struct got_blob_object *blob = NULL;
293 struct got_object *obj = NULL;
295 err = got_object_open(&obj, repo, id);
296 if (err)
297 return err;
299 err = got_object_blob_open(&blob, repo, obj, 8192);
300 if (err)
301 goto done;
302 err = cb(cb_arg, NULL, blob, NULL, f, NULL, id,
303 NULL, label, 0, mode, repo);
304 done:
305 got_object_close(obj);
306 if (blob)
307 got_object_blob_close(blob);
308 return err;
311 static const struct got_error *
312 diff_modified_blob(struct got_object_id *id1, struct got_object_id *id2,
313 FILE *f1, FILE *f2, const char *label1, const char *label2,
314 mode_t mode1, mode_t mode2, struct got_repository *repo,
315 got_diff_blob_cb cb, void *cb_arg)
317 const struct got_error *err;
318 struct got_object *obj1 = NULL;
319 struct got_object *obj2 = NULL;
320 struct got_blob_object *blob1 = NULL;
321 struct got_blob_object *blob2 = NULL;
323 err = got_object_open(&obj1, repo, id1);
324 if (err)
325 return err;
326 if (obj1->type != GOT_OBJ_TYPE_BLOB) {
327 err = got_error(GOT_ERR_OBJ_TYPE);
328 goto done;
331 err = got_object_open(&obj2, repo, id2);
332 if (err)
333 goto done;
334 if (obj2->type != GOT_OBJ_TYPE_BLOB) {
335 err = got_error(GOT_ERR_BAD_OBJ_DATA);
336 goto done;
339 err = got_object_blob_open(&blob1, repo, obj1, 8192);
340 if (err)
341 goto done;
343 err = got_object_blob_open(&blob2, repo, obj2, 8192);
344 if (err)
345 goto done;
347 err = cb(cb_arg, blob1, blob2, f1, f2, id1, id2, label1, label2,
348 mode1, mode2, repo);
349 done:
350 if (obj1)
351 got_object_close(obj1);
352 if (obj2)
353 got_object_close(obj2);
354 if (blob1)
355 got_object_blob_close(blob1);
356 if (blob2)
357 got_object_blob_close(blob2);
358 return err;
361 static const struct got_error *
362 diff_deleted_blob(struct got_object_id *id, FILE *f, const char *label,
363 mode_t mode, struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg)
365 const struct got_error *err;
366 struct got_blob_object *blob = NULL;
367 struct got_object *obj = NULL;
369 err = got_object_open(&obj, repo, id);
370 if (err)
371 return err;
373 err = got_object_blob_open(&blob, repo, obj, 8192);
374 if (err)
375 goto done;
376 err = cb(cb_arg, blob, NULL, f, NULL, id, NULL, label, NULL,
377 mode, 0, repo);
378 done:
379 got_object_close(obj);
380 if (blob)
381 got_object_blob_close(blob);
382 return err;
385 static const struct got_error *
386 diff_added_tree(struct got_object_id *id, FILE *f, const char *label,
387 struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg,
388 int diff_content)
390 const struct got_error *err = NULL;
391 struct got_object *treeobj = NULL;
392 struct got_tree_object *tree = NULL;
394 err = got_object_open(&treeobj, repo, id);
395 if (err)
396 goto done;
398 if (treeobj->type != GOT_OBJ_TYPE_TREE) {
399 err = got_error(GOT_ERR_OBJ_TYPE);
400 goto done;
403 err = got_object_tree_open(&tree, repo, treeobj);
404 if (err)
405 goto done;
407 err = got_diff_tree(NULL, tree, NULL, f, NULL, label,
408 repo, cb, cb_arg, diff_content);
409 done:
410 if (tree)
411 got_object_tree_close(tree);
412 if (treeobj)
413 got_object_close(treeobj);
414 return err;
417 static const struct got_error *
418 diff_modified_tree(struct got_object_id *id1, struct got_object_id *id2,
419 FILE *f1, FILE *f2, const char *label1, const char *label2,
420 struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg,
421 int diff_content)
423 const struct got_error *err;
424 struct got_object *treeobj1 = NULL;
425 struct got_object *treeobj2 = NULL;
426 struct got_tree_object *tree1 = NULL;
427 struct got_tree_object *tree2 = NULL;
429 err = got_object_open(&treeobj1, repo, id1);
430 if (err)
431 goto done;
433 if (treeobj1->type != GOT_OBJ_TYPE_TREE) {
434 err = got_error(GOT_ERR_OBJ_TYPE);
435 goto done;
438 err = got_object_open(&treeobj2, repo, id2);
439 if (err)
440 goto done;
442 if (treeobj2->type != GOT_OBJ_TYPE_TREE) {
443 err = got_error(GOT_ERR_OBJ_TYPE);
444 goto done;
447 err = got_object_tree_open(&tree1, repo, treeobj1);
448 if (err)
449 goto done;
451 err = got_object_tree_open(&tree2, repo, treeobj2);
452 if (err)
453 goto done;
455 err = got_diff_tree(tree1, tree2, f1, f2, label1, label2,
456 repo, cb, cb_arg, diff_content);
458 done:
459 if (tree1)
460 got_object_tree_close(tree1);
461 if (tree2)
462 got_object_tree_close(tree2);
463 if (treeobj1)
464 got_object_close(treeobj1);
465 if (treeobj2)
466 got_object_close(treeobj2);
467 return err;
470 static const struct got_error *
471 diff_deleted_tree(struct got_object_id *id, FILE *f, const char *label,
472 struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg,
473 int diff_content)
475 const struct got_error *err;
476 struct got_object *treeobj = NULL;
477 struct got_tree_object *tree = NULL;
479 err = got_object_open(&treeobj, repo, id);
480 if (err)
481 goto done;
483 if (treeobj->type != GOT_OBJ_TYPE_TREE) {
484 err = got_error(GOT_ERR_OBJ_TYPE);
485 goto done;
488 err = got_object_tree_open(&tree, repo, treeobj);
489 if (err)
490 goto done;
492 err = got_diff_tree(tree, NULL, f, NULL, label, NULL,
493 repo, cb, cb_arg, diff_content);
494 done:
495 if (tree)
496 got_object_tree_close(tree);
497 if (treeobj)
498 got_object_close(treeobj);
499 return err;
502 static const struct got_error *
503 diff_kind_mismatch(struct got_object_id *id1, struct got_object_id *id2,
504 const char *label1, const char *label2, struct got_repository *repo,
505 got_diff_blob_cb cb, void *cb_arg)
507 /* XXX TODO */
508 return NULL;
511 static const struct got_error *
512 diff_entry_old_new(struct got_tree_entry *te1, struct got_tree_entry *te2,
513 FILE *f1, FILE *f2, const char *label1, const char *label2,
514 struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg,
515 int diff_content)
517 const struct got_error *err = NULL;
518 int id_match;
520 if (got_object_tree_entry_is_submodule(te1))
521 return NULL;
523 if (te2 == NULL) {
524 if (S_ISDIR(te1->mode))
525 err = diff_deleted_tree(&te1->id, f1, label1, repo,
526 cb, cb_arg, diff_content);
527 else {
528 if (diff_content)
529 err = diff_deleted_blob(&te1->id, f1, label1,
530 te1->mode, repo, cb, cb_arg);
531 else
532 err = cb(cb_arg, NULL, NULL, NULL, NULL,
533 &te1->id, NULL, label1, NULL,
534 te1->mode, 0, repo);
536 return err;
537 } else if (got_object_tree_entry_is_submodule(te2))
538 return NULL;
540 id_match = (got_object_id_cmp(&te1->id, &te2->id) == 0);
541 if (S_ISDIR(te1->mode) && S_ISDIR(te2->mode)) {
542 if (!id_match)
543 return diff_modified_tree(&te1->id, &te2->id, f1, f2,
544 label1, label2, repo, cb, cb_arg, diff_content);
545 } else if ((S_ISREG(te1->mode) || S_ISLNK(te1->mode)) &&
546 (S_ISREG(te2->mode) || S_ISLNK(te2->mode))) {
547 if (!id_match ||
548 ((te1->mode & (S_IFLNK | S_IXUSR))) !=
549 (te2->mode & (S_IFLNK | S_IXUSR))) {
550 if (diff_content)
551 return diff_modified_blob(&te1->id, &te2->id,
552 f1, f2, label1, label2, te1->mode,
553 te2->mode, repo, cb, cb_arg);
554 else
555 return cb(cb_arg, NULL, NULL, NULL, NULL,
556 &te1->id, &te2->id, label1, label2,
557 te1->mode, te2->mode, repo);
561 if (id_match)
562 return NULL;
564 return diff_kind_mismatch(&te1->id, &te2->id, label1, label2, repo,
565 cb, cb_arg);
568 static const struct got_error *
569 diff_entry_new_old(struct got_tree_entry *te2,
570 struct got_tree_entry *te1, FILE *f2, const char *label2,
571 struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg,
572 int diff_content)
574 if (te1 != NULL) /* handled by diff_entry_old_new() */
575 return NULL;
577 if (got_object_tree_entry_is_submodule(te2))
578 return NULL;
580 if (S_ISDIR(te2->mode))
581 return diff_added_tree(&te2->id, f2, label2,
582 repo, cb, cb_arg, diff_content);
584 if (diff_content)
585 return diff_added_blob(&te2->id, f2, label2, te2->mode,
586 repo, cb, cb_arg);
588 return cb(cb_arg, NULL, NULL, NULL, NULL, NULL, &te2->id,
589 NULL, label2, 0, te2->mode, repo);
592 const struct got_error *
593 got_diff_tree_collect_changed_paths(void *arg, struct got_blob_object *blob1,
594 struct got_blob_object *blob2, FILE *f1, FILE *f2,
595 struct got_object_id *id1, struct got_object_id *id2,
596 const char *label1, const char *label2,
597 mode_t mode1, mode_t mode2, struct got_repository *repo)
599 const struct got_error *err = NULL;
600 struct got_pathlist_head *paths = arg;
601 struct got_diff_changed_path *change = NULL;
602 char *path = NULL;
604 path = strdup(label2 ? label2 : label1);
605 if (path == NULL)
606 return got_error_from_errno("malloc");
608 change = malloc(sizeof(*change));
609 if (change == NULL) {
610 err = got_error_from_errno("malloc");
611 goto done;
614 change->status = GOT_STATUS_NO_CHANGE;
615 if (id1 == NULL)
616 change->status = GOT_STATUS_ADD;
617 else if (id2 == NULL)
618 change->status = GOT_STATUS_DELETE;
619 else {
620 if (got_object_id_cmp(id1, id2) != 0)
621 change->status = GOT_STATUS_MODIFY;
622 else if (mode1 != mode2)
623 change->status = GOT_STATUS_MODE_CHANGE;
626 err = got_pathlist_append(paths, path, change);
627 done:
628 if (err) {
629 free(path);
630 free(change);
632 return err;
635 const struct got_error *
636 got_diff_tree(struct got_tree_object *tree1, struct got_tree_object *tree2,
637 FILE *f1, FILE *f2, const char *label1, const char *label2,
638 struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg,
639 int diff_content)
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;
647 if (tree1) {
648 te1 = got_object_tree_get_entry(tree1, 0);
649 if (te1 && asprintf(&l1, "%s%s%s", label1, label1[0] ? "/" : "",
650 te1->name) == -1)
651 return got_error_from_errno("asprintf");
653 if (tree2) {
654 te2 = got_object_tree_get_entry(tree2, 0);
655 if (te2 && asprintf(&l2, "%s%s%s", label2, label2[0] ? "/" : "",
656 te2->name) == -1)
657 return got_error_from_errno("asprintf");
660 do {
661 if (te1) {
662 struct got_tree_entry *te = NULL;
663 if (tree2)
664 te = got_object_tree_find_entry(tree2,
665 te1->name);
666 if (te) {
667 free(l2);
668 l2 = NULL;
669 if (te && asprintf(&l2, "%s%s%s", label2,
670 label2[0] ? "/" : "", te->name) == -1)
671 return
672 got_error_from_errno("asprintf");
674 err = diff_entry_old_new(te1, te, f1, f2, l1, l2,
675 repo, cb, cb_arg, diff_content);
676 if (err)
677 break;
680 if (te2) {
681 struct got_tree_entry *te = NULL;
682 if (tree1)
683 te = got_object_tree_find_entry(tree1,
684 te2->name);
685 free(l2);
686 if (te) {
687 if (asprintf(&l2, "%s%s%s", label2,
688 label2[0] ? "/" : "", te->name) == -1)
689 return
690 got_error_from_errno("asprintf");
691 } else {
692 if (asprintf(&l2, "%s%s%s", label2,
693 label2[0] ? "/" : "", te2->name) == -1)
694 return
695 got_error_from_errno("asprintf");
697 err = diff_entry_new_old(te2, te, f2, l2, repo,
698 cb, cb_arg, diff_content);
699 if (err)
700 break;
703 free(l1);
704 l1 = NULL;
705 if (te1) {
706 tidx1++;
707 te1 = got_object_tree_get_entry(tree1, tidx1);
708 if (te1 &&
709 asprintf(&l1, "%s%s%s", label1,
710 label1[0] ? "/" : "", te1->name) == -1)
711 return got_error_from_errno("asprintf");
713 free(l2);
714 l2 = NULL;
715 if (te2) {
716 tidx2++;
717 te2 = got_object_tree_get_entry(tree2, tidx2);
718 if (te2 &&
719 asprintf(&l2, "%s%s%s", label2,
720 label2[0] ? "/" : "", te2->name) == -1)
721 return got_error_from_errno("asprintf");
723 } while (te1 || te2);
725 return err;
728 const struct got_error *
729 got_diff_objects_as_blobs(off_t **line_offsets, size_t *nlines,
730 FILE *f1, FILE *f2, struct got_object_id *id1, struct got_object_id *id2,
731 const char *label1, const char *label2, int diff_context,
732 int ignore_whitespace, int force_text_diff,
733 struct got_repository *repo, FILE *outfile)
735 const struct got_error *err;
736 struct got_blob_object *blob1 = NULL, *blob2 = NULL;
738 if (id1 == NULL && id2 == NULL)
739 return got_error(GOT_ERR_NO_OBJ);
741 if (id1) {
742 err = got_object_open_as_blob(&blob1, repo, id1, 8192);
743 if (err)
744 goto done;
746 if (id2) {
747 err = got_object_open_as_blob(&blob2, repo, id2, 8192);
748 if (err)
749 goto done;
751 err = got_diff_blob(line_offsets, nlines, blob1, blob2, f1, f2,
752 label1, label2, diff_context, ignore_whitespace, force_text_diff,
753 outfile);
754 done:
755 if (blob1)
756 got_object_blob_close(blob1);
757 if (blob2)
758 got_object_blob_close(blob2);
759 return err;
762 static const struct got_error *
763 diff_paths(struct got_tree_object *tree1, struct got_tree_object *tree2,
764 FILE *f1, FILE *f2, struct got_pathlist_head *paths,
765 struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg)
767 const struct got_error *err = NULL;
768 struct got_pathlist_entry *pe;
769 struct got_object_id *id1 = NULL, *id2 = NULL;
770 struct got_tree_object *subtree1 = NULL, *subtree2 = NULL;
771 struct got_blob_object *blob1 = NULL, *blob2 = NULL;
773 TAILQ_FOREACH(pe, paths, entry) {
774 int type1 = GOT_OBJ_TYPE_ANY, type2 = GOT_OBJ_TYPE_ANY;
775 mode_t mode1 = 0, mode2 = 0;
777 free(id1);
778 id1 = NULL;
779 free(id2);
780 id2 = NULL;
781 if (subtree1) {
782 got_object_tree_close(subtree1);
783 subtree1 = NULL;
785 if (subtree2) {
786 got_object_tree_close(subtree2);
787 subtree2 = NULL;
789 if (blob1) {
790 got_object_blob_close(blob1);
791 blob1 = NULL;
793 if (blob2) {
794 got_object_blob_close(blob2);
795 blob2 = NULL;
798 err = got_object_tree_find_path(&id1, &mode1, repo, tree1,
799 pe->path);
800 if (err && err->code != GOT_ERR_NO_TREE_ENTRY)
801 goto done;
802 err = got_object_tree_find_path(&id2, &mode2, repo, tree2,
803 pe->path);
804 if (err && err->code != GOT_ERR_NO_TREE_ENTRY)
805 goto done;
806 if (id1 == NULL && id2 == NULL) {
807 err = got_error_path(pe->path, GOT_ERR_NO_TREE_ENTRY);
808 goto done;
810 if (id1) {
811 err = got_object_get_type(&type1, repo, id1);
812 if (err)
813 goto done;
815 if (id2) {
816 err = got_object_get_type(&type2, repo, id2);
817 if (err)
818 goto done;
820 if (type1 == GOT_OBJ_TYPE_ANY &&
821 type2 == GOT_OBJ_TYPE_ANY) {
822 err = got_error_path(pe->path, GOT_ERR_NO_OBJ);
823 goto done;
824 } else if (type1 != GOT_OBJ_TYPE_ANY &&
825 type2 != GOT_OBJ_TYPE_ANY && type1 != type2) {
826 err = got_error(GOT_ERR_OBJ_TYPE);
827 goto done;
830 if (type1 == GOT_OBJ_TYPE_BLOB ||
831 type2 == GOT_OBJ_TYPE_BLOB) {
832 if (id1) {
833 err = got_object_open_as_blob(&blob1, repo,
834 id1, 8192);
835 if (err)
836 goto done;
838 if (id2) {
839 err = got_object_open_as_blob(&blob2, repo,
840 id2, 8192);
841 if (err)
842 goto done;
844 err = cb(cb_arg, blob1, blob2, f1, f2, id1, id2,
845 id1 ? pe->path : "/dev/null",
846 id2 ? pe->path : "/dev/null",
847 mode1, mode2, repo);
848 if (err)
849 goto done;
850 } else if (type1 == GOT_OBJ_TYPE_TREE ||
851 type2 == GOT_OBJ_TYPE_TREE) {
852 if (id1) {
853 err = got_object_open_as_tree(&subtree1, repo,
854 id1);
855 if (err)
856 goto done;
858 if (id2) {
859 err = got_object_open_as_tree(&subtree2, repo,
860 id2);
861 if (err)
862 goto done;
864 err = got_diff_tree(subtree1, subtree2, f1, f2,
865 id1 ? pe->path : "/dev/null",
866 id2 ? pe->path : "/dev/null",
867 repo, cb, cb_arg, 1);
868 if (err)
869 goto done;
870 } else {
871 err = got_error(GOT_ERR_OBJ_TYPE);
872 goto done;
875 done:
876 free(id1);
877 free(id2);
878 if (subtree1)
879 got_object_tree_close(subtree1);
880 if (subtree2)
881 got_object_tree_close(subtree2);
882 if (blob1)
883 got_object_blob_close(blob1);
884 if (blob2)
885 got_object_blob_close(blob2);
886 return err;
889 const struct got_error *
890 got_diff_objects_as_trees(off_t **line_offsets, size_t *nlines,
891 FILE *f1, FILE *f2, struct got_object_id *id1, struct got_object_id *id2,
892 struct got_pathlist_head *paths,
893 char *label1, char *label2, int diff_context, int ignore_whitespace,
894 int force_text_diff, struct got_repository *repo, FILE *outfile)
896 const struct got_error *err;
897 struct got_tree_object *tree1 = NULL, *tree2 = NULL;
898 struct got_diff_blob_output_unidiff_arg arg;
899 int want_lineoffsets = (line_offsets != NULL && *line_offsets != NULL);
901 if (id1 == NULL && id2 == NULL)
902 return got_error(GOT_ERR_NO_OBJ);
904 if (id1) {
905 err = got_object_open_as_tree(&tree1, repo, id1);
906 if (err)
907 goto done;
909 if (id2) {
910 err = got_object_open_as_tree(&tree2, repo, id2);
911 if (err)
912 goto done;
915 arg.diff_context = diff_context;
916 arg.ignore_whitespace = ignore_whitespace;
917 arg.force_text_diff = force_text_diff;
918 arg.outfile = outfile;
919 if (want_lineoffsets) {
920 arg.line_offsets = *line_offsets;
921 arg.nlines = *nlines;
922 } else {
923 arg.line_offsets = NULL;
924 arg.nlines = 0;
926 if (paths == NULL || TAILQ_EMPTY(paths)) {
927 err = got_diff_tree(tree1, tree2, f1, f2, label1, label2,
928 repo, got_diff_blob_output_unidiff, &arg, 1);
929 } else {
930 err = diff_paths(tree1, tree2, f1, f2, paths, repo,
931 got_diff_blob_output_unidiff, &arg);
933 if (want_lineoffsets) {
934 *line_offsets = arg.line_offsets; /* was likely re-allocated */
935 *nlines = arg.nlines;
937 done:
938 if (tree1)
939 got_object_tree_close(tree1);
940 if (tree2)
941 got_object_tree_close(tree2);
942 return err;
945 const struct got_error *
946 got_diff_objects_as_commits(off_t **line_offsets, size_t *nlines,
947 FILE *f1, FILE *f2, struct got_object_id *id1, struct got_object_id *id2,
948 struct got_pathlist_head *paths,
949 int diff_context, int ignore_whitespace, int force_text_diff,
950 struct got_repository *repo, FILE *outfile)
952 const struct got_error *err;
953 struct got_commit_object *commit1 = NULL, *commit2 = NULL;
955 if (id2 == NULL)
956 return got_error(GOT_ERR_NO_OBJ);
958 if (id1) {
959 err = got_object_open_as_commit(&commit1, repo, id1);
960 if (err)
961 goto done;
964 err = got_object_open_as_commit(&commit2, repo, id2);
965 if (err)
966 goto done;
968 err = got_diff_objects_as_trees(line_offsets, nlines, f1, f2,
969 commit1 ? got_object_commit_get_tree_id(commit1) : NULL,
970 got_object_commit_get_tree_id(commit2), paths, "", "",
971 diff_context, ignore_whitespace, force_text_diff, repo, outfile);
972 done:
973 if (commit1)
974 got_object_commit_close(commit1);
975 if (commit2)
976 got_object_commit_close(commit2);
977 return err;
980 const struct got_error *
981 got_diff_files(struct got_diffreg_result **resultp,
982 FILE *f1, const char *label1, FILE *f2, const char *label2,
983 int diff_context, int ignore_whitespace, int force_text_diff,
984 FILE *outfile)
986 const struct got_error *err = NULL;
987 struct got_diffreg_result *diffreg_result = NULL;
989 if (resultp)
990 *resultp = NULL;
992 if (outfile) {
993 fprintf(outfile, "file - %s\n",
994 f1 == NULL ? "/dev/null" : label1);
995 fprintf(outfile, "file + %s\n",
996 f2 == NULL ? "/dev/null" : label2);
999 err = got_diffreg(&diffreg_result, f1, f2, GOT_DIFF_ALGORITHM_PATIENCE,
1000 ignore_whitespace, force_text_diff);
1001 if (err)
1002 goto done;
1004 if (outfile) {
1005 err = got_diffreg_output(NULL, NULL, diffreg_result,
1006 f1 != NULL, f2 != NULL, label1, label2,
1007 GOT_DIFF_OUTPUT_UNIDIFF, diff_context, outfile);
1008 if (err)
1009 goto done;
1012 done:
1013 if (resultp && err == NULL)
1014 *resultp = diffreg_result;
1015 else if (diffreg_result) {
1016 const struct got_error *free_err;
1017 free_err = got_diffreg_result_free(diffreg_result);
1018 if (free_err && err == NULL)
1019 err = free_err;
1022 return err;