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