version 1.54, 2006/04/14 02:45:35 |
version 1.55, 2006/05/27 03:30:30 |
|
|
/* $OpenBSD$ */ |
/* $OpenBSD$ */ |
/* |
/* |
* Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org> |
* Copyright (c) 2006 Joris Vink <joris@openbsd.org> |
* All rights reserved. |
|
* |
* |
* Redistribution and use in source and binary forms, with or without |
* Permission to use, copy, modify, and distribute this software for any |
* modification, are permitted provided that the following conditions |
* purpose with or without fee is hereby granted, provided that the above |
* are met: |
* copyright notice and this permission notice appear in all copies. |
* |
* |
* 1. Redistributions of source code must retain the above copyright |
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
* notice, this list of conditions and the following disclaimer. |
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
* 2. The name of the author may not be used to endorse or promote products |
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
* derived from this software without specific prior written permission. |
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
* |
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, |
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY |
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL |
|
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
*/ |
*/ |
|
|
#include "includes.h" |
#include "includes.h" |
|
|
#include "buf.h" |
|
#include "cvs.h" |
#include "cvs.h" |
|
#include "diff.h" |
#include "log.h" |
#include "log.h" |
#include "proto.h" |
#include "proto.h" |
|
|
|
int cvs_commit(int, char **); |
|
void cvs_commit_local(struct cvs_file *); |
|
void cvs_commit_check_conflicts(struct cvs_file *); |
|
|
static int cvs_commit_init(struct cvs_cmd *, int, char **, int *); |
static char *commit_diff_file(struct cvs_file *); |
static int cvs_commit_prepare(CVSFILE *, void *); |
|
static int cvs_commit_remote(CVSFILE *, void *); |
|
static int cvs_commit_local(CVSFILE *, void *); |
|
static int cvs_commit_pre_exec(struct cvsroot *); |
|
|
|
|
struct cvs_flisthead files_affected; |
|
int conflicts_found; |
|
char *logmsg; |
|
|
struct cvs_cmd cvs_cmd_commit = { |
struct cvs_cmd cvs_cmd_commit = { |
CVS_OP_COMMIT, CVS_REQ_CI, "commit", |
CVS_OP_COMMIT, CVS_REQ_CI, "commit", |
{ "ci", "com" }, |
{ "ci", "com" }, |
"Check files into the repository", |
"Check files into the repository", |
"[-flR] [-F logfile | -m msg] [-r rev] ...", |
"[-flR] [-F logfile | -m msg] [-r rev] ...", |
"F:flm:Rr:", |
"F:flm:Rr:", |
NULL, |
NULL, |
CF_RECURSE | CF_IGNORE | CF_SORT, |
cvs_commit |
cvs_commit_init, |
|
cvs_commit_pre_exec, |
|
cvs_commit_remote, |
|
cvs_commit_local, |
|
NULL, |
|
NULL, |
|
CVS_CMD_SENDDIR | CVS_CMD_ALLOWSPEC | CVS_CMD_SENDARGS2 |
|
}; |
}; |
|
|
static char *mfile = NULL; |
int |
static char *rev = NULL; |
cvs_commit(int argc, char **argv) |
static char **commit_files = NULL; |
|
static int commit_fcount = 0; |
|
static int wantedstatus = 0; |
|
|
|
static int |
|
cvs_commit_init(struct cvs_cmd *cmd, int argc, char **argv, int *arg) |
|
{ |
{ |
int ch; |
int ch; |
|
char *arg = "."; |
|
struct cvs_recursion cr; |
|
|
while ((ch = getopt(argc, argv, cmd->cmd_opts)) != -1) { |
while ((ch = getopt(argc, argv, cvs_cmd_commit.cmd_opts)) != -1) { |
switch (ch) { |
switch (ch) { |
case 'F': |
|
mfile = optarg; |
|
break; |
|
case 'f': |
case 'f': |
/* XXX half-implemented */ |
|
cmd->file_flags &= ~CF_RECURSE; |
|
break; |
break; |
|
case 'F': |
|
break; |
case 'l': |
case 'l': |
cmd->file_flags &= ~CF_RECURSE; |
|
break; |
break; |
case 'm': |
case 'm': |
cvs_msg = xstrdup(optarg); |
logmsg = xstrdup(optarg); |
break; |
break; |
case 'R': |
|
cmd->file_flags |= CF_RECURSE; |
|
break; |
|
case 'r': |
case 'r': |
rev = optarg; |
|
break; |
break; |
|
case 'R': |
|
break; |
default: |
default: |
return (CVS_EX_USAGE); |
fatal("%s", cvs_cmd_commit.cmd_synopsis); |
} |
} |
} |
} |
|
|
if (cvs_msg != NULL && mfile != NULL) { |
argc -= optind; |
cvs_log(LP_ERR, "the -F and -m flags are mutually exclusive"); |
argv += optind; |
return (CVS_EX_USAGE); |
|
} |
|
|
|
if (mfile != NULL) |
if (logmsg == NULL) |
cvs_msg = cvs_logmsg_open(mfile); |
fatal("please use -m to specify a log message for now"); |
|
|
*arg = optind; |
TAILQ_INIT(&files_affected); |
|
conflicts_found = 0; |
|
|
commit_files = (argv + optind); |
cr.enterdir = NULL; |
commit_fcount = (argc - optind); |
cr.leavedir = NULL; |
|
cr.local = cvs_commit_check_conflicts; |
|
cr.remote = NULL; |
|
|
|
if (argc > 0) |
|
cvs_file_run(argc, argv, &cr); |
|
else |
|
cvs_file_run(1, &arg, &cr); |
|
|
|
if (conflicts_found != 0) |
|
fatal("%d conflicts found, please correct these first", |
|
conflicts_found); |
|
|
|
cr.local = cvs_commit_local; |
|
cvs_file_walklist(&files_affected, &cr); |
|
cvs_file_freelist(&files_affected); |
|
|
return (0); |
return (0); |
} |
} |
|
|
int |
void |
cvs_commit_pre_exec(struct cvsroot *root) |
cvs_commit_check_conflicts(struct cvs_file *cf) |
{ |
{ |
CVSFILE *cfp; |
cvs_log(LP_TRACE, "cvs_commit_check_conflicts(%s)", cf->file_path); |
CVSFILE *tmp; |
|
int ret, i, flags = CF_RECURSE | CF_IGNORE | CF_SORT; |
|
struct cvs_flist added, modified, removed, *cl[3]; |
|
int stattype[] = { CVS_FST_ADDED, CVS_FST_MODIFIED, CVS_FST_REMOVED }; |
|
|
|
SIMPLEQ_INIT(&added); |
|
SIMPLEQ_INIT(&modified); |
|
SIMPLEQ_INIT(&removed); |
|
|
|
cl[0] = &added; |
|
cl[1] = &modified; |
|
cl[2] = &removed; |
|
|
|
if ((tmp = cvs_file_loadinfo(".", CF_NOFILES, NULL, NULL, 1)) == NULL) |
|
return (CVS_EX_DATA); |
|
|
|
/* |
/* |
* Obtain the file lists for the logmessage. |
* cvs_file_classify makes the noise for us |
|
* XXX - we want that? |
*/ |
*/ |
for (i = 0; i < 3; i++) { |
cvs_file_classify(cf); |
wantedstatus = stattype[i]; |
|
if (commit_fcount != 0) { |
|
ret = cvs_file_getspec(commit_files, commit_fcount, |
|
flags, cvs_commit_prepare, cl[i], NULL); |
|
} else { |
|
ret = cvs_file_get(".", flags, cvs_commit_prepare, |
|
cl[i], NULL); |
|
} |
|
|
|
if (ret != CVS_EX_OK) { |
if (cf->file_status == FILE_CONFLICT || |
cvs_file_free(tmp); |
cf->file_status == FILE_LOST || |
return (CVS_EX_DATA); |
cf->file_status == FILE_UNLINK) |
} |
conflicts_found++; |
} |
|
|
|
/* |
if (cf->file_status == FILE_ADDED || |
* If we didn't catch any file, don't call the editor. |
cf->file_status == FILE_REMOVED || |
*/ |
cf->file_status == FILE_MODIFIED) |
if (SIMPLEQ_EMPTY(&added) && SIMPLEQ_EMPTY(&modified) && |
cvs_file_get(cf->file_path, &files_affected); |
SIMPLEQ_EMPTY(&removed)) { |
} |
cvs_file_free(tmp); |
|
return (0); |
|
} |
|
|
|
/* |
void |
* Fetch the log message for real, with all the files. |
cvs_commit_local(struct cvs_file *cf) |
*/ |
{ |
if (cvs_msg == NULL) |
BUF *b; |
cvs_msg = cvs_logmsg_get(tmp->cf_name, &added, &modified, |
char *d, *f, rbuf[16]; |
&removed); |
|
|
|
cvs_file_free(tmp); |
cvs_log(LP_TRACE, "cvs_commit_local(%s)", cf->file_path); |
|
cvs_file_classify(cf); |
|
|
/* free the file lists */ |
rcsnum_tostr(cf->file_rcs->rf_head, rbuf, sizeof(rbuf)); |
for (i = 0; i < 3; i++) { |
|
while (!SIMPLEQ_EMPTY(cl[i])) { |
|
cfp = SIMPLEQ_FIRST(cl[i]); |
|
SIMPLEQ_REMOVE_HEAD(cl[i], cf_list); |
|
cvs_file_free(cfp); |
|
} |
|
} |
|
|
|
if (cvs_msg == NULL) |
cvs_printf("Checking in %s:\n", cf->file_path); |
return (CVS_EX_DATA); |
cvs_printf("%s <- %s\n", cf->file_rpath, cf->file_path); |
|
cvs_printf("old revision: %s; ", rbuf); |
|
|
if (root->cr_method != CVS_METHOD_LOCAL) { |
d = commit_diff_file(cf); |
cvs_logmsg_send(root, cvs_msg); |
|
|
|
if (rev != NULL) { |
if ((b = cvs_buf_load(cf->file_path, BUF_AUTOEXT)) == NULL) |
cvs_sendarg(root, "-r", 0); |
fatal("cvs_commit_local: failed to load file"); |
cvs_sendarg(root, rev, 0); |
|
} |
|
} |
|
|
|
return (0); |
cvs_buf_putc(b, '\0'); |
} |
f = cvs_buf_release(b); |
|
|
/* |
if (rcs_deltatext_set(cf->file_rcs, cf->file_rcs->rf_head, d) == -1) |
* cvs_commit_prepare() |
fatal("cvs_commit_local: failed to set delta"); |
* |
|
* Examine the file <cf> to see if it will be part of the commit, in which |
|
* case it gets added to the list passed as second argument. |
|
*/ |
|
int |
|
cvs_commit_prepare(CVSFILE *cf, void *arg) |
|
{ |
|
CVSFILE *copy; |
|
struct cvs_flist *clp = (struct cvs_flist *)arg; |
|
|
|
if (cf->cf_type == DT_REG && cf->cf_cvstat == wantedstatus) { |
if (rcs_rev_add(cf->file_rcs, RCS_HEAD_REV, logmsg, -1, NULL) == -1) |
copy = cvs_file_copy(cf); |
fatal("cvs_commit_local: failed to add new revision"); |
if (copy == NULL) |
|
return (CVS_EX_DATA); |
|
|
|
SIMPLEQ_INSERT_TAIL(clp, copy, cf_list); |
if (rcs_deltatext_set(cf->file_rcs, cf->file_rcs->rf_head, f) == -1) |
} |
fatal("cvs_commit_local: failed to set new HEAD delta"); |
|
|
return (0); |
xfree(f); |
} |
xfree(d); |
|
|
|
rcs_write(cf->file_rcs); |
|
|
/* |
rcsnum_tostr(cf->file_rcs->rf_head, rbuf, sizeof(rbuf)); |
* cvs_commit_remote() |
cvs_printf("new revision: %s\n", rbuf); |
* |
|
* Commit a single file. |
|
*/ |
|
int |
|
cvs_commit_remote(CVSFILE *cf, void *arg) |
|
{ |
|
char fpath[MAXPATHLEN]; |
|
struct cvsroot *root; |
|
|
|
root = CVS_DIR_ROOT(cf); |
(void)unlink(cf->file_path); |
|
(void)close(cf->fd); |
|
cf->fd = -1; |
|
cvs_checkout_file(cf, cf->file_rcs->rf_head, 0); |
|
|
if (cf->cf_type == DT_DIR) { |
cvs_printf("done\n"); |
if (cf->cf_cvstat != CVS_FST_UNKNOWN) |
|
cvs_senddir(root, cf); |
|
return (0); |
|
} |
|
|
|
cvs_file_getpath(cf, fpath, sizeof(fpath)); |
} |
|
|
if (cf->cf_cvstat == CVS_FST_ADDED || |
static char * |
cf->cf_cvstat == CVS_FST_MODIFIED || |
commit_diff_file(struct cvs_file *cf) |
cf->cf_cvstat == CVS_FST_REMOVED) { |
{ |
cvs_sendentry(root, cf); |
char*delta, *p1, *p2; |
|
BUF *b1, *b2, *b3; |
|
|
/* if it's removed, don't bother sending a |
if ((b1 = cvs_buf_load(cf->file_path, BUF_AUTOEXT)) == NULL) |
* Modified request together with the file its |
fatal("commit_diff_file: failed to load '%s'", cf->file_path); |
* contents. |
|
*/ |
|
if (cf->cf_cvstat == CVS_FST_REMOVED) |
|
return (0); |
|
|
|
cvs_sendreq(root, CVS_REQ_MODIFIED, cf->cf_name); |
if ((b2 = rcs_getrev(cf->file_rcs, cf->file_rcs->rf_head)) == NULL) |
cvs_sendfile(root, fpath); |
fatal("commit_diff_file: failed to load HEAD for '%s'", |
} |
cf->file_path); |
|
|
return (0); |
if ((b3 = cvs_buf_alloc(128, BUF_AUTOEXT)) == NULL) |
} |
fatal("commit_diff_file: failed to create diff buf"); |
|
|
static int |
(void)xasprintf(&p1, "%s/diff1.XXXXXXXXXX", cvs_tmpdir); |
cvs_commit_local(CVSFILE *cf, void *arg) |
cvs_buf_write_stmp(b1, p1, 0600, NULL); |
{ |
cvs_buf_free(b1); |
char fpath[MAXPATHLEN], rcspath[MAXPATHLEN]; |
|
|
|
if (cf->cf_type == DT_DIR) { |
(void)xasprintf(&p2, "%s/diff2.XXXXXXXXXX", cvs_tmpdir); |
if (verbosity > 1) |
cvs_buf_write_stmp(b2, p2, 0600, NULL); |
cvs_log(LP_NOTICE, "Examining %s", cf->cf_name); |
cvs_buf_free(b2); |
return (0); |
|
} |
|
|
|
cvs_file_getpath(cf, fpath, sizeof(fpath)); |
diff_format = D_RCSDIFF; |
cvs_rcs_getpath(cf, rcspath, sizeof(rcspath)); |
if (cvs_diffreg(p1, p2, b3) == D_ERROR) |
|
fatal("commit_diff_file: failed to get RCS patch"); |
|
|
return (0); |
cvs_buf_putc(b3, '\0'); |
|
delta = cvs_buf_release(b3); |
|
return (delta); |
} |
} |