1 /* $OpenBSD: update.c,v 1.160 2009/03/24 17:03:32 joris Exp $ */ 2 /* 3 * Copyright (c) 2006 Joris Vink <joris@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/stat.h> 19 20 #include <errno.h> 21 #include <fcntl.h> 22 #include <string.h> 23 #include <unistd.h> 24 25 #include "cvs.h" 26 #include "diff.h" 27 #include "remote.h" 28 29 int prune_dirs = 0; 30 int print_stdout = 0; 31 int build_dirs = 0; 32 int reset_option = 0; 33 int reset_tag = 0; 34 char *cvs_specified_tag = NULL; 35 char *cvs_join_rev1 = NULL; 36 char *cvs_join_rev2 = NULL; 37 38 static char *koptstr; 39 static char *dateflag = NULL; 40 static int Aflag = 0; 41 42 static void update_clear_conflict(struct cvs_file *); 43 static void update_join_file(struct cvs_file *); 44 45 extern CVSENTRIES *current_list; 46 47 struct cvs_cmd cvs_cmd_update = { 48 CVS_OP_UPDATE, CVS_USE_WDIR, "update", 49 { "up", "upd" }, 50 "Bring work tree in sync with repository", 51 "[-ACdflPpR] [-D date | -r rev] [-I ign] [-j rev] [-k mode] " 52 "[-t id] ...", 53 "ACD:dfI:j:k:lPpQqRr:t:u", 54 NULL, 55 cvs_update 56 }; 57 58 int 59 cvs_update(int argc, char **argv) 60 { 61 int ch; 62 char *arg = "."; 63 int flags; 64 struct cvs_recursion cr; 65 66 flags = CR_RECURSE_DIRS; 67 68 while ((ch = getopt(argc, argv, cvs_cmd_update.cmd_opts)) != -1) { 69 switch (ch) { 70 case 'A': 71 Aflag = 1; 72 if (koptstr == NULL) 73 reset_option = 1; 74 if (cvs_specified_tag == NULL) 75 reset_tag = 1; 76 break; 77 case 'C': 78 break; 79 case 'D': 80 dateflag = optarg; 81 cvs_specified_date = cvs_date_parse(dateflag); 82 reset_tag = 0; 83 break; 84 case 'd': 85 build_dirs = 1; 86 break; 87 case 'f': 88 break; 89 case 'I': 90 break; 91 case 'j': 92 if (cvs_join_rev1 == NULL) 93 cvs_join_rev1 = optarg; 94 else if (cvs_join_rev2 == NULL) 95 cvs_join_rev2 = optarg; 96 else 97 fatal("too many -j options"); 98 break; 99 case 'k': 100 reset_option = 0; 101 koptstr = optarg; 102 kflag = rcs_kflag_get(koptstr); 103 if (RCS_KWEXP_INVAL(kflag)) { 104 cvs_log(LP_ERR, 105 "invalid RCS keyword expansion mode"); 106 fatal("%s", cvs_cmd_update.cmd_synopsis); 107 } 108 break; 109 case 'l': 110 flags &= ~CR_RECURSE_DIRS; 111 break; 112 case 'P': 113 prune_dirs = 1; 114 break; 115 case 'p': 116 print_stdout = 1; 117 cvs_noexec = 1; 118 break; 119 case 'Q': 120 case 'q': 121 break; 122 case 'R': 123 flags |= CR_RECURSE_DIRS; 124 break; 125 case 'r': 126 reset_tag = 0; 127 cvs_specified_tag = optarg; 128 break; 129 case 'u': 130 break; 131 default: 132 fatal("%s", cvs_cmd_update.cmd_synopsis); 133 } 134 } 135 136 argc -= optind; 137 argv += optind; 138 139 if (current_cvsroot->cr_method == CVS_METHOD_LOCAL) { 140 cr.enterdir = cvs_update_enterdir; 141 cr.leavedir = prune_dirs ? cvs_update_leavedir : NULL; 142 cr.fileproc = cvs_update_local; 143 flags |= CR_REPO; 144 } else { 145 cvs_client_connect_to_server(); 146 if (Aflag) 147 cvs_client_send_request("Argument -A"); 148 if (dateflag != NULL) 149 cvs_client_send_request("Argument -D%s", dateflag); 150 if (build_dirs) 151 cvs_client_send_request("Argument -d"); 152 if (kflag) 153 cvs_client_send_request("Argument -k%s", koptstr); 154 if (!(flags & CR_RECURSE_DIRS)) 155 cvs_client_send_request("Argument -l"); 156 if (prune_dirs) 157 cvs_client_send_request("Argument -P"); 158 if (print_stdout) 159 cvs_client_send_request("Argument -p"); 160 161 if (cvs_specified_tag != NULL) 162 cvs_client_send_request("Argument -r%s", 163 cvs_specified_tag); 164 165 cr.enterdir = NULL; 166 cr.leavedir = NULL; 167 cr.fileproc = cvs_client_sendfile; 168 } 169 170 cr.flags = flags; 171 172 if (argc > 0) 173 cvs_file_run(argc, argv, &cr); 174 else 175 cvs_file_run(1, &arg, &cr); 176 177 if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) { 178 cvs_client_send_files(argv, argc); 179 cvs_client_senddir("."); 180 cvs_client_send_request("update"); 181 cvs_client_get_responses(); 182 } 183 184 return (0); 185 } 186 187 void 188 cvs_update_enterdir(struct cvs_file *cf) 189 { 190 CVSENTRIES *entlist; 191 char *dirtag, *entry, fpath[MAXPATHLEN]; 192 193 cvs_log(LP_TRACE, "cvs_update_enterdir(%s)", cf->file_path); 194 195 cvs_file_classify(cf, NULL); 196 197 if (cf->file_status == DIR_CREATE && build_dirs == 1) { 198 cvs_parse_tagfile(cf->file_wd, &dirtag, NULL, NULL); 199 cvs_mkpath(cf->file_path, cvs_specified_tag != NULL ? 200 cvs_specified_tag : dirtag); 201 if (dirtag != NULL) 202 xfree(dirtag); 203 204 if ((cf->fd = open(cf->file_path, O_RDONLY)) == -1) 205 fatal("cvs_update_enterdir: `%s': %s", 206 cf->file_path, strerror(errno)); 207 208 if (cvs_server_active == 1 && cvs_cmdop != CVS_OP_CHECKOUT) 209 cvs_server_clear_sticky(cf->file_path); 210 211 if (cvs_cmdop != CVS_OP_EXPORT) { 212 (void)xasprintf(&entry, "D/%s////", cf->file_name); 213 214 entlist = cvs_ent_open(cf->file_wd); 215 cvs_ent_add(entlist, entry); 216 xfree(entry); 217 } 218 } else if ((cf->file_status == DIR_CREATE && build_dirs == 0) || 219 cf->file_status == FILE_UNKNOWN) { 220 cf->file_status = FILE_SKIP; 221 } else if (reset_tag) { 222 (void)xsnprintf(fpath, MAXPATHLEN, "%s/%s", 223 cf->file_path, CVS_PATH_TAG); 224 (void)unlink(fpath); 225 } else { 226 if (cvs_specified_tag != NULL || cvs_specified_date != -1) 227 cvs_write_tagfile(cf->file_path, 228 cvs_specified_tag, NULL); 229 } 230 } 231 232 void 233 cvs_update_leavedir(struct cvs_file *cf) 234 { 235 long base; 236 int nbytes; 237 int isempty; 238 size_t bufsize; 239 struct stat st; 240 struct dirent *dp; 241 char *buf, *ebuf, *cp; 242 CVSENTRIES *entlist; 243 244 cvs_log(LP_TRACE, "cvs_update_leavedir(%s)", cf->file_path); 245 246 if (cvs_server_active == 1 && !strcmp(cf->file_name, ".")) 247 return; 248 249 entlist = cvs_ent_open(cf->file_path); 250 if (!TAILQ_EMPTY(&(entlist->cef_ent))) { 251 isempty = 0; 252 goto prune_it; 253 } 254 255 if (fstat(cf->fd, &st) == -1) 256 fatal("cvs_update_leavedir: %s", strerror(errno)); 257 258 bufsize = st.st_size; 259 if (bufsize < st.st_blksize) 260 bufsize = st.st_blksize; 261 262 if (st.st_size > SIZE_MAX) 263 fatal("cvs_update_leavedir: %s: file size too big", 264 cf->file_name); 265 266 isempty = 1; 267 buf = xmalloc(bufsize); 268 269 if (lseek(cf->fd, 0, SEEK_SET) == -1) 270 fatal("cvs_update_leavedir: %s", strerror(errno)); 271 272 while ((nbytes = getdirentries(cf->fd, buf, bufsize, &base)) > 0) { 273 ebuf = buf + nbytes; 274 cp = buf; 275 276 while (cp < ebuf) { 277 dp = (struct dirent *)cp; 278 if (!strcmp(dp->d_name, ".") || 279 !strcmp(dp->d_name, "..") || 280 dp->d_fileno == 0) { 281 cp += dp->d_reclen; 282 continue; 283 } 284 285 if (strcmp(dp->d_name, CVS_PATH_CVSDIR)) 286 isempty = 0; 287 288 if (isempty == 0) 289 break; 290 291 cp += dp->d_reclen; 292 } 293 } 294 295 if (nbytes == -1) 296 fatal("cvs_update_leavedir: %s", strerror(errno)); 297 298 xfree(buf); 299 300 prune_it: 301 if ((isempty == 1 && prune_dirs == 1) || 302 (cvs_server_active == 1 && cvs_cmdop == CVS_OP_CHECKOUT)) { 303 /* XXX */ 304 cvs_rmdir(cf->file_path); 305 306 if (cvs_server_active == 0 && cvs_cmdop != CVS_OP_EXPORT) { 307 entlist = cvs_ent_open(cf->file_wd); 308 cvs_ent_remove(entlist, cf->file_name); 309 } 310 } 311 } 312 313 void 314 cvs_update_local(struct cvs_file *cf) 315 { 316 CVSENTRIES *entlist; 317 int ent_kflag, rcs_kflag, ret, flags; 318 char *tag, rbuf[CVS_REV_BUFSZ]; 319 320 cvs_log(LP_TRACE, "cvs_update_local(%s)", cf->file_path); 321 322 if (cf->file_type == CVS_DIR) { 323 if (cf->file_status == FILE_SKIP) { 324 if (cvs_cmdop == CVS_OP_EXPORT && verbosity > 0) 325 cvs_printf("? %s\n", cf->file_path); 326 return; 327 } 328 329 if (cf->file_status != FILE_UNKNOWN && 330 verbosity > 1) 331 cvs_log(LP_ERR, "Updating %s", cf->file_path); 332 return; 333 } 334 335 flags = 0; 336 if (cvs_specified_tag != NULL) 337 tag = cvs_specified_tag; 338 else if (cf->file_ent != NULL && cf->file_ent->ce_tag != NULL) 339 tag = cf->file_ent->ce_tag; 340 else 341 tag = cvs_directory_tag; 342 343 cvs_file_classify(cf, tag); 344 345 if (kflag && cf->file_rcs != NULL) 346 rcs_kwexp_set(cf->file_rcs, kflag); 347 348 if ((cf->file_status == FILE_UPTODATE || 349 cf->file_status == FILE_MODIFIED) && cf->file_ent != NULL && 350 cf->file_ent->ce_tag != NULL && reset_tag) { 351 if (cf->file_status == FILE_MODIFIED) 352 cf->file_status = FILE_MERGE; 353 else 354 cf->file_status = FILE_CHECKOUT; 355 356 if ((cf->file_rcsrev = rcs_head_get(cf->file_rcs)) == NULL) 357 fatal("no head revision in RCS file for %s", 358 cf->file_path); 359 360 /* might be a bit overkill */ 361 if (cvs_server_active == 1) 362 cvs_server_clear_sticky(cf->file_wd); 363 } 364 365 if (print_stdout) { 366 if (cf->file_status != FILE_UNKNOWN && cf->file_rcs != NULL && 367 cf->file_rcsrev != NULL && !cf->file_rcs->rf_dead && 368 (cf->file_flags & FILE_HAS_TAG)) { 369 rcsnum_tostr(cf->file_rcsrev, rbuf, sizeof(rbuf)); 370 if (verbosity > 1) { 371 cvs_log(LP_RCS, RCS_DIFF_DIV); 372 cvs_log(LP_RCS, "Checking out %s", 373 cf->file_path); 374 cvs_log(LP_RCS, "RCS: %s", cf->file_rpath); 375 cvs_log(LP_RCS, "VERS: %s", rbuf); 376 cvs_log(LP_RCS, "***************"); 377 } 378 cvs_checkout_file(cf, cf->file_rcsrev, tag, CO_DUMP); 379 } 380 return; 381 } 382 383 if (cf->file_ent != NULL) { 384 if (cf->file_ent->ce_opts == NULL) { 385 if (kflag) 386 cf->file_status = FILE_CHECKOUT; 387 } else if (cf->file_rcs != NULL) { 388 if (strlen(cf->file_ent->ce_opts) < 3) 389 fatal("malformed option for file %s", 390 cf->file_path); 391 392 ent_kflag = rcs_kflag_get(cf->file_ent->ce_opts + 2); 393 rcs_kflag = rcs_kwexp_get(cf->file_rcs); 394 395 if ((kflag && (kflag != ent_kflag)) || 396 (reset_option && (ent_kflag != rcs_kflag))) 397 cf->file_status = FILE_CHECKOUT; 398 } 399 } 400 401 switch (cf->file_status) { 402 case FILE_UNKNOWN: 403 cvs_printf("? %s\n", cf->file_path); 404 break; 405 case FILE_MODIFIED: 406 ret = update_has_conflict_markers(cf); 407 if (cf->file_ent->ce_conflict != NULL && ret == 1) { 408 cvs_printf("C %s\n", cf->file_path); 409 } else { 410 if (cf->file_ent->ce_conflict != NULL && ret == 0) 411 update_clear_conflict(cf); 412 cvs_printf("M %s\n", cf->file_path); 413 } 414 break; 415 case FILE_ADDED: 416 cvs_printf("A %s\n", cf->file_path); 417 break; 418 case FILE_REMOVED: 419 cvs_printf("R %s\n", cf->file_path); 420 break; 421 case FILE_CONFLICT: 422 cvs_printf("C %s\n", cf->file_path); 423 break; 424 case FILE_LOST: 425 case FILE_CHECKOUT: 426 case FILE_PATCH: 427 if (!reset_tag && (tag != NULL || cvs_specified_date != -1 || 428 cvs_directory_date != -1 || (cf->file_ent != NULL && 429 cf->file_ent->ce_tag != NULL))) 430 flags = CO_SETSTICKY; 431 432 cvs_checkout_file(cf, cf->file_rcsrev, tag, flags); 433 cvs_printf("U %s\n", cf->file_path); 434 cvs_history_add(CVS_HISTORY_UPDATE_CO, cf, NULL); 435 break; 436 case FILE_MERGE: 437 d3rev1 = cf->file_ent->ce_rev; 438 d3rev2 = cf->file_rcsrev; 439 cvs_checkout_file(cf, cf->file_rcsrev, tag, CO_MERGE); 440 441 if (diff3_conflicts != 0) { 442 cvs_printf("C %s\n", cf->file_path); 443 cvs_history_add(CVS_HISTORY_UPDATE_MERGED_ERR, 444 cf, NULL); 445 } else { 446 update_clear_conflict(cf); 447 cvs_printf("M %s\n", cf->file_path); 448 cvs_history_add(CVS_HISTORY_UPDATE_MERGED, cf, NULL); 449 } 450 break; 451 case FILE_UNLINK: 452 (void)unlink(cf->file_path); 453 case FILE_REMOVE_ENTRY: 454 entlist = cvs_ent_open(cf->file_wd); 455 cvs_ent_remove(entlist, cf->file_name); 456 cvs_history_add(CVS_HISTORY_UPDATE_REMOVE, cf, NULL); 457 458 if (cvs_server_active == 1) 459 cvs_checkout_file(cf, cf->file_rcsrev, tag, CO_REMOVE); 460 break; 461 case FILE_UPTODATE: 462 if (cvs_cmdop != CVS_OP_UPDATE) 463 break; 464 465 if (reset_tag != 1 && reset_option != 1) 466 break; 467 468 if (cf->file_ent != NULL && cf->file_ent->ce_tag == NULL) 469 break; 470 471 if (cf->file_rcs->rf_dead != 1 && 472 (cf->file_flags & FILE_HAS_TAG)) 473 cvs_checkout_file(cf, cf->file_rcsrev, 474 tag, CO_SETSTICKY); 475 break; 476 default: 477 break; 478 } 479 480 if (cvs_join_rev1 != NULL) 481 update_join_file(cf); 482 } 483 484 static void 485 update_clear_conflict(struct cvs_file *cf) 486 { 487 CVSENTRIES *entlist; 488 char *entry, revbuf[CVS_REV_BUFSZ]; 489 char sticky[CVS_ENT_MAXLINELEN], opt[4]; 490 491 cvs_log(LP_TRACE, "update_clear_conflict(%s)", cf->file_path); 492 493 rcsnum_tostr(cf->file_rcsrev, revbuf, sizeof(revbuf)); 494 495 sticky[0] = '\0'; 496 if (cf->file_ent != NULL && cf->file_ent->ce_tag != NULL) 497 (void)xsnprintf(sticky, sizeof(sticky), "T%s", 498 cf->file_ent->ce_tag); 499 500 opt[0] = '\0'; 501 if (cf->file_ent != NULL && cf->file_ent->ce_opts != NULL) 502 strlcpy(opt, cf->file_ent->ce_opts, sizeof(opt)); 503 504 entry = xmalloc(CVS_ENT_MAXLINELEN); 505 cvs_ent_line_str(cf->file_name, revbuf, "Result of merge", 506 opt[0] != '\0' ? opt : "", sticky, 0, 0, 507 entry, CVS_ENT_MAXLINELEN); 508 509 entlist = cvs_ent_open(cf->file_wd); 510 cvs_ent_add(entlist, entry); 511 xfree(entry); 512 } 513 514 /* 515 * XXX - this is the way GNU cvs checks for outstanding conflicts 516 * in a file after a merge. It is a very very bad approach and 517 * should be looked at once opencvs is working decently. 518 */ 519 int 520 update_has_conflict_markers(struct cvs_file *cf) 521 { 522 BUF *bp; 523 int conflict; 524 char *content; 525 struct cvs_line *lp; 526 struct cvs_lines *lines; 527 size_t len; 528 529 cvs_log(LP_TRACE, "update_has_conflict_markers(%s)", cf->file_path); 530 531 if (!(cf->file_flags & FILE_ON_DISK) || cf->file_ent == NULL) 532 return (0); 533 534 bp = cvs_buf_load_fd(cf->fd); 535 536 cvs_buf_putc(bp, '\0'); 537 len = cvs_buf_len(bp); 538 content = cvs_buf_release(bp); 539 if ((lines = cvs_splitlines(content, len)) == NULL) 540 fatal("update_has_conflict_markers: failed to split lines"); 541 542 conflict = 0; 543 TAILQ_FOREACH(lp, &(lines->l_lines), l_list) { 544 if (lp->l_line == NULL) 545 continue; 546 547 if (!strncmp(lp->l_line, RCS_CONFLICT_MARKER1, 548 sizeof(RCS_CONFLICT_MARKER1) - 1) || 549 !strncmp(lp->l_line, RCS_CONFLICT_MARKER2, 550 sizeof(RCS_CONFLICT_MARKER2) - 1) || 551 !strncmp(lp->l_line, RCS_CONFLICT_MARKER3, 552 sizeof(RCS_CONFLICT_MARKER3) - 1)) { 553 conflict = 1; 554 break; 555 } 556 } 557 558 cvs_freelines(lines); 559 xfree(content); 560 return (conflict); 561 } 562 563 void 564 update_join_file(struct cvs_file *cf) 565 { 566 int flag; 567 time_t told; 568 RCSNUM *rev1, *rev2; 569 const char *state1, *state2; 570 char rbuf[CVS_REV_BUFSZ], *jrev1, *jrev2, *p; 571 572 rev1 = rev2 = NULL; 573 jrev1 = jrev2 = NULL; 574 575 jrev1 = xstrdup(cvs_join_rev1); 576 if (cvs_join_rev2 != NULL) 577 jrev2 = xstrdup(cvs_join_rev2); 578 579 if (jrev2 == NULL) { 580 jrev2 = jrev1; 581 jrev1 = NULL; 582 } 583 584 told = cvs_specified_date; 585 586 if ((p = strchr(jrev2, ':')) != NULL) { 587 (*p++) = '\0'; 588 cvs_specified_date = cvs_date_parse(p); 589 } 590 591 rev2 = rcs_translate_tag(jrev2, cf->file_rcs); 592 cvs_specified_date = told; 593 594 if (jrev1 != NULL) { 595 if ((p = strchr(jrev1, ':')) != NULL) { 596 (*p++) = '\0'; 597 cvs_specified_date = cvs_date_parse(p); 598 } 599 600 rev1 = rcs_translate_tag(jrev1, cf->file_rcs); 601 cvs_specified_date = told; 602 } else { 603 if (rev2 == NULL) 604 goto out; 605 606 rev1 = rcsnum_alloc(); 607 rcsnum_cpy(cf->file_rcsrev, rev1, 0); 608 } 609 610 state1 = state2 = RCS_STATE_DEAD; 611 612 if (rev1 != NULL) 613 state1 = rcs_state_get(cf->file_rcs, rev1); 614 if (rev2 != NULL) 615 state2 = rcs_state_get(cf->file_rcs, rev2); 616 617 if (rev2 == NULL || !strcmp(state2, RCS_STATE_DEAD)) { 618 if (rev1 == NULL || !strcmp(state1, RCS_STATE_DEAD)) 619 goto out; 620 621 if (cf->file_status == FILE_REMOVED || 622 cf->file_rcs->rf_dead == 1) 623 goto out; 624 625 if (cf->file_status == FILE_MODIFIED || 626 cf->file_status == FILE_ADDED) 627 goto out; 628 629 (void)unlink(cf->file_path); 630 (void)close(cf->fd); 631 cf->fd = -1; 632 cvs_remove_local(cf); 633 goto out; 634 } 635 636 if (cf->file_ent != NULL) { 637 if (!rcsnum_cmp(cf->file_ent->ce_rev, rev2, 0)) 638 goto out; 639 } 640 641 if (cf->file_rcsrev == NULL) { 642 cvs_printf("non-mergable file: %s has no head revision!\n", 643 cf->file_path); 644 goto out; 645 } 646 647 if (rev1 == NULL || !strcmp(state1, RCS_STATE_DEAD)) { 648 if (cf->file_flags & FILE_ON_DISK) { 649 cvs_printf("%s exists but has been added in %s\n", 650 cf->file_path, jrev2); 651 } else { 652 cvs_printf("A %s\n", cf->file_path); 653 cvs_checkout_file(cf, cf->file_rcsrev, NULL, 0); 654 cvs_add_local(cf); 655 } 656 goto out; 657 } 658 659 if (!rcsnum_cmp(rev1, rev2, 0)) 660 goto out; 661 662 if (!(cf->file_flags & FILE_ON_DISK)) { 663 cvs_printf("%s does not exist but is present in %s\n", 664 cf->file_path, jrev2); 665 goto out; 666 } 667 668 flag = rcs_kwexp_get(cf->file_rcs); 669 if (flag & RCS_KWEXP_NONE) { 670 cvs_printf("non-mergable file: %s needs merge!\n", 671 cf->file_path); 672 goto out; 673 } 674 675 cvs_printf("joining "); 676 rcsnum_tostr(rev1, rbuf, sizeof(rbuf)); 677 cvs_printf("%s ", rbuf); 678 679 rcsnum_tostr(rev2, rbuf, sizeof(rbuf)); 680 cvs_printf("%s ", rbuf); 681 682 rcsnum_tostr(cf->file_rcsrev, rbuf, sizeof(rbuf)); 683 cvs_printf("into %s (%s)\n", cf->file_path, rbuf); 684 685 d3rev1 = rev1; 686 d3rev2 = rev2; 687 cvs_checkout_file(cf, cf->file_rcsrev, NULL, CO_MERGE); 688 689 if (diff3_conflicts == 0) 690 update_clear_conflict(cf); 691 692 out: 693 if (rev1 != NULL) 694 rcsnum_free(rev1); 695 if (rev2 != NULL) 696 rcsnum_free(rev2); 697 698 if (jrev1 != NULL) 699 xfree(jrev1); 700 if (jrev2 != NULL) 701 xfree(jrev2); 702 } 703