##// END OF EJS Templates
Pass filename to $HGMERGE...
mpm@selenic.com -
r149:8cd45e15 default
parent child Browse files
Show More
@@ -1,892 +1,892 b''
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
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 commit(self, parent, update = None, text = ""):
301 301 tr = self.transaction()
302 302
303 303 try:
304 304 remove = [ l[:-1] for l in self.opener("to-remove") ]
305 305 os.unlink(self.join("to-remove"))
306 306
307 307 except IOError:
308 308 remove = []
309 309
310 310 if update == None:
311 311 update = self.diffdir(self.root, parent)[0]
312 312
313 313 # check in files
314 314 new = {}
315 315 linkrev = self.changelog.count()
316 316 update.sort()
317 317 for f in update:
318 318 self.ui.note(f + "\n")
319 319 try:
320 320 t = file(f).read()
321 321 except IOError:
322 322 remove.append(f)
323 323 continue
324 324 r = self.file(f)
325 325 new[f] = r.add(t, tr, linkrev)
326 326
327 327 # update manifest
328 328 mmap = self.manifest.read(self.manifest.tip())
329 329 mmap.update(new)
330 330 for f in remove:
331 331 del mmap[f]
332 332 mnode = self.manifest.add(mmap, tr, linkrev)
333 333
334 334 # add changeset
335 335 new = new.keys()
336 336 new.sort()
337 337
338 338 edittext = text + "\n"+"".join(["HG: changed %s\n" % f for f in new])
339 339 edittext += "".join(["HG: removed %s\n" % f for f in remove])
340 340 edittext = self.ui.edit(edittext)
341 341
342 342 n = self.changelog.add(mnode, new, edittext, tr)
343 343 tr.close()
344 344
345 345 self.setcurrent(n)
346 346 self.dircache.update(new)
347 347 self.dircache.remove(remove)
348 348
349 349 def checkout(self, node):
350 350 # checkout is really dumb at the moment
351 351 # it ought to basically merge
352 352 change = self.changelog.read(node)
353 353 l = self.manifest.read(change[0]).items()
354 354 l.sort()
355 355
356 356 for f,n in l:
357 357 if f[0] == "/": continue
358 358 self.ui.note(f, "\n")
359 359 t = self.file(f).revision(n)
360 360 try:
361 361 file(f, "w").write(t)
362 362 except IOError:
363 363 os.makedirs(os.path.dirname(f))
364 364 file(f, "w").write(t)
365 365
366 366 self.setcurrent(node)
367 367 self.dircache.clear()
368 368 self.dircache.update([f for f,n in l])
369 369
370 370 def diffdir(self, path, changeset):
371 371 changed = []
372 372 mf = {}
373 373 added = []
374 374
375 375 if changeset:
376 376 change = self.changelog.read(changeset)
377 377 mf = self.manifest.read(change[0])
378 378
379 379 if changeset == self.current:
380 380 dc = self.dircache.copy()
381 381 else:
382 382 dc = dict.fromkeys(mf)
383 383
384 384 def fcmp(fn):
385 385 t1 = file(os.path.join(self.root, fn)).read()
386 386 t2 = self.file(fn).revision(mf[fn])
387 387 return cmp(t1, t2)
388 388
389 389 for dir, subdirs, files in os.walk(self.root):
390 390 d = dir[len(self.root)+1:]
391 391 if ".hg" in subdirs: subdirs.remove(".hg")
392 392
393 393 for f in files:
394 394 fn = os.path.join(d, f)
395 395 try: s = os.stat(os.path.join(self.root, fn))
396 396 except: continue
397 397 if fn in dc:
398 398 c = dc[fn]
399 399 del dc[fn]
400 400 if not c:
401 401 if fcmp(fn):
402 402 changed.append(fn)
403 403 elif c[1] != s.st_size:
404 404 changed.append(fn)
405 405 elif c[0] != s.st_mode or c[2] != s.st_mtime:
406 406 if fcmp(fn):
407 407 changed.append(fn)
408 408 else:
409 409 if self.ignore(fn): continue
410 410 added.append(fn)
411 411
412 412 deleted = dc.keys()
413 413 deleted.sort()
414 414
415 415 return (changed, added, deleted)
416 416
417 417 def diffrevs(self, node1, node2):
418 418 changed, added = [], []
419 419
420 420 change = self.changelog.read(node1)
421 421 mf1 = self.manifest.read(change[0])
422 422 change = self.changelog.read(node2)
423 423 mf2 = self.manifest.read(change[0])
424 424
425 425 for fn in mf2:
426 426 if mf1.has_key(fn):
427 427 if mf1[fn] != mf2[fn]:
428 428 changed.append(fn)
429 429 del mf1[fn]
430 430 else:
431 431 added.append(fn)
432 432
433 433 deleted = mf1.keys()
434 434 deleted.sort()
435 435
436 436 return (changed, added, deleted)
437 437
438 438 def add(self, list):
439 439 self.dircache.taint(list)
440 440
441 441 def remove(self, list):
442 442 dl = self.opener("to-remove", "a")
443 443 for f in list:
444 444 dl.write(f + "\n")
445 445
446 446 def branches(self, nodes):
447 447 if not nodes: nodes = [self.changelog.tip()]
448 448 b = []
449 449 for n in nodes:
450 450 t = n
451 451 while n:
452 452 p = self.changelog.parents(n)
453 453 if p[1] != nullid or p[0] == nullid:
454 454 b.append((t, n, p[0], p[1]))
455 455 break
456 456 n = p[0]
457 457 return b
458 458
459 459 def between(self, pairs):
460 460 r = []
461 461
462 462 for top, bottom in pairs:
463 463 n, l, i = top, [], 0
464 464 f = 1
465 465
466 466 while n != bottom:
467 467 p = self.changelog.parents(n)[0]
468 468 if i == f:
469 469 l.append(n)
470 470 f = f * 2
471 471 n = p
472 472 i += 1
473 473
474 474 r.append(l)
475 475
476 476 return r
477 477
478 478 def newer(self, nodes):
479 479 m = {}
480 480 nl = []
481 481 pm = {}
482 482 cl = self.changelog
483 483 t = l = cl.count()
484 484
485 485 # find the lowest numbered node
486 486 for n in nodes:
487 487 l = min(l, cl.rev(n))
488 488 m[n] = 1
489 489
490 490 for i in xrange(l, t):
491 491 n = cl.node(i)
492 492 if n in m: # explicitly listed
493 493 pm[n] = 1
494 494 nl.append(n)
495 495 continue
496 496 for p in cl.parents(n):
497 497 if p in pm: # parent listed
498 498 pm[n] = 1
499 499 nl.append(n)
500 500 break
501 501
502 502 return nl
503 503
504 504 def getchangegroup(self, remote):
505 505 tip = remote.branches([])[0]
506 506 self.ui.debug("remote tip branch is %s:%s\n" %
507 507 (short(tip[0]), short(tip[1])))
508 508 m = self.changelog.nodemap
509 509 unknown = [tip]
510 510 search = []
511 511 fetch = []
512 512 seen = {}
513 513 seenbranch = {}
514 514
515 515 if tip[0] in m:
516 516 self.ui.note("nothing to do!\n")
517 517 return None
518 518
519 519 while unknown:
520 520 n = unknown.pop(0)
521 521 seen[n[0]] = 1
522 522
523 523 self.ui.debug("examining %s:%s\n" % (short(n[0]), short(n[1])))
524 524 if n == nullid: break
525 525 if n in seenbranch:
526 526 self.ui.debug("branch already found\n")
527 527 continue
528 528 if n[1] and n[1] in m: # do we know the base?
529 529 self.ui.debug("found incomplete branch %s:%s\n"
530 530 % (short(n[0]), short(n[1])))
531 531 search.append(n) # schedule branch range for scanning
532 532 seenbranch[n] = 1
533 533 else:
534 534 if n[2] in m and n[3] in m:
535 535 if n[1] not in fetch:
536 536 self.ui.debug("found new changeset %s\n" %
537 537 short(n[1]))
538 538 fetch.append(n[1]) # earliest unknown
539 539 continue
540 540
541 541 r = []
542 542 for a in n[2:4]:
543 543 if a not in seen: r.append(a)
544 544
545 545 if r:
546 546 self.ui.debug("requesting %s\n" %
547 547 " ".join(map(short, r)))
548 548 for b in remote.branches(r):
549 549 self.ui.debug("received %s:%s\n" %
550 550 (short(b[0]), short(b[1])))
551 551 if b[0] not in m and b[0] not in seen:
552 552 unknown.append(b)
553 553
554 554 while search:
555 555 n = search.pop(0)
556 556 l = remote.between([(n[0], n[1])])[0]
557 557 p = n[0]
558 558 f = 1
559 559 for i in l + [n[1]]:
560 560 if i in m:
561 561 if f <= 2:
562 562 self.ui.debug("found new branch changeset %s\n" %
563 563 short(p))
564 564 fetch.append(p)
565 565 else:
566 566 self.ui.debug("narrowed branch search to %s:%s\n"
567 567 % (short(p), short(i)))
568 568 search.append((p, i))
569 569 break
570 570 p, f = i, f * 2
571 571
572 572 for f in fetch:
573 573 if f in m:
574 574 raise "already have", short(f[:4])
575 575
576 576 self.ui.note("adding new changesets starting at " +
577 577 " ".join([short(f) for f in fetch]) + "\n")
578 578
579 579 return remote.changegroup(fetch)
580 580
581 581 def changegroup(self, basenodes):
582 582 nodes = self.newer(basenodes)
583 583
584 584 # construct the link map
585 585 linkmap = {}
586 586 for n in nodes:
587 587 linkmap[self.changelog.rev(n)] = n
588 588
589 589 # construct a list of all changed files
590 590 changed = {}
591 591 for n in nodes:
592 592 c = self.changelog.read(n)
593 593 for f in c[3]:
594 594 changed[f] = 1
595 595 changed = changed.keys()
596 596 changed.sort()
597 597
598 598 # the changegroup is changesets + manifests + all file revs
599 599 revs = [ self.changelog.rev(n) for n in nodes ]
600 600
601 601 yield self.changelog.group(linkmap)
602 602 yield self.manifest.group(linkmap)
603 603
604 604 for f in changed:
605 605 g = self.file(f).group(linkmap)
606 606 if not g: raise "couldn't find change to %s" % f
607 607 l = struct.pack(">l", len(f))
608 608 yield "".join([l, f, g])
609 609
610 610 def addchangegroup(self, generator):
611 611 class genread:
612 612 def __init__(self, generator):
613 613 self.g = generator
614 614 self.buf = ""
615 615 def read(self, l):
616 616 while l > len(self.buf):
617 617 try:
618 618 self.buf += self.g.next()
619 619 except StopIteration:
620 620 break
621 621 d, self.buf = self.buf[:l], self.buf[l:]
622 622 return d
623 623
624 624 if not generator: return
625 625 source = genread(generator)
626 626
627 627 def getchunk(add = 0):
628 628 d = source.read(4)
629 629 if not d: return ""
630 630 l = struct.unpack(">l", d)[0]
631 631 return source.read(l - 4 + add)
632 632
633 633 tr = self.transaction()
634 634 simple = True
635 635 need = {}
636 636
637 637 self.ui.status("adding changesets\n")
638 638 # pull off the changeset group
639 639 def report(x):
640 640 self.ui.debug("add changeset %s\n" % short(x))
641 641 return self.changelog.count()
642 642
643 643 csg = getchunk()
644 644 co = self.changelog.tip()
645 645 cn = self.changelog.addgroup(csg, report, tr)
646 646
647 647 self.ui.status("adding manifests\n")
648 648 # pull off the manifest group
649 649 mfg = getchunk()
650 650 mm = self.manifest.tip()
651 651 mo = self.manifest.addgroup(mfg, lambda x: self.changelog.rev(x), tr)
652 652
653 653 # do we need a resolve?
654 654 if self.changelog.ancestor(co, cn) != co:
655 655 simple = False
656 656 resolverev = self.changelog.count()
657 657
658 658 # resolve the manifest to determine which files
659 659 # we care about merging
660 660 self.ui.status("resolving manifests\n")
661 661 ma = self.manifest.ancestor(mm, mo)
662 662 omap = self.manifest.read(mo) # other
663 663 amap = self.manifest.read(ma) # ancestor
664 664 mmap = self.manifest.read(mm) # mine
665 665 nmap = {}
666 666
667 667 self.ui.debug(" ancestor %s local %s remote %s\n" %
668 668 (short(ma), short(mm), short(mo)))
669 669
670 670 for f, mid in mmap.iteritems():
671 671 if f in omap:
672 672 if mid != omap[f]:
673 673 self.ui.debug(" %s versions differ, do resolve\n" % f)
674 674 need[f] = mid # use merged version or local version
675 675 else:
676 676 nmap[f] = mid # keep ours
677 677 del omap[f]
678 678 elif f in amap:
679 679 if mid != amap[f]:
680 680 r = self.ui.prompt(
681 681 (" local changed %s which remote deleted\n" % f) +
682 682 "(k)eep or (d)elete?", "[kd]", "k")
683 683 if r == "k": nmap[f] = mid
684 684 else:
685 685 self.ui.debug("other deleted %s\n" % f)
686 686 pass # other deleted it
687 687 else:
688 688 self.ui.debug("local created %s\n" %f)
689 689 nmap[f] = mid # we created it
690 690
691 691 del mmap
692 692
693 693 for f, oid in omap.iteritems():
694 694 if f in amap:
695 695 if oid != amap[f]:
696 696 r = self.ui.prompt(
697 697 ("remote changed %s which local deleted\n" % f) +
698 698 "(k)eep or (d)elete?", "[kd]", "k")
699 699 if r == "k": nmap[f] = oid
700 700 else:
701 701 pass # probably safe
702 702 else:
703 703 self.ui.debug("remote created %s, do resolve\n" % f)
704 704 need[f] = oid
705 705
706 706 del omap
707 707 del amap
708 708
709 709 new = need.keys()
710 710 new.sort()
711 711
712 712 # process the files
713 713 self.ui.status("adding files\n")
714 714 while 1:
715 715 f = getchunk(4)
716 716 if not f: break
717 717 fg = getchunk()
718 718 self.ui.debug("adding %s revisions\n" % f)
719 719 fl = self.file(f)
720 720 o = fl.tip()
721 721 n = fl.addgroup(fg, lambda x: self.changelog.rev(x), tr)
722 722 if f in need:
723 723 del need[f]
724 724 # manifest resolve determined we need to merge the tips
725 725 nmap[f] = self.merge3(fl, f, o, n, tr, resolverev)
726 726
727 727 if need:
728 728 # we need to do trivial merges on local files
729 729 for f in new:
730 730 if f not in need: continue
731 731 fl = self.file(f)
732 732 nmap[f] = self.merge3(fl, f, need[f], fl.tip(), tr, resolverev)
733 733
734 734 # For simple merges, we don't need to resolve manifests or changesets
735 735 if simple:
736 736 self.ui.debug("simple merge, skipping resolve\n")
737 737 tr.close()
738 738 return
739 739
740 740 node = self.manifest.add(nmap, tr, resolverev, mm, mo)
741 741
742 742 # Now all files and manifests are merged, we add the changed files
743 743 # and manifest id to the changelog
744 744 self.ui.status("committing merge changeset\n")
745 745 if co == cn: cn = -1
746 746
747 747 edittext = "\nHG: merge resolve\n" + \
748 748 "".join(["HG: changed %s\n" % f for f in new])
749 749 edittext = self.ui.edit(edittext)
750 750 n = self.changelog.add(node, new, edittext, tr, co, cn)
751 751
752 752 tr.close()
753 753
754 754 def merge3(self, fl, fn, my, other, transaction, link):
755 755 """perform a 3-way merge and append the result"""
756 756
757 757 def temp(prefix, node):
758 758 pre = "%s~%s." % (os.path.basename(fn), prefix)
759 759 (fd, name) = tempfile.mkstemp("", pre)
760 760 f = os.fdopen(fd, "w")
761 761 f.write(fl.revision(node))
762 762 f.close()
763 763 return name
764 764
765 765 base = fl.ancestor(my, other)
766 766 self.ui.note("resolving %s\n" % fn)
767 767 self.ui.debug("local %s remote %s ancestor %s\n" %
768 768 (short(my), short(other), short(base)))
769 769
770 770 if my == base:
771 771 text = fl.revision(other)
772 772 else:
773 773 a = temp("local", my)
774 774 b = temp("remote", other)
775 775 c = temp("parent", base)
776 776
777 777 cmd = os.environ["HGMERGE"]
778 778 self.ui.debug("invoking merge with %s\n" % cmd)
779 r = os.system("%s %s %s %s" % (cmd, a, b, c))
779 r = os.system("%s %s %s %s %s" % (cmd, a, b, c, fn))
780 780 if r:
781 781 raise "Merge failed!"
782 782
783 783 text = open(a).read()
784 784 os.unlink(a)
785 785 os.unlink(b)
786 786 os.unlink(c)
787 787
788 788 return fl.addrevision(text, transaction, link, my, other)
789 789
790 790 class remoterepository:
791 791 def __init__(self, ui, path):
792 792 self.url = path.replace("hg://", "http://", 1)
793 793 self.ui = ui
794 794
795 795 def do_cmd(self, cmd, **args):
796 796 self.ui.debug("sending %s command\n" % cmd)
797 797 q = {"cmd": cmd}
798 798 q.update(args)
799 799 qs = urllib.urlencode(q)
800 800 cu = "%s?%s" % (self.url, qs)
801 801 return urllib.urlopen(cu)
802 802
803 803 def branches(self, nodes):
804 804 n = " ".join(map(hex, nodes))
805 805 d = self.do_cmd("branches", nodes=n).read()
806 806 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
807 807 return br
808 808
809 809 def between(self, pairs):
810 810 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
811 811 d = self.do_cmd("between", pairs=n).read()
812 812 p = [ map(bin, l.split(" ")) for l in d.splitlines() ]
813 813 return p
814 814
815 815 def changegroup(self, nodes):
816 816 n = " ".join(map(hex, nodes))
817 817 zd = zlib.decompressobj()
818 818 f = self.do_cmd("changegroup", roots=n)
819 819 while 1:
820 820 d = f.read(4096)
821 821 if not d:
822 822 yield zd.flush()
823 823 break
824 824 yield zd.decompress(d)
825 825
826 826 def repository(ui, path=None, create=0):
827 827 if path and path[:5] == "hg://":
828 828 return remoterepository(ui, path)
829 829 else:
830 830 return localrepository(ui, path, create)
831 831
832 832 class ui:
833 833 def __init__(self, verbose=False, debug=False, quiet=False,
834 834 interactive=True):
835 835 self.quiet = quiet and not verbose and not debug
836 836 self.verbose = verbose or debug
837 837 self.debugflag = debug
838 838 self.interactive = interactive
839 839 def write(self, *args):
840 840 for a in args:
841 841 sys.stdout.write(str(a))
842 842 def readline(self):
843 843 return sys.stdin.readline()[:-1]
844 844 def prompt(self, msg, pat, default = "y"):
845 845 if not self.interactive: return default
846 846 while 1:
847 847 self.write(msg, " ")
848 848 r = self.readline()
849 849 if re.match(pat, r):
850 850 return r
851 851 else:
852 852 self.write("unrecognized response\n")
853 853 def status(self, *msg):
854 854 if not self.quiet: self.write(*msg)
855 855 def warn(self, msg):
856 856 self.write(*msg)
857 857 def note(self, *msg):
858 858 if self.verbose: self.write(*msg)
859 859 def debug(self, *msg):
860 860 if self.debugflag: self.write(*msg)
861 861 def edit(self, text):
862 862 (fd, name) = tempfile.mkstemp("hg")
863 863 f = os.fdopen(fd, "w")
864 864 f.write(text)
865 865 f.close()
866 866
867 867 editor = os.environ.get("EDITOR", "vi")
868 868 r = os.system("%s %s" % (editor, name))
869 869 if r:
870 870 raise "Edit failed!"
871 871
872 872 t = open(name).read()
873 873 t = re.sub("(?m)^HG:.*\n", "", t)
874 874
875 875 return t
876 876
877 877
878 878 class httprangereader:
879 879 def __init__(self, url):
880 880 self.url = url
881 881 self.pos = 0
882 882 def seek(self, pos):
883 883 self.pos = pos
884 884 def read(self, bytes=None):
885 885 opener = urllib2.build_opener(byterange.HTTPRangeHandler())
886 886 urllib2.install_opener(opener)
887 887 req = urllib2.Request(self.url)
888 888 end = ''
889 889 if bytes: end = self.pos + bytes
890 890 req.add_header('Range', 'bytes=%d-%s' % (self.pos, end))
891 891 f = urllib2.urlopen(req)
892 892 return f.read()
General Comments 0
You need to be logged in to leave comments. Login now