diff options
author | James Fargher <jfargher@gitlab.com> | 2021-11-12 06:19:28 +0300 |
---|---|---|
committer | James Fargher <jfargher@gitlab.com> | 2021-11-15 04:41:13 +0300 |
commit | fbe4bbf8651afefba566455f88738908a440270f (patch) | |
tree | c55748b1c271dfa961b4dacadf6d8efa8cb80b3e | |
parent | b1d5dafcf488f8c48531e2a82dd7a568f01a3b7b (diff) |
git: Implement HEAD guessing logicbackup_set_default_branch
-rw-r--r-- | internal/git/reference.go | 48 | ||||
-rw-r--r-- | internal/git/reference_test.go | 73 |
2 files changed, 121 insertions, 0 deletions
diff --git a/internal/git/reference.go b/internal/git/reference.go index 96a32190d..5966cc5c5 100644 --- a/internal/git/reference.go +++ b/internal/git/reference.go @@ -3,6 +3,7 @@ package git import ( "bytes" "context" + "errors" "strings" ) @@ -120,3 +121,50 @@ func CheckRefFormat(ctx context.Context, gitCmdFactory CommandFactory, refName s return true, nil } + +// GuessHead tries to guess what branch HEAD would be pointing at given a list +// of references. The references are assumed to be ordered by name. +// +// This function should match the corresponding function in git: +// https://github.com/git/git/blob/2a97289ad8b103625d3a1a12f66c27f50df822ce/remote.c#L2198 +func GuessHead(refs []Reference) (ReferenceName, error) { + head := findRefByName(refs, "HEAD") + if head == nil { + return "", errors.New("missing HEAD ref") + } + if head.IsSymbolic { + return ReferenceName(head.Target), nil + } + + main := findRefByName(refs, ReferenceName(DefaultRef)) + if main != nil && head.Target == main.Target { + return main.Name, nil + } + + master := findRefByName(refs, ReferenceName(LegacyDefaultRef)) + if master != nil && head.Target == master.Target { + return master.Name, nil + } + + for i := range refs { + if _, ok := refs[i].Name.Branch(); !ok { + continue + } + if refs[i].Target == head.Target { + return refs[i].Name, nil + } + } + + return "", errors.New("no matching ref") +} + +// findRefByName filters through refs and returns the first ref where name +// matches exactly. nil is returned when no match is found. +func findRefByName(refs []Reference, name ReferenceName) *Reference { + for i := range refs { + if refs[i].Name == name { + return &refs[i] + } + } + return nil +} diff --git a/internal/git/reference_test.go b/internal/git/reference_test.go index 745b182da..05da8bf48 100644 --- a/internal/git/reference_test.go +++ b/internal/git/reference_test.go @@ -131,3 +131,76 @@ func TestReferenceName_Branch(t *testing.T) { }) } } + +func TestGuessHead(t *testing.T) { + for _, tc := range []struct { + desc string + refs []git.Reference + expectedErr string + expectedHead git.ReferenceName + }{ + { + desc: "no refs", + expectedErr: "missing HEAD ref", + }, + { + desc: "symbolic HEAD in refs", + refs: []git.Reference{ + git.NewSymbolicReference("HEAD", "refs/heads/banana"), + git.NewReference("refs/heads/master", "5da601ef10e314884bbade9d5b063be37579ccf9"), + }, + expectedHead: "refs/heads/banana", + }, + { + desc: "matching default branch", + refs: []git.Reference{ + git.NewReference("HEAD", "5da601ef10e314884bbade9d5b063be37579ccf9"), + git.NewReference("refs/heads/apple", "5da601ef10e314884bbade9d5b063be37579ccf9"), + git.NewReference("refs/heads/banana", "5da601ef10e314884bbade9d5b063be37579ccf9"), + git.NewReference("refs/heads/main", "5da601ef10e314884bbade9d5b063be37579ccf9"), + }, + expectedHead: "refs/heads/main", + }, + { + desc: "matching legacy default branch", + refs: []git.Reference{ + git.NewReference("HEAD", "5da601ef10e314884bbade9d5b063be37579ccf9"), + git.NewReference("refs/heads/apple", "5da601ef10e314884bbade9d5b063be37579ccf9"), + git.NewReference("refs/heads/banana", "5da601ef10e314884bbade9d5b063be37579ccf9"), + git.NewReference("refs/heads/master", "5da601ef10e314884bbade9d5b063be37579ccf9"), + }, + expectedHead: "refs/heads/master", + }, + { + desc: "other matches", + refs: []git.Reference{ + git.NewReference("HEAD", "5da601ef10e314884bbade9d5b063be37579ccf9"), + git.NewReference("refs/heads/apple", "5da601ef10e314884bbade9d5b063be37579ccf9"), + git.NewReference("refs/heads/banana", "5da601ef10e314884bbade9d5b063be37579ccf9"), + git.NewReference("refs/heads/carrot", "5da601ef10e314884bbade9d5b063be37579ccf9"), + git.NewReference("refs/heads/main", "5da601ef10e314884bbade9d5b063be37579ccf0"), + }, + expectedHead: "refs/heads/apple", + }, + { + desc: "no matches", + refs: []git.Reference{ + git.NewReference("HEAD", "5da601ef10e314884bbade9d5b063be37579ccf9"), + git.NewReference("refs/heads/apple", "5da601ef10e314884bbade9d5b063be37579ccf0"), + git.NewReference("refs/heads/banana", "5da601ef10e314884bbade9d5b063be37579ccf0"), + git.NewReference("refs/heads/carrot", "5da601ef10e314884bbade9d5b063be37579ccf0"), + }, + expectedErr: "no matching ref", + }, + } { + t.Run(tc.desc, func(t *testing.T) { + head, err := git.GuessHead(tc.refs) + if tc.expectedErr == "" { + require.NoError(t, err) + } else { + require.EqualError(t, err, tc.expectedErr) + } + require.Equal(t, tc.expectedHead, head) + }) + } +} |