fork of go-git with some jj specific features

*: fetch, adds the prune option. Fixes #316

Co-authored-by: Ludovic Fernandez <ldez@users.noreply.github.com>

+4 -1
options.go
··· 166 166 // AllTags fetch all tags from the remote (i.e., fetch remote tags 167 167 // refs/tags/* into local tags with the same name) 168 168 AllTags 169 - //NoTags fetch no tags from the remote at all 169 + // NoTags fetch no tags from the remote at all 170 170 NoTags 171 171 ) 172 172 ··· 198 198 CABundle []byte 199 199 // ProxyOptions provides info required for connecting to a proxy. 200 200 ProxyOptions transport.ProxyOptions 201 + // Prune specify that local refs that match given RefSpecs and that do 202 + // not exist remotely will be removed. 203 + Prune bool 201 204 } 202 205 203 206 // Validate validates the fields and sets the default values.
+30 -1
remote.go
··· 470 470 } 471 471 } 472 472 473 + var updatedPrune bool 474 + if o.Prune { 475 + updatedPrune, err = r.pruneRemotes(o.RefSpecs, localRefs, remoteRefs) 476 + if err != nil { 477 + return nil, err 478 + } 479 + } 480 + 473 481 updated, err := r.updateLocalReferenceStorage(o.RefSpecs, refs, remoteRefs, specToRefs, o.Tags, o.Force) 474 482 if err != nil { 475 483 return nil, err ··· 482 490 } 483 491 } 484 492 485 - if !updated { 493 + if !updated && !updatedPrune { 486 494 return remoteRefs, NoErrAlreadyUpToDate 487 495 } 488 496 ··· 572 580 } 573 581 574 582 return err 583 + } 584 + 585 + func (r *Remote) pruneRemotes(specs []config.RefSpec, localRefs []*plumbing.Reference, remoteRefs memory.ReferenceStorage) (bool, error) { 586 + var updatedPrune bool 587 + for _, spec := range specs { 588 + rev := spec.Reverse() 589 + for _, ref := range localRefs { 590 + if !rev.Match(ref.Name()) { 591 + continue 592 + } 593 + _, err := remoteRefs.Reference(rev.Dst(ref.Name())) 594 + if errors.Is(err, plumbing.ErrReferenceNotFound) { 595 + updatedPrune = true 596 + err := r.s.RemoveReference(ref.Name()) 597 + if err != nil { 598 + return false, err 599 + } 600 + } 601 + } 602 + } 603 + return updatedPrune, nil 575 604 } 576 605 577 606 func (r *Remote) addReferencesToUpdate(
+114
remote_test.go
··· 1444 1444 c.Assert(newRef, Not(DeepEquals), oldRef) 1445 1445 } 1446 1446 1447 + func (s *RemoteSuite) TestFetchPrune(c *C) { 1448 + fs := fixtures.Basic().One().DotGit() 1449 + 1450 + url, clean := s.TemporalDir() 1451 + defer clean() 1452 + 1453 + _, err := PlainClone(url, true, &CloneOptions{ 1454 + URL: fs.Root(), 1455 + }) 1456 + c.Assert(err, IsNil) 1457 + 1458 + dir, clean := s.TemporalDir() 1459 + defer clean() 1460 + 1461 + r, err := PlainClone(dir, true, &CloneOptions{ 1462 + URL: url, 1463 + }) 1464 + c.Assert(err, IsNil) 1465 + 1466 + remote, err := r.Remote(DefaultRemoteName) 1467 + c.Assert(err, IsNil) 1468 + 1469 + ref, err := r.Reference(plumbing.ReferenceName("refs/heads/master"), true) 1470 + c.Assert(err, IsNil) 1471 + 1472 + err = remote.Push(&PushOptions{RefSpecs: []config.RefSpec{ 1473 + "refs/heads/master:refs/heads/branch", 1474 + }}) 1475 + c.Assert(err, IsNil) 1476 + 1477 + dirSave, clean := s.TemporalDir() 1478 + defer clean() 1479 + 1480 + rSave, err := PlainClone(dirSave, true, &CloneOptions{ 1481 + URL: url, 1482 + }) 1483 + c.Assert(err, IsNil) 1484 + 1485 + AssertReferences(c, rSave, map[string]string{ 1486 + "refs/remotes/origin/branch": ref.Hash().String(), 1487 + }) 1488 + 1489 + err = remote.Push(&PushOptions{RefSpecs: []config.RefSpec{ 1490 + ":refs/heads/branch", 1491 + }}) 1492 + 1493 + AssertReferences(c, rSave, map[string]string{ 1494 + "refs/remotes/origin/branch": ref.Hash().String(), 1495 + }) 1496 + 1497 + err = rSave.Fetch(&FetchOptions{Prune: true}) 1498 + c.Assert(err, IsNil) 1499 + 1500 + _, err = rSave.Reference("refs/remotes/origin/branch", true) 1501 + c.Assert(err, ErrorMatches, "reference not found") 1502 + } 1503 + 1504 + func (s *RemoteSuite) TestFetchPruneTags(c *C) { 1505 + fs := fixtures.Basic().One().DotGit() 1506 + 1507 + url, clean := s.TemporalDir() 1508 + defer clean() 1509 + 1510 + _, err := PlainClone(url, true, &CloneOptions{ 1511 + URL: fs.Root(), 1512 + }) 1513 + c.Assert(err, IsNil) 1514 + 1515 + dir, clean := s.TemporalDir() 1516 + defer clean() 1517 + 1518 + r, err := PlainClone(dir, true, &CloneOptions{ 1519 + URL: url, 1520 + }) 1521 + c.Assert(err, IsNil) 1522 + 1523 + remote, err := r.Remote(DefaultRemoteName) 1524 + c.Assert(err, IsNil) 1525 + 1526 + ref, err := r.Reference(plumbing.ReferenceName("refs/heads/master"), true) 1527 + c.Assert(err, IsNil) 1528 + 1529 + err = remote.Push(&PushOptions{RefSpecs: []config.RefSpec{ 1530 + "refs/heads/master:refs/tags/v1", 1531 + }}) 1532 + c.Assert(err, IsNil) 1533 + 1534 + dirSave, clean := s.TemporalDir() 1535 + defer clean() 1536 + 1537 + rSave, err := PlainClone(dirSave, true, &CloneOptions{ 1538 + URL: url, 1539 + }) 1540 + c.Assert(err, IsNil) 1541 + 1542 + AssertReferences(c, rSave, map[string]string{ 1543 + "refs/tags/v1": ref.Hash().String(), 1544 + }) 1545 + 1546 + err = remote.Push(&PushOptions{RefSpecs: []config.RefSpec{ 1547 + ":refs/tags/v1", 1548 + }}) 1549 + 1550 + AssertReferences(c, rSave, map[string]string{ 1551 + "refs/tags/v1": ref.Hash().String(), 1552 + }) 1553 + 1554 + err = rSave.Fetch(&FetchOptions{Prune: true, RefSpecs: []config.RefSpec{"refs/tags/*:refs/tags/*"}}) 1555 + c.Assert(err, IsNil) 1556 + 1557 + _, err = rSave.Reference("refs/tags/v1", true) 1558 + c.Assert(err, ErrorMatches, "reference not found") 1559 + } 1560 + 1447 1561 func (s *RemoteSuite) TestCanPushShasToReference(c *C) { 1448 1562 d, err := os.MkdirTemp("", "TestCanPushShasToReference") 1449 1563 c.Assert(err, IsNil)