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