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