1
2
3
4
5 package main
6
7 import (
8 "bytes"
9 "container/vector"
10 "exec"
11 "flag"
12 "fmt"
13 "io/ioutil"
14 "os"
15 "patch"
16 "path/filepath"
17 "sort"
18 "strings"
19 )
20
21 var checkSync = flag.Bool("checksync", true, "check whether repository is out of sync")
22
23 func usage() {
24 fmt.Fprintf(os.Stderr, "usage: hgpatch [options] [patchfile]\n")
25 flag.PrintDefaults()
26 os.Exit(2)
27 }
28
29 func main() {
30 flag.Usage = usage
31 flag.Parse()
32
33 args := flag.Args()
34 var data []byte
35 var err os.Error
36 switch len(args) {
37 case 0:
38 data, err = ioutil.ReadAll(os.Stdin)
39 case 1:
40 data, err = ioutil.ReadFile(args[0])
41 default:
42 usage()
43 }
44 chk(err)
45
46 pset, err := patch.Parse(data)
47 chk(err)
48
49
50
51 root, err := hgRoot()
52 chk(err)
53 chk(os.Chdir(root))
54
55
56 if *checkSync && hgIncoming() {
57 fmt.Fprintf(os.Stderr, "incoming changes waiting; run hg sync first\n")
58 os.Exit(2)
59 }
60
61
62 dirtylist, err := hgModified()
63 chk(err)
64 dirty := make(map[string]bool)
65 for _, f := range dirtylist {
66 dirty[f] = true
67 }
68 conflict := make(map[string]bool)
69 for _, f := range pset.File {
70 if f.Verb == patch.Delete || f.Verb == patch.Rename {
71 if dirty[f.Src] {
72 conflict[f.Src] = true
73 }
74 }
75 if f.Verb != patch.Delete {
76 if dirty[f.Dst] {
77 conflict[f.Dst] = true
78 }
79 }
80 }
81 if len(conflict) > 0 {
82 fmt.Fprintf(os.Stderr, "cannot apply patch to locally modified files:\n")
83 for name := range conflict {
84 fmt.Fprintf(os.Stderr, "\t%s\n", name)
85 }
86 os.Exit(2)
87 }
88
89
90 op, err := pset.Apply(ioutil.ReadFile)
91 chk(err)
92
93
94
95
96 changed := make(map[string]int)
97
98
99
100
101
102
103 for i := range op {
104 o := &op[i]
105 if o.Verb == patch.Copy {
106 makeParent(o.Dst)
107 chk(hgCopy(o.Dst, o.Src))
108 undoRevert(o.Dst)
109 changed[o.Dst] = 1
110 }
111 }
112 for i := range op {
113 o := &op[i]
114 if o.Verb == patch.Rename {
115 makeParent(o.Dst)
116 chk(hgRename(o.Dst, o.Src))
117 undoRevert(o.Dst)
118 undoRevert(o.Src)
119 changed[o.Src] = 1
120 changed[o.Dst] = 1
121 }
122 }
123
124
125
126 for i := range op {
127 o := &op[i]
128 if o.Verb == patch.Delete {
129 chk(hgRemove(o.Src))
130 undoRevert(o.Src)
131 changed[o.Src] = 1
132 }
133 }
134
135
136 for i := range op {
137 o := &op[i]
138 if o.Verb == patch.Delete {
139 continue
140 }
141 if o.Verb == patch.Add {
142 makeParent(o.Dst)
143 changed[o.Dst] = 1
144 }
145 if o.Data != nil {
146 chk(ioutil.WriteFile(o.Dst, o.Data, 0644))
147 if o.Verb == patch.Add {
148 undoRm(o.Dst)
149 } else {
150 undoRevert(o.Dst)
151 }
152 changed[o.Dst] = 1
153 }
154 if o.Mode != 0 {
155 chk(os.Chmod(o.Dst, uint32(o.Mode&0755)))
156 undoRevert(o.Dst)
157 changed[o.Dst] = 1
158 }
159 }
160
161
162
163 for i := range op {
164 o := &op[i]
165 if o.Verb == patch.Add {
166 chk(hgAdd(o.Dst))
167 undoRevert(o.Dst)
168 changed[o.Dst] = 1
169 }
170 }
171
172
173 list := make([]string, len(changed))
174 i := 0
175 for f := range changed {
176 list[i] = f
177 i++
178 }
179 sort.Strings(list)
180 for _, f := range list {
181 fmt.Printf("%s\n", f)
182 }
183 }
184
185
186 func makeParent(name string) {
187 parent, _ := filepath.Split(name)
188 chk(mkdirAll(parent, 0755))
189 }
190
191
192
193 func mkdirAll(path string, perm uint32) os.Error {
194 dir, err := os.Lstat(path)
195 if err == nil {
196 if dir.IsDirectory() {
197 return nil
198 }
199 return &os.PathError{"mkdir", path, os.ENOTDIR}
200 }
201
202 i := len(path)
203 for i > 0 && path[i-1] == '/' {
204 i--
205 }
206
207 j := i
208 for j > 0 && path[j-1] != '/' {
209 j--
210 }
211
212 if j > 0 {
213 err = mkdirAll(path[0:j-1], perm)
214 if err != nil {
215 return err
216 }
217 }
218
219 err = os.Mkdir(path, perm)
220 if err != nil {
221
222
223 dir, err1 := os.Lstat(path)
224 if err1 == nil && dir.IsDirectory() {
225 return nil
226 }
227 return err
228 }
229 undoRm(path)
230 return nil
231 }
232
233
234 func chk(err os.Error) {
235 if err != nil {
236 fmt.Fprintf(os.Stderr, "%s\n", err)
237 runUndo()
238 os.Exit(2)
239 }
240 }
241
242
243 type undo func() os.Error
244
245 var undoLog vector.Vector
246
247 func undoRevert(name string) { undoLog.Push(undo(func() os.Error { return hgRevert(name) })) }
248
249 func undoRm(name string) { undoLog.Push(undo(func() os.Error { return os.Remove(name) })) }
250
251 func runUndo() {
252 for i := undoLog.Len() - 1; i >= 0; i-- {
253 if err := undoLog.At(i).(undo)(); err != nil {
254 fmt.Fprintf(os.Stderr, "%s\n", err)
255 }
256 }
257 }
258
259
260 func hgRoot() (string, os.Error) {
261 out, err := run([]string{"hg", "root"}, nil)
262 if err != nil {
263 return "", err
264 }
265 return strings.TrimSpace(out), nil
266 }
267
268
269 func hgIncoming() bool {
270
271 _, err := run([]string{"hg", "-q", "incoming"}, nil)
272 return err == nil
273 }
274
275
276
277 func hgModified() ([]string, os.Error) {
278 out, err := run([]string{"hg", "status", "-n"}, nil)
279 if err != nil {
280 return nil, err
281 }
282 return strings.Split(strings.TrimSpace(out), "\n"), nil
283 }
284
285
286 func hgAdd(name string) os.Error {
287 _, err := run([]string{"hg", "add", name}, nil)
288 return err
289 }
290
291
292 func hgRemove(name string) os.Error {
293 _, err := run([]string{"hg", "rm", name}, nil)
294 return err
295 }
296
297
298 func hgRevert(name string) os.Error {
299 _, err := run([]string{"hg", "revert", name}, nil)
300 return err
301 }
302
303
304
305 func hgCopy(dst, src string) os.Error {
306 _, err := run([]string{"hg", "cp", src, dst}, nil)
307 return err
308 }
309
310
311
312 func hgRename(dst, src string) os.Error {
313 _, err := run([]string{"hg", "mv", src, dst}, nil)
314 return err
315 }
316
317 func dup(a []string) []string {
318 b := make([]string, len(a))
319 copy(b, a)
320 return b
321 }
322
323 var lookPathCache = make(map[string]string)
324
325
326
327 func run(argv []string, input []byte) (out string, err os.Error) {
328 if len(argv) < 1 {
329 return "", &runError{dup(argv), os.EINVAL}
330 }
331
332 prog, ok := lookPathCache[argv[0]]
333 if !ok {
334 prog, err = exec.LookPath(argv[0])
335 if err != nil {
336 return "", &runError{dup(argv), err}
337 }
338 lookPathCache[argv[0]] = prog
339 }
340
341 cmd := exec.Command(prog, argv[1:]...)
342 if len(input) > 0 {
343 cmd.Stdin = bytes.NewBuffer(input)
344 }
345 bs, err := cmd.CombinedOutput()
346 if err != nil {
347 return "", &runError{dup(argv), err}
348 }
349 return string(bs), nil
350 }
351
352
353 type runError struct {
354 cmd []string
355 err os.Error
356 }
357
358 func (e *runError) String() string { return strings.Join(e.cmd, " ") + ": " + e.err.String() }