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