summaryrefslogtreecommitdiffstats
path: root/contrib/cvs/src/update.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/cvs/src/update.c')
-rw-r--r--contrib/cvs/src/update.c342
1 files changed, 234 insertions, 108 deletions
diff --git a/contrib/cvs/src/update.c b/contrib/cvs/src/update.c
index 8196c1f..48bed3b 100644
--- a/contrib/cvs/src/update.c
+++ b/contrib/cvs/src/update.c
@@ -3,7 +3,7 @@
* Copyright (c) 1989-1992, Brian Berliner
*
* You may distribute under the terms of the GNU General Public License as
- * specified in the README file that comes with the CVS 1.4 kit.
+ * specified in the README file that comes with the CVS source distribution.
*
* "update" updates the version in the present directory with respect to the RCS
* repository. The present version must have been created by "checkout". The
@@ -64,12 +64,8 @@ static int update_fileproc PROTO ((void *callerdat, struct file_info *));
static int update_filesdone_proc PROTO ((void *callerdat, int err,
char *repository, char *update_dir,
List *entries));
-static int write_letter PROTO((char *file, int letter, char *update_dir));
-#ifdef SERVER_SUPPORT
-static void join_file PROTO ((struct file_info *finfo, Vers_TS *vers_ts));
-#else
+static void write_letter PROTO ((struct file_info *finfo, int letter));
static void join_file PROTO ((struct file_info *finfo, Vers_TS *vers_ts));
-#endif
static char *options = NULL;
static char *tag = NULL;
@@ -113,6 +109,7 @@ static const char *const update_usage[] =
"\t-j rev\tMerge in changes made between current revision and rev.\n",
"\t-I ign\tMore files to ignore (! to reset).\n",
"\t-W spec\tWrappers specification line.\n",
+ "(Specify the --help global option for a list of other help options)\n",
NULL
};
@@ -259,16 +256,18 @@ update (argc, argv)
/* If the server supports the command "update-patches", that means
that it knows how to handle the -u argument to update, which
- means to send patches instead of complete files. */
+ means to send patches instead of complete files.
+
+ We don't send -u if failed_patches != NULL, so that the
+ server doesn't try to send patches which will just fail
+ again. At least currently, the client also clobbers the
+ file and tells the server it is lost, which also will get
+ a full file instead of a patch, but it seems clean to omit
+ -u. */
if (failed_patches == NULL)
{
-#ifndef DONT_USE_PATCH
- /* Systems which don't have the patch program ported to them
- will want to define DONT_USE_PATCH; then CVS won't try to
- invoke patch. */
if (supported_request ("update-patches"))
send_arg ("-u");
-#endif
}
if (failed_patches == NULL)
@@ -548,44 +547,17 @@ update_fileproc (callerdat, finfo)
break;
case T_CONFLICT: /* old punt-type errors */
retval = 1;
- (void) write_letter (finfo->file, 'C', finfo->update_dir);
+ write_letter (finfo, 'C');
break;
case T_NEEDS_MERGE: /* needs merging */
if (noexec)
{
retval = 1;
- (void) write_letter (finfo->file, 'C', finfo->update_dir);
+ write_letter (finfo, 'C');
}
else
{
- if (wrap_merge_is_copy (finfo->file))
-#if 0
- /* Look, we can't clobber the user's file. We
- know it is modified and we're going to
- overwrite their mod? Puh-leeze. The
- correct behavior is probably something like
- what merge_file does for -kb, which is to
- give the users both files and tell them
- what the two filenames are. Of course, -m
- in wrappers needs to be documented *much*
- better. Anyway, until then, make this a
- fatal error. */
-
- /* Should we be warning the user that we are
- * overwriting the user's copy of the file? */
- retval =
- checkout_file (finfo, vers, 0);
-#else
- {
- error (0, 0, "A -m 'COPY' wrapper is specified");
- error (0, 0, "but file %s needs merge",
- finfo->fullname);
- error (1, 0, "\
-You probably want to avoid -m 'COPY' wrappers");
-#endif
- }
- else
- retval = merge_file (finfo, vers);
+ retval = merge_file (finfo, vers);
}
break;
case T_MODIFIED: /* locally modified */
@@ -622,7 +594,7 @@ You probably want to avoid -m 'COPY' wrappers");
if (!retcode)
{
- (void) write_letter (finfo->file, 'C', finfo->update_dir);
+ write_letter (finfo, 'C');
retval = 1;
}
else
@@ -634,7 +606,10 @@ You probably want to avoid -m 'COPY' wrappers");
}
}
if (!retval)
- retval = write_letter (finfo->file, 'M', finfo->update_dir);
+ {
+ write_letter (finfo, 'M');
+ retval = 0;
+ }
break;
#ifdef SERVER_SUPPORT
case T_PATCH: /* needs patch */
@@ -658,10 +633,9 @@ You probably want to avoid -m 'COPY' wrappers");
break;
}
}
- /* Fall through. */
/* If we're not running as a server, just check the
- file out. It's simpler and faster than starting up
- two new processes (diff and patch). */
+ file out. It's simpler and faster than producing
+ and applying patches. */
/* Fall through. */
#endif
case T_CHECKOUT: /* needs checkout */
@@ -674,10 +648,12 @@ You probably want to avoid -m 'COPY' wrappers");
#endif
break;
case T_ADDED: /* added but not committed */
- retval = write_letter (finfo->file, 'A', finfo->update_dir);
+ write_letter (finfo, 'A');
+ retval = 0;
break;
case T_REMOVED: /* removed but not committed */
- retval = write_letter (finfo->file, 'R', finfo->update_dir);
+ write_letter (finfo, 'R');
+ retval = 0;
break;
case T_REMOVE_ENTRY: /* needs to be un-registered */
retval = scratch_file (finfo);
@@ -727,7 +703,23 @@ update_ignproc (file, dir)
char *file;
char *dir;
{
- (void) write_letter (file, '?', dir);
+ struct file_info finfo;
+
+ memset (&finfo, 0, sizeof (finfo));
+ finfo.file = file;
+ finfo.update_dir = dir;
+ if (dir[0] == '\0')
+ finfo.fullname = xstrdup (file);
+ else
+ {
+ finfo.fullname = xmalloc (strlen (file) + strlen (dir) + 10);
+ strcpy (finfo.fullname, dir);
+ strcat (finfo.fullname, "/");
+ strcat (finfo.fullname, file);
+ }
+
+ write_letter (&finfo, '?');
+ free (finfo.fullname);
}
/* ARGSUSED */
@@ -808,7 +800,7 @@ update_dirent_proc (callerdat, dir, repository, update_dir, entries)
if (noexec)
{
/* ignore the missing dir if -n is specified */
- error (0, 0, "New directory `%s' -- ignored", dir);
+ error (0, 0, "New directory `%s' -- ignored", update_dir);
return (R_SKIP_ALL);
}
else
@@ -818,6 +810,7 @@ update_dirent_proc (callerdat, dir, repository, update_dir, entries)
Create_Admin (dir, update_dir, repository, tag, date,
/* This is a guess. We will rewrite it later
via WriteTag. */
+ 0,
0);
rewrite_tag = 1;
nonbranch = 0;
@@ -920,7 +913,8 @@ update_dirleave_proc (callerdat, dir, err, update_dir, entries)
{
if ((cp = strrchr (line, '\n')) != NULL)
*cp = '\0';
- run_setup ("%s %s", line, repository);
+ run_setup (line);
+ run_arg (repository);
cvs_output (program_name, 0);
cvs_output (" ", 1);
cvs_output (command_name, 0);
@@ -1080,6 +1074,9 @@ checkout_file (finfo, vers_ts, adding)
int status;
int file_is_dead;
+ /* Solely to suppress a warning from gcc -Wall. */
+ backup = NULL;
+
/* don't screw with backup files if we're going to stdout */
if (!pipeout)
{
@@ -1093,7 +1090,13 @@ checkout_file (finfo, vers_ts, adding)
else
/* If -f/-t wrappers are being used to wrap up a directory,
then backup might be a directory instead of just a file. */
- (void) unlink_file_dir (backup);
+ if (unlink_file_dir (backup) < 0)
+ {
+ /* Not sure if the existence_error check is needed here. */
+ if (!existence_error (errno))
+ /* FIXME: should include update_dir in message. */
+ error (0, errno, "error removing %s", backup);
+ }
}
file_is_dead = RCS_isdead (vers_ts->srcfile, vers_ts->vn_rcs);
@@ -1133,7 +1136,7 @@ VERS: ", 0);
{
Vers_TS *xvers_ts;
- if (cvswrite == TRUE
+ if (cvswrite
&& !file_is_dead
&& !fileattr_get (finfo->file, "_watched"))
xchmod (finfo->file, 1);
@@ -1155,13 +1158,11 @@ VERS: ", 0);
}
/* set the time from the RCS file iff it was unknown before */
- if (vers_ts->vn_user == NULL ||
- strncmp (vers_ts->ts_rcs, "Initial", 7) == 0)
- {
- set_time = 1;
- }
- else
- set_time = 0;
+ set_time =
+ (!noexec
+ && (vers_ts->vn_user == NULL ||
+ strncmp (vers_ts->ts_rcs, "Initial", 7) == 0)
+ && !file_is_dead);
wrap_fromcvs_process_file (finfo->file);
@@ -1222,7 +1223,7 @@ VERS: ", 0);
if (!really_quiet && !file_is_dead)
{
- write_letter (finfo->file, 'U', finfo->update_dir);
+ write_letter (finfo, 'U');
}
}
}
@@ -1243,7 +1244,13 @@ VERS: ", 0);
{
/* If -f/-t wrappers are being used to wrap up a directory,
then backup might be a directory instead of just a file. */
- (void) unlink_file_dir (backup);
+ if (unlink_file_dir (backup) < 0)
+ {
+ /* Not sure if the existence_error check is needed here. */
+ if (!existence_error (errno))
+ /* FIXME: should include update_dir in message. */
+ error (0, errno, "error removing %s", backup);
+ }
free (backup);
}
@@ -1306,6 +1313,27 @@ patch_file (finfo, vers_ts, docheckout, file_info, checksum)
return 0;
}
+ /* First check that the first revision exists. If it has been nuked
+ by cvs admin -o, then just fall back to checking out entire
+ revisions. In some sense maybe we don't have to do this; after
+ all cvs.texinfo says "Make sure that no-one has checked out a
+ copy of the revision you outdate" but then again, that advice
+ doesn't really make complete sense, because "cvs admin" operates
+ on a working directory and so _someone_ will almost always have
+ _some_ revision checked out. */
+ {
+ char *rev;
+
+ rev = RCS_gettag (finfo->rcs, vers_ts->vn_user, 1, NULL);
+ if (rev == NULL)
+ {
+ *docheckout = 1;
+ return 0;
+ }
+ else
+ free (rev);
+ }
+
backup = xmalloc (strlen (finfo->file)
+ sizeof (CVSADM)
+ sizeof (CVSPREFIX)
@@ -1382,7 +1410,7 @@ patch_file (finfo, vers_ts, docheckout, file_info, checksum)
retcode = 0;
if (! fail)
{
- const char *diff_options;
+ char *diff_options;
/* FIXME: It might be better to come up with a diff library
which can be shared with the diffutils. */
@@ -1400,21 +1428,22 @@ patch_file (finfo, vers_ts, docheckout, file_info, checksum)
}
else
{
- /* FIXME: We should use -a if diff supports it. We should
- probably just copy over most or all of the diff
- handling in the RCS configure script. */
- /* IMHO, we shouldn't copy over anything which even
- vaguely resembles the RCS configure script. That kind of
- thing tends to be ugly, slow, and fragile. It also is a
- a support headache for CVS to behave differently in subtle
- ways based on whether it was installed correctly. Instead we
- should come up with a diff library. -kingdon, Apr 1997. */
+ /* Now that diff is librarified, we could be passing -a if
+ we wanted to. However, it is unclear to me whether we
+ would want to. Does diff -a, in any significant
+ percentage of cases, produce patches which are smaller
+ than the files it is patching? I guess maybe text
+ files with character sets which diff regards as
+ 'binary'. Conversely, do they tend to be much larger
+ in the bad cases? This needs some more
+ thought/investigation, I suspect. */
+
diff_options = "-n";
}
- run_setup ("%s %s %s %s", DIFF, diff_options, file1, file2);
+ retcode = diff_exec (file1, file2, diff_options, finfo->file);
/* A retcode of 0 means no differences. 1 means some differences. */
- if ((retcode = run_exec (RUN_TTY, finfo->file, RUN_TTY, RUN_NORMAL)) != 0
+ if (retcode != 0
&& retcode != 1)
{
fail = 1;
@@ -1434,7 +1463,7 @@ patch_file (finfo, vers_ts, docheckout, file_info, checksum)
file_info->st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH))
< 0)
error (0, errno, "cannot change mode of file %s", finfo->file);
- if (cvswrite == TRUE
+ if (cvswrite
&& !fileattr_get (finfo->file, "_watched"))
xchmod (finfo->file, 1);
@@ -1482,7 +1511,7 @@ patch_file (finfo, vers_ts, docheckout, file_info, checksum)
if (!really_quiet)
{
- write_letter (finfo->file, 'P', finfo->update_dir);
+ write_letter (finfo, 'P');
}
}
else
@@ -1528,7 +1557,7 @@ patch_file_write (callerdat, buffer, len)
data->final_nl = (buffer[len - 1] == '\n');
if (data->compute_checksum)
- MD5Update (&data->context, buffer, len);
+ MD5Update (&data->context, (unsigned char *) buffer, len);
}
#endif /* SERVER_SUPPORT */
@@ -1537,27 +1566,45 @@ patch_file_write (callerdat, buffer, len)
* Several of the types we process only print a bit of information consisting
* of a single letter and the name.
*/
-static int
-write_letter (file, letter, update_dir)
- char *file;
+static void
+write_letter (finfo, letter)
+ struct file_info *finfo;
int letter;
- char *update_dir;
{
if (!really_quiet)
{
- char buf[2];
+ char *tag = NULL;
+ /* Big enough for "+updated" or any of its ilk. */
+ char buf[80];
+
+ switch (letter)
+ {
+ case 'U':
+ tag = "updated";
+ break;
+ default:
+ /* We don't yet support tagged output except for "U". */
+ break;
+ }
+
+ if (tag != NULL)
+ {
+ sprintf (buf, "+%s", tag);
+ cvs_output_tagged (buf, NULL);
+ }
buf[0] = letter;
buf[1] = ' ';
- cvs_output (buf, 2);
- if (update_dir[0])
+ buf[2] = '\0';
+ cvs_output_tagged ("text", buf);
+ cvs_output_tagged ("fname", finfo->fullname);
+ cvs_output_tagged ("newline", NULL);
+ if (tag != NULL)
{
- cvs_output (update_dir, 0);
- cvs_output ("/", 1);
+ sprintf (buf, "-%s", tag);
+ cvs_output_tagged (buf, NULL);
}
- cvs_output (file, 0);
- cvs_output ("\n", 1);
}
- return (0);
+ return;
}
/*
@@ -1590,7 +1637,8 @@ merge_file (finfo, vers)
copy_file (finfo->file, backup);
xchmod (finfo->file, 1);
- if (strcmp (vers->options, "-kb") == 0)
+ if (strcmp (vers->options, "-kb") == 0
+ || wrap_merge_is_copy (finfo->file))
{
/* For binary files, a merge is always a conflict. We give the
user the two files, and let them resolve it. It is possible
@@ -1609,11 +1657,15 @@ merge_file (finfo, vers)
(struct stat *) NULL, (unsigned char *) NULL);
}
#endif
- error (0, 0, "binary file needs merge");
+ /* Is there a better term than "nonmergeable file"? What we
+ really mean is, not something that CVS cannot or does not
+ want to merge (there might be an external manual or
+ automatic merge process). */
+ error (0, 0, "nonmergeable file needs merge");
error (0, 0, "revision %s from repository is now in %s",
vers->vn_rcs, finfo->fullname);
error (0, 0, "file from working directory is now in %s", backup);
- write_letter (finfo->file, 'C', finfo->update_dir);
+ write_letter (finfo, 'C');
history_write ('C', finfo->update_dir, vers->vn_rcs, finfo->file,
finfo->repository);
@@ -1621,7 +1673,7 @@ merge_file (finfo, vers)
goto out;
}
- status = RCS_merge(vers->srcfile->path,
+ status = RCS_merge(finfo->rcs, vers->srcfile->path, finfo->file,
vers->options, vers->vn_user, vers->vn_rcs);
if (status != 0 && status != 1)
{
@@ -1684,7 +1736,7 @@ merge_file (finfo, vers)
if (!noexec)
error (0, 0, "conflicts found in %s", finfo->fullname);
- write_letter (finfo->file, 'C', finfo->update_dir);
+ write_letter (finfo, 'C');
history_write ('C', finfo->update_dir, vers->vn_rcs, finfo->file, finfo->repository);
@@ -1696,7 +1748,7 @@ merge_file (finfo, vers)
}
else
{
- write_letter (finfo->file, 'M', finfo->update_dir);
+ write_letter (finfo, 'M');
history_write ('G', finfo->update_dir, vers->vn_rcs, finfo->file,
finfo->repository);
}
@@ -1731,14 +1783,6 @@ join_file (finfo, vers)
jdate1 = date_rev1;
jdate2 = date_rev2;
- if (wrap_merge_is_copy (finfo->file))
- {
- error (0, 0,
- "Cannot merge %s because it is a merge-by-copy file.",
- finfo->fullname);
- return;
- }
-
/* Determine if we need to do anything at all. */
if (vers->srcfile == NULL ||
vers->srcfile->path == NULL)
@@ -2034,7 +2078,7 @@ join_file (finfo, vers)
"failed to check out %s file", finfo->fullname);
}
#endif
-
+
/*
* The users currently modified file is moved to a backup file name
* ".#filename.version", so that it will stay around for a few days
@@ -2053,14 +2097,96 @@ join_file (finfo, vers)
xchmod (finfo->file, 1);
options = vers->options;
-#ifdef HAVE_RCS5
#if 0
if (*options == '\0')
options = "-kk"; /* to ignore keyword expansions */
#endif
-#endif
- status = RCS_merge (vers->srcfile->path, options, rev1, rev2);
+ /* If the source of the merge is the same as the working file
+ revision, then we can just RCS_checkout the target (no merging
+ as such). In the text file case, this is probably quite
+ similar to the RCS_merge, but in the binary file case,
+ RCS_merge gives all kinds of trouble. */
+ if (vers->vn_user != NULL
+ && strcmp (rev1, vers->vn_user) == 0
+ /* See comments above about how No_Difference has already been
+ called. */
+ && vers->ts_user != NULL
+ && strcmp (vers->ts_user, vers->ts_rcs) == 0
+
+ /* This is because of the worry below about $Name. If that
+ isn't a problem, I suspect this code probably works for
+ text files too. */
+ && (strcmp (options, "-kb") == 0
+ || wrap_merge_is_copy (finfo->file)))
+ {
+ /* FIXME: what about nametag? What does RCS_merge do with
+ $Name? */
+ if (RCS_checkout (finfo->rcs, finfo->file, rev2, NULL, options,
+ RUN_TTY, (RCSCHECKOUTPROC)0, NULL) != 0)
+ status = 2;
+ else
+ status = 0;
+
+ /* OK, this is really stupid. RCS_checkout carefully removes
+ write permissions, and we carefully put them back. But
+ until someone gets around to fixing it, that seems like the
+ easiest way to get what would seem to be the right mode.
+ I don't check CVSWRITE or _watched; I haven't thought about
+ that in great detail, but it seems like a watched file should
+ be checked out (writable) after a merge. */
+ xchmod (finfo->file, 1);
+
+ /* Traditionally, the text file case prints a whole bunch of
+ scary looking and verbose output which fails to tell the user
+ what is really going on (it gives them rev1 and rev2 but doesn't
+ indicate in any way that rev1 == vn_user). I think just a
+ simple "U foo" is good here; it seems analogous to the case in
+ which the file was added on the branch in terms of what to
+ print. */
+ write_letter (finfo, 'U');
+ }
+ else if (strcmp (options, "-kb") == 0
+ || wrap_merge_is_copy (finfo->file))
+ {
+ /* We are dealing with binary files, but real merging would
+ need to take place. This is a conflict. We give the user
+ the two files, and let them resolve it. It is possible
+ that we should require a "touch foo" or similar step before
+ we allow a checkin. */
+ if (RCS_checkout (finfo->rcs, finfo->file, rev2, NULL, options,
+ RUN_TTY, (RCSCHECKOUTPROC)0, NULL) != 0)
+ status = 2;
+ else
+ status = 0;
+
+ /* OK, this is really stupid. RCS_checkout carefully removes
+ write permissions, and we carefully put them back. But
+ until someone gets around to fixing it, that seems like the
+ easiest way to get what would seem to be the right mode.
+ I don't check CVSWRITE or _watched; I haven't thought about
+ that in great detail, but it seems like a watched file should
+ be checked out (writable) after a merge. */
+ xchmod (finfo->file, 1);
+
+ /* Hmm. We don't give them REV1 anywhere. I guess most people
+ probably don't have a 3-way merge tool for the file type in
+ question, and might just get confused if we tried to either
+ provide them with a copy of the file from REV1, or even just
+ told them what REV1 is so they can get it themself, but it
+ might be worth thinking about. */
+ /* See comment in merge_file about the "nonmergeable file"
+ terminology. */
+ error (0, 0, "nonmergeable file needs merge");
+ error (0, 0, "revision %s from repository is now in %s",
+ rev2, finfo->fullname);
+ error (0, 0, "file from working directory is now in %s", backup);
+ write_letter (finfo, 'C');
+ }
+ else
+ status = RCS_merge (finfo->rcs, vers->srcfile->path, finfo->file,
+ options, rev1, rev2);
+
if (status != 0 && status != 1)
{
error (0, status == -1 ? errno : 0,
OpenPOWER on IntegriCloud