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