From c78b4d665e406af7e8cba608f65ac2171d3917b6 Mon Sep 17 00:00:00 2001 From: Mark Probst Date: Wed, 4 Mar 2015 11:38:19 -0800 Subject: [PATCH] Cherrypick --- checkout.go | 13 +++++++++ cherrypick.go | 73 ++++++++++++++++++++++++++++++++++++++++++++++ cherrypick_test.go | 73 ++++++++++++++++++++++++++++++++++++++++++++++ git_test.go | 6 +++- 4 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 cherrypick.go create mode 100644 cherrypick_test.go diff --git a/checkout.go b/checkout.go index 72a1fe3..6eb6098 100644 --- a/checkout.go +++ b/checkout.go @@ -40,6 +40,19 @@ type CheckoutOpts struct { TargetDirectory string // Alternative checkout path to workdir } +func checkoutOptionsFromC(c *C.git_checkout_options) CheckoutOpts { + opts := CheckoutOpts{} + opts.Strategy = CheckoutStrategy(c.checkout_strategy) + opts.DisableFilters = c.disable_filters != 0 + opts.DirMode = os.FileMode(c.dir_mode) + opts.FileMode = os.FileMode(c.file_mode) + opts.FileOpenFlags = int(c.file_open_flags) + if c.target_directory != nil { + opts.TargetDirectory = C.GoString(c.target_directory) + } + return opts +} + func (opts *CheckoutOpts) toC() *C.git_checkout_options { if opts == nil { return nil diff --git a/cherrypick.go b/cherrypick.go new file mode 100644 index 0000000..afc1b7e --- /dev/null +++ b/cherrypick.go @@ -0,0 +1,73 @@ +package git + +/* +#include +*/ +import "C" +import ( + "runtime" +) + +type CherrypickOptions struct { + Version uint + Mainline uint + MergeOpts MergeOptions + CheckoutOpts CheckoutOpts +} + +func cherrypickOptionsFromC(c *C.git_cherrypick_options) CherrypickOptions { + opts := CherrypickOptions{ + Version: uint(c.version), + Mainline: uint(c.mainline), + MergeOpts: mergeOptionsFromC(&c.merge_opts), + CheckoutOpts: checkoutOptionsFromC(&c.checkout_opts), + } + return opts +} + +func (opts *CherrypickOptions) toC() *C.git_cherrypick_options { + if opts == nil { + return nil + } + c := C.git_cherrypick_options{} + c.version = C.uint(opts.Version) + c.mainline = C.uint(opts.Mainline) + c.merge_opts = *opts.MergeOpts.toC() + c.checkout_opts = *opts.CheckoutOpts.toC() + return &c +} + +func freeCherrypickOpts(ptr *C.git_cherrypick_options) { + if ptr == nil { + return + } + freeCheckoutOpts(&ptr.checkout_opts) +} + +func DefaultCherrypickOptions() (CherrypickOptions, error) { + c := C.git_cherrypick_options{} + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ecode := C.git_cherrypick_init_options(&c, C.GIT_CHERRYPICK_OPTIONS_VERSION) + if ecode < 0 { + return CherrypickOptions{}, MakeGitError(ecode) + } + defer freeCherrypickOpts(&c) + return cherrypickOptionsFromC(&c), nil +} + +func (v *Repository) Cherrypick(commit *Commit, opts CherrypickOptions) error { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + cOpts := opts.toC() + defer freeCherrypickOpts(cOpts) + + ecode := C.git_cherrypick(v.ptr, commit.cast_ptr, cOpts) + if ecode < 0 { + return MakeGitError(ecode) + } + return nil +} diff --git a/cherrypick_test.go b/cherrypick_test.go new file mode 100644 index 0000000..c867cf9 --- /dev/null +++ b/cherrypick_test.go @@ -0,0 +1,73 @@ +package git + +import ( + "io/ioutil" + "testing" +) + +func checkout(t *testing.T, repo *Repository, commit *Commit) { + tree, err := commit.Tree() + if err != nil { + t.Fatal(err) + } + + err = repo.CheckoutTree(tree, &CheckoutOpts{Strategy: CheckoutSafe}) + if err != nil { + t.Fatal(err) + } + + err = repo.SetHeadDetached(commit.Id(), commit.Author(), "checkout") + if err != nil { + t.Fatal(err) + } +} + +const content = "Herro, Worrd!" + +func readReadme(t *testing.T, repo *Repository) string { + bytes, err := ioutil.ReadFile(pathInRepo(repo, "README")) + if err != nil { + t.Fatal(err) + } + return string(bytes) +} + +func TestCherrypick(t *testing.T) { + repo := createTestRepo(t) + c1, _ := seedTestRepo(t, repo) + c2, _ := updateReadme(t, repo, content) + + commit1, err := repo.LookupCommit(c1) + if err != nil { + t.Fatal(err) + } + commit2, err := repo.LookupCommit(c2) + if err != nil { + t.Fatal(err) + } + + checkout(t, repo, commit1) + + if readReadme(t, repo) == content { + t.Fatalf("README has wrong content after checking out initial commit") + } + + opts, err := DefaultCherrypickOptions() + if err != nil { + t.Fatal(err) + } + + err = repo.Cherrypick(commit2, opts) + if err != nil { + t.Fatal(err) + } + + if readReadme(t, repo) != content { + t.Fatalf("README has wrong contents after cherry-picking") + } + + state := repo.State() + if state != RepositoryStateCherrypick { + t.Fatal("Incorrect repository state: ", state) + } +} diff --git a/git_test.go b/git_test.go index 56adeed..b9cf0a9 100644 --- a/git_test.go +++ b/git_test.go @@ -57,6 +57,10 @@ func seedTestRepo(t *testing.T, repo *Repository) (*Oid, *Oid) { return commitId, treeId } +func pathInRepo(repo *Repository, name string) string { + return path.Join(path.Dir(path.Dir(repo.Path())), name) +} + func updateReadme(t *testing.T, repo *Repository, content string) (*Oid, *Oid) { loc, err := time.LoadLocation("Europe/Berlin") checkFatal(t, err) @@ -67,7 +71,7 @@ func updateReadme(t *testing.T, repo *Repository, content string) (*Oid, *Oid) { } tmpfile := "README" - err = ioutil.WriteFile(path.Join(path.Dir(path.Dir(repo.Path())), tmpfile), []byte(content), 0644) + err = ioutil.WriteFile(pathInRepo(repo, tmpfile), []byte(content), 0644) checkFatal(t, err) idx, err := repo.Index()