Skip to content

Commit

Permalink
Support git repository checkouts using git worktree (#1) (#5)
Browse files Browse the repository at this point in the history
  • Loading branch information
cgrindel authored Dec 9, 2023
1 parent 92b2c9c commit cc4b917
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 2 deletions.
3 changes: 1 addition & 2 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,10 @@ import (
"time"

"github.com/choffmeister/git-describe-semver/internal"
"github.com/go-git/go-git/v5"
)

func run(dir string, opts internal.GenerateVersionOptions) (*string, error) {
repo, err := git.PlainOpen(dir)
repo, err := internal.OpenRepository(dir)
if err != nil {
return nil, fmt.Errorf("unable to open git repository: %v", err)
}
Expand Down
65 changes: 65 additions & 0 deletions internal/git.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
package internal

import (
"errors"
"fmt"
"os"
"path/filepath"
"strings"

"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object"
)

const (
// Prefix found in .git files that point to another location
GitDirPrefix = "gitdir: "

GitDirName = ".git"
CommonDirName = "commondir"
)

// GitTagMap ...
func GitTagMap(repo git.Repository) (*map[string]string, error) {
iter, err := repo.Tags()
Expand Down Expand Up @@ -103,3 +115,56 @@ func GitDescribe(repo git.Repository) (*string, *int, *string, error) {
tagName := (*tags)[tagHash]
return &tagName, &counter, &headHash, nil
}

func OpenRepository(dir string) (*git.Repository, error) {
gitDir, err := FindGitDir(dir)
if err != nil {
return nil, err
}
enableCommonDir, err := shouldEnableCommondDir(gitDir)
if err != nil {
return nil, err
}
openOpts := &git.PlainOpenOptions{EnableDotGitCommonDir: enableCommonDir}
return git.PlainOpenWithOptions(dir, openOpts)
}

func shouldEnableCommondDir(gitDir string) (bool, error) {
cdPath := filepath.Join(gitDir, CommonDirName)
st, err := os.Stat(cdPath)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
return false, nil
}
return false, err
}
if st.IsDir() {
return false, fmt.Errorf("expected to be a file, not directory: %s", cdPath)
}
return true, nil
}

func FindGitDir(dir string) (string, error) {
gitDirPath := filepath.Join(dir, GitDirName)
st, err := os.Stat(gitDirPath)
if err != nil {
return "", err
}
if st.IsDir() {
return gitDirPath, nil
}
// It is a file, read the contents
contents, err := os.ReadFile(gitDirPath)
if err != nil {
return "", err
}

line := string(contents)
if !strings.HasPrefix(line, GitDirPrefix) {
return "", fmt.Errorf(".git file has no %s prefix", GitDirPrefix)
}

gitdir := strings.Split(line[len(GitDirPrefix):], "\n")[0]
gitdir = strings.TrimSpace(gitdir)
return gitdir, nil
}
80 changes: 80 additions & 0 deletions internal/git_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package internal

import (
"io/ioutil"
"os"
"path/filepath"
"testing"

"github.com/go-git/go-git/v5"
Expand Down Expand Up @@ -127,3 +129,81 @@ func TestGitDescribeWithBranch(t *testing.T) {
repo.CreateTag("v2.0.0", commit3, nil)
test("v2.0.0", 1, commit4.String())
}

func setUpDotGitDirTest(assert *assert.Assertions) (string, string) {
testDir, err := os.MkdirTemp("", "test")
assert.NoError(err, "failed to create temp dir")

gitDirPath := filepath.Join(testDir, GitDirName)
err = os.Mkdir(gitDirPath, 0750)
assert.NoError(err, "failed to create git dir")

return testDir, gitDirPath
}

func setUpDotGitFileTest(assert *assert.Assertions) (string, string, string) {
testDir, err := os.MkdirTemp("", "test")
assert.NoError(err, "failed to create temp dir")

actualDotGitPath := filepath.Join(testDir, "actual")
err = os.Mkdir(actualDotGitPath, 0750)
assert.NoError(err, "failed to create actual git dir")

wtPath := filepath.Join(testDir, "my_worktree")
err = os.Mkdir(wtPath, 0750)
assert.NoError(err, "failed to create worktree dir")

wtDotGitPath := filepath.Join(wtPath, GitDirName)
contents := GitDirPrefix + actualDotGitPath
err = os.WriteFile(wtDotGitPath, []byte(contents), 0666)
assert.NoError(err, "failed to write git dir file in worktree")

return testDir, actualDotGitPath, wtPath
}

func TestFindGitDir(t *testing.T) {
t.Run(".git is a directory", func(t *testing.T) {
assert := assert.New(t)
testDir, gitDirPath := setUpDotGitDirTest(assert)
defer os.RemoveAll(testDir)

result, err := FindGitDir(testDir)
assert.NoError(err, "failed to find git dir")
assert.Equal(gitDirPath, result)
})
t.Run(".git is a file pointing to another directory", func(t *testing.T) {
assert := assert.New(t)
testDir, actualDotGitPath, wtPath := setUpDotGitFileTest(assert)
defer os.RemoveAll(testDir)

result, err := FindGitDir(wtPath)
assert.NoError(err, "failed to find git dir in worktree")
assert.Equal(actualDotGitPath, result)
})
}

func TestShouldEnableCommonDir(t *testing.T) {
t.Run(".git is a directory", func(t *testing.T) {
assert := assert.New(t)
testDir, gitDirPath := setUpDotGitDirTest(assert)
defer os.RemoveAll(testDir)

result, err := shouldEnableCommondDir(gitDirPath)
assert.NoError(err, "failed evaluating whether to enable commond dir")
assert.False(result)
})
t.Run(".git is a file pointing to another directory", func(t *testing.T) {
assert := assert.New(t)
testDir, actualDotGitPath, _ := setUpDotGitFileTest(assert)
defer os.RemoveAll(testDir)

cdPath := filepath.Join(actualDotGitPath, CommonDirName)
contents := "../my_worktree"
err := os.WriteFile(cdPath, []byte(contents), 0666)
assert.NoError(err, "failed writing commondir file")

result, err := shouldEnableCommondDir(actualDotGitPath)
assert.NoError(err, "failed evaluating whether to enable commond dir")
assert.True(result)
})
}

0 comments on commit cc4b917

Please sign in to comment.