feat(show): merge the installed record with the upstream candidate

N?? and `show PKG` now print the installed record in the package
manager's own key/value layout, after one line explaining the format:

    # lines starting with > come from the upstream candidate: a differing
    value, or a field the installed record lacks

After each field whose candidate value differs comes the candidate's
line prefixed with "> "; fields only the candidate has are appended the
same way; installed-only fields stay; identical fields print once.
Multi-line values (pacman Optional Deps, dnf Description, RFC-822
continuation lines) are compared as blocks and a differing candidate
block gets "> " on every line. A package that is not installed shows the
candidate alone ("# not installed: this is the upstream candidate").

Merged where the backend gives comparable records for both sides:
- pacman: -Qi vs -Si REPO/PKG, REPO = the first repository -Sl lists
- apt: dpkg-query -s (Status, Conffiles, the long description; richer
  than apt-cache show PKG=<installed>, which gives the archive record
  when that version is still listed) vs apt-cache show --no-all-versions;
  Description-xx pairs with Description and a one-line candidate
  Description equal to the installed first line counts as identical
  (the archives ship the long text in Translation files)
- dnf: info --installed vs info --upgrades (--available lists other
  architectures first and nothing for an up-to-date package); not
  installed: --available; dnf5's differently padded keys are re-padded
zypper keeps `zypper info --requires`: rpm -qi and zypper info share
almost no keys and zypper cannot print the installed record itself.
No candidate, or not exactly one record per side, keeps the old output.

Formats verified in containers and recorded in discovery/*.md. Unit
tests replay recorded pacman, apt, dnf4 and dnf5 records; the E2E suite
checks 2?? on su-dummy-b on all 12 distros.
This commit is contained in:
bryanpedini committed 2026-10-10 14:24:39 +02:00
1 parent 296c77e319
commit 875c8294bf
30 files changed
+857 -45

No files matched your search

+30
View File
@@ -585,3 +585,33 @@ no-such-pkg error: package 'no-such-pkg' was not found
* `pacman -Qi sh` answers for the provider (`bash`), like the description and `Required By` already do.
* Mapping: `Explicitly installed` -> explicit, `Installed as a dependency for another package` -> dependency, -Qi failing -> not installed, anything else (or no such line) -> unknown.
* Fixture: `su-dummy-b` is installed `--asdeps`, `a`/`c` explicitly; `e2e_reset` re-applies both after its `pacman -U`. No change needed.
## 14. Full info (`N??` / `show`): `-Qi` merged with `-Si REPO/PKG`
Verified 2026-10-10 on archlinux:latest (pacman 7.1.0), `COLUMNS=0 LC_ALL=C`: the fixture (`FIXTURE_ISOLATE=1 FIXTURE_ORPHAN=1 FIXTURE_SYNC=1`) and a real upgradable package after `pacman -Sy` (`glib2` 2.88.3-1 installed, core 2.90.1-1).
```
$ pacman -Qi su-dummy-b $ pacman -Si su-dummy/su-dummy-b
Name : su-dummy-b Repository : su-dummy
Version : 1.0-1 Name : su-dummy-b
Description : Skeptical-updater dummy ... Version : 2.0-1
Architecture : any Description : Skeptical-updater dummy ...
URL : https://example.invalid/... Architecture : any
Licenses : custom URL / Licenses / Groups / Provides / Depends On /
Groups / Provides / Depends On / Optional Deps Optional Deps / Conflicts With / Replaces
Required By : su-dummy-a Download Size : 388.00 B
Optional For : None Installed Size : 4.00 B
Conflicts With / Replaces Packager : su-fixture <...>
Installed Size : 4.00 B Build Date : Tue Nov 14 22:13:20 2023
Packager : su-fixture <...> Validated By : SHA-256 Sum
Build Date : Tue Nov 14 22:13:20 2023
Install Date : Sat Oct 10 11:55:39 2026
Install Reason : Installed as a dependency for another package
Install Script : No
Validated By : None
```
* Both are one record per target (blank line after it), every key padded to the same column (`:` at column 17 in both, C locale), so the keys pair one to one. `-Qi` only: `Required By`, `Optional For`, `Install Date`, `Install Reason`, `Install Script`. `-Si` only: `Repository`, `Download Size`. Both, usually different: `Version`, `Installed Size`, `Build Date`, `Validated By` (`None`/`Signature` installed, `SHA-256 Sum Signature` in the repo), `Depends On`/`Provides`/`Optional Deps` when they changed.
* Multi-line: only `Optional Deps` (continuation lines indented to the value column, one entry each - glib2: `dconf: ...` / `glib2-devel: ...` / `gvfs: ...`); with `COLUMNS=0` nothing else wraps (`Provides` of glib2, 6 entries, stays one line).
* The candidate is `-Si REPO/PKG` with REPO = the first repository `-Sl` lists PKG in (pacman's choice, the join `pacman_upgradable` uses); plain `-Si PKG` prints one record per repository that has PKG.
* Missing sides: `-Qi` of a package only in a sync database fails (not installed: the candidate alone); a local-only package has no `-Si` record (the plain `-Qi` stays).