-
Notifications
You must be signed in to change notification settings - Fork 31
/
vcs_hg.go
150 lines (128 loc) · 3.47 KB
/
vcs_hg.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package main
import (
"errors"
"fmt"
"os"
"strings"
"time"
)
// Hg implements VCS for Mercurial version control.
type Hg struct {
workspace *Workspace
filter FilterOptions
}
// NewHgVCS returns new Mercurial vcs, and checks it it's valid hg workspace.
func NewHgVCS(path string, filter FilterOptions) (*Hg, error) {
// check if this path is under hg control
_, err := Run(path, "hg", "verify")
if err != nil {
return nil, err
}
// find top-level (root) directory of workspace
out, err := Run(path, "hg", "root")
if err != nil {
return nil, errors.New("cannot determine root folder")
}
root := strings.TrimSpace(out)
prefix := findPrefix(path, root)
workspace := NewWorkspace(root, prefix)
vcs := &Hg{
workspace: workspace,
filter: filter,
}
return vcs, nil
}
// Commits returns all commits for the current branch. Implements VCS interface.
func (g *Hg) Commits() ([]Commit, error) {
path := g.Workspace().Path()
// Prepare args, and add user defined args to `hg log` command
args := []string{"log", `--template={node}%{date|rfc822date}%{author}%{desc}\n`}
if len(g.filter.Args) > 0 {
// Append custom arguments, excluding formatting-related ones
cleanedArgs := cleanHgArgs(g.filter.Args...)
args = append(args, cleanedArgs...)
}
if g.filter.LastN > 0 {
lastNArg := fmt.Sprintf("-l %d", g.filter.LastN)
args = append(args, lastNArg)
}
out, err := Run(path, "hg", args...)
if err != nil {
return nil, err
}
lines := strings.Split(out, "\n")
commits := parseHgCommits(lines, time.Local)
// Filter to max entries, if specified
if g.filter.Max > 0 {
commits = FilterMax(commits, g.filter.Max)
}
return commits, nil
}
// parseHgCommits parses output from `hg log` command.
func parseHgCommits(lines []string, location *time.Location) []Commit {
var commits []Commit
for _, str := range lines {
fields := strings.SplitN(str, "%", 4)
if len(fields) != 4 {
fmt.Fprintln(os.Stderr, "[ERROR] Wrong commit info, skipping:", len(fields), str)
continue
}
timestamp, err := time.ParseInLocation(time.RFC1123Z, fields[1], location)
if err != nil {
fmt.Fprintln(os.Stderr, "[ERROR] Cannot parse timestamp:", err)
continue
}
commit := Commit{
Hash: fields[0],
Date: timestamp,
Subject: fields[3],
Author: fields[2],
}
commits = append(commits, commit)
}
return commits
}
// SwitchTo switches to the given commit by hash. Implements VCS interface.
func (g *Hg) SwitchTo(hash string) error {
path := g.Workspace().Path()
_, err := Run(path, "hg", "update", hash)
return err
}
// Workspace returns assosiated Workspace. Implements VCS interface.
func (g *Hg) Workspace() *Workspace {
return g.workspace
}
// Name returns vcs common name. Implements VCS interface.
func (*Hg) Name() string {
return "hg"
}
var (
// TODO: find a person who use mercurial a lot and can
// help to add more ignored args (ones that may affect
// predetermined output)
ignoredHgArgs = []string{
"--template",
}
)
// cleanHgArgs cleans user defined custom hg arguments.
// it basically removes arguments, that may affect formatting
// output (we use specific format for parsing results)
func cleanHgArgs(args ...string) []string {
var ret []string
for _, arg := range args {
trimmed := strings.TrimSpace(arg)
if trimmed == "" {
continue
}
var ignore bool
for _, ignored := range ignoredHgArgs {
if strings.HasPrefix(trimmed, ignored) {
ignore = true
}
}
if !ignore {
ret = append(ret, trimmed)
}
}
return ret
}