LEFT | RIGHT |
1 // Copyright 2013 The Go Authors. All rights reserved. | 1 // Copyright 2013 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 package main | 5 package main |
6 | 6 |
7 import ( | 7 import ( |
8 "encoding/xml" | 8 "encoding/xml" |
9 "fmt" | 9 "fmt" |
10 "log" | 10 "log" |
11 "os" | 11 "os" |
12 "path/filepath" | 12 "path/filepath" |
13 "strconv" | 13 "strconv" |
14 "strings" | 14 "strings" |
15 "sync" | 15 "sync" |
16 ) | 16 ) |
17 | 17 |
18 // Repo represents a mercurial repository | 18 // Repo represents a mercurial repository. |
19 type Repo struct { | 19 type Repo struct { |
20 Path string | 20 Path string |
21 sync.Mutex | 21 sync.Mutex |
22 } | 22 } |
23 | 23 |
24 // RemoteRepo constructs a *Repo representing a remote URL | 24 // RemoteRepo constructs a *Repo representing a remote repository. |
25 func RemoteRepo(url string) *Repo { | 25 func RemoteRepo(url string) *Repo { |
26 return &Repo{ | 26 return &Repo{ |
27 Path: url, | 27 Path: url, |
28 } | 28 } |
29 } | 29 } |
30 | 30 |
31 // Clone clones the current Repo to a new destination | 31 // Clone clones the current Repo to a new destination |
32 // returning a new *Repo if successful | 32 // returning a new *Repo if successful. |
33 func (r *Repo) Clone(path, rev string) (*Repo, error) { | 33 func (r *Repo) Clone(path, rev string) (*Repo, error) { |
34 r.Lock() | 34 r.Lock() |
35 defer r.Unlock() | 35 defer r.Unlock() |
36 if err := run(*cmdTimeout, nil, *buildroot, r.hgCmd("clone", "-r", rev,
r.Path, path)...); err != nil { | 36 if err := run(*cmdTimeout, nil, *buildroot, r.hgCmd("clone", "-r", rev,
r.Path, path)...); err != nil { |
37 return nil, err | 37 return nil, err |
38 } | 38 } |
39 return &Repo{ | 39 return &Repo{ |
40 Path: path, | 40 Path: path, |
41 }, nil | 41 }, nil |
42 } | 42 } |
43 | 43 |
44 // UpdateTo updates the working copy of this Repo to the | 44 // UpdateTo updates the working copy of this Repo to the |
45 // supplied revision. | 45 // supplied revision. |
46 func (r *Repo) UpdateTo(hash string) error { | 46 func (r *Repo) UpdateTo(hash string) error { |
47 r.Lock() | 47 r.Lock() |
48 defer r.Unlock() | 48 defer r.Unlock() |
49 return run(*cmdTimeout, nil, r.Path, r.hgCmd("update", hash)...) | 49 return run(*cmdTimeout, nil, r.Path, r.hgCmd("update", hash)...) |
50 } | 50 } |
51 | 51 |
52 // Exists reports whether this Repo represents a valid Mecurial repository. | 52 // Exists reports whether this Repo represents a valid Mecurial repository. |
53 func (r *Repo) Exists() bool { | 53 func (r *Repo) Exists() bool { |
54 // TODO(dfc) probably don't need to lock here | |
55 fi, err := os.Stat(filepath.Join(r.Path, ".hg")) | 54 fi, err := os.Stat(filepath.Join(r.Path, ".hg")) |
56 if err != nil { | 55 if err != nil { |
57 return false | 56 return false |
58 } | 57 } |
59 return fi.IsDir() | 58 return fi.IsDir() |
60 } | 59 } |
61 | 60 |
62 // Pull pulls changes from the default path, that is, the path | 61 // Pull pulls changes from the default path, that is, the path |
63 // this Repo was cloned from. | 62 // this Repo was cloned from. |
64 func (r *Repo) Pull() error { | 63 func (r *Repo) Pull() error { |
65 r.Lock() | 64 r.Lock() |
66 defer r.Unlock() | 65 defer r.Unlock() |
67 return run(*cmdTimeout, nil, r.Path, r.hgCmd("pull")...) | 66 return run(*cmdTimeout, nil, r.Path, r.hgCmd("pull")...) |
68 } | 67 } |
69 | 68 |
70 // Log returns the changelog for this repository. | 69 // Log returns the changelog for this repository. |
71 func (r *Repo) Log() ([]HgLog, error) { | 70 func (r *Repo) Log() ([]HgLog, error) { |
| 71 if err := r.Pull(); err != nil { |
| 72 return nil, err |
| 73 } |
72 const N = 50 // how many revisions to grab | 74 const N = 50 // how many revisions to grab |
73 | 75 |
74 r.Lock() | 76 r.Lock() |
75 defer r.Unlock() | 77 defer r.Unlock() |
76 data, _, err := runLog(*cmdTimeout, nil, r.Path, r.hgCmd("log", | 78 data, _, err := runLog(*cmdTimeout, nil, r.Path, r.hgCmd("log", |
77 "--encoding=utf-8", | 79 "--encoding=utf-8", |
78 "--limit="+strconv.Itoa(N), | 80 "--limit="+strconv.Itoa(N), |
79 "--template="+xmlLogTemplate)..., | 81 "--template="+xmlLogTemplate)..., |
80 ) | 82 ) |
81 if err != nil { | 83 if err != nil { |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
126 Hash string | 128 Hash string |
127 Author string | 129 Author string |
128 Date string | 130 Date string |
129 Desc string | 131 Desc string |
130 Parent string | 132 Parent string |
131 | 133 |
132 // Internal metadata | 134 // Internal metadata |
133 added bool | 135 added bool |
134 } | 136 } |
135 | 137 |
136 // logByHash is a cache of all Mercurial revisions we know about, | |
137 // indexed by full hash. | |
138 var logByHash = map[string]*HgLog{} | |
139 | |
140 // xmlLogTemplate is a template to pass to Mercurial to make | 138 // xmlLogTemplate is a template to pass to Mercurial to make |
141 // hg log print the log in valid XML for parsing with xml.Unmarshal. | 139 // hg log print the log in valid XML for parsing with xml.Unmarshal. |
142 const xmlLogTemplate = ` | 140 const xmlLogTemplate = ` |
143 <Log> | 141 <Log> |
144 <Hash>{node|escape}</Hash> | 142 <Hash>{node|escape}</Hash> |
145 <Parent>{parent|escape}</Parent> | 143 <Parent>{parent|escape}</Parent> |
146 <Author>{author|escape}</Author> | 144 <Author>{author|escape}</Author> |
147 <Date>{date|rfc3339date}</Date> | 145 <Date>{date|rfc3339date}</Date> |
148 <Desc>{desc|escape}</Desc> | 146 <Desc>{desc|escape}</Desc> |
149 </Log> | 147 </Log> |
150 ` | 148 ` |
LEFT | RIGHT |