xref: /freebsd/contrib/libdiff/test/test019.left.txt (revision 53120fbb68952b7d620c2c0e1cf05c5017fc1b27)
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 */
16
17#include <sys/queue.h>
18#include <sys/stat.h>
19
20#include <stdbool.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <limits.h>
25#include <sha1.h>
26#include <zlib.h>
27
28#include "got_object.h"
29#include "got_repository.h"
30#include "got_error.h"
31#include "got_diff.h"
32#include "got_opentemp.h"
33#include "got_path.h"
34#include "got_cancel.h"
35#include "got_worktree.h"
36
37#include "got_lib_diff.h"
38#include "got_lib_delta.h"
39#include "got_lib_inflate.h"
40#include "got_lib_object.h"
41
42static const struct got_error *
43diff_blobs(struct got_diffreg_result **resultp,
44struct got_blob_object *blob1, struct got_blob_object *blob2,
45    const char *label1, const char *label2, mode_t mode1, mode_t mode2,
46    int diff_context, int ignore_whitespace, FILE *outfile)
47{
48	const struct got_error *err = NULL, *free_err;
49	FILE *f1 = NULL, *f2 = NULL;
50	char hex1[SHA1_DIGEST_STRING_LENGTH];
51	char hex2[SHA1_DIGEST_STRING_LENGTH];
52	char *idstr1 = NULL, *idstr2 = NULL;
53	size_t size1, size2;
54	struct got_diffreg_result *result;
55
56	if (resultp)
57		*resultp = NULL;
58
59	if (blob1) {
60		f1 = got_opentemp();
61		if (f1 == NULL)
62			return got_error_from_errno("got_opentemp");
63	}
64
65	if (blob2) {
66		f2 = got_opentemp();
67		if (f2 == NULL) {
68			err = got_error_from_errno("got_opentemp");
69			fclose(f1);
70			return err;
71		}
72	}
73
74	size1 = 0;
75	if (blob1) {
76		idstr1 = got_object_blob_id_str(blob1, hex1, sizeof(hex1));
77		err = got_object_blob_dump_to_file(&size1, NULL, NULL, f1,
78		    blob1);
79		if (err)
80			goto done;
81	} else
82		idstr1 = "/dev/null";
83
84	size2 = 0;
85	if (blob2) {
86		idstr2 = got_object_blob_id_str(blob2, hex2, sizeof(hex2));
87		err = got_object_blob_dump_to_file(&size2, NULL, NULL, f2,
88		    blob2);
89		if (err)
90			goto done;
91	} else
92		idstr2 = "/dev/null";
93
94	if (outfile) {
95		char *modestr1 = NULL, *modestr2 = NULL;
96		int modebits;
97		if (mode1 && mode1 != mode2) {
98			if (S_ISLNK(mode1))
99				modebits = S_IFLNK;
100			else
101				modebits = (S_IRWXU | S_IRWXG | S_IRWXO);
102			if (asprintf(&modestr1, " (mode %o)",
103			    mode1 & modebits) == -1) {
104				err = got_error_from_errno("asprintf");
105				goto done;
106			}
107		}
108		if (mode2 && mode1 != mode2) {
109			if (S_ISLNK(mode2))
110				modebits = S_IFLNK;
111			else
112				modebits = (S_IRWXU | S_IRWXG | S_IRWXO);
113			if (asprintf(&modestr2, " (mode %o)",
114			    mode2 & modebits) == -1) {
115				err = got_error_from_errno("asprintf");
116				goto done;
117			}
118		}
119		fprintf(outfile, "blob - %s%s\n", idstr1,
120		    modestr1 ? modestr1 : "");
121		fprintf(outfile, "blob + %s%s\n", idstr2,
122		    modestr2 ? modestr2 : "");
123		free(modestr1);
124		free(modestr2);
125	}
126	err = got_diffreg(&result, f1, f2, GOT_DIFF_ALGORITHM_MYERS,
127	    ignore_whitespace);
128	if (err)
129		goto done;
130
131	if (outfile) {
132		err = got_diffreg_output(NULL, NULL, result, f1, f2,
133		    label1 ? label1 : idstr1,
134		    label2 ? label2 : idstr2,
135		    GOT_DIFF_OUTPUT_UNIDIFF, diff_context, outfile);
136		if (err)
137			goto done;
138	}
139
140	if (resultp && err == NULL)
141		*resultp = result;
142	else {
143		free_err = got_diffreg_result_free(result);
144		if (free_err && err == NULL)
145			err = free_err;
146	}
147done:
148	if (f1 && fclose(f1) != 0 && err == NULL)
149		err = got_error_from_errno("fclose");
150	if (f2 && fclose(f2) != 0 && err == NULL)
151		err = got_error_from_errno("fclose");
152	return err;
153}
154
155const struct got_error *
156got_diff_blob_output_unidiff(void *arg, struct got_blob_object *blob1,
157    struct got_blob_object *blob2, struct got_object_id *id1,
158    struct got_object_id *id2, const char *label1, const char *label2,
159    mode_t mode1, mode_t mode2, struct got_repository *repo)
160{
161	const struct got_error *err;
162	struct got_diff_blob_output_unidiff_arg *a = arg;
163
164	err = diff_blobs(NULL, blob1, blob2, label1, label2, mode1, mode2,
165	    a->diff_context, a->ignore_whitespace, a->outfile);
166	return err;
167}
168
169const struct got_error *
170got_diff_blob(struct got_blob_object *blob1, struct got_blob_object *blob2,
171    const char *label1, const char *label2, int diff_context,
172    int ignore_whitespace, FILE *outfile)
173{
174	return diff_blobs(NULL, blob1, blob2, label1, label2, 0, 0, diff_context,
175	    ignore_whitespace, outfile);
176}
177
178static const struct got_error *
179diff_blob_file(struct got_diffreg_result **resultp,
180    struct got_blob_object *blob1, const char *label1, FILE *f2, size_t size2,
181    const char *label2, int diff_context, int ignore_whitespace, FILE *outfile)
182{
183	const struct got_error *err = NULL, *free_err;
184	FILE *f1 = NULL;
185	char hex1[SHA1_DIGEST_STRING_LENGTH];
186	char *idstr1 = NULL;
187	size_t size1;
188	struct got_diffreg_result *result = NULL;
189
190	if (resultp)
191		*resultp = NULL;
192
193	size1 = 0;
194	if (blob1) {
195		f1 = got_opentemp();
196		if (f1 == NULL)
197			return got_error_from_errno("got_opentemp");
198		idstr1 = got_object_blob_id_str(blob1, hex1, sizeof(hex1));
199		err = got_object_blob_dump_to_file(&size1, NULL, NULL, f1,
200		    blob1);
201		if (err)
202			goto done;
203	} else {
204		idstr1 = "/dev/null";
205	}
206
207	if (outfile) {
208		fprintf(outfile, "blob - %s\n", label1 ? label1 : idstr1);
209		fprintf(outfile, "file + %s\n",
210		    f2 == NULL ? "/dev/null" : label2);
211	}
212
213	err = got_diffreg(&result, f1, f2, GOT_DIFF_ALGORITHM_MYERS,
214	    ignore_whitespace);
215	if (err)
216		goto done;
217
218	if (outfile) {
219		err = got_diffreg_output(NULL, NULL, result, f1, f2,
220		    label2, label2, GOT_DIFF_OUTPUT_UNIDIFF, diff_context,
221		    outfile);
222		if (err)
223			goto done;
224	}
225
226	if (resultp && err == NULL)
227		*resultp = result;
228	else if (result) {
229		free_err = got_diffreg_result_free(result);
230		if (free_err && err == NULL)
231			err = free_err;
232	}
233done:
234	if (f1 && fclose(f1) != 0 && err == NULL)
235		err = got_error_from_errno("fclose");
236	return err;
237}
238
239const struct got_error *
240got_diff_blob_file(struct got_blob_object *blob1, const char *label1,
241    FILE *f2, size_t size2, const char *label2, int diff_context,
242    int ignore_whitespace, FILE *outfile)
243{
244	return diff_blob_file(NULL, blob1, label1, f2, size2, label2,
245	    diff_context, ignore_whitespace, outfile);
246}
247
248const struct got_error *
249got_diff_blob_file_lines_changed(struct got_diffreg_result **result,
250    struct got_blob_object *blob1, FILE *f2, size_t size2)
251{
252	return diff_blob_file(result, blob1, NULL, f2, size2, NULL,
253	    0, 0, NULL);
254}
255
256const struct got_error *
257got_diff_blob_lines_changed(struct got_diffreg_result **result,
258    struct got_blob_object *blob1, struct got_blob_object *blob2)
259{
260	const struct got_error *err = NULL;
261
262	err = diff_blobs(result, blob1, blob2, NULL, NULL, 0, 0, 3, 0, NULL);
263	if (err) {
264		got_diffreg_result_free(*result);
265		*result = NULL;
266	}
267	return err;
268}
269
270static const struct got_error *
271diff_added_blob(struct got_object_id *id, const char *label, mode_t mode,
272    struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg)
273{
274	const struct got_error *err;
275	struct got_blob_object  *blob = NULL;
276	struct got_object *obj = NULL;
277
278	err = got_object_open(&obj, repo, id);
279	if (err)
280		return err;
281
282	err = got_object_blob_open(&blob, repo, obj, 8192);
283	if (err)
284		goto done;
285	err = cb(cb_arg, NULL, blob, NULL, id, NULL, label, 0, mode, repo);
286done:
287	got_object_close(obj);
288	if (blob)
289		got_object_blob_close(blob);
290	return err;
291}
292
293static const struct got_error *
294diff_modified_blob(struct got_object_id *id1, struct got_object_id *id2,
295    const char *label1, const char *label2, mode_t mode1, mode_t mode2,
296    struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg)
297{
298	const struct got_error *err;
299	struct got_object *obj1 = NULL;
300	struct got_object *obj2 = NULL;
301	struct got_blob_object *blob1 = NULL;
302	struct got_blob_object *blob2 = NULL;
303
304	err = got_object_open(&obj1, repo, id1);
305	if (err)
306		return err;
307	if (obj1->type != GOT_OBJ_TYPE_BLOB) {
308		err = got_error(GOT_ERR_OBJ_TYPE);
309		goto done;
310	}
311
312	err = got_object_open(&obj2, repo, id2);
313	if (err)
314		goto done;
315	if (obj2->type != GOT_OBJ_TYPE_BLOB) {
316		err = got_error(GOT_ERR_BAD_OBJ_DATA);
317		goto done;
318	}
319
320	err = got_object_blob_open(&blob1, repo, obj1, 8192);
321	if (err)
322		goto done;
323
324	err = got_object_blob_open(&blob2, repo, obj2, 8192);
325	if (err)
326		goto done;
327
328	err = cb(cb_arg, blob1, blob2, id1, id2, label1, label2, mode1, mode2,
329	    repo);
330done:
331	if (obj1)
332		got_object_close(obj1);
333	if (obj2)
334		got_object_close(obj2);
335	if (blob1)
336		got_object_blob_close(blob1);
337	if (blob2)
338		got_object_blob_close(blob2);
339	return err;
340}
341
342static const struct got_error *
343diff_deleted_blob(struct got_object_id *id, const char *label, mode_t mode,
344    struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg)
345{
346	const struct got_error *err;
347	struct got_blob_object  *blob = NULL;
348	struct got_object *obj = NULL;
349
350	err = got_object_open(&obj, repo, id);
351	if (err)
352		return err;
353
354	err = got_object_blob_open(&blob, repo, obj, 8192);
355	if (err)
356		goto done;
357	err = cb(cb_arg, blob, NULL, id, NULL, label, NULL, mode, 0, repo);
358done:
359	got_object_close(obj);
360	if (blob)
361		got_object_blob_close(blob);
362	return err;
363}
364
365static const struct got_error *
366diff_added_tree(struct got_object_id *id, const char *label,
367    struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg,
368    int diff_content)
369{
370	const struct got_error *err = NULL;
371	struct got_object *treeobj = NULL;
372	struct got_tree_object *tree = NULL;
373
374	err = got_object_open(&treeobj, repo, id);
375	if (err)
376		goto done;
377
378	if (treeobj->type != GOT_OBJ_TYPE_TREE) {
379		err = got_error(GOT_ERR_OBJ_TYPE);
380		goto done;
381	}
382
383	err = got_object_tree_open(&tree, repo, treeobj);
384	if (err)
385		goto done;
386
387	err = got_diff_tree(NULL, tree, NULL, label, repo, cb, cb_arg,
388	    diff_content);
389done:
390	if (tree)
391		got_object_tree_close(tree);
392	if (treeobj)
393		got_object_close(treeobj);
394	return err;
395}
396
397static const struct got_error *
398diff_modified_tree(struct got_object_id *id1, struct got_object_id *id2,
399    const char *label1, const char *label2, struct got_repository *repo,
400    got_diff_blob_cb cb, void *cb_arg, int diff_content)
401{
402	const struct got_error *err;
403	struct got_object *treeobj1 = NULL;
404	struct got_object *treeobj2 = NULL;
405	struct got_tree_object *tree1 = NULL;
406	struct got_tree_object *tree2 = NULL;
407
408	err = got_object_open(&treeobj1, repo, id1);
409	if (err)
410		goto done;
411
412	if (treeobj1->type != GOT_OBJ_TYPE_TREE) {
413		err = got_error(GOT_ERR_OBJ_TYPE);
414		goto done;
415	}
416
417	err = got_object_open(&treeobj2, repo, id2);
418	if (err)
419		goto done;
420
421	if (treeobj2->type != GOT_OBJ_TYPE_TREE) {
422		err = got_error(GOT_ERR_OBJ_TYPE);
423		goto done;
424	}
425
426	err = got_object_tree_open(&tree1, repo, treeobj1);
427	if (err)
428		goto done;
429
430	err = got_object_tree_open(&tree2, repo, treeobj2);
431	if (err)
432		goto done;
433
434	err = got_diff_tree(tree1, tree2, label1, label2, repo, cb, cb_arg,
435	    diff_content);
436
437done:
438	if (tree1)
439		got_object_tree_close(tree1);
440	if (tree2)
441		got_object_tree_close(tree2);
442	if (treeobj1)
443		got_object_close(treeobj1);
444	if (treeobj2)
445		got_object_close(treeobj2);
446	return err;
447}
448
449static const struct got_error *
450diff_deleted_tree(struct got_object_id *id, const char *label,
451    struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg,
452    int diff_content)
453{
454	const struct got_error *err;
455	struct got_object *treeobj = NULL;
456	struct got_tree_object *tree = NULL;
457
458	err = got_object_open(&treeobj, repo, id);
459	if (err)
460		goto done;
461
462	if (treeobj->type != GOT_OBJ_TYPE_TREE) {
463		err = got_error(GOT_ERR_OBJ_TYPE);
464		goto done;
465	}
466
467	err = got_object_tree_open(&tree, repo, treeobj);
468	if (err)
469		goto done;
470
471	err = got_diff_tree(tree, NULL, label, NULL, repo, cb, cb_arg,
472	    diff_content);
473done:
474	if (tree)
475		got_object_tree_close(tree);
476	if (treeobj)
477		got_object_close(treeobj);
478	return err;
479}
480
481static const struct got_error *
482diff_kind_mismatch(struct got_object_id *id1, struct got_object_id *id2,
483    const char *label1, const char *label2, struct got_repository *repo,
484    got_diff_blob_cb cb, void *cb_arg)
485{
486	/* XXX TODO */
487	return NULL;
488}
489
490static const struct got_error *
491diff_entry_old_new(struct got_tree_entry *te1,
492    struct got_tree_entry *te2, const char *label1, const char *label2,
493    struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg,
494    int diff_content)
495{
496	const struct got_error *err = NULL;
497	int id_match;
498
499	if (got_object_tree_entry_is_submodule(te1))
500		return NULL;
501
502	if (te2 == NULL) {
503		if (S_ISDIR(te1->mode))
504			err = diff_deleted_tree(&te1->id, label1, repo,
505			    cb, cb_arg, diff_content);
506		else {
507			if (diff_content)
508				err = diff_deleted_blob(&te1->id, label1,
509				    te1->mode, repo, cb, cb_arg);
510			else
511				err = cb(cb_arg, NULL, NULL, &te1->id, NULL,
512				    label1, NULL, te1->mode, 0, repo);
513		}
514		return err;
515	} else if (got_object_tree_entry_is_submodule(te2))
516		return NULL;
517
518	id_match = (got_object_id_cmp(&te1->id, &te2->id) == 0);
519	if (S_ISDIR(te1->mode) && S_ISDIR(te2->mode)) {
520		if (!id_match)
521			return diff_modified_tree(&te1->id, &te2->id,
522			    label1, label2, repo, cb, cb_arg, diff_content);
523	} else if ((S_ISREG(te1->mode) || S_ISLNK(te1->mode)) &&
524	    (S_ISREG(te2->mode) || S_ISLNK(te2->mode))) {
525		if (!id_match ||
526		    ((te1->mode & (S_IFLNK | S_IXUSR))) !=
527		    (te2->mode & (S_IFLNK | S_IXUSR))) {
528			if (diff_content)
529				return diff_modified_blob(&te1->id, &te2->id,
530				    label1, label2, te1->mode, te2->mode,
531				    repo, cb, cb_arg);
532			else
533				return cb(cb_arg, NULL, NULL, &te1->id,
534				    &te2->id, label1, label2, te1->mode,
535				    te2->mode, repo);
536		}
537	}
538
539	if (id_match)
540		return NULL;
541
542	return diff_kind_mismatch(&te1->id, &te2->id, label1, label2, repo,
543	    cb, cb_arg);
544}
545
546static const struct got_error *
547diff_entry_new_old(struct got_tree_entry *te2,
548    struct got_tree_entry *te1, const char *label2,
549    struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg,
550    int diff_content)
551{
552	if (te1 != NULL) /* handled by diff_entry_old_new() */
553		return NULL;
554
555	if (got_object_tree_entry_is_submodule(te2))
556		return NULL;
557
558	if (S_ISDIR(te2->mode))
559		return diff_added_tree(&te2->id, label2, repo, cb, cb_arg,
560		    diff_content);
561
562	if (diff_content)
563		return diff_added_blob(&te2->id, label2, te2->mode, repo, cb,
564		    cb_arg);
565
566	return cb(cb_arg, NULL, NULL, NULL, &te2->id, NULL, label2, 0,
567	    te2->mode, repo);
568}
569
570const struct got_error *
571got_diff_tree_collect_changed_paths(void *arg, struct got_blob_object *blob1,
572    struct got_blob_object *blob2, struct got_object_id *id1,
573    struct got_object_id *id2, const char *label1, const char *label2,
574    mode_t mode1, mode_t mode2, struct got_repository *repo)
575{
576	const struct got_error *err = NULL;
577	struct got_pathlist_head *paths = arg;
578	struct got_diff_changed_path *change = NULL;
579	char *path = NULL;
580
581	path = strdup(label2 ? label2 : label1);
582	if (path == NULL)
583		return got_error_from_errno("malloc");
584
585	change = malloc(sizeof(*change));
586	if (change == NULL) {
587		err = got_error_from_errno("malloc");
588		goto done;
589	}
590
591	change->status = GOT_STATUS_NO_CHANGE;
592	if (id1 == NULL)
593		change->status = GOT_STATUS_ADD;
594	else if (id2 == NULL)
595		change->status = GOT_STATUS_DELETE;
596	else {
597		if (got_object_id_cmp(id1, id2) != 0)
598			change->status = GOT_STATUS_MODIFY;
599		else if (mode1 != mode2)
600			change->status = GOT_STATUS_MODE_CHANGE;
601	}
602
603	err = got_pathlist_insert(NULL, paths, path, change);
604done:
605	if (err) {
606		free(path);
607		free(change);
608	}
609	return err;
610}
611
612const struct got_error *
613got_diff_tree(struct got_tree_object *tree1, struct got_tree_object *tree2,
614    const char *label1, const char *label2, struct got_repository *repo,
615    got_diff_blob_cb cb, void *cb_arg, int diff_content)
616{
617	const struct got_error *err = NULL;
618	struct got_tree_entry *te1 = NULL;
619	struct got_tree_entry *te2 = NULL;
620	char *l1 = NULL, *l2 = NULL;
621	int tidx1 = 0, tidx2 = 0;
622
623	if (tree1) {
624		te1 = got_object_tree_get_entry(tree1, 0);
625		if (te1 && asprintf(&l1, "%s%s%s", label1, label1[0] ? "/" : "",
626		    te1->name) == -1)
627			return got_error_from_errno("asprintf");
628	}
629	if (tree2) {
630		te2 = got_object_tree_get_entry(tree2, 0);
631		if (te2 && asprintf(&l2, "%s%s%s", label2, label2[0] ? "/" : "",
632		    te2->name) == -1)
633			return got_error_from_errno("asprintf");
634	}
635
636	do {
637		if (te1) {
638			struct got_tree_entry *te = NULL;
639			if (tree2)
640				te = got_object_tree_find_entry(tree2,
641				    te1->name);
642			if (te) {
643				free(l2);
644				l2 = NULL;
645				if (te && asprintf(&l2, "%s%s%s", label2,
646				    label2[0] ? "/" : "", te->name) == -1)
647					return
648					    got_error_from_errno("asprintf");
649			}
650			err = diff_entry_old_new(te1, te, l1, l2, repo, cb,
651			    cb_arg, diff_content);
652			if (err)
653				break;
654		}
655
656		if (te2) {
657			struct got_tree_entry *te = NULL;
658			if (tree1)
659				te = got_object_tree_find_entry(tree1,
660				    te2->name);
661			free(l2);
662			if (te) {
663				if (asprintf(&l2, "%s%s%s", label2,
664				    label2[0] ? "/" : "", te->name) == -1)
665					return
666					    got_error_from_errno("asprintf");
667			} else {
668				if (asprintf(&l2, "%s%s%s", label2,
669				    label2[0] ? "/" : "", te2->name) == -1)
670					return
671					    got_error_from_errno("asprintf");
672			}
673			err = diff_entry_new_old(te2, te, l2, repo,
674			    cb, cb_arg, diff_content);
675			if (err)
676				break;
677		}
678
679		free(l1);
680		l1 = NULL;
681		if (te1) {
682			tidx1++;
683			te1 = got_object_tree_get_entry(tree1, tidx1);
684			if (te1 &&
685			    asprintf(&l1, "%s%s%s", label1,
686			    label1[0] ? "/" : "", te1->name) == -1)
687				return got_error_from_errno("asprintf");
688		}
689		free(l2);
690		l2 = NULL;
691		if (te2) {
692			tidx2++;
693			te2 = got_object_tree_get_entry(tree2, tidx2);
694			if (te2 &&
695			    asprintf(&l2, "%s%s%s", label2,
696			        label2[0] ? "/" : "", te2->name) == -1)
697				return got_error_from_errno("asprintf");
698		}
699	} while (te1 || te2);
700
701	return err;
702}
703
704const struct got_error *
705got_diff_objects_as_blobs(struct got_object_id *id1, struct got_object_id *id2,
706    const char *label1, const char *label2, int diff_context,
707    int ignore_whitespace, struct got_repository *repo, FILE *outfile)
708{
709	const struct got_error *err;
710	struct got_blob_object *blob1 = NULL, *blob2 = NULL;
711
712	if (id1 == NULL && id2 == NULL)
713		return got_error(GOT_ERR_NO_OBJ);
714
715	if (id1) {
716		err = got_object_open_as_blob(&blob1, repo, id1, 8192);
717		if (err)
718			goto done;
719	}
720	if (id2) {
721		err = got_object_open_as_blob(&blob2, repo, id2, 8192);
722		if (err)
723			goto done;
724	}
725	err = got_diff_blob(blob1, blob2, label1, label2, diff_context,
726	    ignore_whitespace, outfile);
727done:
728	if (blob1)
729		got_object_blob_close(blob1);
730	if (blob2)
731		got_object_blob_close(blob2);
732	return err;
733}
734
735const struct got_error *
736got_diff_objects_as_trees(struct got_object_id *id1, struct got_object_id *id2,
737    char *label1, char *label2, int diff_context, int ignore_whitespace,
738    struct got_repository *repo, FILE *outfile)
739{
740	const struct got_error *err;
741	struct got_tree_object *tree1 = NULL, *tree2 = NULL;
742	struct got_diff_blob_output_unidiff_arg arg;
743
744	if (id1 == NULL && id2 == NULL)
745		return got_error(GOT_ERR_NO_OBJ);
746
747	if (id1) {
748		err = got_object_open_as_tree(&tree1, repo, id1);
749		if (err)
750			goto done;
751	}
752	if (id2) {
753		err = got_object_open_as_tree(&tree2, repo, id2);
754		if (err)
755			goto done;
756	}
757	arg.diff_context = diff_context;
758	arg.ignore_whitespace = ignore_whitespace;
759	arg.outfile = outfile;
760	err = got_diff_tree(tree1, tree2, label1, label2, repo,
761	    got_diff_blob_output_unidiff, &arg, 1);
762done:
763	if (tree1)
764		got_object_tree_close(tree1);
765	if (tree2)
766		got_object_tree_close(tree2);
767	return err;
768}
769
770const struct got_error *
771got_diff_objects_as_commits(struct got_object_id *id1,
772    struct got_object_id *id2, int diff_context, int ignore_whitespace,
773    struct got_repository *repo, FILE *outfile)
774{
775	const struct got_error *err;
776	struct got_commit_object *commit1 = NULL, *commit2 = NULL;
777
778	if (id2 == NULL)
779		return got_error(GOT_ERR_NO_OBJ);
780
781	if (id1) {
782		err = got_object_open_as_commit(&commit1, repo, id1);
783		if (err)
784			goto done;
785	}
786
787	err = got_object_open_as_commit(&commit2, repo, id2);
788	if (err)
789		goto done;
790
791	err = got_diff_objects_as_trees(
792	    commit1 ? got_object_commit_get_tree_id(commit1) : NULL,
793	    got_object_commit_get_tree_id(commit2), "", "", diff_context,
794	    ignore_whitespace, repo, outfile);
795done:
796	if (commit1)
797		got_object_commit_close(commit1);
798	if (commit2)
799		got_object_commit_close(commit2);
800	return err;
801}
802
803const struct got_error *
804got_diff_files(struct got_diffreg_result **resultp,
805    FILE *f1, const char *label1, FILE *f2, const char *label2,
806    int diff_context, int ignore_whitespace, FILE *outfile)
807{
808	const struct got_error *err = NULL;
809	struct got_diffreg_result *diffreg_result = NULL;
810
811	if (resultp)
812		*resultp = NULL;
813
814	if (outfile) {
815		fprintf(outfile, "file - %s\n",
816		    f1 == NULL ? "/dev/null" : label1);
817		fprintf(outfile, "file + %s\n",
818		    f2 == NULL ? "/dev/null" : label2);
819	}
820
821	err = got_diffreg(&diffreg_result, f1, f2, GOT_DIFF_ALGORITHM_MYERS,
822	    ignore_whitespace);
823	if (err)
824		goto done;
825
826	if (outfile) {
827		err = got_diffreg_output(NULL, NULL, diffreg_result,
828		    f1, f2, label1, label2, GOT_DIFF_OUTPUT_UNIDIFF,
829		    diff_context, outfile);
830		if (err)
831			goto done;
832	}
833
834done:
835	if (resultp && err == NULL)
836		*resultp = diffreg_result;
837	else if (diffreg_result) {
838		const struct got_error *free_err;
839		free_err = got_diffreg_result_free(diffreg_result);
840		if (free_err && err == NULL)
841			err = free_err;
842	}
843
844	return err;
845}
846