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