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