##// END OF EJS Templates
hg merge: fix time asymmetry bug with deleting files on update to past...
mpm@selenic.com -
r383:4862a134 default
parent child Browse files
Show More
@@ -0,0 +1,12 b''
1 #!/bin/sh -x
2 hg init
3 echo This is file a1 > a
4 hg add a
5 hg commit -t "commit #0" -d "0 0" -u user
6 ls
7 echo This is file b1 > b
8 hg add b
9 hg commit -t "commit #1" -d "0 0" -u user
10 hg co 0
11 # B should disappear
12 ls
@@ -0,0 +1,12 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 + ls
6 a
7 + echo This is file b1
8 + hg add b
9 + hg commit -t 'commit #1' -d '0 0' -u user
10 + hg co 0
11 + ls
12 a
@@ -1,1418 +1,1422 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 self.root = path
362 362 self.ui = ui
363 363
364 364 if create:
365 365 os.mkdir(self.path)
366 366 os.mkdir(self.join("data"))
367 367
368 368 self.opener = opener(self.path)
369 369 self.wopener = opener(self.root)
370 370 self.manifest = manifest(self.opener)
371 371 self.changelog = changelog(self.opener)
372 372 self.ignorelist = None
373 373 self.tagscache = None
374 374 self.nodetagscache = None
375 375
376 376 if not self.remote:
377 377 self.dirstate = dirstate(self.opener, ui, self.root)
378 378 try:
379 379 self.ui.readconfig(self.opener("hgrc"))
380 380 except IOError: pass
381 381
382 382 def ignore(self, f):
383 383 if self.ignorelist is None:
384 384 self.ignorelist = []
385 385 try:
386 386 l = self.wfile(".hgignore")
387 387 for pat in l:
388 388 if pat != "\n":
389 389 self.ignorelist.append(re.compile(pat[:-1]))
390 390 except IOError: pass
391 391 for pat in self.ignorelist:
392 392 if pat.search(f): return True
393 393 return False
394 394
395 395 def tags(self):
396 396 '''return a mapping of tag to node'''
397 397 if not self.tagscache:
398 398 self.tagscache = {}
399 399 try:
400 400 # read each head of the tags file, ending with the tip
401 401 # and add each tag found to the map, with "newer" ones
402 402 # taking precedence
403 403 fl = self.file(".hgtags")
404 404 h = fl.heads()
405 405 h.reverse()
406 406 for r in h:
407 407 for l in fl.revision(r).splitlines():
408 408 if l:
409 409 n, k = l.split(" ")
410 410 self.tagscache[k] = bin(n)
411 411 except KeyError: pass
412 412 self.tagscache['tip'] = self.changelog.tip()
413 413
414 414 return self.tagscache
415 415
416 416 def tagslist(self):
417 417 '''return a list of tags ordered by revision'''
418 418 l = []
419 419 for t,n in self.tags().items():
420 420 try:
421 421 r = self.changelog.rev(n)
422 422 except:
423 423 r = -2 # sort to the beginning of the list if unknown
424 424 l.append((r,t,n))
425 425 l.sort()
426 426 return [(t,n) for r,t,n in l]
427 427
428 428 def nodetags(self, node):
429 429 '''return the tags associated with a node'''
430 430 if not self.nodetagscache:
431 431 self.nodetagscache = {}
432 432 for t,n in self.tags().items():
433 433 self.nodetagscache.setdefault(n,[]).append(t)
434 434 return self.nodetagscache.get(node, [])
435 435
436 436 def lookup(self, key):
437 437 try:
438 438 return self.tags()[key]
439 439 except KeyError:
440 440 return self.changelog.lookup(key)
441 441
442 442 def join(self, f):
443 443 return os.path.join(self.path, f)
444 444
445 445 def wjoin(self, f):
446 446 return os.path.join(self.root, f)
447 447
448 448 def file(self, f):
449 449 if f[0] == '/': f = f[1:]
450 450 return filelog(self.opener, f)
451 451
452 452 def wfile(self, f, mode='r'):
453 453 return self.wopener(f, mode)
454 454
455 455 def transaction(self):
456 456 # save dirstate for undo
457 457 try:
458 458 ds = self.opener("dirstate").read()
459 459 except IOError:
460 460 ds = ""
461 461 self.opener("undo.dirstate", "w").write(ds)
462 462
463 463 return transaction.transaction(self.opener, self.join("journal"),
464 464 self.join("undo"))
465 465
466 466 def recover(self):
467 467 lock = self.lock()
468 468 if os.path.exists(self.join("recover")):
469 469 self.ui.status("attempting to rollback interrupted transaction\n")
470 470 return transaction.rollback(self.opener, self.join("recover"))
471 471 else:
472 472 self.ui.warn("no interrupted transaction available\n")
473 473
474 474 def undo(self):
475 475 lock = self.lock()
476 476 if os.path.exists(self.join("undo")):
477 477 self.ui.status("attempting to rollback last transaction\n")
478 478 transaction.rollback(self.opener, self.join("undo"))
479 479 self.dirstate = None
480 480 os.rename(self.join("undo.dirstate"), self.join("dirstate"))
481 481 self.dirstate = dirstate(self.opener, self.ui, self.root)
482 482 else:
483 483 self.ui.warn("no undo information available\n")
484 484
485 485 def lock(self, wait = 1):
486 486 try:
487 487 return lock.lock(self.join("lock"), 0)
488 488 except lock.LockHeld, inst:
489 489 if wait:
490 490 self.ui.warn("waiting for lock held by %s\n" % inst.args[0])
491 491 return lock.lock(self.join("lock"), wait)
492 492 raise inst
493 493
494 494 def rawcommit(self, files, text, user, date, p1=None, p2=None):
495 495 p1 = p1 or self.dirstate.parents()[0] or nullid
496 496 p2 = p2 or self.dirstate.parents()[1] or nullid
497 497 c1 = self.changelog.read(p1)
498 498 c2 = self.changelog.read(p2)
499 499 m1 = self.manifest.read(c1[0])
500 500 mf1 = self.manifest.readflags(c1[0])
501 501 m2 = self.manifest.read(c2[0])
502 502
503 503 tr = self.transaction()
504 504 mm = m1.copy()
505 505 mfm = mf1.copy()
506 506 linkrev = self.changelog.count()
507 507 self.dirstate.setparents(p1, p2)
508 508 for f in files:
509 509 try:
510 510 t = self.wfile(f).read()
511 511 tm = is_exec(self.wjoin(f))
512 512 r = self.file(f)
513 513 mfm[f] = tm
514 514 mm[f] = r.add(t, {}, tr, linkrev,
515 515 m1.get(f, nullid), m2.get(f, nullid))
516 516 self.dirstate.update([f], "n")
517 517 except IOError:
518 518 try:
519 519 del mm[f]
520 520 del mfm[f]
521 521 self.dirstate.forget([f])
522 522 except:
523 523 # deleted from p2?
524 524 pass
525 525
526 526 mnode = self.manifest.add(mm, mfm, tr, linkrev, c1[0], c2[0])
527 527 n = self.changelog.add(mnode, files, text, tr, p1, p2, user, date)
528 528 tr.close()
529 529
530 530 def commit(self, files = None, text = "", user = None, date = None):
531 531 commit = []
532 532 remove = []
533 533 if files:
534 534 for f in files:
535 535 s = self.dirstate.state(f)
536 536 if s in 'nmai':
537 537 commit.append(f)
538 538 elif s == 'r':
539 539 remove.append(f)
540 540 else:
541 541 self.ui.warn("%s not tracked!\n" % f)
542 542 else:
543 543 (c, a, d, u) = self.diffdir(self.root)
544 544 commit = c + a
545 545 remove = d
546 546
547 547 if not commit and not remove:
548 548 self.ui.status("nothing changed\n")
549 549 return
550 550
551 551 p1, p2 = self.dirstate.parents()
552 552 c1 = self.changelog.read(p1)
553 553 c2 = self.changelog.read(p2)
554 554 m1 = self.manifest.read(c1[0])
555 555 mf1 = self.manifest.readflags(c1[0])
556 556 m2 = self.manifest.read(c2[0])
557 557 lock = self.lock()
558 558 tr = self.transaction()
559 559
560 560 # check in files
561 561 new = {}
562 562 linkrev = self.changelog.count()
563 563 commit.sort()
564 564 for f in commit:
565 565 self.ui.note(f + "\n")
566 566 try:
567 567 fp = self.wjoin(f)
568 568 mf1[f] = is_exec(fp)
569 569 t = file(fp).read()
570 570 except IOError:
571 571 self.warn("trouble committing %s!\n" % f)
572 572 raise
573 573
574 574 meta = {}
575 575 cp = self.dirstate.copied(f)
576 576 if cp:
577 577 meta["copy"] = cp
578 578 meta["copyrev"] = hex(m1.get(cp, m2.get(cp, nullid)))
579 579 self.ui.debug(" %s: copy %s:%s\n" % (f, cp, meta["copyrev"]))
580 580
581 581 r = self.file(f)
582 582 fp1 = m1.get(f, nullid)
583 583 fp2 = m2.get(f, nullid)
584 584 new[f] = r.add(t, meta, tr, linkrev, fp1, fp2)
585 585
586 586 # update manifest
587 587 m1.update(new)
588 588 for f in remove: del m1[f]
589 589 mn = self.manifest.add(m1, mf1, tr, linkrev, c1[0], c2[0])
590 590
591 591 # add changeset
592 592 new = new.keys()
593 593 new.sort()
594 594
595 595 if not text:
596 596 edittext = "\n" + "HG: manifest hash %s\n" % hex(mn)
597 597 edittext += "".join(["HG: changed %s\n" % f for f in new])
598 598 edittext += "".join(["HG: removed %s\n" % f for f in remove])
599 599 edittext = self.ui.edit(edittext)
600 600 if not edittext.rstrip():
601 601 return 1
602 602 text = edittext
603 603
604 604 n = self.changelog.add(mn, new, text, tr, p1, p2, user, date)
605 605 tr.close()
606 606
607 607 self.dirstate.setparents(n)
608 608 self.dirstate.update(new, "n")
609 609 self.dirstate.forget(remove)
610 610
611 611 def diffdir(self, path, changeset = None):
612 612 changed = []
613 613 added = []
614 614 unknown = []
615 615 mf = {}
616 616
617 617 if changeset:
618 618 change = self.changelog.read(changeset)
619 619 mf = self.manifest.read(change[0])
620 620 dc = dict.fromkeys(mf)
621 621 else:
622 622 changeset = self.dirstate.parents()[0]
623 623 change = self.changelog.read(changeset)
624 624 mf = self.manifest.read(change[0])
625 625 dc = self.dirstate.dup()
626 626
627 627 def fcmp(fn):
628 628 t1 = self.wfile(fn).read()
629 629 t2 = self.file(fn).revision(mf[fn])
630 630 return cmp(t1, t2)
631 631
632 632 for dir, subdirs, files in os.walk(path):
633 633 d = dir[len(self.root)+1:]
634 634 if ".hg" in subdirs: subdirs.remove(".hg")
635 635
636 636 for f in files:
637 637 fn = os.path.join(d, f)
638 638 try: s = os.stat(os.path.join(self.root, fn))
639 639 except: continue
640 640 if fn in dc:
641 641 c = dc[fn]
642 642 del dc[fn]
643 643 if not c:
644 644 if fcmp(fn):
645 645 changed.append(fn)
646 646 elif c[0] == 'm':
647 647 changed.append(fn)
648 648 elif c[0] == 'a':
649 649 added.append(fn)
650 650 elif c[0] == 'r':
651 651 unknown.append(fn)
652 652 elif c[2] != s.st_size or (c[1] ^ s.st_mode) & 0100:
653 653 changed.append(fn)
654 654 elif c[1] != s.st_mode or c[3] != s.st_mtime:
655 655 if fcmp(fn):
656 656 changed.append(fn)
657 657 else:
658 658 if self.ignore(fn): continue
659 659 unknown.append(fn)
660 660
661 661 deleted = dc.keys()
662 662 deleted.sort()
663 663
664 664 return (changed, added, deleted, unknown)
665 665
666 666 def diffrevs(self, node1, node2):
667 667 changed, added = [], []
668 668
669 669 change = self.changelog.read(node1)
670 670 mf1 = self.manifest.read(change[0])
671 671 change = self.changelog.read(node2)
672 672 mf2 = self.manifest.read(change[0])
673 673
674 674 for fn in mf2:
675 675 if mf1.has_key(fn):
676 676 if mf1[fn] != mf2[fn]:
677 677 changed.append(fn)
678 678 del mf1[fn]
679 679 else:
680 680 added.append(fn)
681 681
682 682 deleted = mf1.keys()
683 683 deleted.sort()
684 684
685 685 return (changed, added, deleted)
686 686
687 687 def add(self, list):
688 688 for f in list:
689 689 p = self.wjoin(f)
690 690 if not os.path.isfile(p):
691 691 self.ui.warn("%s does not exist!\n" % f)
692 692 elif self.dirstate.state(f) == 'n':
693 693 self.ui.warn("%s already tracked!\n" % f)
694 694 else:
695 695 self.dirstate.update([f], "a")
696 696
697 697 def forget(self, list):
698 698 for f in list:
699 699 if self.dirstate.state(f) not in 'ai':
700 700 self.ui.warn("%s not added!\n" % f)
701 701 else:
702 702 self.dirstate.forget([f])
703 703
704 704 def remove(self, list):
705 705 for f in list:
706 706 p = self.wjoin(f)
707 707 if os.path.isfile(p):
708 708 self.ui.warn("%s still exists!\n" % f)
709 709 elif f not in self.dirstate:
710 710 self.ui.warn("%s not tracked!\n" % f)
711 711 else:
712 712 self.dirstate.update([f], "r")
713 713
714 714 def copy(self, source, dest):
715 715 p = self.wjoin(dest)
716 716 if not os.path.isfile(dest):
717 717 self.ui.warn("%s does not exist!\n" % dest)
718 718 else:
719 719 if self.dirstate.state(dest) == '?':
720 720 self.dirstate.update([dest], "a")
721 721 self.dirstate.copy(source, dest)
722 722
723 723 def heads(self):
724 724 return self.changelog.heads()
725 725
726 726 def branches(self, nodes):
727 727 if not nodes: nodes = [self.changelog.tip()]
728 728 b = []
729 729 for n in nodes:
730 730 t = n
731 731 while n:
732 732 p = self.changelog.parents(n)
733 733 if p[1] != nullid or p[0] == nullid:
734 734 b.append((t, n, p[0], p[1]))
735 735 break
736 736 n = p[0]
737 737 return b
738 738
739 739 def between(self, pairs):
740 740 r = []
741 741
742 742 for top, bottom in pairs:
743 743 n, l, i = top, [], 0
744 744 f = 1
745 745
746 746 while n != bottom:
747 747 p = self.changelog.parents(n)[0]
748 748 if i == f:
749 749 l.append(n)
750 750 f = f * 2
751 751 n = p
752 752 i += 1
753 753
754 754 r.append(l)
755 755
756 756 return r
757 757
758 758 def newer(self, nodes):
759 759 m = {}
760 760 nl = []
761 761 pm = {}
762 762 cl = self.changelog
763 763 t = l = cl.count()
764 764
765 765 # find the lowest numbered node
766 766 for n in nodes:
767 767 l = min(l, cl.rev(n))
768 768 m[n] = 1
769 769
770 770 for i in xrange(l, t):
771 771 n = cl.node(i)
772 772 if n in m: # explicitly listed
773 773 pm[n] = 1
774 774 nl.append(n)
775 775 continue
776 776 for p in cl.parents(n):
777 777 if p in pm: # parent listed
778 778 pm[n] = 1
779 779 nl.append(n)
780 780 break
781 781
782 782 return nl
783 783
784 784 def getchangegroup(self, remote):
785 785 m = self.changelog.nodemap
786 786 search = []
787 787 fetch = []
788 788 seen = {}
789 789 seenbranch = {}
790 790
791 791 # if we have an empty repo, fetch everything
792 792 if self.changelog.tip() == nullid:
793 793 self.ui.status("requesting all changes\n")
794 794 return remote.changegroup([nullid])
795 795
796 796 # otherwise, assume we're closer to the tip than the root
797 797 self.ui.status("searching for changes\n")
798 798 heads = remote.heads()
799 799 unknown = []
800 800 for h in heads:
801 801 if h not in m:
802 802 unknown.append(h)
803 803
804 804 if not unknown:
805 805 self.ui.status("nothing to do!\n")
806 806 return None
807 807
808 808 rep = {}
809 809 reqcnt = 0
810 810
811 811 unknown = remote.branches(unknown)
812 812 while unknown:
813 813 r = []
814 814 while unknown:
815 815 n = unknown.pop(0)
816 816 if n[0] in seen:
817 817 continue
818 818
819 819 self.ui.debug("examining %s:%s\n" % (short(n[0]), short(n[1])))
820 820 if n[0] == nullid:
821 821 break
822 822 if n in seenbranch:
823 823 self.ui.debug("branch already found\n")
824 824 continue
825 825 if n[1] and n[1] in m: # do we know the base?
826 826 self.ui.debug("found incomplete branch %s:%s\n"
827 827 % (short(n[0]), short(n[1])))
828 828 search.append(n) # schedule branch range for scanning
829 829 seenbranch[n] = 1
830 830 else:
831 831 if n[1] not in seen and n[1] not in fetch:
832 832 if n[2] in m and n[3] in m:
833 833 self.ui.debug("found new changeset %s\n" %
834 834 short(n[1]))
835 835 fetch.append(n[1]) # earliest unknown
836 836 continue
837 837
838 838 for a in n[2:4]:
839 839 if a not in rep:
840 840 r.append(a)
841 841 rep[a] = 1
842 842
843 843 seen[n[0]] = 1
844 844
845 845 if r:
846 846 reqcnt += 1
847 847 self.ui.debug("request %d: %s\n" %
848 848 (reqcnt, " ".join(map(short, r))))
849 849 for p in range(0, len(r), 10):
850 850 for b in remote.branches(r[p:p+10]):
851 851 self.ui.debug("received %s:%s\n" %
852 852 (short(b[0]), short(b[1])))
853 853 if b[0] not in m and b[0] not in seen:
854 854 unknown.append(b)
855 855
856 856 while search:
857 857 n = search.pop(0)
858 858 reqcnt += 1
859 859 l = remote.between([(n[0], n[1])])[0]
860 860 l.append(n[1])
861 861 p = n[0]
862 862 f = 1
863 863 for i in l:
864 864 self.ui.debug("narrowing %d:%d %s\n" % (f, len(l), short(i)))
865 865 if i in m:
866 866 if f <= 2:
867 867 self.ui.debug("found new branch changeset %s\n" %
868 868 short(p))
869 869 fetch.append(p)
870 870 else:
871 871 self.ui.debug("narrowed branch search to %s:%s\n"
872 872 % (short(p), short(i)))
873 873 search.append((p, i))
874 874 break
875 875 p, f = i, f * 2
876 876
877 877 for f in fetch:
878 878 if f in m:
879 879 raise "already have", short(f[:4])
880 880
881 881 self.ui.note("adding new changesets starting at " +
882 882 " ".join([short(f) for f in fetch]) + "\n")
883 883
884 884 self.ui.debug("%d total queries\n" % reqcnt)
885 885
886 886 return remote.changegroup(fetch)
887 887
888 888 def changegroup(self, basenodes):
889 889 nodes = self.newer(basenodes)
890 890
891 891 # construct the link map
892 892 linkmap = {}
893 893 for n in nodes:
894 894 linkmap[self.changelog.rev(n)] = n
895 895
896 896 # construct a list of all changed files
897 897 changed = {}
898 898 for n in nodes:
899 899 c = self.changelog.read(n)
900 900 for f in c[3]:
901 901 changed[f] = 1
902 902 changed = changed.keys()
903 903 changed.sort()
904 904
905 905 # the changegroup is changesets + manifests + all file revs
906 906 revs = [ self.changelog.rev(n) for n in nodes ]
907 907
908 908 for y in self.changelog.group(linkmap): yield y
909 909 for y in self.manifest.group(linkmap): yield y
910 910 for f in changed:
911 911 yield struct.pack(">l", len(f) + 4) + f
912 912 g = self.file(f).group(linkmap)
913 913 for y in g:
914 914 yield y
915 915
916 916 def addchangegroup(self, generator):
917 917
918 918 class genread:
919 919 def __init__(self, generator):
920 920 self.g = generator
921 921 self.buf = ""
922 922 def read(self, l):
923 923 while l > len(self.buf):
924 924 try:
925 925 self.buf += self.g.next()
926 926 except StopIteration:
927 927 break
928 928 d, self.buf = self.buf[:l], self.buf[l:]
929 929 return d
930 930
931 931 def getchunk():
932 932 d = source.read(4)
933 933 if not d: return ""
934 934 l = struct.unpack(">l", d)[0]
935 935 if l <= 4: return ""
936 936 return source.read(l - 4)
937 937
938 938 def getgroup():
939 939 while 1:
940 940 c = getchunk()
941 941 if not c: break
942 942 yield c
943 943
944 944 def csmap(x):
945 945 self.ui.debug("add changeset %s\n" % short(x))
946 946 return self.changelog.count()
947 947
948 948 def revmap(x):
949 949 return self.changelog.rev(x)
950 950
951 951 if not generator: return
952 952 changesets = files = revisions = 0
953 953
954 954 source = genread(generator)
955 955 lock = self.lock()
956 956 tr = self.transaction()
957 957
958 958 # pull off the changeset group
959 959 self.ui.status("adding changesets\n")
960 960 co = self.changelog.tip()
961 961 cn = self.changelog.addgroup(getgroup(), csmap, tr, 1) # unique
962 962 changesets = self.changelog.rev(cn) - self.changelog.rev(co)
963 963
964 964 # pull off the manifest group
965 965 self.ui.status("adding manifests\n")
966 966 mm = self.manifest.tip()
967 967 mo = self.manifest.addgroup(getgroup(), revmap, tr)
968 968
969 969 # process the files
970 970 self.ui.status("adding file revisions\n")
971 971 while 1:
972 972 f = getchunk()
973 973 if not f: break
974 974 self.ui.debug("adding %s revisions\n" % f)
975 975 fl = self.file(f)
976 976 o = fl.tip()
977 977 n = fl.addgroup(getgroup(), revmap, tr)
978 978 revisions += fl.rev(n) - fl.rev(o)
979 979 files += 1
980 980
981 981 self.ui.status(("modified %d files, added %d changesets" +
982 982 " and %d new revisions\n")
983 983 % (files, changesets, revisions))
984 984
985 985 tr.close()
986 986 return
987 987
988 988 def update(self, node, allow=False, force=False):
989 989 pl = self.dirstate.parents()
990 990 if not force and pl[1] != nullid:
991 991 self.ui.warn("aborting: outstanding uncommitted merges\n")
992 992 return
993 993
994 994 p1, p2 = pl[0], node
995 995 pa = self.changelog.ancestor(p1, p2)
996 996 m1n = self.changelog.read(p1)[0]
997 997 m2n = self.changelog.read(p2)[0]
998 998 man = self.manifest.ancestor(m1n, m2n)
999 999 m1 = self.manifest.read(m1n)
1000 1000 mf1 = self.manifest.readflags(m1n)
1001 1001 m2 = self.manifest.read(m2n)
1002 1002 mf2 = self.manifest.readflags(m2n)
1003 1003 ma = self.manifest.read(man)
1004 1004 mfa = self.manifest.readflags(m2n)
1005 1005
1006 1006 (c, a, d, u) = self.diffdir(self.root)
1007 1007
1008 1008 # resolve the manifest to determine which files
1009 1009 # we care about merging
1010 1010 self.ui.note("resolving manifests\n")
1011 1011 self.ui.debug(" ancestor %s local %s remote %s\n" %
1012 1012 (short(man), short(m1n), short(m2n)))
1013 1013
1014 1014 merge = {}
1015 1015 get = {}
1016 1016 remove = []
1017 1017 mark = {}
1018 1018
1019 1019 # construct a working dir manifest
1020 1020 mw = m1.copy()
1021 1021 mfw = mf1.copy()
1022 1022 for f in a + c + u:
1023 1023 mw[f] = ""
1024 1024 mfw[f] = is_exec(self.wjoin(f))
1025 1025 for f in d:
1026 1026 if f in mw: del mw[f]
1027 1027
1028 1028 for f, n in mw.iteritems():
1029 1029 if f in m2:
1030 1030 s = 0
1031 1031
1032 1032 # are files different?
1033 1033 if n != m2[f]:
1034 1034 a = ma.get(f, nullid)
1035 1035 # are both different from the ancestor?
1036 1036 if n != a and m2[f] != a:
1037 1037 self.ui.debug(" %s versions differ, resolve\n" % f)
1038 1038 merge[f] = (m1.get(f, nullid), m2[f])
1039 1039 # merge executable bits
1040 1040 # "if we changed or they changed, change in merge"
1041 1041 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1042 1042 mode = ((a^b) | (a^c)) ^ a
1043 1043 merge[f] = (m1.get(f, nullid), m2[f], mode)
1044 1044 s = 1
1045 1045 # are we clobbering?
1046 1046 # is remote's version newer?
1047 1047 # or are we going back in time?
1048 1048 elif force or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
1049 1049 self.ui.debug(" remote %s is newer, get\n" % f)
1050 1050 get[f] = m2[f]
1051 1051 s = 1
1052 1052 else:
1053 1053 mark[f] = 1
1054 1054
1055 1055 if not s and mfw[f] != mf2[f]:
1056 1056 if force:
1057 1057 self.ui.debug(" updating permissions for %s\n" % f)
1058 1058 set_exec(self.wjoin(f), mf2[f])
1059 1059 else:
1060 1060 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1061 1061 mode = ((a^b) | (a^c)) ^ a
1062 1062 if mode != b:
1063 1063 self.ui.debug(" updating permissions for %s\n" % f)
1064 1064 set_exec(self.wjoin(f), mode)
1065 1065 mark[f] = 1
1066 1066 del m2[f]
1067 1067 elif f in ma:
1068 1068 if not force and n != ma[f]:
1069 1069 r = self.ui.prompt(
1070 1070 (" local changed %s which remote deleted\n" % f) +
1071 1071 "(k)eep or (d)elete?", "[kd]", "k")
1072 1072 if r == "d":
1073 1073 remove.append(f)
1074 1074 else:
1075 1075 self.ui.debug("other deleted %s\n" % f)
1076 1076 remove.append(f) # other deleted it
1077 1077 else:
1078 1078 if n == m1.get(f, nullid): # same as parent
1079 self.ui.debug("local created %s, keeping\n" % f)
1079 if p2 == pa: # going backwards?
1080 self.ui.debug("remote deleted %s\n" % f)
1081 remove.append(f)
1082 else:
1083 self.ui.debug("local created %s, keeping\n" % f)
1080 1084 else:
1081 1085 self.ui.debug("working dir created %s, keeping\n" % f)
1082 1086
1083 1087 for f, n in m2.iteritems():
1084 1088 if f[0] == "/": continue
1085 1089 if not force and f in ma and n != ma[f]:
1086 1090 r = self.ui.prompt(
1087 1091 ("remote changed %s which local deleted\n" % f) +
1088 1092 "(k)eep or (d)elete?", "[kd]", "k")
1089 1093 if r == "d": remove.append(f)
1090 1094 else:
1091 1095 self.ui.debug("remote created %s\n" % f)
1092 1096 get[f] = n
1093 1097
1094 1098 del mw, m1, m2, ma
1095 1099
1096 1100 if force:
1097 1101 for f in merge:
1098 1102 get[f] = merge[f][1]
1099 1103 merge = {}
1100 1104
1101 1105 if pa == p1 or pa == p2:
1102 1106 # we don't need to do any magic, just jump to the new rev
1103 1107 mode = 'n'
1104 1108 p1, p2 = p2, nullid
1105 1109 else:
1106 1110 if not allow:
1107 1111 self.ui.status("this update spans a branch" +
1108 1112 " affecting the following files:\n")
1109 1113 fl = merge.keys() + get.keys()
1110 1114 fl.sort()
1111 1115 for f in fl:
1112 1116 cf = ""
1113 1117 if f in merge: cf = " (resolve)"
1114 1118 self.ui.status(" %s%s\n" % (f, cf))
1115 1119 self.ui.warn("aborting update spanning branches!\n")
1116 1120 self.ui.status("(use update -m to perform a branch merge)\n")
1117 1121 return 1
1118 1122 # we have to remember what files we needed to get/change
1119 1123 # because any file that's different from either one of its
1120 1124 # parents must be in the changeset
1121 1125 mode = 'm'
1122 1126 self.dirstate.update(mark.keys(), "m")
1123 1127
1124 1128 self.dirstate.setparents(p1, p2)
1125 1129
1126 1130 # get the files we don't need to change
1127 1131 files = get.keys()
1128 1132 files.sort()
1129 1133 for f in files:
1130 1134 if f[0] == "/": continue
1131 1135 self.ui.note("getting %s\n" % f)
1132 1136 t = self.file(f).read(get[f])
1133 1137 try:
1134 1138 self.wfile(f, "w").write(t)
1135 1139 except IOError:
1136 1140 os.makedirs(os.path.dirname(self.wjoin(f)))
1137 1141 self.wfile(f, "w").write(t)
1138 1142 set_exec(self.wjoin(f), mf2[f])
1139 1143 self.dirstate.update([f], mode)
1140 1144
1141 1145 # merge the tricky bits
1142 1146 files = merge.keys()
1143 1147 files.sort()
1144 1148 for f in files:
1145 1149 self.ui.status("merging %s\n" % f)
1146 1150 m, o, flag = merge[f]
1147 1151 self.merge3(f, m, o)
1148 1152 set_exec(self.wjoin(f), flag)
1149 1153 self.dirstate.update([f], 'm')
1150 1154
1151 1155 for f in remove:
1152 1156 self.ui.note("removing %s\n" % f)
1153 1157 os.unlink(f)
1154 1158 if mode == 'n':
1155 1159 self.dirstate.forget(remove)
1156 1160 else:
1157 1161 self.dirstate.update(remove, 'r')
1158 1162
1159 1163 def merge3(self, fn, my, other):
1160 1164 """perform a 3-way merge in the working directory"""
1161 1165
1162 1166 def temp(prefix, node):
1163 1167 pre = "%s~%s." % (os.path.basename(fn), prefix)
1164 1168 (fd, name) = tempfile.mkstemp("", pre)
1165 1169 f = os.fdopen(fd, "w")
1166 1170 f.write(fl.revision(node))
1167 1171 f.close()
1168 1172 return name
1169 1173
1170 1174 fl = self.file(fn)
1171 1175 base = fl.ancestor(my, other)
1172 1176 a = self.wjoin(fn)
1173 1177 b = temp("base", base)
1174 1178 c = temp("other", other)
1175 1179
1176 1180 self.ui.note("resolving %s\n" % fn)
1177 1181 self.ui.debug("file %s: other %s ancestor %s\n" %
1178 1182 (fn, short(other), short(base)))
1179 1183
1180 1184 cmd = os.environ.get("HGMERGE", "hgmerge")
1181 1185 r = os.system("%s %s %s %s" % (cmd, a, b, c))
1182 1186 if r:
1183 1187 self.ui.warn("merging %s failed!\n" % fn)
1184 1188
1185 1189 os.unlink(b)
1186 1190 os.unlink(c)
1187 1191
1188 1192 def verify(self):
1189 1193 filelinkrevs = {}
1190 1194 filenodes = {}
1191 1195 changesets = revisions = files = 0
1192 1196 errors = 0
1193 1197
1194 1198 seen = {}
1195 1199 self.ui.status("checking changesets\n")
1196 1200 for i in range(self.changelog.count()):
1197 1201 changesets += 1
1198 1202 n = self.changelog.node(i)
1199 1203 if n in seen:
1200 1204 self.ui.warn("duplicate changeset at revision %d\n" % i)
1201 1205 errors += 1
1202 1206 seen[n] = 1
1203 1207
1204 1208 for p in self.changelog.parents(n):
1205 1209 if p not in self.changelog.nodemap:
1206 1210 self.ui.warn("changeset %s has unknown parent %s\n" %
1207 1211 (short(n), short(p)))
1208 1212 errors += 1
1209 1213 try:
1210 1214 changes = self.changelog.read(n)
1211 1215 except Exception, inst:
1212 1216 self.ui.warn("unpacking changeset %s: %s\n" % (short(n), inst))
1213 1217 errors += 1
1214 1218
1215 1219 for f in changes[3]:
1216 1220 filelinkrevs.setdefault(f, []).append(i)
1217 1221
1218 1222 seen = {}
1219 1223 self.ui.status("checking manifests\n")
1220 1224 for i in range(self.manifest.count()):
1221 1225 n = self.manifest.node(i)
1222 1226 if n in seen:
1223 1227 self.ui.warn("duplicate manifest at revision %d\n" % i)
1224 1228 errors += 1
1225 1229 seen[n] = 1
1226 1230
1227 1231 for p in self.manifest.parents(n):
1228 1232 if p not in self.manifest.nodemap:
1229 1233 self.ui.warn("manifest %s has unknown parent %s\n" %
1230 1234 (short(n), short(p)))
1231 1235 errors += 1
1232 1236
1233 1237 try:
1234 1238 delta = mdiff.patchtext(self.manifest.delta(n))
1235 1239 except KeyboardInterrupt:
1236 1240 print "aborted"
1237 1241 sys.exit(0)
1238 1242 except Exception, inst:
1239 1243 self.ui.warn("unpacking manifest %s: %s\n"
1240 1244 % (short(n), inst))
1241 1245 errors += 1
1242 1246
1243 1247 ff = [ l.split('\0') for l in delta.splitlines() ]
1244 1248 for f, fn in ff:
1245 1249 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
1246 1250
1247 1251 self.ui.status("crosschecking files in changesets and manifests\n")
1248 1252 for f in filenodes:
1249 1253 if f not in filelinkrevs:
1250 1254 self.ui.warn("file %s in manifest but not in changesets\n" % f)
1251 1255 errors += 1
1252 1256
1253 1257 for f in filelinkrevs:
1254 1258 if f not in filenodes:
1255 1259 self.ui.warn("file %s in changeset but not in manifest\n" % f)
1256 1260 errors += 1
1257 1261
1258 1262 self.ui.status("checking files\n")
1259 1263 ff = filenodes.keys()
1260 1264 ff.sort()
1261 1265 for f in ff:
1262 1266 if f == "/dev/null": continue
1263 1267 files += 1
1264 1268 fl = self.file(f)
1265 1269 nodes = { nullid: 1 }
1266 1270 seen = {}
1267 1271 for i in range(fl.count()):
1268 1272 revisions += 1
1269 1273 n = fl.node(i)
1270 1274
1271 1275 if n in seen:
1272 1276 self.ui.warn("%s: duplicate revision %d\n" % (f, i))
1273 1277 errors += 1
1274 1278
1275 1279 if n not in filenodes[f]:
1276 1280 self.ui.warn("%s: %d:%s not in manifests\n"
1277 1281 % (f, i, short(n)))
1278 1282 print len(filenodes[f].keys()), fl.count(), f
1279 1283 errors += 1
1280 1284 else:
1281 1285 del filenodes[f][n]
1282 1286
1283 1287 flr = fl.linkrev(n)
1284 1288 if flr not in filelinkrevs[f]:
1285 1289 self.ui.warn("%s:%s points to unexpected changeset %d\n"
1286 1290 % (f, short(n), fl.linkrev(n)))
1287 1291 errors += 1
1288 1292 else:
1289 1293 filelinkrevs[f].remove(flr)
1290 1294
1291 1295 # verify contents
1292 1296 try:
1293 1297 t = fl.read(n)
1294 1298 except Exception, inst:
1295 1299 self.ui.warn("unpacking file %s %s: %s\n"
1296 1300 % (f, short(n), inst))
1297 1301 errors += 1
1298 1302
1299 1303 # verify parents
1300 1304 (p1, p2) = fl.parents(n)
1301 1305 if p1 not in nodes:
1302 1306 self.ui.warn("file %s:%s unknown parent 1 %s" %
1303 1307 (f, short(n), short(p1)))
1304 1308 errors += 1
1305 1309 if p2 not in nodes:
1306 1310 self.ui.warn("file %s:%s unknown parent 2 %s" %
1307 1311 (f, short(n), short(p1)))
1308 1312 errors += 1
1309 1313 nodes[n] = 1
1310 1314
1311 1315 # cross-check
1312 1316 for node in filenodes[f]:
1313 1317 self.ui.warn("node %s in manifests not in %s\n"
1314 1318 % (hex(n), f))
1315 1319 errors += 1
1316 1320
1317 1321 self.ui.status("%d files, %d changesets, %d total revisions\n" %
1318 1322 (files, changesets, revisions))
1319 1323
1320 1324 if errors:
1321 1325 self.ui.warn("%d integrity errors encountered!\n" % errors)
1322 1326 return 1
1323 1327
1324 1328 class remoterepository:
1325 1329 def __init__(self, ui, path):
1326 1330 self.url = path
1327 1331 self.ui = ui
1328 1332 no_list = [ "localhost", "127.0.0.1" ]
1329 1333 host = ui.config("http_proxy", "host")
1330 1334 user = ui.config("http_proxy", "user")
1331 1335 passwd = ui.config("http_proxy", "passwd")
1332 1336 no = ui.config("http_proxy", "no")
1333 1337 if no:
1334 1338 no_list = no_list + no.split(",")
1335 1339
1336 1340 no_proxy = 0
1337 1341 for h in no_list:
1338 1342 if (path.startswith("http://" + h + "/") or
1339 1343 path.startswith("http://" + h + ":") or
1340 1344 path == "http://" + h):
1341 1345 no_proxy = 1
1342 1346
1343 1347 # Note: urllib2 takes proxy values from the environment and those will
1344 1348 # take precedence
1345 1349
1346 1350 proxy_handler = urllib2.BaseHandler()
1347 1351 if host and not no_proxy:
1348 1352 proxy_handler = urllib2.ProxyHandler({"http" : "http://" + host})
1349 1353
1350 1354 authinfo = None
1351 1355 if user and passwd:
1352 1356 passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
1353 1357 passmgr.add_password(None, host, user, passwd)
1354 1358 authinfo = urllib2.ProxyBasicAuthHandler(passmgr)
1355 1359
1356 1360 opener = urllib2.build_opener(proxy_handler, authinfo)
1357 1361 urllib2.install_opener(opener)
1358 1362
1359 1363 def do_cmd(self, cmd, **args):
1360 1364 self.ui.debug("sending %s command\n" % cmd)
1361 1365 q = {"cmd": cmd}
1362 1366 q.update(args)
1363 1367 qs = urllib.urlencode(q)
1364 1368 cu = "%s?%s" % (self.url, qs)
1365 1369 return urllib2.urlopen(cu)
1366 1370
1367 1371 def heads(self):
1368 1372 d = self.do_cmd("heads").read()
1369 1373 try:
1370 1374 return map(bin, d[:-1].split(" "))
1371 1375 except:
1372 1376 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1373 1377 raise
1374 1378
1375 1379 def branches(self, nodes):
1376 1380 n = " ".join(map(hex, nodes))
1377 1381 d = self.do_cmd("branches", nodes=n).read()
1378 1382 try:
1379 1383 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
1380 1384 return br
1381 1385 except:
1382 1386 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1383 1387 raise
1384 1388
1385 1389 def between(self, pairs):
1386 1390 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
1387 1391 d = self.do_cmd("between", pairs=n).read()
1388 1392 try:
1389 1393 p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
1390 1394 return p
1391 1395 except:
1392 1396 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1393 1397 raise
1394 1398
1395 1399 def changegroup(self, nodes):
1396 1400 n = " ".join(map(hex, nodes))
1397 1401 zd = zlib.decompressobj()
1398 1402 f = self.do_cmd("changegroup", roots=n)
1399 1403 bytes = 0
1400 1404 while 1:
1401 1405 d = f.read(4096)
1402 1406 bytes += len(d)
1403 1407 if not d:
1404 1408 yield zd.flush()
1405 1409 break
1406 1410 yield zd.decompress(d)
1407 1411 self.ui.note("%d bytes of data transfered\n" % bytes)
1408 1412
1409 1413 def repository(ui, path=None, create=0):
1410 1414 if path and path[:7] == "http://":
1411 1415 return remoterepository(ui, path)
1412 1416 if path and path[:5] == "hg://":
1413 1417 return remoterepository(ui, path.replace("hg://", "http://"))
1414 1418 if path and path[:11] == "old-http://":
1415 1419 return localrepository(ui, path.replace("old-http://", "http://"))
1416 1420 else:
1417 1421 return localrepository(ui, path, create)
1418 1422
General Comments 0
You need to be logged in to leave comments. Login now