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