Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings
This repository was archived by the owner on Sep 11, 2020. It is now read-only.

Commit 8e14928

Browse files
author
Eduardo Lezcano
committed
git describe functionality.
- `Describe` method under repository allows to describe references based on tags. - Options ported from `git describe` as close as possible. - Basic test for `Describe` Signed-off-by: Eduardo Lezcano <eduardo.lezcano@be.atlascopco.com>
1 parent b30763c commit 8e14928

File tree

3 files changed

+224
-0
lines changed

3 files changed

+224
-0
lines changed

‎options.go‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,3 +431,41 @@ type PlainOpenOptions struct {
431431

432432
// Validate validates the fields and sets the default values.
433433
func (o *PlainOpenOptions) Validate() error { return nil }
434+
435+
// DescribeOptions as defined by `git describe`
436+
type DescribeOptions struct {
437+
// Contains find the tag that comes after the commit
438+
//Contains bool
439+
// Debug search strategy on stderr
440+
Debug bool
441+
// All Use any reference
442+
//All bool
443+
// Tags use any tag, even unannotated
444+
Tags bool
445+
// FirstParent only follow first parent
446+
//FirstParent bool
447+
// Use <Abbrev> digits to display SHA-1s
448+
// By default is 8
449+
Abbrev int
450+
// Only output exact matches
451+
//ExactMatch bool
452+
// Consider <Candidates> most recent tags
453+
// By default is 10
454+
Candidates int
455+
// Only consider tags matching <Match> pattern
456+
//Match string
457+
// Show abbreviated commit object as fallback
458+
//Always bool
459+
// Append <mark> on dirty working tree (default: "-dirty")
460+
Dirty string
461+
}
462+
463+
func (o *DescribeOptions) Validate() error {
464+
if o.Abbrev == 0 {
465+
o.Abbrev = 7
466+
}
467+
if o.Candidates == 0 {
468+
o.Candidates = 10
469+
}
470+
return nil
471+
}

‎repository.go‎

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"gopkg.in/src-d/go-git.v4/storage/filesystem"
2121
"gopkg.in/src-d/go-git.v4/utils/ioutil"
2222

23+
"bytes"
2324
"gopkg.in/src-d/go-billy.v4"
2425
"gopkg.in/src-d/go-billy.v4/osfs"
2526
)
@@ -38,6 +39,7 @@ var (
3839
ErrIsBareRepository = errors.New("worktree not available in a bare repository")
3940
ErrUnableToResolveCommit = errors.New("unable to resolve commit")
4041
ErrPackedObjectsNotSupported = errors.New("Packed objects not supported")
42+
ErrTagNotFound = errors.New("tag not found")
4143
)
4244

4345
// Repository represents a git repository
@@ -1220,3 +1222,162 @@ func (r *Repository) createNewObjectPack(cfg *RepackConfig) (h plumbing.Hash, er
12201222

12211223
return h, err
12221224
}
1225+
1226+
type Describe struct {
1227+
// Reference being described
1228+
Reference *plumbing.Reference
1229+
// Tag of the describe object
1230+
Tag *plumbing.Reference
1231+
// Distance to the tag object in commits
1232+
Distance int
1233+
// Dirty string to append
1234+
Dirty string
1235+
// Use <Abbrev> digits to display SHA-ls
1236+
Abbrev int
1237+
}
1238+
1239+
func (d *Describe) String() string {
1240+
var s []string
1241+
1242+
if d.Tag != nil {
1243+
s = append(s, d.Tag.Name().Short())
1244+
}
1245+
if d.Distance > 0 {
1246+
s = append(s, fmt.Sprint(d.Distance))
1247+
}
1248+
s = append(s, "g"+d.Reference.Hash().String()[0:d.Abbrev])
1249+
if d.Dirty != "" {
1250+
s = append(s, d.Dirty)
1251+
}
1252+
1253+
return strings.Join(s, "-")
1254+
}
1255+
1256+
// Describe just like the `git describe` command will return a Describe struct for the hash passed.
1257+
// Describe struct implements String interface so it can be easily printed out.
1258+
func (r *Repository) Describe(ref *plumbing.Reference, opts *DescribeOptions) (*Describe, error) {
1259+
if err := opts.Validate(); err != nil {
1260+
return nil, err
1261+
}
1262+
1263+
// Describes through the commit log ordered by commit time seems to be the best approximation to
1264+
// git describe.
1265+
commitIterator, err := r.Log(&LogOptions{
1266+
From: ref.Hash(),
1267+
Order: LogOrderCommitterTime,
1268+
})
1269+
if err != nil {
1270+
return nil, err
1271+
}
1272+
1273+
// To query tags we create a temporary map.
1274+
tagIterator, err := r.Tags()
1275+
if err != nil {
1276+
return nil, err
1277+
}
1278+
tags := make(map[plumbing.Hash]*plumbing.Reference)
1279+
tagIterator.ForEach(func(t *plumbing.Reference) error {
1280+
if to, err := r.TagObject(t.Hash()); err == nil {
1281+
tags[to.Target] = t
1282+
} else {
1283+
tags[t.Hash()] = t
1284+
}
1285+
return nil
1286+
})
1287+
tagIterator.Close()
1288+
1289+
// The search looks for a number of suitable candidates in the log (specified through the options)
1290+
type describeCandidate struct {
1291+
ref *plumbing.Reference
1292+
annotated bool
1293+
distance int
1294+
}
1295+
var candidates []*describeCandidate
1296+
var candidatesFound int
1297+
var count = -1
1298+
var lastCommit *object.Commit
1299+
1300+
if opts.Debug {
1301+
fmt.Fprintf(os.Stderr, "searching to describe %v\n", ref.Name())
1302+
}
1303+
1304+
for {
1305+
var candidate = &describeCandidate{annotated: false}
1306+
1307+
err = commitIterator.ForEach(func(commit *object.Commit) error {
1308+
lastCommit = commit
1309+
count++
1310+
if tagReference, ok := tags[commit.Hash]; ok {
1311+
delete(tags, commit.Hash)
1312+
candidate.ref = tagReference
1313+
hash := tagReference.Hash()
1314+
if !bytes.Equal(commit.Hash[:], hash[:]) {
1315+
candidate.annotated = true
1316+
}
1317+
return storer.ErrStop
1318+
}
1319+
return nil
1320+
})
1321+
1322+
if candidate.annotated || opts.Tags {
1323+
if candidatesFound < opts.Candidates {
1324+
candidate.distance = count
1325+
candidates = append(candidates, candidate)
1326+
}
1327+
candidatesFound++
1328+
}
1329+
1330+
if candidatesFound > opts.Candidates || len(tags) == 0 {
1331+
break
1332+
}
1333+
1334+
}
1335+
1336+
if opts.Debug {
1337+
for _, c := range candidates {
1338+
var description = "lightweight"
1339+
if c.annotated {
1340+
description = "annotated"
1341+
}
1342+
fmt.Fprintf(os.Stderr, " %-11s %8d %v\n", description, c.distance, c.ref.Name().Short())
1343+
}
1344+
fmt.Fprintf(os.Stderr, "traversed %v commits\n", count)
1345+
if candidatesFound > opts.Candidates {
1346+
fmt.Fprintf(os.Stderr, "more than %v tags found; listed %v most recent\n",
1347+
opts.Candidates, len(candidates))
1348+
}
1349+
fmt.Fprintf(os.Stderr, "gave up search at %v\n", lastCommit.Hash.String())
1350+
}
1351+
1352+
return &Describe{
1353+
ref,
1354+
candidates[0].ref,
1355+
candidates[0].distance,
1356+
opts.Dirty,
1357+
opts.Abbrev,
1358+
}, nil
1359+
1360+
}
1361+
1362+
func (r *Repository) Tag(h plumbing.Hash) (*plumbing.Reference, error) {
1363+
// Get repo tags
1364+
tagIterator, err := r.Tags()
1365+
if err != nil {
1366+
return nil, err
1367+
}
1368+
// Search tag
1369+
var tag *plumbing.Reference = nil
1370+
tagIterator.ForEach(func(t *plumbing.Reference) error {
1371+
tagHash := t.Hash()
1372+
if bytes.Equal(h[:], tagHash[:]) {
1373+
tag = t
1374+
return storer.ErrStop
1375+
}
1376+
return nil
1377+
})
1378+
// Closure
1379+
if tag == nil {
1380+
return nil, ErrTagNotFound
1381+
}
1382+
return tag, nil
1383+
}

‎repository_test.go‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1688,3 +1688,28 @@ func (s *RepositorySuite) TestBrokenMultipleShallowFetch(c *C) {
16881688
})
16891689
c.Assert(err, IsNil)
16901690
}
1691+
1692+
func (s *RepositorySuite) TestDescribe(c *C) {
1693+
url := s.GetLocalRepositoryURL(
1694+
fixtures.ByURL("https://github.com/git-fixtures/tags.git").One(),
1695+
)
1696+
1697+
r, _ := Init(memory.NewStorage(), nil)
1698+
err := r.clone(context.Background(), &CloneOptions{URL: url, Tags: AllTags})
1699+
c.Assert(err, IsNil)
1700+
1701+
datas := map[string]string{
1702+
"lightweight-tag-g7b8777": "f7b877701fbf855b44c0a9e86f3fdce2c298b07f",
1703+
}
1704+
1705+
for desc, hash := range datas {
1706+
1707+
h := plumbing.NewHash(hash)
1708+
d, err := r.Describe(
1709+
plumbing.NewHashReference("test", h),
1710+
&DescribeOptions{})
1711+
1712+
c.Assert(err, IsNil)
1713+
c.Assert(d.String(), Equals, desc)
1714+
}
1715+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /