1 /* $OpenBSD: commit.c,v 1.108 2007/06/28 17:45:49 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 <string.h> 24 #include <unistd.h> 25 26 #include "cvs.h" 27 #include "diff.h" 28 #include "remote.h" 29 30 void cvs_commit_local(struct cvs_file *); 31 void cvs_commit_check_files(struct cvs_file *); 32 33 static BUF *commit_diff_file(struct cvs_file *); 34 static void commit_desc_set(struct cvs_file *); 35 36 struct cvs_flisthead files_affected; 37 struct cvs_flisthead files_added; 38 struct cvs_flisthead files_removed; 39 struct cvs_flisthead files_modified; 40 41 int conflicts_found; 42 char *logmsg = NULL; 43 44 struct cvs_cmd cvs_cmd_commit = { 45 CVS_OP_COMMIT, 0, "commit", 46 { "ci", "com" }, 47 "Check files into the repository", 48 "[-flR] [-F logfile | -m msg] [-r rev] ...", 49 "F:flm:Rr:", 50 NULL, 51 cvs_commit 52 }; 53 54 int 55 cvs_commit(int argc, char **argv) 56 { 57 int ch; 58 char *arg = "."; 59 int flags; 60 struct cvs_recursion cr; 61 62 flags = CR_RECURSE_DIRS; 63 64 while ((ch = getopt(argc, argv, cvs_cmd_commit.cmd_opts)) != -1) { 65 switch (ch) { 66 case 'F': 67 logmsg = cvs_logmsg_read(optarg); 68 break; 69 case 'f': 70 break; 71 case 'l': 72 flags &= ~CR_RECURSE_DIRS; 73 break; 74 case 'm': 75 logmsg = xstrdup(optarg); 76 break; 77 case 'R': 78 break; 79 case 'r': 80 break; 81 default: 82 fatal("%s", cvs_cmd_commit.cmd_synopsis); 83 } 84 } 85 86 argc -= optind; 87 argv += optind; 88 89 TAILQ_INIT(&files_affected); 90 TAILQ_INIT(&files_added); 91 TAILQ_INIT(&files_removed); 92 TAILQ_INIT(&files_modified); 93 conflicts_found = 0; 94 95 cr.enterdir = NULL; 96 cr.leavedir = NULL; 97 cr.fileproc = cvs_commit_check_files; 98 cr.flags = flags; 99 100 if (argc > 0) 101 cvs_file_run(argc, argv, &cr); 102 else 103 cvs_file_run(1, &arg, &cr); 104 105 if (conflicts_found != 0) 106 fatal("%d conflicts found, please correct these first", 107 conflicts_found); 108 109 if (TAILQ_EMPTY(&files_affected)) 110 return (0); 111 112 if (logmsg == NULL && cvs_server_active == 0) { 113 logmsg = cvs_logmsg_create(&files_added, &files_removed, 114 &files_modified); 115 } 116 117 if (logmsg == NULL) 118 fatal("This shouldnt happen, honestly!"); 119 120 cvs_file_freelist(&files_modified); 121 cvs_file_freelist(&files_removed); 122 cvs_file_freelist(&files_added); 123 124 if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) { 125 cvs_client_connect_to_server(); 126 cr.fileproc = cvs_client_sendfile; 127 128 if (argc > 0) 129 cvs_file_run(argc, argv, &cr); 130 else 131 cvs_file_run(1, &arg, &cr); 132 133 if (!(flags & CR_RECURSE_DIRS)) 134 cvs_client_send_request("Argument -l"); 135 136 cvs_client_send_request("Argument -m%s", logmsg); 137 138 cvs_client_send_files(argv, argc); 139 cvs_client_senddir("."); 140 cvs_client_send_request("ci"); 141 cvs_client_get_responses(); 142 } else { 143 cr.fileproc = cvs_commit_local; 144 cvs_file_walklist(&files_affected, &cr); 145 cvs_file_freelist(&files_affected); 146 } 147 148 return (0); 149 } 150 151 void 152 cvs_commit_check_files(struct cvs_file *cf) 153 { 154 cvs_log(LP_TRACE, "cvs_commit_check_files(%s)", cf->file_path); 155 156 if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) 157 cvs_remote_classify_file(cf); 158 else 159 cvs_file_classify(cf, NULL); 160 161 if (cf->file_type == CVS_DIR) { 162 if (verbosity > 1) 163 cvs_log(LP_NOTICE, "Examining %s", cf->file_path); 164 return; 165 } 166 167 if (cf->file_status == FILE_CONFLICT || 168 cf->file_status == FILE_UNLINK) { 169 conflicts_found++; 170 return; 171 } 172 173 if (cf->file_status != FILE_REMOVED && 174 update_has_conflict_markers(cf)) { 175 cvs_log(LP_ERR, "conflict: unresolved conflicts in %s from " 176 "merging, please fix these first", cf->file_path); 177 conflicts_found++; 178 return; 179 } 180 181 if (cf->file_status == FILE_MERGE || 182 cf->file_status == FILE_PATCH || 183 cf->file_status == FILE_CHECKOUT || 184 cf->file_status == FILE_LOST) { 185 cvs_log(LP_ERR, "conflict: %s is not up-to-date", 186 cf->file_path); 187 conflicts_found++; 188 return; 189 } 190 191 if (cf->file_status == FILE_ADDED || 192 cf->file_status == FILE_REMOVED || 193 cf->file_status == FILE_MODIFIED) 194 cvs_file_get(cf->file_path, &files_affected); 195 196 switch (cf->file_status) { 197 case FILE_ADDED: 198 cvs_file_get(cf->file_path, &files_added); 199 break; 200 case FILE_REMOVED: 201 cvs_file_get(cf->file_path, &files_removed); 202 break; 203 case FILE_MODIFIED: 204 cvs_file_get(cf->file_path, &files_modified); 205 break; 206 } 207 } 208 209 void 210 cvs_commit_local(struct cvs_file *cf) 211 { 212 BUF *b, *d; 213 int isnew, histtype; 214 RCSNUM *head; 215 int openflags, rcsflags; 216 char rbuf[24], nbuf[24]; 217 CVSENTRIES *entlist; 218 char attic[MAXPATHLEN], repo[MAXPATHLEN], rcsfile[MAXPATHLEN]; 219 220 cvs_log(LP_TRACE, "cvs_commit_local(%s)", cf->file_path); 221 cvs_file_classify(cf, NULL); 222 223 if (cvs_noexec == 1) 224 return; 225 226 if (cf->file_type != CVS_FILE) 227 fatal("cvs_commit_local: '%s' is not a file", cf->file_path); 228 229 if (cf->file_status != FILE_MODIFIED && 230 cf->file_status != FILE_ADDED && 231 cf->file_status != FILE_REMOVED) { 232 cvs_log(LP_ERR, "skipping bogus file `%s'", cf->file_path); 233 return; 234 } 235 236 if (cf->file_status == FILE_MODIFIED || 237 cf->file_status == FILE_REMOVED || (cf->file_status == FILE_ADDED 238 && cf->file_rcs != NULL && cf->file_rcs->rf_dead == 1)) { 239 head = rcs_head_get(cf->file_rcs); 240 rcsnum_tostr(head, rbuf, sizeof(rbuf)); 241 rcsnum_free(head); 242 } else { 243 strlcpy(rbuf, "Non-existent", sizeof(rbuf)); 244 } 245 246 isnew = 0; 247 if (cf->file_status == FILE_ADDED) { 248 isnew = 1; 249 rcsflags = RCS_CREATE; 250 openflags = O_CREAT | O_TRUNC | O_WRONLY; 251 if (cf->file_rcs != NULL) { 252 if (cf->in_attic == 0) 253 cvs_log(LP_ERR, "warning: expected %s " 254 "to be in the Attic", cf->file_path); 255 256 if (cf->file_rcs->rf_dead == 0) 257 cvs_log(LP_ERR, "warning: expected %s " 258 "to be dead", cf->file_path); 259 260 cvs_get_repository_path(cf->file_wd, repo, MAXPATHLEN); 261 (void)xsnprintf(rcsfile, MAXPATHLEN, "%s/%s%s", 262 repo, cf->file_name, RCS_FILE_EXT); 263 264 if (rename(cf->file_rpath, rcsfile) == -1) 265 fatal("cvs_commit_local: failed to move %s " 266 "outside the Attic: %s", cf->file_path, 267 strerror(errno)); 268 269 xfree(cf->file_rpath); 270 cf->file_rpath = xstrdup(rcsfile); 271 272 rcsflags = RCS_READ | RCS_PARSE_FULLY; 273 openflags = O_RDONLY; 274 rcs_close(cf->file_rcs); 275 isnew = 0; 276 } 277 278 cf->repo_fd = open(cf->file_rpath, openflags); 279 if (cf->repo_fd < 0) 280 fatal("cvs_commit_local: %s", strerror(errno)); 281 282 cf->file_rcs = rcs_open(cf->file_rpath, cf->repo_fd, 283 rcsflags, 0444); 284 if (cf->file_rcs == NULL) 285 fatal("cvs_commit_local: failed to create RCS file " 286 "for %s", cf->file_path); 287 288 commit_desc_set(cf); 289 } 290 291 if (verbosity > 1) { 292 cvs_printf("Checking in %s:\n", cf->file_path); 293 cvs_printf("%s <- %s\n", cf->file_rpath, cf->file_path); 294 cvs_printf("old revision: %s; ", rbuf); 295 } 296 297 if (isnew == 0) 298 d = commit_diff_file(cf); 299 300 if (cf->file_status == FILE_REMOVED) { 301 b = rcs_rev_getbuf(cf->file_rcs, cf->file_rcs->rf_head, 0); 302 if (b == NULL) 303 fatal("cvs_commit_local: failed to get HEAD"); 304 } else { 305 if ((b = cvs_buf_load_fd(cf->fd, BUF_AUTOEXT)) == NULL) 306 fatal("cvs_commit_local: failed to load file"); 307 } 308 309 if (isnew == 0) { 310 if (rcs_deltatext_set(cf->file_rcs, 311 cf->file_rcs->rf_head, d) == -1) 312 fatal("cvs_commit_local: failed to set delta"); 313 } 314 315 if (rcs_rev_add(cf->file_rcs, RCS_HEAD_REV, logmsg, -1, NULL) == -1) 316 fatal("cvs_commit_local: failed to add new revision"); 317 318 if (rcs_deltatext_set(cf->file_rcs, cf->file_rcs->rf_head, b) == -1) 319 fatal("cvs_commit_local: failed to set new HEAD delta"); 320 321 if (cf->file_status == FILE_REMOVED) { 322 if (rcs_state_set(cf->file_rcs, 323 cf->file_rcs->rf_head, RCS_STATE_DEAD) == -1) 324 fatal("cvs_commit_local: failed to set state"); 325 } 326 327 if (cf->file_rcs->rf_branch != NULL) { 328 rcsnum_free(cf->file_rcs->rf_branch); 329 cf->file_rcs->rf_branch = NULL; 330 } 331 332 if (cf->file_status == FILE_ADDED && cf->file_ent->ce_opts != NULL) { 333 int kflag; 334 335 kflag = rcs_kflag_get(cf->file_ent->ce_opts + 2); 336 rcs_kwexp_set(cf->file_rcs, kflag); 337 } 338 339 rcs_write(cf->file_rcs); 340 341 if (cf->file_status == FILE_REMOVED) { 342 strlcpy(nbuf, "Removed", sizeof(nbuf)); 343 } else if (cf->file_status == FILE_ADDED) { 344 if (cf->file_rcs->rf_dead == 1) 345 strlcpy(nbuf, "Initial Revision", sizeof(nbuf)); 346 else 347 rcsnum_tostr(cf->file_rcs->rf_head, 348 nbuf, sizeof(nbuf)); 349 } else if (cf->file_status == FILE_MODIFIED) { 350 rcsnum_tostr(cf->file_rcs->rf_head, nbuf, sizeof(nbuf)); 351 } 352 353 if (verbosity > 1) 354 cvs_printf("new revision: %s\n", nbuf); 355 356 (void)unlink(cf->file_path); 357 (void)close(cf->fd); 358 cf->fd = -1; 359 360 if (cf->file_status != FILE_REMOVED) { 361 cvs_checkout_file(cf, cf->file_rcs->rf_head, CO_COMMIT); 362 } else { 363 entlist = cvs_ent_open(cf->file_wd); 364 cvs_ent_remove(entlist, cf->file_name); 365 cvs_ent_close(entlist, ENT_SYNC); 366 367 cvs_get_repository_path(cf->file_wd, repo, MAXPATHLEN); 368 369 (void)xsnprintf(attic, MAXPATHLEN, "%s/%s", 370 repo, CVS_PATH_ATTIC); 371 372 if (mkdir(attic, 0755) == -1 && errno != EEXIST) 373 fatal("cvs_commit_local: failed to create Attic"); 374 375 (void)xsnprintf(attic, MAXPATHLEN, "%s/%s/%s%s", repo, 376 CVS_PATH_ATTIC, cf->file_name, RCS_FILE_EXT); 377 378 if (rename(cf->file_rpath, attic) == -1) 379 fatal("cvs_commit_local: failed to move %s to Attic", 380 cf->file_path); 381 382 if (cvs_server_active == 1) 383 cvs_server_update_entry("Remove-entry", cf); 384 } 385 386 if (verbosity > 1) 387 cvs_printf("done\n"); 388 else { 389 cvs_log(LP_NOTICE, "checking in '%s'; revision %s -> %s", 390 cf->file_path, rbuf, nbuf); 391 } 392 393 switch (cf->file_status) { 394 case FILE_MODIFIED: 395 histtype = CVS_HISTORY_COMMIT_MODIFIED; 396 break; 397 case FILE_ADDED: 398 histtype = CVS_HISTORY_COMMIT_ADDED; 399 break; 400 case FILE_REMOVED: 401 histtype = CVS_HISTORY_COMMIT_REMOVED; 402 break; 403 } 404 405 cvs_history_add(histtype, cf, NULL); 406 } 407 408 static BUF * 409 commit_diff_file(struct cvs_file *cf) 410 { 411 char *p1, *p2; 412 BUF *b1, *b2; 413 414 (void)xasprintf(&p1, "%s/diff1.XXXXXXXXXX", cvs_tmpdir); 415 416 if (cf->file_status == FILE_MODIFIED || 417 cf->file_status == FILE_ADDED) { 418 if ((b1 = cvs_buf_load_fd(cf->fd, BUF_AUTOEXT)) == NULL) 419 fatal("commit_diff_file: failed to load '%s'", 420 cf->file_path); 421 cvs_buf_write_stmp(b1, p1, NULL); 422 cvs_buf_free(b1); 423 } else { 424 rcs_rev_write_stmp(cf->file_rcs, cf->file_rcs->rf_head, p1, 0); 425 } 426 427 (void)xasprintf(&p2, "%s/diff2.XXXXXXXXXX", cvs_tmpdir); 428 rcs_rev_write_stmp(cf->file_rcs, cf->file_rcs->rf_head, p2, 0); 429 430 if ((b2 = cvs_buf_alloc(128, BUF_AUTOEXT)) == NULL) 431 fatal("commit_diff_file: failed to create diff buf"); 432 433 diff_format = D_RCSDIFF; 434 if (cvs_diffreg(p1, p2, b2) == D_ERROR) 435 fatal("commit_diff_file: failed to get RCS patch"); 436 437 xfree(p1); 438 xfree(p2); 439 440 return (b2); 441 } 442 443 static void 444 commit_desc_set(struct cvs_file *cf) 445 { 446 BUF *bp; 447 int fd; 448 char desc_path[MAXPATHLEN], *desc; 449 450 (void)xsnprintf(desc_path, MAXPATHLEN, "%s/%s%s", 451 CVS_PATH_CVSDIR, cf->file_name, CVS_DESCR_FILE_EXT); 452 453 if ((fd = open(desc_path, O_RDONLY)) == -1) 454 return; 455 456 bp = cvs_buf_load_fd(fd, BUF_AUTOEXT); 457 cvs_buf_putc(bp, '\0'); 458 desc = cvs_buf_release(bp); 459 460 rcs_desc_set(cf->file_rcs, desc); 461 462 (void)close(fd); 463 (void)cvs_unlink(desc_path); 464 465 xfree(desc); 466 } 467