summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--MANIFEST1
-rw-r--r--NEWS1
-rw-r--r--pod/urpme.8.pod5
-rw-r--r--pod/urpmi.8.pod4
-rw-r--r--pod/urpmi.files.5.pod7
-rw-r--r--pod/urpmq.8.pod4
-rw-r--r--t/01compile.t2
-rw-r--r--t/data/SPECS/orphans-1/a.spec13
-rw-r--r--t/data/SPECS/orphans-1/b.spec13
-rw-r--r--t/data/SPECS/orphans-1/c.spec14
-rw-r--r--t/data/SPECS/orphans-1/cc.spec13
-rw-r--r--t/data/SPECS/orphans-1/d.spec14
-rw-r--r--t/data/SPECS/orphans-1/dd.spec13
-rw-r--r--t/data/SPECS/orphans-1/e.spec14
-rw-r--r--t/data/SPECS/orphans-1/ee1.spec13
-rw-r--r--t/data/SPECS/orphans-1/f.spec14
-rw-r--r--t/data/SPECS/orphans-1/ff1.spec13
-rw-r--r--t/data/SPECS/orphans-1/g.spec14
-rw-r--r--t/data/SPECS/orphans-1/gg.spec13
-rw-r--r--t/data/SPECS/orphans-1/h.spec14
-rw-r--r--t/data/SPECS/orphans-1/hh.spec13
-rw-r--r--t/data/SPECS/orphans-1/l.spec14
-rw-r--r--t/data/SPECS/orphans-1/ll.spec14
-rw-r--r--t/data/SPECS/orphans-1/m.spec14
-rw-r--r--t/data/SPECS/orphans-1/mm.spec14
-rw-r--r--t/data/SPECS/orphans-1/n.spec14
-rw-r--r--t/data/SPECS/orphans-1/nn.spec14
-rw-r--r--t/data/SPECS/orphans-1/o.spec14
-rw-r--r--t/data/SPECS/orphans-1/oo1.spec14
-rw-r--r--t/data/SPECS/orphans-1/r.spec14
-rw-r--r--t/data/SPECS/orphans-1/req-a.spec14
-rw-r--r--t/data/SPECS/orphans-1/req-b.spec14
-rw-r--r--t/data/SPECS/orphans-1/req-c.spec14
-rw-r--r--t/data/SPECS/orphans-1/req-d.spec14
-rw-r--r--t/data/SPECS/orphans-1/req-e.spec14
-rw-r--r--t/data/SPECS/orphans-1/req-f.spec14
-rw-r--r--t/data/SPECS/orphans-1/req-g.spec14
-rw-r--r--t/data/SPECS/orphans-1/req-h.spec14
-rw-r--r--t/data/SPECS/orphans-1/req-l.spec14
-rw-r--r--t/data/SPECS/orphans-1/req-m.spec14
-rw-r--r--t/data/SPECS/orphans-1/req-n.spec14
-rw-r--r--t/data/SPECS/orphans-1/req-o.spec14
-rw-r--r--t/data/SPECS/orphans-1/req-r.spec14
-rw-r--r--t/data/SPECS/orphans-1/req-s.spec14
-rw-r--r--t/data/SPECS/orphans-1/req-t.spec14
-rw-r--r--t/data/SPECS/orphans-1/rr1.spec14
-rw-r--r--t/data/SPECS/orphans-1/rr2.spec14
-rw-r--r--t/data/SPECS/orphans-1/s.spec14
-rw-r--r--t/data/SPECS/orphans-1/ss1.spec14
-rw-r--r--t/data/SPECS/orphans-1/ss2.spec14
-rw-r--r--t/data/SPECS/orphans-1/t.spec14
-rw-r--r--t/data/SPECS/orphans-1/tt1.spec14
-rw-r--r--t/data/SPECS/orphans-2/a-2.spec13
-rw-r--r--t/data/SPECS/orphans-2/bb-2.spec15
-rw-r--r--t/data/SPECS/orphans-2/c-2.spec14
-rw-r--r--t/data/SPECS/orphans-2/d-2.spec13
-rw-r--r--t/data/SPECS/orphans-2/e-2.spec14
-rw-r--r--t/data/SPECS/orphans-2/ee2.spec13
-rw-r--r--t/data/SPECS/orphans-2/f-2.spec14
-rw-r--r--t/data/SPECS/orphans-2/ff2-2.spec14
-rw-r--r--t/data/SPECS/orphans-2/g-2.spec14
-rw-r--r--t/data/SPECS/orphans-2/gg-2.spec13
-rw-r--r--t/data/SPECS/orphans-2/h-2.spec13
-rw-r--r--t/data/SPECS/orphans-2/l-2.spec14
-rw-r--r--t/data/SPECS/orphans-2/m-2.spec14
-rw-r--r--t/data/SPECS/orphans-2/mm-2.spec14
-rw-r--r--t/data/SPECS/orphans-2/n-2.spec14
-rw-r--r--t/data/SPECS/orphans-2/nn-2.spec14
-rw-r--r--t/data/SPECS/orphans-2/o-2.spec14
-rw-r--r--t/data/SPECS/orphans-2/oo2-2.spec14
-rw-r--r--t/data/SPECS/orphans-2/r-2.spec13
-rw-r--r--t/data/SPECS/orphans-2/req-a-2.spec13
-rw-r--r--t/data/SPECS/orphans-2/req-b-2.spec13
-rw-r--r--t/data/SPECS/orphans-2/req-c-2.spec13
-rw-r--r--t/data/SPECS/orphans-2/req-d-2.spec13
-rw-r--r--t/data/SPECS/orphans-2/req-e-2.spec13
-rw-r--r--t/data/SPECS/orphans-2/req-f-2.spec13
-rw-r--r--t/data/SPECS/orphans-2/req-g-2.spec13
-rw-r--r--t/data/SPECS/orphans-2/req-h-2.spec13
-rw-r--r--t/data/SPECS/orphans-2/req-l-2.spec13
-rw-r--r--t/data/SPECS/orphans-2/req-m-2.spec13
-rw-r--r--t/data/SPECS/orphans-2/req-n-2.spec13
-rw-r--r--t/data/SPECS/orphans-2/req-o-2.spec13
-rw-r--r--t/data/SPECS/orphans-2/req-r-2.spec13
-rw-r--r--t/data/SPECS/orphans-2/req-s-2.spec13
-rw-r--r--t/data/SPECS/orphans-2/req-t-2.spec13
-rw-r--r--t/data/SPECS/orphans-2/s-2.spec14
-rw-r--r--t/data/SPECS/orphans-2/t-2.spec14
-rw-r--r--t/data/SPECS/orphans-2/tt2-2.spec14
-rw-r--r--t/superuser--orphans.t99
-rw-r--r--urpm.pm1
-rw-r--r--urpm/args.pm5
-rw-r--r--urpm/cfg.pm1
-rw-r--r--urpm/main_loop.pm4
-rw-r--r--urpm/orphans.pm341
-rw-r--r--urpme36
-rwxr-xr-xurpmi21
-rw-r--r--urpmi.bash-completion7
-rwxr-xr-xurpmq31
99 files changed, 1672 insertions, 18 deletions
diff --git a/MANIFEST b/MANIFEST
index 169aa459..59c663a5 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -321,6 +321,7 @@ urpm/md5sum.pm
urpm/media.pm
urpm/mirrors.pm
urpm/msg.pm
+urpm/orphans.pm
urpm/parallel.pm
urpm/parallel_ka_run.pm
urpm/parallel_ssh.pm
diff --git a/NEWS b/NEWS
index 56fb1256..25d3bcd4 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,5 @@
- all tools:
+ o handle "unrequested orphans" (similar to "deborphan")
o statedir files are now in /var/lib/urpmi/<medium-name>/
for eg: /var/lib/urpmi/synthesis.hdlist.<medium-name> is now
/var/lib/urpmi/<medium-name>/synthesis.hdlist.cz
diff --git a/pod/urpme.8.pod b/pod/urpme.8.pod
index 77d441c2..27cb5468 100644
--- a/pod/urpme.8.pod
+++ b/pod/urpme.8.pod
@@ -5,6 +5,7 @@ urpme - rpm deinstaller
=head1 SYNOPSIS
urpme [options] [package_name...]
+ urpme [options] --auto-orphans
=head1 DESCRIPTION
@@ -37,6 +38,10 @@ This is the same as B<--verbose>.
Removes packages non-interactively, without asking questions.
+=item B<--auto-orphans>
+
+Removes orphans.
+
=item B<--test>
Test deinstallation of packages but do not modify the system.
diff --git a/pod/urpmi.8.pod b/pod/urpmi.8.pod
index 408f7001..b80b81a5 100644
--- a/pod/urpmi.8.pod
+++ b/pod/urpmi.8.pod
@@ -107,6 +107,10 @@ Like B<--auto-select>, but also updates all relevant media before
selection of upgradeable packages is made. This avoids a previous call to
C<urpmi.update>.
+=item B<--auto-orphans>
+
+Remove all orphans without asking (see also C<urpme --auto-orphans>)
+
=item B<--no-md5sum>
Disable MD5SUM file checking when updating media.
diff --git a/pod/urpmi.files.5.pod b/pod/urpmi.files.5.pod
index 45baf3ba..26512a23 100644
--- a/pod/urpmi.files.5.pod
+++ b/pod/urpmi.files.5.pod
@@ -72,6 +72,13 @@ This file is handled by urpmi: when adding a media from an URL containing a
password, urpmi will remove the password from the URL written into urpmi.cfg
and write it in this file.
+=item I<< /var/lib/urpmi/installed-through-deps.list >>
+
+Contains the name of the packages that were selected indirectly, ie not requested by user
+(eg: libxxxN). It is used to detect orphans (try C<urpme --auto-orphans> and see).
+
+The format of the file is: one package name per line, or optionally "xxx (required by ...)"
+
=back
=head1 SEE ALSO
diff --git a/pod/urpmq.8.pod b/pod/urpmq.8.pod
index fd266802..959044cc 100644
--- a/pod/urpmq.8.pod
+++ b/pod/urpmq.8.pod
@@ -84,6 +84,10 @@ searching packages and resolving dependencies.
Select all packages that can be upgraded, according to already installed
packages and packages listed in various registered media.
+=item B<--auto-orphans>
+
+List orphans.
+
=item B<--no-suggests>
With this option, urpmq will not require "suggested" packages.
diff --git a/t/01compile.t b/t/01compile.t
index 03410fca..2b81d0c3 100644
--- a/t/01compile.t
+++ b/t/01compile.t
@@ -2,7 +2,7 @@
use strict;
use warnings;
-use Test::More tests => 25;
+use Test::More tests => 26;
for my $module (glob("urpm/*.pm")) {
$module =~ s,/,::,g;
diff --git a/t/data/SPECS/orphans-1/a.spec b/t/data/SPECS/orphans-1/a.spec
new file mode 100644
index 00000000..f75c94d2
--- /dev/null
+++ b/t/data/SPECS/orphans-1/a.spec
@@ -0,0 +1,13 @@
+Summary: x
+Name: a
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/b.spec b/t/data/SPECS/orphans-1/b.spec
new file mode 100644
index 00000000..337a7807
--- /dev/null
+++ b/t/data/SPECS/orphans-1/b.spec
@@ -0,0 +1,13 @@
+Summary: x
+Name: b
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/c.spec b/t/data/SPECS/orphans-1/c.spec
new file mode 100644
index 00000000..4bb4a950
--- /dev/null
+++ b/t/data/SPECS/orphans-1/c.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: c
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: cc
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/cc.spec b/t/data/SPECS/orphans-1/cc.spec
new file mode 100644
index 00000000..ee8387c0
--- /dev/null
+++ b/t/data/SPECS/orphans-1/cc.spec
@@ -0,0 +1,13 @@
+Summary: x
+Name: cc
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/d.spec b/t/data/SPECS/orphans-1/d.spec
new file mode 100644
index 00000000..53983ffb
--- /dev/null
+++ b/t/data/SPECS/orphans-1/d.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: d
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: dd
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/dd.spec b/t/data/SPECS/orphans-1/dd.spec
new file mode 100644
index 00000000..c89f2aeb
--- /dev/null
+++ b/t/data/SPECS/orphans-1/dd.spec
@@ -0,0 +1,13 @@
+Summary: x
+Name: dd
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/e.spec b/t/data/SPECS/orphans-1/e.spec
new file mode 100644
index 00000000..b8178f7d
--- /dev/null
+++ b/t/data/SPECS/orphans-1/e.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: e
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: ee1
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/ee1.spec b/t/data/SPECS/orphans-1/ee1.spec
new file mode 100644
index 00000000..889c0506
--- /dev/null
+++ b/t/data/SPECS/orphans-1/ee1.spec
@@ -0,0 +1,13 @@
+Summary: x
+Name: ee1
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/f.spec b/t/data/SPECS/orphans-1/f.spec
new file mode 100644
index 00000000..75f073b3
--- /dev/null
+++ b/t/data/SPECS/orphans-1/f.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: f
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: ff1
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/ff1.spec b/t/data/SPECS/orphans-1/ff1.spec
new file mode 100644
index 00000000..a078af5f
--- /dev/null
+++ b/t/data/SPECS/orphans-1/ff1.spec
@@ -0,0 +1,13 @@
+Summary: x
+Name: ff1
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/g.spec b/t/data/SPECS/orphans-1/g.spec
new file mode 100644
index 00000000..a626432d
--- /dev/null
+++ b/t/data/SPECS/orphans-1/g.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: g
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: gg = %version
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/gg.spec b/t/data/SPECS/orphans-1/gg.spec
new file mode 100644
index 00000000..7531870c
--- /dev/null
+++ b/t/data/SPECS/orphans-1/gg.spec
@@ -0,0 +1,13 @@
+Summary: x
+Name: gg
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/h.spec b/t/data/SPECS/orphans-1/h.spec
new file mode 100644
index 00000000..4f9b2682
--- /dev/null
+++ b/t/data/SPECS/orphans-1/h.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: h
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+Suggests: hh
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/hh.spec b/t/data/SPECS/orphans-1/hh.spec
new file mode 100644
index 00000000..7467d74d
--- /dev/null
+++ b/t/data/SPECS/orphans-1/hh.spec
@@ -0,0 +1,13 @@
+Summary: x
+Name: hh
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/l.spec b/t/data/SPECS/orphans-1/l.spec
new file mode 100644
index 00000000..b74f0fa8
--- /dev/null
+++ b/t/data/SPECS/orphans-1/l.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: l
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: ll
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/ll.spec b/t/data/SPECS/orphans-1/ll.spec
new file mode 100644
index 00000000..071650cc
--- /dev/null
+++ b/t/data/SPECS/orphans-1/ll.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: ll
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: l
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/m.spec b/t/data/SPECS/orphans-1/m.spec
new file mode 100644
index 00000000..4074abe4
--- /dev/null
+++ b/t/data/SPECS/orphans-1/m.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: m
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: mm = %version
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/mm.spec b/t/data/SPECS/orphans-1/mm.spec
new file mode 100644
index 00000000..3dc9d24e
--- /dev/null
+++ b/t/data/SPECS/orphans-1/mm.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: mm
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: m
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/n.spec b/t/data/SPECS/orphans-1/n.spec
new file mode 100644
index 00000000..af7fb8a5
--- /dev/null
+++ b/t/data/SPECS/orphans-1/n.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: n
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: nn = %version
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/nn.spec b/t/data/SPECS/orphans-1/nn.spec
new file mode 100644
index 00000000..e3c95c2b
--- /dev/null
+++ b/t/data/SPECS/orphans-1/nn.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: nn
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: n = %version
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/o.spec b/t/data/SPECS/orphans-1/o.spec
new file mode 100644
index 00000000..a85d038c
--- /dev/null
+++ b/t/data/SPECS/orphans-1/o.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: o
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: oo%version
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/oo1.spec b/t/data/SPECS/orphans-1/oo1.spec
new file mode 100644
index 00000000..319a05a4
--- /dev/null
+++ b/t/data/SPECS/orphans-1/oo1.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: oo1
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: o = %version
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/r.spec b/t/data/SPECS/orphans-1/r.spec
new file mode 100644
index 00000000..28feb112
--- /dev/null
+++ b/t/data/SPECS/orphans-1/r.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: r
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: rr
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/req-a.spec b/t/data/SPECS/orphans-1/req-a.spec
new file mode 100644
index 00000000..1939f59e
--- /dev/null
+++ b/t/data/SPECS/orphans-1/req-a.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: req-a
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: a
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/req-b.spec b/t/data/SPECS/orphans-1/req-b.spec
new file mode 100644
index 00000000..1abe81b8
--- /dev/null
+++ b/t/data/SPECS/orphans-1/req-b.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: req-b
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: b
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/req-c.spec b/t/data/SPECS/orphans-1/req-c.spec
new file mode 100644
index 00000000..9de29a7e
--- /dev/null
+++ b/t/data/SPECS/orphans-1/req-c.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: req-c
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: c
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/req-d.spec b/t/data/SPECS/orphans-1/req-d.spec
new file mode 100644
index 00000000..e18c4e39
--- /dev/null
+++ b/t/data/SPECS/orphans-1/req-d.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: req-d
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: d
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/req-e.spec b/t/data/SPECS/orphans-1/req-e.spec
new file mode 100644
index 00000000..eb279f06
--- /dev/null
+++ b/t/data/SPECS/orphans-1/req-e.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: req-e
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: e
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/req-f.spec b/t/data/SPECS/orphans-1/req-f.spec
new file mode 100644
index 00000000..0c92520b
--- /dev/null
+++ b/t/data/SPECS/orphans-1/req-f.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: req-f
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: f
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/req-g.spec b/t/data/SPECS/orphans-1/req-g.spec
new file mode 100644
index 00000000..8ead4246
--- /dev/null
+++ b/t/data/SPECS/orphans-1/req-g.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: req-g
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: g
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/req-h.spec b/t/data/SPECS/orphans-1/req-h.spec
new file mode 100644
index 00000000..5af41ab0
--- /dev/null
+++ b/t/data/SPECS/orphans-1/req-h.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: req-h
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: h
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/req-l.spec b/t/data/SPECS/orphans-1/req-l.spec
new file mode 100644
index 00000000..e5acbee7
--- /dev/null
+++ b/t/data/SPECS/orphans-1/req-l.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: req-l
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: l
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/req-m.spec b/t/data/SPECS/orphans-1/req-m.spec
new file mode 100644
index 00000000..3e225a18
--- /dev/null
+++ b/t/data/SPECS/orphans-1/req-m.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: req-m
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: m
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/req-n.spec b/t/data/SPECS/orphans-1/req-n.spec
new file mode 100644
index 00000000..8e6473bc
--- /dev/null
+++ b/t/data/SPECS/orphans-1/req-n.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: req-n
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: n
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/req-o.spec b/t/data/SPECS/orphans-1/req-o.spec
new file mode 100644
index 00000000..beb04be2
--- /dev/null
+++ b/t/data/SPECS/orphans-1/req-o.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: req-o
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: o
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/req-r.spec b/t/data/SPECS/orphans-1/req-r.spec
new file mode 100644
index 00000000..d05bae88
--- /dev/null
+++ b/t/data/SPECS/orphans-1/req-r.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: req-r
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: r
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/req-s.spec b/t/data/SPECS/orphans-1/req-s.spec
new file mode 100644
index 00000000..e85a3c35
--- /dev/null
+++ b/t/data/SPECS/orphans-1/req-s.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: req-s
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: s
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/req-t.spec b/t/data/SPECS/orphans-1/req-t.spec
new file mode 100644
index 00000000..70b8ce17
--- /dev/null
+++ b/t/data/SPECS/orphans-1/req-t.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: req-t
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: t
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/rr1.spec b/t/data/SPECS/orphans-1/rr1.spec
new file mode 100644
index 00000000..f89b79b0
--- /dev/null
+++ b/t/data/SPECS/orphans-1/rr1.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: rr1
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+Provides: rr
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/rr2.spec b/t/data/SPECS/orphans-1/rr2.spec
new file mode 100644
index 00000000..a2cf3057
--- /dev/null
+++ b/t/data/SPECS/orphans-1/rr2.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: rr2
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+Provides: rr
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/s.spec b/t/data/SPECS/orphans-1/s.spec
new file mode 100644
index 00000000..da80e9db
--- /dev/null
+++ b/t/data/SPECS/orphans-1/s.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: s
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: ss1 ss2
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/ss1.spec b/t/data/SPECS/orphans-1/ss1.spec
new file mode 100644
index 00000000..434dc73a
--- /dev/null
+++ b/t/data/SPECS/orphans-1/ss1.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: ss1
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+Provides: ss
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/ss2.spec b/t/data/SPECS/orphans-1/ss2.spec
new file mode 100644
index 00000000..a7a14f4e
--- /dev/null
+++ b/t/data/SPECS/orphans-1/ss2.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: ss2
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+Provides: ss
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/t.spec b/t/data/SPECS/orphans-1/t.spec
new file mode 100644
index 00000000..b511a836
--- /dev/null
+++ b/t/data/SPECS/orphans-1/t.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: t
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: tt >= %version
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-1/tt1.spec b/t/data/SPECS/orphans-1/tt1.spec
new file mode 100644
index 00000000..f4b8b35f
--- /dev/null
+++ b/t/data/SPECS/orphans-1/tt1.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: tt1
+Version: 1
+Release: 1
+License: x
+Group: x
+Url: x
+Provides: tt = %version
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-2/a-2.spec b/t/data/SPECS/orphans-2/a-2.spec
new file mode 100644
index 00000000..0cd2afc8
--- /dev/null
+++ b/t/data/SPECS/orphans-2/a-2.spec
@@ -0,0 +1,13 @@
+Summary: x
+Name: a
+Version: 2
+Release: 1
+License: x
+Group: x
+Url: x
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-2/bb-2.spec b/t/data/SPECS/orphans-2/bb-2.spec
new file mode 100644
index 00000000..0a57c173
--- /dev/null
+++ b/t/data/SPECS/orphans-2/bb-2.spec
@@ -0,0 +1,15 @@
+Summary: x
+Name: bb
+Version: 2
+Release: 1
+License: x
+Group: x
+Url: x
+Obsoletes: b < 2
+Provides: b = 2
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-2/c-2.spec b/t/data/SPECS/orphans-2/c-2.spec
new file mode 100644
index 00000000..55d29981
--- /dev/null
+++ b/t/data/SPECS/orphans-2/c-2.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: c
+Version: 2
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: cc
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-2/d-2.spec b/t/data/SPECS/orphans-2/d-2.spec
new file mode 100644
index 00000000..85bfbf5e
--- /dev/null
+++ b/t/data/SPECS/orphans-2/d-2.spec
@@ -0,0 +1,13 @@
+Summary: x
+Name: d
+Version: 2
+Release: 1
+License: x
+Group: x
+Url: x
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-2/e-2.spec b/t/data/SPECS/orphans-2/e-2.spec
new file mode 100644
index 00000000..538aa45c
--- /dev/null
+++ b/t/data/SPECS/orphans-2/e-2.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: e
+Version: 2
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: ee2
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-2/ee2.spec b/t/data/SPECS/orphans-2/ee2.spec
new file mode 100644
index 00000000..00a4cbf5
--- /dev/null
+++ b/t/data/SPECS/orphans-2/ee2.spec
@@ -0,0 +1,13 @@
+Summary: x
+Name: ee2
+Version: 2
+Release: 1
+License: x
+Group: x
+Url: x
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-2/f-2.spec b/t/data/SPECS/orphans-2/f-2.spec
new file mode 100644
index 00000000..01fccf8c
--- /dev/null
+++ b/t/data/SPECS/orphans-2/f-2.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: f
+Version: 2
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: ff2
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-2/ff2-2.spec b/t/data/SPECS/orphans-2/ff2-2.spec
new file mode 100644
index 00000000..deb7e536
--- /dev/null
+++ b/t/data/SPECS/orphans-2/ff2-2.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: ff2
+Version: 2
+Release: 1
+License: x
+Group: x
+Url: x
+Conflicts: ff1 < 2
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-2/g-2.spec b/t/data/SPECS/orphans-2/g-2.spec
new file mode 100644
index 00000000..d08c3677
--- /dev/null
+++ b/t/data/SPECS/orphans-2/g-2.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: g
+Version: 2
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: gg = %version
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-2/gg-2.spec b/t/data/SPECS/orphans-2/gg-2.spec
new file mode 100644
index 00000000..ccc6d4e7
--- /dev/null
+++ b/t/data/SPECS/orphans-2/gg-2.spec
@@ -0,0 +1,13 @@
+Summary: x
+Name: gg
+Version: 2
+Release: 1
+License: x
+Group: x
+Url: x
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-2/h-2.spec b/t/data/SPECS/orphans-2/h-2.spec
new file mode 100644
index 00000000..2bbcc00d
--- /dev/null
+++ b/t/data/SPECS/orphans-2/h-2.spec
@@ -0,0 +1,13 @@
+Summary: x
+Name: h
+Version: 2
+Release: 1
+License: x
+Group: x
+Url: x
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-2/l-2.spec b/t/data/SPECS/orphans-2/l-2.spec
new file mode 100644
index 00000000..f1a456f1
--- /dev/null
+++ b/t/data/SPECS/orphans-2/l-2.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: l
+Version: 2
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: ll
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-2/m-2.spec b/t/data/SPECS/orphans-2/m-2.spec
new file mode 100644
index 00000000..b3d7c233
--- /dev/null
+++ b/t/data/SPECS/orphans-2/m-2.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: m
+Version: 2
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: mm = %version
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-2/mm-2.spec b/t/data/SPECS/orphans-2/mm-2.spec
new file mode 100644
index 00000000..78c8e3ee
--- /dev/null
+++ b/t/data/SPECS/orphans-2/mm-2.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: mm
+Version: 2
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: m
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-2/n-2.spec b/t/data/SPECS/orphans-2/n-2.spec
new file mode 100644
index 00000000..438e8a2e
--- /dev/null
+++ b/t/data/SPECS/orphans-2/n-2.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: n
+Version: 2
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: nn = %version
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-2/nn-2.spec b/t/data/SPECS/orphans-2/nn-2.spec
new file mode 100644
index 00000000..193e2903
--- /dev/null
+++ b/t/data/SPECS/orphans-2/nn-2.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: nn
+Version: 2
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: n = %version
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-2/o-2.spec b/t/data/SPECS/orphans-2/o-2.spec
new file mode 100644
index 00000000..0451c55c
--- /dev/null
+++ b/t/data/SPECS/orphans-2/o-2.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: o
+Version: 2
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: oo%version
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-2/oo2-2.spec b/t/data/SPECS/orphans-2/oo2-2.spec
new file mode 100644
index 00000000..4a8568ed
--- /dev/null
+++ b/t/data/SPECS/orphans-2/oo2-2.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: oo2
+Version: 2
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: o = %version
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-2/r-2.spec b/t/data/SPECS/orphans-2/r-2.spec
new file mode 100644
index 00000000..ef6c3a75
--- /dev/null
+++ b/t/data/SPECS/orphans-2/r-2.spec
@@ -0,0 +1,13 @@
+Summary: x
+Name: r
+Version: 2
+Release: 1
+License: x
+Group: x
+Url: x
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-2/req-a-2.spec b/t/data/SPECS/orphans-2/req-a-2.spec
new file mode 100644
index 00000000..d463a2ad
--- /dev/null
+++ b/t/data/SPECS/orphans-2/req-a-2.spec
@@ -0,0 +1,13 @@
+Summary: x
+Name: req-a
+Version: 2
+Release: 1
+License: x
+Group: x
+Url: x
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-2/req-b-2.spec b/t/data/SPECS/orphans-2/req-b-2.spec
new file mode 100644
index 00000000..7d7ca3a7
--- /dev/null
+++ b/t/data/SPECS/orphans-2/req-b-2.spec
@@ -0,0 +1,13 @@
+Summary: x
+Name: req-b
+Version: 2
+Release: 1
+License: x
+Group: x
+Url: x
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-2/req-c-2.spec b/t/data/SPECS/orphans-2/req-c-2.spec
new file mode 100644
index 00000000..205f2eb6
--- /dev/null
+++ b/t/data/SPECS/orphans-2/req-c-2.spec
@@ -0,0 +1,13 @@
+Summary: x
+Name: req-c
+Version: 2
+Release: 1
+License: x
+Group: x
+Url: x
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-2/req-d-2.spec b/t/data/SPECS/orphans-2/req-d-2.spec
new file mode 100644
index 00000000..d2ef1ddb
--- /dev/null
+++ b/t/data/SPECS/orphans-2/req-d-2.spec
@@ -0,0 +1,13 @@
+Summary: x
+Name: req-d
+Version: 2
+Release: 1
+License: x
+Group: x
+Url: x
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-2/req-e-2.spec b/t/data/SPECS/orphans-2/req-e-2.spec
new file mode 100644
index 00000000..9053971c
--- /dev/null
+++ b/t/data/SPECS/orphans-2/req-e-2.spec
@@ -0,0 +1,13 @@
+Summary: x
+Name: req-e
+Version: 2
+Release: 1
+License: x
+Group: x
+Url: x
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-2/req-f-2.spec b/t/data/SPECS/orphans-2/req-f-2.spec
new file mode 100644
index 00000000..4ced265b
--- /dev/null
+++ b/t/data/SPECS/orphans-2/req-f-2.spec
@@ -0,0 +1,13 @@
+Summary: x
+Name: req-f
+Version: 2
+Release: 1
+License: x
+Group: x
+Url: x
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-2/req-g-2.spec b/t/data/SPECS/orphans-2/req-g-2.spec
new file mode 100644
index 00000000..9794dd1c
--- /dev/null
+++ b/t/data/SPECS/orphans-2/req-g-2.spec
@@ -0,0 +1,13 @@
+Summary: x
+Name: req-g
+Version: 2
+Release: 1
+License: x
+Group: x
+Url: x
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-2/req-h-2.spec b/t/data/SPECS/orphans-2/req-h-2.spec
new file mode 100644
index 00000000..145a7310
--- /dev/null
+++ b/t/data/SPECS/orphans-2/req-h-2.spec
@@ -0,0 +1,13 @@
+Summary: x
+Name: req-h
+Version: 2
+Release: 1
+License: x
+Group: x
+Url: x
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-2/req-l-2.spec b/t/data/SPECS/orphans-2/req-l-2.spec
new file mode 100644
index 00000000..8422178c
--- /dev/null
+++ b/t/data/SPECS/orphans-2/req-l-2.spec
@@ -0,0 +1,13 @@
+Summary: x
+Name: req-l
+Version: 2
+Release: 1
+License: x
+Group: x
+Url: x
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-2/req-m-2.spec b/t/data/SPECS/orphans-2/req-m-2.spec
new file mode 100644
index 00000000..21767f34
--- /dev/null
+++ b/t/data/SPECS/orphans-2/req-m-2.spec
@@ -0,0 +1,13 @@
+Summary: x
+Name: req-m
+Version: 2
+Release: 1
+License: x
+Group: x
+Url: x
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-2/req-n-2.spec b/t/data/SPECS/orphans-2/req-n-2.spec
new file mode 100644
index 00000000..abc0b8d0
--- /dev/null
+++ b/t/data/SPECS/orphans-2/req-n-2.spec
@@ -0,0 +1,13 @@
+Summary: x
+Name: req-n
+Version: 2
+Release: 1
+License: x
+Group: x
+Url: x
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-2/req-o-2.spec b/t/data/SPECS/orphans-2/req-o-2.spec
new file mode 100644
index 00000000..f7d463fb
--- /dev/null
+++ b/t/data/SPECS/orphans-2/req-o-2.spec
@@ -0,0 +1,13 @@
+Summary: x
+Name: req-o
+Version: 2
+Release: 1
+License: x
+Group: x
+Url: x
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-2/req-r-2.spec b/t/data/SPECS/orphans-2/req-r-2.spec
new file mode 100644
index 00000000..a3c52f82
--- /dev/null
+++ b/t/data/SPECS/orphans-2/req-r-2.spec
@@ -0,0 +1,13 @@
+Summary: x
+Name: req-r
+Version: 2
+Release: 1
+License: x
+Group: x
+Url: x
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-2/req-s-2.spec b/t/data/SPECS/orphans-2/req-s-2.spec
new file mode 100644
index 00000000..38164a06
--- /dev/null
+++ b/t/data/SPECS/orphans-2/req-s-2.spec
@@ -0,0 +1,13 @@
+Summary: x
+Name: req-s
+Version: 2
+Release: 1
+License: x
+Group: x
+Url: x
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-2/req-t-2.spec b/t/data/SPECS/orphans-2/req-t-2.spec
new file mode 100644
index 00000000..f008de2a
--- /dev/null
+++ b/t/data/SPECS/orphans-2/req-t-2.spec
@@ -0,0 +1,13 @@
+Summary: x
+Name: req-t
+Version: 2
+Release: 1
+License: x
+Group: x
+Url: x
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-2/s-2.spec b/t/data/SPECS/orphans-2/s-2.spec
new file mode 100644
index 00000000..5b4775e1
--- /dev/null
+++ b/t/data/SPECS/orphans-2/s-2.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: s
+Version: 2
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: ss
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-2/t-2.spec b/t/data/SPECS/orphans-2/t-2.spec
new file mode 100644
index 00000000..8d5801ae
--- /dev/null
+++ b/t/data/SPECS/orphans-2/t-2.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: t
+Version: 2
+Release: 1
+License: x
+Group: x
+Url: x
+Requires: tt >= %version
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/data/SPECS/orphans-2/tt2-2.spec b/t/data/SPECS/orphans-2/tt2-2.spec
new file mode 100644
index 00000000..30937923
--- /dev/null
+++ b/t/data/SPECS/orphans-2/tt2-2.spec
@@ -0,0 +1,14 @@
+Summary: x
+Name: tt2
+Version: 2
+Release: 1
+License: x
+Group: x
+Url: x
+Provides: tt = %version
+BuildRoot: %{_tmppath}/%{name}
+
+%description
+x
+
+%files
diff --git a/t/superuser--orphans.t b/t/superuser--orphans.t
new file mode 100644
index 00000000..d9369a36
--- /dev/null
+++ b/t/superuser--orphans.t
@@ -0,0 +1,99 @@
+#!/usr/bin/perl
+
+# a-1 upgrades to a-2
+# b-1 upgrades to bb-2 (via obsoletes)
+# c-1 (requires cc) upgrades to c-2 (requires cc)
+# d-1 (requires dd) upgrades to d-2
+# e-1 (requires ee1) upgrades to e-2 (requires ee2)
+# f-1 (requires ff1) upgrades to f-2 (requires ff2), ff2 conflicts with ff1
+# g-1 (requires gg = 1) upgrades to g-2 (requires gg = 2)
+# h-1 (suggests hh) upgrades to h-2
+#
+# l-1 upgrades to l-2, l requires ll and ll requires l
+# m-1 (requires mm = 1) upgrades to m-2 (requires mm = 2), mm requires m (circular dep)
+# n-1 (requires nn = 1) upgrades to n-2 (requires nn = 2), nn-1 requires n-1, nn-2 requires n-2 (circular dep)
+# o-1 (requires oo1) upgrades to o-2 (requires oo2), oo1 requires o = 1, oo2 requires o = 2 (circular dep)
+#
+# r-1 (requires rr) upgrades to r-2, rr1 provides rr, rr2 provides rr
+# s-1 (requires ss1 and ss2) upgrades to s-2 (requires ss), ss1-2 provides ss, ss2-2 provides ss
+# t-1 (requires tt >= 1) upgrades to t-2 (requires tt >= 2), tt1-1 provides tt = 1, tt1-2 provides tt = 2
+#
+# req-a requires a, req-b requires b...
+#
+use strict;
+use lib '.', 't';
+use helper;
+use urpm::util;
+use Test::More 'no_plan';
+
+need_root_and_prepare();
+
+
+my $name = 'orphans';
+urpmi_addmedia("$name-1 $::pwd/media/$name-1");
+urpmi_addmedia("$name-2 $::pwd/media/$name-2");
+
+# we want urpmi --auto-select to always check orphans (when not using --auto-orphans)
+set_urpmi_cfg_global_options({ 'nb-of-new-unrequested-pkgs-between-auto-select-orphans-check' => 0 });
+
+
+test_auto_select_both('a', 'a', 'a-2');
+test_auto_select_both('b', 'b', 'bb-2');
+test_auto_select_both('c', 'c cc', 'c-2 cc-1');
+test_auto_select_both('d', 'd dd', 'd-2', 'dd-1');
+test_auto_select_both('e', 'e ee1', 'e-2 ee2-2', 'ee1-1');
+test_auto_select_both('f', 'f ff1', 'f-2 ff2-2');
+test_auto_select_both('g', 'g gg', 'g-2 gg-2');
+test_auto_select_both('h', 'h hh', 'h-2', 'hh-1');
+test_auto_select_both('l', 'l ll', 'l-2 ll-1');
+test_auto_select_both('m', 'm mm', 'm-2 mm-2');
+test_auto_select_both('n', 'n nn', 'n-2 nn-2');
+test_auto_select_both('o', 'o oo1', 'o-2 oo2-2');
+test_auto_select_both('r', 'r rr2', 'r-2', 'rr2-1');
+test_auto_select_both('s', 's ss1 ss2', 's-2 ss1-1 ss2-1');
+test_auto_select_both('t', 't tt1', 't-2 tt2-2', 'tt1-1');
+
+test_auto_select(['r', 'rr1'], 'r rr1 rr2', 'r-2 rr1-1', 'rr2-1');
+#test_auto_select(['s ss1'], 's ss1 ss2', 's-2 ss1-1', 'ss2-1'); # this fails, but that's ok
+
+sub add_version1 { map { "$_-1-1" } split(' ', $_[0] || '') }
+sub add_version2 { map { "$_-2-1" } split(' ', $_[0] || '') }
+sub add_release { map { "$_-1" } split(' ', $_[0] || '') }
+sub add_version1_s { join(' ', add_version1(@_)) }
+sub add_version2_s { join(' ', add_version2(@_)) }
+sub add_release_s { join(' ', add_release(@_)) }
+
+sub test_auto_select_both {
+ my ($pkg, $wanted_v1, $wanted_v2, $orphans_v2) = @_;
+
+ $orphans_v2 ||= '';
+ test_auto_select([$pkg], $wanted_v1, $wanted_v2, $orphans_v2);
+ test_auto_select(["req-$pkg"], "req-$pkg $wanted_v1", "req-$pkg-2", "$wanted_v2 $orphans_v2");
+}
+
+sub test_auto_select {
+ my ($req_v1, $wanted_v1, $wanted_v2, $orphans_v2) = @_;
+ test_auto_select_raw_urpmq_urpme ($req_v1, add_version1_s($wanted_v1), add_release_s($wanted_v2), add_release_s($orphans_v2));
+ test_auto_select_raw_auto_orphans($req_v1, add_version1_s($wanted_v1), add_release_s($wanted_v2));
+}
+
+sub test_auto_select_raw_urpmq_urpme {
+ my ($req_v1, $wanted_v1, $wanted_v2, $orphans_v2) = @_;
+ print "# test_auto_select_raw_urpmq_urpme(@$req_v1, $wanted_v1, $wanted_v2)\n";
+ urpmi("--media $name-1 --auto $_") foreach @$req_v1;
+ check_installed_fullnames(split ' ', $wanted_v1);
+ urpmi("--media $name-2 --auto --auto-select");
+ check_installed_fullnames(split ' ', "$wanted_v2 $orphans_v2");
+ is(run_urpm_cmd('urpmq -r --auto-orphans'), join('', sort map { "$_\n" } split ' ', $orphans_v2));
+ urpme("--auto --auto-orphans");
+ check_installed_fullnames_and_remove(split ' ', $wanted_v2);
+}
+
+sub test_auto_select_raw_auto_orphans {
+ my ($req_v1, $wanted_v1, $wanted_v2) = @_;
+ print "# test_auto_select_raw_auto_orphans(@$req_v1, $wanted_v1, $wanted_v2)\n";
+ urpmi("--media $name-1 --auto $_") foreach @$req_v1;
+ check_installed_fullnames(split ' ', $wanted_v1);
+ urpmi("--media $name-2 --auto --auto-select --auto-orphans");
+ check_installed_fullnames_and_remove(split ' ', $wanted_v2);
+}
diff --git a/urpm.pm b/urpm.pm
index 151300cd..e40d44db 100644
--- a/urpm.pm
+++ b/urpm.pm
@@ -35,6 +35,7 @@ sub default_options {
'xml-info' => 'on-demand',
'max-round-robin-tries' => 5,
'days-between-mirrorlist-update' => 5,
+ 'nb-of-new-unrequested-pkgs-between-auto-select-orphans-check' => 10,
};
}
diff --git a/urpm/args.pm b/urpm/args.pm
index 54013793..5b98d03a 100644
--- a/urpm/args.pm
+++ b/urpm/args.pm
@@ -84,6 +84,7 @@ my %options_spec = (
'gui' => \$::gui,
'auto-select' => \$::auto_select,
'auto-update' => sub { $::auto_update = $::auto_select = 1 },
+ 'auto-orphans' => \$options{auto_orphans},
'no-remove|no-uninstall' => \$::no_remove,
'no-install|noinstall' => \$::no_install,
'keep!' => sub { $urpm->{options}{keep} = $_[1] },
@@ -409,12 +410,12 @@ foreach my $k ('allow-medium-change', 'auto', 'auto-select', 'force', 'expect-in
$options_spec{gurpmi2} = $options_spec{gurpmi};
foreach my $k ("help|h", "version", "no-locales", "test!", "force", "root=s", "use-distrib=s",
- 'repackage', 'noscripts', 'auto',
+ 'repackage', 'noscripts', 'auto', 'auto-orphans',
"parallel=s")
{
$options_spec{urpme}{$k} = $options_spec{urpmi}{$k};
}
-foreach my $k ("root=s", "nolock", "use-distrib=s", "skip=s", "prefer=s", "synthesis=s", 'suggests', 'no-suggests', 'allow-suggests')
+foreach my $k ("root=s", "nolock", "use-distrib=s", "skip=s", "prefer=s", "synthesis=s", 'suggests', 'no-suggests', 'allow-suggests', 'auto-orphans')
{
$options_spec{urpmq}{$k} = $options_spec{urpmi}{$k};
}
diff --git a/urpm/cfg.pm b/urpm/cfg.pm
index 53f610ee..f8b98ed6 100644
--- a/urpm/cfg.pm
+++ b/urpm/cfg.pm
@@ -151,6 +151,7 @@ sub load_config_raw {
|removable
|md5sum
|limit-rate
+ |nb-of-new-unrequested-pkgs-between-auto-select-orphans-check
|xml-info
|excludepath
|split-(?:level|length)
diff --git a/urpm/main_loop.pm b/urpm/main_loop.pm
index 3dec70c8..224a9909 100644
--- a/urpm/main_loop.pm
+++ b/urpm/main_loop.pm
@@ -26,6 +26,7 @@ use urpm::msg;
use urpm::install;
use urpm::media;
use urpm::select;
+use urpm::orphans;
use urpm::get_pkgs;
use urpm::signature;
use urpm::util qw(untaint difference2 intersection member partition);
@@ -209,6 +210,9 @@ foreach my $set (@{$state->{transaction} || []}) {
callback_trans => $callbacks->{trans},
callback_report_uninst => $callbacks->{callback_report_uninst},
);
+
+ urpm::orphans::add_unrequested($urpm, $state);
+
my @l = urpm::install::install($urpm,
$to_remove,
\%transaction_sources_install, \%transaction_sources,
diff --git a/urpm/orphans.pm b/urpm/orphans.pm
new file mode 100644
index 00000000..fa8bb28a
--- /dev/null
+++ b/urpm/orphans.pm
@@ -0,0 +1,341 @@
+package urpm::orphans;
+
+use urpm::util;
+use urpm::msg;
+use urpm;
+
+# $Id: select.pm 243120 2008-07-01 12:24:34Z pixel $
+
+my $fullname2name_re = qr/^(.*)-[^\-]*-[^\-]*\.[^\.\-]*$/;
+
+#- side-effects: none
+sub installed_packages_packed {
+ my ($urpm) = @_;
+
+ my $db = urpm::db_open_or_die_($urpm);
+ my @l;
+ $db->traverse(sub {
+ my ($pkg) = @_;
+ $pkg->pack_header;
+ push @l, $pkg;
+ });
+ \@l;
+}
+
+#- side-effects: none
+sub unrequested_list__file {
+ my ($urpm) = @_;
+ "$urpm->{root}/var/lib/urpmi/installed-through-deps.list";
+}
+#- side-effects: none
+sub unrequested_list {
+ my ($urpm) = @_;
+ +{ map {
+ chomp;
+ s/\s+\(.*\)$//;
+ $_ => 1;
+ } cat_(unrequested_list__file($urpm)) };
+}
+
+#- side-effects:
+#- + those of _installed_req_and_unreq_and_update_unrequested_list (<root>/var/lib/urpmi/installed-through-deps.list)
+sub _installed_req_and_unreq {
+ my ($urpm) = @_;
+ my ($req, $unreq, $_unrequested) = _installed_req_and_unreq_and_update_unrequested_list($urpm);
+ ($req, $unreq);
+}
+#- side-effects:
+#- + those of _installed_req_and_unreq_and_update_unrequested_list (<root>/var/lib/urpmi/installed-through-deps.list)
+sub _installed_and_unrequested_lists {
+ my ($urpm) = @_;
+ my ($pkgs, $pkgs2, $unrequested) = _installed_req_and_unreq_and_update_unrequested_list($urpm);
+ push @$pkgs, @$pkgs2;
+ ($pkgs, $unrequested);
+}
+#- side-effects: <root>/var/lib/urpmi/installed-through-deps.list
+sub _installed_req_and_unreq_and_update_unrequested_list {
+ my ($urpm) = @_;
+
+ my $pkgs = installed_packages_packed($urpm);
+
+ $urpm->{debug}("reading and cleaning " . unrequested_list__file($urpm)) if $urpm->{debug};
+ my $unrequested = unrequested_list($urpm);
+ my ($unreq, $req) = partition { $unrequested->{$_->name} } @$pkgs;
+
+ # update the list (to filter dups and now-removed-pkgs)
+ output_safe(unrequested_list__file($urpm),
+ join('', sort map { $_->name . "\n" } @$unreq),
+ ".old");
+
+ ($req, $unreq, $unrequested);
+}
+
+
+#- side-effects: none
+sub _selected_unrequested {
+ my ($urpm, $selected) = @_;
+
+ map {
+ if (my $from = $selected->{$_}{from}) {
+ ($urpm->{depslist}[$_]->name => "(required by " . $from->fullname . ")");
+ } elsif ($selected->{$_}{suggested}) {
+ ($urpm->{depslist}[$_]->name => "(suggested)");
+ } else {
+ ();
+ }
+ } keys %$selected;
+}
+#- side-effects: $o_unrequested_list
+sub _renamed_unrequested {
+ my ($urpm, $rejected, $o_unrequested_list) = @_;
+
+ my @obsoleted = grep { $rejected->{$_}{obsoleted} } keys %$rejected or return;
+
+ # we have to read the list to know if the old package was marked "unrequested"
+ my $current = $o_unrequested_list || unrequested_list($urpm);
+
+ my %l;
+ foreach my $fn (@obsoleted) {
+ my ($n) = $fn =~ $fullname2name_re;
+ $current->{$n} or next;
+
+ my ($new_fn) = keys %{$rejected->{$fn}{closure}};
+ my ($new_n) = $new_fn =~ $fullname2name_re;
+ $l{$new_n} = "(obsoletes $fn)";
+ }
+ %l;
+}
+sub _new_unrequested {
+ my ($urpm, $state) = @_;
+ (
+ _selected_unrequested($urpm, $state->{selected}),
+ _renamed_unrequested($urpm, $state->{rejected}),
+ );
+}
+#- side-effects: <root>/var/lib/urpmi/installed-through-deps.list
+sub add_unrequested {
+ my ($urpm, $state) = @_;
+
+ my %l = _new_unrequested($urpm, $state);
+ append_to_file(unrequested_list__file($urpm), join('', map { "$_\t\t$l{$_}\n" } keys %l));
+}
+
+#- we don't want to check orphans on every auto-select,
+#- doing it only after many packages have been added
+#-
+#- side-effects: none
+sub check_unrequested_orphans_after_auto_select {
+ my ($urpm) = @_;
+ my $f = unrequested_list__file($urpm);
+ my $nb_added = wc_l($f) - wc_l("$f.old");
+ $nb_added >= $urpm->{options}{'nb-of-new-unrequested-pkgs-between-auto-select-orphans-check'};
+}
+
+#- this function computes wether removing $toremove packages will create
+#- unrequested orphans.
+#-
+#- it does not return the new orphans since "whatsuggests" is not available,
+#- if it detects there are new orphans, _all_unrequested_orphans()
+#- must be used to have the list of the orphans
+#-
+#- side-effects: none
+sub unrequested_orphans_after_remove {
+ my ($urpm, $toremove) = @_;
+
+ my $db = urpm::db_open_or_die_($urpm);
+ my %toremove = map { $_ => 1 } @$toremove;
+ _unrequested_orphans_after_remove_once($urpm, $db, unrequested_list($urpm), \%toremove);
+}
+#- side-effects: none
+sub _unrequested_orphans_after_remove_once {
+ my ($urpm, $db, $unrequested, $toremove) = @_;
+
+ my @requires;
+ foreach my $fn (keys %$toremove) {
+ my ($n) = $fn =~ $fullname2name_re;
+
+ $db->traverse_tag('name', [ $n ], sub {
+ my ($p) = @_;
+ $p->fullname eq $fn or return;
+ push @requires, $p->requires;
+ });
+ }
+
+ foreach my $req (uniq(@requires)) {
+ $db->traverse_tag_find('whatprovides', $req, sub {
+ my ($p) = @_;
+ $toremove->{$p->fullname} and return; # already done
+ $unrequested->{$p->name} or return;
+ $p->provides_overlap($req) or return;
+
+ # cool we have a potential "unrequested" package newly unneeded
+ if (_check_potential_unrequested_package_newly_unneeded($urpm, $db, $toremove, $p)) {
+ $urpm->{debug}("installed " . $p->fullname . " can now be removed") if $urpm->{debug};
+ return 1;
+ } else {
+ $urpm->{debug}("installed " . $p->fullname . " can not be removed") if $urpm->{debug};
+ }
+ 0;
+ }) and return 1;
+ }
+ 0;
+}
+#- side-effects: none
+sub _check_potential_unrequested_package_newly_unneeded {
+ my ($urpm, $db, $toremove, $pkg) = @_;
+
+ my $required_maybe_loop;
+
+ foreach my $prop ($pkg->provides) {
+ _check_potential_unrequested_provide_newly_unneeded($urpm, $db, $toremove,
+ scalar($pkg->fullname), $prop, \$required_maybe_loop)
+ and return;
+ }
+
+ if ($required_maybe_loop) {
+ my ($fullname, @provides) = @$required_maybe_loop;
+ $urpm->{debug}("checking wether $fullname is a depency loop") if $urpm->{debug};
+
+ # doing it locally, since we may fail (and so we must backtrack this change)
+ my %ignore = %$toremove;
+ $ignore{$pkg->fullname} = 1;
+
+ foreach my $prop (@provides) {
+ _check_potential_unrequested_provide_newly_unneeded($urpm, $db, \%ignore,
+ $fullname, $prop, \$required_maybe_loop)
+ and return;
+ }
+ }
+ 1;
+}
+#- side-effects: none
+sub _check_potential_unrequested_provide_newly_unneeded {
+ my ($urpm, $db, $toremove, $fullname, $prop, $required_maybe_loop) = @_;
+
+ my ($prov, $range) = URPM::property2name_range($prop) or return;
+
+ $db->traverse_tag_find('whatrequires', $prov, sub {
+ my ($p2) = @_;
+ $toremove->{$p2->fullname} and return 0; # this one is going to be removed, skip it
+
+ foreach ($p2->requires) {
+ my ($pn, $ps) = URPM::property2name_range($_) or next;
+ if ($pn eq $prov && URPM::ranges_overlap($ps, $range)) {
+ if ($$required_maybe_loop) {
+ $urpm->{debug}(" installed " . $p2->fullname . " still requires " . $fullname) if $urpm->{debug};
+ return 1;
+ }
+ $urpm->{debug}(" installed " . $p2->fullname . " may still requires " . $fullname) if $urpm->{debug};
+ $$required_maybe_loop = [ scalar $p2->fullname, $p2->provides ];
+ }
+ }
+ 0;
+ });
+}
+
+#- returns the list of "unrequested" orphans.
+#-
+#- side-effects: none
+sub _all_unrequested_orphans {
+ my ($req, $unreq) = @_;
+
+ my (%l, %provides);
+ foreach my $pkg (@$unreq) {
+ $l{$pkg->name} = $pkg;
+ push @{$provides{$_}}, $pkg foreach $pkg->provides_nosense;
+ }
+
+ while (my $pkg = shift @$req) {
+ foreach my $prop ($pkg->requires) {
+ my $n = URPM::property2name($prop);
+ foreach my $p (@{$provides{$n} || []}) {
+ if ($p != $pkg && $l{$p->name} && $p->provides_overlap($prop)) {
+ delete $l{$p->name};
+ push @$req, $p;
+ }
+ }
+ }
+ }
+
+ [ values %l ];
+}
+
+
+#- side-effects: $state->{orphans_to_remove}
+#- + those of _installed_and_unrequested_lists (<root>/var/lib/urpmi/installed-through-deps.list)
+sub compute_future_unrequested_orphans {
+ my ($urpm, $state) = @_;
+
+ $urpm->{log}("computing unrequested orphans");
+
+ my ($current_pkgs, $unrequested) = _installed_and_unrequested_lists($urpm);
+
+ put_in_hash($unrequested, { _new_unrequested($urpm, $state) });
+
+ my %toremove = map { $_ => 1 } URPM::removed_or_obsoleted_packages($state);
+ my @pkgs = grep { !$toremove{$_->fullname} } @$current_pkgs;
+ push @pkgs, map { $urpm->{depslist}[$_] } keys %{$state->{selected} || {}};
+
+ my ($unreq, $req) = partition { $unrequested->{$_->name} } @pkgs;
+
+ $state->{orphans_to_remove} = _all_unrequested_orphans($req, $unreq);
+
+ # nb: $state->{orphans_to_remove} is used when computing ->selected_size
+}
+
+#- it is quite fast. the slow part is the creation of $installed_packages_packed
+#- (using installed_packages_packed())
+#
+#- side-effects:
+#- + those of _installed_req_and_unreq (<root>/var/lib/urpmi/installed-through-deps.list)
+sub get_orphans {
+ my ($urpm) = @_;
+
+ $urpm->{log}("computing unrequested orphans");
+
+ my ($req, $unreq) = _installed_req_and_unreq($urpm);
+ _all_unrequested_orphans($req, $unreq);
+}
+sub get_now_orphans_msg {
+ my ($urpm) = @_;
+
+ my $orphans = get_orphans($urpm);
+ my @orphans = map { scalar $_->fullname } @$orphans or return '';
+
+ P("The following package is now orphan, use \"urpme --auto-orphans\" to remove it.",
+ "The following packages are now orphans, use \"urpme --auto-orphans\" to remove them.", scalar(@orphans))
+ . "\n" . add_leading_spaces(join("\n", @orphans) . "\n");
+}
+
+#- side-effects: none
+sub add_leading_spaces {
+ my ($s) = @_;
+ $s =~ s/^/ /gm;
+ $s;
+}
+
+#- side-effects: none
+sub installed_leaves {
+ my ($urpm, $o_discard) = @_;
+
+ my $packages = installed_packages_packed($urpm);
+
+ my (%l, %provides);
+ foreach my $pkg (@$packages) {
+ next if $o_discard && $o_discard->($pkg);
+ $l{$pkg->name} = $pkg;
+ push @{$provides{$_}}, $pkg foreach $pkg->provides_nosense;
+ }
+
+ foreach my $pkg (@$packages) {
+ foreach my $prop ($pkg->requires) {
+ my $n = URPM::property2name($prop);
+ foreach my $p (@{$provides{$n} || []}) {
+ $p != $pkg && $p->provides_overlap($prop) and
+ delete $l{$p->name};
+ }
+ }
+ }
+
+ [ values %l ];
+}
diff --git a/urpme b/urpme
index 1e559d59..a0121935 100644
--- a/urpme
+++ b/urpme
@@ -26,6 +26,7 @@ use urpm::msg;
use urpm::install;
use urpm::media;
use urpm::select;
+use urpm::orphans;
$ENV{PATH} = "/sbin:/usr/sbin:/bin:/usr/bin:/usr/X11R6/bin";
@@ -44,6 +45,7 @@ This is free software and may be redistributed under the terms of the GNU GPL.
usage:
", $urpm::VERSION) . N(" --help - print this help message.
") . N(" --auto - automatically select a package in choices.
+") . N(" --auto-orphans - remove orphans
") . N(" --test - verify if the removal can be achieved correctly.
") . N(" --force - force invocation even if some packages do not exist.
") . N(" --parallel - distributed urpmi across machines of alias.
@@ -62,7 +64,7 @@ usage:
my @origARGV = @ARGV;
my $urpm = urpm->new_parse_cmdline or exit(1);
my @cmdline_pkgs_to_remove = @ARGV;
-@cmdline_pkgs_to_remove || $options{matches} or usage();
+@cmdline_pkgs_to_remove || $options{matches} || $options{auto_orphans} or usage();
my $state = {};
@@ -89,7 +91,9 @@ urpm::media::configure($urpm,
);
#- examine packages...
-my @toremove = urpm::select::find_packages_to_remove(
+my @toremove;
+if (@cmdline_pkgs_to_remove || $options{matches}) {
+ @toremove = urpm::select::find_packages_to_remove(
$urpm,
$state,
\@cmdline_pkgs_to_remove,
@@ -118,13 +122,35 @@ my @toremove = urpm::select::find_packages_to_remove(
0;
},
) or $urpm->{fatal}(0, N("Nothing to remove"));
+}
+
+my $may_be_orphans = 1;
+if (@toremove && !$urpm->{options}{auto}) {
+ urpm::orphans::unrequested_orphans_after_remove($urpm, \@toremove)
+ or $may_be_orphans = 0;
+}
+
+my @toremove_no_orphans = @toremove;
+my @orphans;
+if ($options{auto_orphans} && $may_be_orphans) {
+ urpm::orphans::compute_future_unrequested_orphans($urpm, $state);
+ @orphans = map { scalar $_->fullname } @{$state->{orphans_to_remove}};
+
+ push @toremove, @orphans;
+ if (!@toremove) {
+ print N("No orphans to remove"), "\n";
+ exit 0;
+ }
+}
my $msg =
P("To satisfy dependencies, the following package will be removed",
"To satisfy dependencies, the following %d packages will be removed",
scalar(@toremove), scalar(@toremove))
. sprintf(" (%s)", formatXiB(-$urpm->selected_size($state))) . ":\n"
- . add_leading_spaces(urpm::select::translate_why_removed($urpm, $state, @toremove)) . "\n";
+ . add_leading_spaces(urpm::select::translate_why_removed($urpm, $state, @toremove_no_orphans)) . "\n"
+ . (@orphans ? P("(orphan package)", "(orphan packages)", scalar(@orphans)) . "\n" .
+ add_leading_spaces(join("\n", @orphans) . "\n") : ());
if ($urpm->{options}{auto}) {
$test and print STDOUT $msg;
@@ -148,6 +174,10 @@ my @errors = $parallel
if (@errors) {
#- Warning : the following message is parsed in urpm::parallel_*
$urpm->{fatal}(2, N("Removal failed") . ":\n" . join("\n", map { "\t$_" } @errors));
+} elsif ($may_be_orphans && !$options{auto_orphans} && !$test) {
+ if (my $msg = urpm::orphans::get_now_orphans_msg($urpm)) {
+ print "\n", $msg;
+ }
}
sub add_leading_spaces {
diff --git a/urpmi b/urpmi
index b79bf901..23e0b811 100755
--- a/urpmi
+++ b/urpmi
@@ -83,6 +83,7 @@ usage:
") . N(" --auto-update - update media then upgrade the system.
") . N(" --no-md5sum - disable MD5SUM file checking.
") . N(" --force-key - force update of gpg key.
+") . N(" --auto-orphans - remove orphans without asking
") . N(" --no-suggests - do not auto select \"suggested\" packages.
") . N(" --no-uninstall - never ask to uninstall a package, abort the installation.
") . N(" --no-install - don't install packages (only download)
@@ -239,7 +240,7 @@ if ($restricted) {
#- force some options
foreach (qw(keep verify-rpm)) { $urpm->{options}{$_} = 1 }
#- forbid some other options
- urpm::error_restricted($urpm) if $urpm->{root} || $options{usedistrib} || $force || $env || $parallel || $options{synthesis} || $auto_update;
+ urpm::error_restricted($urpm) if $urpm->{root} || $options{usedistrib} || $force || $env || $parallel || $options{synthesis} || $auto_update || $options{auto_orphans};
foreach (qw(allow-nodeps allow-force curl-options rsync-options wget-options prozilla-options noscripts)) {
urpm::error_restricted($urpm) if $urpm->{options}{$_};
}
@@ -587,6 +588,16 @@ my @to_install = @{$urpm->{depslist}}[sort { $a <=> $b } keys %{$state->{selecte
}
}
+
+if (@to_install && $options{auto_orphans}) {
+ urpm::orphans::compute_future_unrequested_orphans($urpm, $state);
+ if (my @orphans = map { scalar $_->fullname } @{$state->{orphans_to_remove}}) {
+ print P("The following orphan package will be removed.",
+ "The following orphan packages will be removed.", scalar(@orphans))
+ . "\n" . urpm::orphans::add_leading_spaces(join("\n", @orphans) . "\n");
+ }
+}
+
foreach my $pkg (@to_install) {
#- reflect change in flag usage, now requested is set whatever a package is selected or not,
#- but required is always set (so a required but not requested is a pure dependency).
@@ -669,6 +680,14 @@ my $exit_code = urpm::main_loop::run($urpm, $state,
}
});
+if ($exit_code == 0 && $auto_select && !$options{auto_orphans}) {
+ if (urpm::orphans::check_unrequested_orphans_after_auto_select($urpm)) {
+ if (my $msg = urpm::orphans::get_now_orphans_msg($urpm)) {
+ print "\n", $msg;
+ }
+ }
+}
+
unless ($env || $options{nolock}) {
$urpmi_lock->unlock;
$rpm_lock->unlock if $rpm_lock;
diff --git a/urpmi.bash-completion b/urpmi.bash-completion
index 09852b97..d244d282 100644
--- a/urpmi.bash-completion
+++ b/urpmi.bash-completion
@@ -95,7 +95,7 @@ _urpmi()
# add dangereous option for everything else as rurpmi
if [[ ${COMP_WORDS[0]} != *rurpmi ]]; then
options="$options --root --use-distrib --env \
- --replacepkgs --justdb \
+ --auto-orphans --replacepkgs --justdb \
--allow-nodeps --allow-force --no-suggests --force \
--noscripts --auto-update --keep --nokeep \
--verify-rpm"
@@ -162,7 +162,8 @@ _urpmq()
# return list of available options
COMPREPLY=( $( compgen -W "$common_options -d -u -m -a -c -y -s -i -g -p \
-r -f -l --update --media --excludemedia \
- --sortmedia --synthesis --auto-select --no-suggests --fuzzy --keep --nokeep \
+ --sortmedia --synthesis --auto-select --auto-orphans \
+ --no-suggests --fuzzy --keep --nokeep \
--list --list-url --list-media --list-nodes --list-aliases \
--src --sources --provides --requires --summary --sourcerpm --force --parallel --wget --curl --prozilla \
--changelog --conflicts --proxy --proxy-user --env --dump-config \
@@ -188,7 +189,7 @@ _urpme()
if [[ "$cur" == -* ]]; then
# return list of available options
COMPREPLY=( $( compgen -W "$common_options -a --auto --test \
- --parallel --noscripts" -- $cur ) )
+ --auto-orphans --parallel --noscripts" -- $cur ) )
else
# return list of available packages
_rpm_installed_packages
diff --git a/urpmq b/urpmq
index 09f9d090..e6b46707 100755
--- a/urpmq
+++ b/urpmq
@@ -50,6 +50,7 @@ usage:
") . N(" --sortmedia - sort media according to substrings separated by comma.
") . N(" --synthesis - use the given synthesis instead of urpmi db.
") . N(" --auto-select - automatically select packages to upgrade the system.
+") . N(" --auto-orphans - list orphans
") . N(" --no-suggests - do not auto select \"suggested\" packages.
") . N(" --fuzzy - impose fuzzy search (same as -y).
") . N(" --keep - keep existing packages if possible, reject requested
@@ -137,6 +138,27 @@ $options{nodepslist} = $options{list_aliases}
|| $options{list_url};
$options{nolock} = 1 if $options{nodepslist};
+#- print sub for query.
+my $pkg_to_string = sub {
+ my ($pkg) = @_;
+ my $str = '';
+ $options{group} and $str .= $pkg->group . '/';
+ $str .= $pkg->name;
+ $options{version} and $str .= '-' . $pkg->version;
+ $options{release} and $str .= '-' . $pkg->release;
+ $options{arch} and $str .= '.' . $pkg->arch;
+ $str;
+};
+
+if ($options{auto_orphans}) {
+ !@names && !@src_names or $urpm->{fatal}(1, N("usage: \"urpmq --auto-orphans\" with no argument"));
+ $options{env} and $urpm->{fatal}(1, N("Can't use %s with %s", '--env', '--auto-orphans'));
+ require urpm::orphans;
+ my $orphans = urpm::orphans::get_orphans($urpm);
+ print "$_\n" foreach sort map { $pkg_to_string->($_) } @$orphans;
+ exit 0;
+}
+
if ($options{env}) {
print N("using specific environment on %s\n", $options{env});
#- setting new environment.
@@ -487,14 +509,7 @@ $rpm_lock and $rpm_lock->unlock;
#- print sub for query.
my $query_sub = sub {
my ($id) = @_;
- my $pkg = $urpm->{depslist}[$id];
- my $str = '';
- $options{group} and $str .= $pkg->group . '/';
- $str .= $pkg->name;
- $options{version} and $str .= '-' . $pkg->version;
- $options{release} and $str .= '-' . $pkg->release;
- $options{arch} and $str .= '.' . $pkg->arch;
- $str;
+ $pkg_to_string->($urpm->{depslist}[$id]);
};
my %hack_only_one;