[ On Wednesday, May 10, 2000 at 16:03:00 (-0500), Michael Gersten wrote: ]
> Subject: Re: bug in import?
>
> Ok, so what I hear you saying is that I need to tag the vendor branch
> both before AND after doing the import, then give those two tags to an
> update -j. (something else that I didn't see in the manual -- I had
> thought that the vendor branch support was magic).

No, not necessarily.  The imports themselves provide the tags necessary
for the merge -- the only trick is that you might have to look up the
"previous" tag in order to do the merge (unless it's 100% predictable
courtesy of some local naming policy you implement).

For example with my test I did:

        $ cvs import -m 'first import' testi TESTI TESTI-1
        $ rm foo.c
        $ cvs import -m 'second import' testi TESTI TESTI-2

and thus the merge was:

        $ cvs update -j TESTI-1 -j TESTI-2 .
        cvs update: Updating .
        cvs update: scheduling foo.c for removal

The vendor branch support is supposed to be magic, but because of the
way "cvs import" is implemented it is quite "hard" for it to see when a
file exists on the head of the vendor branch but not in the tree being
imported.  It would pretty well be necessary to require that the user
specify the previous tag to "cvs import" in order to make this foolproof
(guessing it is "hard" -- i.e. it cannot be done safely without first
looking at *all* of the files in that module in the repository (except
in the Attic dirctories) and finding the newest revision on the vendor
branch in any file and then looking to see what tag on that file matches
that revision), and of course the import algorithm would also have to be
updated so that it walks the repository directories in sync just as it
does now in most other sub-commands (thus making it slower and quite a
bit more complex internally).

I haven't yet figured out why the "-jVENDOR:yesterday" trick suggested
by "cvs import" doesn't work properly for doing these merges either.  I
just got frustrated and tried an exact merge using the actual tags and
it worked (better) so that's what I've been doing ever since.  At one
time I advocated that the suggestion not be given at all and instead
that the user be advised in the manual to always follow the procedure I
outline of doing an explicit merge with the explicit vendor release
tags.

All that's really "automatic magic" in the vendor branch support is the
fact that "cvs import" automatically creates the branch structure, and
any truly obvious conflicts with local changes on the trunk are detected
and reported so that the maintainer can have some indication as to how
difficult the creation of a local release might be.  In the end it might
be just as easy to skip the automatic conflict detection, in which case
it's really pointless to use a magic "vendor" branch -- it would be just
as easy to import the vendor release directly to the trunk and to create
normal branches to identify local changes, re-starting a new local
"release" branch after every import.  In fact I've actually done this a
couple of times where I was dealing with a primary third party release
and a number of secondary third party versions, plus of course local
changes; since multiple vendor branches as described in the paper were
never really implemented and don't stand much of a chance of ever
working right anyway (that's why I wrote the hack to import to the trunk
that is/was available in the Cyclic CVS patch archive).  This was a bit
more work, of course, but it was quite straight forward and it worked.
It doesn't work very well if each secondary third-party variant moves a
lot of files around, but if everything stays put it's just fine.

>  [ ahh -- now I understand what you were saying -- foo.c is modified,
> but "cvs rm" isn't a patch against /dev/null + remove if empty, but just
> a remove. ]

Yes!  Exactly.

So far as I can tell at first glance there's no easy way for the CVS
"merge" operation to record in the working directory that the file needs
to be removed in order to bring things into sync with the new vendor
version, but to also indicate that there are conflicts.

> Here I have a suggestion that will fix this.
> 
> The idea of "File Removed" should be changed to "file is atomically both
> made equal to /dev/null and removed".
> 
> If I have a file on a branch, that has been removed on another branch,
> then when these are brought into sync with each other (regardless of
> which was on the trunk or common branch first), then the "removed" file
> should just be considered "/dev/null", the modified file will differ in
> that all the lines that are new in it are not removed (they weren't in
> the original, so they could not have been removed when the file was
> deleted), so the resulting merge will be a non-empty set of conflicts
> (the added lines), and the resulting "has conflicts' file will just be
> the changes.
> 
> The result in this case: The file being removed on the vendor branch is
> the same as all the original vendor lines being removed; all that is
> left is your lines, all of which are in conflict with an empty file.
> And, since this is (at least, this is what I'm proposing here) the
> meaning of a removed file, you CAN just mark it as removed, and it will
> not be brought back by another cvs update.

Hmmm....  Patching the modified file against /dev/null won't really
work, I don't think, but I suppose something like the opposite of a
patch against /dev/null could be done, i.e. the equivalent of putting
just the local changes into the file:

        cvs diff -r TESTI-1 -r 1 foo.c > foo.c

Then you'd be left with a change to foo.c that effectively removed all
of the vendor code and left just the local changes.  If this were
doctored up with conflict markers, maybe like this:

        $ cvs co -r 1 foo.c > foo.c-trunk
        $ cvs co -r 1.1.1 foo.c > foo.c-last-vendor
        $ diff3 -aAm foo.c-trunk foo.c-last-vendor /dev/null > foo.c

(note that due to a bug you can't actually do the "cvs update -p
-r1.1.1" in the working directory and the error message given is very
misleading)

i.e. do a three-way diff between the head of the trunk and the head of
the vendor branch (i.e. the revision as of the previous import), and
/dev/null, with conflict markers.  In my example from earlier today I
don't have enough lines of text to really show this properly, but
perhaps it'll do:

        $ cat foo.c-1.2
        foo
        barf
        $ cat foo.c-1.1.1.1
        foo
        $ diff3 -aAm foo.c-1.2 foo.c-1.1.1.1 /dev/null          
        <<<<<<< foo.c-1.2
        foo
        barf
        ||||||| foo.c-1.1.1.1
        foo
        =======
        >>>>>>> /dev/null

This might not be so pretty if the file's been removed, re-added, and
then removed again, perhaps several times, but it would at least make
the "conflict" quite obvious.

Even better if the merge marks the file as removed but also leave it
there with just the diff3 output in it then the next "cvs update" will
give a warning message that really makes things very obvious:

        $ cvs -nq update 
        cvs update: foo.c should be removed and is still there
        R foo.c
        ? foo.c-1.2
        ? foo.c-1.1.1.1

(this warning doesn't currently prevent a commit from proceeding, but in
this case it probably should....  maybe someone else familiar with the
internals could comment on whether or not it would be sensible for "cvs
update" to continue looking for a "+CONFLICT" timestamp in the
CVS/Entries file even when the file is also marked as removed -- this
should prevent a commit from proceeding because of the unresolved
conflict)

Hmmm....  Yes, I like this idea....  Now, how to implement it....

> Now this worries me. Are you saying that this can happen on non-vendor
> branch changes if a file is modified on a branch (or trunk), and deleted
> on the trunk (or branch), and then the changes are merged with 'cvs
> update -A -jbranch'?

That may well be true.  I don't use normal branches near as much as I
use vendor branches so I've not yet encountered this situation....

Your trick of preserving just the changes in the merged file would
require knowing the ancestor revision though -- i.e. the branch point.
This is "easy" in the vendor branch case if you assume the branch
point is always 1.1!  ;-)

-- 
                                                        Greg A. Woods

+1 416 218-0098      VE3TCP      <[EMAIL PROTECTED]>      <robohack!woods>
Planix, Inc. <[EMAIL PROTECTED]>; Secrets of the Weird <[EMAIL PROTECTED]>

Reply via email to