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