##// END OF EJS Templates
Minor annotate performance tweaks...
mpm@selenic.com -
r471:4c7f687e default
parent child Browse files
Show More
@@ -1,1456 +1,1450 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, os
9 9 import util
10 10 from revlog import *
11 11 from demandload import *
12 12 demandload(globals(), "re lock urllib urllib2 transaction time socket")
13 13 demandload(globals(), "tempfile httprangereader bdiff")
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 t = self.revision(node)
23 23 if t[:2] != '\1\n':
24 24 return t
25 25 s = t.find('\1\n', 2)
26 26 return t[s+2:]
27 27
28 28 def readmeta(self, node):
29 29 t = self.revision(node)
30 30 if t[:2] != '\1\n':
31 31 return t
32 32 s = t.find('\1\n', 2)
33 33 mt = t[2:s]
34 34 for l in mt.splitlines():
35 35 k, v = l.split(": ", 1)
36 36 m[k] = v
37 37 return m
38 38
39 39 def add(self, text, meta, transaction, link, p1=None, p2=None):
40 40 if meta or text[:2] == '\1\n':
41 41 mt = ""
42 42 if meta:
43 43 mt = [ "%s: %s\n" % (k, v) for k,v in meta.items() ]
44 44 text = "\1\n" + "".join(mt) + "\1\n" + text
45 45 return self.addrevision(text, transaction, link, p1, p2)
46 46
47 47 def annotate(self, node):
48 48
49 49 def decorate(text, rev):
50 50 return ([rev] * len(text.splitlines()), text)
51 51
52 52 def pair(parent, child):
53 new = []
54 lb = 0
55 53 for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]):
56 new[lb:] = child[0][lb:b1]
57 new[b1:] = parent[0][a1:a2]
58 lb = b2
59 return (new, child[1])
54 child[0][b1:b2] = parent[0][a1:a2]
55 return child
60 56
61 57 # find all ancestors
62 58 needed = {node:1}
63 59 visit = [node]
64 60 while visit:
65 61 n = visit.pop(0)
66 62 for p in self.parents(n):
67 63 if p not in needed:
68 64 needed[p] = 1
69 65 visit.append(p)
70 66 else:
71 67 # count how many times we'll use this
72 68 needed[p] += 1
73 69
74 70 # sort by revision which is a topological order
75 visit = needed.keys()
76 visit = [ (self.rev(n), n) for n in visit ]
71 visit = [ (self.rev(n), n) for n in needed.keys() ]
77 72 visit.sort()
78 visit = [ p[1] for p in visit ]
79 73 hist = {}
80 74
81 for n in visit:
75 for r,n in visit:
82 76 curr = decorate(self.read(n), self.linkrev(n))
83 77 for p in self.parents(n):
84 78 if p != nullid:
85 79 curr = pair(hist[p], curr)
86 80 # trim the history of unneeded revs
87 81 needed[p] -= 1
88 82 if not needed[p]:
89 83 del hist[p]
90 84 hist[n] = curr
91 85
92 86 return zip(hist[n][0], hist[n][1].splitlines(1))
93 87
94 88 class manifest(revlog):
95 89 def __init__(self, opener):
96 90 self.mapcache = None
97 91 self.listcache = None
98 92 self.addlist = None
99 93 revlog.__init__(self, opener, "00manifest.i", "00manifest.d")
100 94
101 95 def read(self, node):
102 96 if node == nullid: return {} # don't upset local cache
103 97 if self.mapcache and self.mapcache[0] == node:
104 98 return self.mapcache[1].copy()
105 99 text = self.revision(node)
106 100 map = {}
107 101 flag = {}
108 102 self.listcache = (text, text.splitlines(1))
109 103 for l in self.listcache[1]:
110 104 (f, n) = l.split('\0')
111 105 map[f] = bin(n[:40])
112 106 flag[f] = (n[40:-1] == "x")
113 107 self.mapcache = (node, map, flag)
114 108 return map
115 109
116 110 def readflags(self, node):
117 111 if node == nullid: return {} # don't upset local cache
118 112 if not self.mapcache or self.mapcache[0] != node:
119 113 self.read(node)
120 114 return self.mapcache[2]
121 115
122 116 def diff(self, a, b):
123 117 # this is sneaky, as we're not actually using a and b
124 118 if self.listcache and self.addlist and self.listcache[0] == a:
125 119 d = mdiff.diff(self.listcache[1], self.addlist, 1)
126 120 if mdiff.patch(a, d) != b:
127 121 sys.stderr.write("*** sortdiff failed, falling back ***\n")
128 122 return mdiff.textdiff(a, b)
129 123 return d
130 124 else:
131 125 return mdiff.textdiff(a, b)
132 126
133 127 def add(self, map, flags, transaction, link, p1=None, p2=None):
134 128 files = map.keys()
135 129 files.sort()
136 130
137 131 self.addlist = ["%s\000%s%s\n" %
138 132 (f, hex(map[f]), flags[f] and "x" or '')
139 133 for f in files]
140 134 text = "".join(self.addlist)
141 135
142 136 n = self.addrevision(text, transaction, link, p1, p2)
143 137 self.mapcache = (n, map, flags)
144 138 self.listcache = (text, self.addlist)
145 139 self.addlist = None
146 140
147 141 return n
148 142
149 143 class changelog(revlog):
150 144 def __init__(self, opener):
151 145 revlog.__init__(self, opener, "00changelog.i", "00changelog.d")
152 146
153 147 def extract(self, text):
154 148 if not text:
155 149 return (nullid, "", "0", [], "")
156 150 last = text.index("\n\n")
157 151 desc = text[last + 2:]
158 152 l = text[:last].splitlines()
159 153 manifest = bin(l[0])
160 154 user = l[1]
161 155 date = l[2]
162 156 files = l[3:]
163 157 return (manifest, user, date, files, desc)
164 158
165 159 def read(self, node):
166 160 return self.extract(self.revision(node))
167 161
168 162 def add(self, manifest, list, desc, transaction, p1=None, p2=None,
169 163 user=None, date=None):
170 164 user = (user or
171 165 os.environ.get("HGUSER") or
172 166 os.environ.get("EMAIL") or
173 167 os.environ.get("LOGNAME", "unknown") + '@' + socket.getfqdn())
174 168 date = date or "%d %d" % (time.time(), time.timezone)
175 169 list.sort()
176 170 l = [hex(manifest), user, date] + list + ["", desc]
177 171 text = "\n".join(l)
178 172 return self.addrevision(text, transaction, self.count(), p1, p2)
179 173
180 174 class dirstate:
181 175 def __init__(self, opener, ui, root):
182 176 self.opener = opener
183 177 self.root = root
184 178 self.dirty = 0
185 179 self.ui = ui
186 180 self.map = None
187 181 self.pl = None
188 182 self.copies = {}
189 183
190 184 def __del__(self):
191 185 if self.dirty:
192 186 self.write()
193 187
194 188 def __getitem__(self, key):
195 189 try:
196 190 return self.map[key]
197 191 except TypeError:
198 192 self.read()
199 193 return self[key]
200 194
201 195 def __contains__(self, key):
202 196 if not self.map: self.read()
203 197 return key in self.map
204 198
205 199 def parents(self):
206 200 if not self.pl:
207 201 self.read()
208 202 return self.pl
209 203
210 204 def setparents(self, p1, p2 = nullid):
211 205 self.dirty = 1
212 206 self.pl = p1, p2
213 207
214 208 def state(self, key):
215 209 try:
216 210 return self[key][0]
217 211 except KeyError:
218 212 return "?"
219 213
220 214 def read(self):
221 215 if self.map is not None: return self.map
222 216
223 217 self.map = {}
224 218 self.pl = [nullid, nullid]
225 219 try:
226 220 st = self.opener("dirstate").read()
227 221 if not st: return
228 222 except: return
229 223
230 224 self.pl = [st[:20], st[20: 40]]
231 225
232 226 pos = 40
233 227 while pos < len(st):
234 228 e = struct.unpack(">cllll", st[pos:pos+17])
235 229 l = e[4]
236 230 pos += 17
237 231 f = st[pos:pos + l]
238 232 if '\0' in f:
239 233 f, c = f.split('\0')
240 234 self.copies[f] = c
241 235 self.map[f] = e[:4]
242 236 pos += l
243 237
244 238 def copy(self, source, dest):
245 239 self.read()
246 240 self.dirty = 1
247 241 self.copies[dest] = source
248 242
249 243 def copied(self, file):
250 244 return self.copies.get(file, None)
251 245
252 246 def update(self, files, state):
253 247 ''' current states:
254 248 n normal
255 249 m needs merging
256 250 r marked for removal
257 251 a marked for addition'''
258 252
259 253 if not files: return
260 254 self.read()
261 255 self.dirty = 1
262 256 for f in files:
263 257 if state == "r":
264 258 self.map[f] = ('r', 0, 0, 0)
265 259 else:
266 260 s = os.stat(os.path.join(self.root, f))
267 261 self.map[f] = (state, s.st_mode, s.st_size, s.st_mtime)
268 262
269 263 def forget(self, files):
270 264 if not files: return
271 265 self.read()
272 266 self.dirty = 1
273 267 for f in files:
274 268 try:
275 269 del self.map[f]
276 270 except KeyError:
277 271 self.ui.warn("not in dirstate: %s!\n" % f)
278 272 pass
279 273
280 274 def clear(self):
281 275 self.map = {}
282 276 self.dirty = 1
283 277
284 278 def write(self):
285 279 st = self.opener("dirstate", "w")
286 280 st.write("".join(self.pl))
287 281 for f, e in self.map.items():
288 282 c = self.copied(f)
289 283 if c:
290 284 f = f + "\0" + c
291 285 e = struct.pack(">cllll", e[0], e[1], e[2], e[3], len(f))
292 286 st.write(e + f)
293 287 self.dirty = 0
294 288
295 289 def dup(self):
296 290 self.read()
297 291 return self.map.copy()
298 292
299 293 # used to avoid circular references so destructors work
300 294 def opener(base):
301 295 p = base
302 296 def o(path, mode="r"):
303 297 if p[:7] == "http://":
304 298 f = os.path.join(p, urllib.quote(path))
305 299 return httprangereader.httprangereader(f)
306 300
307 301 f = os.path.join(p, path)
308 302
309 303 mode += "b" # for that other OS
310 304
311 305 if mode[0] != "r":
312 306 try:
313 307 s = os.stat(f)
314 308 except OSError:
315 309 d = os.path.dirname(f)
316 310 if not os.path.isdir(d):
317 311 os.makedirs(d)
318 312 else:
319 313 if s.st_nlink > 1:
320 314 file(f + ".tmp", "wb").write(file(f, "rb").read())
321 315 util.rename(f+".tmp", f)
322 316
323 317 return file(f, mode)
324 318
325 319 return o
326 320
327 321 class localrepository:
328 322 def __init__(self, ui, path=None, create=0):
329 323 self.remote = 0
330 324 if path and path[:7] == "http://":
331 325 self.remote = 1
332 326 self.path = path
333 327 else:
334 328 if not path:
335 329 p = os.getcwd()
336 330 while not os.path.isdir(os.path.join(p, ".hg")):
337 331 oldp = p
338 332 p = os.path.dirname(p)
339 333 if p == oldp: raise "No repo found"
340 334 path = p
341 335 self.path = os.path.join(path, ".hg")
342 336
343 337 if not create and not os.path.isdir(self.path):
344 338 raise "repository %s not found" % self.path
345 339
346 340 self.root = path
347 341 self.ui = ui
348 342
349 343 if create:
350 344 os.mkdir(self.path)
351 345 os.mkdir(self.join("data"))
352 346
353 347 self.opener = opener(self.path)
354 348 self.wopener = opener(self.root)
355 349 self.manifest = manifest(self.opener)
356 350 self.changelog = changelog(self.opener)
357 351 self.ignorelist = None
358 352 self.tagscache = None
359 353 self.nodetagscache = None
360 354
361 355 if not self.remote:
362 356 self.dirstate = dirstate(self.opener, ui, self.root)
363 357 try:
364 358 self.ui.readconfig(self.opener("hgrc"))
365 359 except IOError: pass
366 360
367 361 def ignore(self, f):
368 362 if self.ignorelist is None:
369 363 self.ignorelist = []
370 364 try:
371 365 l = file(self.wjoin(".hgignore"))
372 366 for pat in l:
373 367 if pat != "\n":
374 368 self.ignorelist.append(re.compile(util.pconvert(pat[:-1])))
375 369 except IOError: pass
376 370 for pat in self.ignorelist:
377 371 if pat.search(f): return True
378 372 return False
379 373
380 374 def tags(self):
381 375 '''return a mapping of tag to node'''
382 376 if not self.tagscache:
383 377 self.tagscache = {}
384 378 try:
385 379 # read each head of the tags file, ending with the tip
386 380 # and add each tag found to the map, with "newer" ones
387 381 # taking precedence
388 382 fl = self.file(".hgtags")
389 383 h = fl.heads()
390 384 h.reverse()
391 385 for r in h:
392 386 for l in fl.revision(r).splitlines():
393 387 if l:
394 388 n, k = l.split(" ", 1)
395 389 self.tagscache[k.strip()] = bin(n)
396 390 except KeyError: pass
397 391 for k, n in self.ui.configitems("tags"):
398 392 self.tagscache[k] = bin(n)
399 393
400 394 self.tagscache['tip'] = self.changelog.tip()
401 395
402 396 return self.tagscache
403 397
404 398 def tagslist(self):
405 399 '''return a list of tags ordered by revision'''
406 400 l = []
407 401 for t,n in self.tags().items():
408 402 try:
409 403 r = self.changelog.rev(n)
410 404 except:
411 405 r = -2 # sort to the beginning of the list if unknown
412 406 l.append((r,t,n))
413 407 l.sort()
414 408 return [(t,n) for r,t,n in l]
415 409
416 410 def nodetags(self, node):
417 411 '''return the tags associated with a node'''
418 412 if not self.nodetagscache:
419 413 self.nodetagscache = {}
420 414 for t,n in self.tags().items():
421 415 self.nodetagscache.setdefault(n,[]).append(t)
422 416 return self.nodetagscache.get(node, [])
423 417
424 418 def lookup(self, key):
425 419 try:
426 420 return self.tags()[key]
427 421 except KeyError:
428 422 return self.changelog.lookup(key)
429 423
430 424 def join(self, f):
431 425 return os.path.join(self.path, f)
432 426
433 427 def wjoin(self, f):
434 428 return os.path.join(self.root, f)
435 429
436 430 def file(self, f):
437 431 if f[0] == '/': f = f[1:]
438 432 return filelog(self.opener, f)
439 433
440 434 def wfile(self, f, mode='r'):
441 435 return self.wopener(f, mode)
442 436
443 437 def transaction(self):
444 438 # save dirstate for undo
445 439 try:
446 440 ds = self.opener("dirstate").read()
447 441 except IOError:
448 442 ds = ""
449 443 self.opener("undo.dirstate", "w").write(ds)
450 444
451 445 return transaction.transaction(self.opener, self.join("journal"),
452 446 self.join("undo"))
453 447
454 448 def recover(self):
455 449 lock = self.lock()
456 450 if os.path.exists(self.join("recover")):
457 451 self.ui.status("attempting to rollback interrupted transaction\n")
458 452 return transaction.rollback(self.opener, self.join("recover"))
459 453 else:
460 454 self.ui.warn("no interrupted transaction available\n")
461 455
462 456 def undo(self):
463 457 lock = self.lock()
464 458 if os.path.exists(self.join("undo")):
465 459 self.ui.status("attempting to rollback last transaction\n")
466 460 transaction.rollback(self.opener, self.join("undo"))
467 461 self.dirstate = None
468 462 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
469 463 self.dirstate = dirstate(self.opener, self.ui, self.root)
470 464 else:
471 465 self.ui.warn("no undo information available\n")
472 466
473 467 def lock(self, wait = 1):
474 468 try:
475 469 return lock.lock(self.join("lock"), 0)
476 470 except lock.LockHeld, inst:
477 471 if wait:
478 472 self.ui.warn("waiting for lock held by %s\n" % inst.args[0])
479 473 return lock.lock(self.join("lock"), wait)
480 474 raise inst
481 475
482 476 def rawcommit(self, files, text, user, date, p1=None, p2=None):
483 477 orig_parent = self.dirstate.parents()[0] or nullid
484 478 p1 = p1 or self.dirstate.parents()[0] or nullid
485 479 p2 = p2 or self.dirstate.parents()[1] or nullid
486 480 c1 = self.changelog.read(p1)
487 481 c2 = self.changelog.read(p2)
488 482 m1 = self.manifest.read(c1[0])
489 483 mf1 = self.manifest.readflags(c1[0])
490 484 m2 = self.manifest.read(c2[0])
491 485
492 486 if orig_parent == p1:
493 487 update_dirstate = 1
494 488 else:
495 489 update_dirstate = 0
496 490
497 491 tr = self.transaction()
498 492 mm = m1.copy()
499 493 mfm = mf1.copy()
500 494 linkrev = self.changelog.count()
501 495 for f in files:
502 496 try:
503 497 t = self.wfile(f).read()
504 498 tm = util.is_exec(self.wjoin(f), mfm.get(f, False))
505 499 r = self.file(f)
506 500 mfm[f] = tm
507 501 mm[f] = r.add(t, {}, tr, linkrev,
508 502 m1.get(f, nullid), m2.get(f, nullid))
509 503 if update_dirstate:
510 504 self.dirstate.update([f], "n")
511 505 except IOError:
512 506 try:
513 507 del mm[f]
514 508 del mfm[f]
515 509 if update_dirstate:
516 510 self.dirstate.forget([f])
517 511 except:
518 512 # deleted from p2?
519 513 pass
520 514
521 515 mnode = self.manifest.add(mm, mfm, tr, linkrev, c1[0], c2[0])
522 516 n = self.changelog.add(mnode, files, text, tr, p1, p2, user, date)
523 517 tr.close()
524 518 if update_dirstate:
525 519 self.dirstate.setparents(n, nullid)
526 520
527 521 def commit(self, files = None, text = "", user = None, date = None):
528 522 commit = []
529 523 remove = []
530 524 if files:
531 525 for f in files:
532 526 s = self.dirstate.state(f)
533 527 if s in 'nmai':
534 528 commit.append(f)
535 529 elif s == 'r':
536 530 remove.append(f)
537 531 else:
538 532 self.ui.warn("%s not tracked!\n" % f)
539 533 else:
540 534 (c, a, d, u) = self.diffdir(self.root)
541 535 commit = c + a
542 536 remove = d
543 537
544 538 if not commit and not remove:
545 539 self.ui.status("nothing changed\n")
546 540 return
547 541
548 542 p1, p2 = self.dirstate.parents()
549 543 c1 = self.changelog.read(p1)
550 544 c2 = self.changelog.read(p2)
551 545 m1 = self.manifest.read(c1[0])
552 546 mf1 = self.manifest.readflags(c1[0])
553 547 m2 = self.manifest.read(c2[0])
554 548 lock = self.lock()
555 549 tr = self.transaction()
556 550
557 551 # check in files
558 552 new = {}
559 553 linkrev = self.changelog.count()
560 554 commit.sort()
561 555 for f in commit:
562 556 self.ui.note(f + "\n")
563 557 try:
564 558 mf1[f] = util.is_exec(self.wjoin(f), mf1.get(f, False))
565 559 t = self.wfile(f).read()
566 560 except IOError:
567 561 self.warn("trouble committing %s!\n" % f)
568 562 raise
569 563
570 564 meta = {}
571 565 cp = self.dirstate.copied(f)
572 566 if cp:
573 567 meta["copy"] = cp
574 568 meta["copyrev"] = hex(m1.get(cp, m2.get(cp, nullid)))
575 569 self.ui.debug(" %s: copy %s:%s\n" % (f, cp, meta["copyrev"]))
576 570
577 571 r = self.file(f)
578 572 fp1 = m1.get(f, nullid)
579 573 fp2 = m2.get(f, nullid)
580 574 new[f] = r.add(t, meta, tr, linkrev, fp1, fp2)
581 575
582 576 # update manifest
583 577 m1.update(new)
584 578 for f in remove:
585 579 if f in m1:
586 580 del m1[f]
587 581 mn = self.manifest.add(m1, mf1, tr, linkrev, c1[0], c2[0])
588 582
589 583 # add changeset
590 584 new = new.keys()
591 585 new.sort()
592 586
593 587 if not text:
594 588 edittext = "\n" + "HG: manifest hash %s\n" % hex(mn)
595 589 edittext += "".join(["HG: changed %s\n" % f for f in new])
596 590 edittext += "".join(["HG: removed %s\n" % f for f in remove])
597 591 edittext = self.ui.edit(edittext)
598 592 if not edittext.rstrip():
599 593 return 1
600 594 text = edittext
601 595
602 596 n = self.changelog.add(mn, new, text, tr, p1, p2, user, date)
603 597 tr.close()
604 598
605 599 self.dirstate.setparents(n)
606 600 self.dirstate.update(new, "n")
607 601 self.dirstate.forget(remove)
608 602
609 603 def diffdir(self, path, changeset = None):
610 604 changed = []
611 605 added = []
612 606 unknown = []
613 607 mf = {}
614 608
615 609 if changeset:
616 610 change = self.changelog.read(changeset)
617 611 mf = self.manifest.read(change[0])
618 612 dc = dict.fromkeys(mf)
619 613 else:
620 614 changeset = self.dirstate.parents()[0]
621 615 change = self.changelog.read(changeset)
622 616 mf = self.manifest.read(change[0])
623 617 dc = self.dirstate.dup()
624 618
625 619 def fcmp(fn):
626 620 t1 = self.wfile(fn).read()
627 621 t2 = self.file(fn).revision(mf[fn])
628 622 return cmp(t1, t2)
629 623
630 624 for dir, subdirs, files in os.walk(path):
631 625 d = dir[len(self.root)+1:]
632 626 if ".hg" in subdirs: subdirs.remove(".hg")
633 627
634 628 for f in files:
635 629 fn = util.pconvert(os.path.join(d, f))
636 630 try: s = os.stat(os.path.join(self.root, fn))
637 631 except: continue
638 632 if fn in dc:
639 633 c = dc[fn]
640 634 del dc[fn]
641 635 if not c:
642 636 if fcmp(fn):
643 637 changed.append(fn)
644 638 elif c[0] == 'm':
645 639 changed.append(fn)
646 640 elif c[0] == 'a':
647 641 added.append(fn)
648 642 elif c[0] == 'r':
649 643 unknown.append(fn)
650 644 elif c[2] != s.st_size or (c[1] ^ s.st_mode) & 0100:
651 645 changed.append(fn)
652 646 elif c[1] != s.st_mode or c[3] != s.st_mtime:
653 647 if fcmp(fn):
654 648 changed.append(fn)
655 649 else:
656 650 if self.ignore(fn): continue
657 651 unknown.append(fn)
658 652
659 653 deleted = dc.keys()
660 654 deleted.sort()
661 655
662 656 return (changed, added, deleted, unknown)
663 657
664 658 def diffrevs(self, node1, node2):
665 659 changed, added = [], []
666 660
667 661 change = self.changelog.read(node1)
668 662 mf1 = self.manifest.read(change[0])
669 663 change = self.changelog.read(node2)
670 664 mf2 = self.manifest.read(change[0])
671 665
672 666 for fn in mf2:
673 667 if mf1.has_key(fn):
674 668 if mf1[fn] != mf2[fn]:
675 669 changed.append(fn)
676 670 del mf1[fn]
677 671 else:
678 672 added.append(fn)
679 673
680 674 deleted = mf1.keys()
681 675 deleted.sort()
682 676
683 677 return (changed, added, deleted)
684 678
685 679 def add(self, list):
686 680 for f in list:
687 681 p = self.wjoin(f)
688 682 if not os.path.isfile(p):
689 683 self.ui.warn("%s does not exist!\n" % f)
690 684 elif self.dirstate.state(f) == 'n':
691 685 self.ui.warn("%s already tracked!\n" % f)
692 686 else:
693 687 self.dirstate.update([f], "a")
694 688
695 689 def forget(self, list):
696 690 for f in list:
697 691 if self.dirstate.state(f) not in 'ai':
698 692 self.ui.warn("%s not added!\n" % f)
699 693 else:
700 694 self.dirstate.forget([f])
701 695
702 696 def remove(self, list):
703 697 for f in list:
704 698 p = self.wjoin(f)
705 699 if os.path.isfile(p):
706 700 self.ui.warn("%s still exists!\n" % f)
707 701 elif self.dirstate.state(f) == 'a':
708 702 self.ui.warn("%s never committed!\n" % f)
709 703 self.dirstate.forget(f)
710 704 elif f not in self.dirstate:
711 705 self.ui.warn("%s not tracked!\n" % f)
712 706 else:
713 707 self.dirstate.update([f], "r")
714 708
715 709 def copy(self, source, dest):
716 710 p = self.wjoin(dest)
717 711 if not os.path.isfile(dest):
718 712 self.ui.warn("%s does not exist!\n" % dest)
719 713 else:
720 714 if self.dirstate.state(dest) == '?':
721 715 self.dirstate.update([dest], "a")
722 716 self.dirstate.copy(source, dest)
723 717
724 718 def heads(self):
725 719 return self.changelog.heads()
726 720
727 721 def branches(self, nodes):
728 722 if not nodes: nodes = [self.changelog.tip()]
729 723 b = []
730 724 for n in nodes:
731 725 t = n
732 726 while n:
733 727 p = self.changelog.parents(n)
734 728 if p[1] != nullid or p[0] == nullid:
735 729 b.append((t, n, p[0], p[1]))
736 730 break
737 731 n = p[0]
738 732 return b
739 733
740 734 def between(self, pairs):
741 735 r = []
742 736
743 737 for top, bottom in pairs:
744 738 n, l, i = top, [], 0
745 739 f = 1
746 740
747 741 while n != bottom:
748 742 p = self.changelog.parents(n)[0]
749 743 if i == f:
750 744 l.append(n)
751 745 f = f * 2
752 746 n = p
753 747 i += 1
754 748
755 749 r.append(l)
756 750
757 751 return r
758 752
759 753 def newer(self, nodes):
760 754 m = {}
761 755 nl = []
762 756 pm = {}
763 757 cl = self.changelog
764 758 t = l = cl.count()
765 759
766 760 # find the lowest numbered node
767 761 for n in nodes:
768 762 l = min(l, cl.rev(n))
769 763 m[n] = 1
770 764
771 765 for i in xrange(l, t):
772 766 n = cl.node(i)
773 767 if n in m: # explicitly listed
774 768 pm[n] = 1
775 769 nl.append(n)
776 770 continue
777 771 for p in cl.parents(n):
778 772 if p in pm: # parent listed
779 773 pm[n] = 1
780 774 nl.append(n)
781 775 break
782 776
783 777 return nl
784 778
785 779 def getchangegroup(self, remote):
786 780 m = self.changelog.nodemap
787 781 search = []
788 782 fetch = []
789 783 seen = {}
790 784 seenbranch = {}
791 785
792 786 # if we have an empty repo, fetch everything
793 787 if self.changelog.tip() == nullid:
794 788 self.ui.status("requesting all changes\n")
795 789 return remote.changegroup([nullid])
796 790
797 791 # otherwise, assume we're closer to the tip than the root
798 792 self.ui.status("searching for changes\n")
799 793 heads = remote.heads()
800 794 unknown = []
801 795 for h in heads:
802 796 if h not in m:
803 797 unknown.append(h)
804 798
805 799 if not unknown:
806 800 self.ui.status("nothing to do!\n")
807 801 return None
808 802
809 803 rep = {}
810 804 reqcnt = 0
811 805
812 806 unknown = remote.branches(unknown)
813 807 while unknown:
814 808 r = []
815 809 while unknown:
816 810 n = unknown.pop(0)
817 811 if n[0] in seen:
818 812 continue
819 813
820 814 self.ui.debug("examining %s:%s\n" % (short(n[0]), short(n[1])))
821 815 if n[0] == nullid:
822 816 break
823 817 if n in seenbranch:
824 818 self.ui.debug("branch already found\n")
825 819 continue
826 820 if n[1] and n[1] in m: # do we know the base?
827 821 self.ui.debug("found incomplete branch %s:%s\n"
828 822 % (short(n[0]), short(n[1])))
829 823 search.append(n) # schedule branch range for scanning
830 824 seenbranch[n] = 1
831 825 else:
832 826 if n[1] not in seen and n[1] not in fetch:
833 827 if n[2] in m and n[3] in m:
834 828 self.ui.debug("found new changeset %s\n" %
835 829 short(n[1]))
836 830 fetch.append(n[1]) # earliest unknown
837 831 continue
838 832
839 833 for a in n[2:4]:
840 834 if a not in rep:
841 835 r.append(a)
842 836 rep[a] = 1
843 837
844 838 seen[n[0]] = 1
845 839
846 840 if r:
847 841 reqcnt += 1
848 842 self.ui.debug("request %d: %s\n" %
849 843 (reqcnt, " ".join(map(short, r))))
850 844 for p in range(0, len(r), 10):
851 845 for b in remote.branches(r[p:p+10]):
852 846 self.ui.debug("received %s:%s\n" %
853 847 (short(b[0]), short(b[1])))
854 848 if b[0] not in m and b[0] not in seen:
855 849 unknown.append(b)
856 850
857 851 while search:
858 852 n = search.pop(0)
859 853 reqcnt += 1
860 854 l = remote.between([(n[0], n[1])])[0]
861 855 l.append(n[1])
862 856 p = n[0]
863 857 f = 1
864 858 for i in l:
865 859 self.ui.debug("narrowing %d:%d %s\n" % (f, len(l), short(i)))
866 860 if i in m:
867 861 if f <= 2:
868 862 self.ui.debug("found new branch changeset %s\n" %
869 863 short(p))
870 864 fetch.append(p)
871 865 else:
872 866 self.ui.debug("narrowed branch search to %s:%s\n"
873 867 % (short(p), short(i)))
874 868 search.append((p, i))
875 869 break
876 870 p, f = i, f * 2
877 871
878 872 for f in fetch:
879 873 if f in m:
880 874 raise "already have", short(f[:4])
881 875
882 876 self.ui.note("adding new changesets starting at " +
883 877 " ".join([short(f) for f in fetch]) + "\n")
884 878
885 879 self.ui.debug("%d total queries\n" % reqcnt)
886 880
887 881 return remote.changegroup(fetch)
888 882
889 883 def changegroup(self, basenodes):
890 884 nodes = self.newer(basenodes)
891 885
892 886 # construct the link map
893 887 linkmap = {}
894 888 for n in nodes:
895 889 linkmap[self.changelog.rev(n)] = n
896 890
897 891 # construct a list of all changed files
898 892 changed = {}
899 893 for n in nodes:
900 894 c = self.changelog.read(n)
901 895 for f in c[3]:
902 896 changed[f] = 1
903 897 changed = changed.keys()
904 898 changed.sort()
905 899
906 900 # the changegroup is changesets + manifests + all file revs
907 901 revs = [ self.changelog.rev(n) for n in nodes ]
908 902
909 903 for y in self.changelog.group(linkmap): yield y
910 904 for y in self.manifest.group(linkmap): yield y
911 905 for f in changed:
912 906 yield struct.pack(">l", len(f) + 4) + f
913 907 g = self.file(f).group(linkmap)
914 908 for y in g:
915 909 yield y
916 910
917 911 def addchangegroup(self, generator):
918 912
919 913 class genread:
920 914 def __init__(self, generator):
921 915 self.g = generator
922 916 self.buf = ""
923 917 def read(self, l):
924 918 while l > len(self.buf):
925 919 try:
926 920 self.buf += self.g.next()
927 921 except StopIteration:
928 922 break
929 923 d, self.buf = self.buf[:l], self.buf[l:]
930 924 return d
931 925
932 926 def getchunk():
933 927 d = source.read(4)
934 928 if not d: return ""
935 929 l = struct.unpack(">l", d)[0]
936 930 if l <= 4: return ""
937 931 return source.read(l - 4)
938 932
939 933 def getgroup():
940 934 while 1:
941 935 c = getchunk()
942 936 if not c: break
943 937 yield c
944 938
945 939 def csmap(x):
946 940 self.ui.debug("add changeset %s\n" % short(x))
947 941 return self.changelog.count()
948 942
949 943 def revmap(x):
950 944 return self.changelog.rev(x)
951 945
952 946 if not generator: return
953 947 changesets = files = revisions = 0
954 948
955 949 source = genread(generator)
956 950 lock = self.lock()
957 951 tr = self.transaction()
958 952
959 953 # pull off the changeset group
960 954 self.ui.status("adding changesets\n")
961 955 co = self.changelog.tip()
962 956 cn = self.changelog.addgroup(getgroup(), csmap, tr, 1) # unique
963 957 changesets = self.changelog.rev(cn) - self.changelog.rev(co)
964 958
965 959 # pull off the manifest group
966 960 self.ui.status("adding manifests\n")
967 961 mm = self.manifest.tip()
968 962 mo = self.manifest.addgroup(getgroup(), revmap, tr)
969 963
970 964 # process the files
971 965 self.ui.status("adding file revisions\n")
972 966 while 1:
973 967 f = getchunk()
974 968 if not f: break
975 969 self.ui.debug("adding %s revisions\n" % f)
976 970 fl = self.file(f)
977 971 o = fl.tip()
978 972 n = fl.addgroup(getgroup(), revmap, tr)
979 973 revisions += fl.rev(n) - fl.rev(o)
980 974 files += 1
981 975
982 976 self.ui.status(("modified %d files, added %d changesets" +
983 977 " and %d new revisions\n")
984 978 % (files, changesets, revisions))
985 979
986 980 tr.close()
987 981 return
988 982
989 983 def update(self, node, allow=False, force=False):
990 984 pl = self.dirstate.parents()
991 985 if not force and pl[1] != nullid:
992 986 self.ui.warn("aborting: outstanding uncommitted merges\n")
993 987 return
994 988
995 989 p1, p2 = pl[0], node
996 990 pa = self.changelog.ancestor(p1, p2)
997 991 m1n = self.changelog.read(p1)[0]
998 992 m2n = self.changelog.read(p2)[0]
999 993 man = self.manifest.ancestor(m1n, m2n)
1000 994 m1 = self.manifest.read(m1n)
1001 995 mf1 = self.manifest.readflags(m1n)
1002 996 m2 = self.manifest.read(m2n)
1003 997 mf2 = self.manifest.readflags(m2n)
1004 998 ma = self.manifest.read(man)
1005 999 mfa = self.manifest.readflags(man)
1006 1000
1007 1001 (c, a, d, u) = self.diffdir(self.root)
1008 1002
1009 1003 # is this a jump, or a merge? i.e. is there a linear path
1010 1004 # from p1 to p2?
1011 1005 linear_path = (pa == p1 or pa == p2)
1012 1006
1013 1007 # resolve the manifest to determine which files
1014 1008 # we care about merging
1015 1009 self.ui.note("resolving manifests\n")
1016 1010 self.ui.debug(" ancestor %s local %s remote %s\n" %
1017 1011 (short(man), short(m1n), short(m2n)))
1018 1012
1019 1013 merge = {}
1020 1014 get = {}
1021 1015 remove = []
1022 1016 mark = {}
1023 1017
1024 1018 # construct a working dir manifest
1025 1019 mw = m1.copy()
1026 1020 mfw = mf1.copy()
1027 1021 for f in a + c + u:
1028 1022 mw[f] = ""
1029 1023 mfw[f] = util.is_exec(self.wjoin(f), mfw.get(f, False))
1030 1024 for f in d:
1031 1025 if f in mw: del mw[f]
1032 1026
1033 1027 # If we're jumping between revisions (as opposed to merging),
1034 1028 # and if neither the working directory nor the target rev has
1035 1029 # the file, then we need to remove it from the dirstate, to
1036 1030 # prevent the dirstate from listing the file when it is no
1037 1031 # longer in the manifest.
1038 1032 if linear_path and f not in m2:
1039 1033 self.dirstate.forget((f,))
1040 1034
1041 1035 for f, n in mw.iteritems():
1042 1036 if f in m2:
1043 1037 s = 0
1044 1038
1045 1039 # is the wfile new since m1, and match m2?
1046 1040 if f not in m1:
1047 1041 t1 = self.wfile(f).read()
1048 1042 t2 = self.file(f).revision(m2[f])
1049 1043 if cmp(t1, t2) == 0:
1050 1044 mark[f] = 1
1051 1045 n = m2[f]
1052 1046 del t1, t2
1053 1047
1054 1048 # are files different?
1055 1049 if n != m2[f]:
1056 1050 a = ma.get(f, nullid)
1057 1051 # are both different from the ancestor?
1058 1052 if n != a and m2[f] != a:
1059 1053 self.ui.debug(" %s versions differ, resolve\n" % f)
1060 1054 # merge executable bits
1061 1055 # "if we changed or they changed, change in merge"
1062 1056 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1063 1057 mode = ((a^b) | (a^c)) ^ a
1064 1058 merge[f] = (m1.get(f, nullid), m2[f], mode)
1065 1059 s = 1
1066 1060 # are we clobbering?
1067 1061 # is remote's version newer?
1068 1062 # or are we going back in time?
1069 1063 elif force or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
1070 1064 self.ui.debug(" remote %s is newer, get\n" % f)
1071 1065 get[f] = m2[f]
1072 1066 s = 1
1073 1067 else:
1074 1068 mark[f] = 1
1075 1069
1076 1070 if not s and mfw[f] != mf2[f]:
1077 1071 if force:
1078 1072 self.ui.debug(" updating permissions for %s\n" % f)
1079 1073 util.set_exec(self.wjoin(f), mf2[f])
1080 1074 else:
1081 1075 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1082 1076 mode = ((a^b) | (a^c)) ^ a
1083 1077 if mode != b:
1084 1078 self.ui.debug(" updating permissions for %s\n" % f)
1085 1079 util.set_exec(self.wjoin(f), mode)
1086 1080 mark[f] = 1
1087 1081 del m2[f]
1088 1082 elif f in ma:
1089 1083 if not force and n != ma[f]:
1090 1084 r = ""
1091 1085 if linear_path or allow:
1092 1086 r = self.ui.prompt(
1093 1087 (" local changed %s which remote deleted\n" % f) +
1094 1088 "(k)eep or (d)elete?", "[kd]", "k")
1095 1089 if r == "d":
1096 1090 remove.append(f)
1097 1091 else:
1098 1092 self.ui.debug("other deleted %s\n" % f)
1099 1093 remove.append(f) # other deleted it
1100 1094 else:
1101 1095 if n == m1.get(f, nullid): # same as parent
1102 1096 if p2 == pa: # going backwards?
1103 1097 self.ui.debug("remote deleted %s\n" % f)
1104 1098 remove.append(f)
1105 1099 else:
1106 1100 self.ui.debug("local created %s, keeping\n" % f)
1107 1101 else:
1108 1102 self.ui.debug("working dir created %s, keeping\n" % f)
1109 1103
1110 1104 for f, n in m2.iteritems():
1111 1105 if f[0] == "/": continue
1112 1106 if not force and f in ma and n != ma[f]:
1113 1107 r = ""
1114 1108 if linear_path or allow:
1115 1109 r = self.ui.prompt(
1116 1110 ("remote changed %s which local deleted\n" % f) +
1117 1111 "(k)eep or (d)elete?", "[kd]", "k")
1118 1112 if r == "d": remove.append(f)
1119 1113 else:
1120 1114 self.ui.debug("remote created %s\n" % f)
1121 1115 get[f] = n
1122 1116
1123 1117 del mw, m1, m2, ma
1124 1118
1125 1119 if force:
1126 1120 for f in merge:
1127 1121 get[f] = merge[f][1]
1128 1122 merge = {}
1129 1123
1130 1124 if linear_path:
1131 1125 # we don't need to do any magic, just jump to the new rev
1132 1126 mode = 'n'
1133 1127 p1, p2 = p2, nullid
1134 1128 else:
1135 1129 if not allow:
1136 1130 self.ui.status("this update spans a branch" +
1137 1131 " affecting the following files:\n")
1138 1132 fl = merge.keys() + get.keys()
1139 1133 fl.sort()
1140 1134 for f in fl:
1141 1135 cf = ""
1142 1136 if f in merge: cf = " (resolve)"
1143 1137 self.ui.status(" %s%s\n" % (f, cf))
1144 1138 self.ui.warn("aborting update spanning branches!\n")
1145 1139 self.ui.status("(use update -m to perform a branch merge)\n")
1146 1140 return 1
1147 1141 # we have to remember what files we needed to get/change
1148 1142 # because any file that's different from either one of its
1149 1143 # parents must be in the changeset
1150 1144 mode = 'm'
1151 1145 self.dirstate.update(mark.keys(), "m")
1152 1146
1153 1147 self.dirstate.setparents(p1, p2)
1154 1148
1155 1149 # get the files we don't need to change
1156 1150 files = get.keys()
1157 1151 files.sort()
1158 1152 for f in files:
1159 1153 if f[0] == "/": continue
1160 1154 self.ui.note("getting %s\n" % f)
1161 1155 t = self.file(f).read(get[f])
1162 1156 try:
1163 1157 self.wfile(f, "w").write(t)
1164 1158 except IOError:
1165 1159 os.makedirs(os.path.dirname(self.wjoin(f)))
1166 1160 self.wfile(f, "w").write(t)
1167 1161 util.set_exec(self.wjoin(f), mf2[f])
1168 1162 self.dirstate.update([f], mode)
1169 1163
1170 1164 # merge the tricky bits
1171 1165 files = merge.keys()
1172 1166 files.sort()
1173 1167 for f in files:
1174 1168 self.ui.status("merging %s\n" % f)
1175 1169 m, o, flag = merge[f]
1176 1170 self.merge3(f, m, o)
1177 1171 util.set_exec(self.wjoin(f), flag)
1178 1172 self.dirstate.update([f], 'm')
1179 1173
1180 1174 for f in remove:
1181 1175 self.ui.note("removing %s\n" % f)
1182 1176 os.unlink(f)
1183 1177 if mode == 'n':
1184 1178 self.dirstate.forget(remove)
1185 1179 else:
1186 1180 self.dirstate.update(remove, 'r')
1187 1181
1188 1182 def merge3(self, fn, my, other):
1189 1183 """perform a 3-way merge in the working directory"""
1190 1184
1191 1185 def temp(prefix, node):
1192 1186 pre = "%s~%s." % (os.path.basename(fn), prefix)
1193 1187 (fd, name) = tempfile.mkstemp("", pre)
1194 1188 f = os.fdopen(fd, "wb")
1195 1189 f.write(fl.revision(node))
1196 1190 f.close()
1197 1191 return name
1198 1192
1199 1193 fl = self.file(fn)
1200 1194 base = fl.ancestor(my, other)
1201 1195 a = self.wjoin(fn)
1202 1196 b = temp("base", base)
1203 1197 c = temp("other", other)
1204 1198
1205 1199 self.ui.note("resolving %s\n" % fn)
1206 1200 self.ui.debug("file %s: other %s ancestor %s\n" %
1207 1201 (fn, short(other), short(base)))
1208 1202
1209 1203 cmd = os.environ.get("HGMERGE", "hgmerge")
1210 1204 r = os.system("%s %s %s %s" % (cmd, a, b, c))
1211 1205 if r:
1212 1206 self.ui.warn("merging %s failed!\n" % fn)
1213 1207
1214 1208 os.unlink(b)
1215 1209 os.unlink(c)
1216 1210
1217 1211 def verify(self):
1218 1212 filelinkrevs = {}
1219 1213 filenodes = {}
1220 1214 changesets = revisions = files = 0
1221 1215 errors = 0
1222 1216
1223 1217 seen = {}
1224 1218 self.ui.status("checking changesets\n")
1225 1219 for i in range(self.changelog.count()):
1226 1220 changesets += 1
1227 1221 n = self.changelog.node(i)
1228 1222 if n in seen:
1229 1223 self.ui.warn("duplicate changeset at revision %d\n" % i)
1230 1224 errors += 1
1231 1225 seen[n] = 1
1232 1226
1233 1227 for p in self.changelog.parents(n):
1234 1228 if p not in self.changelog.nodemap:
1235 1229 self.ui.warn("changeset %s has unknown parent %s\n" %
1236 1230 (short(n), short(p)))
1237 1231 errors += 1
1238 1232 try:
1239 1233 changes = self.changelog.read(n)
1240 1234 except Exception, inst:
1241 1235 self.ui.warn("unpacking changeset %s: %s\n" % (short(n), inst))
1242 1236 errors += 1
1243 1237
1244 1238 for f in changes[3]:
1245 1239 filelinkrevs.setdefault(f, []).append(i)
1246 1240
1247 1241 seen = {}
1248 1242 self.ui.status("checking manifests\n")
1249 1243 for i in range(self.manifest.count()):
1250 1244 n = self.manifest.node(i)
1251 1245 if n in seen:
1252 1246 self.ui.warn("duplicate manifest at revision %d\n" % i)
1253 1247 errors += 1
1254 1248 seen[n] = 1
1255 1249
1256 1250 for p in self.manifest.parents(n):
1257 1251 if p not in self.manifest.nodemap:
1258 1252 self.ui.warn("manifest %s has unknown parent %s\n" %
1259 1253 (short(n), short(p)))
1260 1254 errors += 1
1261 1255
1262 1256 try:
1263 1257 delta = mdiff.patchtext(self.manifest.delta(n))
1264 1258 except KeyboardInterrupt:
1265 1259 print "aborted"
1266 1260 sys.exit(0)
1267 1261 except Exception, inst:
1268 1262 self.ui.warn("unpacking manifest %s: %s\n"
1269 1263 % (short(n), inst))
1270 1264 errors += 1
1271 1265
1272 1266 ff = [ l.split('\0') for l in delta.splitlines() ]
1273 1267 for f, fn in ff:
1274 1268 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
1275 1269
1276 1270 self.ui.status("crosschecking files in changesets and manifests\n")
1277 1271 for f in filenodes:
1278 1272 if f not in filelinkrevs:
1279 1273 self.ui.warn("file %s in manifest but not in changesets\n" % f)
1280 1274 errors += 1
1281 1275
1282 1276 for f in filelinkrevs:
1283 1277 if f not in filenodes:
1284 1278 self.ui.warn("file %s in changeset but not in manifest\n" % f)
1285 1279 errors += 1
1286 1280
1287 1281 self.ui.status("checking files\n")
1288 1282 ff = filenodes.keys()
1289 1283 ff.sort()
1290 1284 for f in ff:
1291 1285 if f == "/dev/null": continue
1292 1286 files += 1
1293 1287 fl = self.file(f)
1294 1288 nodes = { nullid: 1 }
1295 1289 seen = {}
1296 1290 for i in range(fl.count()):
1297 1291 revisions += 1
1298 1292 n = fl.node(i)
1299 1293
1300 1294 if n in seen:
1301 1295 self.ui.warn("%s: duplicate revision %d\n" % (f, i))
1302 1296 errors += 1
1303 1297
1304 1298 if n not in filenodes[f]:
1305 1299 self.ui.warn("%s: %d:%s not in manifests\n"
1306 1300 % (f, i, short(n)))
1307 1301 print len(filenodes[f].keys()), fl.count(), f
1308 1302 errors += 1
1309 1303 else:
1310 1304 del filenodes[f][n]
1311 1305
1312 1306 flr = fl.linkrev(n)
1313 1307 if flr not in filelinkrevs[f]:
1314 1308 self.ui.warn("%s:%s points to unexpected changeset %d\n"
1315 1309 % (f, short(n), fl.linkrev(n)))
1316 1310 errors += 1
1317 1311 else:
1318 1312 filelinkrevs[f].remove(flr)
1319 1313
1320 1314 # verify contents
1321 1315 try:
1322 1316 t = fl.read(n)
1323 1317 except Exception, inst:
1324 1318 self.ui.warn("unpacking file %s %s: %s\n"
1325 1319 % (f, short(n), inst))
1326 1320 errors += 1
1327 1321
1328 1322 # verify parents
1329 1323 (p1, p2) = fl.parents(n)
1330 1324 if p1 not in nodes:
1331 1325 self.ui.warn("file %s:%s unknown parent 1 %s" %
1332 1326 (f, short(n), short(p1)))
1333 1327 errors += 1
1334 1328 if p2 not in nodes:
1335 1329 self.ui.warn("file %s:%s unknown parent 2 %s" %
1336 1330 (f, short(n), short(p1)))
1337 1331 errors += 1
1338 1332 nodes[n] = 1
1339 1333
1340 1334 # cross-check
1341 1335 for node in filenodes[f]:
1342 1336 self.ui.warn("node %s in manifests not in %s\n"
1343 1337 % (hex(n), f))
1344 1338 errors += 1
1345 1339
1346 1340 self.ui.status("%d files, %d changesets, %d total revisions\n" %
1347 1341 (files, changesets, revisions))
1348 1342
1349 1343 if errors:
1350 1344 self.ui.warn("%d integrity errors encountered!\n" % errors)
1351 1345 return 1
1352 1346
1353 1347 class remoterepository:
1354 1348 def __init__(self, ui, path):
1355 1349 self.url = path
1356 1350 self.ui = ui
1357 1351 no_list = [ "localhost", "127.0.0.1" ]
1358 1352 host = ui.config("http_proxy", "host")
1359 1353 if host is None:
1360 1354 host = os.environ.get("http_proxy")
1361 1355 if host and host.startswith('http://'):
1362 1356 host = host[7:]
1363 1357 user = ui.config("http_proxy", "user")
1364 1358 passwd = ui.config("http_proxy", "passwd")
1365 1359 no = ui.config("http_proxy", "no")
1366 1360 if no is None:
1367 1361 no = os.environ.get("no_proxy")
1368 1362 if no:
1369 1363 no_list = no_list + no.split(",")
1370 1364
1371 1365 no_proxy = 0
1372 1366 for h in no_list:
1373 1367 if (path.startswith("http://" + h + "/") or
1374 1368 path.startswith("http://" + h + ":") or
1375 1369 path == "http://" + h):
1376 1370 no_proxy = 1
1377 1371
1378 1372 # Note: urllib2 takes proxy values from the environment and those will
1379 1373 # take precedence
1380 1374 for env in ["HTTP_PROXY", "http_proxy", "no_proxy"]:
1381 1375 if os.environ.has_key(env):
1382 1376 del os.environ[env]
1383 1377
1384 1378 proxy_handler = urllib2.BaseHandler()
1385 1379 if host and not no_proxy:
1386 1380 proxy_handler = urllib2.ProxyHandler({"http" : "http://" + host})
1387 1381
1388 1382 authinfo = None
1389 1383 if user and passwd:
1390 1384 passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
1391 1385 passmgr.add_password(None, host, user, passwd)
1392 1386 authinfo = urllib2.ProxyBasicAuthHandler(passmgr)
1393 1387
1394 1388 opener = urllib2.build_opener(proxy_handler, authinfo)
1395 1389 urllib2.install_opener(opener)
1396 1390
1397 1391 def do_cmd(self, cmd, **args):
1398 1392 self.ui.debug("sending %s command\n" % cmd)
1399 1393 q = {"cmd": cmd}
1400 1394 q.update(args)
1401 1395 qs = urllib.urlencode(q)
1402 1396 cu = "%s?%s" % (self.url, qs)
1403 1397 return urllib2.urlopen(cu)
1404 1398
1405 1399 def heads(self):
1406 1400 d = self.do_cmd("heads").read()
1407 1401 try:
1408 1402 return map(bin, d[:-1].split(" "))
1409 1403 except:
1410 1404 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1411 1405 raise
1412 1406
1413 1407 def branches(self, nodes):
1414 1408 n = " ".join(map(hex, nodes))
1415 1409 d = self.do_cmd("branches", nodes=n).read()
1416 1410 try:
1417 1411 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
1418 1412 return br
1419 1413 except:
1420 1414 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1421 1415 raise
1422 1416
1423 1417 def between(self, pairs):
1424 1418 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
1425 1419 d = self.do_cmd("between", pairs=n).read()
1426 1420 try:
1427 1421 p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
1428 1422 return p
1429 1423 except:
1430 1424 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1431 1425 raise
1432 1426
1433 1427 def changegroup(self, nodes):
1434 1428 n = " ".join(map(hex, nodes))
1435 1429 zd = zlib.decompressobj()
1436 1430 f = self.do_cmd("changegroup", roots=n)
1437 1431 bytes = 0
1438 1432 while 1:
1439 1433 d = f.read(4096)
1440 1434 bytes += len(d)
1441 1435 if not d:
1442 1436 yield zd.flush()
1443 1437 break
1444 1438 yield zd.decompress(d)
1445 1439 self.ui.note("%d bytes of data transfered\n" % bytes)
1446 1440
1447 1441 def repository(ui, path=None, create=0):
1448 1442 if path and path[:7] == "http://":
1449 1443 return remoterepository(ui, path)
1450 1444 if path and path[:5] == "hg://":
1451 1445 return remoterepository(ui, path.replace("hg://", "http://"))
1452 1446 if path and path[:11] == "old-http://":
1453 1447 return localrepository(ui, path.replace("old-http://", "http://"))
1454 1448 else:
1455 1449 return localrepository(ui, path, create)
1456 1450
General Comments 0
You need to be logged in to leave comments. Login now