Skip to content

Commit cc04792

Browse files
authored
Merge pull request #110 from github/scan-arbitrary-roots
Scan starting at arbitrary roots
2 parents 0b6d3a2 + 5d339ec commit cc04792

9 files changed

+457
-213
lines changed

git-sizer.go

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package main
22

33
import (
4+
"context"
45
"encoding/json"
56
"errors"
67
"fmt"
@@ -19,7 +20,9 @@ import (
1920
"github.com/github/git-sizer/sizes"
2021
)
2122

22-
const usage = `usage: git-sizer [OPTS]
23+
const usage = `usage: git-sizer [OPTS] [ROOT...]
24+
25+
Scan objects in your Git repository and emit statistics about them.
2326
2427
--threshold THRESHOLD minimum level of concern (i.e., number of stars)
2528
that should be reported. Default:
@@ -45,12 +48,29 @@ const usage = `usage: git-sizer [OPTS]
4548
be set via gitconfig: 'sizer.progress'.
4649
--version only report the git-sizer version number
4750
51+
Object selection:
52+
53+
git-sizer traverses through your Git history to find objects to
54+
process. By default, it processes all objects that are reachable from
55+
any reference. You can tell it to process only some of your
56+
references; see "Reference selection" below.
57+
58+
If explicit ROOTs are specified on the command line, each one should
59+
be a string that 'git rev-parse' can convert into a single Git object
60+
ID, like 'main', 'main~:src', or an abbreviated SHA-1. See
61+
git-rev-parse(1) for details. In that case, git-sizer also treats
62+
those objects as starting points for its traversal, and also includes
63+
the Git objects that are reachable from those roots in the analysis.
64+
65+
As a special case, if one or more ROOTs are specified on the command
66+
line but _no_ reference selection options, then _only_ the specified
67+
ROOTs are traversed, and no references.
68+
4869
Reference selection:
4970
50-
By default, git-sizer processes all Git objects that are reachable
51-
from any reference. The following options can be used to limit which
52-
references to process. The last rule matching a reference determines
53-
whether that reference is processed.
71+
The following options can be used to limit which references to
72+
process. The last rule matching a reference determines whether that
73+
reference is processed.
5474
5575
--[no-]branches process [don't process] branches
5676
--[no-]tags process [don't process] tags
@@ -93,14 +113,16 @@ var ReleaseVersion string
93113
var BuildVersion string
94114

95115
func main() {
96-
err := mainImplementation(os.Stdout, os.Stderr, os.Args[1:])
116+
ctx := context.Background()
117+
118+
err := mainImplementation(ctx, os.Stdout, os.Stderr, os.Args[1:])
97119
if err != nil {
98120
fmt.Fprintf(os.Stderr, "error: %s\n", err)
99121
os.Exit(1)
100122
}
101123
}
102124

103-
func mainImplementation(stdout, stderr io.Writer, args []string) error {
125+
func mainImplementation(ctx context.Context, stdout, stderr io.Writer, args []string) error {
104126
var nameStyle sizes.NameStyle = sizes.NameStyleFull
105127
var cpuprofile string
106128
var jsonOutput bool
@@ -216,10 +238,6 @@ func mainImplementation(stdout, stderr io.Writer, args []string) error {
216238
return nil
217239
}
218240

219-
if len(flags.Args()) != 0 {
220-
return errors.New("excess arguments")
221-
}
222-
223241
if repoErr != nil {
224242
return fmt.Errorf("couldn't open Git repository: %w", repoErr)
225243
}
@@ -273,7 +291,7 @@ func mainImplementation(stdout, stderr io.Writer, args []string) error {
273291
progress = v
274292
}
275293

276-
rg, err := rgb.Finish()
294+
rg, err := rgb.Finish(len(flags.Args()) == 0)
277295
if err != nil {
278296
return err
279297
}
@@ -288,7 +306,27 @@ func mainImplementation(stdout, stderr io.Writer, args []string) error {
288306
progressMeter = meter.NewProgressMeter(stderr, 100*time.Millisecond)
289307
}
290308

291-
historySize, err := sizes.ScanRepositoryUsingGraph(repo, rg, nameStyle, progressMeter)
309+
refRoots, err := sizes.CollectReferences(ctx, repo, rg)
310+
if err != nil {
311+
return fmt.Errorf("determining which reference to scan: %w", err)
312+
}
313+
314+
roots := make([]sizes.Root, 0, len(refRoots)+len(flags.Args()))
315+
for _, refRoot := range refRoots {
316+
roots = append(roots, refRoot)
317+
}
318+
319+
for _, arg := range flags.Args() {
320+
oid, err := repo.ResolveObject(arg)
321+
if err != nil {
322+
return fmt.Errorf("resolving command-line argument %q: %w", arg, err)
323+
}
324+
roots = append(roots, sizes.NewExplicitRoot(arg, oid))
325+
}
326+
327+
historySize, err := sizes.ScanRepositoryUsingGraph(
328+
ctx, repo, roots, nameStyle, progressMeter,
329+
)
292330
if err != nil {
293331
return fmt.Errorf("error scanning repository: %w", err)
294332
}

git/obj_resolver.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package git
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
)
7+
8+
func (repo *Repository) ResolveObject(name string) (OID, error) {
9+
cmd := repo.GitCommand("rev-parse", "--verify", "--end-of-options", name)
10+
output, err := cmd.Output()
11+
if err != nil {
12+
return NullOID, fmt.Errorf("resolving object %q: %w", name, err)
13+
}
14+
oidString := string(bytes.TrimSpace(output))
15+
oid, err := NewOID(oidString)
16+
if err != nil {
17+
return NullOID, fmt.Errorf("parsing output %q from 'rev-parse': %w", oidString, err)
18+
}
19+
return oid, nil
20+
}

git/ref_filter.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,15 +83,23 @@ func (_ allReferencesFilter) Filter(_ string) bool {
8383

8484
var AllReferencesFilter allReferencesFilter
8585

86+
type noReferencesFilter struct{}
87+
88+
func (_ noReferencesFilter) Filter(_ string) bool {
89+
return false
90+
}
91+
92+
var NoReferencesFilter noReferencesFilter
93+
8694
// PrefixFilter returns a `ReferenceFilter` that matches references
8795
// whose names start with the specified `prefix`, which must match at
8896
// a component boundary. For example,
8997
//
90-
// * Prefix "refs/foo" matches "refs/foo" and "refs/foo/bar" but not
91-
// "refs/foobar".
98+
// - Prefix "refs/foo" matches "refs/foo" and "refs/foo/bar" but not
99+
// "refs/foobar".
92100
//
93-
// * Prefix "refs/foo/" matches "refs/foo/bar" but not "refs/foo" or
94-
// "refs/foobar".
101+
// - Prefix "refs/foo/" matches "refs/foo/bar" but not "refs/foo" or
102+
// "refs/foobar".
95103
func PrefixFilter(prefix string) ReferenceFilter {
96104
if prefix == "" {
97105
return AllReferencesFilter

0 commit comments

Comments
 (0)