[Without this patch, there is no way to show log entries corresponding to changes from tag "foo" to tag "bar" ("cvs log -rfoo:bar" doesn't cut it, because it erroneously shows the changes associated with the change from the revision before foo to foo). (In case it helps with identification, this issue is #182 in the TODO file). This patch adds a "cvs log -rfoo::bar" syntax with the correct behavior. Known issues with this patch: 1. Deprecate the old losing behavior "V1:V2". Update NEWS and cvs.texinfo, both concerning this and the the new V1::V2 syntax. 1a. What about "cvs log -r foo -r bar" as a syntax? Would seem to be a better analogy with "cvs diff". 2. Need testcases for new behaviors. (1) case where a file is changed, (2) case where a file is not changed, e.g. V1 and V2 are both revision 1.3 (should print nothing for that file), (3) the following cases: File exists in V1 File exists in V2 Should log ---------------------------------------------------------------- yes yes yes yes no (Attic) yes * no (not created) yes (added) yes no (Attic) yes (resurrected) yes no (Attic) no (Attic) no + * The cvs 1.9 log command will print a warning about "no revision `V2'" and then dump all existing log entries. I suppose this is a bug: CVS 1.8 printed only a message "rlog: Symbolic name `V2' is undefined.". I could argue that since it recognizes the first tag (V1) it should instead perform the equivalent of "cvs log -rV1: deleted.txt". You easily run into this when running cvs log on a directory since it recurses into the Attic. (The "cvs log -rV1:" behavior looks right; needs a testcase). + This case may also be buggy ("two warnings, then all logs are dumped."). 3. One particular nasty case: PB>As I write this I see a problem. If a file exists in V1, but not in V2 and is then resurrected in V3 the command "cvs log -rV1:V2" should not behave as "cvs log -rV1:". Ouch! I don't see a solution to this. OK, now I really need your help! JK>The only solution I can think of for this is to have tag and rtag also tag the dead revisions in the Attic. I suppose another, somewhat kludgy, solution would be for "cvs log -rV1:V2" to log until the first dead revision. That involves a smaller change (and fewer fears of breaking anything), but isn't really correct. PB>Apparently "rtag -r V1 V2" tags files in the Attic if they have the V1 tag. This would work for me. Is this generally done? Before releasing V2 I always run "rtag -r V1 V2" to get a copy of V1, then add the files I want in V2 by manually tagging a suitable revision, then checking out the V2 files to build them. If a file is removed, I remove the V2 tag so I know it isn't part of the release, perhaps I shouldn't? Any comments on this? [Can run "rtag -r V1 V2" to force tagging the attic (the above case with V1 == "HEAD") -kingdon] JK>The bit about tagging dead revisions looks very nasty (would "cvs tag" rely on Entries.Static or something?). 4. Adding yet another meaning to the -q global option is almost surely a bad idea (see #190 in TODO). -kingdon, 12 Oct 1997] Date: Thu, 09 Oct 1997 13:13:53 +0200 From: Peter Brandstrom MIME-Version: 1.0 To: Jim Kingdon Subject: Re: Using 'cvs log' to see changes between releases References: <199710081517.LAA26496@harvey.cyclic.com> Content-Type: multipart/mixed; boundary="------------15E47EC814CD" This is a multi-part message in MIME format. - --------------15E47EC814CD Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit . . . So I send you three patches to be applied in order. This is the first time I create patches so please let me know if there's a problem. Patches were created with "diff -w -c -C2" and "diff -v" prints "diff - GNU diffutils version 2.7". "cat patch* | patch" seemed to work. The only file modified is log.c in the src directory. I started out with the source to CVS 1.9. The first patch enables "log -rV1::V2" which logs from V1 to V2 but does not include V1. The second patch makes log not print anything if no revisions were selected for a file. There is room for improvement here, a separate function should be used for the "log_data_and_rcs" operations. The third patch tries to change the behaviour when you supply a tag which a file doesn't have. In CVS 1.9 warnings were always printed and all revisions were printed. The patch makes "-q" hide warnings (perhaps not good) and ignore missing tags. Suppose "-rV1:V2" is used. If V1 is missing, log behaves like "-r:V2". If V2 is missing, log behaves like "-rV1:". If both are missing, warnings are printed unless "-q" was also supplied and no revisions will be printed on account of this option (other "-r" are not affected and might result in logs). I've generally opted to go for the smallest possible change. The sanity check runs OK which probably says more about it than my programming. I tried Purify at one point and it didn't complain much. What are the chances of adding such a patch to the official distribution? Regards, Peter Brandstrom - --------------15E47EC814CD Content-Type: text/plain; charset=us-ascii; name="patch1" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="patch1" *** 1/log.c Thu Oct 9 11:16:14 1997 - --- 2/log.c Thu Oct 9 11:16:19 1997 *************** *** 34,37 **** - --- 34,40 ---- the head revision of a branch. */ int branchhead; + /* Zero if we want to include the first revision in a range. + One if we should skip the first revision in a range. */ + int exclude_first; }; *************** *** 50,53 **** - --- 53,59 ---- numdots). */ int fields; + /* Zero if we want to include the first revision in a range. + One if we should skip the first revision in a range. */ + int exclude_first; }; *************** *** 274,277 **** - --- 280,284 ---- ret->next = NULL; ret->branchhead = 0; + ret->exclude_first = 0; return ret; } *************** *** 291,294 **** - --- 298,302 ---- char *first, *last; struct option_revlist *r; + int exclude_first = 0; comma = strchr (copy, ','); *************** *** 303,306 **** - --- 311,320 ---- { *cp++ = '\0'; + if (*cp == ':') + { + /* -rV1::V2 means don't include V1 */ + exclude_first = 1; + ++cp; + } last = cp; } *************** *** 315,318 **** - --- 329,334 ---- r->first = first; r->last = last; + r->exclude_first = exclude_first; + if (first != last || first[strlen (first) - 1] != '.') *************** *** 785,788 **** - --- 801,805 ---- nr = (struct revlist *) xmalloc (sizeof *nr); + nr->exclude_first = r->exclude_first; if (r->first == NULL && r->last == NULL) *************** *** 1068,1073 **** for (r = revlist; r != NULL; r = r->next) { if (vfields == r->fields + (r->fields & 1) ! && version_compare (v, r->first, r->fields) >= 0 && version_compare (v, r->last, r->fields) <= 0) { - --- 1085,1092 ---- for (r = revlist; r != NULL; r = r->next) { + /* r->exclude_first is one if first version should be omitted, + ie (r->first > v) rather than (r->first >= v) */ if (vfields == r->fields + (r->fields & 1) ! && version_compare (v, r->first, r->fields) >= r->exclude_first && version_compare (v, r->last, r->fields) <= 0) { - --------------15E47EC814CD Content-Type: text/plain; charset=us-ascii; name="patch2" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="patch2" *** 2/log.c Thu Oct 9 11:16:19 1997 - --- 3/log.c Thu Oct 9 11:16:22 1997 *************** *** 494,497 **** - --- 494,498 ---- struct revlist *revlist; struct log_data_and_rcs log_data_and_rcs; + int selected_revisions; if ((rcsfile = finfo->rcs) == NULL) *************** *** 534,537 **** - --- 535,559 ---- log_data->default_branch); + + log_data_and_rcs.log_data = log_data; + log_data_and_rcs.revlist = revlist; + log_data_and_rcs.rcs = rcsfile; + + /* If any single dates were specified, we need to identify the + revisions they select. Each one selects the single + revision, which is otherwise selected, of that date or + earlier. The log_fix_singledate routine will fill in the + start date for each specific revision. */ + if (log_data->singledatelist != NULL) + walklist (rcsfile->versions, log_fix_singledate, + (void *) &log_data_and_rcs); + + selected_revisions = walklist (rcsfile->versions, log_count_print, + (void *) &log_data_and_rcs); + + /* If no revisions selected, don't print it. */ + if (selected_revisions == 0) + return 0; + /* The output here is intended to be exactly compatible with the output of rlog. I'm not sure whether this code should be here *************** *** 700,718 **** cvs_output (";\tselected revisions: ", 0); ! log_data_and_rcs.log_data = log_data; ! log_data_and_rcs.revlist = revlist; ! log_data_and_rcs.rcs = rcsfile; ! ! /* If any single dates were specified, we need to identify the ! revisions they select. Each one selects the single ! revision, which is otherwise selected, of that date or ! earlier. The log_fix_singledate routine will fill in the ! start date for each specific revision. */ ! if (log_data->singledatelist != NULL) ! walklist (rcsfile->versions, log_fix_singledate, ! (void *) &log_data_and_rcs); ! ! sprintf (buf, "%d", walklist (rcsfile->versions, log_count_print, ! (void *) &log_data_and_rcs)); cvs_output (buf, 0); } - --- 722,726 ---- cvs_output (";\tselected revisions: ", 0); ! sprintf (buf, "%d", selected_revisions); cvs_output (buf, 0); } - --------------15E47EC814CD Content-Type: text/plain; charset=us-ascii; name="patch3" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="patch3" *** 3/log.c Thu Oct 9 11:16:22 1997 - --- 4/log.c Thu Oct 9 11:17:22 1997 *************** *** 859,869 **** else nr->first = RCS_gettag (rcs, r->first, 1, (int *) NULL); ! if (nr->first == NULL) ! { error (0, 0, "warning: no revision `%s' in `%s'", r->first, rcs->path); - - free (nr); - - continue; - - } } - --- 859,866 ---- else nr->first = RCS_gettag (rcs, r->first, 1, (int *) NULL); ! ! if (!quiet && nr->first == NULL) error (0, 0, "warning: no revision `%s' in `%s'", r->first, rcs->path); } *************** *** 878,890 **** else nr->last = RCS_gettag (rcs, r->last, 1, (int *) NULL); ! if (nr->last == NULL) ! { error (0, 0, "warning: no revision `%s' in `%s'", r->last, rcs->path); - - if (nr->first != NULL) - - free (nr->first); - - free (nr); - - continue; } } - --- 875,891 ---- else nr->last = RCS_gettag (rcs, r->last, 1, (int *) NULL); ! ! if (!quiet && nr->last == NULL) error (0, 0, "warning: no revision `%s' in `%s'", r->last, rcs->path); } + + if (nr->first == NULL && nr->last == NULL) + { + /* This file is not at all included in the range. Use + versions that will never match any revision of this + file so it isn't processed. */ + nr->first = xstrdup (".0"); + nr->last = xstrdup (".0"); } *************** *** 893,897 **** keeping the same implementation as rlog ensures a certain degree of compatibility. */ ! if (r->first == NULL) { nr->fields = numdots (nr->last) + 1; - --- 894,898 ---- keeping the same implementation as rlog ensures a certain degree of compatibility. */ ! if (r->first == NULL || nr->first == NULL) { nr->fields = numdots (nr->last) + 1; *************** *** 907,911 **** } } ! else if (r->last == NULL) { nr->fields = numdots (nr->first) + 1; - --- 908,912 ---- } } ! else if (r->last == NULL || nr->last == NULL) { nr->fields = numdots (nr->first) + 1; - --------------15E47EC814CD-- ------- End of forwarded message -------