Skip to content
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

git: add mirror clone option #735

Merged
merged 1 commit into from
Apr 17, 2023
Merged
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
8 changes: 8 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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