git: add repository and remote archive support#2013
Conversation
3fd5170 to
f9a4d62
Compare
| "github.com/stretchr/testify/require" | ||
| ) | ||
|
|
||
| func TestArchiveRemote(t *testing.T) { |
There was a problem hiding this comment.
We already test the remote transport logic in our git transport tests. This ensures the high-level API does validation.
f9a4d62 to
0183df3
Compare
pjbgf
left a comment
There was a problem hiding this comment.
Overall, LGTM. Let's just tighten the validation on ArchiveOptions please as per comment below.
| } | ||
|
|
||
| // Validate validates the ArchiveOptions. | ||
| func (o *ArchiveOptions) Validate() error { |
There was a problem hiding this comment.
Let's add stricter rules here please, for example:
Formatmust be empty or one oftar,tar.gz,tgz,zip.Prefixmust be empty or a valid path component, not containing...Treeishmust be a sha1/sha256 valid hex, or a validheadsname.Pathsmust be empty, or a valid relative path not containing...
There was a problem hiding this comment.
Hey @pjbgf,
I made some changes to validate the treeish during git.Archive and before calling WriteArchive. The rules here are intentional for different reasons.
- For
Format, we do the validation ingit.Archivebecause a remote server andgit.ArchiveRemotecan have different supported archive formats than local. - The
Prefixhere is the prepended prefix component added to the files being archived. Canonical git allows path components containing../and leave unsafe non-normalized prefixes to be the responsibility of the extractor. Manytarandunziptools reject extracting to../by default. - Since we moved resolving the tree before actually calling
WriteArchive, we validate the tree is valid and has either valid sha1/sha256 or valid heads. - Paths are just filters, if a path doesn't exist, we already return
pathspec did not match any filessimilar to canonical git. This validation cannot happen duringValidatewithout resolving the tree and start writing the archive.
There was a problem hiding this comment.
@pjbgf Prefix now rejects paths with .. components and absolute paths.
There was a problem hiding this comment.
Pull request overview
Adds porcelain-level archive creation APIs to the git package, enabling git archive-like behavior for local repositories and git archive --remote-like behavior via git-upload-archive, building on the existing transport-layer support.
Changes:
- Introduces
Repository.Archive(opts)/Repository.ArchiveContext(ctx, opts)for streaming local repository archives. - Adds
ArchiveRemote(url, opts)/ArchiveRemoteContext(ctx, url, opts)to request archives from remote repositories usinggit-upload-archive. - Refactors internal archive generation to resolve tree-ish metadata separately and expands tests around formats/prefix/path filtering.
Show a summary per file
| File | Description |
|---|---|
| repository.go | Adds ArchiveOptions and local repository archive streaming APIs. |
| archive.go | Adds remote archive entrypoints that handshake and invoke git-upload-archive. |
| internal/archive/archive.go | Refactors archive generation API to accept a resolved tree + commit metadata; adds ErrUnsupportedFormat. |
| plumbing/transport/upload_archive.go | Updates server-side UploadArchive to resolve tree-ish before writing the archive. |
| repository_test.go | Adds coverage for local repository archive generation (formats/prefix/path filtering/cancellation). |
| archive_test.go | Adds unit tests for ArchiveOptions.Validate and basic ArchiveRemote argument/validation behavior. |
Copilot's findings
Comments suppressed due to low confidence (1)
archive_test.go:122
- These parallel subtests capture the range variable
tt. Sincettis reused across iterations, t.Parallel() can lead to subtests using the wrong url/options/wantErr values. Rebindtt := tt(or switch to an index loop) before t.Run.
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
_, err := ArchiveRemote(tt.url, tt.opts)
- Files reviewed: 6/6 changed files
- Comments generated: 6
| return arch.Archive(ctx, &transport.ArchiveRequest{ | ||
| Args: args, | ||
| Progress: o.Progress, | ||
| }) |
5924d14 to
9293937
Compare
| name: "remote allows custom format", | ||
| url: "file:///tmp/repo.git", | ||
| opts: &ArchiveOptions{Format: "tar.xz", Treeish: "master"}, | ||
| wantErr: "", | ||
| }, |
There was a problem hiding this comment.
File here is the transport and our client resolved the url to our default file transport. Custom transports can be added via ClientOptions. If a transport is not supported or valid, we error during the construction of client.
ff3e445 to
c1dafd7
Compare
There was a problem hiding this comment.
Copilot's findings
Comments suppressed due to low confidence (2)
archive_test.go:136
- The subtests run with
t.Parallel()but capture the range variablettfrom the outer loop. Rebindtt := ttinside the loop beforet.Runto avoid all subtests using the last case / racing.
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
_, err := ArchiveRemote(tt.url, tt.opts)
if tt.wantErr == "" {
archive_test.go:213
- The subtests run with
t.Parallel()but capture the range variablettfrom the outer loop. Rebindtt := ttinside the loop beforet.Run(this also ensures each subtest uses the intendedtt.optspointer).
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
rc, err := ArchiveRemote(repoURL, tt.opts)
require.NoError(t, err)
- Files reviewed: 7/7 changed files
- Comments generated: 5
| base := t.TempDir() | ||
| repoFS := test.PrepareRepository(t, fixtures.Basic().One(), base, "basic.git") | ||
| repoPath, err := filepath.Abs(repoFS.Root()) | ||
| require.NoError(t, err) | ||
| repoURL := "file://" + repoPath | ||
|
|
Signed-off-by: Ayman Bagabas <[email protected]>
pjbgf
left a comment
There was a problem hiding this comment.
Tiny nit on defer sess.Close(). Apart from that, please rebase it.
pjbgf
left a comment
There was a problem hiding this comment.
@aymanbagabas thanks for working on this. 🙇
This implements the high level porcelain end of
git-upload-archiveand thegit archiveequivalent APIs. It adds arepo.Archive(opts)andgit.ArchiveRemote(url, opts)methods to create archives of local and remote repositories.This continues the work that was introduced in #1986