Skip to content

Commit a278da1

Browse files
committed
fix: fetch raw base SHAs before unshallowing
Detect explicit 40-char SHAs as first-class baselines and fetch them directly in shallow clones before falling back to unshallow. Extract history validation into a helper script, cover the new paths in tests, and document fetch-depth: 1 as the default checkout path.
1 parent d5176ca commit a278da1

5 files changed

Lines changed: 225 additions & 40 deletions

File tree

.github/workflows/test.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ jobs:
2222
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
2323
- name: Run summary fixture tests
2424
run: ./tests/test_summary.sh
25+
- name: Run history fixture tests
26+
run: bash ./tests/test_ensure_history.sh
2527

2628
# ============================================================================
2729
# Self-test: Create minimal workspace, no external dependencies

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424
steps:
2525
- uses: actions/checkout@v4
2626
with:
27-
fetch-depth: 0
27+
fetch-depth: 1
2828

2929
- uses: loadingalias/cargo-rail-action@v4
3030
id: rail
@@ -58,6 +58,9 @@ jobs:
5858
Use the stable major tag `@v4`, or pin a commit SHA for maximum reproducibility. Pin `version` for deterministic
5959
`cargo-rail` installs.
6060

61+
The action handles shallow checkouts intentionally: raw commit SHAs are fetched directly when possible, and broader
62+
history fetch only happens when a branch-based comparison still needs merge-base history.
63+
6164
## Inputs
6265

6366
| Input | Default | Description |

action.yaml

Lines changed: 1 addition & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -323,45 +323,7 @@ runs:
323323
env:
324324
BASE_REF: ${{ steps.base.outputs.ref }}
325325
run: |
326-
set -euo pipefail
327-
328-
# Track if we're in a shallow clone
329-
if [[ -f .git/shallow ]]; then
330-
echo "shallow=true" >> "$GITHUB_OUTPUT"
331-
else
332-
echo "shallow=false" >> "$GITHUB_OUTPUT"
333-
fi
334-
335-
# Check if base ref is resolvable; if not, fetch needed history
336-
if [[ "$BASE_REF" =~ ^HEAD~[0-9]+$ ]]; then
337-
# Relative ref: check if commit exists
338-
if ! git rev-parse "$BASE_REF" &>/dev/null; then
339-
depth="${BASE_REF#HEAD~}"
340-
fetch_depth=$((depth + 1))
341-
echo "Fetching $fetch_depth commits for $BASE_REF..."
342-
git fetch --depth="$fetch_depth" origin HEAD
343-
fi
344-
else
345-
# Branch ref: check if merge-base is resolvable
346-
if ! git merge-base HEAD "$BASE_REF" &>/dev/null; then
347-
echo "Fetching history for merge-base with $BASE_REF..."
348-
git fetch --unshallow origin || git fetch origin "$BASE_REF"
349-
fi
350-
fi
351-
352-
# Final verification
353-
if [[ "$BASE_REF" =~ ^HEAD~[0-9]+$ ]]; then
354-
if ! git rev-parse "$BASE_REF" &>/dev/null; then
355-
echo "::error::Cannot resolve $BASE_REF after fetch. Check repository state."
356-
exit 1
357-
fi
358-
else
359-
if ! git merge-base HEAD "$BASE_REF" &>/dev/null; then
360-
echo "::error::Cannot resolve merge-base with $BASE_REF after fetch. Check repository state."
361-
exit 1
362-
fi
363-
fi
364-
echo "Base ref $BASE_REF verified"
326+
bash "$GITHUB_ACTION_PATH/scripts/ensure_history.sh"
365327
366328
# Run planner and emit selected output mode
367329
- name: Run cargo-rail plan

scripts/ensure_history.sh

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
if [[ -z "${BASE_REF:-}" ]]; then
5+
echo "::error::BASE_REF is required"
6+
exit 1
7+
fi
8+
9+
emit_output() {
10+
local key="$1"
11+
local value="$2"
12+
if [[ -n "${GITHUB_OUTPUT:-}" ]]; then
13+
echo "$key=$value" >> "$GITHUB_OUTPUT"
14+
fi
15+
}
16+
17+
shallow_file() {
18+
git rev-parse --git-path shallow
19+
}
20+
21+
is_shallow_clone() {
22+
[[ -f "$(shallow_file)" ]]
23+
}
24+
25+
has_commit() {
26+
git rev-parse --verify "$1^{commit}" &>/dev/null
27+
}
28+
29+
is_head_relative_ref() {
30+
[[ "$1" =~ ^HEAD~[0-9]+$ ]]
31+
}
32+
33+
is_raw_sha() {
34+
[[ "$1" =~ ^[0-9a-fA-F]{40}$ ]]
35+
}
36+
37+
fetch_branch_ref() {
38+
local ref="$1"
39+
local mode="${2:-depth1}"
40+
41+
if [[ "$ref" == origin/* ]]; then
42+
local branch="${ref#origin/}"
43+
if [[ "$mode" == "depth1" ]]; then
44+
git fetch --no-tags --depth=1 origin "refs/heads/$branch:refs/remotes/origin/$branch"
45+
else
46+
git fetch --no-tags origin "refs/heads/$branch:refs/remotes/origin/$branch"
47+
fi
48+
elif [[ "$mode" == "depth1" ]]; then
49+
git fetch --no-tags --depth=1 origin "$ref"
50+
else
51+
git fetch --no-tags origin "$ref"
52+
fi
53+
}
54+
55+
emit_output "shallow" "$(if is_shallow_clone; then echo true; else echo false; fi)"
56+
57+
if is_head_relative_ref "$BASE_REF"; then
58+
if ! has_commit "$BASE_REF"; then
59+
depth="${BASE_REF#HEAD~}"
60+
fetch_depth=$((depth + 1))
61+
echo "Fetching $fetch_depth commits for $BASE_REF..."
62+
git fetch --no-tags --depth="$fetch_depth" origin HEAD
63+
fi
64+
elif is_raw_sha "$BASE_REF"; then
65+
if ! has_commit "$BASE_REF"; then
66+
echo "Fetching raw base SHA $BASE_REF directly..."
67+
fetch_branch_ref "$BASE_REF" depth1 || true
68+
69+
if ! has_commit "$BASE_REF"; then
70+
if is_shallow_clone; then
71+
echo "Direct SHA fetch did not resolve $BASE_REF; unshallowing current checkout..."
72+
git fetch --no-tags --unshallow origin || true
73+
fi
74+
75+
if ! has_commit "$BASE_REF"; then
76+
echo "Fetching raw base SHA $BASE_REF without depth limit..."
77+
fetch_branch_ref "$BASE_REF" full
78+
fi
79+
fi
80+
fi
81+
else
82+
if ! has_commit "$BASE_REF"; then
83+
echo "Fetching branch ref $BASE_REF..."
84+
fetch_branch_ref "$BASE_REF" depth1 || true
85+
fi
86+
87+
if ! git merge-base HEAD "$BASE_REF" &>/dev/null; then
88+
echo "Fetching history for merge-base with $BASE_REF..."
89+
90+
if is_shallow_clone; then
91+
git fetch --no-tags --unshallow origin || true
92+
fi
93+
94+
if ! git merge-base HEAD "$BASE_REF" &>/dev/null; then
95+
fetch_branch_ref "$BASE_REF" full || true
96+
fi
97+
fi
98+
fi
99+
100+
if is_head_relative_ref "$BASE_REF" || is_raw_sha "$BASE_REF"; then
101+
if ! has_commit "$BASE_REF"; then
102+
echo "::error::Cannot resolve $BASE_REF after fetch. Check repository state."
103+
exit 1
104+
fi
105+
else
106+
if ! git merge-base HEAD "$BASE_REF" &>/dev/null; then
107+
echo "::error::Cannot resolve merge-base with $BASE_REF after fetch. Check repository state."
108+
exit 1
109+
fi
110+
fi
111+
112+
echo "Base ref $BASE_REF verified"

tests/test_ensure_history.sh

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
5+
SCRIPT="$ROOT/scripts/ensure_history.sh"
6+
TMP_DIR="$(mktemp -d)"
7+
trap 'rm -rf "$TMP_DIR"' EXIT
8+
9+
init_remote_repo() {
10+
local name="$1"
11+
INIT_REMOTE="$TMP_DIR/$name-remote.git"
12+
INIT_WORK="$TMP_DIR/$name-work"
13+
14+
git init --bare --initial-branch=main "$INIT_REMOTE" >/dev/null
15+
git clone "$INIT_REMOTE" "$INIT_WORK" >/dev/null
16+
git -C "$INIT_WORK" config user.email "test@example.com"
17+
git -C "$INIT_WORK" config user.name "Test"
18+
git -C "$INIT_WORK" branch -M main
19+
}
20+
21+
run_history_check() {
22+
local repo="$1"
23+
local base_ref="$2"
24+
local output_file="$3"
25+
26+
(
27+
cd "$repo"
28+
BASE_REF="$base_ref" GITHUB_OUTPUT="$output_file" bash "$SCRIPT"
29+
)
30+
}
31+
32+
test_raw_sha_fetch_stays_shallow() {
33+
init_remote_repo raw-sha
34+
local remote="$INIT_REMOTE"
35+
local work="$INIT_WORK"
36+
local clone="$TMP_DIR/raw-sha-clone"
37+
local output_file="$TMP_DIR/raw-sha-output.txt"
38+
local base_sha
39+
40+
printf 'one\n' > "$work/file.txt"
41+
git -C "$work" add file.txt
42+
git -C "$work" commit -m "one" >/dev/null
43+
base_sha="$(git -C "$work" rev-parse HEAD)"
44+
45+
printf 'two\n' > "$work/file.txt"
46+
git -C "$work" add file.txt
47+
git -C "$work" commit -m "two" >/dev/null
48+
git -C "$work" push -u origin main >/dev/null
49+
50+
git clone --depth 1 --branch main "file://$remote" "$clone" >/dev/null
51+
52+
if git -C "$clone" rev-parse --verify "$base_sha^{commit}" >/dev/null 2>&1; then
53+
echo "raw SHA unexpectedly present before targeted fetch"
54+
exit 1
55+
fi
56+
57+
run_history_check "$clone" "$base_sha" "$output_file"
58+
59+
git -C "$clone" rev-parse --verify "$base_sha^{commit}" >/dev/null
60+
[[ -f "$(git -C "$clone" rev-parse --git-path shallow)" ]]
61+
grep -qx 'shallow=true' "$output_file"
62+
}
63+
64+
test_branch_ref_falls_back_to_full_history() {
65+
init_remote_repo branch-ref
66+
local remote="$INIT_REMOTE"
67+
local work="$INIT_WORK"
68+
local clone="$TMP_DIR/branch-ref-clone"
69+
local output_file="$TMP_DIR/branch-ref-output.txt"
70+
71+
printf 'base\n' > "$work/file.txt"
72+
git -C "$work" add file.txt
73+
git -C "$work" commit -m "base" >/dev/null
74+
git -C "$work" push -u origin main >/dev/null
75+
76+
git -C "$work" checkout -b feature >/dev/null
77+
printf 'feature\n' > "$work/feature.txt"
78+
git -C "$work" add feature.txt
79+
git -C "$work" commit -m "feature" >/dev/null
80+
git -C "$work" push -u origin feature >/dev/null
81+
82+
git -C "$work" checkout main >/dev/null
83+
printf 'main\n' > "$work/main.txt"
84+
git -C "$work" add main.txt
85+
git -C "$work" commit -m "main advance" >/dev/null
86+
git -C "$work" push >/dev/null
87+
88+
git clone --depth 1 --branch feature "file://$remote" "$clone" >/dev/null
89+
90+
if git -C "$clone" rev-parse --verify "origin/main^{commit}" >/dev/null 2>&1; then
91+
echo "origin/main unexpectedly present before branch fetch"
92+
exit 1
93+
fi
94+
95+
run_history_check "$clone" "origin/main" "$output_file"
96+
97+
git -C "$clone" rev-parse --verify "origin/main^{commit}" >/dev/null
98+
git -C "$clone" merge-base HEAD origin/main >/dev/null
99+
[[ ! -f "$(git -C "$clone" rev-parse --git-path shallow)" ]]
100+
grep -qx 'shallow=true' "$output_file"
101+
}
102+
103+
test_raw_sha_fetch_stays_shallow
104+
test_branch_ref_falls_back_to_full_history
105+
106+
echo "ensure_history tests passed"

0 commit comments

Comments
 (0)