aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config/config.go8
-rw-r--r--go.mod16
-rw-r--r--go.sum41
-rw-r--r--internal/url/url.go6
-rwxr-xr-xinternal/url/url_test.go107
-rw-r--r--options.go8
-rw-r--r--plumbing/reference_test.go2
-rw-r--r--plumbing/transport/common_test.go20
-rw-r--r--repository.go14
-rw-r--r--repository_test.go29
-rw-r--r--storage/filesystem/dotgit/dotgit.go80
11 files changed, 225 insertions, 106 deletions
diff --git a/config/config.go b/config/config.go
index 83629fc..60dfca4 100644
--- a/config/config.go
+++ b/config/config.go
@@ -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.
@@ -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
@@ -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
}
@@ -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
}
diff --git a/go.mod b/go.mod
index 95cb02b..27b74a3 100644
--- a/go.mod
+++ b/go.mod
@@ -3,28 +3,26 @@ module github.com/go-git/go-git/v5
go 1.13
require (
- github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8
+ github.com/ProtonMail/go-crypto v0.0.0-20230417170513-8ee5748c52b5
github.com/acomagu/bufpipe v1.0.4
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5
github.com/emirpasic/gods v1.18.1
github.com/gliderlabs/ssh v0.3.5
- github.com/go-git/gcfg v1.5.0
+ github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376
github.com/go-git/go-billy/v5 v5.4.1
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f
github.com/google/go-cmp v0.5.9
- github.com/imdario/mergo v0.3.13
+ github.com/imdario/mergo v0.3.15
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99
github.com/jessevdk/go-flags v1.5.0
github.com/kevinburke/ssh_config v1.2.0
github.com/pjbgf/sha1cd v0.3.0
- github.com/pkg/errors v0.9.1 // indirect
github.com/sergi/go-diff v1.1.0
github.com/skeema/knownhosts v1.1.0
github.com/xanzy/ssh-agent v0.3.3
- golang.org/x/crypto v0.6.0
- golang.org/x/net v0.7.0
- golang.org/x/sys v0.5.0
- golang.org/x/text v0.7.0
+ golang.org/x/crypto v0.8.0
+ golang.org/x/net v0.9.0
+ golang.org/x/sys v0.7.0
+ golang.org/x/text v0.9.0
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c
- gopkg.in/warnings.v0 v0.1.2 // indirect
)
diff --git a/go.sum b/go.sum
index bb9bc85..f4fb146 100644
--- a/go.sum
+++ b/go.sum
@@ -1,7 +1,7 @@
github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
-github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 h1:wPbRQzjjwFc0ih8puEVAOFGELsn1zoIIYdxvML7mDxA=
-github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g=
+github.com/ProtonMail/go-crypto v0.0.0-20230417170513-8ee5748c52b5 h1:QXMwHM/lB4ZQhdEF7JUTNgYOJR/gWoFbgQ/2Aj1h3Dk=
+github.com/ProtonMail/go-crypto v0.0.0-20230417170513-8ee5748c52b5/go.mod h1:8TI4H3IbrackdNgv+92dI+rhpCaLqM0IfpgCgenFvRE=
github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ=
github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
@@ -19,8 +19,8 @@ github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
-github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
-github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
+github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
+github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4=
github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg=
@@ -28,8 +28,8 @@ github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f h1:Pz0
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
-github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
+github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM=
+github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc=
@@ -73,10 +73,12 @@ golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
-golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
-golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
+golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
+golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
+golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
+golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
@@ -84,10 +86,12 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
-golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
-golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
+golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
+golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
+golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -104,25 +108,32 @@ golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
+golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
+golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
+golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ=
+golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
+golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
+golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
+golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -134,6 +145,6 @@ gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRN
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA=
-gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
diff --git a/internal/url/url.go b/internal/url/url.go
index 14cf133..2662448 100644
--- a/internal/url/url.go
+++ b/internal/url/url.go
@@ -5,8 +5,10 @@ import (
)
var (
- isSchemeRegExp = regexp.MustCompile(`^[^:]+://`)
- scpLikeUrlRegExp = regexp.MustCompile(`^(?:(?P<user>[^@]+)@)?(?P<host>[^:\s]+):(?:(?P<port>[0-9]{1,5})(?:\/|:))?(?P<path>[^\\].*\/[^\\].*)$`)
+ isSchemeRegExp = regexp.MustCompile(`^[^:]+://`)
+
+ // Ref: https://github.com/git/git/blob/master/Documentation/urls.txt#L37
+ scpLikeUrlRegExp = regexp.MustCompile(`^(?:(?P<user>[^@]+)@)?(?P<host>[^:\s]+):(?:(?P<port>[0-9]{1,5}):)?(?P<path>[^\\].*)$`)
)
// MatchesScheme returns true if the given string matches a URL-like
diff --git a/internal/url/url_test.go b/internal/url/url_test.go
index d168db6..29c3f3e 100755
--- a/internal/url/url_test.go
+++ b/internal/url/url_test.go
@@ -13,11 +13,27 @@ type URLSuite struct{}
var _ = Suite(&URLSuite{})
func (s *URLSuite) TestMatchesScpLike(c *C) {
+ // See https://github.com/git/git/blob/master/Documentation/urls.txt#L37
examples := []string{
+ // Most-extended case
"git@github.com:james/bond",
- "git@github.com:007/bond",
+ // Most-extended case with port
"git@github.com:22:james/bond",
+ // Most-extended case with numeric path
+ "git@github.com:007/bond",
+ // Most-extended case with port and numeric "username"
"git@github.com:22:007/bond",
+ // Single repo path
+ "git@github.com:bond",
+ // Single repo path with port
+ "git@github.com:22:bond",
+ // Single repo path with port and numeric repo
+ "git@github.com:22:007",
+ // Repo path ending with .git and starting with _
+ "git@github.com:22:_007.git",
+ "git@github.com:_007.git",
+ "git@github.com:_james.git",
+ "git@github.com:_james/bond.git",
}
for _, url := range examples {
@@ -26,35 +42,68 @@ func (s *URLSuite) TestMatchesScpLike(c *C) {
}
func (s *URLSuite) TestFindScpLikeComponents(c *C) {
- url := "git@github.com:james/bond"
- user, host, port, path := FindScpLikeComponents(url)
-
- c.Check(user, Equals, "git")
- c.Check(host, Equals, "github.com")
- c.Check(port, Equals, "")
- c.Check(path, Equals, "james/bond")
-
- url = "git@github.com:007/bond"
- user, host, port, path = FindScpLikeComponents(url)
-
- c.Check(user, Equals, "git")
- c.Check(host, Equals, "github.com")
- c.Check(port, Equals, "")
- c.Check(path, Equals, "007/bond")
-
- url = "git@github.com:22:james/bond"
- user, host, port, path = FindScpLikeComponents(url)
+ testCases := []struct {
+ url, user, host, port, path string
+ }{
+ {
+ // Most-extended case
+ url: "git@github.com:james/bond", user: "git", host: "github.com", port: "", path: "james/bond",
+ },
+ {
+ // Most-extended case with port
+ url: "git@github.com:22:james/bond", user: "git", host: "github.com", port: "22", path: "james/bond",
+ },
+ {
+ // Most-extended case with numeric path
+ url: "git@github.com:007/bond", user: "git", host: "github.com", port: "", path: "007/bond",
+ },
+ {
+ // Most-extended case with port and numeric path
+ url: "git@github.com:22:007/bond", user: "git", host: "github.com", port: "22", path: "007/bond",
+ },
+ {
+ // Single repo path
+ url: "git@github.com:bond", user: "git", host: "github.com", port: "", path: "bond",
+ },
+ {
+ // Single repo path with port
+ url: "git@github.com:22:bond", user: "git", host: "github.com", port: "22", path: "bond",
+ },
+ {
+ // Single repo path with port and numeric path
+ url: "git@github.com:22:007", user: "git", host: "github.com", port: "22", path: "007",
+ },
+ {
+ // Repo path ending with .git and starting with _
+ url: "git@github.com:22:_007.git", user: "git", host: "github.com", port: "22", path: "_007.git",
+ },
+ {
+ // Repo path ending with .git and starting with _
+ url: "git@github.com:_007.git", user: "git", host: "github.com", port: "", path: "_007.git",
+ },
+ {
+ // Repo path ending with .git and starting with _
+ url: "git@github.com:_james.git", user: "git", host: "github.com", port: "", path: "_james.git",
+ },
+ {
+ // Repo path ending with .git and starting with _
+ url: "git@github.com:_james/bond.git", user: "git", host: "github.com", port: "", path: "_james/bond.git",
+ },
+ }
- c.Check(user, Equals, "git")
- c.Check(host, Equals, "github.com")
- c.Check(port, Equals, "22")
- c.Check(path, Equals, "james/bond")
+ for _, tc := range testCases {
+ user, host, port, path := FindScpLikeComponents(tc.url)
- url = "git@github.com:22:007/bond"
- user, host, port, path = FindScpLikeComponents(url)
+ logf := func(ok bool) {
+ if ok {
+ return
+ }
+ c.Logf("%q check failed", tc.url)
+ }
- c.Check(user, Equals, "git")
- c.Check(host, Equals, "github.com")
- c.Check(port, Equals, "22")
- c.Check(path, Equals, "007/bond")
+ logf(c.Check(user, Equals, tc.user))
+ logf(c.Check(host, Equals, tc.host))
+ logf(c.Check(port, Equals, tc.port))
+ logf(c.Check(path, Equals, tc.path))
+ }
}
diff --git a/options.go b/options.go
index 112d600..e79cf9d 100644
--- a/options.go
+++ b/options.go
@@ -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.
diff --git a/plumbing/reference_test.go b/plumbing/reference_test.go
index e69076f..04dfef9 100644
--- a/plumbing/reference_test.go
+++ b/plumbing/reference_test.go
@@ -105,7 +105,7 @@ func (s *ReferenceSuite) TestIsTag(c *C) {
func benchMarkReferenceString(r *Reference, b *testing.B) {
for n := 0; n < b.N; n++ {
- r.String()
+ _ = r.String()
}
}
diff --git a/plumbing/transport/common_test.go b/plumbing/transport/common_test.go
index 0c5a01a..db11303 100644
--- a/plumbing/transport/common_test.go
+++ b/plumbing/transport/common_test.go
@@ -95,16 +95,28 @@ func (s *SuiteCommon) TestNewEndpointSCPLike(c *C) {
c.Assert(e.String(), Equals, "ssh://git@github.com/user/repository.git")
}
-func (s *SuiteCommon) TestNewEndpointSCPLikeWithPort(c *C) {
+func (s *SuiteCommon) TestNewEndpointSCPLikeWithNumericPath(c *C) {
e, err := NewEndpoint("git@github.com:9999/user/repository.git")
c.Assert(err, IsNil)
c.Assert(e.Protocol, Equals, "ssh")
c.Assert(e.User, Equals, "git")
c.Assert(e.Password, Equals, "")
c.Assert(e.Host, Equals, "github.com")
- c.Assert(e.Port, Equals, 9999)
- c.Assert(e.Path, Equals, "user/repository.git")
- c.Assert(e.String(), Equals, "ssh://git@github.com:9999/user/repository.git")
+ c.Assert(e.Port, Equals, 22)
+ c.Assert(e.Path, Equals, "9999/user/repository.git")
+ c.Assert(e.String(), Equals, "ssh://git@github.com/9999/user/repository.git")
+}
+
+func (s *SuiteCommon) TestNewEndpointSCPLikeWithPort(c *C) {
+ e, err := NewEndpoint("git@github.com:8080:9999/user/repository.git")
+ c.Assert(err, IsNil)
+ c.Assert(e.Protocol, Equals, "ssh")
+ c.Assert(e.User, Equals, "git")
+ c.Assert(e.Password, Equals, "")
+ c.Assert(e.Host, Equals, "github.com")
+ c.Assert(e.Port, Equals, 8080)
+ c.Assert(e.Path, Equals, "9999/user/repository.git")
+ c.Assert(e.String(), Equals, "ssh://git@github.com:8080/9999/user/repository.git")
}
func (s *SuiteCommon) TestNewEndpointFileAbs(c *C) {
diff --git a/repository.go b/repository.go
index 56ae976..e009c5d 100644
--- a/repository.go
+++ b/repository.go
@@ -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
@@ -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 {
@@ -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]
@@ -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())),
diff --git a/repository_test.go b/repository_test.go
index 468ce33..ed3e7e6 100644
--- a/repository_test.go
+++ b/repository_test.go
@@ -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(),
diff --git a/storage/filesystem/dotgit/dotgit.go b/storage/filesystem/dotgit/dotgit.go
index 6868324..5bde37f 100644
--- a/storage/filesystem/dotgit/dotgit.go
+++ b/storage/filesystem/dotgit/dotgit.go
@@ -718,48 +718,56 @@ func (d *DotGit) Ref(name plumbing.ReferenceName) (*plumbing.Reference, error) {
return d.packedRef(name)
}
-func (d *DotGit) findPackedRefsInFile(f billy.File) ([]*plumbing.Reference, error) {
+func (d *DotGit) findPackedRefsInFile(f billy.File, recv refsRecv) error {
s := bufio.NewScanner(f)
- var refs []*plumbing.Reference
for s.Scan() {
ref, err := d.processLine(s.Text())
if err != nil {
- return nil, err
+ return err
}
- if ref != nil {
- refs = append(refs, ref)
+ if !recv(ref) {
+ // skip parse
+ return nil
}
}
-
- return refs, s.Err()
+ if err := s.Err(); err != nil {
+ return err
+ }
+ return nil
}
-func (d *DotGit) findPackedRefs() (r []*plumbing.Reference, err error) {
+// refsRecv: returning true means that the reference continues to be resolved, otherwise it is stopped, which will speed up the lookup of a single reference.
+type refsRecv func(*plumbing.Reference) bool
+
+func (d *DotGit) findPackedRefs(recv refsRecv) error {
f, err := d.fs.Open(packedRefsPath)
if err != nil {
if os.IsNotExist(err) {
- return nil, nil
+ return nil
}
- return nil, err
+ return err
}
defer ioutil.CheckClose(f, &err)
- return d.findPackedRefsInFile(f)
+ return d.findPackedRefsInFile(f, recv)
}
func (d *DotGit) packedRef(name plumbing.ReferenceName) (*plumbing.Reference, error) {
- refs, err := d.findPackedRefs()
- if err != nil {
+ var ref *plumbing.Reference
+ if err := d.findPackedRefs(func(r *plumbing.Reference) bool {
+ if r != nil && r.Name() == name {
+ ref = r
+ // ref found
+ return false
+ }
+ return true
+ }); err != nil {
return nil, err
}
-
- for _, ref := range refs {
- if ref.Name() == name {
- return ref, nil
- }
+ if ref != nil {
+ return ref, nil
}
-
return nil, plumbing.ErrReferenceNotFound
}
@@ -779,34 +787,22 @@ func (d *DotGit) RemoveRef(name plumbing.ReferenceName) error {
return d.rewritePackedRefsWithoutRef(name)
}
-func (d *DotGit) addRefsFromPackedRefs(refs *[]*plumbing.Reference, seen map[plumbing.ReferenceName]bool) (err error) {
- packedRefs, err := d.findPackedRefs()
- if err != nil {
- return err
- }
-
- for _, ref := range packedRefs {
- if !seen[ref.Name()] {
- *refs = append(*refs, ref)
- seen[ref.Name()] = true
+func refsRecvFunc(refs *[]*plumbing.Reference, seen map[plumbing.ReferenceName]bool) refsRecv {
+ return func(r *plumbing.Reference) bool {
+ if r != nil && !seen[r.Name()] {
+ *refs = append(*refs, r)
+ seen[r.Name()] = true
}
+ return true
}
- return nil
}
-func (d *DotGit) addRefsFromPackedRefsFile(refs *[]*plumbing.Reference, f billy.File, seen map[plumbing.ReferenceName]bool) (err error) {
- packedRefs, err := d.findPackedRefsInFile(f)
- if err != nil {
- return err
- }
+func (d *DotGit) addRefsFromPackedRefs(refs *[]*plumbing.Reference, seen map[plumbing.ReferenceName]bool) (err error) {
+ return d.findPackedRefs(refsRecvFunc(refs, seen))
+}
- for _, ref := range packedRefs {
- if !seen[ref.Name()] {
- *refs = append(*refs, ref)
- seen[ref.Name()] = true
- }
- }
- return nil
+func (d *DotGit) addRefsFromPackedRefsFile(refs *[]*plumbing.Reference, f billy.File, seen map[plumbing.ReferenceName]bool) (err error) {
+ return d.findPackedRefsInFile(f, refsRecvFunc(refs, seen))
}
func (d *DotGit) openAndLockPackedRefs(doCreate bool) (