Commit graph

51 commits

Author SHA1 Message Date
Caleb Connolly
185d8bcef5
meta: introduce a Cache decorator (MR 2252)
Generalise pmb.helpers.other.cache with a more python decorator.

The Cache decorator takes a list of function arguments to use as cache
keys, keyword args can be used to restrict caching so that it is skipped
entirely unless the attribute has a specific value.

For example, pmb.helpers.pmaports.get() has the decorator:
@Cache("pkgname", subpackages=True)

This means the return value will be cached only when subpackages is
True, otherwise it will always miss.

Signed-off-by: Caleb Connolly <caleb@postmarketos.org>
2024-06-23 12:38:39 +02:00
Caleb Connolly
133091248f
repo: use new config.mirrors (MR 2252)
Use the new config.mirrors section to handle repository URLs instead of
the old mirror_alpine / mirrors_postmarketos options. This let's us
add the systemd staging repo automatically when on the systemd staging
branch / systemd is enabled.

Signed-off-by: Caleb Connolly <caleb@postmarketos.org>
2024-06-23 12:38:39 +02:00
Caleb Connolly
866e5bcfab
core: add an Arch type (MR 2252)
Move pmb/parse/arch.py over to core and refactor it as an Arch type,
similar to how Chroot was done. Fix all the uses (that I can find) of
arch in the codebase that need adjusting.

The new Arch type is an Enum, making it clear what architectures can be
represented and making it much easier to reason about. Since we support
~5 (kinda) different representations of an Architecture (Alpine, Kernel,
target triple, platform, and QEMU), we now formalise that the Alpine
format is what we represent internally, with methods to convert to any
of the others as-needed.

Signed-off-by: Caleb Connolly <caleb@postmarketos.org>
2024-06-23 12:38:39 +02:00
Caleb Connolly
560cea46ea
treewide: migrate to pkgrepo API (MR 2252)
Make use of the new pmb.core.pkgrepo API to handle multiple aports and
extra-repos.

Signed-off-by: Caleb Connolly <caleb@postmarketos.org>
2024-06-23 12:38:39 +02:00
Caleb Connolly
de4c912692
WIP: 2024-06-05: args hacking and more (MR 2252)
Continue removing args and do some other optimisations.

Signed-off-by: Caleb Connolly <caleb@postmarketos.org>
2024-06-23 12:38:38 +02:00
Caleb Connolly
34dd9d42ba
WIP: start ripping out args (MR 2252)
Cease merging pmbootstrap.cfg into args, implement a Context type to let
us pull globals out of thin air (as an intermediate workaround) and rip
args out of a lot of the codebase.

This is just a first pass, after this we can split all the state that
leaked over into Context into types with narrower scopes (like a
BuildContext(), etc).

Signed-off-by: Caleb Connolly <caleb@postmarketos.org>
2024-06-23 12:38:38 +02:00
Caleb Connolly
05c86be11c
helpers: drop args from helpers.run functions (MR 2252)
Now we can run commands without needs args available!

Signed-off-by: Caleb Connolly <caleb@postmarketos.org>
2024-06-23 12:38:37 +02:00
Caleb Connolly
b82c4eb167
chroot: always run apk static (MR 2252)
Testing by building postmarketos-initramfs (which installs >100 packages
but is very fast to build, so a worst-case scenario) this results in a
~15-20% speedup (which everything cached and doing multiple back to back
runs). From 32 seconds down to 25.

Doing a full install with --no-image, this takes us from 70 seconds on
my laptop down to 40s!

This also lets us drastically simplify pmb/helpers/apk.py!

Signed-off-by: Caleb Connolly <caleb@postmarketos.org>
2024-06-23 12:38:37 +02:00
Caleb Connolly
31cc898dd5
treewide: adopt pathlib.Path and type hinting (MR 2252)
With the new chroot type, we can now write fancy paths in the pythonic
way. Convert most of the codebase over, as well as adding various other
type hints.

Signed-off-by: Caleb Connolly <caleb@postmarketos.org>
2024-06-23 12:38:37 +02:00
Caleb Connolly
71e7af57e6
pmb.helpers.logging: wrap logging module (MR 2252)
We use a custom verbose log level in pmbootstrap, unfortunately it isn't
possible to correctly type this due to some limitations in the logging
library [1], [2].

Given that our usecase is fairly simple, we can just wrap the module
with our own so we only have to tell mypy to ignore the error once
instead of at every callsite.

[1]: https://github.com/cryptax/droidlysis/issues/15
[2]: https://github.com/python/typing/discussions/980

Signed-off-by: Caleb Connolly <caleb@postmarketos.org>
2024-06-23 12:38:37 +02:00
Caleb Connolly
7b14ef597b
treewide: type hint args (MR 2252)
Signed-off-by: Caleb Connolly <caleb@postmarketos.org>
2024-06-23 12:38:37 +02:00
Caleb Connolly
198f302a36
treewide: add a Chroot type and adopt pathlib.Path (MR 2252)
Introduce a new module: pmb.core to contain explicitly typed pmbootstrap
API. The first component being Suffix and SuffixType. This explicitly
defines what suffixes are possible, future changes should aim to further
constrain this API (e.g. by validating against available device
codenames or architectures for buildroot suffixes).

Additionally, migrate the entire codebase over to using pathlib.Path.
This is a relatively new part of the Python standard library that uses a
more object oriented model for path handling. It also uses strong type
hinting and has other features that make it much cleaner and easier to
work with than pure f-strings. The Chroot class overloads the "/"
operator the same way the Path object does, allowing one to write paths
relative to a given chroot as:

builddir = chroot / "home/pmos/build"

The Chroot class also has a string representation ("native", or
"rootfs_valve-jupiter"), and a .path property for directly accessing the
absolute path (as a Path object).

The general idea here is to encapsulate common patterns into type hinted
code, and gradually reduce the amount of assumptions made around the
codebase so that future changes are easier to implement.

As the chroot suffixes are now part of the Chroot class, we also
implement validation for them, this encodes the rules on suffix naming
and will cause a runtime exception if a suffix doesn't follow the rules.
2024-06-23 12:38:37 +02:00
Robert Eckelmann
044d3b5a6a
pmb.*: various comment reformatting to assist with generating docs (MR 2266) 2024-05-14 14:36:22 +02:00
Oliver Smith
fdbb8eebb8
chroot: /mnt/pmbootstrap-* -> /mnt/pmbootstrap/*
Have one /mnt/pmbootstrap directory with subdirectories, instead of
several /mnt/pmbootstrap-* directories.

Reviewed-by: Pablo Correa Gómez <ablocorrea@hotmail.com>
Reviewed-by: Luca Weiss <luca@z3ntu.xyz>
Link: https://lists.sr.ht/~postmarketos/pmbootstrap-devel/%3C20230806184729.4891-2-ollieparanoid@postmarketos.org%3E
2023-08-14 09:52:17 +02:00
Oliver Smith
9975d373b0
Bump copyright to 2023 2023-01-22 19:18:06 +01:00
Oliver Smith
6f6a3b0408
Happy new year 2022! 2022-01-02 22:39:14 +01:00
BO41
ce0f1c2d4a
args.cache: remove (MR 2136)
Replace "args.cache" with a global variable in order to
avoid passing "args" to all functions. This is a step to get rid of this
args-passed-to-all-functions pattern in pmbootstrap.
2021-11-27 14:13:27 +01:00
bo41
caf7973e24
args.arch_native: remove (MR 2130)
Replace "args.arch_native" with the direct function call in order to
avoid passing "args" to all functions. This is a step to get rid of this
args-passed-to-all-functions pattern in pmbootstrap.
2021-10-24 14:34:30 +02:00
Newbyte
2d23849aa3
pmb, test: remove redundant commas (MR 2115) 2021-09-26 17:58:40 +02:00
Oliver Smith
1c791da482
treewide: bump copyright to 2021 2021-01-07 23:30:47 +01:00
Oliver Smith
a9d4ba32a2
cli: --details-to-stdout: no progress bars (MR 2007)
Hide progress bars if --details-to-stdout is used, which redirects all
output that would land in the pmbootstrap log to stdout. This caused the
progress bar output to get mixed with the apk output. A new progress bar
would get drawn whenever a new package was installed, without removing
the previous progress bar.
2020-12-16 23:20:46 +01:00
Johannes Marbach
e058841709
pmb.helpers.repo: Show progress bar when downloading APKINDEX files (MR 1996)
Run pmbootstrap update to test
2020-12-07 12:41:28 +01:00
Oliver Smith
7f60a6d782
chroot: put built packages into channel subdir (MR 1912)
Migrate to workdir version 5 and move already built packages into the edge
channel subdir, for example:
	$WORK/packages/x86_64/hello-world-1-r5.apk
to:
	$WORK/packages/edge/x86_64/hello-world-1-r5.apk

The build.postmarketos.org code has already been adjusted to find built
packages in either directory structure.
2020-05-17 08:08:45 +02:00
Oliver Smith
5a256f66de
pmb.helpers.pkgrel_bump.auto_apkindex_files: remove (MR 1912)
Replace the call to this function with the almost identical
pmb.helpers.repo.apkindex_files(), so release channel related changes
only need to be done in one place.
2020-05-17 08:08:45 +02:00
Oliver Smith
ce433ae012
pmb.helpers.repo.urls: use channels.cfg mirrordirs (MR 1912)
Use mirrordir_pmos and mirrordir_alpine from channels.cfg to generate
the mirror URLs for postmarketOS and Alpine, which get written to
/etc/apk/repositories and which postmarketOS uses to download the
APKINDEX files.

Remove hardcoded "master" at the end of the postmarketOS mirror and use
mirrordir_pmos instead (which is "master" for the edge channel). Let the
postmarketOS mirror end in a '/' for consistency with the Alpine mirror
in pmb/config/__init__.py.

Remove obsolete --alpine-version. To experiment with a different Alpine
version, one should pass a custom --config-channels from now on.
2020-05-17 08:08:45 +02:00
Oliver Smith
2fb98adf92
pmb.helpers.repo.apkindex_files: deduplicate pmos urls (MR 1912)
Don't generate the postmarketOS mirror URLs here, let the urls()
function do it. A follow-up commit will touch this code, hence it's
important to have it de-duplicated.
2020-05-17 08:08:45 +02:00
Oliver Smith
2f018c44ef
pmb.helpers.{package,pmaports,repo}: fix comments (MR 1928)
Make the comments on top of the three files less confusing and more
consistent.
2020-04-25 13:17:21 +03:00
Oliver Smith
f21c216a26
Cosmetic: use SPDX license header (!1877)
While at it, also remove unnecessary "#!/usr/bin/env python3" in files
that only get imported, and adjust other empty/comment lines in the
beginnings of the files for consistency.

This makes files easier to read, and makes the pmbootstrap codebase more
consistent with the build.postmarketos.org codebase.
2020-02-24 03:11:10 +03:00
Oliver Smith
948e3f931f
Change copyright to 2020 2020-01-06 02:43:00 +01:00
Oliver Smith
d7ec81a00e repo.py: fix downloading too many APKINDEX files (!1764)
In pmb.helpers.repo.alpine_apkindex_path(), default to
arch = args.arch_native *before* calling pmb.helpers.repo.update().
Because otherwise update() defaults to all arches instead of
args.arch_native.

This caused the APKINDEX files for all arches to get downloaded, as
pmb.chroot.apk_static.init() calls alpine_apkindex_path() without
setting arch = args.arch_native explicitly.
2019-03-02 16:28:39 +01:00
Oliver Smith
f16bdaf0ca
Update copyright to 2019
Happy new year \o/
2019-01-02 09:31:20 +01:00
Oliver Smith
5102d48064 Make APKINDEX downloads returning 404 non-fatal (!1726)
Do not fail when an APKINDEX can not be downloaded, print a WARNING
instead. This matches, what apk does. Add a new allow_404 parameter to
pmb.helpers.http.download(), and use it in pmb.helpers.repo.update()
for downloading APKINDEX files. Cache the APKINDEX URLs that gave a 404
for the session, so we do not attempt to download these again.

This is needed for the new binary repository: the initial build is done
without existing APKINDEX files, so we must not fail in that case.
2018-12-11 06:56:53 +00:00
Oliver Smith
fa7860c8f6 pmb.helpers.repo.update: refactor cache code (!1726)
Change the cache format from args.cache["offline_msg_shown"] to
args.cache["pmb.helpers.repo.update"]["offline_msg_shown"]. This is in
preparation for saving more data in the cache of
pmb.helpers.repo.update in the next commit.
2018-12-11 06:56:53 +00:00
Oliver Smith
49212ba317 Cosmetic: pmb.helpers.repo.update(): fix comment (!1726)
APKBUILD was written where it should have been APKINDEX.
2018-12-11 06:56:53 +00:00
Oliver Smith
a5a64158e9
allow specifying multiple postmarketOS mirrors (!1718)
Multiple -mp arguments can be used to list multiple mirrors:
$ pmbootstrap -mp=first -mp=second chroot -- cat /etc/apk/repositories

This is needed for the new build infrastructure, so we can have a WIP
repository to which we push packages until all of them are up to date,
and then publish all of them at once. Software like KDE/Plasma Mobile,
which expect a lot of packages to be updated from one version to
another will not end up with a half-way through upgrade that way.
2018-12-06 07:31:43 +01:00
Oliver Smith
a92e6a89d0
remove local postmarketOS mirror folder support (!1718)
Obscure feature: it was possible to specify a local path as
--mirror-pmOS. It would then get mounted to /mnt/postmarketos-mirror
inside the chroot, and be specified as such in the generated
/etc/apk/repositories file.

I had used this once for some testing scripts, but I am sure nobody is
using this anymore. The same can be achieved with running a local http
server anyway:
<https://wiki.postmarketos.org/wiki/Installing_packages_on_a_running_phone>

Removing this makes it easier to support multiple postmarketOS mirrors
(next commit).
2018-12-06 07:31:38 +01:00
Oliver Smith
933c4d0f0d new action: 'pmbootstrap repo_missing'
Add a new action that lists all aports, for which no binary packages
exist. Only list packages that can be built for the relevant arch
(specified with --arch). This works recursively: when a package can be
built for a certain arch, but one of its dependencies
(or their depends) can not be built for that arch, then don't list it.

This action will be used for the new sr.ht based build infrastructure,
to figure out which packages need to be built ahead of time (so we can
trigger each of them as single build job). Determining the order of the
packages to be built is not determined with pmbootstrap, the serverside
code of build.postmarketos.org takes care of that.

For testing purposes, a single package can also be specified and the
action will list if it can be built for that arch with its
dependencies, and what needs to be built exactly.

Add pmb/helpers/package.py to hold functions that work on both pmaports
and (binary package) repos - in contrary to the existing
pmb/helpers/pmaports.py (see previous commit) and pmb/helpers/repo.py,
which only work with one of those.

Refactoring:
* pmb/helpers/pmaports.py: add a get_list() function, which lists all
  aports and use it instead of writing the same glob loop over and over
* add pmb.helpers.pmaports.get(), which finds an APKBUILD and parses it
  in one step.
* rename pmb.build._package.check_arch to ...check_arch_abort to
  distinguish it from the other check_arch function
2018-12-01 21:30:59 +00:00
Oliver Smith
e458b1fdbc Add --offline flag
Allow working offline with pmbootstrap, as long as all packages that
are being used have already been downloaded.
2018-10-25 20:37:33 +00:00
Oliver Smith
b8f35d45b8
aportgen: Gracefully handle old aports_upstream (#1291)
In order to get cross-compilers, we generate a few aports (e.g.
binutils-armhf, gcc-armhf) automatically from Alpine's aports.
pmbootstrap was already able to perform a git checkout of Alpine's
aports repository. But it needed to be manually updated. Otherwise
the `pmbootstrap aportgen` command could actually downgrade the aport
instead of updating it to the current version.

After thinking about adding a dedicated pmbootstrap command for
updating git repositories, I thought it would be better to not open
that can of worms (pmbootstrap as general git wrapper? no thanks).

The solution implemented here compares the upstream aport version of
the git checkout of a certain package (e.g. gcc for gcc-armhf) with the
version in Alpine's binary package APKINDEX. When the aport version is
lower than the binary package version, it shows the user how to update
the git repository with just one command:

    pmbootstrap chroot --add=git --user -- \
        git -C /mnt/pmbootstrap-git/aports_upstream pull

Changes:
* `pmb.aportgen.core.get_upstream_aport()`: new function, that returns
  the absolute path to the upstream aport on disk, after checking the
  version of the aport against the binary package.
* Use that new function in pmb.aportgen.gcc and pmb.aportgen.binutils
* New function `pmb.helpers.repo.alpine_apkindex_path()`: updates the
  APKINDEX if necessary and returns the absolute path to the APKINDEX.
  This code was basically present already, but not as function, so now
  we have a bit less overhead there.
* `pmbootstrap chroot`: new `--user` argument
* `pmb.parse.apkbuild`: make pkgname check optional, as it fails with
  the official gcc APKBUILD before we modify it (the current APKBUILD
  parser is not meant to be perfect, as this would require a full shell
  parsing implementation).
* Extended `test_aportgen.py` and enabled it by default in
  `testcases_fast.sh`.  Previously it was disabled due to traffic
  concerns (cloning the aports repo, but then again we do a full KDE
  plasma mobile installation in Travis now, so that shouldn't matter
  too much).
* `testcases_fast.sh`: With "test_aport_in_sync_with_git" removed
  from the disabled-by-default list (left over from timestamp based
  rebuilds), there were no more test cases disabled by default. I've
  changed it, so now the qemu_running_processes test case is disabled,
  and added an `--all` parameter to the script to disable no test
  cases. Travis runs with the `--all` parameter while it's useful to
  do a quick local test without `--all` in roughly 2 minutes instead of
  10.
* `aports/cross/binutils-*`: Fix `_mirror` variable to point to current
  default Alpine mirror (so the aportgen testcase runs through).
2018-03-11 14:18:21 +00:00
Oliver Smith
99127111a1
Only download APKINDEX for relevant architectures (#1201)
### Only download APKINDEX for relevant architectures
We're downloading the APKINDEX files for all architectures supported by
postmarketOS currently (x86, x86_64, armhf, aarch64). Most of the time,
we only need it for the native and device arch, so this PR reduces the
downloaded files to what is really necessary.

### Intuitive pmbootstrap update logic
* pmb.helpers.repo.update():
  * Default is updating all arches where the APKBUILD files exist
  * Add existing_only parameter
  * Return True when files have been downloaded
  * Properly print which arches will be updated
  * Print update reason only in verbose log
  * Add and improve comments
* pmb.parse.arguments(), update action:
  * Add --non-existing parameter
  * Default for --arch is None (instead of arch.native)
* pmb.helpers.frontend.update():
  * Inform about --non-existing if no APKBUILDs have been updated
2018-03-04 13:44:27 +00:00
Oliver Smith
e8c27795a8
Remove rest of 'pmbootstrap challenge' left overs (#1173)
Follow up to #1162.

* `pmb.build.buildinfo()`: Used to record the build environment. It is
  flawed because it scans the repo APKINDEX files instead of using the
  actually installed packages list. When it was implemented we were not
  able to do the latter. After this is removed, `pmb.parse.depends` can
  be simplified (it needs to be rewritten for #1122).
* `pmb.helpers.repo.diff()` and `pmb.helpers.repo.files()`: These were
  used exclusively by `pmb.build.buildinfo()`, to learn about which
  files have been changed in the local repository folder after a
  package was built. The idea was, that we could find subpackages that
  way. But this information is present in the installed package list as
  well, which is a much cleaner approach.
2018-02-01 22:03:21 +00:00
Oliver Smith
a5d2b2b064
Fix: Native arch APKINDEX downloaded twice (#1190)
Nowadays pmb.config.build_device_architectures holds the native
architecture as well, so we don't need to explicitly download the
native architecture APKINDEX.
2018-02-01 21:27:17 +00:00
Oliver Smith
7750c1dd40
Happy new year! (update copyright to 2018) 2018-01-04 04:53:35 +01:00
Oliver Smith
a7b881e4cc
Close #871: Enable binary repository (#887)
* add my own build key
* enable the repo in the config
* update the README file
* Adjust testcase, that validates the keys and enable it in testcases_fast.sh
* Only save/load keys to/from the config file, which we ask for during
  'pmbootstrap init', so the binary repo gets used even if a config file
  already exists (this also removes a workaround, that deletes the work
  folder path from the config dictionary before writing it)
* Download missing APKINDEX.tar.gz files with Python code, before
  attempting to build packages (so we know which ones aleady exist in
  the binary packages repository)
* Consider APKINDEX files older than 4 hours as outdated and download
  them again (also in Python code)
* Provide 'pmbootstrap update' to force-update the APKINDEX files
* Travis: more logging output on failure
* Only allow keys from config_keys to be used by "pmbootstrap config"
2017-11-19 15:04:08 +00:00
Pablo Castellano
b4dd7a89d2 Close #709: Improve user creation (#725)
* Allow to specify a custom username in "pmbootstrap init"
* Build chroots have "pmos" instead of "user" as username now
* Installation user UID is 1000 now (as in all other Linux distributions)
* Adjust autologins
* postmarketos-base: enable wheel group for sudo, removed previous sudoers file
* Implement safe upgrade path:
We save the version of the work folder format now, in $WORK/version.
When this file does not exist, it defaults to 0.
In case it does not match the currently required version
(pmb.config.work_version), then ask the user if it should
automatically be upgraded.
2017-10-12 20:08:10 +00:00
Oliver Smith
21c09b3b3c Fix various build issues (fix #189, fix #341) (#345)
Changes:
* Removed the apkindex_files cache. That particular cache caused
  bug #189 and didn't bring any real-world performance improvements
  (tested 3x with that cache and 3x without, no significant speed
  difference). I decided to remove it instead of keeping it/adding
  even more code to resolve the bug.
* Fix the check for already built packages: always use the architecture,
  that the package should be built for instead of the architecture of
  the build environment (e.g. use armhf, even when building a noarch
  package in the x86_64 chroot). This partially resolves #341.
* Make pmb.chroot.apk.install_is_necessary() more robust: If the binary
  package is missing, although it should be there, print a warning and
  build it with force.
2017-08-09 17:59:21 +00:00
Oliver Smith
f37367c57c
Properly support specifying a local folder as --mirror-pmOS
This is required for developing and testing the binary repository
scripts (see #64). Changes:
* When specified, the local folder gets mounted inside the chroots
  as /mnt/postmarketos-mirror
* The apkindex_files() function outputs the correct path to the local
  repository (it does *not* hash the URL in that case, which would
  be wrong)
* /etc/apk/repositories: when the pmOS mirror is a local folder,
  the path "/mnt/postmarketos-mirror" gets added to that file instead
  of the outside path (so apk finds it properly inside the chroot)
2017-07-12 21:55:47 +02:00
Oliver Smith
51bdc24315 Properly rebuild/install packages when something changed (Fix #120, #108, #131) (#129)
TLDR: Always rebuild/install packages when something changed when executing "pmbootstrap install/initfs/flash", more speed in dependency resolution.
---
pmbootstrap has already gotten some support for "timestamp based rebuilds", which modifies the logic for when packages should be rebuilt. It doesn't only consider packages outdated with old pkgver/pkgrel combinations, but also packages, where a source file has a newer timestamp, than the built package has.

I've found out, that this can lead to more rebuilds than expected. For example, when you check out the pmbootstrap git repository again into another folder, although you have already built packages. Then all files have the timestamp of the checkout, and the packages will appear to be outdated. While this is not largely a concern now, this will become a problem once we have a binary package repository, because then the packages from the binary repo will always seem to be outdated, if you just freshly checked out the repository.

To combat this, git gets asked if the files from the aport we're looking at are in sync with upstream, or not. Only when the files are not in sync with upstream and the timestamps of the sources are newer, a rebuild gets triggered from now on.

In case this logic should fail, I've added an option during "pmbootstrap init" where you can enable or disable the "timestamp based rebuilds" option.

In addition to that, this commit also works on fixing #120: packages do not get updated in "pmbootstrap install" after they have been rebuilt. For this to work, we specify all packages explicitly for abuild, instead of letting abuild do the resolving. This feature will also work with the "timestamp based rebuilds".

This commit also fixes the working_dir argument in pmb.helpers.run.user, which was simply ignored before.

Finally, the performance of the dependency resolution is faster again (when compared to the current version in master), because the parsed apkbuilds and finding the aport by pkgname gets cached during one pmbootstrap call (in args.cache, which also makes it easy to put fake data there in testcases).

The new dependency resolution code can output lots of verbose messages for debugging by specifying the `-v` parameter. The meaning of that changed, it used to output the file names where log messages come from, but no one seemed to use that anyway.
2017-07-10 15:23:43 +00:00
Oliver Smith
90c3a67329
Fix syntax issues / unused import reported by static code analysis 2017-06-14 20:12:47 +02:00
Oliver Smith
c0f90ee65a
pmb/helpers/repo.py: Diff ofthe package repo works properly now.
Previously, it would always report all files as new files, although
some of them may not have changed. I've added testcases for the
repo functions.
2017-06-14 19:48:23 +02:00