Skip to content

Commit 90d2d8b

Browse files
committed
git: allow cloning commit shas not referenced by branch/tag
Signed-off-by: Justin Chadwell <[email protected]>
1 parent ac3eb58 commit 90d2d8b

File tree

4 files changed

+83
-10
lines changed

4 files changed

+83
-10
lines changed

source/git/source.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,9 @@ func (gs *gitSourceHandler) Snapshot(ctx context.Context, g session.Group) (out
475475
}
476476
}
477477
args = append(args, "origin")
478-
if !gitutil.IsCommitSHA(ref) {
478+
if gitutil.IsCommitSHA(ref) {
479+
args = append(args, ref)
480+
} else {
479481
// local refs are needed so they would be advertised on next fetches. Force is used
480482
// in case the ref is a branch and it now points to a different commit sha
481483
// TODO: is there a better way to do this?

source/git/source_test.go

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -218,15 +218,31 @@ func testFetchBySHA(t *testing.T, keepGitDir bool) {
218218
}
219219

220220
func TestFetchUnreferencedTagSha(t *testing.T) {
221-
testFetchUnreferencedTagSha(t, false)
221+
testFetchUnreferencedRefSha(t, "v1.2.3-special", false)
222222
}
223223

224224
func TestFetchUnreferencedTagShaKeepGitDir(t *testing.T) {
225-
testFetchUnreferencedTagSha(t, true)
225+
testFetchUnreferencedRefSha(t, "v1.2.3-special", true)
226226
}
227227

228-
// testFetchUnreferencedTagSha tests fetching a SHA that points to a tag that is not reachable from any branch.
229-
func testFetchUnreferencedTagSha(t *testing.T, keepGitDir bool) {
228+
func TestFetchUnreferencedRefSha(t *testing.T) {
229+
testFetchUnreferencedRefSha(t, "refs/special", false)
230+
}
231+
232+
func TestFetchUnreferencedRefShaKeepGitDir(t *testing.T) {
233+
testFetchUnreferencedRefSha(t, "refs/special", true)
234+
}
235+
236+
func TestFetchUnadvertisedRefSha(t *testing.T) {
237+
testFetchUnreferencedRefSha(t, "refs/special~", false)
238+
}
239+
240+
func TestFetchUnadvertisedRefShaKeepGitDir(t *testing.T) {
241+
testFetchUnreferencedRefSha(t, "refs/special~", true)
242+
}
243+
244+
// testFetchUnreferencedRefSha tests fetching a SHA that points to a ref that is not reachable from any branch.
245+
func testFetchUnreferencedRefSha(t *testing.T, ref string, keepGitDir bool) {
230246
if runtime.GOOS == "windows" {
231247
t.Skip("Depends on unimplemented containerd bind-mount support on Windows")
232248
}
@@ -239,7 +255,7 @@ func testFetchUnreferencedTagSha(t *testing.T, keepGitDir bool) {
239255

240256
repo := setupGitRepo(t)
241257

242-
cmd := exec.Command("git", "rev-parse", "v1.2.3-special")
258+
cmd := exec.Command("git", "rev-parse", ref)
243259
cmd.Dir = repo.mainPath
244260

245261
out, err := cmd.Output()
@@ -691,6 +707,8 @@ func setupGitRepo(t *testing.T) gitRepoFixture {
691707
// * (refs/heads/feature) withsub
692708
// * feature
693709
// * (HEAD -> refs/heads/master, tag: refs/tags/lightweight-tag) third
710+
// | * ref only
711+
// | * commit only
694712
// | * (tag: refs/tags/v1.2.3-special) tagonly-leaf
695713
// |/
696714
// * (tag: refs/tags/v1.2.3) second
@@ -699,35 +717,53 @@ func setupGitRepo(t *testing.T) gitRepoFixture {
699717
"git -c init.defaultBranch=master init",
700718
"git config --local user.email test",
701719
"git config --local user.name test",
720+
702721
"echo foo > abc",
703722
"git add abc",
704723
"git commit -m initial",
705724
"git tag --no-sign a/v1.2.3",
725+
706726
"echo bar > def",
707727
"mkdir subdir",
708728
"echo subcontents > subdir/subfile",
709729
"git add def subdir",
710730
"git commit -m second",
711731
"git tag -a -m \"this is an annotated tag\" v1.2.3",
732+
712733
"echo foo > bar",
713734
"git add bar",
714735
"git commit -m tagonly-leaf",
715736
"git tag --no-sign v1.2.3-special",
737+
738+
"echo foo2 > bar2",
739+
"git add bar2",
740+
"git commit -m \"commit only\"",
741+
"echo foo3 > bar3",
742+
"git add bar3",
743+
"git commit -m \"ref only\"",
744+
"git update-ref refs/special $(git rev-parse HEAD)",
745+
716746
// switch master back to v1.2.3
717747
"git checkout -B master v1.2.3",
748+
718749
"echo sbb > foo13",
719750
"git add foo13",
720751
"git commit -m third",
721752
"git tag --no-sign lightweight-tag",
753+
722754
"git checkout -B feature",
755+
723756
"echo baz > ghi",
724757
"git add ghi",
725758
"git commit -m feature",
726759
"git update-ref refs/test $(git rev-parse HEAD)",
760+
727761
"git submodule add "+fixture.subURL+" sub",
728762
"git add -A",
729763
"git commit -m withsub",
764+
730765
"git checkout master",
766+
731767
// "git log --oneline --graph --decorate=full --all",
732768
)
733769
return fixture
@@ -785,6 +821,7 @@ func runShell(t *testing.T, dir string, cmds ...string) {
785821
cmd = exec.Command("sh", "-c", args)
786822
}
787823
cmd.Dir = dir
824+
// cmd.Stdout = os.Stdout
788825
cmd.Stderr = os.Stderr
789826
require.NoErrorf(t, cmd.Run(), "error running %v", args)
790827
}

util/gitutil/git_cli.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,14 @@ func (cli *GitCLI) Run(ctx context.Context, args ...string) (_ []byte, err error
218218
continue
219219
}
220220
}
221+
if strings.Contains(errbuf.String(), "not our ref") || strings.Contains(errbuf.String(), "unadvertised object") {
222+
// server-side error: https://github.com/git/git/blob/34b6ce9b30747131b6e781ff718a45328aa887d0/upload-pack.c#L811-L812
223+
// client-side error: https://github.com/git/git/blob/34b6ce9b30747131b6e781ff718a45328aa887d0/fetch-pack.c#L2250-L2253
224+
if newArgs := argsNoCommitRefspec(args); len(args) > len(newArgs) {
225+
args = newArgs
226+
continue
227+
}
228+
}
221229

222230
return buf.Bytes(), errors.Wrapf(err, "git stderr:\n%s", errbuf.String())
223231
}
@@ -244,3 +252,19 @@ func argsNoDepth(args []string) []string {
244252
}
245253
return out
246254
}
255+
256+
func argsNoCommitRefspec(args []string) []string {
257+
if len(args) <= 2 {
258+
return args
259+
}
260+
if args[0] != "fetch" {
261+
return args
262+
}
263+
264+
// assume the refspec is the last arg
265+
if IsCommitSHA(args[len(args)-1]) {
266+
return args[:len(args)-1]
267+
}
268+
269+
return args
270+
}

util/gitutil/git_commit.go

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,19 @@
11
package gitutil
22

3-
import "regexp"
3+
func IsCommitSHA(str string) bool {
4+
if len(str) != 40 {
5+
return false
6+
}
47

5-
var validHex = regexp.MustCompile(`^[a-f0-9]{40}$`)
8+
for _, ch := range str {
9+
if ch >= '0' && ch <= '9' {
10+
continue
11+
}
12+
if ch >= 'a' && ch <= 'f' {
13+
continue
14+
}
15+
return false
16+
}
617

7-
func IsCommitSHA(str string) bool {
8-
return validHex.MatchString(str)
18+
return true
919
}

0 commit comments

Comments
 (0)