Skip to content

Commit

Permalink
export ApplyURLRules(...)
Browse files Browse the repository at this point in the history
call ensureURLRulesApplied() from all Remote.{fetch,push,list}() functions
supporting tests
  • Loading branch information
BryanStenson-okta committed Oct 20, 2023
1 parent 72ce996 commit 542c21c
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 10 deletions.
18 changes: 9 additions & 9 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ func (c *Config) unmarshalRemotes() error {

// Apply insteadOf url rules
for _, r := range c.Remotes {
r.applyURLRules(c.URLs)
r.ApplyURLRules(c.URLs)
}

return nil
Expand Down Expand Up @@ -583,6 +583,7 @@ type RemoteConfig struct {

// insteadOfRulesApplied have urls been modified
insteadOfRulesApplied bool

// originalURLs are the urls before applying insteadOf rules
originalURLs []string

Expand Down Expand Up @@ -677,19 +678,18 @@ func (c *RemoteConfig) IsFirstURLLocal() bool {
return url.IsLocalEndpoint(c.URLs[0])
}

func (c *RemoteConfig) applyURLRules(urlRules map[string]*URL) {
// save original urls
originalURLs := make([]string, len(c.URLs))
copy(originalURLs, c.URLs)
// ApplyURLRules() updates c.URLs by substituting the longest matching insteadOf value found in urlRules.
func (c *RemoteConfig) ApplyURLRules(urlRules map[string]*URL) {
// save original urls if we haven't already
// never overwrite c.originalURLs on subsequent calls
if !c.insteadOfRulesApplied {
copy(c.originalURLs, c.URLs)
}

for i, url := range c.URLs {
if matchingURLRule := findLongestInsteadOfMatch(url, urlRules); matchingURLRule != nil {
c.URLs[i] = matchingURLRule.ApplyInsteadOf(c.URLs[i])
c.insteadOfRulesApplied = true
}
}

if c.insteadOfRulesApplied {
c.originalURLs = originalURLs
}
}
2 changes: 2 additions & 0 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ type CloneOptions struct {
//
// [Reference]: https://git-scm.com/docs/git-clone#Documentation/git-clone.txt---shared
Shared bool
// URLRules are used to substitute urls details via the config's insteadOf sections
URLRules map[string]*config.URL
}

// Validate validates the fields and sets the default values.
Expand Down
25 changes: 25 additions & 0 deletions remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ func (r *Remote) PushContext(ctx context.Context, o *PushOptions) (err error) {
return err
}

if err := r.ensureURLRulesApplied(); err != nil {
return err
}

if o.RemoteName != r.c.Name {
return fmt.Errorf("remote names don't match: %s != %s", o.RemoteName, r.c.Name)
}
Expand Down Expand Up @@ -411,6 +415,10 @@ func (r *Remote) fetch(ctx context.Context, o *FetchOptions) (sto storer.Referen
o.RefSpecs = r.c.Fetch
}

if err := r.ensureURLRulesApplied(); err != nil {
return nil, err
}

if o.RemoteURL == "" {
o.RemoteURL = r.c.URLs[0]
}
Expand Down Expand Up @@ -1302,6 +1310,10 @@ func (r *Remote) list(ctx context.Context, o *ListOptions) (rfs []*plumbing.Refe
return nil, ErrEmptyUrls
}

if err := r.ensureURLRulesApplied(); err != nil {
return nil, err
}

s, err := newUploadPackSession(r.c.URLs[0], o.Auth, o.InsecureSkipTLS, o.CABundle, o.ProxyOptions)
if err != nil {
return nil, err
Expand Down Expand Up @@ -1480,3 +1492,16 @@ func (r *Remote) checkRequireRemoteRefs(requires []config.RefSpec, remoteRefs st
}
return nil
}

func (r *Remote) ensureURLRulesApplied() error {
if r.s != nil {
cfg, err := r.s.Config()
if err != nil {
return err
}

r.c.ApplyURLRules(cfg.URLs)
}

return nil
}
120 changes: 119 additions & 1 deletion remote_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -562,7 +562,6 @@ func (s *RemoteSuite) TestPushToEmptyRepository(c *C) {
c.Assert(err, IsNil)

AssertReferences(c, server, expected)

}

func (s *RemoteSuite) TestPushContext(c *C) {
Expand Down Expand Up @@ -1555,3 +1554,122 @@ func (s *RemoteSuite) TestFetchAfterShallowClone(c *C) {
plumbing.NewSymbolicReference("HEAD", "refs/heads/master"),
})
}

// ensure insteadOf substitution is applied on Fetch()
func (s *RemoteSuite) TestFetchInsteadOf(c *C) {
url := s.GetLocalRepositoryURL(fixtures.ByTag("tags").One())
prefix := url[:len(url)-1]
suffix := url[len(url)-1:]

// modify url to be invalid if insteadof is not applied
url = "tobereplaced" + suffix

r := NewRemote(memory.NewStorage(), &config.RemoteConfig{
URLs: []string{url},
})

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

cfg.URLs[prefix] = &config.URL{
Name: prefix,
InsteadOf: "tobereplaced",
}

c.Assert(r.s.SetConfig(cfg), NotNil)

s.testFetch(c, r, &FetchOptions{
RefSpecs: []config.RefSpec{
config.RefSpec("+refs/heads/master:refs/remotes/origin/master"),
},
}, []*plumbing.Reference{
plumbing.NewReferenceFromStrings("refs/remotes/origin/master", "f7b877701fbf855b44c0a9e86f3fdce2c298b07f"),
})
}

// ensure insteadOf substitution is applied on Push()
//
// adapted from TestPushNoErrAlreadyUpToDate(...)
func (s *RemoteSuite) TestPushInsteadOf(c *C) {
fs := fixtures.Basic().One().DotGit()
sto := filesystem.NewStorage(fs, cache.NewObjectLRUDefault())

url := fs.Root()
prefix := url[:len(url)-1]
suffix := url[len(url)-1:]

// modify url to be invalid if insteadof is not applied
url = "tobereplaced" + suffix

r := NewRemote(sto, &config.RemoteConfig{
Name: DefaultRemoteName,
URLs: []string{url},
})

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

cfg.URLs[prefix] = &config.URL{
Name: prefix,
InsteadOf: "tobereplaced",
}

c.Assert(r.s.SetConfig(cfg), IsNil)

err = r.Push(&PushOptions{
RefSpecs: []config.RefSpec{"refs/heads/*:refs/heads/*"},
})
c.Assert(err, Equals, NoErrAlreadyUpToDate)
}

// ensure insteadOf substitution is applied on List()
//
// adapted from TestList(...)
func (s *RemoteSuite) TestListInsteadOf(c *C) {
repo := fixtures.Basic().One()

url := repo.URL
prefix := url[:len(url)-1]
suffix := url[len(url)-1:]

// modify url to be invalid if insteadof is not applied
url = "tobereplaced" + suffix

r := NewRemote(memory.NewStorage(), &config.RemoteConfig{
Name: DefaultRemoteName,
URLs: []string{url},
})

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

cfg.URLs[prefix] = &config.URL{
Name: prefix,
InsteadOf: "tobereplaced",
}

c.Assert(r.s.SetConfig(cfg), NotNil)

refs, err := r.List(&ListOptions{})
c.Assert(err, IsNil)

expected := []*plumbing.Reference{
plumbing.NewSymbolicReference("HEAD", "refs/heads/master"),
plumbing.NewReferenceFromStrings("refs/heads/master", "6ecf0ef2c2dffb796033e5a02219af86ec6584e5"),
plumbing.NewReferenceFromStrings("refs/heads/branch", "e8d3ffab552895c19b9fcf7aa264d277cde33881"),
plumbing.NewReferenceFromStrings("refs/pull/1/head", "b8e471f58bcbca63b07bda20e428190409c2db47"),
plumbing.NewReferenceFromStrings("refs/pull/2/head", "9632f02833b2f9613afb5e75682132b0b22e4a31"),
plumbing.NewReferenceFromStrings("refs/pull/2/merge", "c37f58a130ca555e42ff96a071cb9ccb3f437504"),
}
c.Assert(len(refs), Equals, len(expected))
for _, e := range expected {
found := false
for _, r := range refs {
if r.Name() == e.Name() {
found = true
c.Assert(r, DeepEquals, e)
}
}
c.Assert(found, Equals, true)
}
}

0 comments on commit 542c21c

Please sign in to comment.