[This doesn't provide any way to handle merges onto two different branches. For example, .MH_1.3.2.4 might want to reside on both 1.4 and 1.3.4.7. -kingdon] Date: Fri, 18 Dec 1998 10:03:35 -0800 From: John Cavanaugh To: bug-cvs@gnu.org Subject: Merge meta-data proposal... >From the CVS TODO file... 39. Think about a version of "cvs update -j" which remembers what from that other branch is already merged. This has pitfalls--it could easily lead to invisible state which could confuse users very rapidly--but having to create a tag or some such mechanism to keep track of what has been merged is a pain. Ok, I did some thinking and the answer is 42... Seriously though, I have been thinking about this merge meta data problem for awhile now and I think have come up with a way to accomplish this. Since this item has been around for a long time Im sure other people have had ideas and I wanted to run this by the group before sitting down to work on implmenting this. (Besides I was looking for some people willing to help ;-) Basically we can use specially named tags to store merge metadata (The specially named tags, might also work for dealing with renaming, but thats a *whole* other subject). The trick though is that we need to place the tag on the revision where the merge was made TO. (I think it is unnecesarily more complicated to place the tags on the FROM nodes) To describe how it works Ill work thru an example of merging from branch bar to the main branch. (This will also assume no previous merges have been done. ie. No merge meta data) Lets assume the revision tree for file foo.c looks like the following: foo.c@@/main/1.1 foo.c@@/main/1.2 foo.c@@/main/1.3 [bar] foo.c@@/main/bar/1.3.2.1 foo.c@@/main/bar/1.3.2.2 foo.c@@/main/bar/1.3.2.3 foo.c@@/main/bar/1.3.2.4 foo.c@@/main/1.4 Note: Branch tags placed in []'s and revision tags in ()' cvs checkout example/foo.c cd example cvs up -j bar foo.c Ok. Everything is like old hat so far, *but* cvs will note that a merge has been made into the CVS/Entries file. Ill propose something like. [CVS/Entries Before Merge] /foo.c/1.4/Thu Sep 17 23:25:21 1998// [CVS/Entries Before Merge] /foo.c/1.4/Thu Sep 17 23:25:21 1998/MH_1.3.2.4/ The user can then go about there business resolving any merge conflicts etc. vi foo.c cvs commit foo.c During the commit process, cvs will checkin the file and create the new revision *and* it will place a tag ".MH_1.3.2.4" on the new revision to denote that a merged occured from revision 1.3.2.4 to this revision. The reason that it is important to place the revision on the TO target instead of the FROM target is to simplify the search for the common ancestor during later merges. Consider now that the version tree has grown to look like: foo.c@@/main/1.1 foo.c@@/main/1.2 foo.c@@/main/1.3 [bar] foo.c@@/main/bar/1.3.2.1 foo.c@@/main/bar/1.3.2.2 foo.c@@/main/bar/1.3.2.3 foo.c@@/main/bar/1.3.2.4 foo.c@@/main/bar/1.3.2.5 foo.c@@/main/bar/1.3.2.6 foo.c@@/main/bar/1.3.2.7 foo.c@@/main/1.4 foo.c@@/main/1.5 (.MH_1.3.2.4) foo.c@@/main/1.6 foo.c@@/main/1.7 cvs update -A cvs update -j bar Now this time is where we imploy a search for the common ancestor in an intelligent fashion. We start with the identification of the "from" node (1.3.2.7). Now we need to find the common ancestor of the "to" node (1.7) and the "from" node. Traversing backwards from "to" node (1.7) to the root we find the following routes: #1 - 1.7, 1.6, 1.5, 1.4, 1.3, 1.2, 1.1 #2 - 1.7, 1.6, 1.5, *1.3.2.4, 1.3.2.3, 1.3.2.2, 1.3.2.1, 1.3, 1.2, 1.1 The * shows where we followed a merge hyperlink (ie MH) from our previous merge. Traversing backwards from "from" node (1.3.2.7) to the root we find the following route: #1 - 1.3.2.7, 1.3.2.6, 1.3.2.5, 1.3.2.4, 1.3.2.3, 1.3.2.2, 1.3.2.1, 1.3, 1.2, 1.1 Examining the two we find our first common ancestor of 1.3.2.4 which we will use for the merge. (You might be thinking, this is easy enough to do by hand why do we need changes. Well, please keep in mind that this is a simple example with only 2 branches. Things get mightly complicated with more than two branches and lots of merging happening between them. Way more than you want to keep track of by hand... ;-) After finding the common "ancestor" node (1.3.2.4) internally cvs now merges the equivalent of "cvs up -j 1.3.2.4 -j 1.3.2.7" Having this type of functionality (via stored merge meta-data) would tremendously improve the power of cvs when dealing with multiple branches and would give CVS a bit more umph when compared to perforce or other tools.. So much for the basics. Now Ill go into all the things that I have thought of (Im sure there are more) that will need to be addressed to make this happen. 1. We should reserve all ".*" tags to be CVS special (I think Jim K has already suggested this) 2. Changes in commit.c to cover the post commit tagging if there were any merges for the file in question. 3. Changes in status.c so that a default "cvs status -v" wont show all the .MH tags. Probably need to add another option that will show including the .MH tags. Should we also disallow references to all .MH tags via -r ??? 4. What should the thinking be on allowing users to add, move, modify, delete .MH tags on there own. Depending on how the group feels, modifications will need to made in tag.c. 5. The search for common ancestor routine would need to be written and added to update.c, also the routines for dealing with the CVS/Entries file in entries.c would need to be updated. 6. As an auxillary item, we will need to be able to refer the main trunk by a symbolic name. ie. We need to officially name the main trunk. Yes I know it might break things but I *really* think we should name it "main". I know stuff like ".MAIN" has been suggested before but lets just bite the bullet and do it right and figure out how to gracefully deal with folks that have already created a "main" branch. There are also some hidden issues with naming of the main branch as well. Most of the revolve around how to correctly deal with the vendor branch and its psuedo overlay of the main trunk etc. 7. Documentation!! Test Routines!! 8. Unless someone can think of a reason why (and figure out how to do the implementation), I dont think we should place any type of .MH* tag on fully selected merges. IE. If someone uses the "old" way to reference merges ala "cvs up -j 1.4.4.3 -j 1.4.4.6". Granted a super smart ancestor detection algorithm coupled with a very intelligent post commit (with merge) algorithm could try to detect and condense multiple fully selected merges into a proper ancestor based tag. But Ill be honest, I dont know of a way to do it and wouldnt want to be stuck writing the code for it... The corner cases Im concerned about: 1. People using cvs admin to "outdate" a range of revisions. We can allow it, but we need to clean up and remove any any .MH tags that reference the outdated revisions. (Initially you might think its no big deal to leave them, but if someone outdates some revs, and then future commits recreate those revs the merging algorithm would have valid hyperlinks to revisions that were never involved in a merge. Not a good thing... ;-) 2. The following sequence of events cvs update (revision 1.6 of file foo.c is in your workspace) cvs up -j branch foo.c <> cvs up -p -r 1.6 > foo.c <> cvs commit The danger here is that the -j modified the CVS/Entries file, but when we changed our minds it never got removed from it and the commit *will* create a .MH tag even though it shouldnt. I have not come up with any graceful way to handle this, everything that has come to mind has been real ugly and still not fully bulletproof. We cant just say "Dont do that" because I have seen way to many developers use the "cvs up -p -r >" trick. Perhaps adding a new subcommand (or option to update) that would dump all local changes and revert to a new pure revision from the repository??? Yes, its a total sledgehammer approach... ;-( ----------------------------------------------------------------------- John Cavanaugh Hewlett-Packard Company Project Engineer 1400 Fountaingrove Pkwy EESof Division Santa Rosa, CA 95403-1799 Email: cavanaug@sr.hp.com Phone: 707-577-4780 707-577-3948 (Fax) ----------------------------------------------------------------------- Our doubts are traitors, And make us lose the good we oft might win, By fearing to attempt. -- William Sharespeare -----------------------------------------------------------------------