1 /* $OpenBSD: commit.c,v 1.154 2015/11/05 09:48:21 nicm Exp $ */ 2 /* 3 * Copyright (c) 2006 Joris Vink <joris@openbsd.org> 4 * Copyright (c) 2006 Xavier Santolaria <xsa@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/stat.h> 20 21 #include <errno.h> 22 #include <fcntl.h> 23 #include <libgen.h> 24 #include <stdlib.h> 25 #include <string.h> 26 #include <unistd.h> 27 28 #include "cvs.h" 29 #include "diff.h" 30 #include "remote.h" 31 32 void cvs_commit_local(struct cvs_file *); 33 void cvs_commit_check_files(struct cvs_file *); 34 void cvs_commit_loginfo(char *); 35 void cvs_commit_lock_dirs(struct cvs_file *); 36 37 static BUF *commit_diff(struct cvs_file *, RCSNUM *, int); 38 static void commit_desc_set(struct cvs_file *); 39 40 struct file_info_list files_info; 41 struct trigger_list *line_list; 42 43 struct cvs_flisthead files_affected; 44 struct cvs_flisthead files_added; 45 struct cvs_flisthead files_removed; 46 struct cvs_flisthead files_modified; 47 48 char *logmsg = NULL; 49 char *loginfo = NULL; 50 51 static int conflicts_found; 52 53 struct cvs_cmd cvs_cmd_commit = { 54 CVS_OP_COMMIT, CVS_USE_WDIR | CVS_LOCK_REPO, "commit", 55 { "ci", "com" }, 56 "Check files into the repository", 57 "[-flR] [-F logfile | -m msg] [-r rev] ...", 58 "F:flm:Rr:", 59 NULL, 60 cvs_commit 61 }; 62 63 int 64 cvs_commit(int argc, char **argv) 65 { 66 int flags; 67 int ch, Fflag, mflag; 68 struct module_checkout *mc; 69 struct cvs_recursion cr; 70 struct cvs_filelist *l; 71 struct file_info *fi; 72 char *arg = ".", repo[PATH_MAX]; 73 74 flags = CR_RECURSE_DIRS; 75 Fflag = mflag = 0; 76 77 while ((ch = getopt(argc, argv, cvs_cmd_commit.cmd_opts)) != -1) { 78 switch (ch) { 79 case 'F': 80 /* free previously assigned value */ 81 free(logmsg); 82 logmsg = cvs_logmsg_read(optarg); 83 Fflag = 1; 84 break; 85 case 'f': 86 break; 87 case 'l': 88 flags &= ~CR_RECURSE_DIRS; 89 break; 90 case 'm': 91 /* free previously assigned value */ 92 free(logmsg); 93 logmsg = xstrdup(optarg); 94 mflag = 1; 95 break; 96 case 'R': 97 flags |= CR_RECURSE_DIRS; 98 break; 99 case 'r': 100 break; 101 default: 102 fatal("%s", cvs_cmd_commit.cmd_synopsis); 103 } 104 } 105 106 argc -= optind; 107 argv += optind; 108 109 /* -F and -m are mutually exclusive */ 110 if (Fflag && mflag) 111 fatal("cannot specify both a log file and a message"); 112 113 RB_INIT(&files_affected); 114 RB_INIT(&files_added); 115 RB_INIT(&files_removed); 116 RB_INIT(&files_modified); 117 118 TAILQ_INIT(&files_info); 119 conflicts_found = 0; 120 121 cr.enterdir = NULL; 122 cr.leavedir = NULL; 123 cr.fileproc = cvs_commit_check_files; 124 cr.flags = flags; 125 126 if (argc > 0) 127 cvs_file_run(argc, argv, &cr); 128 else 129 cvs_file_run(1, &arg, &cr); 130 131 if (conflicts_found != 0) 132 fatal("%d conflicts found, please correct these first", 133 conflicts_found); 134 135 if (RB_EMPTY(&files_affected)) 136 return (0); 137 138 if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) { 139 if (logmsg == NULL) { 140 logmsg = cvs_logmsg_create(NULL, &files_added, 141 &files_removed, &files_modified); 142 if (logmsg == NULL) 143 fatal("This shouldnt happen, honestly!"); 144 } 145 cvs_client_connect_to_server(); 146 cr.fileproc = cvs_client_sendfile; 147 148 if (argc > 0) 149 cvs_file_run(argc, argv, &cr); 150 else 151 cvs_file_run(1, &arg, &cr); 152 153 if (!(flags & CR_RECURSE_DIRS)) 154 cvs_client_send_request("Argument -l"); 155 156 cvs_client_send_logmsg(logmsg); 157 cvs_client_send_files(argv, argc); 158 cvs_client_senddir("."); 159 cvs_client_send_request("ci"); 160 cvs_client_get_responses(); 161 } else { 162 cvs_get_repository_name(".", repo, PATH_MAX); 163 164 line_list = cvs_trigger_getlines(CVS_PATH_COMMITINFO, repo); 165 if (line_list != NULL) { 166 RB_FOREACH(l, cvs_flisthead, &files_affected) { 167 fi = xcalloc(1, sizeof(*fi)); 168 fi->file_path = xstrdup(l->file_path); 169 TAILQ_INSERT_TAIL(&files_info, fi, 170 flist); 171 } 172 173 if (cvs_trigger_handle(CVS_TRIGGER_COMMITINFO, 174 repo, NULL, line_list, &files_info)) { 175 cvs_log(LP_ERR, 176 "Pre-commit check failed"); 177 cvs_trigger_freelist(line_list); 178 goto end; 179 } 180 181 cvs_trigger_freelist(line_list); 182 cvs_trigger_freeinfo(&files_info); 183 } 184 185 if (cvs_server_active) { 186 if (logmsg == NULL) 187 fatal("no log message specified"); 188 } else if (logmsg == NULL) { 189 logmsg = cvs_logmsg_create(NULL, &files_added, 190 &files_removed, &files_modified); 191 if (logmsg == NULL) 192 fatal("This shouldnt happen, honestly!"); 193 } 194 195 if (cvs_logmsg_verify(logmsg)) 196 goto end; 197 198 cr.fileproc = cvs_commit_lock_dirs; 199 cvs_file_walklist(&files_affected, &cr); 200 201 line_list = cvs_trigger_getlines(CVS_PATH_LOGINFO, repo); 202 203 cr.fileproc = cvs_commit_local; 204 cvs_file_walklist(&files_affected, &cr); 205 206 if (line_list != NULL) { 207 cvs_commit_loginfo(repo); 208 209 cvs_trigger_handle(CVS_TRIGGER_LOGINFO, repo, 210 loginfo, line_list, &files_info); 211 212 free(loginfo); 213 cvs_trigger_freelist(line_list); 214 cvs_trigger_freeinfo(&files_info); 215 } 216 217 mc = cvs_module_lookup(repo); 218 if (mc->mc_prog != NULL && 219 (mc->mc_flags & MODULE_RUN_ON_COMMIT)) 220 cvs_exec(mc->mc_prog, NULL, 0); 221 } 222 223 end: 224 cvs_trigger_freeinfo(&files_info); 225 free(logmsg); 226 return (0); 227 } 228 229 void 230 cvs_commit_loginfo(char *repo) 231 { 232 BUF *buf; 233 char pwd[PATH_MAX]; 234 struct cvs_filelist *cf; 235 236 if (getcwd(pwd, sizeof(pwd)) == NULL) 237 fatal("Can't get working directory"); 238 239 buf = buf_alloc(1024); 240 241 cvs_trigger_loginfo_header(buf, repo); 242 243 if (!RB_EMPTY(&files_added)) { 244 buf_puts(buf, "Added Files:"); 245 246 RB_FOREACH(cf, cvs_flisthead, &files_added) { 247 buf_putc(buf, '\n'); 248 buf_putc(buf, '\t'); 249 buf_puts(buf, cf->file_path); 250 } 251 252 buf_putc(buf, '\n'); 253 } 254 255 if (!RB_EMPTY(&files_modified)) { 256 buf_puts(buf, "Modified Files:"); 257 258 RB_FOREACH(cf, cvs_flisthead, &files_modified) { 259 buf_putc(buf, '\n'); 260 buf_putc(buf, '\t'); 261 buf_puts(buf, cf->file_path); 262 } 263 264 buf_putc(buf, '\n'); 265 } 266 267 if (!RB_EMPTY(&files_removed)) { 268 buf_puts(buf, "Removed Files:"); 269 270 RB_FOREACH(cf, cvs_flisthead, &files_removed) { 271 buf_putc(buf, '\n'); 272 buf_putc(buf, '\t'); 273 buf_puts(buf, cf->file_path); 274 } 275 276 buf_putc(buf, '\n'); 277 } 278 279 buf_puts(buf, "Log Message:\n"); 280 281 buf_puts(buf, logmsg); 282 283 buf_putc(buf, '\n'); 284 buf_putc(buf, '\0'); 285 286 loginfo = buf_release(buf); 287 } 288 289 void 290 cvs_commit_lock_dirs(struct cvs_file *cf) 291 { 292 char repo[PATH_MAX]; 293 294 cvs_get_repository_path(cf->file_wd, repo, sizeof(repo)); 295 cvs_log(LP_TRACE, "cvs_commit_lock_dirs: %s", repo); 296 297 /* locks stay in place until we are fully done and exit */ 298 cvs_repository_lock(repo, 1); 299 } 300 301 void 302 cvs_commit_check_files(struct cvs_file *cf) 303 { 304 char *tag; 305 RCSNUM *branch, *brev; 306 307 branch = brev = NULL; 308 309 cvs_log(LP_TRACE, "cvs_commit_check_files(%s)", cf->file_path); 310 311 if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) 312 cvs_remote_classify_file(cf); 313 else 314 cvs_file_classify(cf, cvs_directory_tag); 315 316 if (cf->file_type == CVS_DIR) { 317 if (verbosity > 1) 318 cvs_log(LP_NOTICE, "Examining %s", cf->file_path); 319 return; 320 } 321 322 if (cf->file_status == FILE_UPTODATE) 323 return; 324 325 if (cf->file_status == FILE_MERGE || 326 cf->file_status == FILE_PATCH || 327 cf->file_status == FILE_CHECKOUT || 328 cf->file_status == FILE_LOST || 329 cf->file_status == FILE_UNLINK) { 330 cvs_log(LP_ERR, "conflict: %s is not up-to-date", 331 cf->file_path); 332 conflicts_found++; 333 return; 334 } 335 336 if (cf->file_status == FILE_CONFLICT && 337 cf->file_ent->ce_conflict != NULL) { 338 cvs_log(LP_ERR, "conflict: unresolved conflicts in %s from " 339 "merging, please fix these first", cf->file_path); 340 conflicts_found++; 341 return; 342 } 343 344 if (cf->file_status == FILE_MODIFIED && 345 cf->file_ent->ce_conflict != NULL && 346 update_has_conflict_markers(cf)) { 347 cvs_log(LP_ERR, "warning: file %s seems to still contain " 348 "conflict indicators", cf->file_path); 349 } 350 351 if (cf->file_ent != NULL && cf->file_ent->ce_date != -1) { 352 cvs_log(LP_ERR, "conflict: cannot commit to sticky date for %s", 353 cf->file_path); 354 conflicts_found++; 355 return; 356 } 357 358 if (current_cvsroot->cr_method == CVS_METHOD_LOCAL) { 359 tag = cvs_directory_tag; 360 if (cf->file_ent != NULL) 361 tag = cf->file_ent->ce_tag; 362 363 if (tag != NULL && cf->file_rcs != NULL) { 364 brev = rcs_sym_getrev(cf->file_rcs, tag); 365 if (brev != NULL) { 366 if (RCSNUM_ISBRANCH(brev)) 367 goto next; 368 rcsnum_free(brev); 369 } 370 371 brev = rcs_translate_tag(tag, cf->file_rcs); 372 373 if (brev == NULL) { 374 if (cf->file_status == FILE_ADDED) 375 goto next; 376 fatal("failed to resolve tag: %s", 377 cf->file_ent->ce_tag); 378 } 379 380 if ((branch = rcsnum_revtobr(brev)) == NULL) { 381 cvs_log(LP_ERR, "sticky tag %s is not " 382 "a branch for file %s", tag, 383 cf->file_path); 384 conflicts_found++; 385 rcsnum_free(brev); 386 return; 387 } 388 389 if (!RCSNUM_ISBRANCHREV(brev)) { 390 cvs_log(LP_ERR, "sticky tag %s is not " 391 "a branch for file %s", tag, 392 cf->file_path); 393 conflicts_found++; 394 rcsnum_free(branch); 395 rcsnum_free(brev); 396 return; 397 } 398 399 if (!RCSNUM_ISBRANCH(branch)) { 400 cvs_log(LP_ERR, "sticky tag %s is not " 401 "a branch for file %s", tag, 402 cf->file_path); 403 conflicts_found++; 404 rcsnum_free(branch); 405 rcsnum_free(brev); 406 return; 407 } 408 } 409 } 410 411 next: 412 if (branch != NULL) 413 rcsnum_free(branch); 414 if (brev != NULL) 415 rcsnum_free(brev); 416 417 if (cf->file_status != FILE_ADDED && 418 cf->file_status != FILE_REMOVED && 419 cf->file_status != FILE_MODIFIED) 420 return; 421 422 cvs_file_get(cf->file_path, 0, &files_affected, CVS_FILE); 423 424 switch (cf->file_status) { 425 case FILE_ADDED: 426 cvs_file_get(cf->file_path, 0, &files_added, CVS_FILE); 427 break; 428 case FILE_REMOVED: 429 cvs_file_get(cf->file_path, 0, &files_removed, CVS_FILE); 430 break; 431 case FILE_MODIFIED: 432 cvs_file_get(cf->file_path, 0, &files_modified, CVS_FILE); 433 break; 434 } 435 } 436 437 void 438 cvs_commit_local(struct cvs_file *cf) 439 { 440 char *tag; 441 BUF *b, *d; 442 int onbranch, isnew, histtype, branchadded; 443 RCSNUM *nrev, *crev, *rrev, *brev; 444 int openflags, rcsflags; 445 char rbuf[CVS_REV_BUFSZ], nbuf[CVS_REV_BUFSZ]; 446 CVSENTRIES *entlist; 447 char attic[PATH_MAX], repo[PATH_MAX], rcsfile[PATH_MAX]; 448 struct file_info *fi; 449 450 cvs_log(LP_TRACE, "cvs_commit_local(%s)", cf->file_path); 451 cvs_file_classify(cf, cvs_directory_tag); 452 453 if (cvs_noexec == 1) 454 return; 455 456 if (cf->file_type != CVS_FILE) 457 fatal("cvs_commit_local: '%s' is not a file", cf->file_path); 458 459 tag = cvs_directory_tag; 460 if (cf->file_ent != NULL && cf->file_ent->ce_tag != NULL) 461 tag = cf->file_ent->ce_tag; 462 463 branchadded = 0; 464 switch (cf->file_status) { 465 case FILE_ADDED: 466 if (cf->file_rcs == NULL && tag != NULL) { 467 branchadded = 1; 468 cvs_add_tobranch(cf, tag); 469 } 470 break; 471 case FILE_MODIFIED: 472 case FILE_REMOVED: 473 if (cf->file_rcs == NULL) { 474 cvs_log(LP_ERR, "RCS file for %s got lost", 475 cf->file_path); 476 return; 477 } 478 break; 479 default: 480 cvs_log(LP_ERR, "skipping bogus file `%s'", cf->file_path); 481 return; 482 } 483 484 onbranch = 0; 485 nrev = RCS_HEAD_REV; 486 crev = NULL; 487 rrev = NULL; 488 d = NULL; 489 490 if (cf->file_rcs != NULL && cf->file_rcs->rf_branch != NULL) { 491 rcsnum_free(cf->file_rcs->rf_branch); 492 cf->file_rcs->rf_branch = NULL; 493 } 494 495 if (cf->file_rcs != NULL) { 496 rrev = rcs_head_get(cf->file_rcs); 497 crev = rcs_head_get(cf->file_rcs); 498 if (crev == NULL || rrev == NULL) 499 fatal("no head revision in RCS file for %s", 500 cf->file_path); 501 502 if (tag != NULL) { 503 rcsnum_free(crev); 504 rcsnum_free(rrev); 505 brev = rcs_sym_getrev(cf->file_rcs, tag); 506 crev = rcs_translate_tag(tag, cf->file_rcs); 507 if (brev == NULL || crev == NULL) { 508 fatal("failed to resolve existing tag: %s", 509 tag); 510 } 511 512 rrev = rcsnum_alloc(); 513 rcsnum_cpy(brev, rrev, brev->rn_len - 1); 514 515 if (RCSNUM_ISBRANCHREV(crev) && 516 rcsnum_cmp(crev, rrev, 0)) { 517 nrev = rcsnum_alloc(); 518 rcsnum_cpy(crev, nrev, 0); 519 rcsnum_inc(nrev); 520 } else if (!RCSNUM_ISBRANCH(crev)) { 521 nrev = rcsnum_brtorev(brev); 522 if (nrev == NULL) 523 fatal("failed to create branch rev"); 524 } else { 525 fatal("this isnt suppose to happen, honestly"); 526 } 527 528 rcsnum_free(brev); 529 rcsnum_free(rrev); 530 rrev = rcsnum_branch_root(nrev); 531 532 /* branch stuff was checked in cvs_commit_check_files */ 533 onbranch = 1; 534 } 535 536 rcsnum_tostr(crev, rbuf, sizeof(rbuf)); 537 } else { 538 strlcpy(rbuf, "Non-existent", sizeof(rbuf)); 539 } 540 541 if (rrev != NULL) 542 rcsnum_free(rrev); 543 isnew = 0; 544 if (cf->file_status == FILE_ADDED) { 545 isnew = 1; 546 rcsflags = RCS_CREATE; 547 openflags = O_CREAT | O_RDONLY; 548 if (cf->file_rcs != NULL) { 549 if (!onbranch) { 550 if (cf->in_attic == 0) 551 cvs_log(LP_ERR, "warning: expected %s " 552 "to be in the Attic", 553 cf->file_path); 554 555 if (cf->file_rcs->rf_dead == 0) 556 cvs_log(LP_ERR, "warning: expected %s " 557 "to be dead", cf->file_path); 558 559 cvs_get_repository_path(cf->file_wd, repo, 560 PATH_MAX); 561 (void)xsnprintf(rcsfile, PATH_MAX, "%s/%s%s", 562 repo, cf->file_name, RCS_FILE_EXT); 563 564 if (rename(cf->file_rpath, rcsfile) == -1) 565 fatal("cvs_commit_local: failed to " 566 "move %s outside the Attic: %s", 567 cf->file_path, strerror(errno)); 568 569 free(cf->file_rpath); 570 cf->file_rpath = xstrdup(rcsfile); 571 isnew = 0; 572 } 573 574 rcsflags = RCS_READ | RCS_PARSE_FULLY; 575 openflags = O_RDONLY; 576 rcs_close(cf->file_rcs); 577 } 578 579 cf->repo_fd = open(cf->file_rpath, openflags); 580 if (cf->repo_fd < 0) 581 fatal("cvs_commit_local: %s", strerror(errno)); 582 583 cf->file_rcs = rcs_open(cf->file_rpath, cf->repo_fd, 584 rcsflags, 0444); 585 if (cf->file_rcs == NULL) 586 fatal("cvs_commit_local: failed to create RCS file " 587 "for %s", cf->file_path); 588 589 commit_desc_set(cf); 590 591 if (branchadded) 592 strlcpy(rbuf, "Non-existent", sizeof(rbuf)); 593 } 594 595 if (verbosity > 1) { 596 cvs_printf("Checking in %s:\n", cf->file_path); 597 cvs_printf("%s <- %s\n", cf->file_rpath, cf->file_path); 598 cvs_printf("old revision: %s; ", rbuf); 599 } 600 601 if (isnew == 0 && cf->file_rcs->rf_head == NULL) 602 fatal("no head revision in RCS file for %s", cf->file_path); 603 604 if (isnew == 0 && onbranch == 0) 605 d = commit_diff(cf, cf->file_rcs->rf_head, 0); 606 607 if (cf->file_status == FILE_REMOVED) { 608 b = rcs_rev_getbuf(cf->file_rcs, crev, 0); 609 } else if (onbranch == 1) { 610 b = commit_diff(cf, crev, 1); 611 } else { 612 b = buf_load_fd(cf->fd); 613 } 614 615 if (isnew == 0 && onbranch == 0) { 616 if (rcs_deltatext_set(cf->file_rcs, crev, d) == -1) 617 fatal("cvs_commit_local: failed to set delta"); 618 } 619 620 if (rcs_rev_add(cf->file_rcs, nrev, logmsg, -1, NULL) == -1) 621 fatal("cvs_commit_local: failed to add new revision"); 622 623 if (nrev == RCS_HEAD_REV) 624 nrev = cf->file_rcs->rf_head; 625 626 if (rcs_deltatext_set(cf->file_rcs, nrev, b) == -1) 627 fatal("cvs_commit_local: failed to set new HEAD delta"); 628 629 if (cf->file_status == FILE_REMOVED) { 630 if (rcs_state_set(cf->file_rcs, nrev, RCS_STATE_DEAD) == -1) 631 fatal("cvs_commit_local: failed to set state"); 632 } 633 634 if (cf->file_status == FILE_ADDED && cf->file_ent->ce_opts != NULL) { 635 int cf_kflag; 636 637 cf_kflag = rcs_kflag_get(cf->file_ent->ce_opts + 2); 638 rcs_kwexp_set(cf->file_rcs, cf_kflag); 639 } 640 641 rcs_write(cf->file_rcs); 642 643 if (cf->file_status == FILE_REMOVED) { 644 strlcpy(nbuf, "Removed", sizeof(nbuf)); 645 } else if (cf->file_status == FILE_ADDED) { 646 if (cf->file_rcs->rf_dead == 1) 647 strlcpy(nbuf, "Initial Revision", sizeof(nbuf)); 648 else 649 rcsnum_tostr(nrev, nbuf, sizeof(nbuf)); 650 } else if (cf->file_status == FILE_MODIFIED) { 651 rcsnum_tostr(nrev, nbuf, sizeof(nbuf)); 652 } 653 654 if (verbosity > 1) 655 cvs_printf("new revision: %s\n", nbuf); 656 657 (void)unlink(cf->file_path); 658 (void)close(cf->fd); 659 cf->fd = -1; 660 661 if (cf->file_status != FILE_REMOVED) { 662 cvs_checkout_file(cf, nrev, NULL, CO_COMMIT); 663 } else { 664 entlist = cvs_ent_open(cf->file_wd); 665 cvs_ent_remove(entlist, cf->file_name); 666 667 cvs_get_repository_path(cf->file_wd, repo, PATH_MAX); 668 669 (void)xsnprintf(attic, PATH_MAX, "%s/%s", 670 repo, CVS_PATH_ATTIC); 671 672 if (mkdir(attic, 0755) == -1 && errno != EEXIST) 673 fatal("cvs_commit_local: failed to create Attic"); 674 675 (void)xsnprintf(attic, PATH_MAX, "%s/%s/%s%s", repo, 676 CVS_PATH_ATTIC, cf->file_name, RCS_FILE_EXT); 677 678 if (rename(cf->file_rpath, attic) == -1) 679 fatal("cvs_commit_local: failed to move %s to Attic", 680 cf->file_path); 681 682 if (cvs_server_active == 1) 683 cvs_server_update_entry("Remove-entry", cf); 684 } 685 686 if (verbosity > 1) 687 cvs_printf("done\n"); 688 else { 689 cvs_log(LP_NOTICE, "checking in '%s'; revision %s -> %s", 690 cf->file_path, rbuf, nbuf); 691 } 692 693 if (line_list != NULL) { 694 fi = xcalloc(1, sizeof(*fi)); 695 fi->file_path = xstrdup(cf->file_path); 696 fi->crevstr = xstrdup(rbuf); 697 fi->nrevstr = xstrdup(nbuf); 698 if (tag != NULL) 699 fi->tag_new = xstrdup(tag); 700 TAILQ_INSERT_TAIL(&files_info, fi, flist); 701 } 702 703 switch (cf->file_status) { 704 case FILE_MODIFIED: 705 histtype = CVS_HISTORY_COMMIT_MODIFIED; 706 break; 707 case FILE_ADDED: 708 histtype = CVS_HISTORY_COMMIT_ADDED; 709 break; 710 case FILE_REMOVED: 711 histtype = CVS_HISTORY_COMMIT_REMOVED; 712 break; 713 default: 714 histtype = -1; 715 break; 716 } 717 718 if (crev != NULL) 719 rcsnum_free(crev); 720 721 if (histtype != -1) 722 cvs_history_add(histtype, cf, NULL); 723 else 724 cvs_log(LP_NOTICE, "histtype was -1 for %s", cf->file_path); 725 } 726 727 static BUF * 728 commit_diff(struct cvs_file *cf, RCSNUM *rev, int reverse) 729 { 730 int fd1, fd2, d; 731 char *p1, *p2; 732 BUF *b; 733 734 (void)xasprintf(&p1, "%s/diff1.XXXXXXXXXX", cvs_tmpdir); 735 736 if (cf->file_status == FILE_MODIFIED || 737 cf->file_status == FILE_ADDED) { 738 b = buf_load_fd(cf->fd); 739 fd1 = buf_write_stmp(b, p1, NULL); 740 buf_free(b); 741 } else { 742 fd1 = rcs_rev_write_stmp(cf->file_rcs, rev, p1, 0); 743 } 744 745 (void)xasprintf(&p2, "%s/diff2.XXXXXXXXXX", cvs_tmpdir); 746 fd2 = rcs_rev_write_stmp(cf->file_rcs, rev, p2, RCS_KWEXP_NONE); 747 748 b = buf_alloc(128); 749 750 diff_format = D_RCSDIFF; 751 752 if (reverse == 1) 753 d = diffreg(p2, p1, fd2, fd1, b, D_FORCEASCII); 754 else 755 d = diffreg(p1, p2, fd1, fd2, b, D_FORCEASCII); 756 if (d == D_ERROR) 757 fatal("commit_diff: failed to get RCS patch"); 758 759 close(fd1); 760 close(fd2); 761 762 free(p1); 763 free(p2); 764 765 return (b); 766 } 767 768 static void 769 commit_desc_set(struct cvs_file *cf) 770 { 771 BUF *bp; 772 int fd; 773 char desc_path[PATH_MAX], *desc; 774 775 (void)xsnprintf(desc_path, PATH_MAX, "%s/%s/%s%s", 776 cf->file_wd, CVS_PATH_CVSDIR, cf->file_name, CVS_DESCR_FILE_EXT); 777 778 if ((fd = open(desc_path, O_RDONLY)) == -1) 779 return; 780 781 bp = buf_load_fd(fd); 782 buf_putc(bp, '\0'); 783 desc = buf_release(bp); 784 785 rcs_desc_set(cf->file_rcs, desc); 786 787 (void)close(fd); 788 (void)cvs_unlink(desc_path); 789 790 free(desc); 791 } 792