Git archive generation meets Hyrum's law
One widely used GitHub feature is the ability to download an archive file of the state of the repository at an arbitrary commit; it is often used by build systems to obtain a specific release of a package of interest. Internally, this archive is created at request time by the git archive subcommand. Most build systems will compare the resulting archive against a separately stored checksum to be sure that the archive is as expected and has not been corrupted; if the checksum fails to match, the build will be aborted. So when the checksums of GitHub-generated tarballs abruptly changed, builds started failing.
Unsurprisingly, people started to complain. The initial response from GitHub employee (and major Git contributor) brian m. carlson was less than fully understanding:
I'm saying that policy has never been correct and we've never guaranteed stable checksums for archives, just like Git has never guaranteed that. I apologize that things are broken here and that there hasn't been clearer communication in the past on this, but our policy hasn't changed in over 4 years.
This answer, it might be said, was not received well. Wyatt Anderson, for example, said:
The collective amount of human effort it will take to break glass, recover broken build systems that are impacted by this change, and republish artifacts across entire software ecosystems could probably cure cancer. Please consider reverting this change as soon as possible.
The outcry grew
louder, and it took about two hours for Matt Cooper (another GitHub
employee) to announce
that the change was being reverted — for now: "we're reverting the
change, and we'll communicate better about such changes in the future
(including timelines)
". Builds resumed working, and peace
reigned once again.
The source of the problem
The developers at GitHub did not wake up one morning and hatch a scheme to break large numbers of build systems; instead, all they did was upgrade the version of Git used internally. In June 2022, René Scharfe changed git archive to use an internal implementation of the gzip compression algorithm rather than invoking the gzip program separately. This change, which found its way into the Git 2.38 release, allowed Git to drop the gzip dependency, more easily support compression across operating systems, and compress the data with less CPU time.
It also caused git archive to compress files differently. While the uncompressed data is identical, the compressed form differs, so the checksum of the compressed data differs as well. Once this change landed on GitHub's production systems, the checksums for tarballs generated on the fly abruptly changed. GitHub backed out the change, either by reverting to an older Git or by explicitly configuring the use of the standalone gzip program, and the immediate problem went away.
The resulting discussion on the Git mailing list has been relatively muted
so far. Eli Schwartz started
things off with a suggestion that Git should change its default back to
using the external gzip program for now, then implement a "v2
archive format
" using the internal compressor. Using a heuristic,
git archive would always default to the older format for
commits before some sort of cutoff date. That would ensure ongoing
compatibility for older archives, but the idea of wiring that sort of
heuristic into Git was not generally popular.
Ævar Arnfjörð Bjarmason, instead, suggested that the default could be changed to use the external gzip, retaining the internal implementation as an option or as a fallback should the external program not be found. The responsibility for output compatibility could then be shifted to the compression program: anybody who wants to ensure that their generated archive files do not change will have to ensure that their gzip does not change. Since the Git developers do not control that program, they cannot guarantee its forward compatibility in any case.
Carlson, though, argued for avoiding stability guarantees — especially implicit guarantees — if possible:
I made a change some years back to the archive format to fix the permissions on pax headers when extracted as files, and kernel.org was relying on that and broke. Linus yelled at me because of that.Since then, I've been very opposed to us guaranteeing output format consistency without explicitly doing so. I had sent some patches before that I don't think ever got picked up that documented this explicitly. I very much don't want people to come to rely on our behaviour unless we explicitly guarantee it.
He went on to suggest that Git could guarantee the stability of the
archive format in uncompressed form. That format would have to be
versioned, though, since the SHA-256 transition, if and when it happens,
will force changes in that format anyway (a claim that Bjarmason questioned). In
general, carlson concluded, it may well become necessary for anybody who
wants consistent results to decompress archive
files before checking checksums. He later reiterated
that, in his opinion, implementing a stable tar format is
feasible, but adding compression is not: "I personally
feel that's too hard to get right and am not planning on working on it
".
Konstantin Ryabitsev said
that, while he understands carlson's desire to avoid committing to an
output format, "I also think it's one of those things that happen
despite your best efforts to prevent it
". He suggested adding a
--stable option to git archive that was guaranteed
to not change.
What next?
As of this writing, the Git community has not decided whether to make any changes as the result of this episode. Bjarmason argued that the Git community should accommodate the needs of its users, even if they came to depend on a feature that was never advertised as being stable:
That's unfortunate, and those people probably shouldn't have done that, but that's water under the bridge. I think it would be irresponsible to change the output willy-nilly at this point, especially when it seems rather easy to find some compromise everyone will be happy with.
He has since posted a patch set restoring the old behavior, but also documenting that this behavior could change in the future.
Committing to stability of this type is never a thing to be done lightly, though; such stability can be hard to maintain (especially when dealing with file formats defined by others) and can block other types of progress. For example, replacing gzip can yield better compression that can be performed more efficiently; an inability to move beyond that algorithm would prevent Git from obtaining those benefits. Even if Git restores the use of an external gzip program by default, that program might, itself, change, or downstream users like GitHub may decide that they no longer want to support that format.
It would thus be unsurprising if this problem were to refuse to go away.
The Git project is reluctant to add a stability guarantee to its
maintenance load, and the same is true of its downstream users;
GitHub has said that it would give some warning before a checksum change
returns, but has not said that such a change would not happen. The
developers and users of build systems may want to be rethinking their reliance
on the specific compression format used by one proprietary service on the
Internet. The next time problems turn up, they will not be able to say
they haven't been warned.
