Skip to content

Only write layer files whose content actually changed - #169

Draft
viktordick wants to merge 2 commits into
mainfrom
unpack-only-changed
Draft

viktordick wants to merge 2 commits into
mainfrom
unpack-only-changed

Conversation

@viktordick

Copy link
Copy Markdown
Collaborator

Unpacking a layer source rewrote every file in the tarball, even if only
a handful of them changed. With a layer of several hundred MB that is a
lot of needless disk traffic, and since the timestamps are not taken from
the archive, git had to rehash every single file afterwards.

Replace the call to tar by helpers.unpack_tar, which compares each member
to the file that is already there and only writes it if it differs.
Superfluous elements in the target are removed afterwards, so the result
is the same as before. Each member is expected to occur only once in the
archive, but no specific order is required - anything that is in the way
of a member is cleared when that member is unpacked, including parent
elements that are not directories.

As before, timestamps from the archive are not applied. Permissions now
are applied exactly, which is what the sources intend: they set 2775 on
directories and 664 on files so the workdir becomes a group repository.
tar dropped the setgid bit (it only restores it with -p) and masked the
group write permission with the umask, so this only worked by accident of
the umask, if at all. The permissions of the extraction root itself, which
the archives carry as ".", are applied as well.

Viktor Dick and others added 2 commits August 4, 2026 06:45
GNU tar before 1.35 (e.g. Ubuntu 22.04) tries to remove the extraction
root itself if the tarball contains "." as a member, which fails with

    tar: .: Cannot unlink: Invalid argument

and makes the whole layer-init/layer-update run fail. Clear the target
directory in Python instead. This is equivalent: --recursive-unlink
emptied the same hierarchy, and tar rewrites every member on extraction
in any case, so no additional disk load.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Unpacking a layer source rewrote every file in the tarball, even if only
a handful of them changed. With a layer of several hundred MB that is a
lot of needless disk traffic, and since the timestamps are not taken from
the archive, git had to rehash every single file afterwards.

Replace the call to tar by helpers.unpack_tar, which compares each member
to the file that is already there and only writes it if it differs.
Superfluous elements in the target are removed afterwards, so the result
is the same as before. Each member is expected to occur only once in the
archive, but no specific order is required - anything that is in the way
of a member is cleared when that member is unpacked, including parent
elements that are not directories.

As before, timestamps from the archive are not applied. Permissions now
are applied exactly, which is what the sources intend: they set 2775 on
directories and 664 on files so the workdir becomes a group repository.
tar dropped the setgid bit (it only restores it with -p) and masked the
group write permission with the umask, so this only worked by accident of
the umask, if at all. The permissions of the extraction root itself, which
the archives carry as ".", are applied as well.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@viktordick

Copy link
Copy Markdown
Collaborator Author

Builds on top of #168

@viktordick
viktordick marked this pull request as draft August 4, 2026 09:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant