##// END OF EJS Templates
Add $HGEDITOR hook and example script...
mpm@selenic.com -
r186:9a2075c0 default
parent child Browse files
Show More
@@ -0,0 +1,21
1 #!/bin/bash
2 #
3 # This is an example of using HGEDITOR to automate the signing of
4 # commits and so on.
5
6 MANIFEST=`grep '^HG: manifest hash' $1 | cut -b 19-`
7 if grep -q "^HG: merge resolve" $1 ; then
8 # we don't sign merges
9 $EDITOR $1
10 else
11 T=`mktemp`
12 CHANGED=`grep '^HG: changed' $1 | cut -b 13-`
13 # show a diff so writing commit comments is easier
14 hg diff $CHANGED >> $T
15 echo -e "\n\nmanifest hash: $MANIFEST" > $1
16 emacs -nw $T $1
17 head -1 $1 > $T
18 echo >> $T
19 gpg -a -u $HGUSER -o - --clearsign $1 >> $T
20 mv $T $1
21 fi
@@ -1,921 +1,921
1 1 # hg.py - repository classes for mercurial
2 2 #
3 3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 import sys, struct, sha, socket, os, time, re, urllib2
9 9 import urllib
10 10 from mercurial import byterange, lock
11 11 from mercurial.transaction import *
12 12 from mercurial.revlog import *
13 13 from difflib import SequenceMatcher
14 14
15 15 class filelog(revlog):
16 16 def __init__(self, opener, path):
17 17 revlog.__init__(self, opener,
18 18 os.path.join("data", path + ".i"),
19 19 os.path.join("data", path + ".d"))
20 20
21 21 def read(self, node):
22 22 return self.revision(node)
23 23 def add(self, text, transaction, link, p1=None, p2=None):
24 24 return self.addrevision(text, transaction, link, p1, p2)
25 25
26 26 def annotate(self, node):
27 27 revs = []
28 28 while node != nullid:
29 29 revs.append(node)
30 30 node = self.parents(node)[0]
31 31 revs.reverse()
32 32 prev = []
33 33 annotate = []
34 34
35 35 for node in revs:
36 36 curr = self.read(node).splitlines(1)
37 37 linkrev = self.linkrev(node)
38 38 sm = SequenceMatcher(None, prev, curr)
39 39 new = []
40 40 for o, m, n, s, t in sm.get_opcodes():
41 41 if o == 'equal':
42 42 new += annotate[m:n]
43 43 else:
44 44 new += [(linkrev, l) for l in curr[s:t]]
45 45 annotate, prev = new, curr
46 46 return annotate
47 47
48 48 class manifest(revlog):
49 49 def __init__(self, opener):
50 50 self.mapcache = None
51 51 self.listcache = None
52 52 self.addlist = None
53 53 revlog.__init__(self, opener, "00manifest.i", "00manifest.d")
54 54
55 55 def read(self, node):
56 56 if self.mapcache and self.mapcache[0] == node:
57 57 return self.mapcache[1].copy()
58 58 text = self.revision(node)
59 59 map = {}
60 60 self.listcache = (text, text.splitlines(1))
61 61 for l in self.listcache[1]:
62 62 (f, n) = l.split('\0')
63 63 map[f] = bin(n[:40])
64 64 self.mapcache = (node, map)
65 65 return map
66 66
67 67 def diff(self, a, b):
68 68 # this is sneaky, as we're not actually using a and b
69 69 if self.listcache and self.addlist and self.listcache[0] == a:
70 70 d = mdiff.diff(self.listcache[1], self.addlist, 1)
71 71 if mdiff.patch(a, d) != b:
72 72 sys.stderr.write("*** sortdiff failed, falling back ***\n")
73 73 return mdiff.textdiff(a, b)
74 74 return d
75 75 else:
76 76 return mdiff.textdiff(a, b)
77 77
78 78 def add(self, map, transaction, link, p1=None, p2=None):
79 79 files = map.keys()
80 80 files.sort()
81 81
82 82 self.addlist = ["%s\000%s\n" % (f, hex(map[f])) for f in files]
83 83 text = "".join(self.addlist)
84 84
85 85 n = self.addrevision(text, transaction, link, p1, p2)
86 86 self.mapcache = (n, map)
87 87 self.listcache = (text, self.addlist)
88 88 self.addlist = None
89 89
90 90 return n
91 91
92 92 class changelog(revlog):
93 93 def __init__(self, opener):
94 94 revlog.__init__(self, opener, "00changelog.i", "00changelog.d")
95 95
96 96 def extract(self, text):
97 97 if not text:
98 98 return (nullid, "", "0", [], "")
99 99 last = text.index("\n\n")
100 100 desc = text[last + 2:]
101 101 l = text[:last].splitlines()
102 102 manifest = bin(l[0])
103 103 user = l[1]
104 104 date = l[2]
105 105 files = l[3:]
106 106 return (manifest, user, date, files, desc)
107 107
108 108 def read(self, node):
109 109 return self.extract(self.revision(node))
110 110
111 111 def add(self, manifest, list, desc, transaction, p1=None, p2=None):
112 112 user = (os.environ.get("HGUSER") or
113 113 os.environ.get("EMAIL") or
114 114 os.environ.get("LOGNAME", "unknown") + '@' + socket.getfqdn())
115 115 date = "%d %d" % (time.time(), time.timezone)
116 116 list.sort()
117 117 l = [hex(manifest), user, date] + list + ["", desc]
118 118 text = "\n".join(l)
119 119 return self.addrevision(text, transaction, self.count(), p1, p2)
120 120
121 121 class dircache:
122 122 def __init__(self, opener, ui):
123 123 self.opener = opener
124 124 self.dirty = 0
125 125 self.ui = ui
126 126 self.map = None
127 127 def __del__(self):
128 128 if self.dirty: self.write()
129 129 def __getitem__(self, key):
130 130 try:
131 131 return self.map[key]
132 132 except TypeError:
133 133 self.read()
134 134 return self[key]
135 135
136 136 def read(self):
137 137 if self.map is not None: return self.map
138 138
139 139 self.map = {}
140 140 try:
141 141 st = self.opener("dircache").read()
142 142 except: return
143 143
144 144 pos = 0
145 145 while pos < len(st):
146 146 e = struct.unpack(">llll", st[pos:pos+16])
147 147 l = e[3]
148 148 pos += 16
149 149 f = st[pos:pos + l]
150 150 self.map[f] = e[:3]
151 151 pos += l
152 152
153 153 def update(self, files):
154 154 if not files: return
155 155 self.read()
156 156 self.dirty = 1
157 157 for f in files:
158 158 try:
159 159 s = os.stat(f)
160 160 self.map[f] = (s.st_mode, s.st_size, s.st_mtime)
161 161 except IOError:
162 162 self.remove(f)
163 163
164 164 def taint(self, files):
165 165 if not files: return
166 166 self.read()
167 167 self.dirty = 1
168 168 for f in files:
169 169 self.map[f] = (0, -1, 0)
170 170
171 171 def remove(self, files):
172 172 if not files: return
173 173 self.read()
174 174 self.dirty = 1
175 175 for f in files:
176 176 try:
177 177 del self.map[f]
178 178 except KeyError:
179 179 self.ui.warn("Not in dircache: %s\n" % f)
180 180 pass
181 181
182 182 def clear(self):
183 183 self.map = {}
184 184 self.dirty = 1
185 185
186 186 def write(self):
187 187 st = self.opener("dircache", "w")
188 188 for f, e in self.map.items():
189 189 e = struct.pack(">llll", e[0], e[1], e[2], len(f))
190 190 st.write(e + f)
191 191 self.dirty = 0
192 192
193 193 def copy(self):
194 194 self.read()
195 195 return self.map.copy()
196 196
197 197 # used to avoid circular references so destructors work
198 198 def opener(base):
199 199 p = base
200 200 def o(path, mode="r"):
201 201 if p[:7] == "http://":
202 202 f = os.path.join(p, urllib.quote(path))
203 203 return httprangereader(f)
204 204
205 205 f = os.path.join(p, path)
206 206
207 207 if mode != "r":
208 208 try:
209 209 s = os.stat(f)
210 210 except OSError:
211 211 d = os.path.dirname(f)
212 212 if not os.path.isdir(d):
213 213 os.makedirs(d)
214 214 else:
215 215 if s.st_nlink > 1:
216 216 file(f + ".tmp", "w").write(file(f).read())
217 217 os.rename(f+".tmp", f)
218 218
219 219 return file(f, mode)
220 220
221 221 return o
222 222
223 223 class localrepository:
224 224 def __init__(self, ui, path=None, create=0):
225 225 self.remote = 0
226 226 if path and path[:7] == "http://":
227 227 self.remote = 1
228 228 self.path = path
229 229 else:
230 230 if not path:
231 231 p = os.getcwd()
232 232 while not os.path.isdir(os.path.join(p, ".hg")):
233 233 p = os.path.dirname(p)
234 234 if p == "/": raise "No repo found"
235 235 path = p
236 236 self.path = os.path.join(path, ".hg")
237 237
238 238 self.root = path
239 239 self.ui = ui
240 240
241 241 if create:
242 242 os.mkdir(self.path)
243 243 os.mkdir(self.join("data"))
244 244
245 245 self.opener = opener(self.path)
246 246 self.manifest = manifest(self.opener)
247 247 self.changelog = changelog(self.opener)
248 248 self.ignorelist = None
249 249 self.tags = None
250 250
251 251 if not self.remote:
252 252 self.dircache = dircache(self.opener, ui)
253 253 try:
254 254 self.current = bin(self.opener("current").read())
255 255 except IOError:
256 256 self.current = None
257 257
258 258 def setcurrent(self, node):
259 259 self.current = node
260 260 self.opener("current", "w").write(hex(node))
261 261
262 262 def ignore(self, f):
263 263 if self.ignorelist is None:
264 264 self.ignorelist = []
265 265 try:
266 266 l = open(os.path.join(self.root, ".hgignore"))
267 267 for pat in l:
268 268 if pat != "\n":
269 269 self.ignorelist.append(re.compile(pat[:-1]))
270 270 except IOError: pass
271 271 for pat in self.ignorelist:
272 272 if pat.search(f): return True
273 273 return False
274 274
275 275 def lookup(self, key):
276 276 if self.tags is None:
277 277 self.tags = {}
278 278 try:
279 279 fl = self.file(".hgtags")
280 280 for l in fl.revision(fl.tip()).splitlines():
281 281 if l:
282 282 n, k = l.split(" ")
283 283 self.tags[k] = bin(n)
284 284 except KeyError: pass
285 285 try:
286 286 return self.tags[key]
287 287 except KeyError:
288 288 return self.changelog.lookup(key)
289 289
290 290 def join(self, f):
291 291 return os.path.join(self.path, f)
292 292
293 293 def file(self, f):
294 294 return filelog(self.opener, f)
295 295
296 296 def transaction(self):
297 297 return transaction(self.opener, self.join("journal"),
298 298 self.join("undo"))
299 299
300 300 def recover(self, f = "journal"):
301 301 self.lock()
302 302 if os.path.exists(self.join(f)):
303 303 self.ui.status("attempting to rollback %s information\n" % f)
304 304 return rollback(self.opener, self.join(f))
305 305 else:
306 306 self.ui.warn("no %s information available\n" % f)
307 307
308 308 def lock(self, wait = 1):
309 309 try:
310 310 return lock.lock(self.join("lock"), 0)
311 311 except lock.LockHeld, inst:
312 312 if wait:
313 313 self.ui.warn("waiting for lock held by %s\n" % inst.args[0])
314 314 return lock.lock(self.join("lock"), wait)
315 315 raise inst
316 316
317 317 def commit(self, parent, update = None, text = ""):
318 318 self.lock()
319 319 try:
320 320 remove = [ l[:-1] for l in self.opener("to-remove") ]
321 321 os.unlink(self.join("to-remove"))
322 322
323 323 except IOError:
324 324 remove = []
325 325
326 326 if update == None:
327 327 update = self.diffdir(self.root, parent)[0]
328 328
329 329 if not update:
330 330 self.ui.status("nothing changed\n")
331 331 return
332 332
333 333 tr = self.transaction()
334 334
335 335 # check in files
336 336 new = {}
337 337 linkrev = self.changelog.count()
338 338 update.sort()
339 339 for f in update:
340 340 self.ui.note(f + "\n")
341 341 try:
342 342 t = file(f).read()
343 343 except IOError:
344 344 remove.append(f)
345 345 continue
346 346 r = self.file(f)
347 347 new[f] = r.add(t, tr, linkrev)
348 348
349 349 # update manifest
350 350 mmap = self.manifest.read(self.manifest.tip())
351 351 mmap.update(new)
352 352 for f in remove:
353 353 del mmap[f]
354 354 mnode = self.manifest.add(mmap, tr, linkrev)
355 355
356 356 # add changeset
357 357 new = new.keys()
358 358 new.sort()
359 359
360 360 edittext = text + "\n" + "HG: manifest hash %s\n" % hex(mnode)
361 361 edittext += "".join(["HG: changed %s\n" % f for f in new])
362 362 edittext += "".join(["HG: removed %s\n" % f for f in remove])
363 363 edittext = self.ui.edit(edittext)
364 364
365 365 n = self.changelog.add(mnode, new, edittext, tr)
366 366 tr.close()
367 367
368 368 self.setcurrent(n)
369 369 self.dircache.update(new)
370 370 self.dircache.remove(remove)
371 371
372 372 def checkout(self, node):
373 373 # checkout is really dumb at the moment
374 374 # it ought to basically merge
375 375 change = self.changelog.read(node)
376 376 l = self.manifest.read(change[0]).items()
377 377 l.sort()
378 378
379 379 for f,n in l:
380 380 if f[0] == "/": continue
381 381 self.ui.note(f, "\n")
382 382 t = self.file(f).revision(n)
383 383 try:
384 384 file(f, "w").write(t)
385 385 except IOError:
386 386 os.makedirs(os.path.dirname(f))
387 387 file(f, "w").write(t)
388 388
389 389 self.setcurrent(node)
390 390 self.dircache.clear()
391 391 self.dircache.update([f for f,n in l])
392 392
393 393 def diffdir(self, path, changeset):
394 394 changed = []
395 395 mf = {}
396 396 added = []
397 397
398 398 if changeset:
399 399 change = self.changelog.read(changeset)
400 400 mf = self.manifest.read(change[0])
401 401
402 402 if changeset == self.current:
403 403 dc = self.dircache.copy()
404 404 else:
405 405 dc = dict.fromkeys(mf)
406 406
407 407 def fcmp(fn):
408 408 t1 = file(os.path.join(self.root, fn)).read()
409 409 t2 = self.file(fn).revision(mf[fn])
410 410 return cmp(t1, t2)
411 411
412 412 for dir, subdirs, files in os.walk(self.root):
413 413 d = dir[len(self.root)+1:]
414 414 if ".hg" in subdirs: subdirs.remove(".hg")
415 415
416 416 for f in files:
417 417 fn = os.path.join(d, f)
418 418 try: s = os.stat(os.path.join(self.root, fn))
419 419 except: continue
420 420 if fn in dc:
421 421 c = dc[fn]
422 422 del dc[fn]
423 423 if not c:
424 424 if fcmp(fn):
425 425 changed.append(fn)
426 426 elif c[1] != s.st_size:
427 427 changed.append(fn)
428 428 elif c[0] != s.st_mode or c[2] != s.st_mtime:
429 429 if fcmp(fn):
430 430 changed.append(fn)
431 431 else:
432 432 if self.ignore(fn): continue
433 433 added.append(fn)
434 434
435 435 deleted = dc.keys()
436 436 deleted.sort()
437 437
438 438 return (changed, added, deleted)
439 439
440 440 def diffrevs(self, node1, node2):
441 441 changed, added = [], []
442 442
443 443 change = self.changelog.read(node1)
444 444 mf1 = self.manifest.read(change[0])
445 445 change = self.changelog.read(node2)
446 446 mf2 = self.manifest.read(change[0])
447 447
448 448 for fn in mf2:
449 449 if mf1.has_key(fn):
450 450 if mf1[fn] != mf2[fn]:
451 451 changed.append(fn)
452 452 del mf1[fn]
453 453 else:
454 454 added.append(fn)
455 455
456 456 deleted = mf1.keys()
457 457 deleted.sort()
458 458
459 459 return (changed, added, deleted)
460 460
461 461 def add(self, list):
462 462 self.dircache.taint(list)
463 463
464 464 def remove(self, list):
465 465 dl = self.opener("to-remove", "a")
466 466 for f in list:
467 467 dl.write(f + "\n")
468 468
469 469 def branches(self, nodes):
470 470 if not nodes: nodes = [self.changelog.tip()]
471 471 b = []
472 472 for n in nodes:
473 473 t = n
474 474 while n:
475 475 p = self.changelog.parents(n)
476 476 if p[1] != nullid or p[0] == nullid:
477 477 b.append((t, n, p[0], p[1]))
478 478 break
479 479 n = p[0]
480 480 return b
481 481
482 482 def between(self, pairs):
483 483 r = []
484 484
485 485 for top, bottom in pairs:
486 486 n, l, i = top, [], 0
487 487 f = 1
488 488
489 489 while n != bottom:
490 490 p = self.changelog.parents(n)[0]
491 491 if i == f:
492 492 l.append(n)
493 493 f = f * 2
494 494 n = p
495 495 i += 1
496 496
497 497 r.append(l)
498 498
499 499 return r
500 500
501 501 def newer(self, nodes):
502 502 m = {}
503 503 nl = []
504 504 pm = {}
505 505 cl = self.changelog
506 506 t = l = cl.count()
507 507
508 508 # find the lowest numbered node
509 509 for n in nodes:
510 510 l = min(l, cl.rev(n))
511 511 m[n] = 1
512 512
513 513 for i in xrange(l, t):
514 514 n = cl.node(i)
515 515 if n in m: # explicitly listed
516 516 pm[n] = 1
517 517 nl.append(n)
518 518 continue
519 519 for p in cl.parents(n):
520 520 if p in pm: # parent listed
521 521 pm[n] = 1
522 522 nl.append(n)
523 523 break
524 524
525 525 return nl
526 526
527 527 def getchangegroup(self, remote):
528 528 tip = remote.branches([])[0]
529 529 self.ui.debug("remote tip branch is %s:%s\n" %
530 530 (short(tip[0]), short(tip[1])))
531 531 m = self.changelog.nodemap
532 532 unknown = [tip]
533 533 search = []
534 534 fetch = []
535 535 seen = {}
536 536 seenbranch = {}
537 537
538 538 if tip[0] in m:
539 539 self.ui.note("nothing to do!\n")
540 540 return None
541 541
542 542 while unknown:
543 543 n = unknown.pop(0)
544 544 seen[n[0]] = 1
545 545
546 546 self.ui.debug("examining %s:%s\n" % (short(n[0]), short(n[1])))
547 547 if n == nullid: break
548 548 if n in seenbranch:
549 549 self.ui.debug("branch already found\n")
550 550 continue
551 551 if n[1] and n[1] in m: # do we know the base?
552 552 self.ui.debug("found incomplete branch %s:%s\n"
553 553 % (short(n[0]), short(n[1])))
554 554 search.append(n) # schedule branch range for scanning
555 555 seenbranch[n] = 1
556 556 else:
557 557 if n[2] in m and n[3] in m:
558 558 if n[1] not in fetch:
559 559 self.ui.debug("found new changeset %s\n" %
560 560 short(n[1]))
561 561 fetch.append(n[1]) # earliest unknown
562 562 continue
563 563
564 564 r = []
565 565 for a in n[2:4]:
566 566 if a not in seen: r.append(a)
567 567
568 568 if r:
569 569 self.ui.debug("requesting %s\n" %
570 570 " ".join(map(short, r)))
571 571 for b in remote.branches(r):
572 572 self.ui.debug("received %s:%s\n" %
573 573 (short(b[0]), short(b[1])))
574 574 if b[0] not in m and b[0] not in seen:
575 575 unknown.append(b)
576 576
577 577 while search:
578 578 n = search.pop(0)
579 579 l = remote.between([(n[0], n[1])])[0]
580 580 p = n[0]
581 581 f = 1
582 582 for i in l + [n[1]]:
583 583 if i in m:
584 584 if f <= 2:
585 585 self.ui.debug("found new branch changeset %s\n" %
586 586 short(p))
587 587 fetch.append(p)
588 588 else:
589 589 self.ui.debug("narrowed branch search to %s:%s\n"
590 590 % (short(p), short(i)))
591 591 search.append((p, i))
592 592 break
593 593 p, f = i, f * 2
594 594
595 595 for f in fetch:
596 596 if f in m:
597 597 raise "already have", short(f[:4])
598 598
599 599 self.ui.note("adding new changesets starting at " +
600 600 " ".join([short(f) for f in fetch]) + "\n")
601 601
602 602 return remote.changegroup(fetch)
603 603
604 604 def changegroup(self, basenodes):
605 605 nodes = self.newer(basenodes)
606 606
607 607 # construct the link map
608 608 linkmap = {}
609 609 for n in nodes:
610 610 linkmap[self.changelog.rev(n)] = n
611 611
612 612 # construct a list of all changed files
613 613 changed = {}
614 614 for n in nodes:
615 615 c = self.changelog.read(n)
616 616 for f in c[3]:
617 617 changed[f] = 1
618 618 changed = changed.keys()
619 619 changed.sort()
620 620
621 621 # the changegroup is changesets + manifests + all file revs
622 622 revs = [ self.changelog.rev(n) for n in nodes ]
623 623
624 624 yield self.changelog.group(linkmap)
625 625 yield self.manifest.group(linkmap)
626 626
627 627 for f in changed:
628 628 g = self.file(f).group(linkmap)
629 629 if not g: raise "couldn't find change to %s" % f
630 630 l = struct.pack(">l", len(f))
631 631 yield "".join([l, f, g])
632 632
633 633 def addchangegroup(self, generator):
634 634 self.lock()
635 635 class genread:
636 636 def __init__(self, generator):
637 637 self.g = generator
638 638 self.buf = ""
639 639 def read(self, l):
640 640 while l > len(self.buf):
641 641 try:
642 642 self.buf += self.g.next()
643 643 except StopIteration:
644 644 break
645 645 d, self.buf = self.buf[:l], self.buf[l:]
646 646 return d
647 647
648 648 if not generator: return
649 649 source = genread(generator)
650 650
651 651 def getchunk(add = 0):
652 652 d = source.read(4)
653 653 if not d: return ""
654 654 l = struct.unpack(">l", d)[0]
655 655 return source.read(l - 4 + add)
656 656
657 657 tr = self.transaction()
658 658 simple = True
659 659 need = {}
660 660
661 661 self.ui.status("adding changesets\n")
662 662 # pull off the changeset group
663 663 def report(x):
664 664 self.ui.debug("add changeset %s\n" % short(x))
665 665 return self.changelog.count()
666 666
667 667 csg = getchunk()
668 668 co = self.changelog.tip()
669 669 cn = self.changelog.addgroup(csg, report, tr)
670 670
671 671 self.ui.status("adding manifests\n")
672 672 # pull off the manifest group
673 673 mfg = getchunk()
674 674 mm = self.manifest.tip()
675 675 mo = self.manifest.addgroup(mfg, lambda x: self.changelog.rev(x), tr)
676 676
677 677 # do we need a resolve?
678 678 if self.changelog.ancestor(co, cn) != co:
679 679 simple = False
680 680 resolverev = self.changelog.count()
681 681
682 682 # resolve the manifest to determine which files
683 683 # we care about merging
684 684 self.ui.status("resolving manifests\n")
685 685 ma = self.manifest.ancestor(mm, mo)
686 686 omap = self.manifest.read(mo) # other
687 687 amap = self.manifest.read(ma) # ancestor
688 688 mmap = self.manifest.read(mm) # mine
689 689 nmap = {}
690 690
691 691 self.ui.debug(" ancestor %s local %s remote %s\n" %
692 692 (short(ma), short(mm), short(mo)))
693 693
694 694 for f, mid in mmap.iteritems():
695 695 if f in omap:
696 696 if mid != omap[f]:
697 697 self.ui.debug(" %s versions differ, do resolve\n" % f)
698 698 need[f] = mid # use merged version or local version
699 699 else:
700 700 nmap[f] = mid # keep ours
701 701 del omap[f]
702 702 elif f in amap:
703 703 if mid != amap[f]:
704 704 r = self.ui.prompt(
705 705 (" local changed %s which remote deleted\n" % f) +
706 706 "(k)eep or (d)elete?", "[kd]", "k")
707 707 if r == "k": nmap[f] = mid
708 708 else:
709 709 self.ui.debug("other deleted %s\n" % f)
710 710 pass # other deleted it
711 711 else:
712 712 self.ui.debug("local created %s\n" %f)
713 713 nmap[f] = mid # we created it
714 714
715 715 del mmap
716 716
717 717 for f, oid in omap.iteritems():
718 718 if f in amap:
719 719 if oid != amap[f]:
720 720 r = self.ui.prompt(
721 721 ("remote changed %s which local deleted\n" % f) +
722 722 "(k)eep or (d)elete?", "[kd]", "k")
723 723 if r == "k": nmap[f] = oid
724 724 else:
725 725 pass # probably safe
726 726 else:
727 727 self.ui.debug("remote created %s, do resolve\n" % f)
728 728 need[f] = oid
729 729
730 730 del omap
731 731 del amap
732 732
733 733 new = need.keys()
734 734 new.sort()
735 735
736 736 # process the files
737 737 self.ui.status("adding files\n")
738 738 while 1:
739 739 f = getchunk(4)
740 740 if not f: break
741 741 fg = getchunk()
742 742 self.ui.debug("adding %s revisions\n" % f)
743 743 fl = self.file(f)
744 744 o = fl.tip()
745 745 n = fl.addgroup(fg, lambda x: self.changelog.rev(x), tr)
746 746 if f in need:
747 747 del need[f]
748 748 # manifest resolve determined we need to merge the tips
749 749 nmap[f] = self.merge3(fl, f, o, n, tr, resolverev)
750 750
751 751 if need:
752 752 # we need to do trivial merges on local files
753 753 for f in new:
754 754 if f not in need: continue
755 755 fl = self.file(f)
756 756 nmap[f] = self.merge3(fl, f, need[f], fl.tip(), tr, resolverev)
757 757
758 758 # For simple merges, we don't need to resolve manifests or changesets
759 759 if simple:
760 760 self.ui.debug("simple merge, skipping resolve\n")
761 761 tr.close()
762 762 return
763 763
764 764 node = self.manifest.add(nmap, tr, resolverev, mm, mo)
765 765
766 766 # Now all files and manifests are merged, we add the changed files
767 767 # and manifest id to the changelog
768 768 self.ui.status("committing merge changeset\n")
769 769 if co == cn: cn = -1
770 770
771 771 edittext = "\nHG: merge resolve\n" + \
772 772 "HG: manifest hash %s\n" % hex(mnode) + \
773 773 "".join(["HG: changed %s\n" % f for f in new])
774 774 edittext = self.ui.edit(edittext)
775 775 n = self.changelog.add(node, new, edittext, tr, co, cn)
776 776
777 777 tr.close()
778 778
779 779 def merge3(self, fl, fn, my, other, transaction, link):
780 780 """perform a 3-way merge and append the result"""
781 781
782 782 def temp(prefix, node):
783 783 pre = "%s~%s." % (os.path.basename(fn), prefix)
784 784 (fd, name) = tempfile.mkstemp("", pre)
785 785 f = os.fdopen(fd, "w")
786 786 f.write(fl.revision(node))
787 787 f.close()
788 788 return name
789 789
790 790 base = fl.ancestor(my, other)
791 791 self.ui.note("resolving %s\n" % fn)
792 792 self.ui.debug("local %s remote %s ancestor %s\n" %
793 793 (short(my), short(other), short(base)))
794 794
795 795 if my == base:
796 796 text = fl.revision(other)
797 797 else:
798 798 a = temp("local", my)
799 799 b = temp("remote", other)
800 800 c = temp("parent", base)
801 801
802 802 cmd = os.environ["HGMERGE"]
803 803 self.ui.debug("invoking merge with %s\n" % cmd)
804 804 r = os.system("%s %s %s %s %s" % (cmd, a, b, c, fn))
805 805 if r:
806 806 raise "Merge failed!"
807 807
808 808 text = open(a).read()
809 809 os.unlink(a)
810 810 os.unlink(b)
811 811 os.unlink(c)
812 812
813 813 return fl.add(text, transaction, link, my, other)
814 814
815 815 class remoterepository:
816 816 def __init__(self, ui, path):
817 817 self.url = path
818 818 self.ui = ui
819 819
820 820 def do_cmd(self, cmd, **args):
821 821 self.ui.debug("sending %s command\n" % cmd)
822 822 q = {"cmd": cmd}
823 823 q.update(args)
824 824 qs = urllib.urlencode(q)
825 825 cu = "%s?%s" % (self.url, qs)
826 826 return urllib.urlopen(cu)
827 827
828 828 def branches(self, nodes):
829 829 n = " ".join(map(hex, nodes))
830 830 d = self.do_cmd("branches", nodes=n).read()
831 831 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
832 832 return br
833 833
834 834 def between(self, pairs):
835 835 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
836 836 d = self.do_cmd("between", pairs=n).read()
837 837 p = [ map(bin, l.split(" ")) for l in d.splitlines() ]
838 838 return p
839 839
840 840 def changegroup(self, nodes):
841 841 n = " ".join(map(hex, nodes))
842 842 zd = zlib.decompressobj()
843 843 f = self.do_cmd("changegroup", roots=n)
844 844 while 1:
845 845 d = f.read(4096)
846 846 if not d:
847 847 yield zd.flush()
848 848 break
849 849 yield zd.decompress(d)
850 850
851 851 def repository(ui, path=None, create=0):
852 852 if path and path[:7] == "http://":
853 853 return remoterepository(ui, path)
854 854 if path and path[:5] == "hg://":
855 855 return remoterepository(ui, path.replace("hg://", "http://"))
856 856 if path and path[:11] == "old-http://":
857 857 return localrepository(ui, path.replace("old-http://", "http://"))
858 858 else:
859 859 return localrepository(ui, path, create)
860 860
861 861 class ui:
862 862 def __init__(self, verbose=False, debug=False, quiet=False,
863 863 interactive=True):
864 864 self.quiet = quiet and not verbose and not debug
865 865 self.verbose = verbose or debug
866 866 self.debugflag = debug
867 867 self.interactive = interactive
868 868 def write(self, *args):
869 869 for a in args:
870 870 sys.stdout.write(str(a))
871 871 def readline(self):
872 872 return sys.stdin.readline()[:-1]
873 873 def prompt(self, msg, pat, default = "y"):
874 874 if not self.interactive: return default
875 875 while 1:
876 876 self.write(msg, " ")
877 877 r = self.readline()
878 878 if re.match(pat, r):
879 879 return r
880 880 else:
881 881 self.write("unrecognized response\n")
882 882 def status(self, *msg):
883 883 if not self.quiet: self.write(*msg)
884 884 def warn(self, msg):
885 885 self.write(*msg)
886 886 def note(self, *msg):
887 887 if self.verbose: self.write(*msg)
888 888 def debug(self, *msg):
889 889 if self.debugflag: self.write(*msg)
890 890 def edit(self, text):
891 891 (fd, name) = tempfile.mkstemp("hg")
892 892 f = os.fdopen(fd, "w")
893 893 f.write(text)
894 894 f.close()
895 895
896 editor = os.environ.get("EDITOR", "vi")
896 editor = os.environ.get("HGEDITOR") or os.environ.get("EDITOR", "vi")
897 897 r = os.system("%s %s" % (editor, name))
898
898 899 if r:
899 900 raise "Edit failed!"
900 901
901 902 t = open(name).read()
902 903 t = re.sub("(?m)^HG:.*\n", "", t)
903 904
904 905 return t
905
906 906
907 907 class httprangereader:
908 908 def __init__(self, url):
909 909 self.url = url
910 910 self.pos = 0
911 911 def seek(self, pos):
912 912 self.pos = pos
913 913 def read(self, bytes=None):
914 914 opener = urllib2.build_opener(byterange.HTTPRangeHandler())
915 915 urllib2.install_opener(opener)
916 916 req = urllib2.Request(self.url)
917 917 end = ''
918 918 if bytes: end = self.pos + bytes
919 919 req.add_header('Range', 'bytes=%d-%s' % (self.pos, end))
920 920 f = urllib2.urlopen(req)
921 921 return f.read()
General Comments 0
You need to be logged in to leave comments. Login now