diff options
-rw-r--r-- | _examples/README.md | 1 | ||||
-rw-r--r-- | _examples/submodule/main.go | 55 | ||||
-rw-r--r-- | options.go | 3 | ||||
-rw-r--r-- | plumbing/format/diff/unified_encoder.go | 79 | ||||
-rw-r--r-- | plumbing/format/diff/unified_encoder_test.go | 22 | ||||
-rw-r--r-- | plumbing/object/commit.go | 9 | ||||
-rw-r--r-- | plumbing/object/commit_test.go | 11 | ||||
-rw-r--r-- | plumbing/object/tree_test.go | 11 | ||||
-rw-r--r-- | remote.go | 9 | ||||
-rw-r--r-- | remote_test.go | 28 |
10 files changed, 187 insertions, 41 deletions
diff --git a/_examples/README.md b/_examples/README.md index cf9c2d3..6c17941 100644 --- a/_examples/README.md +++ b/_examples/README.md @@ -20,6 +20,7 @@ Here you can find a list of annotated _go-git_ examples: - [remotes](remotes/main.go) - Working with remotes: adding, removing, etc - [progress](progress/main.go) - Printing the progress information from the sideband - [revision](revision/main.go) - Solve a revision into a commit +- [submodule](submodule/main.go) - Submodule update remote ### Advanced - [custom_http](custom_http/main.go) - Replacing the HTTP client using a custom one diff --git a/_examples/submodule/main.go b/_examples/submodule/main.go new file mode 100644 index 0000000..1a76193 --- /dev/null +++ b/_examples/submodule/main.go @@ -0,0 +1,55 @@ +package main + +import ( + "os" + + "github.com/go-git/go-git/v5" + . "github.com/go-git/go-git/v5/_examples" +) + +// Basic example of how to clone a repository including a submodule and +// updating submodule ref +func main() { + CheckArgs("<url>", "<directory>", "<submodule>") + url := os.Args[1] + directory := os.Args[2] + submodule := os.Args[3] + + // Clone the given repository to the given directory + Info("git clone %s %s --recursive", url, directory) + + r, err := git.PlainClone(directory, false, &git.CloneOptions{ + URL: url, + RecurseSubmodules: git.DefaultSubmoduleRecursionDepth, + }) + + CheckIfError(err) + + w, err := r.Worktree() + if err != nil { + CheckIfError(err) + } + + sub, err := w.Submodule(submodule) + if err != nil { + CheckIfError(err) + } + + sr, err := sub.Repository() + if err != nil { + CheckIfError(err) + } + + sw, err := sr.Worktree() + if err != nil { + CheckIfError(err) + } + + Info("git submodule update --remote") + err = sw.Pull(&git.PullOptions{ + RemoteName: "origin", + }) + if err != nil { + CheckIfError(err) + } +} @@ -190,6 +190,9 @@ type PushOptions struct { // Prune specify that remote refs that match given RefSpecs and that do // not exist locally will be removed. Prune bool + // Force allows the push to update a remote branch even when the local + // branch does not descend from it. + Force bool } // Validate validates the fields and sets the default values. diff --git a/plumbing/format/diff/unified_encoder.go b/plumbing/format/diff/unified_encoder.go index 19c3f0b..413984a 100644 --- a/plumbing/format/diff/unified_encoder.go +++ b/plumbing/format/diff/unified_encoder.go @@ -86,48 +86,73 @@ func (e *UnifiedEncoder) writeFilePatchHeader(sb *strings.Builder, filePatch Fil } isBinary := filePatch.IsBinary() - sb.WriteString(e.color[Meta]) + var lines []string switch { case from != nil && to != nil: hashEquals := from.Hash() == to.Hash() - fmt.Fprintf(sb, "diff --git a/%s b/%s\n", from.Path(), to.Path()) + lines = append(lines, + fmt.Sprintf("diff --git a/%s b/%s", from.Path(), to.Path()), + ) if from.Mode() != to.Mode() { - fmt.Fprintf(sb, "old mode %o\n", from.Mode()) - fmt.Fprintf(sb, "new mode %o\n", to.Mode()) + lines = append(lines, + fmt.Sprintf("old mode %o", from.Mode()), + fmt.Sprintf("new mode %o", to.Mode()), + ) } if from.Path() != to.Path() { - fmt.Fprintf(sb, "rename from %s\n", from.Path()) - fmt.Fprintf(sb, "rename to %s\n", to.Path()) + lines = append(lines, + fmt.Sprintf("rename from %s", from.Path()), + fmt.Sprintf("rename to %s", to.Path()), + ) } if from.Mode() != to.Mode() && !hashEquals { - fmt.Fprintf(sb, "index %s..%s\n", from.Hash(), to.Hash()) + lines = append(lines, + fmt.Sprintf("index %s..%s", from.Hash(), to.Hash()), + ) } else if !hashEquals { - fmt.Fprintf(sb, "index %s..%s %o\n", from.Hash(), to.Hash(), from.Mode()) + lines = append(lines, + fmt.Sprintf("index %s..%s %o", from.Hash(), to.Hash(), from.Mode()), + ) } if !hashEquals { - e.writePathLines(sb, "a/"+from.Path(), "b/"+to.Path(), isBinary) + lines = e.appendPathLines(lines, "a/"+from.Path(), "b/"+to.Path(), isBinary) } case from == nil: - fmt.Fprintf(sb, "diff --git a/%s b/%s\n", to.Path(), to.Path()) - fmt.Fprintf(sb, "new file mode %o\n", to.Mode()) - fmt.Fprintf(sb, "index %s..%s\n", plumbing.ZeroHash, to.Hash()) - e.writePathLines(sb, "/dev/null", "b/"+to.Path(), isBinary) + lines = append(lines, + fmt.Sprintf("diff --git a/%s b/%s", to.Path(), to.Path()), + fmt.Sprintf("new file mode %o", to.Mode()), + fmt.Sprintf("index %s..%s", plumbing.ZeroHash, to.Hash()), + ) + lines = e.appendPathLines(lines, "/dev/null", "b/"+to.Path(), isBinary) case to == nil: - fmt.Fprintf(sb, "diff --git a/%s b/%s\n", from.Path(), from.Path()) - fmt.Fprintf(sb, "deleted file mode %o\n", from.Mode()) - fmt.Fprintf(sb, "index %s..%s\n", from.Hash(), plumbing.ZeroHash) - e.writePathLines(sb, "a/"+from.Path(), "/dev/null", isBinary) + lines = append(lines, + fmt.Sprintf("diff --git a/%s b/%s", from.Path(), from.Path()), + fmt.Sprintf("deleted file mode %o", from.Mode()), + fmt.Sprintf("index %s..%s", from.Hash(), plumbing.ZeroHash), + ) + lines = e.appendPathLines(lines, "a/"+from.Path(), "/dev/null", isBinary) + } + + sb.WriteString(e.color[Meta]) + sb.WriteString(lines[0]) + for _, line := range lines[1:] { + sb.WriteByte('\n') + sb.WriteString(line) } sb.WriteString(e.color.Reset(Meta)) + sb.WriteByte('\n') } -func (e *UnifiedEncoder) writePathLines(sb *strings.Builder, fromPath, toPath string, isBinary bool) { +func (e *UnifiedEncoder) appendPathLines(lines []string, fromPath, toPath string, isBinary bool) []string { if isBinary { - fmt.Fprintf(sb, "Binary files %s and %s differ\n", fromPath, toPath) - } else { - fmt.Fprintf(sb, "--- %s\n", fromPath) - fmt.Fprintf(sb, "+++ %s\n", toPath) + return append(lines, + fmt.Sprintf("Binary files %s and %s differ", fromPath, toPath), + ) } + return append(lines, + fmt.Sprintf("--- %s", fromPath), + fmt.Sprintf("+++ %s", toPath), + ) } type hunksGenerator struct { @@ -341,9 +366,11 @@ func (o *op) writeTo(sb *strings.Builder, color ColorConfig) { colorKey := operationColorKey[o.t] sb.WriteString(color[colorKey]) sb.WriteByte(operationChar[o.t]) - sb.WriteString(o.text) - sb.WriteString(color.Reset(colorKey)) - if !strings.HasSuffix(o.text, "\n") { - sb.WriteString("\n\\ No newline at end of file\n") + if strings.HasSuffix(o.text, "\n") { + sb.WriteString(strings.TrimSuffix(o.text, "\n")) + } else { + sb.WriteString(o.text + "\n\\ No newline at end of file") } + sb.WriteString(color.Reset(colorKey)) + sb.WriteByte('\n') } diff --git a/plumbing/format/diff/unified_encoder_test.go b/plumbing/format/diff/unified_encoder_test.go index 1e44572..22dc4f1 100644 --- a/plumbing/format/diff/unified_encoder_test.go +++ b/plumbing/format/diff/unified_encoder_test.go @@ -894,11 +894,11 @@ index 0adddcde4fd38042c354518351820eb06c417c82..d39ae38aad7ba9447b5e7998b2e4714f color.Bold + "diff --git a/README.md b/README.md\n" + "index 94954abda49de8615a048f8d2e64b5de848e27a1..f3dad9514629b9ff9136283ae331ad1fc95748a8 100644\n" + "--- a/README.md\n" + - "+++ b/README.md\n" + color.Reset + + "+++ b/README.md" + color.Reset + "\n" + color.Cyan + "@@ -1,2 +1,2 @@" + color.Reset + "\n" + " hello\n" + - color.Red + "-world\n" + color.Reset + - color.Green + "+bug\n" + color.Reset, + color.Red + "-world" + color.Reset + "\n" + + color.Green + "+bug" + color.Reset + "\n", }, { patch: testPatch{ message: "", @@ -933,10 +933,10 @@ index 0adddcde4fd38042c354518351820eb06c417c82..d39ae38aad7ba9447b5e7998b2e4714f color.Bold + "diff --git a/test.txt b/test.txt\n" + "index 9daeafb9864cf43055ae93beb0afd6c7d144bfa4..180cf8328022becee9aaa2577a8f84ea2b9f3827 100644\n" + "--- a/test.txt\n" + - "+++ b/test.txt\n" + color.Reset + + "+++ b/test.txt" + color.Reset + "\n" + color.Cyan + "@@ -1 +1 @@" + color.Reset + "\n" + - color.Red + "-test\n" + color.Reset + - color.Green + "+test2\n" + color.Reset, + color.Red + "-test" + color.Reset + "\n" + + color.Green + "+test2" + color.Reset + "\n", }, { patch: oneChunkPatch, desc: "modified deleting lines file with context to 1 with color", @@ -948,21 +948,21 @@ index 0adddcde4fd38042c354518351820eb06c417c82..d39ae38aad7ba9447b5e7998b2e4714f color.Bold + "diff --git a/onechunk.txt b/onechunk.txt\n" + "index ab5eed5d4a2c33aeef67e0188ee79bed666bde6f..0adddcde4fd38042c354518351820eb06c417c82 100644\n" + "--- a/onechunk.txt\n" + - "+++ b/onechunk.txt\n" + color.Reset + + "+++ b/onechunk.txt" + color.Reset + "\n" + color.Cyan + "@@ -1,2 +1 @@" + color.Reset + "\n" + - color.Red + "-A\n" + color.Reset + + color.Red + "-A" + color.Reset + "\n" + " B\n" + color.Cyan + "@@ -7,3 +6,2 @@" + color.Reset + " " + color.Reverse + "F" + color.Reset + "\n" + " G\n" + - color.Red + "-H\n" + color.Reset + + color.Red + "-H" + color.Reset + "\n" + " I\n" + color.Cyan + "@@ -14,3 +12,2 @@" + color.Reset + " " + color.Reverse + "M" + color.Reset + "\n" + " N\n" + - color.Red + "-Ñ\n" + color.Reset + + color.Red + "-Ñ" + color.Reset + "\n" + " O\n" + color.Cyan + "@@ -21,3 +18,2 @@" + color.Reset + " " + color.Reverse + "S" + color.Reset + "\n" + " T\n" + - color.Red + "-U\n" + color.Reset + + color.Red + "-U" + color.Reset + "\n" + " V\n", }} diff --git a/plumbing/object/commit.go b/plumbing/object/commit.go index b27f20b..113cb29 100644 --- a/plumbing/object/commit.go +++ b/plumbing/object/commit.go @@ -87,9 +87,12 @@ func (c *Commit) PatchContext(ctx context.Context, to *Commit) (*Patch, error) { return nil, err } - toTree, err := to.Tree() - if err != nil { - return nil, err + var toTree *Tree + if to != nil { + toTree, err = to.Tree() + if err != nil { + return nil, err + } } return fromTree.PatchContext(ctx, toTree) diff --git a/plumbing/object/commit_test.go b/plumbing/object/commit_test.go index 28a7a81..e260a7f 100644 --- a/plumbing/object/commit_test.go +++ b/plumbing/object/commit_test.go @@ -12,8 +12,8 @@ import ( "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/cache" - . "gopkg.in/check.v1" "github.com/go-git/go-git/v5/storage/filesystem" + . "gopkg.in/check.v1" ) type SuiteCommit struct { @@ -188,6 +188,15 @@ Binary files /dev/null and b/binary.jpg differ c.Assert(buf.String(), Equals, patch.String()) } +func (s *SuiteCommit) TestPatchContext_ToNil(c *C) { + from := s.commit(c, plumbing.NewHash("918c48b83bd081e863dbe1b80f8998f058cd8294")) + + patch, err := from.PatchContext(context.Background(), nil) + c.Assert(err, IsNil) + + c.Assert(len(patch.String()), Equals, 242679) +} + func (s *SuiteCommit) TestCommitEncodeDecodeIdempotent(c *C) { ts, err := time.Parse(time.RFC3339, "2006-01-02T15:04:05-07:00") c.Assert(err, IsNil) diff --git a/plumbing/object/tree_test.go b/plumbing/object/tree_test.go index 474bb6a..d9dad47 100644 --- a/plumbing/object/tree_test.go +++ b/plumbing/object/tree_test.go @@ -377,6 +377,17 @@ func (s *TreeSuite) TestTreeWalkerNextNonRecursive(c *C) { c.Assert(count, Equals, 8) } +func (s *TreeSuite) TestPatchContext_ToNil(c *C) { + commit := s.commit(c, plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5")) + tree, err := commit.Tree() + c.Assert(err, IsNil) + + patch, err := tree.PatchContext(context.Background(), nil) + c.Assert(err, IsNil) + + c.Assert(len(patch.String()), Equals, 242971) +} + func (s *TreeSuite) TestTreeWalkerNextSubmodule(c *C) { dotgit := fixtures.ByURL("https://github.com/git-fixtures/submodule.git").One().DotGit() st := filesystem.NewStorage(dotgit, cache.NewObjectLRUDefault()) @@ -123,6 +123,15 @@ func (r *Remote) PushContext(ctx context.Context, o *PushOptions) (err error) { return ErrDeleteRefNotSupported } + if o.Force { + for i := 0; i < len(o.RefSpecs); i++ { + rs := &o.RefSpecs[i] + if !rs.IsForceUpdate() { + o.RefSpecs[i] = config.RefSpec("+" + rs.String()) + } + } + } + localRefs, err := r.references() if err != nil { return err diff --git a/remote_test.go b/remote_test.go index 0fc3449..ce46390 100644 --- a/remote_test.go +++ b/remote_test.go @@ -612,6 +612,34 @@ func (s *RemoteSuite) TestPushForce(c *C) { c.Assert(newRef, Not(DeepEquals), oldRef) } +func (s *RemoteSuite) TestPushForceWithOption(c *C) { + f := fixtures.Basic().One() + sto := filesystem.NewStorage(f.DotGit(), cache.NewObjectLRUDefault()) + + dstFs := f.DotGit() + dstSto := filesystem.NewStorage(dstFs, cache.NewObjectLRUDefault()) + + url := dstFs.Root() + r := NewRemote(sto, &config.RemoteConfig{ + Name: DefaultRemoteName, + URLs: []string{url}, + }) + + oldRef, err := dstSto.Reference(plumbing.ReferenceName("refs/heads/branch")) + c.Assert(err, IsNil) + c.Assert(oldRef, NotNil) + + err = r.Push(&PushOptions{ + RefSpecs: []config.RefSpec{"refs/heads/master:refs/heads/branch"}, + Force: true, + }) + c.Assert(err, IsNil) + + newRef, err := dstSto.Reference(plumbing.ReferenceName("refs/heads/branch")) + c.Assert(err, IsNil) + c.Assert(newRef, Not(DeepEquals), oldRef) +} + func (s *RemoteSuite) TestPushPrune(c *C) { fs := fixtures.Basic().One().DotGit() url := c.MkDir() |