aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--COMPATIBILITY.md6
-rw-r--r--worktree.go49
-rw-r--r--worktree_test.go60
3 files changed, 109 insertions, 6 deletions
diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md
index 948eb14..fa998bf 100644
--- a/COMPATIBILITY.md
+++ b/COMPATIBILITY.md
@@ -10,7 +10,7 @@ is supported by go-git.
| config | ✔ | Reading and modifying per-repository configuration (`.git/config`) is supported. Global configuration (`$HOME/.gitconfig`) is not. |
| **getting and creating repositories** |
| init | ✔ | Plain init and `--bare` are supported. Flags `--template`, `--separate-git-dir` and `--shared` are not. |
-| clone | ✔ | Plain clone and equivalents to `--progress`, `--single-branch`, `--depth`, `--origin`, `--recurse-submodules` are supported. Others are not. |
+| clone | ✔ | Plain clone and equivalents to `--progress`, `--single-branch`, `--depth`, `--origin`, `--recurse-submodules` are supported. Others are not. |
| **basic snapshotting** |
| add | ✔ | Plain add is supported. Any other flag aren't supported |
| status | ✔ |
@@ -27,7 +27,7 @@ is supported by go-git.
| tag | ✔ |
| **sharing and updating projects** |
| fetch | ✔ |
-| pull | ✔ |
+| pull | ✔ | Only supports merges where the merge can be resolved as a fast-forward. |
| push | ✔ |
| remote | ✔ |
| submodule | ✔ |
@@ -108,4 +108,4 @@ is supported by go-git.
| gitattributes | ✖ |
| index version | |
| packfile version | |
-| push-certs | ✖ | \ No newline at end of file
+| push-certs | ✖ |
diff --git a/worktree.go b/worktree.go
index 5768888..01f8b01 100644
--- a/worktree.go
+++ b/worktree.go
@@ -15,6 +15,7 @@ import (
"gopkg.in/src-d/go-git.v4/plumbing/filemode"
"gopkg.in/src-d/go-git.v4/plumbing/format/index"
"gopkg.in/src-d/go-git.v4/plumbing/object"
+ "gopkg.in/src-d/go-git.v4/plumbing/storer"
"gopkg.in/src-d/go-git.v4/utils/ioutil"
"gopkg.in/src-d/go-git.v4/utils/merkletrie"
@@ -36,6 +37,8 @@ type Worktree struct {
// Pull incorporates changes from a remote repository into the current branch.
// Returns nil if the operation is successful, NoErrAlreadyUpToDate if there are
// no changes to be fetched, or an error.
+//
+// Pull only supports merges where the can be resolved as a fast-forward.
func (w *Worktree) Pull(o *PullOptions) error {
return w.PullContext(context.Background(), o)
}
@@ -44,6 +47,8 @@ func (w *Worktree) Pull(o *PullOptions) error {
// branch. Returns nil if the operation is successful, NoErrAlreadyUpToDate if
// there are no changes to be fetched, or an error.
//
+// Pull only supports merges where the can be resolved as a fast-forward.
+//
// The provided Context must be non-nil. If the context expires before the
// operation is complete, an error is returned. The context only affects to the
// transport operations.
@@ -52,17 +57,55 @@ func (w *Worktree) PullContext(ctx context.Context, o *PullOptions) error {
return err
}
- head, err := w.r.fetchAndUpdateReferences(ctx, &FetchOptions{
+ remote, err := w.r.Remote(o.RemoteName)
+ if err != nil {
+ return err
+ }
+
+ fetchHead, err := remote.fetch(ctx, &FetchOptions{
RemoteName: o.RemoteName,
Depth: o.Depth,
Auth: o.Auth,
Progress: o.Progress,
- }, o.ReferenceName)
+ })
+
+ updated := true
+ if err == NoErrAlreadyUpToDate {
+ updated = false
+ } else if err != nil {
+ return err
+ }
+
+ ref, err := storer.ResolveReference(fetchHead, o.ReferenceName)
if err != nil {
return err
}
- if err := w.Reset(&ResetOptions{Commit: head.Hash()}); err != nil {
+ head, err := w.r.Head()
+ if err == nil {
+ if !updated && head.Hash() == ref.Hash() {
+ return NoErrAlreadyUpToDate
+ }
+
+ ff, err := isFastForward(w.r.Storer, head.Hash(), ref.Hash())
+ if err != nil {
+ return err
+ }
+
+ if !ff {
+ return fmt.Errorf("non-fast-forward update")
+ }
+ }
+
+ if err != nil && err != plumbing.ErrReferenceNotFound {
+ return err
+ }
+
+ if err := w.updateHEAD(ref.Hash()); err != nil {
+ return err
+ }
+
+ if err := w.Reset(&ResetOptions{Commit: ref.Hash()}); err != nil {
return err
}
diff --git a/worktree_test.go b/worktree_test.go
index 150176f..c565e29 100644
--- a/worktree_test.go
+++ b/worktree_test.go
@@ -51,6 +51,66 @@ func (s *WorktreeSuite) TestPullCheckout(c *C) {
c.Assert(fi, HasLen, 8)
}
+func (s *WorktreeSuite) TestPullFastForward(c *C) {
+ url := c.MkDir()
+ path := fixtures.Basic().ByTag("worktree").One().Worktree().Root()
+
+ server, err := PlainClone(url, false, &CloneOptions{
+ URL: path,
+ })
+
+ r, err := PlainClone(c.MkDir(), false, &CloneOptions{
+ URL: url,
+ })
+
+ w, err := server.Worktree()
+ c.Assert(err, IsNil)
+ err = ioutil.WriteFile(filepath.Join(path, "foo"), []byte("foo"), 0755)
+ c.Assert(err, IsNil)
+ hash, err := w.Commit("foo", &CommitOptions{Author: defaultSignature()})
+ c.Assert(err, IsNil)
+
+ w, err = r.Worktree()
+ c.Assert(err, IsNil)
+
+ err = w.Pull(&PullOptions{})
+ c.Assert(err, IsNil)
+
+ head, err := r.Head()
+ c.Assert(err, IsNil)
+ c.Assert(head.Hash(), Equals, hash)
+}
+
+func (s *WorktreeSuite) TestPullNonFastForward(c *C) {
+ url := c.MkDir()
+ path := fixtures.Basic().ByTag("worktree").One().Worktree().Root()
+
+ server, err := PlainClone(url, false, &CloneOptions{
+ URL: path,
+ })
+
+ r, err := PlainClone(c.MkDir(), false, &CloneOptions{
+ URL: url,
+ })
+
+ w, err := server.Worktree()
+ c.Assert(err, IsNil)
+ err = ioutil.WriteFile(filepath.Join(path, "foo"), []byte("foo"), 0755)
+ c.Assert(err, IsNil)
+ _, err = w.Commit("foo", &CommitOptions{Author: defaultSignature()})
+ c.Assert(err, IsNil)
+
+ w, err = r.Worktree()
+ c.Assert(err, IsNil)
+ err = ioutil.WriteFile(filepath.Join(path, "bar"), []byte("bar"), 0755)
+ c.Assert(err, IsNil)
+ _, err = w.Commit("bar", &CommitOptions{Author: defaultSignature()})
+ c.Assert(err, IsNil)
+
+ err = w.Pull(&PullOptions{})
+ c.Assert(err, ErrorMatches, "non-fast-forward update")
+}
+
func (s *WorktreeSuite) TestPullUpdateReferencesIfNeeded(c *C) {
r, _ := Init(memory.NewStorage(), memfs.New())
r.CreateRemote(&config.RemoteConfig{