fork of go-git with some jj specific features
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.14.0 1827 lines 53 kB view raw
1package git 2 3import ( 4 "bytes" 5 "context" 6 "errors" 7 "fmt" 8 "io" 9 "os" 10 "path/filepath" 11 "runtime" 12 "strings" 13 "testing" 14 "time" 15 16 "github.com/go-git/go-billy/v5/memfs" 17 "github.com/go-git/go-billy/v5/osfs" 18 "github.com/go-git/go-billy/v5/util" 19 20 "github.com/go-git/go-git/v5/config" 21 "github.com/go-git/go-git/v5/plumbing" 22 "github.com/go-git/go-git/v5/plumbing/cache" 23 "github.com/go-git/go-git/v5/plumbing/object" 24 "github.com/go-git/go-git/v5/plumbing/protocol/packp" 25 "github.com/go-git/go-git/v5/plumbing/protocol/packp/capability" 26 "github.com/go-git/go-git/v5/plumbing/storer" 27 "github.com/go-git/go-git/v5/storage" 28 "github.com/go-git/go-git/v5/storage/filesystem" 29 "github.com/go-git/go-git/v5/storage/memory" 30 31 fixtures "github.com/go-git/go-git-fixtures/v4" 32 . "gopkg.in/check.v1" 33) 34 35type RemoteSuite struct { 36 BaseSuite 37} 38 39var _ = Suite(&RemoteSuite{}) 40 41func (s *RemoteSuite) TestFetchInvalidEndpoint(c *C) { 42 r := NewRemote(nil, &config.RemoteConfig{Name: "foo", URLs: []string{"http://\\"}}) 43 err := r.Fetch(&FetchOptions{RemoteName: "foo"}) 44 c.Assert(err, ErrorMatches, ".*invalid character.*") 45} 46 47func (s *RemoteSuite) TestFetchNonExistentEndpoint(c *C) { 48 r := NewRemote(nil, &config.RemoteConfig{Name: "foo", URLs: []string{"ssh://non-existent/foo.git"}}) 49 err := r.Fetch(&FetchOptions{}) 50 c.Assert(err, NotNil) 51} 52 53func (s *RemoteSuite) TestFetchInvalidSchemaEndpoint(c *C) { 54 r := NewRemote(nil, &config.RemoteConfig{Name: "foo", URLs: []string{"qux://foo"}}) 55 err := r.Fetch(&FetchOptions{}) 56 c.Assert(err, ErrorMatches, ".*unsupported scheme.*") 57} 58 59func (s *RemoteSuite) TestFetchOverriddenEndpoint(c *C) { 60 r := NewRemote(nil, &config.RemoteConfig{Name: "foo", URLs: []string{"http://perfectly-valid-url.example.com"}}) 61 err := r.Fetch(&FetchOptions{RemoteURL: "http://\\"}) 62 c.Assert(err, ErrorMatches, ".*invalid character.*") 63} 64 65func (s *RemoteSuite) TestFetchInvalidFetchOptions(c *C) { 66 r := NewRemote(nil, &config.RemoteConfig{Name: "foo", URLs: []string{"qux://foo"}}) 67 invalid := config.RefSpec("^*$ñ") 68 err := r.Fetch(&FetchOptions{RefSpecs: []config.RefSpec{invalid}}) 69 c.Assert(err, Equals, config.ErrRefSpecMalformedSeparator) 70} 71 72func (s *RemoteSuite) TestFetchWildcard(c *C) { 73 r := NewRemote(memory.NewStorage(), &config.RemoteConfig{ 74 URLs: []string{s.GetBasicLocalRepositoryURL()}, 75 }) 76 77 s.testFetch(c, r, &FetchOptions{ 78 RefSpecs: []config.RefSpec{ 79 config.RefSpec("+refs/heads/*:refs/remotes/origin/*"), 80 }, 81 }, []*plumbing.Reference{ 82 plumbing.NewReferenceFromStrings("refs/remotes/origin/master", "6ecf0ef2c2dffb796033e5a02219af86ec6584e5"), 83 plumbing.NewReferenceFromStrings("refs/remotes/origin/branch", "e8d3ffab552895c19b9fcf7aa264d277cde33881"), 84 plumbing.NewReferenceFromStrings("refs/tags/v1.0.0", "6ecf0ef2c2dffb796033e5a02219af86ec6584e5"), 85 }) 86} 87 88func (s *RemoteSuite) TestFetchExactSHA1(c *C) { 89 r := NewRemote(memory.NewStorage(), &config.RemoteConfig{ 90 URLs: []string{"https://github.com/git-fixtures/basic.git"}, 91 }) 92 93 s.testFetch(c, r, &FetchOptions{ 94 RefSpecs: []config.RefSpec{ 95 config.RefSpec("35e85108805c84807bc66a02d91535e1e24b38b9:refs/heads/foo"), 96 }, 97 }, []*plumbing.Reference{ 98 plumbing.NewReferenceFromStrings("refs/heads/foo", "35e85108805c84807bc66a02d91535e1e24b38b9"), 99 }) 100} 101 102func (s *RemoteSuite) TestFetchExactSHA1_NotSoported(c *C) { 103 r := NewRemote(memory.NewStorage(), &config.RemoteConfig{ 104 URLs: []string{s.GetBasicLocalRepositoryURL()}, 105 }) 106 107 err := r.Fetch(&FetchOptions{ 108 RefSpecs: []config.RefSpec{ 109 config.RefSpec("35e85108805c84807bc66a02d91535e1e24b38b9:refs/heads/foo"), 110 }, 111 }) 112 113 c.Assert(err, Equals, ErrExactSHA1NotSupported) 114 115} 116 117func (s *RemoteSuite) TestFetchWildcardTags(c *C) { 118 r := NewRemote(memory.NewStorage(), &config.RemoteConfig{ 119 URLs: []string{s.GetLocalRepositoryURL(fixtures.ByTag("tags").One())}, 120 }) 121 122 s.testFetch(c, r, &FetchOptions{ 123 RefSpecs: []config.RefSpec{ 124 config.RefSpec("+refs/heads/*:refs/remotes/origin/*"), 125 }, 126 }, []*plumbing.Reference{ 127 plumbing.NewReferenceFromStrings("refs/remotes/origin/master", "f7b877701fbf855b44c0a9e86f3fdce2c298b07f"), 128 plumbing.NewReferenceFromStrings("refs/tags/annotated-tag", "b742a2a9fa0afcfa9a6fad080980fbc26b007c69"), 129 plumbing.NewReferenceFromStrings("refs/tags/tree-tag", "152175bf7e5580299fa1f0ba41ef6474cc043b70"), 130 plumbing.NewReferenceFromStrings("refs/tags/commit-tag", "ad7897c0fb8e7d9a9ba41fa66072cf06095a6cfc"), 131 plumbing.NewReferenceFromStrings("refs/tags/blob-tag", "fe6cb94756faa81e5ed9240f9191b833db5f40ae"), 132 plumbing.NewReferenceFromStrings("refs/tags/lightweight-tag", "f7b877701fbf855b44c0a9e86f3fdce2c298b07f"), 133 }) 134} 135 136func (s *RemoteSuite) TestFetch(c *C) { 137 r := NewRemote(memory.NewStorage(), &config.RemoteConfig{ 138 URLs: []string{s.GetLocalRepositoryURL(fixtures.ByTag("tags").One())}, 139 }) 140 141 s.testFetch(c, r, &FetchOptions{ 142 RefSpecs: []config.RefSpec{ 143 config.RefSpec("+refs/heads/master:refs/remotes/origin/master"), 144 }, 145 }, []*plumbing.Reference{ 146 plumbing.NewReferenceFromStrings("refs/remotes/origin/master", "f7b877701fbf855b44c0a9e86f3fdce2c298b07f"), 147 }) 148} 149 150func (s *RemoteSuite) TestFetchToNewBranch(c *C) { 151 r := NewRemote(memory.NewStorage(), &config.RemoteConfig{ 152 URLs: []string{s.GetLocalRepositoryURL(fixtures.ByTag("tags").One())}, 153 }) 154 155 s.testFetch(c, r, &FetchOptions{ 156 RefSpecs: []config.RefSpec{ 157 // qualified branch to unqualified branch 158 "refs/heads/master:foo", 159 // unqualified branch to unqualified branch 160 "+master:bar", 161 // unqualified tag to unqualified branch 162 config.RefSpec("tree-tag:tree-tag"), 163 // unqualified tag to qualified tag 164 config.RefSpec("+commit-tag:refs/tags/renamed-tag"), 165 }, 166 }, []*plumbing.Reference{ 167 plumbing.NewReferenceFromStrings("refs/heads/foo", "f7b877701fbf855b44c0a9e86f3fdce2c298b07f"), 168 plumbing.NewReferenceFromStrings("refs/heads/bar", "f7b877701fbf855b44c0a9e86f3fdce2c298b07f"), 169 plumbing.NewReferenceFromStrings("refs/heads/tree-tag", "152175bf7e5580299fa1f0ba41ef6474cc043b70"), 170 plumbing.NewReferenceFromStrings("refs/tags/tree-tag", "152175bf7e5580299fa1f0ba41ef6474cc043b70"), 171 plumbing.NewReferenceFromStrings("refs/tags/renamed-tag", "ad7897c0fb8e7d9a9ba41fa66072cf06095a6cfc"), 172 plumbing.NewReferenceFromStrings("refs/tags/commit-tag", "ad7897c0fb8e7d9a9ba41fa66072cf06095a6cfc"), 173 }) 174} 175 176func (s *RemoteSuite) TestFetchToNewBranchWithAllTags(c *C) { 177 r := NewRemote(memory.NewStorage(), &config.RemoteConfig{ 178 URLs: []string{s.GetLocalRepositoryURL(fixtures.ByTag("tags").One())}, 179 }) 180 181 s.testFetch(c, r, &FetchOptions{ 182 Tags: AllTags, 183 RefSpecs: []config.RefSpec{ 184 // qualified branch to unqualified branch 185 "+refs/heads/master:foo", 186 // unqualified branch to unqualified branch 187 "master:bar", 188 // unqualified tag to unqualified branch 189 config.RefSpec("+tree-tag:tree-tag"), 190 // unqualified tag to qualified tag 191 config.RefSpec("commit-tag:refs/tags/renamed-tag"), 192 }, 193 }, []*plumbing.Reference{ 194 plumbing.NewReferenceFromStrings("refs/heads/foo", "f7b877701fbf855b44c0a9e86f3fdce2c298b07f"), 195 plumbing.NewReferenceFromStrings("refs/heads/bar", "f7b877701fbf855b44c0a9e86f3fdce2c298b07f"), 196 plumbing.NewReferenceFromStrings("refs/heads/tree-tag", "152175bf7e5580299fa1f0ba41ef6474cc043b70"), 197 plumbing.NewReferenceFromStrings("refs/tags/tree-tag", "152175bf7e5580299fa1f0ba41ef6474cc043b70"), 198 plumbing.NewReferenceFromStrings("refs/tags/renamed-tag", "ad7897c0fb8e7d9a9ba41fa66072cf06095a6cfc"), 199 plumbing.NewReferenceFromStrings("refs/tags/commit-tag", "ad7897c0fb8e7d9a9ba41fa66072cf06095a6cfc"), 200 plumbing.NewReferenceFromStrings("refs/tags/annotated-tag", "b742a2a9fa0afcfa9a6fad080980fbc26b007c69"), 201 plumbing.NewReferenceFromStrings("refs/tags/blob-tag", "fe6cb94756faa81e5ed9240f9191b833db5f40ae"), 202 plumbing.NewReferenceFromStrings("refs/tags/lightweight-tag", "f7b877701fbf855b44c0a9e86f3fdce2c298b07f"), 203 }) 204} 205 206func (s *RemoteSuite) TestFetchNonExistentReference(c *C) { 207 r := NewRemote(memory.NewStorage(), &config.RemoteConfig{ 208 URLs: []string{s.GetLocalRepositoryURL(fixtures.ByTag("tags").One())}, 209 }) 210 211 err := r.Fetch(&FetchOptions{ 212 RefSpecs: []config.RefSpec{ 213 config.RefSpec("+refs/heads/foo:refs/remotes/origin/foo"), 214 }, 215 }) 216 217 c.Assert(err, ErrorMatches, "couldn't find remote ref.*") 218 c.Assert(errors.Is(err, NoMatchingRefSpecError{}), Equals, true) 219} 220 221func (s *RemoteSuite) TestFetchContext(c *C) { 222 r := NewRemote(memory.NewStorage(), &config.RemoteConfig{ 223 URLs: []string{s.GetLocalRepositoryURL(fixtures.ByTag("tags").One())}, 224 }) 225 226 ctx, cancel := context.WithCancel(context.Background()) 227 defer cancel() 228 229 err := r.FetchContext(ctx, &FetchOptions{ 230 RefSpecs: []config.RefSpec{ 231 config.RefSpec("+refs/heads/master:refs/remotes/origin/master"), 232 }, 233 }) 234 c.Assert(err, IsNil) 235} 236 237func (s *RemoteSuite) TestFetchContextCanceled(c *C) { 238 r := NewRemote(memory.NewStorage(), &config.RemoteConfig{ 239 URLs: []string{s.GetLocalRepositoryURL(fixtures.ByTag("tags").One())}, 240 }) 241 242 ctx, cancel := context.WithCancel(context.Background()) 243 cancel() 244 245 err := r.FetchContext(ctx, &FetchOptions{ 246 RefSpecs: []config.RefSpec{ 247 config.RefSpec("+refs/heads/master:refs/remotes/origin/master"), 248 }, 249 }) 250 c.Assert(err, Equals, context.Canceled) 251} 252 253func (s *RemoteSuite) TestFetchWithAllTags(c *C) { 254 r := NewRemote(memory.NewStorage(), &config.RemoteConfig{ 255 URLs: []string{s.GetLocalRepositoryURL(fixtures.ByTag("tags").One())}, 256 }) 257 258 s.testFetch(c, r, &FetchOptions{ 259 Tags: AllTags, 260 RefSpecs: []config.RefSpec{ 261 config.RefSpec("+refs/heads/master:refs/remotes/origin/master"), 262 }, 263 }, []*plumbing.Reference{ 264 plumbing.NewReferenceFromStrings("refs/remotes/origin/master", "f7b877701fbf855b44c0a9e86f3fdce2c298b07f"), 265 plumbing.NewReferenceFromStrings("refs/tags/annotated-tag", "b742a2a9fa0afcfa9a6fad080980fbc26b007c69"), 266 plumbing.NewReferenceFromStrings("refs/tags/tree-tag", "152175bf7e5580299fa1f0ba41ef6474cc043b70"), 267 plumbing.NewReferenceFromStrings("refs/tags/commit-tag", "ad7897c0fb8e7d9a9ba41fa66072cf06095a6cfc"), 268 plumbing.NewReferenceFromStrings("refs/tags/blob-tag", "fe6cb94756faa81e5ed9240f9191b833db5f40ae"), 269 plumbing.NewReferenceFromStrings("refs/tags/lightweight-tag", "f7b877701fbf855b44c0a9e86f3fdce2c298b07f"), 270 }) 271} 272 273func (s *RemoteSuite) TestFetchWithNoTags(c *C) { 274 r := NewRemote(memory.NewStorage(), &config.RemoteConfig{ 275 URLs: []string{s.GetLocalRepositoryURL(fixtures.ByTag("tags").One())}, 276 }) 277 278 s.testFetch(c, r, &FetchOptions{ 279 Tags: NoTags, 280 RefSpecs: []config.RefSpec{ 281 config.RefSpec("+refs/heads/*:refs/remotes/origin/*"), 282 }, 283 }, []*plumbing.Reference{ 284 plumbing.NewReferenceFromStrings("refs/remotes/origin/master", "f7b877701fbf855b44c0a9e86f3fdce2c298b07f"), 285 }) 286 287} 288 289func (s *RemoteSuite) TestFetchWithDepth(c *C) { 290 r := NewRemote(memory.NewStorage(), &config.RemoteConfig{ 291 URLs: []string{s.GetBasicLocalRepositoryURL()}, 292 }) 293 294 s.testFetch(c, r, &FetchOptions{ 295 Depth: 1, 296 RefSpecs: []config.RefSpec{ 297 config.RefSpec("+refs/heads/*:refs/remotes/origin/*"), 298 }, 299 }, []*plumbing.Reference{ 300 plumbing.NewReferenceFromStrings("refs/remotes/origin/master", "6ecf0ef2c2dffb796033e5a02219af86ec6584e5"), 301 plumbing.NewReferenceFromStrings("refs/remotes/origin/branch", "e8d3ffab552895c19b9fcf7aa264d277cde33881"), 302 plumbing.NewReferenceFromStrings("refs/tags/v1.0.0", "6ecf0ef2c2dffb796033e5a02219af86ec6584e5"), 303 }) 304 305 c.Assert(r.s.(*memory.Storage).Objects, HasLen, 18) 306} 307 308func (s *RemoteSuite) TestFetchWithDepthChange(c *C) { 309 r := NewRemote(memory.NewStorage(), &config.RemoteConfig{ 310 URLs: []string{s.GetBasicLocalRepositoryURL()}, 311 }) 312 313 s.testFetch(c, r, &FetchOptions{ 314 Depth: 1, 315 RefSpecs: []config.RefSpec{ 316 config.RefSpec("refs/heads/master:refs/heads/master"), 317 }, 318 }, []*plumbing.Reference{ 319 plumbing.NewReferenceFromStrings("refs/heads/master", "6ecf0ef2c2dffb796033e5a02219af86ec6584e5"), 320 }) 321 c.Assert(r.s.(*memory.Storage).Commits, HasLen, 1) 322 323 s.testFetch(c, r, &FetchOptions{ 324 Depth: 3, 325 RefSpecs: []config.RefSpec{ 326 config.RefSpec("refs/heads/master:refs/heads/master"), 327 }, 328 }, []*plumbing.Reference{ 329 plumbing.NewReferenceFromStrings("refs/heads/master", "6ecf0ef2c2dffb796033e5a02219af86ec6584e5"), 330 }) 331 c.Assert(r.s.(*memory.Storage).Commits, HasLen, 3) 332} 333 334func (s *RemoteSuite) testFetch(c *C, r *Remote, o *FetchOptions, expected []*plumbing.Reference) { 335 err := r.Fetch(o) 336 c.Assert(err, IsNil) 337 338 var refs int 339 l, err := r.s.IterReferences() 340 c.Assert(err, IsNil) 341 l.ForEach(func(r *plumbing.Reference) error { refs++; return nil }) 342 343 c.Assert(refs, Equals, len(expected)) 344 345 for _, exp := range expected { 346 r, err := r.s.Reference(exp.Name()) 347 c.Assert(err, IsNil) 348 c.Assert(exp.String(), Equals, r.String()) 349 } 350} 351 352func (s *RemoteSuite) TestFetchOfMissingObjects(c *C) { 353 tmp := c.MkDir() 354 355 // clone to a local temp folder 356 _, err := PlainClone(tmp, true, &CloneOptions{ 357 URL: fixtures.Basic().One().DotGit().Root(), 358 }) 359 c.Assert(err, IsNil) 360 361 // Delete the pack files 362 fsTmp := osfs.New(tmp) 363 err = util.RemoveAll(fsTmp, "objects/pack") 364 c.Assert(err, IsNil) 365 366 // Reopen the repo from the filesystem (with missing objects) 367 r, err := Open(filesystem.NewStorage(fsTmp, cache.NewObjectLRUDefault()), nil) 368 c.Assert(err, IsNil) 369 370 // Confirm we are missing a commit 371 _, err = r.CommitObject(plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5")) 372 c.Assert(err, Equals, plumbing.ErrObjectNotFound) 373 374 // Refetch to get all the missing objects 375 err = r.Fetch(&FetchOptions{}) 376 c.Assert(err, IsNil) 377 378 // Confirm we now have the commit 379 _, err = r.CommitObject(plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5")) 380 c.Assert(err, IsNil) 381} 382 383func (s *RemoteSuite) TestFetchWithProgress(c *C) { 384 url := s.GetBasicLocalRepositoryURL() 385 sto := memory.NewStorage() 386 buf := bytes.NewBuffer(nil) 387 388 r := NewRemote(sto, &config.RemoteConfig{Name: "foo", URLs: []string{url}}) 389 390 refspec := config.RefSpec("+refs/heads/*:refs/remotes/origin/*") 391 err := r.Fetch(&FetchOptions{ 392 RefSpecs: []config.RefSpec{refspec}, 393 Progress: buf, 394 }) 395 396 c.Assert(err, IsNil) 397 c.Assert(sto.Objects, HasLen, 31) 398 399 c.Assert(buf.Len(), Not(Equals), 0) 400} 401 402type mockPackfileWriter struct { 403 storage.Storer 404 PackfileWriterCalled bool 405} 406 407func (m *mockPackfileWriter) PackfileWriter() (io.WriteCloser, error) { 408 m.PackfileWriterCalled = true 409 return m.Storer.(storer.PackfileWriter).PackfileWriter() 410} 411 412func (s *RemoteSuite) TestFetchWithPackfileWriter(c *C) { 413 fs := s.TemporalFilesystem(c) 414 415 fss := filesystem.NewStorage(fs, cache.NewObjectLRUDefault()) 416 mock := &mockPackfileWriter{Storer: fss} 417 418 url := s.GetBasicLocalRepositoryURL() 419 r := NewRemote(mock, &config.RemoteConfig{Name: "foo", URLs: []string{url}}) 420 421 refspec := config.RefSpec("+refs/heads/*:refs/remotes/origin/*") 422 err := r.Fetch(&FetchOptions{ 423 RefSpecs: []config.RefSpec{refspec}, 424 }) 425 426 c.Assert(err, IsNil) 427 428 var count int 429 iter, err := mock.IterEncodedObjects(plumbing.AnyObject) 430 c.Assert(err, IsNil) 431 432 iter.ForEach(func(plumbing.EncodedObject) error { 433 count++ 434 return nil 435 }) 436 437 c.Assert(count, Equals, 31) 438 c.Assert(mock.PackfileWriterCalled, Equals, true) 439} 440 441func (s *RemoteSuite) TestFetchNoErrAlreadyUpToDate(c *C) { 442 url := s.GetBasicLocalRepositoryURL() 443 s.doTestFetchNoErrAlreadyUpToDate(c, url) 444} 445 446func (s *RemoteSuite) TestFetchNoErrAlreadyUpToDateButStillUpdateLocalRemoteRefs(c *C) { 447 r := NewRemote(memory.NewStorage(), &config.RemoteConfig{ 448 URLs: []string{s.GetBasicLocalRepositoryURL()}, 449 }) 450 451 o := &FetchOptions{ 452 RefSpecs: []config.RefSpec{ 453 config.RefSpec("+refs/heads/*:refs/remotes/origin/*"), 454 }, 455 } 456 457 err := r.Fetch(o) 458 c.Assert(err, IsNil) 459 460 // Simulate an out of date remote ref even though we have the new commit locally 461 r.s.SetReference(plumbing.NewReferenceFromStrings( 462 "refs/remotes/origin/master", "918c48b83bd081e863dbe1b80f8998f058cd8294", 463 )) 464 465 err = r.Fetch(o) 466 c.Assert(err, IsNil) 467 468 exp := plumbing.NewReferenceFromStrings( 469 "refs/remotes/origin/master", "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", 470 ) 471 472 ref, err := r.s.Reference("refs/remotes/origin/master") 473 c.Assert(err, IsNil) 474 c.Assert(exp.String(), Equals, ref.String()) 475} 476 477func (s *RemoteSuite) TestFetchNoErrAlreadyUpToDateWithNonCommitObjects(c *C) { 478 fixture := fixtures.ByTag("tags").One() 479 url := s.GetLocalRepositoryURL(fixture) 480 s.doTestFetchNoErrAlreadyUpToDate(c, url) 481} 482 483func (s *RemoteSuite) doTestFetchNoErrAlreadyUpToDate(c *C, url string) { 484 r := NewRemote(memory.NewStorage(), &config.RemoteConfig{URLs: []string{url}}) 485 486 o := &FetchOptions{ 487 RefSpecs: []config.RefSpec{ 488 config.RefSpec("+refs/heads/*:refs/remotes/origin/*"), 489 }, 490 } 491 492 err := r.Fetch(o) 493 c.Assert(err, IsNil) 494 err = r.Fetch(o) 495 c.Assert(err, Equals, NoErrAlreadyUpToDate) 496} 497 498func (s *RemoteSuite) testFetchFastForward(c *C, sto storage.Storer) { 499 r := NewRemote(sto, &config.RemoteConfig{ 500 URLs: []string{s.GetBasicLocalRepositoryURL()}, 501 }) 502 503 s.testFetch(c, r, &FetchOptions{ 504 RefSpecs: []config.RefSpec{ 505 config.RefSpec("+refs/heads/master:refs/heads/master"), 506 }, 507 }, []*plumbing.Reference{ 508 plumbing.NewReferenceFromStrings("refs/heads/master", "6ecf0ef2c2dffb796033e5a02219af86ec6584e5"), 509 }) 510 511 // First make sure that we error correctly when a force is required. 512 err := r.Fetch(&FetchOptions{ 513 RefSpecs: []config.RefSpec{ 514 config.RefSpec("refs/heads/branch:refs/heads/master"), 515 }, 516 }) 517 c.Assert(err, Equals, ErrForceNeeded) 518 519 // And that forcing it fixes the problem. 520 err = r.Fetch(&FetchOptions{ 521 RefSpecs: []config.RefSpec{ 522 config.RefSpec("+refs/heads/branch:refs/heads/master"), 523 }, 524 }) 525 c.Assert(err, IsNil) 526 527 // Now test that a fast-forward, non-force fetch works. 528 r.s.SetReference(plumbing.NewReferenceFromStrings( 529 "refs/heads/master", "918c48b83bd081e863dbe1b80f8998f058cd8294", 530 )) 531 s.testFetch(c, r, &FetchOptions{ 532 RefSpecs: []config.RefSpec{ 533 config.RefSpec("refs/heads/master:refs/heads/master"), 534 }, 535 }, []*plumbing.Reference{ 536 plumbing.NewReferenceFromStrings("refs/heads/master", "6ecf0ef2c2dffb796033e5a02219af86ec6584e5"), 537 }) 538} 539 540func (s *RemoteSuite) TestFetchFastForwardMem(c *C) { 541 s.testFetchFastForward(c, memory.NewStorage()) 542} 543 544func (s *RemoteSuite) TestFetchFastForwardFS(c *C) { 545 fs := s.TemporalFilesystem(c) 546 547 fss := filesystem.NewStorage(fs, cache.NewObjectLRUDefault()) 548 549 // This exercises `storage.filesystem.Storage.CheckAndSetReference()`. 550 s.testFetchFastForward(c, fss) 551} 552 553func (s *RemoteSuite) TestString(c *C) { 554 r := NewRemote(nil, &config.RemoteConfig{ 555 Name: "foo", 556 URLs: []string{"https://github.com/git-fixtures/basic.git"}, 557 }) 558 559 c.Assert(r.String(), Equals, ""+ 560 "foo\thttps://github.com/git-fixtures/basic.git (fetch)\n"+ 561 "foo\thttps://github.com/git-fixtures/basic.git (push)", 562 ) 563} 564 565func (s *RemoteSuite) TestPushToEmptyRepository(c *C) { 566 url := c.MkDir() 567 568 server, err := PlainInit(url, true) 569 c.Assert(err, IsNil) 570 571 srcFs := fixtures.Basic().One().DotGit() 572 sto := filesystem.NewStorage(srcFs, cache.NewObjectLRUDefault()) 573 574 r := NewRemote(sto, &config.RemoteConfig{ 575 Name: DefaultRemoteName, 576 URLs: []string{url}, 577 }) 578 579 rs := config.RefSpec("refs/heads/*:refs/heads/*") 580 err = r.Push(&PushOptions{ 581 RefSpecs: []config.RefSpec{rs}, 582 }) 583 c.Assert(err, IsNil) 584 585 iter, err := r.s.IterReferences() 586 c.Assert(err, IsNil) 587 588 expected := make(map[string]string) 589 iter.ForEach(func(ref *plumbing.Reference) error { 590 if !ref.Name().IsBranch() { 591 return nil 592 } 593 594 expected[ref.Name().String()] = ref.Hash().String() 595 return nil 596 }) 597 c.Assert(err, IsNil) 598 599 AssertReferences(c, server, expected) 600 601} 602 603func (s *RemoteSuite) TestPushContext(c *C) { 604 url := c.MkDir() 605 606 _, err := PlainInit(url, true) 607 c.Assert(err, IsNil) 608 609 fs := fixtures.ByURL("https://github.com/git-fixtures/tags.git").One().DotGit() 610 sto := filesystem.NewStorage(fs, cache.NewObjectLRUDefault()) 611 612 r := NewRemote(sto, &config.RemoteConfig{ 613 Name: DefaultRemoteName, 614 URLs: []string{url}, 615 }) 616 617 ctx, cancel := context.WithCancel(context.Background()) 618 defer cancel() 619 620 numGoroutines := runtime.NumGoroutine() 621 622 err = r.PushContext(ctx, &PushOptions{ 623 RefSpecs: []config.RefSpec{"refs/tags/*:refs/tags/*"}, 624 }) 625 c.Assert(err, IsNil) 626 627 eventually(c, func() bool { 628 return runtime.NumGoroutine() <= numGoroutines 629 }) 630} 631 632func eventually(c *C, condition func() bool) { 633 select { 634 case <-time.After(5 * time.Second): 635 default: 636 if condition() { 637 break 638 } 639 time.Sleep(100 * time.Millisecond) 640 } 641 642 c.Assert(condition(), Equals, true) 643} 644 645func (s *RemoteSuite) TestPushContextCanceled(c *C) { 646 url := c.MkDir() 647 648 _, err := PlainInit(url, true) 649 c.Assert(err, IsNil) 650 651 fs := fixtures.ByURL("https://github.com/git-fixtures/tags.git").One().DotGit() 652 sto := filesystem.NewStorage(fs, cache.NewObjectLRUDefault()) 653 654 r := NewRemote(sto, &config.RemoteConfig{ 655 Name: DefaultRemoteName, 656 URLs: []string{url}, 657 }) 658 659 ctx, cancel := context.WithCancel(context.Background()) 660 cancel() 661 662 numGoroutines := runtime.NumGoroutine() 663 664 err = r.PushContext(ctx, &PushOptions{ 665 RefSpecs: []config.RefSpec{"refs/tags/*:refs/tags/*"}, 666 }) 667 c.Assert(err, Equals, context.Canceled) 668 669 eventually(c, func() bool { 670 return runtime.NumGoroutine() <= numGoroutines 671 }) 672} 673 674func (s *RemoteSuite) TestPushTags(c *C) { 675 url := c.MkDir() 676 677 server, err := PlainInit(url, true) 678 c.Assert(err, IsNil) 679 680 fs := fixtures.ByURL("https://github.com/git-fixtures/tags.git").One().DotGit() 681 sto := filesystem.NewStorage(fs, cache.NewObjectLRUDefault()) 682 683 r := NewRemote(sto, &config.RemoteConfig{ 684 Name: DefaultRemoteName, 685 URLs: []string{url}, 686 }) 687 688 err = r.Push(&PushOptions{ 689 RefSpecs: []config.RefSpec{"refs/tags/*:refs/tags/*"}, 690 }) 691 c.Assert(err, IsNil) 692 693 AssertReferences(c, server, map[string]string{ 694 "refs/tags/lightweight-tag": "f7b877701fbf855b44c0a9e86f3fdce2c298b07f", 695 "refs/tags/annotated-tag": "b742a2a9fa0afcfa9a6fad080980fbc26b007c69", 696 "refs/tags/commit-tag": "ad7897c0fb8e7d9a9ba41fa66072cf06095a6cfc", 697 "refs/tags/blob-tag": "fe6cb94756faa81e5ed9240f9191b833db5f40ae", 698 "refs/tags/tree-tag": "152175bf7e5580299fa1f0ba41ef6474cc043b70", 699 }) 700} 701 702func (s *RemoteSuite) TestPushFollowTags(c *C) { 703 url := c.MkDir() 704 705 server, err := PlainInit(url, true) 706 c.Assert(err, IsNil) 707 708 fs := fixtures.ByURL("https://github.com/git-fixtures/basic.git").One().DotGit() 709 sto := filesystem.NewStorage(fs, cache.NewObjectLRUDefault()) 710 711 r := NewRemote(sto, &config.RemoteConfig{ 712 Name: DefaultRemoteName, 713 URLs: []string{url}, 714 }) 715 716 localRepo := newRepository(sto, fs) 717 tipTag, err := localRepo.CreateTag( 718 "tip", 719 plumbing.NewHash("e8d3ffab552895c19b9fcf7aa264d277cde33881"), 720 &CreateTagOptions{ 721 Message: "an annotated tag", 722 }, 723 ) 724 c.Assert(err, IsNil) 725 726 initialTag, err := localRepo.CreateTag( 727 "initial-commit", 728 plumbing.NewHash("b029517f6300c2da0f4b651b8642506cd6aaf45d"), 729 &CreateTagOptions{ 730 Message: "a tag for the initial commit", 731 }, 732 ) 733 c.Assert(err, IsNil) 734 735 _, err = localRepo.CreateTag( 736 "master-tag", 737 plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"), 738 &CreateTagOptions{ 739 Message: "a tag with a commit not reachable from branch", 740 }, 741 ) 742 c.Assert(err, IsNil) 743 744 err = r.Push(&PushOptions{ 745 RefSpecs: []config.RefSpec{"+refs/heads/branch:refs/heads/branch"}, 746 FollowTags: true, 747 }) 748 c.Assert(err, IsNil) 749 750 AssertReferences(c, server, map[string]string{ 751 "refs/heads/branch": "e8d3ffab552895c19b9fcf7aa264d277cde33881", 752 "refs/tags/tip": tipTag.Hash().String(), 753 "refs/tags/initial-commit": initialTag.Hash().String(), 754 }) 755 756 AssertReferencesMissing(c, server, []string{ 757 "refs/tags/master-tag", 758 }) 759} 760 761func (s *RemoteSuite) TestPushNoErrAlreadyUpToDate(c *C) { 762 fs := fixtures.Basic().One().DotGit() 763 sto := filesystem.NewStorage(fs, cache.NewObjectLRUDefault()) 764 765 r := NewRemote(sto, &config.RemoteConfig{ 766 Name: DefaultRemoteName, 767 URLs: []string{fs.Root()}, 768 }) 769 770 err := r.Push(&PushOptions{ 771 RefSpecs: []config.RefSpec{"refs/heads/*:refs/heads/*"}, 772 }) 773 c.Assert(err, Equals, NoErrAlreadyUpToDate) 774} 775 776func (s *RemoteSuite) TestPushDeleteReference(c *C) { 777 fs := fixtures.Basic().One().DotGit() 778 sto := filesystem.NewStorage(fs, cache.NewObjectLRUDefault()) 779 780 url := c.MkDir() 781 782 r, err := PlainClone(url, true, &CloneOptions{ 783 URL: fs.Root(), 784 }) 785 c.Assert(err, IsNil) 786 787 remote, err := r.Remote(DefaultRemoteName) 788 c.Assert(err, IsNil) 789 790 err = remote.Push(&PushOptions{ 791 RefSpecs: []config.RefSpec{":refs/heads/branch"}, 792 }) 793 c.Assert(err, IsNil) 794 795 _, err = sto.Reference(plumbing.ReferenceName("refs/heads/branch")) 796 c.Assert(err, Equals, plumbing.ErrReferenceNotFound) 797 798 _, err = r.Storer.Reference(plumbing.ReferenceName("refs/heads/branch")) 799 c.Assert(err, Equals, plumbing.ErrReferenceNotFound) 800} 801 802func (s *RemoteSuite) TestForcePushDeleteReference(c *C) { 803 fs := fixtures.Basic().One().DotGit() 804 sto := filesystem.NewStorage(fs, cache.NewObjectLRUDefault()) 805 806 url := c.MkDir() 807 808 r, err := PlainClone(url, true, &CloneOptions{ 809 URL: fs.Root(), 810 }) 811 c.Assert(err, IsNil) 812 813 remote, err := r.Remote(DefaultRemoteName) 814 c.Assert(err, IsNil) 815 816 err = remote.Push(&PushOptions{ 817 RefSpecs: []config.RefSpec{":refs/heads/branch"}, 818 Force: true, 819 }) 820 c.Assert(err, IsNil) 821 822 _, err = sto.Reference(plumbing.ReferenceName("refs/heads/branch")) 823 c.Assert(err, Equals, plumbing.ErrReferenceNotFound) 824 825 _, err = r.Storer.Reference(plumbing.ReferenceName("refs/heads/branch")) 826 c.Assert(err, Equals, plumbing.ErrReferenceNotFound) 827} 828 829func (s *RemoteSuite) TestPushRejectNonFastForward(c *C) { 830 fs := fixtures.Basic().One().DotGit() 831 server := filesystem.NewStorage(fs, cache.NewObjectLRUDefault()) 832 833 url := c.MkDir() 834 835 r, err := PlainClone(url, true, &CloneOptions{ 836 URL: fs.Root(), 837 }) 838 c.Assert(err, IsNil) 839 840 remote, err := r.Remote(DefaultRemoteName) 841 c.Assert(err, IsNil) 842 843 branch := plumbing.ReferenceName("refs/heads/branch") 844 oldRef, err := server.Reference(branch) 845 c.Assert(err, IsNil) 846 c.Assert(oldRef, NotNil) 847 848 err = remote.Push(&PushOptions{RefSpecs: []config.RefSpec{ 849 "refs/heads/master:refs/heads/branch", 850 }}) 851 c.Assert(err, ErrorMatches, "non-fast-forward update: refs/heads/branch") 852 853 newRef, err := server.Reference(branch) 854 c.Assert(err, IsNil) 855 c.Assert(newRef, DeepEquals, oldRef) 856} 857 858func (s *RemoteSuite) TestPushForce(c *C) { 859 f := fixtures.Basic().One() 860 sto := filesystem.NewStorage(f.DotGit(), cache.NewObjectLRUDefault()) 861 862 dstFs := f.DotGit() 863 dstSto := filesystem.NewStorage(dstFs, cache.NewObjectLRUDefault()) 864 865 url := dstFs.Root() 866 r := NewRemote(sto, &config.RemoteConfig{ 867 Name: DefaultRemoteName, 868 URLs: []string{url}, 869 }) 870 871 oldRef, err := dstSto.Reference(plumbing.ReferenceName("refs/heads/branch")) 872 c.Assert(err, IsNil) 873 c.Assert(oldRef, NotNil) 874 875 err = r.Push(&PushOptions{RefSpecs: []config.RefSpec{ 876 config.RefSpec("+refs/heads/master:refs/heads/branch"), 877 }}) 878 c.Assert(err, IsNil) 879 880 newRef, err := dstSto.Reference(plumbing.ReferenceName("refs/heads/branch")) 881 c.Assert(err, IsNil) 882 c.Assert(newRef, Not(DeepEquals), oldRef) 883} 884 885func (s *RemoteSuite) TestPushForceWithOption(c *C) { 886 f := fixtures.Basic().One() 887 sto := filesystem.NewStorage(f.DotGit(), cache.NewObjectLRUDefault()) 888 889 dstFs := f.DotGit() 890 dstSto := filesystem.NewStorage(dstFs, cache.NewObjectLRUDefault()) 891 892 url := dstFs.Root() 893 r := NewRemote(sto, &config.RemoteConfig{ 894 Name: DefaultRemoteName, 895 URLs: []string{url}, 896 }) 897 898 oldRef, err := dstSto.Reference(plumbing.ReferenceName("refs/heads/branch")) 899 c.Assert(err, IsNil) 900 c.Assert(oldRef, NotNil) 901 902 err = r.Push(&PushOptions{ 903 RefSpecs: []config.RefSpec{"refs/heads/master:refs/heads/branch"}, 904 Force: true, 905 }) 906 c.Assert(err, IsNil) 907 908 newRef, err := dstSto.Reference(plumbing.ReferenceName("refs/heads/branch")) 909 c.Assert(err, IsNil) 910 c.Assert(newRef, Not(DeepEquals), oldRef) 911} 912 913func (s *RemoteSuite) TestPushForceWithLease_success(c *C) { 914 testCases := []struct { 915 desc string 916 forceWithLease ForceWithLease 917 }{ 918 { 919 desc: "no arguments", 920 forceWithLease: ForceWithLease{}, 921 }, 922 { 923 desc: "ref name", 924 forceWithLease: ForceWithLease{ 925 RefName: plumbing.ReferenceName("refs/heads/branch"), 926 }, 927 }, 928 { 929 desc: "ref name and sha", 930 forceWithLease: ForceWithLease{ 931 RefName: plumbing.ReferenceName("refs/heads/branch"), 932 Hash: plumbing.NewHash("e8d3ffab552895c19b9fcf7aa264d277cde33881"), 933 }, 934 }, 935 } 936 937 for _, tc := range testCases { 938 c.Log("Executing test cases:", tc.desc) 939 940 f := fixtures.Basic().One() 941 sto := filesystem.NewStorage(f.DotGit(), cache.NewObjectLRUDefault()) 942 dstFs := f.DotGit() 943 dstSto := filesystem.NewStorage(dstFs, cache.NewObjectLRUDefault()) 944 945 newCommit := plumbing.NewHashReference( 946 "refs/heads/branch", plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9"), 947 ) 948 c.Assert(sto.SetReference(newCommit), IsNil) 949 950 ref, err := sto.Reference("refs/heads/branch") 951 c.Assert(err, IsNil) 952 c.Log(ref.String()) 953 954 url := dstFs.Root() 955 r := NewRemote(sto, &config.RemoteConfig{ 956 Name: DefaultRemoteName, 957 URLs: []string{url}, 958 }) 959 960 oldRef, err := dstSto.Reference("refs/heads/branch") 961 c.Assert(err, IsNil) 962 c.Assert(oldRef, NotNil) 963 964 c.Assert(r.Push(&PushOptions{ 965 RefSpecs: []config.RefSpec{"refs/heads/branch:refs/heads/branch"}, 966 ForceWithLease: &ForceWithLease{}, 967 }), IsNil) 968 969 newRef, err := dstSto.Reference("refs/heads/branch") 970 c.Assert(err, IsNil) 971 c.Assert(newRef, DeepEquals, newCommit) 972 } 973} 974 975func (s *RemoteSuite) TestPushForceWithLease_failure(c *C) { 976 testCases := []struct { 977 desc string 978 forceWithLease ForceWithLease 979 }{ 980 { 981 desc: "no arguments", 982 forceWithLease: ForceWithLease{}, 983 }, 984 { 985 desc: "ref name", 986 forceWithLease: ForceWithLease{ 987 RefName: plumbing.ReferenceName("refs/heads/branch"), 988 }, 989 }, 990 { 991 desc: "ref name and sha", 992 forceWithLease: ForceWithLease{ 993 RefName: plumbing.ReferenceName("refs/heads/branch"), 994 Hash: plumbing.NewHash("152175bf7e5580299fa1f0ba41ef6474cc043b70"), 995 }, 996 }, 997 } 998 999 for _, tc := range testCases { 1000 c.Log("Executing test cases:", tc.desc) 1001 1002 f := fixtures.Basic().One() 1003 sto := filesystem.NewStorage(f.DotGit(), cache.NewObjectLRUDefault()) 1004 c.Assert(sto.SetReference( 1005 plumbing.NewHashReference( 1006 "refs/heads/branch", plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9"), 1007 ), 1008 ), IsNil) 1009 1010 dstFs := f.DotGit() 1011 dstSto := filesystem.NewStorage(dstFs, cache.NewObjectLRUDefault()) 1012 c.Assert(dstSto.SetReference( 1013 plumbing.NewHashReference( 1014 "refs/heads/branch", plumbing.NewHash("ad7897c0fb8e7d9a9ba41fa66072cf06095a6cfc"), 1015 ), 1016 ), IsNil) 1017 1018 url := dstFs.Root() 1019 r := NewRemote(sto, &config.RemoteConfig{ 1020 Name: DefaultRemoteName, 1021 URLs: []string{url}, 1022 }) 1023 1024 oldRef, err := dstSto.Reference("refs/heads/branch") 1025 c.Assert(err, IsNil) 1026 c.Assert(oldRef, NotNil) 1027 1028 err = r.Push(&PushOptions{ 1029 RefSpecs: []config.RefSpec{"refs/heads/branch:refs/heads/branch"}, 1030 ForceWithLease: &ForceWithLease{}, 1031 }) 1032 1033 c.Assert(err, DeepEquals, errors.New("non-fast-forward update: refs/heads/branch")) 1034 1035 newRef, err := dstSto.Reference("refs/heads/branch") 1036 c.Assert(err, IsNil) 1037 c.Assert(newRef, Not(DeepEquals), plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9")) 1038 } 1039} 1040 1041func (s *RemoteSuite) TestPushPrune(c *C) { 1042 fs := fixtures.Basic().One().DotGit() 1043 1044 url := c.MkDir() 1045 1046 server, err := PlainClone(url, true, &CloneOptions{ 1047 URL: fs.Root(), 1048 }) 1049 c.Assert(err, IsNil) 1050 1051 dir := c.MkDir() 1052 1053 r, err := PlainClone(dir, true, &CloneOptions{ 1054 URL: url, 1055 }) 1056 c.Assert(err, IsNil) 1057 1058 tag, err := r.Reference(plumbing.ReferenceName("refs/tags/v1.0.0"), true) 1059 c.Assert(err, IsNil) 1060 1061 err = r.DeleteTag("v1.0.0") 1062 c.Assert(err, IsNil) 1063 1064 remote, err := r.Remote(DefaultRemoteName) 1065 c.Assert(err, IsNil) 1066 1067 ref, err := r.Reference(plumbing.ReferenceName("refs/heads/master"), true) 1068 c.Assert(err, IsNil) 1069 1070 err = remote.Push(&PushOptions{ 1071 RefSpecs: []config.RefSpec{ 1072 config.RefSpec("refs/heads/*:refs/heads/*"), 1073 }, 1074 Prune: true, 1075 }) 1076 c.Assert(err, Equals, NoErrAlreadyUpToDate) 1077 1078 AssertReferences(c, server, map[string]string{ 1079 "refs/tags/v1.0.0": tag.Hash().String(), 1080 }) 1081 1082 err = remote.Push(&PushOptions{ 1083 RefSpecs: []config.RefSpec{ 1084 config.RefSpec("*:*"), 1085 }, 1086 Prune: true, 1087 }) 1088 c.Assert(err, IsNil) 1089 1090 AssertReferences(c, server, map[string]string{ 1091 "refs/remotes/origin/master": ref.Hash().String(), 1092 }) 1093 1094 AssertReferences(c, server, map[string]string{ 1095 "refs/remotes/origin/master": ref.Hash().String(), 1096 }) 1097 1098 _, err = server.Reference(plumbing.ReferenceName("refs/tags/v1.0.0"), true) 1099 c.Assert(err, Equals, plumbing.ErrReferenceNotFound) 1100} 1101 1102func (s *RemoteSuite) TestPushNewReference(c *C) { 1103 fs := fixtures.Basic().One().DotGit() 1104 1105 url := c.MkDir() 1106 1107 server, err := PlainClone(url, true, &CloneOptions{ 1108 URL: fs.Root(), 1109 }) 1110 c.Assert(err, IsNil) 1111 1112 dir := c.MkDir() 1113 1114 r, err := PlainClone(dir, true, &CloneOptions{ 1115 URL: url, 1116 }) 1117 c.Assert(err, IsNil) 1118 1119 remote, err := r.Remote(DefaultRemoteName) 1120 c.Assert(err, IsNil) 1121 1122 ref, err := r.Reference(plumbing.ReferenceName("refs/heads/master"), true) 1123 c.Assert(err, IsNil) 1124 1125 err = remote.Push(&PushOptions{RefSpecs: []config.RefSpec{ 1126 "refs/heads/master:refs/heads/branch2", 1127 }}) 1128 c.Assert(err, IsNil) 1129 1130 AssertReferences(c, server, map[string]string{ 1131 "refs/heads/branch2": ref.Hash().String(), 1132 }) 1133 1134 AssertReferences(c, r, map[string]string{ 1135 "refs/remotes/origin/branch2": ref.Hash().String(), 1136 }) 1137} 1138 1139func (s *RemoteSuite) TestPushNewReferenceAndDeleteInBatch(c *C) { 1140 fs := fixtures.Basic().One().DotGit() 1141 1142 url := c.MkDir() 1143 1144 server, err := PlainClone(url, true, &CloneOptions{ 1145 URL: fs.Root(), 1146 }) 1147 c.Assert(err, IsNil) 1148 1149 dir := c.MkDir() 1150 1151 r, err := PlainClone(dir, true, &CloneOptions{ 1152 URL: url, 1153 }) 1154 c.Assert(err, IsNil) 1155 1156 remote, err := r.Remote(DefaultRemoteName) 1157 c.Assert(err, IsNil) 1158 1159 ref, err := r.Reference(plumbing.ReferenceName("refs/heads/master"), true) 1160 c.Assert(err, IsNil) 1161 1162 err = remote.Push(&PushOptions{RefSpecs: []config.RefSpec{ 1163 "refs/heads/master:refs/heads/branch2", 1164 ":refs/heads/branch", 1165 }}) 1166 c.Assert(err, IsNil) 1167 1168 AssertReferences(c, server, map[string]string{ 1169 "refs/heads/branch2": ref.Hash().String(), 1170 }) 1171 1172 AssertReferences(c, r, map[string]string{ 1173 "refs/remotes/origin/branch2": ref.Hash().String(), 1174 }) 1175 1176 _, err = server.Storer.Reference(plumbing.ReferenceName("refs/heads/branch")) 1177 c.Assert(err, Equals, plumbing.ErrReferenceNotFound) 1178} 1179 1180func (s *RemoteSuite) TestPushInvalidEndpoint(c *C) { 1181 r := NewRemote(nil, &config.RemoteConfig{Name: "foo", URLs: []string{"http://\\"}}) 1182 err := r.Push(&PushOptions{RemoteName: "foo"}) 1183 c.Assert(err, ErrorMatches, ".*invalid character.*") 1184} 1185 1186func (s *RemoteSuite) TestPushNonExistentEndpoint(c *C) { 1187 r := NewRemote(nil, &config.RemoteConfig{Name: "foo", URLs: []string{"ssh://non-existent/foo.git"}}) 1188 err := r.Push(&PushOptions{}) 1189 c.Assert(err, NotNil) 1190} 1191 1192func (s *RemoteSuite) TestPushOverriddenEndpoint(c *C) { 1193 r := NewRemote(nil, &config.RemoteConfig{Name: "origin", URLs: []string{"http://perfectly-valid-url.example.com"}}) 1194 err := r.Push(&PushOptions{RemoteURL: "http://\\"}) 1195 c.Assert(err, ErrorMatches, ".*invalid character.*") 1196} 1197 1198func (s *RemoteSuite) TestPushInvalidSchemaEndpoint(c *C) { 1199 r := NewRemote(nil, &config.RemoteConfig{Name: "origin", URLs: []string{"qux://foo"}}) 1200 err := r.Push(&PushOptions{}) 1201 c.Assert(err, ErrorMatches, ".*unsupported scheme.*") 1202} 1203 1204func (s *RemoteSuite) TestPushInvalidFetchOptions(c *C) { 1205 r := NewRemote(nil, &config.RemoteConfig{Name: "foo", URLs: []string{"qux://foo"}}) 1206 invalid := config.RefSpec("^*$ñ") 1207 err := r.Push(&PushOptions{RefSpecs: []config.RefSpec{invalid}}) 1208 c.Assert(err, Equals, config.ErrRefSpecMalformedSeparator) 1209} 1210 1211func (s *RemoteSuite) TestPushInvalidRefSpec(c *C) { 1212 r := NewRemote(nil, &config.RemoteConfig{ 1213 Name: DefaultRemoteName, 1214 URLs: []string{"some-url"}, 1215 }) 1216 1217 rs := config.RefSpec("^*$**") 1218 err := r.Push(&PushOptions{ 1219 RefSpecs: []config.RefSpec{rs}, 1220 }) 1221 c.Assert(err, Equals, config.ErrRefSpecMalformedSeparator) 1222} 1223 1224func (s *RemoteSuite) TestPushWrongRemoteName(c *C) { 1225 r := NewRemote(nil, &config.RemoteConfig{ 1226 Name: DefaultRemoteName, 1227 URLs: []string{"some-url"}, 1228 }) 1229 1230 err := r.Push(&PushOptions{ 1231 RemoteName: "other-remote", 1232 }) 1233 c.Assert(err, ErrorMatches, ".*remote names don't match.*") 1234} 1235 1236func (s *RemoteSuite) TestGetHaves(c *C) { 1237 f := fixtures.Basic().One() 1238 sto := filesystem.NewStorage(f.DotGit(), cache.NewObjectLRUDefault()) 1239 1240 var localRefs = []*plumbing.Reference{ 1241 // Exists 1242 plumbing.NewReferenceFromStrings( 1243 "foo", 1244 "b029517f6300c2da0f4b651b8642506cd6aaf45d", 1245 ), 1246 // Exists 1247 plumbing.NewReferenceFromStrings( 1248 "bar", 1249 "b8e471f58bcbca63b07bda20e428190409c2db47", 1250 ), 1251 // Doesn't Exist 1252 plumbing.NewReferenceFromStrings( 1253 "qux", 1254 "0000000", 1255 ), 1256 } 1257 1258 l, err := getHaves(localRefs, memory.NewStorage(), sto, 0) 1259 c.Assert(err, IsNil) 1260 c.Assert(l, HasLen, 2) 1261} 1262 1263func (s *RemoteSuite) TestList(c *C) { 1264 repo := fixtures.Basic().One() 1265 remote := NewRemote(memory.NewStorage(), &config.RemoteConfig{ 1266 Name: DefaultRemoteName, 1267 URLs: []string{repo.URL}, 1268 }) 1269 1270 refs, err := remote.List(&ListOptions{}) 1271 c.Assert(err, IsNil) 1272 1273 expected := []*plumbing.Reference{ 1274 plumbing.NewSymbolicReference("HEAD", "refs/heads/master"), 1275 plumbing.NewReferenceFromStrings("refs/heads/master", "6ecf0ef2c2dffb796033e5a02219af86ec6584e5"), 1276 plumbing.NewReferenceFromStrings("refs/heads/branch", "e8d3ffab552895c19b9fcf7aa264d277cde33881"), 1277 plumbing.NewReferenceFromStrings("refs/pull/1/head", "b8e471f58bcbca63b07bda20e428190409c2db47"), 1278 plumbing.NewReferenceFromStrings("refs/pull/2/head", "9632f02833b2f9613afb5e75682132b0b22e4a31"), 1279 plumbing.NewReferenceFromStrings("refs/pull/2/merge", "c37f58a130ca555e42ff96a071cb9ccb3f437504"), 1280 } 1281 c.Assert(len(refs), Equals, len(expected)) 1282 for _, e := range expected { 1283 found := false 1284 for _, r := range refs { 1285 if r.Name() == e.Name() { 1286 found = true 1287 c.Assert(r, DeepEquals, e) 1288 } 1289 } 1290 c.Assert(found, Equals, true) 1291 } 1292} 1293 1294func (s *RemoteSuite) TestListPeeling(c *C) { 1295 remote := NewRemote(memory.NewStorage(), &config.RemoteConfig{ 1296 Name: DefaultRemoteName, 1297 URLs: []string{"https://github.com/git-fixtures/tags.git"}, 1298 }) 1299 1300 for _, tc := range []struct { 1301 peelingOption PeelingOption 1302 expectPeeled bool 1303 expectNonPeeled bool 1304 }{ 1305 {peelingOption: AppendPeeled, expectPeeled: true, expectNonPeeled: true}, 1306 {peelingOption: IgnorePeeled, expectPeeled: false, expectNonPeeled: true}, 1307 {peelingOption: OnlyPeeled, expectPeeled: true, expectNonPeeled: false}, 1308 } { 1309 refs, err := remote.List(&ListOptions{ 1310 PeelingOption: tc.peelingOption, 1311 }) 1312 c.Assert(err, IsNil) 1313 c.Assert(len(refs) > 0, Equals, true) 1314 1315 foundPeeled, foundNonPeeled := false, false 1316 for _, ref := range refs { 1317 if strings.HasSuffix(ref.Name().String(), peeledSuffix) { 1318 foundPeeled = true 1319 } else { 1320 foundNonPeeled = true 1321 } 1322 } 1323 1324 c.Assert(foundPeeled, Equals, tc.expectPeeled) 1325 c.Assert(foundNonPeeled, Equals, tc.expectNonPeeled) 1326 } 1327} 1328 1329func (s *RemoteSuite) TestListTimeout(c *C) { 1330 remote := NewRemote(memory.NewStorage(), &config.RemoteConfig{ 1331 Name: DefaultRemoteName, 1332 URLs: []string{"https://deelay.me/60000/https://httpstat.us/503"}, 1333 }) 1334 1335 _, err := remote.List(&ListOptions{}) 1336 1337 c.Assert(err, NotNil) 1338} 1339 1340func (s *RemoteSuite) TestUpdateShallows(c *C) { 1341 hashes := []plumbing.Hash{ 1342 plumbing.NewHash("0000000000000000000000000000000000000001"), 1343 plumbing.NewHash("0000000000000000000000000000000000000002"), 1344 plumbing.NewHash("0000000000000000000000000000000000000003"), 1345 plumbing.NewHash("0000000000000000000000000000000000000004"), 1346 plumbing.NewHash("0000000000000000000000000000000000000005"), 1347 plumbing.NewHash("0000000000000000000000000000000000000006"), 1348 } 1349 1350 tests := []struct { 1351 hashes []plumbing.Hash 1352 result []plumbing.Hash 1353 }{ 1354 // add to empty shallows 1355 {hashes[0:2], hashes[0:2]}, 1356 // add new hashes 1357 {hashes[2:4], hashes[0:4]}, 1358 // add some hashes already in shallow list 1359 {hashes[2:6], hashes[0:6]}, 1360 // add all hashes 1361 {hashes[0:6], hashes[0:6]}, 1362 // add empty list 1363 {nil, hashes[0:6]}, 1364 } 1365 1366 remote := NewRemote(memory.NewStorage(), &config.RemoteConfig{ 1367 Name: DefaultRemoteName, 1368 }) 1369 1370 shallows, err := remote.s.Shallow() 1371 c.Assert(err, IsNil) 1372 c.Assert(len(shallows), Equals, 0) 1373 1374 resp := new(packp.UploadPackResponse) 1375 o := &FetchOptions{ 1376 Depth: 1, 1377 } 1378 1379 for _, t := range tests { 1380 resp.Shallows = t.hashes 1381 err = remote.updateShallow(o, resp) 1382 c.Assert(err, IsNil) 1383 1384 shallow, err := remote.s.Shallow() 1385 c.Assert(err, IsNil) 1386 c.Assert(len(shallow), Equals, len(t.result)) 1387 c.Assert(shallow, DeepEquals, t.result) 1388 } 1389} 1390 1391func (s *RemoteSuite) TestUseRefDeltas(c *C) { 1392 url := c.MkDir() 1393 1394 _, err := PlainInit(url, true) 1395 c.Assert(err, IsNil) 1396 1397 fs := fixtures.ByURL("https://github.com/git-fixtures/tags.git").One().DotGit() 1398 sto := filesystem.NewStorage(fs, cache.NewObjectLRUDefault()) 1399 1400 r := NewRemote(sto, &config.RemoteConfig{ 1401 Name: DefaultRemoteName, 1402 URLs: []string{url}, 1403 }) 1404 1405 ar := packp.NewAdvRefs() 1406 1407 ar.Capabilities.Add(capability.OFSDelta) 1408 c.Assert(r.useRefDeltas(ar), Equals, false) 1409 1410 ar.Capabilities.Delete(capability.OFSDelta) 1411 c.Assert(r.useRefDeltas(ar), Equals, true) 1412} 1413 1414func (s *RemoteSuite) TestPushRequireRemoteRefs(c *C) { 1415 f := fixtures.Basic().One() 1416 sto := filesystem.NewStorage(f.DotGit(), cache.NewObjectLRUDefault()) 1417 1418 dstFs := f.DotGit() 1419 dstSto := filesystem.NewStorage(dstFs, cache.NewObjectLRUDefault()) 1420 1421 url := dstFs.Root() 1422 r := NewRemote(sto, &config.RemoteConfig{ 1423 Name: DefaultRemoteName, 1424 URLs: []string{url}, 1425 }) 1426 1427 oldRef, err := dstSto.Reference(plumbing.ReferenceName("refs/heads/branch")) 1428 c.Assert(err, IsNil) 1429 c.Assert(oldRef, NotNil) 1430 1431 otherRef, err := dstSto.Reference(plumbing.ReferenceName("refs/heads/master")) 1432 c.Assert(err, IsNil) 1433 c.Assert(otherRef, NotNil) 1434 1435 err = r.Push(&PushOptions{ 1436 RefSpecs: []config.RefSpec{"refs/heads/master:refs/heads/branch"}, 1437 RequireRemoteRefs: []config.RefSpec{config.RefSpec(otherRef.Hash().String() + ":refs/heads/branch")}, 1438 }) 1439 c.Assert(err, ErrorMatches, "remote ref refs/heads/branch required to be .* but is .*") 1440 1441 newRef, err := dstSto.Reference(plumbing.ReferenceName("refs/heads/branch")) 1442 c.Assert(err, IsNil) 1443 c.Assert(newRef, DeepEquals, oldRef) 1444 1445 err = r.Push(&PushOptions{ 1446 RefSpecs: []config.RefSpec{"refs/heads/master:refs/heads/branch"}, 1447 RequireRemoteRefs: []config.RefSpec{config.RefSpec(oldRef.Hash().String() + ":refs/heads/branch")}, 1448 }) 1449 c.Assert(err, ErrorMatches, "non-fast-forward update: .*") 1450 1451 newRef, err = dstSto.Reference(plumbing.ReferenceName("refs/heads/branch")) 1452 c.Assert(err, IsNil) 1453 c.Assert(newRef, DeepEquals, oldRef) 1454 1455 err = r.Push(&PushOptions{ 1456 RefSpecs: []config.RefSpec{"refs/heads/master:refs/heads/branch"}, 1457 RequireRemoteRefs: []config.RefSpec{config.RefSpec(oldRef.Hash().String() + ":refs/heads/branch")}, 1458 Force: true, 1459 }) 1460 c.Assert(err, IsNil) 1461 1462 newRef, err = dstSto.Reference(plumbing.ReferenceName("refs/heads/branch")) 1463 c.Assert(err, IsNil) 1464 c.Assert(newRef, Not(DeepEquals), oldRef) 1465} 1466 1467func (s *RemoteSuite) TestFetchPrune(c *C) { 1468 fs := fixtures.Basic().One().DotGit() 1469 1470 url := c.MkDir() 1471 1472 _, err := PlainClone(url, true, &CloneOptions{ 1473 URL: fs.Root(), 1474 }) 1475 c.Assert(err, IsNil) 1476 1477 dir := c.MkDir() 1478 1479 r, err := PlainClone(dir, true, &CloneOptions{ 1480 URL: url, 1481 }) 1482 c.Assert(err, IsNil) 1483 1484 remote, err := r.Remote(DefaultRemoteName) 1485 c.Assert(err, IsNil) 1486 1487 ref, err := r.Reference(plumbing.ReferenceName("refs/heads/master"), true) 1488 c.Assert(err, IsNil) 1489 1490 err = remote.Push(&PushOptions{RefSpecs: []config.RefSpec{ 1491 "refs/heads/master:refs/heads/branch", 1492 }}) 1493 c.Assert(err, IsNil) 1494 1495 dirSave := c.MkDir() 1496 1497 rSave, err := PlainClone(dirSave, true, &CloneOptions{ 1498 URL: url, 1499 }) 1500 c.Assert(err, IsNil) 1501 1502 AssertReferences(c, rSave, map[string]string{ 1503 "refs/remotes/origin/branch": ref.Hash().String(), 1504 }) 1505 1506 err = remote.Push(&PushOptions{RefSpecs: []config.RefSpec{ 1507 ":refs/heads/branch", 1508 }}) 1509 c.Assert(err, IsNil) 1510 1511 AssertReferences(c, rSave, map[string]string{ 1512 "refs/remotes/origin/branch": ref.Hash().String(), 1513 }) 1514 1515 err = rSave.Fetch(&FetchOptions{Prune: true}) 1516 c.Assert(err, IsNil) 1517 1518 _, err = rSave.Reference("refs/remotes/origin/branch", true) 1519 c.Assert(err, ErrorMatches, "reference not found") 1520} 1521 1522func (s *RemoteSuite) TestFetchPruneTags(c *C) { 1523 fs := fixtures.Basic().One().DotGit() 1524 1525 url := c.MkDir() 1526 1527 _, err := PlainClone(url, true, &CloneOptions{ 1528 URL: fs.Root(), 1529 }) 1530 c.Assert(err, IsNil) 1531 1532 dir := c.MkDir() 1533 1534 r, err := PlainClone(dir, true, &CloneOptions{ 1535 URL: url, 1536 }) 1537 c.Assert(err, IsNil) 1538 1539 remote, err := r.Remote(DefaultRemoteName) 1540 c.Assert(err, IsNil) 1541 1542 ref, err := r.Reference(plumbing.ReferenceName("refs/heads/master"), true) 1543 c.Assert(err, IsNil) 1544 1545 err = remote.Push(&PushOptions{RefSpecs: []config.RefSpec{ 1546 "refs/heads/master:refs/tags/v1", 1547 }}) 1548 c.Assert(err, IsNil) 1549 1550 dirSave := c.MkDir() 1551 1552 rSave, err := PlainClone(dirSave, true, &CloneOptions{ 1553 URL: url, 1554 }) 1555 c.Assert(err, IsNil) 1556 1557 AssertReferences(c, rSave, map[string]string{ 1558 "refs/tags/v1": ref.Hash().String(), 1559 }) 1560 1561 err = remote.Push(&PushOptions{RefSpecs: []config.RefSpec{ 1562 ":refs/tags/v1", 1563 }}) 1564 c.Assert(err, IsNil) 1565 1566 AssertReferences(c, rSave, map[string]string{ 1567 "refs/tags/v1": ref.Hash().String(), 1568 }) 1569 1570 err = rSave.Fetch(&FetchOptions{Prune: true, RefSpecs: []config.RefSpec{"refs/tags/*:refs/tags/*"}}) 1571 c.Assert(err, IsNil) 1572 1573 _, err = rSave.Reference("refs/tags/v1", true) 1574 c.Assert(err, ErrorMatches, "reference not found") 1575} 1576 1577func (s *RemoteSuite) TestCanPushShasToReference(c *C) { 1578 d := c.MkDir() 1579 d, err := os.MkdirTemp(d, "TestCanPushShasToReference") 1580 c.Assert(err, IsNil) 1581 if err != nil { 1582 return 1583 } 1584 1585 // remote currently forces a plain path for path based remotes inside the PushContext function. 1586 // This makes it impossible, in the current state to use memfs. 1587 // For the sake of readability, use the same osFS everywhere and use plain git repositories on temporary files 1588 remote, err := PlainInit(filepath.Join(d, "remote"), true) 1589 c.Assert(err, IsNil) 1590 c.Assert(remote, NotNil) 1591 1592 repo, err := PlainInit(filepath.Join(d, "repo"), false) 1593 c.Assert(err, IsNil) 1594 c.Assert(repo, NotNil) 1595 1596 sha := CommitNewFile(c, repo, "README.md") 1597 1598 gitremote, err := repo.CreateRemote(&config.RemoteConfig{ 1599 Name: "local", 1600 URLs: []string{filepath.Join(d, "remote")}, 1601 }) 1602 c.Assert(err, IsNil) 1603 if err != nil { 1604 return 1605 } 1606 1607 err = gitremote.Push(&PushOptions{ 1608 RemoteName: "local", 1609 RefSpecs: []config.RefSpec{ 1610 // TODO: check with short hashes that this is still respected 1611 config.RefSpec(sha.String() + ":refs/heads/branch"), 1612 }, 1613 }) 1614 c.Assert(err, IsNil) 1615 if err != nil { 1616 return 1617 } 1618 1619 ref, err := remote.Reference(plumbing.ReferenceName("refs/heads/branch"), false) 1620 c.Assert(err, IsNil) 1621 if err != nil { 1622 return 1623 } 1624 c.Assert(ref.Hash().String(), Equals, sha.String()) 1625} 1626 1627func (s *RemoteSuite) TestFetchAfterShallowClone(c *C) { 1628 tempDir := c.MkDir() 1629 remoteUrl := filepath.Join(tempDir, "remote") 1630 repoDir := filepath.Join(tempDir, "repo") 1631 1632 // Create a new repo and add more than 1 commit (so we can have a shallow commit) 1633 remote, err := PlainInit(remoteUrl, false) 1634 c.Assert(err, IsNil) 1635 c.Assert(remote, NotNil) 1636 1637 _ = CommitNewFile(c, remote, "File1") 1638 _ = CommitNewFile(c, remote, "File2") 1639 1640 // Clone the repo with a depth of 1 1641 repo, err := PlainClone(repoDir, false, &CloneOptions{ 1642 URL: remoteUrl, 1643 Depth: 1, 1644 Tags: NoTags, 1645 SingleBranch: true, 1646 ReferenceName: "master", 1647 }) 1648 c.Assert(err, IsNil) 1649 1650 // Add new commits to the origin (more than 1 so that our next test hits a missing commit) 1651 _ = CommitNewFile(c, remote, "File3") 1652 sha4 := CommitNewFile(c, remote, "File4") 1653 1654 // Try fetch with depth of 1 again (note, we need to ensure no remote branch remains pointing at the old commit) 1655 r, err := repo.Remote(DefaultRemoteName) 1656 c.Assert(err, IsNil) 1657 s.testFetch(c, r, &FetchOptions{ 1658 Depth: 2, 1659 Tags: NoTags, 1660 1661 RefSpecs: []config.RefSpec{ 1662 "+refs/heads/master:refs/heads/master", 1663 "+refs/heads/master:refs/remotes/origin/master", 1664 }, 1665 }, []*plumbing.Reference{ 1666 plumbing.NewReferenceFromStrings("refs/heads/master", sha4.String()), 1667 plumbing.NewReferenceFromStrings("refs/remotes/origin/master", sha4.String()), 1668 plumbing.NewSymbolicReference("HEAD", "refs/heads/master"), 1669 }) 1670 1671 // Add another commit to the origin 1672 sha5 := CommitNewFile(c, remote, "File5") 1673 1674 // Try fetch with depth of 2 this time (to reach a commit that we don't have locally) 1675 r, err = repo.Remote(DefaultRemoteName) 1676 c.Assert(err, IsNil) 1677 s.testFetch(c, r, &FetchOptions{ 1678 Depth: 1, 1679 Tags: NoTags, 1680 1681 RefSpecs: []config.RefSpec{ 1682 "+refs/heads/master:refs/heads/master", 1683 "+refs/heads/master:refs/remotes/origin/master", 1684 }, 1685 }, []*plumbing.Reference{ 1686 plumbing.NewReferenceFromStrings("refs/heads/master", sha5.String()), 1687 plumbing.NewReferenceFromStrings("refs/remotes/origin/master", sha5.String()), 1688 plumbing.NewSymbolicReference("HEAD", "refs/heads/master"), 1689 }) 1690} 1691 1692func TestFetchFastForwardForCustomRef(t *testing.T) { 1693 customRef := "refs/custom/branch" 1694 // 1. Set up a remote with a URL 1695 remoteURL := t.TempDir() 1696 remoteRepo, err := PlainInit(remoteURL, true) 1697 if err != nil { 1698 t.Fatal(err) 1699 } 1700 1701 // 2. Add a commit with an empty tree to master and custom ref, also set HEAD 1702 emptyTreeID := writeEmptyTree(t, remoteRepo) 1703 writeCommitToRef(t, remoteRepo, "refs/heads/master", emptyTreeID, time.Now()) 1704 writeCommitToRef(t, remoteRepo, customRef, emptyTreeID, time.Now()) 1705 if err := remoteRepo.Storer.SetReference(plumbing.NewSymbolicReference(plumbing.HEAD, "refs/heads/master")); err != nil { 1706 t.Fatal(err) 1707 } 1708 1709 // 3. Clone repo, then fetch the custom ref 1710 // Note that using custom ref in ReferenceName has an IsBranch issue 1711 localRepo, err := Clone(memory.NewStorage(), memfs.New(), &CloneOptions{ 1712 URL: remoteURL, 1713 }) 1714 if err != nil { 1715 t.Fatal(err) 1716 } 1717 if err := localRepo.Fetch(&FetchOptions{ 1718 RefSpecs: []config.RefSpec{ 1719 config.RefSpec(fmt.Sprintf("%s:%s", customRef, customRef)), 1720 }, 1721 }); err != nil { 1722 t.Fatal(err) 1723 } 1724 1725 // 4. Make divergent changes 1726 remoteCommitID := writeCommitToRef(t, remoteRepo, customRef, emptyTreeID, time.Now()) 1727 // Consecutive calls to writeCommitToRef with time.Now() might have the same 1728 // time value, explicitly set distinct ones to ensure the commit hashes 1729 // differ 1730 writeCommitToRef(t, localRepo, customRef, emptyTreeID, time.Now().Add(time.Second)) 1731 1732 // 5. Try to fetch with fast-forward only mode 1733 remote, err := localRepo.Remote(DefaultRemoteName) 1734 if err != nil { 1735 t.Fatal(err) 1736 } 1737 1738 err = remote.Fetch(&FetchOptions{RefSpecs: []config.RefSpec{ 1739 config.RefSpec(fmt.Sprintf("%s:%s", customRef, customRef)), 1740 }}) 1741 if !errors.Is(err, ErrForceNeeded) { 1742 t.Errorf("expected %v, got %v", ErrForceNeeded, err) 1743 } 1744 1745 // 6. Fetch with force 1746 err = remote.Fetch(&FetchOptions{RefSpecs: []config.RefSpec{ 1747 config.RefSpec(fmt.Sprintf("+%s:%s", customRef, customRef)), 1748 }}) 1749 if err != nil { 1750 t.Errorf("unexpected error %v", err) 1751 } 1752 1753 // 7. Assert commit ID matches 1754 ref, err := localRepo.Reference(plumbing.ReferenceName(customRef), true) 1755 if err != nil { 1756 t.Fatal(err) 1757 } 1758 if remoteCommitID != ref.Hash() { 1759 t.Errorf("expected %s, got %s", remoteCommitID.String(), ref.Hash().String()) 1760 } 1761} 1762 1763func writeEmptyTree(t *testing.T, repo *Repository) plumbing.Hash { 1764 t.Helper() 1765 1766 obj := repo.Storer.NewEncodedObject() 1767 obj.SetType(plumbing.TreeObject) 1768 1769 tree := object.Tree{Entries: nil} 1770 if err := tree.Encode(obj); err != nil { 1771 t.Fatal(err) 1772 } 1773 1774 treeID, err := repo.Storer.SetEncodedObject(obj) 1775 if err != nil { 1776 t.Fatal(err) 1777 } 1778 1779 return treeID 1780} 1781 1782func writeCommitToRef(t *testing.T, repo *Repository, refName string, treeID plumbing.Hash, when time.Time) plumbing.Hash { 1783 t.Helper() 1784 1785 ref, err := repo.Reference(plumbing.ReferenceName(refName), true) 1786 if err != nil { 1787 if errors.Is(err, plumbing.ErrReferenceNotFound) { 1788 if err := repo.Storer.SetReference(plumbing.NewHashReference(plumbing.ReferenceName(refName), plumbing.ZeroHash)); err != nil { 1789 t.Fatal(err) 1790 } 1791 1792 ref, err = repo.Reference(plumbing.ReferenceName(refName), true) 1793 if err != nil { 1794 t.Fatal(err) 1795 } 1796 } else { 1797 t.Fatal(err) 1798 } 1799 } 1800 1801 commit := &object.Commit{ 1802 TreeHash: treeID, 1803 Author: object.Signature{ 1804 When: when, 1805 }, 1806 } 1807 if !ref.Hash().IsZero() { 1808 commit.ParentHashes = []plumbing.Hash{ref.Hash()} 1809 } 1810 1811 obj := repo.Storer.NewEncodedObject() 1812 if err := commit.Encode(obj); err != nil { 1813 t.Fatal(err) 1814 } 1815 1816 commitID, err := repo.Storer.SetEncodedObject(obj) 1817 if err != nil { 1818 t.Fatal(err) 1819 } 1820 1821 newRef := plumbing.NewHashReference(plumbing.ReferenceName(refName), commitID) 1822 if err := repo.Storer.CheckAndSetReference(newRef, ref); err != nil { 1823 t.Fatal(err) 1824 } 1825 1826 return commitID 1827}