summaryrefslogtreecommitdiff
path: root/usr.bin
diff options
context:
space:
mode:
authorJoris Vink <joris@cvs.openbsd.org>2006-05-28 01:24:29 +0000
committerJoris Vink <joris@cvs.openbsd.org>2006-05-28 01:24:29 +0000
commit342f3ae6ce1c83fe66ffe451203aa5422a5c55dd (patch)
tree6613db867184039df62d338830b35622c738a3dd /usr.bin
parentf3cd77e41486334a04b835661b504ca4b4cf799c (diff)
enable merging on update. if your file in your working copy
has been modified by yourself and there is a newer revision we try and merge them together. if the merge fails and has conflicts it will mark them inside the file, you will need to resolve these by hand first before you will be able to commit your modified file. works transparent with gnu cvs, as it is suppose to.
Diffstat (limited to 'usr.bin')
-rw-r--r--usr.bin/cvs/checkout.c39
-rw-r--r--usr.bin/cvs/commit.c14
-rw-r--r--usr.bin/cvs/cvs.h7
-rw-r--r--usr.bin/cvs/rcs.h5
-rw-r--r--usr.bin/cvs/update.c117
5 files changed, 152 insertions, 30 deletions
diff --git a/usr.bin/cvs/checkout.c b/usr.bin/cvs/checkout.c
index 187eb933b78..f44664d686c 100644
--- a/usr.bin/cvs/checkout.c
+++ b/usr.bin/cvs/checkout.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: checkout.c,v 1.57 2006/05/27 16:10:01 joris Exp $ */
+/* $OpenBSD: checkout.c,v 1.58 2006/05/28 01:24:28 joris Exp $ */
/*
* Copyright (c) 2006 Joris Vink <joris@openbsd.org>
*
@@ -110,28 +110,22 @@ checkout_repository(const char *repobase, const char *wdbase)
cvs_file_freelist(&dl);
}
-int
-cvs_checkout_file(struct cvs_file *cf, RCSNUM *rnum, int flags)
+void
+cvs_checkout_file(struct cvs_file *cf, RCSNUM *rnum, BUF *bp, int flags)
{
- BUF *bp;
+ BUF *nbp;
int l, oflags, exists;
time_t rcstime;
CVSENTRIES *ent;
struct timeval tv[2];
- char *entry, rev[16], timebuf[32];
+ char *entry, rev[16], timebuf[64], tbuf[32];
rcsnum_tostr(rnum, rev, sizeof(rev));
cvs_log(LP_TRACE, "cvs_checkout_file(%s, %s, %d)",
cf->file_path, rev, flags);
- if ((bp = rcs_getrev(cf->file_rcs, rnum)) == NULL) {
- cvs_log(LP_ERR, "%s: cannot find revision %s",
- cf->file_path, rev);
- return (0);
- }
-
- bp = rcs_kwexp_buf(bp, cf->file_rcs, rnum);
+ nbp = rcs_kwexp_buf(bp, cf->file_rcs, rnum);
oflags = O_WRONLY | O_TRUNC;
if (cf->fd != -1) {
@@ -146,10 +140,10 @@ cvs_checkout_file(struct cvs_file *cf, RCSNUM *rnum, int flags)
if (cf->fd == -1)
fatal("cvs_checkout_file: open: %s", strerror(errno));
- if (cvs_buf_write_fd(bp, cf->fd) == -1)
+ if (cvs_buf_write_fd(nbp, cf->fd) == -1)
fatal("cvs_checkout_file: %s", strerror(errno));
- cvs_buf_free(bp);
+ cvs_buf_free(nbp);
if (fchmod(cf->fd, 0644) == -1)
fatal("cvs_checkout_file: fchmod: %s", strerror(errno));
@@ -171,9 +165,18 @@ cvs_checkout_file(struct cvs_file *cf, RCSNUM *rnum, int flags)
if ((rcstime = cvs_hack_time(rcstime, 1)) == 0)
fatal("cvs_checkout_file: to gmt failed");
- ctime_r(&rcstime, timebuf);
- if (timebuf[strlen(timebuf) - 1] == '\n')
- timebuf[strlen(timebuf) - 1] = '\0';
+ ctime_r(&rcstime, tbuf);
+ if (tbuf[strlen(tbuf) - 1] == '\n')
+ tbuf[strlen(tbuf) - 1] = '\0';
+
+ if (flags & CO_MERGE) {
+ l = snprintf(timebuf, sizeof(timebuf), "Result of merge+%s",
+ tbuf);
+ if (l == -1 || l >= (int)sizeof(timebuf))
+ fatal("cvs_checkout_file: overflow");
+ } else {
+ strlcpy(timebuf, tbuf, sizeof(timebuf));
+ }
entry = xmalloc(CVS_ENT_MAXLINELEN);
l = snprintf(entry, CVS_ENT_MAXLINELEN, "/%s/%s/%s//", cf->file_name,
@@ -184,6 +187,4 @@ cvs_checkout_file(struct cvs_file *cf, RCSNUM *rnum, int flags)
cvs_ent_close(ent, ENT_SYNC);
xfree(entry);
-
- return (1);
}
diff --git a/usr.bin/cvs/commit.c b/usr.bin/cvs/commit.c
index 1e6c0eac2dd..9e8cce9ca47 100644
--- a/usr.bin/cvs/commit.c
+++ b/usr.bin/cvs/commit.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: commit.c,v 1.59 2006/05/27 15:17:42 joris Exp $ */
+/* $OpenBSD: commit.c,v 1.60 2006/05/28 01:24:28 joris Exp $ */
/*
* Copyright (c) 2006 Joris Vink <joris@openbsd.org>
*
@@ -126,6 +126,12 @@ cvs_commit_check_conflicts(struct cvs_file *cf)
cf->file_status == FILE_UNLINK)
conflicts_found++;
+ if (update_has_conflict_markers(cf)) {
+ cvs_log(LP_ERR, "conflict: unresolved conflicts in %s from "
+ "merging, please fix these first", cf->file_path);
+ conflicts_found++;
+ }
+
if (cf->file_status == FILE_MERGE ||
cf->file_status == FILE_PATCH) {
cvs_log(LP_ERR, "conflict: %s is not up-to-date",
@@ -204,7 +210,11 @@ cvs_commit_local(struct cvs_file *cf)
cf->fd = -1;
if (cf->file_status != FILE_REMOVED) {
- cvs_checkout_file(cf, cf->file_rcs->rf_head, 0);
+ b = rcs_getrev(cf->file_rcs, cf->file_rcs->rf_head);
+ if (b == NULL)
+ fatal("cvs_commit_local: failed to get HEAD");
+
+ cvs_checkout_file(cf, cf->file_rcs->rf_head, b, 0);
} else {
entlist = cvs_ent_open(cf->file_wd);
cvs_ent_remove(entlist, cf->file_name);
diff --git a/usr.bin/cvs/cvs.h b/usr.bin/cvs/cvs.h
index 19ad84c73c1..41429c40ee4 100644
--- a/usr.bin/cvs/cvs.h
+++ b/usr.bin/cvs/cvs.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: cvs.h,v 1.108 2006/05/27 20:57:42 joris Exp $ */
+/* $OpenBSD: cvs.h,v 1.109 2006/05/28 01:24:28 joris Exp $ */
/*
* Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
* All rights reserved.
@@ -347,6 +347,9 @@ void cvsroot_remove(struct cvsroot *);
void cvs_update_local(struct cvs_file *);
void cvs_update_enterdir(struct cvs_file *);
void cvs_update_leavedir(struct cvs_file *);
-int cvs_checkout_file(struct cvs_file *, RCSNUM *, int);
+void cvs_checkout_file(struct cvs_file *, RCSNUM *, BUF *, int);
+int update_has_conflict_markers(struct cvs_file *);
+
+#define CO_MERGE 0x01
#endif
diff --git a/usr.bin/cvs/rcs.h b/usr.bin/cvs/rcs.h
index 5024ed29485..a05961baaa2 100644
--- a/usr.bin/cvs/rcs.h
+++ b/usr.bin/cvs/rcs.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: rcs.h,v 1.62 2006/05/27 03:30:31 joris Exp $ */
+/* $OpenBSD: rcs.h,v 1.63 2006/05/28 01:24:28 joris Exp $ */
/*
* Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
* All rights reserved.
@@ -40,6 +40,9 @@
#define RCS_HEAD_INIT "1.1"
#define RCS_HEAD_REV ((RCSNUM *)(-1))
+#define RCS_CONFLICT_MARKER1 "<<<<<<< "
+#define RCS_CONFLICT_MARKER2 ">>>>>>> "
+#define RCS_CONFLICT_MARKER3 "=======\n"
#define RCS_SYM_INVALCHAR "$,.:;@"
diff --git a/usr.bin/cvs/update.c b/usr.bin/cvs/update.c
index df1722e9d06..324dac5ca41 100644
--- a/usr.bin/cvs/update.c
+++ b/usr.bin/cvs/update.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: update.c,v 1.64 2006/05/27 21:20:52 joris Exp $ */
+/* $OpenBSD: update.c,v 1.65 2006/05/28 01:24:28 joris Exp $ */
/*
* Copyright (c) 2006 Joris Vink <joris@openbsd.org>
*
@@ -26,6 +26,8 @@ int cvs_update(int, char **);
int prune_dirs = 0;
int build_dirs = 0;
+static void update_clear_conflict(struct cvs_file *);
+
#define UPDATE_SKIP 100
struct cvs_cmd cvs_cmd_update = {
@@ -218,6 +220,8 @@ cvs_update_leavedir(struct cvs_file *cf)
void
cvs_update_local(struct cvs_file *cf)
{
+ int ret;
+ BUF *bp;
CVSENTRIES *entlist;
cvs_log(LP_TRACE, "cvs_update_local(%s)", cf->file_path);
@@ -232,6 +236,11 @@ cvs_update_local(struct cvs_file *cf)
return;
}
+ /*
+ * the bp buffer will be released inside rcs_kwexp_buf,
+ * which is called from cvs_checkout_file().
+ */
+ bp = NULL;
cvs_file_classify(cf);
switch (cf->file_status) {
@@ -239,10 +248,14 @@ cvs_update_local(struct cvs_file *cf)
cvs_printf("? %s\n", cf->file_path);
break;
case FILE_MODIFIED:
- if (cf->file_ent->ce_conflict != NULL)
+ ret = update_has_conflict_markers(cf);
+ if (cf->file_ent->ce_conflict != NULL && ret == 1) {
cvs_printf("C %s\n", cf->file_path);
- else
+ } else {
+ if (cf->file_ent->ce_conflict != NULL && ret == 0)
+ update_clear_conflict(cf);
cvs_printf("M %s\n", cf->file_path);
+ }
break;
case FILE_ADDED:
cvs_printf("A %s\n", cf->file_path);
@@ -256,11 +269,27 @@ cvs_update_local(struct cvs_file *cf)
case FILE_LOST:
case FILE_CHECKOUT:
case FILE_PATCH:
- if (cvs_checkout_file(cf, cf->file_rcs->rf_head, 0))
- cvs_printf("U %s\n", cf->file_path);
+ bp = rcs_getrev(cf->file_rcs, cf->file_rcs->rf_head);
+ if (bp == NULL)
+ fatal("cvs_update_local: failed to get HEAD");
+
+ cvs_checkout_file(cf, cf->file_rcs->rf_head, bp, 0);
+ cvs_printf("U %s\n", cf->file_path);
break;
case FILE_MERGE:
- cvs_printf("needs merge: %s\n", cf->file_path);
+ bp = cvs_diff3(cf->file_rcs, cf->file_path,
+ cf->file_ent->ce_rev, cf->file_rcs->rf_head, 1);
+ if (bp == NULL)
+ fatal("cvs_update_local: failed to merge");
+
+ cvs_checkout_file(cf, cf->file_rcs->rf_head, bp, CO_MERGE);
+
+ if (diff3_conflicts != 0) {
+ cvs_printf("C %s\n", cf->file_path);
+ } else {
+ update_clear_conflict(cf);
+ cvs_printf("M %s\n", cf->file_path);
+ }
break;
case FILE_UNLINK:
(void)unlink(cf->file_path);
@@ -273,3 +302,79 @@ cvs_update_local(struct cvs_file *cf)
break;
}
}
+
+static void
+update_clear_conflict(struct cvs_file *cf)
+{
+ int l;
+ time_t now;
+ CVSENTRIES *entlist;
+ char *entry, revbuf[16], timebuf[32];
+
+ cvs_log(LP_TRACE, "update_clear_conflict(%s)", cf->file_path);
+
+ time(&now);
+ ctime_r(&now, timebuf);
+ if (timebuf[strlen(timebuf) - 1] == '\n')
+ timebuf[strlen(timebuf) - 1] = '\0';
+
+ rcsnum_tostr(cf->file_ent->ce_rev, revbuf, sizeof(revbuf));
+
+ entry = xmalloc(CVS_ENT_MAXLINELEN);
+ l = snprintf(entry, CVS_ENT_MAXLINELEN, "/%s/%s/%s//",
+ cf->file_name, revbuf, timebuf);
+ if (l == -1 || l >= CVS_ENT_MAXLINELEN)
+ fatal("update_clear_conflict: overflow");
+
+ entlist = cvs_ent_open(cf->file_wd);
+ cvs_ent_add(entlist, entry);
+ cvs_ent_close(entlist, ENT_SYNC);
+ xfree(entry);
+}
+
+/*
+ * XXX - this is the way GNU cvs checks for outstanding conflicts
+ * in a file after a merge. It is a very very bad approach and
+ * should be looked at once opencvs is working decently.
+ */
+int
+update_has_conflict_markers(struct cvs_file *cf)
+{
+ BUF *bp;
+ int conflict;
+ char *content;
+ struct cvs_line *lp;
+ struct cvs_lines *lines;
+
+ cvs_log(LP_TRACE, "update_has_conflict_markers(%s)", cf->file_path);
+
+ if ((bp = cvs_buf_load(cf->file_path, BUF_AUTOEXT)) == NULL)
+ fatal("update_has_conflict_markers: failed to load %s",
+ cf->file_path);
+
+ cvs_buf_putc(bp, '\0');
+ content = cvs_buf_release(bp);
+ if ((lines = cvs_splitlines(content)) == NULL)
+ fatal("update_has_conflict_markers: failed to split lines");
+
+ xfree(content);
+
+ conflict = 0;
+ TAILQ_FOREACH(lp, &(lines->l_lines), l_list) {
+ if (lp->l_line == NULL)
+ continue;
+
+ if (!strncmp(lp->l_line, RCS_CONFLICT_MARKER1,
+ strlen(RCS_CONFLICT_MARKER1)) ||
+ !strncmp(lp->l_line, RCS_CONFLICT_MARKER2,
+ strlen(RCS_CONFLICT_MARKER2)) ||
+ !strncmp(lp->l_line, RCS_CONFLICT_MARKER3,
+ strlen(RCS_CONFLICT_MARKER3))) {
+ conflict = 1;
+ break;
+ }
+ }
+
+ cvs_freelines(lines);
+ return (conflict);
+}