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