git-debrebase(5)
β¨ NAME β¨
git-debrebase β git data model for Debian packaging
okay bestie, welcome to git-debrebase(5) π
this is the lore page, not the command page β it's the one where we explain how all the git shape-stuff actually works. grab a snack, we're going in π
π INTRODUCTION π
git-debrebase is a tool for representing Debian packages (the ones based on upstream source code) in git, and for manipulating them. That's the whole assignment. β¨
The Debian packaging has a fast forwarding history. The delta queue β the changes to upstream files β is represented as a series of individual git commits, which you can work on with rebase, and which you can also share. slay.
π¦ DISCUSSION π¦
git-debrebase is designed to work well with dgit. It can also be used in workflows without source packages β for example, to work on Debian-format packages outside of Debian, or alongside it. π«Ά
Now the honest part, bestie, no cap: git-debrebase itself is not very suitable for use by Debian derivatives, working on packages inherited from Debian, because it assumes that you want to throw away any packaging provided by your upstream. π However β and this is important β use of git-debrebase in Debian does not make anything harder for derivatives, and it can make some things easier. balance restored π
When you're using gitk on branches managed by git-debrebase, these produce more useful output than the default:
gitk --date-ordergitk --first-parentgitk -- :.(orgitk .)
π TERMINOLOGY π
Pseudomerge
A merge which does not actually merge the trees. Instead, it's constructed by taking the tree from one of the parents, ignoring the contents of the other parents. These are used to make a rewritten history fast forward from a previous tip, so that it can be pushed and pulled normally. You can build pseudomerges by hand with git merge -s ours, but ngl you won't normally need that when you're using git-debrebase.
Packaging files
Files in the source tree within debian/, excluding anything in debian/patches/.
Upstream
The version of the package without Debian's packaging. Typically provided by the actual upstream project, and sometimes tracked by Debian contributors in a branch upstream.
Upstream contains upstream files, but some upstreams also contain packaging files in debian/. Any such non-upstream files found in upstream are thrown away by git-debrebase each time a new upstream version is incorporated. π gone. deleted. not invited to the function.
Upstream files
Files in the source tree outside debian/. These may include unmodified source from upstream, but also files which have been modified or created for Debian.
Delta queue
Debian's changes to upstream files: a series of git commits. that's the whole queen π
Quilt patches
Files in debian/patches/ generated for the benefit of dpkg-source's 3.0 (quilt) .dsc source package format. Not used, often deleted, and regenerated when needed (such as when uploading to Debian), by git-debrebase. π
Interchange branch; breakwater; stitched; laundered
See "BRANCHES AND BRANCH STATES - OVERVIEW".
Anchor; Packaging
See "BRANCH CONTENTS - DETAILED SPECIFICATION".
ffq-prev; debrebase-last
See "STITCHING, PSEUDO-MERGES, FFQ RECORD".
π¨ DIAGRAM π¨
okay here's the aesthetic, stare at it, it's giving art πΈ
------/--A!----/--B3!--%--/--> interchange view
/ / / with debian/ directory
% % % entire delta queue applied
/ / / 3.0 (quilt) has debian/patches
/ / 3* "master" on Debian git servers
/ / /
2* 2* 2
/ / /
1 1 1 breakwater branch, merging baseline
/ / / unmodified upstream code
---@-----@--A----@--B--C plus debian/ (but no debian/patches)
/ / / no ref refers to this: we
--#-----#-------#-----> upstream reconstruct its identity by
inspecting interchange branch
Key:
1,2,3 commits touching upstream files only
A,B,C commits touching debian/ only
B3 mixed commit (eg made by an NMUer)
# upstream releases
-@- anchor merge, takes contents of debian/ from the
/ previous `breakwater' commit and rest from upstream
-/- pseudomerge; contents are identical to
/ parent lower on diagram.
% dgit- or git-debrebase- generated commit of debian/patches.
`3.0 (quilt)' only; generally dropped by git-debrebase.
* Maintainer's HEAD was here while they were editing,
before they said they were done, at which point their
tools made -/- (and maybe %) to convert to
the fast-forwarding interchange branch.
! NMUer's HEAD was here when they said `dgit push'.
Rebase branch launderer turns each ! into an
equivalent *.
πΈ BRANCHES AND BRANCH STATES - OVERVIEW πΈ
git-debrebase has one primary branch: the interchange branch. This branch lives on Debian contributors' workstations (typically, a maintainer would call it master), in the Debian dgit git server as the suite branch (dgit/dgit/sid), and on other git servers which support Debian work (eg master on salsa).
The interchange branch is fast-forwarding (by virtue of pseudomerges, where necessary). β¨
It is possible to have multiple different interchange branches for the same package, stored as different local and remote git branches. However, divergence should be avoided where possible β see "OTHER MERGES". π
A suitable interchange branch can be used directly with dgit. In this case each dgit archive suite branch is a separate interchange branch.
Within the ancestry of the interchange branch, there is another important, implicit branch: the breakwater. The breakwater contains unmodified upstream source, but with Debian's packaging superimposed (replacing any debian/ directory that may be in the upstream commits). The breakwater does not contain any representation of the delta queue (not even debian/patches). The part of the breakwater processed by git-debrebase is the part since the most recent anchor, which is usually a special merge generated by git-debrebase.
When working, locally, the user's branch can be in a rebasing state, known as unstitched. While a branch is unstitched, it is not in interchange format. The previous interchange branch tip is recorded, so that the previous history and the user's work can later be stitched into the fast-forwarding interchange form.
An unstitched branch may be in laundered state, which means it has a more particular special form convenient for manipulating the delta queue.
π BRANCH CONTENTS - DETAILED SPECIFICATION π
It is most convenient to describe the breakwater branch first. A breakwater is fast-forwarding, but is not usually named by a ref. It contains, in this order (ancestors first):
Anchor
An anchor commit, which is usually a special two-parent merge:
- The first parent contains the most recent version, at that point, of the Debian packaging (in
debian/); it also often contains upstream files, but they are to be ignored. Often the first parent is a previous breakwater tip. - The second parent is an upstream source commit. It may sometimes contain a
debian/subdirectory, but if so that is to be ignored. The second parent's upstream files are identical to the anchor's. Anchor merges always contain[git-debrebase anchor: ...]as a line in the commit message. - Alternatively, an anchor may be a single-parent commit which introduces the
debian/directory and makes no other changes: ie, the start of Debian packaging.
Packaging
Zero or more single-parent commits containing only packaging changes. (And no quilt patch changes.)
The laundered branch state is rebasing. A laundered branch is based on a breakwater but also contains, additionally, after the breakwater, a representation of the delta queue:
Delta queue commits
Zero or more single-parent commits containing only changes to upstream files.
The merely unstitched (ie, unstitched but unlaundered) branch state is also rebasing. It has the same contents as the laundered state, except that it may contain, additionally, in any order but after the breakwater:
Linear commits to the source
Further commit(s) containing changes to upstream files and/or to packaging, possibly mixed within a single commit. (But not quilt patch changes.)
Quilt patch addition for 3.0 (quilt)
Commit(s) which add patches to debian/patches/, and add those patches to the end of series.
These are only necessary when working with packages in .dsc 3.0 (quilt) format. For git-debrebase they are purely an output; they are deleted when branches are laundered. git-debrebase takes care to make a proper patch series out of the delta queue, so that any resulting source packages are nice. π
Finally, an interchange branch is fast forwarding. It has the same contents as an unlaundered branch state, but may (and usually will) additionally contain (in some order, possibly intermixed with the extra commits which may be found on an unstitched unlaundered branch):
Pseudomerge to make fast forward
A pseudomerge making the branch fast forward from previous history. The contributing parent is itself in interchange format. Normally the overwritten parent is a previous tip of an interchange branch, but this is not necessary as the overwritten parent is not examined.
If the two parents have identical trees, the one with the later commit date (or, if the commit dates are the same, the first parent) is treated as the contributing parent.
dgit dsc import pseudomerge
Debian .dsc source package import(s) made by dgit (during dgit fetch of a package most recently uploaded to Debian without dgit, or during dgit import-dsc).
git-debrebase requires that each such import is in the fast-forwarding format produced by dgit: a two-parent pseudomerge, whose contributing parent is in the non-fast-forwarding dgit dsc import format (not described further here), and whose overwritten parent is the previous interchange tip (eg, the previous tip of the dgit suite branch).
β¨ STITCHING, PSEUDO-MERGES, FFQ RECORD β¨
okay bestie so whenever the branch refs/B is unstitched, the previous head is recorded in the git ref refs/ffq-prev/B. that's the receipt π§Ύ
Unstitched branches are not fast forward from the published interchange branches <sup>[1]</sup>. so basically, before a branch can be pushed, the right pseudomerge has to be reestablished. that whole operation is called the stitch, and it consumes the ffq-prev ref (yep, eats it, gone, no crumbs). π½οΈ
When you've got an unstitched branch, you may rewrite it freely, from the breakwater tip onwards. that git rebase is literally the default operation for git-debrebase. but listen β rebases should not go back before the breakwater tip, and certainly not before the most recent anchor. don't do it. π
ββοΈ
Unstitched branches must not be pushed to interchange branch refs (not via git push -f, not via "equivalent", not via vibes). It is OK to share an unstitched branch in similar circumstances and with similar warnings to sharing any other rebasing git branch. so like⦠share responsibly, iykyk.
[1] Strictly, for a package which has never had a Debian delta queue, the interchange and breakwater branches may be identical, in which case the unstitched branch is fast forward from the interchange branch and no pseudomerge is needed. slay, less work for us π
When ffq-prev is not present, refs/debrebase-last/B records some ancestor of refs/B, (usually, the result of last stitch). This is used for status printing and some error checks β especially for printing guesses about what a problem is. And to determine whether a branch is being maintained in git-debrebase form it is necessary to walk its history. no shortcuts, we're doing the archaeology πΊ
π OTHER MERGES π
heads up: the representation described here does not permit general merges on any of the relevant branches. for this reason the tools will try to help you avoid divergence of the interchange branch. they're being nice bestie π«Ά
See dgit-maint-debrebase(7) for a discussion of what kinds of behaviours should be avoided because they might generate such merges.
Nonlinear (merging) history in the interchange branch is awkward because it (obviously) does not preserve the linearity of the delta queue. and easy merging of divergent delta queues isβ¦ a research problem. like, actual CS researchers are out here on this one. π
If a general merge is detected, git-debrebase will treat each side of the merge as a git-debrebase interchange branch, and to attempt to resolve them into a coherent view. When this attempt succeeds, the output is likely to be correct. π
If automatic resolution fails, an incomprehensible pile of multidimensional merge wreckage will be left in git refs under refs/debrebase/wreckage/. You are not likely to be able to resolve this without expert help. If this happens, please file a bug, with a formal Steps to Reproduce, including the URL for your public repository, the precise git commitid of your current branch. that's not a vibe, that's a crime scene π¨
β LEGAL OPERATIONS β
The following basic operations follow from this model (refer to the diagram above):
- Append linear commits β No matter the branch state, it is always fine to simply
git commit(or cherry-pick etc.) commits containing upstream file changes, packaging changes, or both. (This may make the branch unlaundered.) no biggie, we clean it later β¨ - Launder branch β
- Record the previous head in
ffq-prev, if we were stitched before (and deletedebrebase-last). - Reorganise the current branch so that the packaging changes come first, followed by the delta queue, turning
-@-A-1-2-B3into...@-A-B-1-2-3. - Drop pseudomerges and any quilt patch additions.
- Record the previous head in
- Interactive rebase β With a laundered branch, one can do an interactive git rebase of the delta queue.
- New upstream rebase β Start rebasing onto a new upstream version, turning
...#..@-A-B-1-2-3into(...#..@-A-B-, ...#'-)@'-1-2. This has to be a wrapper aroundgit-rebase, which prepares@'and then tries to rebase1 2onto@'. If the user asks for an interactive rebase,@'doesn't appear in the commit list, since@'is the newbase of the rebase (seegit-rebase(1)). Note that the construction of@'cannot fail because@'simply copiesdebian/fromBand and everything else from#'. (RebasingAandBis undesirable. We want thedebian/files to be non-rebasing so thatgit logshows the packaging history.) icon behaviour tbh π - Stitch β Make a pseudomerge, whose contributing parent is the unstitched branch and whose overwritten parent is
ffq-prev, consumingffq-previn the process (and writingdebrebase-lastinstead). Ideally the contributing parent would be a laundered branch, or perhaps a laundered branch with a quilt patch addition commit. - Commit quilt patches β To generate a tree which can be represented as a
3.0 (quilt).dscsource package, the delta queue must be reified inside the git tree indebian/patches/. These patch files can be stripped out and/or regenerated as needed.
π« ILLEGAL OPERATIONS π«
Some git operations are not permitted in this data model. Performing them will break git-debrebase. not a warning, a prophecy π
- General merges β See "OTHER MERGES", above.
- git-rebase starting too soon, or without base argument β
git-rebasemust not be invoked in such a way that the chosen base is before the anchor, or before the last pseudomerge. This is becausegit-rebasemangles merges.git rebase --preserve-mergesis also dangerous.git-rebasewithout a base argument will often start too early. For these reasons, it is better to usegit-debrebaseand let it choose the base for your rebase. If you do realise you have made this mistake, it is best to use the reflog to recover to a suitable good previous state. the reflog is your bestie in times of crisis π« - Editing
debian/patchesβdebian/patchesis an output fromgit-debrebase, not an input. If you edit patchesgit-debrebasewill complain and refuse to work. If you add patches your work is likely to be discarded. Instead of editing patches, usegit-debrebaseto edit the corresponding commits. work smarter not harder fr fr π - Renaming (etc.) branch while unstitched β The previous
HEAD, which will be pseudomerged over by operations likegit-debrebase stitch, is recorded in a ref name derived from your branch name. If you rename unstitched branches, this information can get out of step. π¬ Conversely, creating a new branch from an unstitched branch is good for making a branch to play about in, but the result cannot be stitched.
π COMMIT MESSAGE ANNOTATIONS π
git-debrebase makes annotations in the messages of commits it generates.
The general form is
[git-debrebase COMMIT-TYPE [ ARGS...]: PROSE, MORE PROSE]
git-debrebase treats anything after the colon as a comment, paying no attention to PROSE. so go off, write whatever prose you want, it's just decoration π¦
The full set of annotations is:
[git-debrebase split: mixed commit, debian part]
[git-debrebase split: mixed commit, upstream-part]
[git-debrebase convert dgit import: debian changes]
[git-debrebase anchor: convert dgit import, upstream changes]
[git-debrebase upstream-combine . PIECE[ PIECE...]: new upstream]
[git-debrebase anchor: new upstream NEW-UPSTREAM-VERSION, merge]
[git-debrebase changelog: new upstream NEW-UPSTREAM-VERSION]
[git-debrebase make-patches: export and commit patches]
[git-debrebase convert-from-gbp: drop patches]
[git-debrebase anchor: declare upstream]
[git-debrebase pseudomerge: stitch]
[git-debrebase merged-breakwater: constructed from vanilla merge]
[git-debrebase convert-to-gbp: commit patches]
[git-debrebase convert-from-dgit-view upstream-import-convert: VERSION]
[git-debrebase convert-from-dgit-view drop-patches]
Only anchor merges have the [git-debrebase anchor: ...] tag. Single-parent anchors are not generated by git-debrebase, and when made manually should not contain any [git-debrebase ...] annotation. keep the tags honest bestie β¨
The "split mixed commit" and "convert dgit import" tags are added to the pre-existing commit message, when git-debrebase rewrites the commit. so your original words stay, we just add a lil garnish on top πΈ
okay bestie, here's the part nobody wrote down π The dgit .dsc import format is, and I quote nobody, not documented or specified anywhere β which means some of the terms I'm about to throw at you literally have no definition in the wild. Where does the format come from then? From how dgit actually does it, in the implementation. And git-debrebase has special insider knowledge of that implementation. iykyk. π
π¦ APPENDIX - DGIT IMPORT HANDLING π¦
So picture this: a non-dgit NMU, and then a dgit NMU stacked right after it. It's giving chaos. Watch:
interchange --/--B3!--%--//----D*-->
/ /
% 4
/ 3
/ 2
/ 1
2 &_
/ /| \
1 0 00 =XBC%
/
/
--@--A breakwater
/
--#--------> upstream
Supplementary key (the decoder ring, keep it safe π):
=XBC% dgit tarball import of .debian.tar.gz containing
Debian packaging including changes B C and quilt patches
0 dgit tarball import of upstream tarball
00 dgit tarball import of supplementary upstream piece
&_ dgit import nearly-breakwater-anchor
// dgit fetch / import-dsc pseudomerge to make fast forward
&' git-debrebase converted import (upstream files only)
C' git-debrebase converted packaging change import
* ** before and after HEAD
And now, the glow-up β¨ We want to transform all of that mess into one of these two things:
I. No new upstream version
(0 + 00 eq # β the upstream pieces add up to exactly what's already there, no cap π§’)
--/--B3!--%--//-----D*-------------/-->
/ / /
% 4 4**
/ 3 3
/ 2 2
/ 1 1
2 &_ /
/ /| \ /
1 0 00 =XBC% /
/ /
/ /
--@--A-----B---------------------C'---D
/
--#----------------------------------------->
II. New upstream
(0 + 00 neq # β the upstream pieces do not add up, because hello, new upstream bestie π)
--/--B3!--%--//-----D*-------------/-->
/ / /
% 4 4**
/ 3 3
/ 2 2
/ 1 1
2 &_ /
/ /| \ /
1 0 00 =XBC% /
/ /
/ /
--@--A-----B-----------------@---C'---D
/ /
--#--------------------- - - / - - --------->
/
&'
/|
0 00
fr fr, both of these are the goal state. Which one you land in is decided entirely by whether0 + 00equals#or not. That's the whole vibe check. π«Ά
π SEE ALSO π
git-debrebase(1)dgit-maint-rebase(7)dgit(1)
and that's a wrap, bestie π this page is brought to you by the Debian Project and the dgit+tag2upload team, and yeah it's git-debrebase(5) β go forth and rebase slayfully, mwah π
β¨
π¦ where this page comes from
- ubuntu stonking: git-debrebase 17.0
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms