diff --git a/src/ChangeLog b/src/ChangeLog index f4246b70fa33ad21d97b8505312a40b66c688a2d..f0f2733df7d0a6b2147d4e7d7541479e61fa6ee9 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,25 @@ +2008-11-05 Mark D. Baushke <mdb@gnu.org> + + *diff.c (diff): Use SEND_FORCE on files when a -k option is used + with diff to avoid client/server not finding the requested file. + * sanity.sh (keyword): Add tests for this. + + * checkout.c (checkout): For multiroot checkout/update operations, + do not assume that the static join_rev1 and join_rev2 strings will + not be seen again with another repository. + * update.c (update): Ditto. + * commit.c (commit): For multiroot commits with -F logmessage + do not generate "cannot specify both a message and a log file" + errors. Track the use of -m as an argument explicitly. + (finaladd): API change to pass the saved_message. + (check_fileproc, commit_fileproc, commit_filesdoneproc): Pass the + saved_message via the callerdat block. + (commit_direntproc): Ditto. + * sanity.sh (multiroot-{commit-2,update-join-{1,2},admin-{1,2,3,4}}): + Do commits with -F logmessage to test above and also look at other + broken behavior which needs to be fixed. There are some FIXME + tests here which should be addressed. + 2008-10-15 Derek R. Price <derek@ximbiot.com> * checkout.c (findslash): Declare inputs const. Use ISSLASH(). diff --git a/src/checkout.c b/src/checkout.c index 0f408341f7fe172b9125fdbeb8dc59b32f23f918..ddf1b2e1ab2564a57b2c8f6d1ef61f2d24406401 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -128,6 +128,7 @@ checkout (int argc, char **argv) int shorten = -1; char *where = NULL; const char *valid_options; + int jrev_count = 0; const char *const *valid_usage; char *join_orig1, *join_orig2; @@ -235,15 +236,15 @@ checkout (int argc, char **argv) checkout_prune_dirs = 1; break; case 'j': - if (join_rev2 || join_date2) + jrev_count++; + if (jrev_count > 2) error (1, 0, "only two -j options can be specified"); - if (join_rev1 || join_date1) - { + if (jrev_count == 2) { if (join_orig2) free (join_orig2); join_orig2 = xstrdup (optarg); parse_tagdate (&join_rev2, &join_date2, optarg); } - else + else if (jrev_count == 1) { if (join_orig1) free (join_orig1); join_orig1 = xstrdup (optarg); diff --git a/src/commit.c b/src/commit.c index 5e5fbabc9db6255b1a8a3759b0cdd89de6f09ebc..0397f754be4b941535ec859a5a97f7154baef740 100644 --- a/src/commit.c +++ b/src/commit.c @@ -60,7 +60,7 @@ static int commit_filesdoneproc (void *callerdat, int err, const char *repository, const char *update_dir, List *entries); static int finaladd (struct file_info *finfo, char *revision, char *tag, - char *options); + char *options, char *msg); static int findmaxrev (Node * p, void *closure); static int lock_RCS (const char *user, RCSNode *rcs, const char *rev, const char *repository); @@ -85,6 +85,10 @@ struct master_lists List *cilist; /* list with commit_info structs */ }; +struct commit_data +{ + char *saved_message; +}; static int check_valid_edit = 0; static int force_ci = 0; static int got_message; @@ -94,7 +98,6 @@ static char *write_dirtag; static int write_dirnonbranch; static char *logfile; static List *mulist; -static char *saved_message; static time_t last_register_time; static const char *const commit_usage[] = @@ -363,6 +366,7 @@ commit (int argc, char **argv) int c; int err = 0; int local = 0; + struct commit_data commit_data; #ifdef CLIENT_SUPPORT int flags; @@ -375,6 +379,8 @@ commit (int argc, char **argv) const char short_options[] = COMMIT_OPTIONS; #endif /* SERVER_SUPPORT */ + memset(&commit_data, 0, sizeof commit_data); + if (argc == -1) usage (commit_usage); @@ -423,13 +429,13 @@ commit (int argc, char **argv) #else use_editor = 0; #endif - if (saved_message) + if (commit_data.saved_message) { - free (saved_message); - saved_message = NULL; + free (commit_data.saved_message); + commit_data.saved_message = NULL; } - saved_message = xstrdup (optarg); + commit_data.saved_message = xstrdup (optarg); break; case 'r': if (saved_tag) @@ -481,10 +487,11 @@ commit (int argc, char **argv) { size_t size = 0, len; - if (saved_message) + if (commit_data.saved_message) error (1, 0, "cannot specify both a message and a log file"); - get_file (logfile, logfile, "r", &saved_message, &size, &len); + get_file (logfile, logfile, "r", &commit_data.saved_message, + &size, &len); } #ifdef CLIENT_SUPPORT @@ -553,10 +560,12 @@ commit (int argc, char **argv) * The protocol is designed this way. This is a feature. */ if (use_editor) - do_editor (NULL, &saved_message, NULL, find_args.ulist); - + do_editor (NULL, &commit_data.saved_message, NULL, + find_args.ulist); + /* We always send some sort of message, even if empty. */ - option_with_arg ("-m", saved_message ? saved_message : ""); + option_with_arg ("-m", commit_data.saved_message + ? commit_data.saved_message : ""); /* OK, now process all the questionable files we have been saving up. */ @@ -646,7 +655,7 @@ commit (int argc, char **argv) send_to_server ("ci\012", 0); err = get_responses_and_close (); - if (err != 0 && use_editor && saved_message != NULL) + if (err != 0 && use_editor && commit_data.saved_message != NULL) { /* If there was an error, don't nuke the user's carefully constructed prose. This is something of a kludge; a better @@ -664,8 +673,9 @@ commit (int argc, char **argv) if (fp == NULL) error (1, 0, "cannot create temporary file %s", fname ? fname : "(null)"); - if (fwrite (saved_message, 1, strlen (saved_message), fp) - != strlen (saved_message)) + if (fwrite (commit_data.saved_message, 1, + strlen (commit_data.saved_message), fp) + != strlen (commit_data.saved_message)) error (1, errno, "cannot write temporary file %s", fname); if (fclose (fp) < 0) error (0, errno, "cannot close temporary file %s", fname); @@ -710,7 +720,8 @@ commit (int argc, char **argv) * Run the recursion processor to verify the files are all up-to-date */ if (start_recursion (check_fileproc, check_filesdoneproc, - check_direntproc, NULL, NULL, argc, argv, local, + check_direntproc, NULL, &commit_data, + argc, argv, local, W_LOCAL, aflag, CVS_LOCK_NONE, NULL, 1, NULL)) error (1, 0, "correct above errors first!"); @@ -720,7 +731,8 @@ commit (int argc, char **argv) write_dirnonbranch = 0; if (noexec == 0) err = start_recursion (commit_fileproc, commit_filesdoneproc, - commit_direntproc, commit_dirleaveproc, NULL, + commit_direntproc, commit_dirleaveproc, + &commit_data, argc, argv, local, W_LOCAL, aflag, CVS_LOCK_WRITE, NULL, 1, NULL); @@ -730,6 +742,12 @@ commit (int argc, char **argv) Lock_Cleanup (); dellist (&mulist); + if (commit_data.saved_message) + { + free (commit_data.saved_message); + commit_data.saved_message = NULL; + } + /* see if we need to sleep before returning to avoid time-stamp races */ if (!server_active && last_register_time) { @@ -1381,6 +1399,7 @@ check_filesdoneproc (void *callerdat, int err, const char *repos, int n; Node *p; List *saved_ulist; + struct commit_data *commit_data = callerdat; TRACE (TRACE_FLOW, "check_filesdoneproc (%d, %s, %s, %s)", err, repos, update_dir, TRACE_BOOL (use_editor)); @@ -1413,9 +1432,10 @@ check_filesdoneproc (void *callerdat, int err, const char *repos, */ got_message = 1; if (!server_active && use_editor) - do_editor (update_dir, &saved_message, repos, saved_ulist); + do_editor (update_dir, &commit_data->saved_message, repos, + saved_ulist); - err += do_verify (&saved_message, repos, saved_ulist); + err += do_verify (&commit_data->saved_message, repos, saved_ulist); return err; } @@ -1436,6 +1456,7 @@ commit_fileproc (void *callerdat, struct file_info *finfo) int err = 0; List *ulist, *cilist; struct commit_info *ci; + struct commit_data *commit_data = callerdat; /* Keep track of whether write_dirtag is a branch tag. Note that if it is a branch tag in some files and a nonbranch tag @@ -1510,7 +1531,7 @@ commit_fileproc (void *callerdat, struct file_info *finfo) free (ci->rev); ci->rev = RCS_whatbranch (finfo->rcs, ci->tag); err = Checkin ('A', finfo, ci->rev, - ci->tag, ci->options, saved_message); + ci->tag, ci->options, commit_data->saved_message); if (err != 0) { unlockrcs (finfo->rcs); @@ -1548,14 +1569,16 @@ commit_fileproc (void *callerdat, struct file_info *finfo) } /* XXX - an added file with symbolic -r should add tag as well */ - err = finaladd (finfo, ci->rev ? ci->rev : xrev, ci->tag, ci->options); + err = finaladd (finfo, ci->rev ? ci->rev : xrev, ci->tag, ci->options, + commit_data->saved_message); + if (xrev) free (xrev); } else if (ci->status == T_MODIFIED) { err = Checkin ('M', finfo, ci->rev, ci->tag, - ci->options, saved_message); + ci->options, commit_data->saved_message); (void) time (&last_register_time); @@ -1567,7 +1590,7 @@ commit_fileproc (void *callerdat, struct file_info *finfo) } else if (ci->status == T_REMOVED) { - err = remove_file (finfo, ci->tag, saved_message); + err = remove_file (finfo, ci->tag, commit_data->saved_message); #ifdef SERVER_SUPPORT if (server_active) { @@ -1647,6 +1670,7 @@ commit_filesdoneproc (void *callerdat, int err, const char *repository, { Node *p; List *ulist; + struct commit_data *commit_data = callerdat; assert (repository); @@ -1715,7 +1739,7 @@ commit_filesdoneproc (void *callerdat, int err, const char *repository, * A more general solution I have been considering is calling a generic * "postwrite" hook from the remove write lock routine. */ - Update_Logfile (repository, saved_message, NULL, ulist); + Update_Logfile (repository, commit_data->saved_message, NULL, ulist); return err; } @@ -1966,11 +1990,12 @@ remove_file (struct file_info *finfo, char *tag, char *message) * Do the actual checkin for added files */ static int -finaladd (struct file_info *finfo, char *rev, char *tag, char *options) +finaladd (struct file_info *finfo, char *rev, char *tag, char *options, + char *msg) { int ret; - ret = Checkin ('A', finfo, rev, tag, options, saved_message); + ret = Checkin ('A', finfo, rev, tag, options, msg); if (ret == 0) { char *tmp = Xasprintf ("%s/%s%s", CVSADM, finfo->file, CVSEXT_LOG); diff --git a/src/diff.c b/src/diff.c index e8a5394c409f4631badd9c44c0fc02d5a12f6230..5fa6fd07505dc41e7af7e3ed2d610d8aa8adc448 100644 --- a/src/diff.c +++ b/src/diff.c @@ -445,6 +445,8 @@ diff (int argc, char **argv) if ((!suppress_bases && supported_request ("Base-diff")) || diff_rev2 || diff_date2) flags |= SEND_NO_CONTENTS; + else if (options[0] != '\0') + flags |= SEND_FORCE; send_files (argc, argv, local, 0, flags); diff --git a/src/sanity.sh b/src/sanity.sh index 7f268bddfc6f173044eecba322f901ff55b3d4bf..9478818e7542a0f1d8dee077e1a4e3c7707cdfc6 100755 --- a/src/sanity.sh +++ b/src/sanity.sh @@ -26540,6 +26540,18 @@ xx "'\$'"Log"'\$' dotest keyword-24 "cat file1" '\$'"Name: "'\$'" change" + dotest_fail keyword-25 "${testcvs} diff -kk file1" \ +"Index: file1 +=================================================================== +RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v +retrieving revision 1\.3 +diff -r1\.3 file1 +1c1 +< \$Name\$ +--- +> \$Name: $" + dotest keyword-26 "${testcvs} diff -kkv file1" "" + dokeep cd ../.. rm -rf 1 @@ -30922,6 +30934,91 @@ new revision: 1.2; previous revision: 1.1 ${CVSROOT2_DIRNAME}/mod2-2/file2-2,v <-- mod2-2/file2-2 new revision: 1.2; previous revision: 1.1" + echo Extra-line >> mod1-1/file1-1 + echo Extra-line >> mod2-2/file2-2 + echo 'Using a message file.' >log-message.txt + dotest multiroot-commit-2 \ +"${testcvs} commit -F log-message.txt mod1-1/file1-1 mod2-2/file2-2" \ +"${CVSROOT1_DIRNAME}/mod1-1/file1-1,v <-- mod1-1/file1-1 +new revision: 1\.3; previous revision: 1\.2 +${CVSROOT2_DIRNAME}/mod2-2/file2-2,v <-- mod2-2/file2-2 +new revision: 1\.3; previous revision: 1\.2" + + dotest multiroot-update-join-1 \ +"${testcvs} update -j1.3 -j1.2 mod1-1/file1-1 mod2-2/file2-2" \ +"${CPROG} update: Replacing \`mod1-1/file1-1' with contents of revision 1\.2\. +M mod1-1/file1-1 +${CPROG} update: Replacing \`mod2-2/file2-2' with contents of revision 1\.2\. +M mod2-2/file2-2" + dotest multiroot-update-join-2 \ +"${testcvs} -Q update -C mod1-1/file1-1 mod2-2/file2-2" \ +"" + + rm log-message.txt + dotest multiroot-admin-1 "${testcvs} admin -o1.3 mod1-1/file1-1" \ +"RCS file: ${CVSROOT1_DIRNAME}/mod1-1/file1-1,v +deleting revision 1.3 +done" + # FIXME: For $remote operation, an error like: + # cvs [admin aborted]: no such directory `mod2-2' + # will happen if the CVS/Root has $CVSROOT1 in it. + # If CVS is missing and there is no CVSROOT environment + # variable, there is also a "No CVSROOT specified!" + # error which really needs to be fixed too. + dotest multiroot-admin-2 \ +"${testcvs} -d ${CVSROOT2} admin -o1.3 mod2-2/file2-2" \ +"RCS file: ${CVSROOT2_DIRNAME}/mod2-2/file2-2,v +deleting revision 1.3 +done" + + if $remote; then + # FIXME: Just becuase CVS/Root does not have a mod2-2 + # directory does not mean that recurse should print an + # error. + dotest multiroot-admin-3ra "${testcvs} status mod2-2/file2-2" \ +"${CPROG} \[status aborted\]: no such directory \`mod2-2' +=================================================================== +File: file2-2 Status: Needs Patch + + Working revision: 1\.3 + Repository revision: 1\.2 ${CVSROOT2_DIRNAME}/mod2-2/file2-2,v + Commit Identifier: ${commitid} + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: (none)" + + dotest_fail multiroot-admin-3rb \ +"${testcvs} update mod1-1/file1-1 mod2-2/file2-2" \ +"${CPROG} \[update aborted\]: could not find desired version 1\.3 in ${CVSROOT1_DIRNAME}/mod1-1/file1-1,v +${CPROG} \[update aborted\]: could not find desired version 1\.3 in ${CVSROOT2_DIRNAME}/mod2-2/file2-2,v" + + # FIXME. For client/server, the removal of revisoin 1.3 is not + # directly recoverable without hacking CVS/Entries here. + mv mod1-1/CVS/Entries mod1-1/CVS/Entries.bad + grep -v file1-1 < mod1-1/CVS/Entries.bad > mod1-1/CVS/Entries + mv mod2-2/CVS/Entries mod2-2/CVS/Entries.bad + grep -v file2-2 < mod2-2/CVS/Entries.bad > mod2-2/CVS/Entries + rm mod1-1/CVS/Entries.bad mod2-2/CVS/Entries.bad + rm mod1-1/file1-1 mod2-2/file2-2 + else + dotest multiroot-admin-3 "${testcvs} status mod2-2/file2-2" \ +"=================================================================== +File: file2-2 Status: Needs Patch + + Working revision: 1\.3.* + Repository revision: 1\.2 ${CVSROOT2_DIRNAME}/mod2-2/file2-2,v + Commit Identifier: ${commitid} + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: (none)" + fi + + dotest multiroot-admin-4 \ +"${testcvs} update mod1-1/file1-1 mod2-2/file2-2" \ +"U mod1-1/file1-1 +U mod2-2/file2-2" + + dotest multiroot-update-2 "${testcvs} update" \ "${CPROG} update: Updating \. ${CPROG} update: Updating mod1-1 diff --git a/src/update.c b/src/update.c index 6d0bfc3b67b3d36f67c33ad5291ac51f8c43cb9f..af051d513bae1d4604b4fc2dc69179b49f70de12 100644 --- a/src/update.c +++ b/src/update.c @@ -165,6 +165,7 @@ update (int argc, char **argv) int c, err; int local = 0; /* recursive by default */ int which; /* where to look for files and dirs */ + int jrev_count = 0; /* multiple cvsroot w/ -j options */ char *xjoin_rev1, *xjoin_date1, *xjoin_rev2, *xjoin_date2, *join_orig1, *join_orig2; @@ -237,14 +238,15 @@ update (int argc, char **argv) noexec = 1; /* so no locks will be created */ break; case 'j': - if (join_orig2) + jrev_count++; + if (jrev_count > 2) error (1, 0, "only two -j options can be specified"); - if (join_orig1) + if (jrev_count == 2) { join_orig2 = xstrdup (optarg); parse_tagdate (&xjoin_rev2, &xjoin_date2, optarg); } - else + else if (jrev_count == 1) { join_orig1 = xstrdup (optarg); parse_tagdate (&xjoin_rev1, &xjoin_date1, optarg);