Skip to content

Commit

Permalink
Merge pull request #26 from SierraSoftworks/fix/branch_origin
Browse files Browse the repository at this point in the history
Automatically checkout remote branches if they exist
  • Loading branch information
spartan563 authored Mar 3, 2020
2 parents 40749af + 19e0ccd commit 02bf6d8
Show file tree
Hide file tree
Showing 5 changed files with 197 additions and 9 deletions.
2 changes: 1 addition & 1 deletion internal/app/branch.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ var branchCommand = cli.Command{
return errors.New("usage: command requires a branch to be provided")
}

return tasks.GitCheckout(c.Args().First()).ApplyRepo(repo)
return tasks.GitCheckout(c.Args().First(), false).ApplyRepo(repo)
},
BashComplete: func(c *cli.Context) {
repo, err := di.GetMapper().GetCurrentDirectoryRepo()
Expand Down
2 changes: 1 addition & 1 deletion internal/app/branch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ var _ = Describe("gt branch", func() {
tasks.GitInit(),
tasks.NewFile("README.md", []byte("# Test Repo")),
tasks.GitCommit("Initial Commit", "README.md"),
tasks.GitCheckout("master"),
tasks.GitCheckout("master", false),
).ApplyRepo(repo)
if err != nil {
return
Expand Down
33 changes: 28 additions & 5 deletions internal/pkg/tasks/gitcheckout.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,17 @@ import (
)

// GitCheckout is responsible for checking out a specific branch.
func GitCheckout(ref string) Task {
func GitCheckout(ref string, keep bool) Task {
return &gitCheckout{
RefName: ref,
Keep: keep,
}
}

// gitCheckout is responsible for running the equivalent of a `git checkout -b` for a repository.
type gitCheckout struct {
RefName string
Keep bool
}

// ApplyRepo runs the task against a repository
Expand All @@ -29,14 +31,32 @@ func (t *gitCheckout) ApplyRepo(r models.Repo) error {

co := &git.CheckoutOptions{
Branch: plumbing.NewBranchReferenceName(t.RefName),
Keep: true,
Keep: t.Keep,
}

ref, err := gr.Reference(co.Branch, true)
if err != nil && err != plumbing.ErrReferenceNotFound {
return errors.Wrap(err, "repo: unable to find branch")
refs, err := gr.References()
if err != nil {
return errors.Wrap(err, "repo: unable to gather references")
}

var ref *plumbing.Reference

refs.ForEach(func(r *plumbing.Reference) error {
if r.Type() == plumbing.SymbolicReference {
return nil
}

if r.Name().Short() == t.RefName || r.Name().Short() == "origin/"+t.RefName {
if ref == nil {
ref = r
} else if ref.Name().IsRemote() && !r.Name().IsRemote() {
ref = r
}
}

return nil
})

if ref == nil {
head, err := gr.Head()
if err != nil {
Expand All @@ -45,6 +65,9 @@ func (t *gitCheckout) ApplyRepo(r models.Repo) error {

co.Hash = head.Hash()
co.Create = true
} else if ref.Name().IsRemote() {
co.Hash = ref.Hash()
co.Create = true
}

w, err := gr.Worktree()
Expand Down
119 changes: 117 additions & 2 deletions internal/pkg/tasks/gitcheckout_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/SierraSoftworks/git-tool/test"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"gopkg.in/src-d/go-git.v4"
)

var _ = Describe("Git Checkout Task", func() {
Expand Down Expand Up @@ -43,7 +44,7 @@ var _ = Describe("Git Checkout Task", func() {
Describe("GitCheckout()", func() {
Context("when applied to a repo", func() {
JustBeforeEach(func() {
err = tasks.GitCheckout(ref).ApplyRepo(r)
err = tasks.GitCheckout(ref, false).ApplyRepo(r)
})

Context("which doesn't exist locally", func() {
Expand Down Expand Up @@ -95,9 +96,15 @@ var _ = Describe("Git Checkout Task", func() {
cloneError = tasks.Sequence(
tasks.NewFolder(),
tasks.GitInit(),
tasks.GitRemote("origin"),
tasks.NewFile("README.md", []byte("# Test Repo")),
tasks.GitCommit("Initial Commit", "README.md"),
tasks.GitCheckout("master"),
tasks.GitNewRef("refs/remotes/origin/test-branch"),
tasks.GitNewRef("refs/remotes/origin/test-branch2"),
tasks.NewFile("README.md", []byte("# Test Repo\nWith changes")),
tasks.GitCommit("Made changes to README", "README.md"),
tasks.GitNewRef("refs/heads/test-branch2"),
tasks.GitCheckout("master", false),
).ApplyRepo(r)
})

Expand Down Expand Up @@ -131,6 +138,101 @@ var _ = Describe("Git Checkout Task", func() {
Expect(err).ToNot(HaveOccurred())
Expect(branches).To(ContainElement(ref))
})

It("Should have the correct ref", func() {
gr, err := git.PlainOpen(r.Path())
Expect(err).ToNot(HaveOccurred())

head, err := gr.Head()
Expect(err).ToNot(HaveOccurred())

master, err := gr.Reference("refs/heads/master", true)
Expect(err).ToNot(HaveOccurred())

Expect(head.Hash().String()).To(Equal(master.Hash().String()))
})
})

Context("With a branch which exists on origin", func() {
BeforeEach(func() {
ref = "test-branch"
})

It("should not log anything", func() {
Expect(out.GetOperations()).To(BeEmpty())
})

It("Should not return an error", func() {
Expect(err).ToNot(HaveOccurred())
})

It("Should still have the local repo folder", func() {
Expect(r.Exists()).To(BeTrue())
})

It("Should not create a new branch", func() {
branches, err := di.GetMapper().GetBranches(r)
Expect(err).ToNot(HaveOccurred())
Expect(branches).To(ContainElement(ref))
})

It("Should have the correct ref", func() {
gr, err := git.PlainOpen(r.Path())
Expect(err).ToNot(HaveOccurred())

head, err := gr.Head()
Expect(err).ToNot(HaveOccurred())

master, err := gr.Reference("refs/heads/master", true)
Expect(err).ToNot(HaveOccurred())

test, err := gr.Reference("refs/remotes/origin/test-branch", true)
Expect(err).ToNot(HaveOccurred())

Expect(head.Hash().String()).ToNot(Equal(master.Hash().String()))
Expect(head.Hash().String()).To(Equal(test.Hash().String()))
})
})

Context("With a branch which exists both locally and on origin", func() {
BeforeEach(func() {
ref = "test-branch2"
})

It("should not log anything", func() {
Expect(out.GetOperations()).To(BeEmpty())
})

It("Should not return an error", func() {
Expect(err).ToNot(HaveOccurred())
})

It("Should still have the local repo folder", func() {
Expect(r.Exists()).To(BeTrue())
})

It("Should not create a new branch", func() {
branches, err := di.GetMapper().GetBranches(r)
Expect(err).ToNot(HaveOccurred())
Expect(branches).To(ContainElement(ref))
})

It("Should have the correct ref", func() {
gr, err := git.PlainOpen(r.Path())
Expect(err).ToNot(HaveOccurred())

head, err := gr.Head()
Expect(err).ToNot(HaveOccurred())

master, err := gr.Reference("refs/heads/master", true)
Expect(err).ToNot(HaveOccurred())

test, err := gr.Reference("refs/remotes/origin/test-branch2", true)
Expect(err).ToNot(HaveOccurred())

Expect(head.Hash().String()).To(Equal(master.Hash().String()))
Expect(head.Hash().String()).ToNot(Equal(test.Hash().String()))
})
})

Context("With a branch which doesn't exist", func() {
Expand All @@ -155,6 +257,19 @@ var _ = Describe("Git Checkout Task", func() {
Expect(err).ToNot(HaveOccurred())
Expect(branches).To(ContainElement(ref))
})

It("Should have the correct ref", func() {
gr, err := git.PlainOpen(r.Path())
Expect(err).ToNot(HaveOccurred())

head, err := gr.Head()
Expect(err).ToNot(HaveOccurred())

master, err := gr.Reference("refs/heads/master", true)
Expect(err).ToNot(HaveOccurred())

Expect(head.Hash().String()).To(Equal(master.Hash().String()))
})
})
})
})
Expand Down
50 changes: 50 additions & 0 deletions internal/pkg/tasks/gitnewref.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package tasks

import (
"github.com/SierraSoftworks/git-tool/pkg/models"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"gopkg.in/src-d/go-git.v4"
"gopkg.in/src-d/go-git.v4/plumbing"
)

// GitNewRef is responsible for creating a new Git reference which points at HEAD.
func GitNewRef(name string) Task {
return &gitNewRef{
Name: name,
}
}

// gitCommit is responsible for creating a new git reference which points at HEAD.
type gitNewRef struct {
Name string
}

// ApplyRepo runs the task against a repository
func (t *gitNewRef) ApplyRepo(r models.Repo) error {
gr, err := git.PlainOpen(r.Path())
if err != nil {
return errors.Wrap(err, "repo: unable to open git repository")
}

head, err := gr.Head()
if err != nil {
return errors.Wrap(err, "repo: unable to get repo HEAD")
}

ref := plumbing.NewHashReference(plumbing.ReferenceName(t.Name), head.Hash())

logrus.WithField("repo", r).WithField("ref", t.Name).Debugf("Creating new git ref")
err = gr.Storer.SetReference(ref)

if err != nil {
return errors.Wrap(err, "repo: unable to create git ref")
}

return nil
}

// ApplyScratchpad runs the task against a scratchpad
func (t *gitNewRef) ApplyScratchpad(r models.Scratchpad) error {
return nil
}

0 comments on commit 02bf6d8

Please sign in to comment.