== Derivation == Derivation is a way to allow people to share recipes by allowing one person to build on the work of others without copying and so freezing the work at that point. It obviously brings risks associated with that, but it is up to users to manage those risks and only derive from recipes that will suit them in future. Derivation allows you to specify another recipe to derive from, and then make the changes that you want to the recipe to tweak it for your needs. If you just want to use someone else's recipe that is fine, you only need to derive if you want to make small changes. '''NOTE: this is all just a proposal.''' == Use Cases == * Anne wants to set up a daily build of a project with her personal branch merged in. She wants to use the same packaging and just add her branch, and so she wants to receive the benefits of any changes made to the recipe she is based off. [ Probably more ] == Deriving == A new instruction will be added to the recipe format: {{{ derive NICK URI }}} this states that the recipe is derived from the one at `URI`, and that the other recipe is known as `NICK` within this one. This line replaces the base-branch statement at the start of the recipe, but the header remains in place. Currently there can only be one `derive` instruction per recipe. A derivation can be thought of as a textual include of the other recipe, with some substitutions done. == Appending == There can be the usual lines that appear in a recipe after the derive instruction. These instructions will be processed as if they are at the end of the parent recipe. A common use case would therefore be {{{ derive NICK URI merge NICK BRANCH }}} in order to merge your branch in to the tree created by the other recipe. == Altering == Often it will be desired to alter the recipe rather than just append to it. To do this there are three further instructions added. {{{ replace NICK .... }}} This replaces the line with NICK with the rest of the line given after NICK. NICK can be dotted notation to refer to the line in the parent recipe. E.g. if the recipe was derived from with the nick "parent", and contained a line with the nick "packaging" then you could replace that line using {{{ replace parent.packaging .... }}} The base branch of a recipe is given the implicit nick "base", so that it can be referred to. The second added instruction is "insert-after" {{{ insert-after NICK .... }}} For most lines this will act as if the line after NICK was placed on the line following the one it refers to. However, it it refers to a "nest" line then it will be placed after any lines that apply to the nested branch. In order to add an instruction that applies to a nested branch we also add {{{ insert-nested NICK .... }}} where NICK must refer to a "nest" line and places the line given after NICK indented as the first line after the "nest" line. If you do not want to be the first line after the "nest" then you can use "insert-after" to place the line where you like. Using these instructions to alter the recipe they are in is discouraged, as it is needlessly confusing. == Examples == Given this base recipe: {{{ lp:bzr nest configobj lp:configobj util/configobj merge fix-configobj lp:~bzr/configobj/fixes merge fix-build lp:~foo/bzr/fix-build merge packaging lp:ubuntu/bzr }}} then you could derive it in the following ways {{{ derive parent URL merge awesomeness lp:~me/bzr/awesome-new-feature }}} which would merge your awesome new feature in to the package. {{{ derive parent URL replace parent.fix-build merge better-build-fixes lp:~bar/bzr/really-fix-build }}} which would merge the really-fix-build branch rather than the fix-build branch. {{{ derive parent URL insert-after parent.configobj merge critical-bug lp:~bzr/bzr/critical-fix }}} which would merge critical-fix after the nest of configobj, but before fix-build. {{{ derive parent URL insert-after parent.configobj merge critical-bug lp:~bzr/bzr/critical-fix insert-after critical-bug merge another-fix lp:~bzr/bzr/another-fix }}} which shows merging in two branches in that position [is this correct namespacing?] {{{ derive parent URL insert-after parent.fix-configobj merge other-config-fixes lp:~bzr/bzr/more-config-objfixes }}} {{{ derive parent URL insert-nested parent.configobj merge other-config-fixes lp:~bzr/bzr/more-configobj-fixes }}} both of which merge the more-configobj-fixes branch in to the configobj nested branch, the difference being that the first merges it after the fix-configobj branch, and the latter before. == Error handling == Any of the new instructions fail if there target is not found. "derive" fails if the URI isn't found or can't be parsed as a recipe, and the other instructions fail if the nick they refer to can't be resolved. All of replace, insert-after and insert-nested are fairly sheltered from changes in the other file, failing only when the line they refer to is removed (or renamed, but that's the same thing). This is probably what we want, as the idea is to pick up these changes, and we have merge conflicts etc. to catch problems. If that isn't good enough then the other recipe should be copied, not derived from. == Questions == * Should we have a remove instruction? * Is the namespacing too much? It leaves space for multiple inheritance, but does such a thing make sense?