RFC: Proposal to combine diversions and Replaces into DEBIAN/replaces
recently on irc we discussed the problem of using Breaks + Replaces when
moving files from one package to another. The problem is that Replaces
is a one way street. Once the replacing package gets unpacked you can not
abort or later remove the the replacing package without loosing files in
the replaced package, which usualy breaks the replaced package.
The only real fix for this problem seems to be to preserve the replaced
files under another name and restore them when the replacing package is
removed. That starts to sound a lot like diversions doesn't it. The
only difference is that Replaces is usualy versioned.
On the other hand diversions have problems of their own. While the use
case is nearly always the same their actual use is verry fragile. One
has to get the preinst and postrm scripts just right and obsoleting a
diversion leaves cruft in the maintainer scripts for years to
come. Worse moving a diversion from one package to another is nearly
So here is my proposal:
1) dpkg-divert is deprecated for use in packages. It should only be used
for diversions by the local administrator. The functionality remains
though for backward compatibility.
2) the Replaces field is deprecated for moving files from one package to
another. Its use to replaces whole packages could remain (used with
Conflicts). The functionality remains though for backward compatibility.
3) A new file is introduced in the control.tar.gz called "replaces" that
takes over the job of diversions and file replacements.
Syntax of the "replaces" file:
The "replaces" file usualy contains one entry per line. Except that
lines starting with a single space are concatenated to the line before
with the space, but not the newline, removed. This is solely to support
filenames containing newlines.
Each entry has one of the following forms:
pkg (<< ver) /absolute/path
pkg (<= ver) /absolute/path
This means that the file </absolute/path> from the package <pkg> is to
be diverted or replaced. If a version is given the entry only applies if
<pkg> has version << 'ver' or <= 'ver'. Dpkg will rename the
</absolute/path> file from <pkg> to </absolute/path.dpkg-<pkg>> the same
way diversions function now.
The first form provides what dpkg-divert does now while the other two
provide what Replaces does.
To make replacing multiple files simpler the </absolute/path> could
contain wildcards like e.g. package foo-data containing
foo (<< 1.2-3) /usr/share/foo/*
Implementation wise dpkg will handle additions or removals to the
"replaces" file automatically. That means that if on upgrade a new entry
appears in "replaces" then dpkg will rename the existing file before
unpacking the new package. If an old-style diversion for this file
already exists by the same package that diversion is taken over by dpkg
and now automatically handled. If an entry is removed from "replaces"
on upgrade then dpkg will restore the replaced file after removing the
files from the old package.
Dpkg will also have to handle the case of multiple packages diverting
or replacing the same file. I see 2 possible reasons why this could
1) A file conflict is turned into an alternative
Say package A, B and C all contain /usr/bin/tool. To turn that into an
alternative each of these packages have to divert /usr/bin/tool from the
other too prior to the version with alternatives. That ensures that at
the time the alternative is created in postinst /usr/bin/tool will not
exist. It might be possible to use a virtual package name in the
"replaces" file to capture all packages providing /usr/bin/tool with a
single entry instead of listing each individually. [Which then adds the
exception that a package never diverts/repalces itself.]
2) A file is moved multiple times
Say a file moves from A to B and later from B to C. Then B will replace
the file from A and C will replace the same file from B. But dpkg will
know which is ment in each case and only the file from C is left with
the original name. The other 2 files will be names .dpkg-A and .dpkg-B
if they exist.
The idea is that no effort has to be taken in the maintainer script to
keep the diversions in line. Simply listing them in the "replaces" file
is all that is needed. Also converting from old style dpkg-divert to
the "replaces" file is simply done by removing all dpkg-divert calls and
listing the respective files in "replaces". All the dangers and
dificulties of using diversions right is eliminated.
Further replacing files becomes more specific preventing accidentally
replacing the wrong files and reversible. This would make the use of
Breaks + "replaces" file a safe operation unlike Breaks + Replaces is
now, not just when aborting an upgrade but even when downgrading.