+4
-1
options.go
+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
+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
+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)