Skip to content

Add Merge with ff-only #442

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ type CloneOptions struct {
CABundle []byte
}

// MergeOptions describes how a merge should be erformed
type MergeOptions struct {
// Requires a merge to be fast forward only. If this is true, then a merge will
// throw an error if ff is not possible.
FFOnly bool
}

// Validate validates the fields and sets the default values.
func (o *CloneOptions) Validate() error {
if o.URL == "" {
Expand Down
6 changes: 3 additions & 3 deletions remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -1032,7 +1032,7 @@ func checkFastForwardUpdate(s storer.EncodedObjectStorer, remoteRefs storer.Refe
return fmt.Errorf("non-fast-forward update: %s", cmd.Name.String())
}

ff, err := isFastForward(s, cmd.Old, cmd.New)
ff, err := IsFastForward(s, cmd.Old, cmd.New)
if err != nil {
return err
}
Expand All @@ -1044,7 +1044,7 @@ func checkFastForwardUpdate(s storer.EncodedObjectStorer, remoteRefs storer.Refe
return nil
}

func isFastForward(s storer.EncodedObjectStorer, old, new plumbing.Hash) (bool, error) {
func IsFastForward(s storer.EncodedObjectStorer, old, new plumbing.Hash) (bool, error) {
c, err := object.GetCommit(s, new)
if err != nil {
return false, err
Expand Down Expand Up @@ -1166,7 +1166,7 @@ func (r *Remote) updateLocalReferenceStorage(
// If the ref exists locally as a branch and force is not specified,
// only update if the new ref is an ancestor of the old
if old != nil && old.Name().IsBranch() && !force && !spec.IsForceUpdate() {
ff, err := isFastForward(r.s, old.Hash(), new.Hash())
ff, err := IsFastForward(r.s, old.Hash(), new.Hash())
if err != nil {
return updated, err
}
Expand Down
19 changes: 19 additions & 0 deletions repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -1642,6 +1642,25 @@ func (r *Repository) RepackObjects(cfg *RepackConfig) (err error) {
return nil
}

// Merge attempts to merge ref onto HEAD. Currently only supports fast-forward merges
func (r *Repository) Merge(ref plumbing.Reference, opts MergeOptions) error {
if !opts.FFOnly {
return errors.New("non fast-forward merges are not supported yet")
}

head, err := r.Head()
if err != nil {
return err
}

ff, err := IsFastForward(r.Storer, head.Hash(), ref.Hash())
if !ff {
return errors.New("fast forward is not possible")
}

return r.Storer.SetReference(plumbing.NewHashReference(head.Name(), ref.Hash()))
}

// createNewObjectPack is a helper for RepackObjects taking care
// of creating a new pack. It is used so the the PackfileWriter
// deferred close has the right scope.
Expand Down
53 changes: 53 additions & 0 deletions repository_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,59 @@ func (s *RepositorySuite) TestCreateBranchAndBranch(c *C) {
c.Assert(branch.Merge, Equals, testBranch.Merge)
}

func (s *RepositorySuite) TestMergeFF(c *C) {
r, _ := Init(memory.NewStorage(), memfs.New())
err := r.clone(context.Background(), &CloneOptions{
URL: s.GetBasicLocalRepositoryURL(),
})
c.Assert(err, IsNil)
head, err := r.Head()
c.Assert(err, IsNil)

mergeBranchRefname := plumbing.NewBranchReferenceName("foo")
err = r.Storer.SetReference(plumbing.NewHashReference(mergeBranchRefname, head.Hash()))
c.Assert(err, IsNil)

commit, err := r.CommitObject(head.Hash())
c.Assert(err, IsNil)
treeHash := commit.TreeHash

hash := commit.Hash

for i := 0; i < 10; i++ {
commit = &object.Commit{
Author: object.Signature{
Name: "A U Thor",
Email: "author@example.com",
},
Committer: object.Signature{
Name: "A U Thor",
Email: "author@example.com",
},
Message: fmt.Sprintf("commit #%d", i),
TreeHash: treeHash,
ParentHashes: []plumbing.Hash{
hash,
},
}

o := r.Storer.NewEncodedObject()
c.Assert(commit.Encode(o), IsNil)
hash, err = r.Storer.SetEncodedObject(o)
}

mergeBranchRef := plumbing.NewHashReference(mergeBranchRefname, hash)
c.Assert(r.Storer.SetReference(mergeBranchRef), IsNil)

err = r.Merge(*mergeBranchRef, MergeOptions{
FFOnly: true,
})
c.Assert(err, IsNil)

head, err = r.Head()
c.Assert(head.Hash(), Equals, mergeBranchRef.Hash())
}

func (s *RepositorySuite) TestCreateBranchUnmarshal(c *C) {
r, _ := Init(memory.NewStorage(), nil)

Expand Down
4 changes: 2 additions & 2 deletions worktree.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ func (w *Worktree) PullContext(ctx context.Context, o *PullOptions) error {

head, err := w.r.Head()
if err == nil {
headAheadOfRef, err := isFastForward(w.r.Storer, ref.Hash(), head.Hash())
headAheadOfRef, err := IsFastForward(w.r.Storer, ref.Hash(), head.Hash())
if err != nil {
return err
}
Expand All @@ -104,7 +104,7 @@ func (w *Worktree) PullContext(ctx context.Context, o *PullOptions) error {
return NoErrAlreadyUpToDate
}

ff, err := isFastForward(w.r.Storer, head.Hash(), ref.Hash())
ff, err := IsFastForward(w.r.Storer, head.Hash(), ref.Hash())
if err != nil {
return err
}
Expand Down