commit.c revision 1.63
1/*	$OpenBSD: commit.c,v 1.63 2006/05/28 10:15:35 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
25int	cvs_commit(int, char **);
26void	cvs_commit_local(struct cvs_file *);
27void	cvs_commit_check_conflicts(struct cvs_file *);
28
29static char *commit_diff_file(struct cvs_file *);
30
31struct	cvs_flisthead files_affected;
32int	conflicts_found;
33char	*logmsg;
34
35struct 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
45int
46cvs_commit(int argc, char **argv)
47{
48	int ch;
49	char *arg = ".";
50	int flags;
51	struct cvs_recursion cr;
52
53	flags = CR_RECURSE_DIRS;
54
55	while ((ch = getopt(argc, argv, cvs_cmd_commit.cmd_opts)) != -1) {
56		switch (ch) {
57		case 'f':
58			break;
59		case 'F':
60			break;
61		case 'l':
62			flags &= ~CR_RECURSE_DIRS;
63			break;
64		case 'm':
65			logmsg = xstrdup(optarg);
66			break;
67		case 'r':
68			break;
69		case 'R':
70			break;
71		default:
72			fatal("%s", cvs_cmd_commit.cmd_synopsis);
73		}
74	}
75
76	argc -= optind;
77	argv += optind;
78
79	if (logmsg == NULL)
80		fatal("please use -m to specify a log message for now");
81
82	TAILQ_INIT(&files_affected);
83	conflicts_found = 0;
84
85	cr.enterdir = NULL;
86	cr.leavedir = NULL;
87	cr.local = cvs_commit_check_conflicts;
88	cr.remote = NULL;
89	cr.flags = flags;
90
91	if (argc > 0)
92		cvs_file_run(argc, argv, &cr);
93	else
94		cvs_file_run(1, &arg, &cr);
95
96	if (conflicts_found != 0)
97		fatal("%d conflicts found, please correct these first",
98		    conflicts_found);
99
100	cr.local = cvs_commit_local;
101	cvs_file_walklist(&files_affected, &cr);
102	cvs_file_freelist(&files_affected);
103
104	return (0);
105}
106
107void
108cvs_commit_check_conflicts(struct cvs_file *cf)
109{
110	cvs_log(LP_TRACE, "cvs_commit_check_conflicts(%s)", cf->file_path);
111
112	/*
113	 * cvs_file_classify makes the noise for us
114	 * XXX - we want that?
115	 */
116	cvs_file_classify(cf, 1);
117
118	if (cf->file_type == CVS_DIR) {
119		if (verbosity > 1)
120			cvs_log(LP_NOTICE, "Examining %s", cf->file_path);
121		return;
122	}
123
124	if (cf->file_status == FILE_CONFLICT ||
125	    cf->file_status == FILE_LOST ||
126	    cf->file_status == FILE_UNLINK)
127		conflicts_found++;
128
129	if (cf->file_status != FILE_REMOVED &&
130	    update_has_conflict_markers(cf)) {
131		cvs_log(LP_ERR, "conflict: unresolved conflicts in %s from "
132		    "merging, please fix these first", cf->file_path);
133		conflicts_found++;
134	}
135
136	if (cf->file_status == FILE_MERGE ||
137	    cf->file_status == FILE_PATCH) {
138		cvs_log(LP_ERR, "conflict: %s is not up-to-date",
139		    cf->file_path);
140		conflicts_found++;
141	}
142
143	if (cf->file_status == FILE_ADDED ||
144	    cf->file_status == FILE_REMOVED ||
145	    cf->file_status == FILE_MODIFIED)
146		cvs_file_get(cf->file_path, &files_affected);
147}
148
149void
150cvs_commit_local(struct cvs_file *cf)
151{
152	BUF *b;
153	int isadded;
154	char *d, *f, rbuf[24];
155	CVSENTRIES *entlist;
156
157	cvs_log(LP_TRACE, "cvs_commit_local(%s)", cf->file_path);
158	cvs_file_classify(cf, 0);
159
160	if (cf->file_status == FILE_MODIFIED ||
161	    cf->file_status == FILE_REMOVED)
162		rcsnum_tostr(cf->file_rcs->rf_head, rbuf, sizeof(rbuf));
163	else
164		strlcpy(rbuf, "Non-existent", sizeof(rbuf));
165
166	isadded = (cf->file_status == FILE_ADDED && cf->file_rcs == NULL);
167	if (isadded) {
168		cf->repo_fd = open(cf->file_rpath, O_CREAT|O_TRUNC|O_WRONLY);
169		if (cf->repo_fd < 0)
170			fatal("cvs_commit_local: %s", strerror(errno));
171
172		cf->file_rcs = rcs_open(cf->file_rpath, cf->repo_fd,
173		    RCS_CREATE, 0600);
174		if (cf->file_rcs == NULL)
175			fatal("cvs_commit_local: failed to create RCS file "
176			    "for %s", cf->file_path);
177	}
178
179	cvs_printf("Checking in %s:\n", cf->file_path);
180	cvs_printf("%s <- %s\n", cf->file_rpath, cf->file_path);
181	cvs_printf("old revision: %s; ", rbuf);
182
183	if (isadded == 0)
184		d = commit_diff_file(cf);
185
186	if (cf->file_status == FILE_REMOVED) {
187		b = rcs_getrev(cf->file_rcs, cf->file_rcs->rf_head);
188		if (b == NULL)
189			fatal("cvs_commit_local: failed to get HEAD");
190	} else {
191		if ((b = cvs_buf_load(cf->file_path, BUF_AUTOEXT)) == NULL)
192			fatal("cvs_commit_local: failed to load file");
193	}
194
195	cvs_buf_putc(b, '\0');
196	f = cvs_buf_release(b);
197
198	if (isadded == 0) {
199		if (rcs_deltatext_set(cf->file_rcs,
200		    cf->file_rcs->rf_head, d) == -1)
201			fatal("cvs_commit_local: failed to set delta");
202	}
203
204	if (rcs_rev_add(cf->file_rcs, RCS_HEAD_REV, logmsg, -1, NULL) == -1)
205		fatal("cvs_commit_local: failed to add new revision");
206
207	if (rcs_deltatext_set(cf->file_rcs, cf->file_rcs->rf_head, f) == -1)
208		fatal("cvs_commit_local: failed to set new HEAD delta");
209
210	xfree(f);
211
212	if (isadded == 0)
213		xfree(d);
214
215	if (cf->file_status == FILE_REMOVED) {
216		if (rcs_state_set(cf->file_rcs,
217		    cf->file_rcs->rf_head, RCS_STATE_DEAD) == -1)
218			fatal("cvs_commit_local: failed to set state");
219	}
220
221	rcs_write(cf->file_rcs);
222
223	if (cf->file_status == FILE_REMOVED) {
224		strlcpy(rbuf, "Removed", sizeof(rbuf));
225	} else if (cf->file_status == FILE_ADDED) {
226		if (cf->file_rcs->rf_dead == 0)
227			strlcpy(rbuf, "Initial Revision", sizeof(rbuf));
228		else
229			rcsnum_tostr(cf->file_rcs->rf_head,
230			    rbuf, sizeof(rbuf));
231	} else if (cf->file_status == FILE_MODIFIED) {
232		rcsnum_tostr(cf->file_rcs->rf_head, rbuf, sizeof(rbuf));
233	}
234
235	cvs_printf("new revision: %s\n", rbuf);
236
237	(void)unlink(cf->file_path);
238	(void)close(cf->fd);
239	cf->fd = -1;
240
241	if (cf->file_status != FILE_REMOVED) {
242		b = rcs_getrev(cf->file_rcs, cf->file_rcs->rf_head);
243		if (b == NULL)
244			fatal("cvs_commit_local: failed to get HEAD");
245
246		cvs_checkout_file(cf, cf->file_rcs->rf_head, b, 0);
247	} else {
248		entlist = cvs_ent_open(cf->file_wd);
249		cvs_ent_remove(entlist, cf->file_name);
250		cvs_ent_close(entlist, ENT_SYNC);
251	}
252
253	cvs_printf("done\n");
254
255}
256
257static char *
258commit_diff_file(struct cvs_file *cf)
259{
260	char*delta,  *p1, *p2;
261	BUF *b1, *b2, *b3;
262
263	if (cf->file_status == FILE_MODIFIED ||
264	    cf->file_status == FILE_ADDED) {
265		if ((b1 = cvs_buf_load(cf->file_path, BUF_AUTOEXT)) == NULL)
266			fatal("commit_diff_file: failed to load '%s'",
267			    cf->file_path);
268	} else {
269		b1 = rcs_getrev(cf->file_rcs, cf->file_rcs->rf_head);
270		if (b1 == NULL)
271			fatal("commit_diff_file: failed to load HEAD");
272		b1 = rcs_kwexp_buf(b1, cf->file_rcs, cf->file_rcs->rf_head);
273	}
274
275	if ((b2 = rcs_getrev(cf->file_rcs, cf->file_rcs->rf_head)) == NULL)
276		fatal("commit_diff_file: failed to load HEAD for '%s'",
277		    cf->file_path);
278
279	if ((b3 = cvs_buf_alloc(128, BUF_AUTOEXT)) == NULL)
280		fatal("commit_diff_file: failed to create diff buf");
281
282	(void)xasprintf(&p1, "%s/diff1.XXXXXXXXXX", cvs_tmpdir);
283	cvs_buf_write_stmp(b1, p1, 0600, NULL);
284	cvs_buf_free(b1);
285
286	(void)xasprintf(&p2, "%s/diff2.XXXXXXXXXX", cvs_tmpdir);
287	cvs_buf_write_stmp(b2, p2, 0600, NULL);
288	cvs_buf_free(b2);
289
290	diff_format = D_RCSDIFF;
291	if (cvs_diffreg(p1, p2, b3) == D_ERROR)
292		fatal("commit_diff_file: failed to get RCS patch");
293
294	cvs_buf_putc(b3, '\0');
295	delta = cvs_buf_release(b3);
296	return (delta);
297}
298