1 /* $OpenBSD: commit.c,v 1.70 2006/06/06 05:18:23 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 "includes.h" 19 20 #include "cvs.h" 21 #include "diff.h" 22 #include "log.h" 23 #include "proto.h" 24 25 int cvs_commit(int, char **); 26 void cvs_commit_local(struct cvs_file *); 27 void cvs_commit_check_conflicts(struct cvs_file *); 28 29 static char *commit_diff_file(struct cvs_file *); 30 31 struct cvs_flisthead files_affected; 32 int conflicts_found; 33 char *logmsg; 34 35 struct cvs_cmd cvs_cmd_commit = { 36 CVS_OP_COMMIT, CVS_REQ_CI, "commit", 37 { "ci", "com" }, 38 "Check files into the repository", 39 "[-flR] [-F logfile | -m msg] [-r rev] ...", 40 "F:flm:Rr:", 41 NULL, 42 cvs_commit 43 }; 44 45 int 46 cvs_commit(int argc, char **argv) 47 { 48 int ch; 49 BUF *bp; 50 char *arg = "."; 51 int flags; 52 struct cvs_recursion cr; 53 54 flags = CR_RECURSE_DIRS; 55 56 while ((ch = getopt(argc, argv, cvs_cmd_commit.cmd_opts)) != -1) { 57 switch (ch) { 58 case 'f': 59 break; 60 case 'F': 61 bp = cvs_buf_load(optarg, BUF_AUTOEXT); 62 if (bp == NULL) 63 fatal("failed to load commit message %s", 64 optarg); 65 cvs_buf_putc(bp, '\0'); 66 logmsg = cvs_buf_release(bp); 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 if (logmsg == NULL) 87 fatal("please use -m to specify a log message for now"); 88 89 TAILQ_INIT(&files_affected); 90 conflicts_found = 0; 91 92 cr.enterdir = NULL; 93 cr.leavedir = NULL; 94 cr.local = cvs_commit_check_conflicts; 95 cr.remote = NULL; 96 cr.flags = flags; 97 98 if (argc > 0) 99 cvs_file_run(argc, argv, &cr); 100 else 101 cvs_file_run(1, &arg, &cr); 102 103 if (conflicts_found != 0) 104 fatal("%d conflicts found, please correct these first", 105 conflicts_found); 106 107 cr.local = cvs_commit_local; 108 cvs_file_walklist(&files_affected, &cr); 109 cvs_file_freelist(&files_affected); 110 111 return (0); 112 } 113 114 void 115 cvs_commit_check_conflicts(struct cvs_file *cf) 116 { 117 cvs_log(LP_TRACE, "cvs_commit_check_conflicts(%s)", cf->file_path); 118 119 /* 120 * cvs_file_classify makes the noise for us 121 * XXX - we want that? 122 */ 123 cvs_file_classify(cf, NULL, 1); 124 125 if (cf->file_type == CVS_DIR) { 126 if (verbosity > 1) 127 cvs_log(LP_NOTICE, "Examining %s", cf->file_path); 128 return; 129 } 130 131 if (cf->file_status == FILE_CONFLICT || 132 cf->file_status == FILE_LOST || 133 cf->file_status == FILE_UNLINK) 134 conflicts_found++; 135 136 if (cf->file_status != FILE_REMOVED && 137 update_has_conflict_markers(cf)) { 138 cvs_log(LP_ERR, "conflict: unresolved conflicts in %s from " 139 "merging, please fix these first", cf->file_path); 140 conflicts_found++; 141 } 142 143 if (cf->file_status == FILE_MERGE || 144 cf->file_status == FILE_PATCH) { 145 cvs_log(LP_ERR, "conflict: %s is not up-to-date", 146 cf->file_path); 147 conflicts_found++; 148 } 149 150 if (cf->file_status == FILE_ADDED || 151 cf->file_status == FILE_REMOVED || 152 cf->file_status == FILE_MODIFIED) 153 cvs_file_get(cf->file_path, &files_affected); 154 } 155 156 void 157 cvs_commit_local(struct cvs_file *cf) 158 { 159 BUF *b; 160 int isnew; 161 int l, openflags, rcsflags; 162 char *d, *f, rbuf[24]; 163 CVSENTRIES *entlist; 164 char *attic, *repo, *rcsfile; 165 166 cvs_log(LP_TRACE, "cvs_commit_local(%s)", cf->file_path); 167 cvs_file_classify(cf, NULL, 0); 168 169 if (cvs_noexec == 1) 170 return; 171 172 if (cf->file_type != CVS_FILE) 173 fatal("cvs_commit_local: '%s' is not a file", cf->file_path); 174 175 if (cf->file_status == FILE_MODIFIED || 176 cf->file_status == FILE_REMOVED || (cf->file_status == FILE_ADDED 177 && cf->file_rcs != NULL && cf->file_rcs->rf_dead == 1)) 178 rcsnum_tostr(rcs_head_get(cf->file_rcs), rbuf, sizeof(rbuf)); 179 else 180 strlcpy(rbuf, "Non-existent", sizeof(rbuf)); 181 182 isnew = 0; 183 if (cf->file_status == FILE_ADDED) { 184 isnew = 1; 185 rcsflags = RCS_CREATE; 186 openflags = O_CREAT | O_TRUNC | O_WRONLY; 187 if (cf->file_rcs != NULL) { 188 if (cf->file_rcs->rf_inattic == 0) 189 cvs_log(LP_ERR, "warning: expected %s " 190 "to be in the Attic", cf->file_path); 191 192 if (cf->file_rcs->rf_dead == 0) 193 cvs_log(LP_ERR, "warning: expected %s " 194 "to be dead", cf->file_path); 195 196 rcsfile = xmalloc(MAXPATHLEN); 197 repo = xmalloc(MAXPATHLEN); 198 cvs_get_repository_path(cf->file_wd, repo, MAXPATHLEN); 199 l = snprintf(rcsfile, MAXPATHLEN, "%s/%s%s", 200 repo, cf->file_name, RCS_FILE_EXT); 201 if (l == -1 || l >= MAXPATHLEN) 202 fatal("cvs_commit_local: overflow"); 203 204 if (rename(cf->file_rpath, rcsfile) == -1) 205 fatal("cvs_commit_local: failed to move %s " 206 "outside the Attic: %s", cf->file_path, 207 strerror(errno)); 208 209 xfree(cf->file_rpath); 210 cf->file_rpath = xstrdup(rcsfile); 211 xfree(rcsfile); 212 xfree(repo); 213 214 rcsflags = RCS_READ | RCS_PARSE_FULLY; 215 openflags = O_RDONLY; 216 rcs_close(cf->file_rcs); 217 isnew = 0; 218 } 219 220 cf->repo_fd = open(cf->file_rpath, openflags); 221 if (cf->repo_fd < 0) 222 fatal("cvs_commit_local: %s", strerror(errno)); 223 224 cf->file_rcs = rcs_open(cf->file_rpath, cf->repo_fd, 225 rcsflags, 0600); 226 if (cf->file_rcs == NULL) 227 fatal("cvs_commit_local: failed to create RCS file " 228 "for %s", cf->file_path); 229 } 230 231 cvs_printf("Checking in %s:\n", cf->file_path); 232 cvs_printf("%s <- %s\n", cf->file_rpath, cf->file_path); 233 cvs_printf("old revision: %s; ", rbuf); 234 235 if (isnew == 0) 236 d = commit_diff_file(cf); 237 238 if (cf->file_status == FILE_REMOVED) { 239 b = rcs_getrev(cf->file_rcs, cf->file_rcs->rf_head); 240 if (b == NULL) 241 fatal("cvs_commit_local: failed to get HEAD"); 242 } else { 243 if ((b = cvs_buf_load(cf->file_path, BUF_AUTOEXT)) == NULL) 244 fatal("cvs_commit_local: failed to load file"); 245 } 246 247 cvs_buf_putc(b, '\0'); 248 f = cvs_buf_release(b); 249 250 if (isnew == 0) { 251 if (rcs_deltatext_set(cf->file_rcs, 252 cf->file_rcs->rf_head, d) == -1) 253 fatal("cvs_commit_local: failed to set delta"); 254 } 255 256 if (rcs_rev_add(cf->file_rcs, RCS_HEAD_REV, logmsg, -1, NULL) == -1) 257 fatal("cvs_commit_local: failed to add new revision"); 258 259 if (rcs_deltatext_set(cf->file_rcs, cf->file_rcs->rf_head, f) == -1) 260 fatal("cvs_commit_local: failed to set new HEAD delta"); 261 262 xfree(f); 263 264 if (isnew == 0) 265 xfree(d); 266 267 if (cf->file_status == FILE_REMOVED) { 268 if (rcs_state_set(cf->file_rcs, 269 cf->file_rcs->rf_head, RCS_STATE_DEAD) == -1) 270 fatal("cvs_commit_local: failed to set state"); 271 } 272 273 if (cf->file_rcs->rf_branch != NULL) { 274 rcsnum_free(cf->file_rcs->rf_branch); 275 cf->file_rcs->rf_branch = NULL; 276 } 277 278 rcs_write(cf->file_rcs); 279 280 if (cf->file_status == FILE_REMOVED) { 281 strlcpy(rbuf, "Removed", sizeof(rbuf)); 282 } else if (cf->file_status == FILE_ADDED) { 283 if (cf->file_rcs->rf_dead == 1) 284 strlcpy(rbuf, "Initial Revision", sizeof(rbuf)); 285 else 286 rcsnum_tostr(cf->file_rcs->rf_head, 287 rbuf, sizeof(rbuf)); 288 } else if (cf->file_status == FILE_MODIFIED) { 289 rcsnum_tostr(cf->file_rcs->rf_head, rbuf, sizeof(rbuf)); 290 } 291 292 cvs_printf("new revision: %s\n", rbuf); 293 294 (void)unlink(cf->file_path); 295 (void)close(cf->fd); 296 cf->fd = -1; 297 298 if (cf->file_status != FILE_REMOVED) { 299 b = rcs_getrev(cf->file_rcs, cf->file_rcs->rf_head); 300 if (b == NULL) 301 fatal("cvs_commit_local: failed to get HEAD"); 302 303 cvs_checkout_file(cf, cf->file_rcs->rf_head, b, 0); 304 } else { 305 entlist = cvs_ent_open(cf->file_wd); 306 cvs_ent_remove(entlist, cf->file_name); 307 cvs_ent_close(entlist, ENT_SYNC); 308 309 repo = xmalloc(MAXPATHLEN); 310 attic = xmalloc(MAXPATHLEN); 311 cvs_get_repository_path(cf->file_wd, repo, MAXPATHLEN); 312 313 l = snprintf(attic, MAXPATHLEN, "%s/%s", repo, CVS_PATH_ATTIC); 314 if (l == -1 || l >= MAXPATHLEN) 315 fatal("cvs_commit_local: overflow"); 316 317 if (mkdir(attic, 0755) == -1 && errno != EEXIST) 318 fatal("cvs_commit_local: failed to create Attic"); 319 320 l = snprintf(attic, MAXPATHLEN, "%s/%s/%s%s", repo, 321 CVS_PATH_ATTIC, cf->file_name, RCS_FILE_EXT); 322 if (l == -1 || l >= MAXPATHLEN) 323 fatal("cvs_commit_local: overflow"); 324 325 if (rename(cf->file_rpath, attic) == -1) 326 fatal("cvs_commit_local: failed to move %s to Attic", 327 cf->file_path); 328 329 xfree(repo); 330 xfree(attic); 331 } 332 333 cvs_printf("done\n"); 334 335 } 336 337 static char * 338 commit_diff_file(struct cvs_file *cf) 339 { 340 char*delta, *p1, *p2; 341 BUF *b1, *b2, *b3; 342 343 if (cf->file_status == FILE_MODIFIED || 344 cf->file_status == FILE_ADDED) { 345 if ((b1 = cvs_buf_load(cf->file_path, BUF_AUTOEXT)) == NULL) 346 fatal("commit_diff_file: failed to load '%s'", 347 cf->file_path); 348 } else { 349 b1 = rcs_getrev(cf->file_rcs, cf->file_rcs->rf_head); 350 if (b1 == NULL) 351 fatal("commit_diff_file: failed to load HEAD"); 352 b1 = rcs_kwexp_buf(b1, cf->file_rcs, cf->file_rcs->rf_head); 353 } 354 355 if ((b2 = rcs_getrev(cf->file_rcs, cf->file_rcs->rf_head)) == NULL) 356 fatal("commit_diff_file: failed to load HEAD for '%s'", 357 cf->file_path); 358 359 if ((b3 = cvs_buf_alloc(128, BUF_AUTOEXT)) == NULL) 360 fatal("commit_diff_file: failed to create diff buf"); 361 362 (void)xasprintf(&p1, "%s/diff1.XXXXXXXXXX", cvs_tmpdir); 363 cvs_buf_write_stmp(b1, p1, 0600, NULL); 364 cvs_buf_free(b1); 365 366 (void)xasprintf(&p2, "%s/diff2.XXXXXXXXXX", cvs_tmpdir); 367 cvs_buf_write_stmp(b2, p2, 0600, NULL); 368 cvs_buf_free(b2); 369 370 diff_format = D_RCSDIFF; 371 if (cvs_diffreg(p1, p2, b3) == D_ERROR) 372 fatal("commit_diff_file: failed to get RCS patch"); 373 374 cvs_buf_putc(b3, '\0'); 375 delta = cvs_buf_release(b3); 376 return (delta); 377 } 378