Skip to content

Commit

Permalink
Merge pull request go-git#735 from aymanbagabas/clone-mirror
Browse files Browse the repository at this point in the history
git: add mirror clone option
  • Loading branch information
pjbgf committed Apr 17, 2023
2 parents 7cd387b + 9a5b08f commit 0a1c5ab
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 4 deletions.
8 changes: 8 additions & 0 deletions config/config.go
Expand Up @@ -264,6 +264,7 @@ const (
defaultBranchKey = "defaultBranch"
repositoryFormatVersionKey = "repositoryformatversion"
objectFormat = "objectformat"
mirrorKey = "mirror"

// DefaultPackWindow holds the number of previous objects used to
// generate deltas. The value 10 is the same used by git command.
Expand Down Expand Up @@ -578,6 +579,8 @@ type RemoteConfig struct {
// URLs the URLs of a remote repository. It must be non-empty. Fetch will
// always use the first URL, while push will use all of them.
URLs []string
// Mirror indicates that the repository is a mirror of remote.
Mirror bool

// insteadOfRulesApplied have urls been modified
insteadOfRulesApplied bool
Expand Down Expand Up @@ -631,6 +634,7 @@ func (c *RemoteConfig) unmarshal(s *format.Subsection) error {
c.Name = c.raw.Name
c.URLs = append([]string(nil), c.raw.Options.GetAll(urlKey)...)
c.Fetch = fetch
c.Mirror = c.raw.Options.Get(mirrorKey) == "true"

return nil
}
Expand Down Expand Up @@ -663,6 +667,10 @@ func (c *RemoteConfig) marshal() *format.Subsection {
c.raw.SetOption(fetchKey, values...)
}

if c.Mirror {
c.raw.SetOption(mirrorKey, strconv.FormatBool(c.Mirror))
}

return c.raw
}

Expand Down
8 changes: 8 additions & 0 deletions options.go
Expand Up @@ -46,6 +46,14 @@ type CloneOptions struct {
ReferenceName plumbing.ReferenceName
// Fetch only ReferenceName if true.
SingleBranch bool
// Mirror clones the repository as a mirror.
//
// Compared to a bare clone, mirror not only maps local branches of the
// source to local branches of the target, it maps all refs (including
// remote-tracking branches, notes etc.) and sets up a refspec configuration
// such that all these refs are overwritten by a git remote update in the
// target repository.
Mirror bool
// No checkout of HEAD after clone if true.
NoCheckout bool
// Limit fetching to the specified number of commits.
Expand Down
14 changes: 10 additions & 4 deletions repository.go
Expand Up @@ -444,6 +444,9 @@ func PlainCloneContext(ctx context.Context, path string, isBare bool, o *CloneOp
return nil, err
}

if o.Mirror {
isBare = true
}
r, err := PlainInit(path, isBare)
if err != nil {
return nil, err
Expand Down Expand Up @@ -851,9 +854,10 @@ func (r *Repository) clone(ctx context.Context, o *CloneOptions) error {
}

c := &config.RemoteConfig{
Name: o.RemoteName,
URLs: []string{o.URL},
Fetch: r.cloneRefSpec(o),
Name: o.RemoteName,
URLs: []string{o.URL},
Fetch: r.cloneRefSpec(o),
Mirror: o.Mirror,
}

if _, err := r.CreateRemote(c); err != nil {
Expand Down Expand Up @@ -906,7 +910,7 @@ func (r *Repository) clone(ctx context.Context, o *CloneOptions) error {
return err
}

if ref.Name().IsBranch() {
if !o.Mirror && ref.Name().IsBranch() {
branchRef := ref.Name()
branchName := strings.Split(string(branchRef), "refs/heads/")[1]

Expand Down Expand Up @@ -937,6 +941,8 @@ const (

func (r *Repository) cloneRefSpec(o *CloneOptions) []config.RefSpec {
switch {
case o.Mirror:
return []config.RefSpec{"+refs/*:refs/*"}
case o.ReferenceName.IsTag():
return []config.RefSpec{
config.RefSpec(fmt.Sprintf(refspecTag, o.ReferenceName.Short())),
Expand Down
29 changes: 29 additions & 0 deletions repository_test.go
Expand Up @@ -189,6 +189,35 @@ func (s *RepositorySuite) TestCloneContext(c *C) {
c.Assert(err, Equals, context.Canceled)
}

func (s *RepositorySuite) TestCloneMirror(c *C) {
r, err := Clone(memory.NewStorage(), nil, &CloneOptions{
URL: fixtures.Basic().One().URL,
Mirror: true,
})

c.Assert(err, IsNil)

refs, err := r.References()
var count int
refs.ForEach(func(r *plumbing.Reference) error { c.Log(r); count++; return nil })
c.Assert(err, IsNil)
// 6 refs total from github.com/git-fixtures/basic.git:
// - HEAD
// - refs/heads/master
// - refs/heads/branch
// - refs/pull/1/head
// - refs/pull/2/head
// - refs/pull/2/merge
c.Assert(count, Equals, 6)

cfg, err := r.Config()
c.Assert(err, IsNil)

c.Assert(cfg.Core.IsBare, Equals, true)
c.Assert(cfg.Remotes[DefaultRemoteName].Validate(), IsNil)
c.Assert(cfg.Remotes[DefaultRemoteName].Mirror, Equals, true)
}

func (s *RepositorySuite) TestCloneWithTags(c *C) {
url := s.GetLocalRepositoryURL(
fixtures.ByURL("https://github.com/git-fixtures/tags.git").One(),
Expand Down

0 comments on commit 0a1c5ab

Please sign in to comment.