git-lfs/git/git_test.go
Taylor Blau a7ada95228 git/git.go: remove git.RefTypeRemoteTag
Since 5e0f81d2 (Adding utility methods to get recent refs, 2015-08-17),
we have treated references beginning with the prefix "refs/remotes/tags"
as being of type "remote tag".

This is not a namespace that upstream Git uses in practice to denote
remote tags. Instead, tags are automatically namespaced into our local
refs/tags, hence rendering this prefix (and its type) meaningless.

As such, let's remove it so that the 'git lfs migrate' (et. al.)
commands do not get confused about references that do not exist.

Co-authored-by: brian m. carlson <bk2204@github.com>
2018-09-25 14:38:45 -07:00

705 lines
18 KiB
Go

package git_test // to avoid import cycles
import (
"io/ioutil"
"os"
"path/filepath"
"sort"
"testing"
"time"
. "github.com/git-lfs/git-lfs/git"
test "github.com/git-lfs/git-lfs/t/cmd/util"
"github.com/stretchr/testify/assert"
)
func TestRefString(t *testing.T) {
const sha = "0000000000000000000000000000000000000000"
for s, r := range map[string]*Ref{
"refs/heads/master": {
Name: "master",
Type: RefTypeLocalBranch,
Sha: sha,
},
"refs/remotes/origin/master": {
Name: "origin/master",
Type: RefTypeRemoteBranch,
Sha: sha,
},
"refs/tags/v1.0.0": {
Name: "v1.0.0",
Type: RefTypeLocalTag,
Sha: sha,
},
"HEAD": {
Name: "HEAD",
Type: RefTypeHEAD,
Sha: sha,
},
"other": {
Name: "other",
Type: RefTypeOther,
Sha: sha,
},
} {
assert.Equal(t, s, r.Refspec())
}
}
func TestParseRefs(t *testing.T) {
tests := map[string]RefType{
"refs/heads": RefTypeLocalBranch,
"refs/tags": RefTypeLocalTag,
"refs/remotes": RefTypeRemoteBranch,
}
for prefix, expectedType := range tests {
r := ParseRef(prefix+"/branch", "abc123")
assert.Equal(t, "abc123", r.Sha, "prefix: "+prefix)
assert.Equal(t, "branch", r.Name, "prefix: "+prefix)
assert.Equal(t, expectedType, r.Type, "prefix: "+prefix)
}
r := ParseRef("refs/foo/branch", "abc123")
assert.Equal(t, "abc123", r.Sha, "prefix: refs/foo")
assert.Equal(t, "refs/foo/branch", r.Name, "prefix: refs/foo")
assert.Equal(t, RefTypeOther, r.Type, "prefix: refs/foo")
r = ParseRef("HEAD", "abc123")
assert.Equal(t, "abc123", r.Sha, "prefix: HEAD")
assert.Equal(t, "HEAD", r.Name, "prefix: HEAD")
assert.Equal(t, RefTypeHEAD, r.Type, "prefix: HEAD")
}
func TestCurrentRefAndCurrentRemoteRef(t *testing.T) {
repo := test.NewRepo(t)
repo.Pushd()
defer func() {
repo.Popd()
repo.Cleanup()
}()
// test commits; we'll just modify the same file each time since we're
// only interested in branches
inputs := []*test.CommitInput{
{ // 0
Files: []*test.FileInput{
{Filename: "file1.txt", Size: 20},
},
},
{ // 1
NewBranch: "branch2",
Files: []*test.FileInput{
{Filename: "file1.txt", Size: 25},
},
},
{ // 2
ParentBranches: []string{"master"}, // back on master
Files: []*test.FileInput{
{Filename: "file1.txt", Size: 30},
},
},
{ // 3
NewBranch: "branch3",
Files: []*test.FileInput{
{Filename: "file1.txt", Size: 32},
},
},
}
outputs := repo.AddCommits(inputs)
// last commit was on branch3
gitConf := repo.GitConfig()
ref, err := CurrentRef()
assert.Nil(t, err)
assert.Equal(t, &Ref{
Name: "branch3",
Type: RefTypeLocalBranch,
Sha: outputs[3].Sha,
}, ref)
test.RunGitCommand(t, true, "checkout", "master")
ref, err = CurrentRef()
assert.Nil(t, err)
assert.Equal(t, &Ref{
Name: "master",
Type: RefTypeLocalBranch,
Sha: outputs[2].Sha,
}, ref)
// Check remote
repo.AddRemote("origin")
test.RunGitCommand(t, true, "push", "-u", "origin", "master:someremotebranch")
ref, err = gitConf.CurrentRemoteRef()
assert.Nil(t, err)
assert.Equal(t, &Ref{
Name: "origin/someremotebranch",
Type: RefTypeRemoteBranch,
Sha: outputs[2].Sha,
}, ref)
refname, err := gitConf.RemoteRefNameForCurrentBranch()
assert.Nil(t, err)
assert.Equal(t, "refs/remotes/origin/someremotebranch", refname)
ref, err = ResolveRef(outputs[2].Sha)
assert.Nil(t, err)
assert.Equal(t, &Ref{
Name: outputs[2].Sha,
Type: RefTypeOther,
Sha: outputs[2].Sha,
}, ref)
}
func TestRecentBranches(t *testing.T) {
repo := test.NewRepo(t)
repo.Pushd()
defer func() {
repo.Popd()
repo.Cleanup()
}()
now := time.Now()
// test commits; we'll just modify the same file each time since we're
// only interested in branches & dates
inputs := []*test.CommitInput{
{ // 0
CommitDate: now.AddDate(0, 0, -20),
Files: []*test.FileInput{
{Filename: "file1.txt", Size: 20},
},
},
{ // 1
CommitDate: now.AddDate(0, 0, -15),
NewBranch: "excluded_branch", // new branch & tag but too old
Tags: []string{"excluded_tag"},
Files: []*test.FileInput{
{Filename: "file1.txt", Size: 25},
},
},
{ // 2
CommitDate: now.AddDate(0, 0, -12),
ParentBranches: []string{"master"}, // back on master
Files: []*test.FileInput{
{Filename: "file1.txt", Size: 30},
},
},
{ // 3
CommitDate: now.AddDate(0, 0, -6),
NewBranch: "included_branch", // new branch within 7 day limit
Files: []*test.FileInput{
{Filename: "file1.txt", Size: 32},
},
},
{ // 4
CommitDate: now.AddDate(0, 0, -3),
NewBranch: "included_branch_2", // new branch within 7 day limit
Files: []*test.FileInput{
{Filename: "file1.txt", Size: 36},
},
},
{ // 5
// Final commit, current date/time
ParentBranches: []string{"master"}, // back on master
Files: []*test.FileInput{
{Filename: "file1.txt", Size: 21},
},
},
}
outputs := repo.AddCommits(inputs)
// Add a couple of remotes and push some branches
repo.AddRemote("origin")
repo.AddRemote("upstream")
test.RunGitCommand(t, true, "push", "origin", "master")
test.RunGitCommand(t, true, "push", "origin", "excluded_branch")
test.RunGitCommand(t, true, "push", "origin", "included_branch")
test.RunGitCommand(t, true, "push", "upstream", "master")
test.RunGitCommand(t, true, "push", "upstream", "included_branch_2")
// Recent, local only
refs, err := RecentBranches(now.AddDate(0, 0, -7), false, "")
assert.Equal(t, nil, err)
expectedRefs := []*Ref{
{
Name: "master",
Type: RefTypeLocalBranch,
Sha: outputs[5].Sha,
},
{
Name: "included_branch_2",
Type: RefTypeLocalBranch,
Sha: outputs[4].Sha,
},
{
Name: "included_branch",
Type: RefTypeLocalBranch,
Sha: outputs[3].Sha,
},
}
assert.Equal(t, expectedRefs, refs, "Refs should be correct")
// Recent, remotes too (all of them)
refs, err = RecentBranches(now.AddDate(0, 0, -7), true, "")
assert.Equal(t, nil, err)
expectedRefs = []*Ref{
{
Name: "master",
Type: RefTypeLocalBranch,
Sha: outputs[5].Sha,
},
{
Name: "included_branch_2",
Type: RefTypeLocalBranch,
Sha: outputs[4].Sha,
},
{
Name: "included_branch",
Type: RefTypeLocalBranch,
Sha: outputs[3].Sha,
},
{
Name: "upstream/master",
Type: RefTypeRemoteBranch,
Sha: outputs[5].Sha,
},
{
Name: "upstream/included_branch_2",
Type: RefTypeRemoteBranch,
Sha: outputs[4].Sha,
},
{
Name: "origin/master",
Type: RefTypeRemoteBranch,
Sha: outputs[5].Sha,
},
{
Name: "origin/included_branch",
Type: RefTypeRemoteBranch,
Sha: outputs[3].Sha,
},
}
// Need to sort for consistent comparison
sort.Sort(test.RefsByName(expectedRefs))
sort.Sort(test.RefsByName(refs))
assert.Equal(t, expectedRefs, refs, "Refs should be correct")
// Recent, only single remote
refs, err = RecentBranches(now.AddDate(0, 0, -7), true, "origin")
assert.Equal(t, nil, err)
expectedRefs = []*Ref{
{
Name: "master",
Type: RefTypeLocalBranch,
Sha: outputs[5].Sha,
},
{
Name: "origin/master",
Type: RefTypeRemoteBranch,
Sha: outputs[5].Sha,
},
{
Name: "included_branch_2",
Type: RefTypeLocalBranch,
Sha: outputs[4].Sha,
},
{
Name: "included_branch",
Type: RefTypeLocalBranch,
Sha: outputs[3].Sha,
},
{
Name: "origin/included_branch",
Type: RefTypeRemoteBranch,
Sha: outputs[3].Sha,
},
}
// Need to sort for consistent comparison
sort.Sort(test.RefsByName(expectedRefs))
sort.Sort(test.RefsByName(refs))
assert.Equal(t, expectedRefs, refs, "Refs should be correct")
}
func TestResolveEmptyCurrentRef(t *testing.T) {
repo := test.NewRepo(t)
repo.Pushd()
defer func() {
repo.Popd()
repo.Cleanup()
}()
_, err := CurrentRef()
assert.NotEqual(t, nil, err)
}
func TestWorkTrees(t *testing.T) {
// Only git 2.5+
if !IsGitVersionAtLeast("2.5.0") {
return
}
repo := test.NewRepo(t)
repo.Pushd()
defer func() {
repo.Popd()
repo.Cleanup()
}()
// test commits; we'll just modify the same file each time since we're
// only interested in branches & dates
inputs := []*test.CommitInput{
{ // 0
Files: []*test.FileInput{
{Filename: "file1.txt", Size: 20},
},
},
{ // 1
NewBranch: "branch2",
Files: []*test.FileInput{
{Filename: "file1.txt", Size: 25},
},
},
{ // 2
NewBranch: "branch3",
ParentBranches: []string{"master"}, // back on master
Files: []*test.FileInput{
{Filename: "file1.txt", Size: 30},
},
},
{ // 3
NewBranch: "branch4",
ParentBranches: []string{"master"}, // back on master
Files: []*test.FileInput{
{Filename: "file1.txt", Size: 40},
},
},
}
outputs := repo.AddCommits(inputs)
// Checkout master again otherwise can't create a worktree from branch4 if we're on it here
test.RunGitCommand(t, true, "checkout", "master")
// We can create worktrees as subfolders for convenience
// Each one is checked out to a different branch
// Note that we *won't* create one for branch3
test.RunGitCommand(t, true, "worktree", "add", "branch2_wt", "branch2")
test.RunGitCommand(t, true, "worktree", "add", "branch4_wt", "branch4")
refs, err := GetAllWorkTreeHEADs(filepath.Join(repo.Path, ".git"))
assert.Equal(t, nil, err)
expectedRefs := []*Ref{
{
Name: "master",
Type: RefTypeLocalBranch,
Sha: outputs[0].Sha,
},
{
Name: "branch2",
Type: RefTypeLocalBranch,
Sha: outputs[1].Sha,
},
{
Name: "branch4",
Type: RefTypeLocalBranch,
Sha: outputs[3].Sha,
},
}
// Need to sort for consistent comparison
sort.Sort(test.RefsByName(expectedRefs))
sort.Sort(test.RefsByName(refs))
assert.Equal(t, expectedRefs, refs, "Refs should be correct")
}
func TestVersionCompare(t *testing.T) {
assert.True(t, IsVersionAtLeast("2.6.0", "2.6.0"))
assert.True(t, IsVersionAtLeast("2.6.0", "2.6"))
assert.True(t, IsVersionAtLeast("2.6.0", "2"))
assert.True(t, IsVersionAtLeast("2.6.10", "2.6.5"))
assert.True(t, IsVersionAtLeast("2.8.1", "2.7.2"))
assert.False(t, IsVersionAtLeast("1.6.0", "2"))
assert.False(t, IsVersionAtLeast("2.5.0", "2.6"))
assert.False(t, IsVersionAtLeast("2.5.0", "2.5.1"))
assert.False(t, IsVersionAtLeast("2.5.2", "2.5.10"))
}
func TestGitAndRootDirs(t *testing.T) {
repo := test.NewRepo(t)
repo.Pushd()
defer func() {
repo.Popd()
repo.Cleanup()
}()
git, root, err := GitAndRootDirs()
if err != nil {
t.Fatal(err)
}
expected, err := os.Stat(git)
if err != nil {
t.Fatal(err)
}
actual, err := os.Stat(filepath.Join(root, ".git"))
if err != nil {
t.Fatal(err)
}
assert.True(t, os.SameFile(expected, actual))
}
func TestGetTrackedFiles(t *testing.T) {
repo := test.NewRepo(t)
repo.Pushd()
defer func() {
repo.Popd()
repo.Cleanup()
}()
// test commits; we'll just modify the same file each time since we're
// only interested in branches
inputs := []*test.CommitInput{
{ // 0
Files: []*test.FileInput{
{Filename: "file1.txt", Size: 20},
{Filename: "file2.txt", Size: 20},
{Filename: "folder1/file10.txt", Size: 20},
{Filename: "folder1/anotherfile.txt", Size: 20},
},
},
{ // 1
Files: []*test.FileInput{
{Filename: "file3.txt", Size: 20},
{Filename: "file4.txt", Size: 20},
{Filename: "folder2/something.txt", Size: 20},
{Filename: "folder2/folder3/deep.txt", Size: 20},
},
},
}
repo.AddCommits(inputs)
tracked, err := GetTrackedFiles("*.txt")
assert.Nil(t, err)
sort.Strings(tracked) // for direct comparison
fulllist := []string{"file1.txt", "file2.txt", "file3.txt", "file4.txt", "folder1/anotherfile.txt", "folder1/file10.txt", "folder2/folder3/deep.txt", "folder2/something.txt"}
assert.Equal(t, fulllist, tracked)
tracked, err = GetTrackedFiles("*file*.txt")
assert.Nil(t, err)
sort.Strings(tracked)
sublist := []string{"file1.txt", "file2.txt", "file3.txt", "file4.txt", "folder1/anotherfile.txt", "folder1/file10.txt"}
assert.Equal(t, sublist, tracked)
tracked, err = GetTrackedFiles("folder1/*")
assert.Nil(t, err)
sort.Strings(tracked)
sublist = []string{"folder1/anotherfile.txt", "folder1/file10.txt"}
assert.Equal(t, sublist, tracked)
tracked, err = GetTrackedFiles("folder2/*")
assert.Nil(t, err)
sort.Strings(tracked)
sublist = []string{"folder2/folder3/deep.txt", "folder2/something.txt"}
assert.Equal(t, sublist, tracked)
// relative dir
os.Chdir("folder1")
tracked, err = GetTrackedFiles("*.txt")
assert.Nil(t, err)
sort.Strings(tracked)
sublist = []string{"anotherfile.txt", "file10.txt"}
assert.Equal(t, sublist, tracked)
os.Chdir("..")
// absolute paths only includes matches in repo root
tracked, err = GetTrackedFiles("/*.txt")
assert.Nil(t, err)
sort.Strings(tracked)
assert.Equal(t, []string{"file1.txt", "file2.txt", "file3.txt", "file4.txt"}, tracked)
// Test includes staged but uncommitted files
ioutil.WriteFile("z_newfile.txt", []byte("Hello world"), 0644)
test.RunGitCommand(t, true, "add", "z_newfile.txt")
tracked, err = GetTrackedFiles("*.txt")
assert.Nil(t, err)
sort.Strings(tracked)
fulllist = append(fulllist, "z_newfile.txt")
assert.Equal(t, fulllist, tracked)
// Test includes modified files (not staged)
ioutil.WriteFile("file1.txt", []byte("Modifications"), 0644)
tracked, err = GetTrackedFiles("*.txt")
assert.Nil(t, err)
sort.Strings(tracked)
assert.Equal(t, fulllist, tracked)
// Test includes modified files (staged)
test.RunGitCommand(t, true, "add", "file1.txt")
tracked, err = GetTrackedFiles("*.txt")
assert.Nil(t, err)
sort.Strings(tracked)
assert.Equal(t, fulllist, tracked)
// Test excludes deleted files (not committed)
test.RunGitCommand(t, true, "rm", "file2.txt")
tracked, err = GetTrackedFiles("*.txt")
assert.Nil(t, err)
sort.Strings(tracked)
deletedlist := []string{"file1.txt", "file3.txt", "file4.txt", "folder1/anotherfile.txt", "folder1/file10.txt", "folder2/folder3/deep.txt", "folder2/something.txt", "z_newfile.txt"}
assert.Equal(t, deletedlist, tracked)
}
func TestLocalRefs(t *testing.T) {
repo := test.NewRepo(t)
repo.Pushd()
defer func() {
repo.Popd()
repo.Cleanup()
}()
repo.AddCommits([]*test.CommitInput{
{
Files: []*test.FileInput{
{Filename: "file1.txt", Size: 20},
},
},
{
NewBranch: "branch",
ParentBranches: []string{"master"},
Files: []*test.FileInput{
{Filename: "file1.txt", Size: 20},
},
},
})
test.RunGitCommand(t, true, "tag", "v1")
refs, err := LocalRefs()
if err != nil {
t.Fatal(err)
}
actual := make(map[string]bool)
for _, r := range refs {
t.Logf("REF: %s", r.Name)
switch r.Type {
case RefTypeHEAD:
t.Errorf("Local HEAD ref: %v", r)
case RefTypeOther:
t.Errorf("Stash or unknown ref: %v", r)
default:
actual[r.Name] = true
}
}
expected := []string{"master", "branch", "v1"}
found := 0
for _, refname := range expected {
if actual[refname] {
found += 1
} else {
t.Errorf("could not find ref %q", refname)
}
}
if found != len(expected) {
t.Errorf("Unexpected local refs: %v", actual)
}
}
func TestGetFilesChanges(t *testing.T) {
repo := test.NewRepo(t)
repo.Pushd()
defer func() {
repo.Popd()
repo.Cleanup()
}()
commits := repo.AddCommits([]*test.CommitInput{
{
Files: []*test.FileInput{
{Filename: "file1.txt", Size: 20},
},
},
{
Files: []*test.FileInput{
{Filename: "file1.txt", Size: 25},
{Filename: "file2.txt", Size: 20},
{Filename: "folder/file3.txt", Size: 10},
},
Tags: []string{"tag1"},
},
{
NewBranch: "abranch",
ParentBranches: []string{"master"},
Files: []*test.FileInput{
{Filename: "file1.txt", Size: 30},
{Filename: "file4.txt", Size: 40},
},
},
})
expected0to1 := []string{"file1.txt", "file2.txt", "folder/file3.txt"}
expected1to2 := []string{"file1.txt", "file4.txt"}
expected0to2 := []string{"file1.txt", "file2.txt", "file4.txt", "folder/file3.txt"}
// Test 2 SHAs
changes, err := GetFilesChanged(commits[0].Sha, commits[1].Sha)
assert.Nil(t, err)
assert.Equal(t, expected0to1, changes)
// Test SHA & tag
changes, err = GetFilesChanged(commits[0].Sha, "tag1")
assert.Nil(t, err)
assert.Equal(t, expected0to1, changes)
// Test SHA & branch
changes, err = GetFilesChanged(commits[0].Sha, "abranch")
assert.Nil(t, err)
assert.Equal(t, expected0to2, changes)
// Test tag & branch
changes, err = GetFilesChanged("tag1", "abranch")
assert.Nil(t, err)
assert.Equal(t, expected1to2, changes)
// Test fail
_, err = GetFilesChanged("tag1", "nonexisting")
assert.NotNil(t, err)
_, err = GetFilesChanged("nonexisting", "tag1")
assert.NotNil(t, err)
// Test Single arg version
changes, err = GetFilesChanged(commits[1].Sha, "")
assert.Nil(t, err)
assert.Equal(t, expected0to1, changes)
changes, err = GetFilesChanged("abranch", "")
assert.Nil(t, err)
assert.Equal(t, expected1to2, changes)
}
func TestValidateRemoteURL(t *testing.T) {
assert.Nil(t, ValidateRemoteURL("https://github.com/git-lfs/git-lfs"))
assert.Nil(t, ValidateRemoteURL("http://github.com/git-lfs/git-lfs"))
assert.Nil(t, ValidateRemoteURL("git://github.com/git-lfs/git-lfs"))
assert.Nil(t, ValidateRemoteURL("ssh://git@github.com/git-lfs/git-lfs"))
assert.Nil(t, ValidateRemoteURL("ssh://git@github.com:22/git-lfs/git-lfs"))
assert.Nil(t, ValidateRemoteURL("git@github.com:git-lfs/git-lfs"))
assert.Nil(t, ValidateRemoteURL("git@server:/absolute/path.git"))
assert.NotNil(t, ValidateRemoteURL("ftp://git@github.com/git-lfs/git-lfs"))
}
func TestRefTypeKnownPrefixes(t *testing.T) {
for typ, expected := range map[RefType]struct {
Prefix string
Ok bool
}{
RefTypeLocalBranch: {"refs/heads", true},
RefTypeRemoteBranch: {"refs/remotes", true},
RefTypeLocalTag: {"refs/tags", true},
RefTypeHEAD: {"", false},
RefTypeOther: {"", false},
} {
prefix, ok := typ.Prefix()
assert.Equal(t, expected.Prefix, prefix)
assert.Equal(t, expected.Ok, ok)
}
}