Lightweight versioned bug fix

I wonder. In Git and Mercurial you could checkout an old commit, fix the bug and then merge it to the branch tip. That way you can clearly see the bug fix history graph and the ancestry information could then be used to automatically propagate that fix to other branches.

In BK you should clone the repository first, perform all that operations there and push it back to the trunk. It looks somewhat awkward. BK can create local clone in RESYNC when it pulls from a remote and use that local clone to seemlessly merge it. Could something like that be used to simplify the bug fix workflow too?

Let me see if I can understand the question…

[[ hmm. In trying to explain what I do to fix a bug I ending up describing my normal workflow. I suspect your “looks somewhat awkward” is going to switch to “very awkward”. :wink: Perhaps this will be a good baseline to suggest where you want to put the “lightweight” fixes. ]]

In my experience, few changes are really as simple as you think at first. If you haven’t run the full process including testing on all platforms then you probably will break something.

Internally we typically have 2 main integration trees.

  • bugfix: Which contains the last release and any bugfixes that have accumulated since that release. Releases from here are usually a bk-X.Y.Z release.
  • dev: Which contains larger more risky changes that will take a while before we are ready. This will probably be the next bk-X.0 or bk-X.Y release.

I usually have clones of these on my local machine for fast access.
If a simple bugfix problem is reported in the current release my workflow is usually like this:

cd ~/bk
bk clone bugfix bugfix-fixname
cd bugfix-fixname
... create fix, run regressions, update documentation ...
bk citool    # create a new cset
  • That local clone will use hardlinks and so is reasonably fast. (0.6s on my machine)
  • It is true that I am working in a new directory and so I need to rebuild everything. We have tweaked the build so the expensive part (tcltk) is cached in /build/obj and I use ccache so a full build is about 20 seconds on my machine. In some environments being able to work in an already built tree is really important. And git’s branches can make this better.

Now at this point, I could push this fix to the integration tree, but that isn’t really how we tend to work. We have a peer review process.

Now this cset is on my local machine and it needs to be put in a unique repository on the remote machine. You could do that with ‘bk clone’, but that is pretty expensive with a slow net connection so you want to use ‘bk push’. But for push to work I need a matching baseline repository on the remote machine.

That looks something like this:
On remote machine:
bk clone -r$BASEREV /home/bk/bugfix /home/bk/wscott/bugfix-fixname

On local machine:
bk push bk://work/wscott/bugfix-fixname

(but I have a script that do this with a couple more tweaks)

Then I create an entry on our internal RTI review system and people make comments and push fixes. We also run “crankturns” on a collection of these RTIs on a pile of machines running different processors and operating systems. In the process I might make a series of fixups to my ‘fixname’ RTI and other people might also push fixups. In the process the history gets messy. Eventually, the RTI gets approved.

At that point I would usually do something like this (on my local machine):

cd bugfix-fixname
bk pull bk://work/wscott/bugfix-fixname   # fetch any updates from reviewers
bk pull                                   # fetch new stuff from tip
bk collapse -e@                           # collapse csets together
bk citool                                 # recreate cset and fix comments
bk push                                   # push to my local master
cd ../bugfix
bk push                                   # push to official tree

One change to this process we have in mind to to remove this overhead of having to maintain a remote copy of the cset on the integration tree. Just let the RTI system store the repository directly.

Yes, that totally make sense for an enterprise workflow. But if you are a single developer then it only unnecessary complicates things. Suppose that I’m a home developer who don’t use peer review and don’t publish all the dirty details. But I still want to benefit from revision control. So, I have two trees: development and bugfix. The bugfix tree is stable and I only do bug fixes there, then I merge it to the development. So, it really doesn’t make sense to create another tree to just make a single commit to bugfix. Yet, I want that a history graph would properly reflect which commit introduced the bug, so it could be the GCA to all other branches which could emerge from development. Having an unnamed local automatic branch is good for that simple workflow.

Yup. We don’t have that. You can commit at the tip of a repository. If you want to create a commit in the past then you need to create a clone with the desired cset as the tip.

However as a “home” developer, you are going to publish your changes shortly after you make them so it seems to me you are always going to have a repository locally that contains the cset you want to fix. The only time you end up with multiple repositories is when something takes so long another project comes up before you can finish. Then you have multiple repos to separate the different fixes.

[quote=“wscott, post:4, topic:148”]
Yup. We don’t have that. You can commit at the tip of a repository. If you want to create a commit in the past then you need to create a clone with the desired cset as the tip.[/quote]
Actually, you do. When BK is doing a pull it creates a local branch in RESYNC directory. I’m just looking for a way to do the same for checkout.

I’m not sure if I understood you correctly. I certainly want several branches when I want to separate unstable code. Also, I want to experiment with different crazy ideas and don’t want to clobber the main development branch until I’m sure that they worth it.

Yes, pull (and undo) use RESYNC as a staging area for changes in progress. In the case of pull, RESYNC is where the incoming patch is applied by takepatch. It is a partial copy of a repository and uses the remote repository’s file layout, so if you have a rename conflict sfiles in RESYNC will match the remote repository. The sfiles in RESYNC are also allowed to have 2 open tips. Then the resolve command fixes all the conflicts either automatically or interactively and then the changes in RESYNC are applied to the repository.

So RESYNC is a staging area. Yes it is possible to do what you want:
(commands totally off the top of my head, probably missing details)

  • copy a couple files into RESYNC
    mkdir RESYNC
    cat sfiles | bk sfio -om | bk --cd=RESYNC sfio -im
  • make your changes on a branch
    bk edit -r@CSET file
    bk ci -ycomments file
    cat list-of-tips | bk commit -ymycommit -
  • run resolve (this creates the merge cset as a side-effect)
    cd ..
    bk resolve -a

But consider you would be making changes without the context of any of the other files. You wouldn’t be able to test them. If you want to make a fix in an old version, don’t you need a copy of that version to create them?

Yes, I do. I’m lacking the knowledge of BK internals, so that’s why I’m asking about it here. When I’ll be more familiar with it I could try to make a script. I believe that it’d be a popular feature that worth considering.