mirror of
https://github.com/tailscale/tailscale.git
synced 2026-09-14 14:59:57 -04:00
The pre-push large-blob check diffed the pushed tree only against the remote's old tree for the same ref. After rebasing a stale branch past an unrelated large-file change on the default branch, that diff shows the large file as changed even though the exact blob is already on the remote via main, rejecting the push with a false positive. Diff against every available base tree instead: the remote's old commit for the ref plus the merge base with the remote's default branch. Only flag a file that is a large addition relative to all bases, so blobs the remote already has are not reported, while genuinely new large files on the branch are still rejected. Updates tailscale/corp#9863 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com> Change-Id: Ia0c98fc1f5ab67a2913f948aeff605c72641ada7
111 lines
3.4 KiB
Go
111 lines
3.4 KiB
Go
// Copyright (c) Tailscale Inc & contributors
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package githook
|
|
|
|
import (
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// testMaxSize is the large-blob threshold used by the tests, chosen
|
|
// small so test fixtures stay tiny.
|
|
const testMaxSize = 100
|
|
|
|
func git(t *testing.T, args ...string) string {
|
|
t.Helper()
|
|
cmd := exec.Command("git", args...)
|
|
cmd.Env = append(cmd.Environ(),
|
|
"GIT_AUTHOR_NAME=test", "GIT_AUTHOR_EMAIL=test@example.com",
|
|
"GIT_COMMITTER_NAME=test", "GIT_COMMITTER_EMAIL=test@example.com",
|
|
)
|
|
out, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
t.Fatalf("git %v: %v\n%s", args, err, out)
|
|
}
|
|
return strings.TrimSpace(string(out))
|
|
}
|
|
|
|
func writeAndCommit(t *testing.T, name, contents, msg string) string {
|
|
t.Helper()
|
|
if err := os.WriteFile(name, []byte(contents), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
git(t, "add", "-A")
|
|
git(t, "commit", "-q", "-m", msg)
|
|
return git(t, "rev-parse", "HEAD")
|
|
}
|
|
|
|
// setupRepo creates a scratch repo laying out the rebase scenario:
|
|
//
|
|
// c1 (initial) -- c2 (adds big.bin on main, origin/main)
|
|
// \
|
|
// f1 (old branch tip, as previously pushed)
|
|
//
|
|
// It returns the three commit hashes.
|
|
func setupRepo(t *testing.T) (c1, c2, f1 string) {
|
|
t.Chdir(t.TempDir())
|
|
git(t, "init", "-q", "-b", "main")
|
|
c1 = writeAndCommit(t, "small.txt", "hello", "initial")
|
|
|
|
git(t, "checkout", "-q", "-b", "feature")
|
|
f1 = writeAndCommit(t, "feature.txt", "feature work", "feature commit")
|
|
|
|
git(t, "checkout", "-q", "main")
|
|
c2 = writeAndCommit(t, "big.bin", strings.Repeat("x", 2*testMaxSize), "add big file on main")
|
|
git(t, "update-ref", "refs/remotes/origin/main", c2)
|
|
return c1, c2, f1
|
|
}
|
|
|
|
// TestCheckLargeBlobsRebasePastLargeFile verifies that pushing a
|
|
// branch rebased past an unrelated large-file change on main is not
|
|
// flagged: the large blob is already on the remote via main.
|
|
func TestCheckLargeBlobsRebasePastLargeFile(t *testing.T) {
|
|
t.Setenv(skipLargeFileCheckEnv, "")
|
|
_, _, f1 := setupRepo(t)
|
|
|
|
git(t, "checkout", "-q", "feature")
|
|
git(t, "rebase", "-q", "main")
|
|
f2 := git(t, "rev-parse", "HEAD")
|
|
|
|
err := checkLargeBlobs("origin", push{
|
|
localRef: "refs/heads/feature",
|
|
localSHA: f2,
|
|
remoteRef: "refs/heads/feature",
|
|
remoteSHA: f1,
|
|
}, testMaxSize)
|
|
if err != nil {
|
|
t.Errorf("checkLargeBlobs = %v; want nil (big.bin is already on origin/main)", err)
|
|
}
|
|
}
|
|
|
|
// TestCheckLargeBlobsNewLargeFile verifies that a genuinely new large
|
|
// blob on the branch is flagged, both for updates to an existing
|
|
// remote ref and for new refs.
|
|
func TestCheckLargeBlobsNewLargeFile(t *testing.T) {
|
|
t.Setenv(skipLargeFileCheckEnv, "")
|
|
_, _, f1 := setupRepo(t)
|
|
|
|
git(t, "checkout", "-q", "feature")
|
|
git(t, "rebase", "-q", "main")
|
|
f2 := writeAndCommit(t, "mine.bin", strings.Repeat("y", 2*testMaxSize), "add my own big file")
|
|
|
|
for _, remoteSHA := range []string{f1, zeroRef} {
|
|
err := checkLargeBlobs("origin", push{
|
|
localRef: "refs/heads/feature",
|
|
localSHA: f2,
|
|
remoteRef: "refs/heads/feature",
|
|
remoteSHA: remoteSHA,
|
|
}, testMaxSize)
|
|
if err == nil {
|
|
t.Errorf("remoteSHA=%s: checkLargeBlobs = nil; want error flagging mine.bin", remoteSHA)
|
|
} else if !strings.Contains(err.Error(), "mine.bin") {
|
|
t.Errorf("remoteSHA=%s: error %v does not mention mine.bin", remoteSHA, err)
|
|
} else if strings.Contains(err.Error(), "big.bin") {
|
|
t.Errorf("remoteSHA=%s: error %v should not mention big.bin", remoteSHA, err)
|
|
}
|
|
}
|
|
}
|