##// END OF EJS Templates
Add a scheme for handling remote locking...
Matt Mackall -
r638:35f7adfe default
parent child Browse files
Show More
@@ -1,1285 +1,1294 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 if dest is None:
365 if dest is None:
366 dest = os.path.basename(os.path.normpath(source))
366 dest = os.path.basename(os.path.normpath(source))
367
367
368 if os.path.exists(dest):
368 if os.path.exists(dest):
369 ui.warn("abort: destination '%s' already exists\n" % dest)
369 ui.warn("abort: destination '%s' already exists\n" % dest)
370 return 1
370 return 1
371
371
372 class dircleanup:
372 class dircleanup:
373 def __init__(self, dir):
373 def __init__(self, dir):
374 import shutil
374 import shutil
375 self.rmtree = shutil.rmtree
375 self.rmtree = shutil.rmtree
376 self.dir = dir
376 self.dir = dir
377 os.mkdir(dir)
377 os.mkdir(dir)
378 def close(self):
378 def close(self):
379 self.dir = None
379 self.dir = None
380 def __del__(self):
380 def __del__(self):
381 if self.dir:
381 if self.dir:
382 self.rmtree(self.dir, True)
382 self.rmtree(self.dir, True)
383
383
384 d = dircleanup(dest)
384 d = dircleanup(dest)
385 link = 0
385 link = 0
386 abspath = source
386 abspath = source
387 source = ui.expandpath(source)
387 source = ui.expandpath(source)
388 other = hg.repository(ui, source)
388 other = hg.repository(ui, source)
389
389
390 if other.dev() != -1 and os.stat(dest).st_dev == other.dev():
390 if other.dev() != -1 and os.stat(dest).st_dev == other.dev():
391 ui.status("cloning by hardlink\n")
391 ui.status("cloning by hardlink\n")
392 util.system("cp -al '%s'/.hg '%s'/.hg" % (source, dest))
392 util.system("cp -al '%s'/.hg '%s'/.hg" % (source, dest))
393 try:
393 try:
394 os.remove(os.path.join(dest, ".hg", "dirstate"))
394 os.remove(os.path.join(dest, ".hg", "dirstate"))
395 except: pass
395 except: pass
396
396
397 repo = hg.repository(ui, dest)
397 repo = hg.repository(ui, dest)
398
398
399 else:
399 else:
400 repo = hg.repository(ui, dest, create=1)
400 repo = hg.repository(ui, dest, create=1)
401 repo.pull(other)
401 repo.pull(other)
402
402
403 f = repo.opener("hgrc", "w")
403 f = repo.opener("hgrc", "w")
404 f.write("[paths]\n")
404 f.write("[paths]\n")
405 f.write("default = %s\n" % abspath)
405 f.write("default = %s\n" % abspath)
406
406
407 if not opts['noupdate']:
407 if not opts['noupdate']:
408 update(ui, repo)
408 update(ui, repo)
409
409
410 d.close()
410 d.close()
411
411
412 def commit(ui, repo, *files, **opts):
412 def commit(ui, repo, *files, **opts):
413 """commit the specified files or all outstanding changes"""
413 """commit the specified files or all outstanding changes"""
414 text = opts['text']
414 text = opts['text']
415 if not text and opts['logfile']:
415 if not text and opts['logfile']:
416 try: text = open(opts['logfile']).read()
416 try: text = open(opts['logfile']).read()
417 except IOError: pass
417 except IOError: pass
418
418
419 if opts['addremove']:
419 if opts['addremove']:
420 addremove(ui, repo, *files)
420 addremove(ui, repo, *files)
421 repo.commit(relpath(repo, files), text, opts['user'], opts['date'])
421 repo.commit(relpath(repo, files), text, opts['user'], opts['date'])
422
422
423 def copy(ui, repo, source, dest):
423 def copy(ui, repo, source, dest):
424 """mark a file as copied or renamed for the next commit"""
424 """mark a file as copied or renamed for the next commit"""
425 return repo.copy(*relpath(repo, (source, dest)))
425 return repo.copy(*relpath(repo, (source, dest)))
426
426
427 def debugcheckstate(ui, repo):
427 def debugcheckstate(ui, repo):
428 """validate the correctness of the current dirstate"""
428 """validate the correctness of the current dirstate"""
429 parent1, parent2 = repo.dirstate.parents()
429 parent1, parent2 = repo.dirstate.parents()
430 repo.dirstate.read()
430 repo.dirstate.read()
431 dc = repo.dirstate.map
431 dc = repo.dirstate.map
432 keys = dc.keys()
432 keys = dc.keys()
433 keys.sort()
433 keys.sort()
434 m1n = repo.changelog.read(parent1)[0]
434 m1n = repo.changelog.read(parent1)[0]
435 m2n = repo.changelog.read(parent2)[0]
435 m2n = repo.changelog.read(parent2)[0]
436 m1 = repo.manifest.read(m1n)
436 m1 = repo.manifest.read(m1n)
437 m2 = repo.manifest.read(m2n)
437 m2 = repo.manifest.read(m2n)
438 errors = 0
438 errors = 0
439 for f in dc:
439 for f in dc:
440 state = repo.dirstate.state(f)
440 state = repo.dirstate.state(f)
441 if state in "nr" and f not in m1:
441 if state in "nr" and f not in m1:
442 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))
443 errors += 1
443 errors += 1
444 if state in "a" and f in m1:
444 if state in "a" and f in m1:
445 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))
446 errors += 1
446 errors += 1
447 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:
448 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" %
449 (f, state))
449 (f, state))
450 errors += 1
450 errors += 1
451 for f in m1:
451 for f in m1:
452 state = repo.dirstate.state(f)
452 state = repo.dirstate.state(f)
453 if state not in "nrm":
453 if state not in "nrm":
454 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))
455 errors += 1
455 errors += 1
456 if errors:
456 if errors:
457 ui.warn(".hg/dirstate inconsistent with current parent's manifest\n")
457 ui.warn(".hg/dirstate inconsistent with current parent's manifest\n")
458 sys.exit(1)
458 sys.exit(1)
459
459
460 def debugstate(ui, repo):
460 def debugstate(ui, repo):
461 """show the contents of the current dirstate"""
461 """show the contents of the current dirstate"""
462 repo.dirstate.read()
462 repo.dirstate.read()
463 dc = repo.dirstate.map
463 dc = repo.dirstate.map
464 keys = dc.keys()
464 keys = dc.keys()
465 keys.sort()
465 keys.sort()
466 for file in keys:
466 for file in keys:
467 ui.write("%c %s\n" % (dc[file][0], file))
467 ui.write("%c %s\n" % (dc[file][0], file))
468
468
469 def debugindex(ui, file):
469 def debugindex(ui, file):
470 """dump the contents of an index file"""
470 """dump the contents of an index file"""
471 r = hg.revlog(hg.opener(""), file, "")
471 r = hg.revlog(hg.opener(""), file, "")
472 ui.write(" rev offset length base linkrev" +
472 ui.write(" rev offset length base linkrev" +
473 " p1 p2 nodeid\n")
473 " p1 p2 nodeid\n")
474 for i in range(r.count()):
474 for i in range(r.count()):
475 e = r.index[i]
475 e = r.index[i]
476 ui.write("% 6d % 9d % 7d % 6d % 7d %s.. %s.. %s..\n" % (
476 ui.write("% 6d % 9d % 7d % 6d % 7d %s.. %s.. %s..\n" % (
477 i, e[0], e[1], e[2], e[3],
477 i, e[0], e[1], e[2], e[3],
478 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])))
479
479
480 def debugindexdot(ui, file):
480 def debugindexdot(ui, file):
481 """dump an index DAG as a .dot file"""
481 """dump an index DAG as a .dot file"""
482 r = hg.revlog(hg.opener(""), file, "")
482 r = hg.revlog(hg.opener(""), file, "")
483 ui.write("digraph G {\n")
483 ui.write("digraph G {\n")
484 for i in range(r.count()):
484 for i in range(r.count()):
485 e = r.index[i]
485 e = r.index[i]
486 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
486 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
487 if e[5] != hg.nullid:
487 if e[5] != hg.nullid:
488 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
488 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
489 ui.write("}\n")
489 ui.write("}\n")
490
490
491 def diff(ui, repo, *files, **opts):
491 def diff(ui, repo, *files, **opts):
492 """diff working directory (or selected files)"""
492 """diff working directory (or selected files)"""
493 revs = []
493 revs = []
494 if opts['rev']:
494 if opts['rev']:
495 revs = map(lambda x: repo.lookup(x), opts['rev'])
495 revs = map(lambda x: repo.lookup(x), opts['rev'])
496
496
497 if len(revs) > 2:
497 if len(revs) > 2:
498 ui.warn("too many revisions to diff\n")
498 ui.warn("too many revisions to diff\n")
499 sys.exit(1)
499 sys.exit(1)
500
500
501 if files:
501 if files:
502 files = relpath(repo, files)
502 files = relpath(repo, files)
503 else:
503 else:
504 files = relpath(repo, [""])
504 files = relpath(repo, [""])
505
505
506 dodiff(sys.stdout, ui, repo, files, *revs)
506 dodiff(sys.stdout, ui, repo, files, *revs)
507
507
508 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
508 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
509 node = repo.lookup(changeset)
509 node = repo.lookup(changeset)
510 prev, other = repo.changelog.parents(node)
510 prev, other = repo.changelog.parents(node)
511 change = repo.changelog.read(node)
511 change = repo.changelog.read(node)
512
512
513 if opts['output'] and opts['output'] != '-':
513 if opts['output'] and opts['output'] != '-':
514 try:
514 try:
515 outname = make_filename(repo, repo.changelog, opts['output'],
515 outname = make_filename(repo, repo.changelog, opts['output'],
516 node=node, total=total, seqno=seqno,
516 node=node, total=total, seqno=seqno,
517 revwidth=revwidth)
517 revwidth=revwidth)
518 fp = open(outname, 'wb')
518 fp = open(outname, 'wb')
519 except KeyError, inst:
519 except KeyError, inst:
520 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" %
521 inst.args[0])
521 inst.args[0])
522 sys.exit(1)
522 sys.exit(1)
523 else:
523 else:
524 fp = sys.stdout
524 fp = sys.stdout
525
525
526 fp.write("# HG changeset patch\n")
526 fp.write("# HG changeset patch\n")
527 fp.write("# User %s\n" % change[1])
527 fp.write("# User %s\n" % change[1])
528 fp.write("# Node ID %s\n" % hg.hex(node))
528 fp.write("# Node ID %s\n" % hg.hex(node))
529 fp.write("# Parent %s\n" % hg.hex(prev))
529 fp.write("# Parent %s\n" % hg.hex(prev))
530 if other != hg.nullid:
530 if other != hg.nullid:
531 fp.write("# Parent %s\n" % hg.hex(other))
531 fp.write("# Parent %s\n" % hg.hex(other))
532 fp.write(change[4].rstrip())
532 fp.write(change[4].rstrip())
533 fp.write("\n\n")
533 fp.write("\n\n")
534
534
535 dodiff(fp, ui, repo, None, prev, node)
535 dodiff(fp, ui, repo, None, prev, node)
536
536
537 def export(ui, repo, *changesets, **opts):
537 def export(ui, repo, *changesets, **opts):
538 """dump the header and diffs for one or more changesets"""
538 """dump the header and diffs for one or more changesets"""
539 if not changesets:
539 if not changesets:
540 ui.warn("error: export requires at least one changeset\n")
540 ui.warn("error: export requires at least one changeset\n")
541 sys.exit(1)
541 sys.exit(1)
542 seqno = 0
542 seqno = 0
543 revs = list(revrange(ui, repo, changesets))
543 revs = list(revrange(ui, repo, changesets))
544 total = len(revs)
544 total = len(revs)
545 revwidth = max(len(revs[0]), len(revs[-1]))
545 revwidth = max(len(revs[0]), len(revs[-1]))
546 for cset in revs:
546 for cset in revs:
547 seqno += 1
547 seqno += 1
548 doexport(ui, repo, cset, seqno, total, revwidth, opts)
548 doexport(ui, repo, cset, seqno, total, revwidth, opts)
549
549
550 def forget(ui, repo, file, *files):
550 def forget(ui, repo, file, *files):
551 """don't add the specified files on the next commit"""
551 """don't add the specified files on the next commit"""
552 repo.forget(relpath(repo, (file,) + files))
552 repo.forget(relpath(repo, (file,) + files))
553
553
554 def heads(ui, repo):
554 def heads(ui, repo):
555 """show current repository heads"""
555 """show current repository heads"""
556 for n in repo.changelog.heads():
556 for n in repo.changelog.heads():
557 show_changeset(ui, repo, changenode=n)
557 show_changeset(ui, repo, changenode=n)
558
558
559 def identify(ui, repo):
559 def identify(ui, repo):
560 """print information about the working copy"""
560 """print information about the working copy"""
561 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]
562 if not parents:
562 if not parents:
563 ui.write("unknown\n")
563 ui.write("unknown\n")
564 return
564 return
565
565
566 hexfunc = ui.verbose and hg.hex or hg.short
566 hexfunc = ui.verbose and hg.hex or hg.short
567 (c, a, d, u) = repo.changes(None, None)
567 (c, a, d, u) = repo.changes(None, None)
568 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
568 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
569 (c or a or d) and "+" or "")]
569 (c or a or d) and "+" or "")]
570
570
571 if not ui.quiet:
571 if not ui.quiet:
572 # multiple tags for a single parent separated by '/'
572 # multiple tags for a single parent separated by '/'
573 parenttags = ['/'.join(tags)
573 parenttags = ['/'.join(tags)
574 for tags in map(repo.nodetags, parents) if tags]
574 for tags in map(repo.nodetags, parents) if tags]
575 # tags for multiple parents separated by ' + '
575 # tags for multiple parents separated by ' + '
576 output.append(' + '.join(parenttags))
576 output.append(' + '.join(parenttags))
577
577
578 ui.write("%s\n" % ' '.join(output))
578 ui.write("%s\n" % ' '.join(output))
579
579
580 def import_(ui, repo, patch1, *patches, **opts):
580 def import_(ui, repo, patch1, *patches, **opts):
581 """import an ordered set of patches"""
581 """import an ordered set of patches"""
582 try:
582 try:
583 import psyco
583 import psyco
584 psyco.full()
584 psyco.full()
585 except:
585 except:
586 pass
586 pass
587
587
588 patches = (patch1,) + patches
588 patches = (patch1,) + patches
589
589
590 d = opts["base"]
590 d = opts["base"]
591 strip = opts["strip"]
591 strip = opts["strip"]
592
592
593 for patch in patches:
593 for patch in patches:
594 ui.status("applying %s\n" % patch)
594 ui.status("applying %s\n" % patch)
595 pf = os.path.join(d, patch)
595 pf = os.path.join(d, patch)
596
596
597 text = ""
597 text = ""
598 for l in file(pf):
598 for l in file(pf):
599 if l.startswith("--- ") or l.startswith("diff -r"): break
599 if l.startswith("--- ") or l.startswith("diff -r"): break
600 text += l
600 text += l
601
601
602 # 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
603 hgpatch = user = snippet = None
603 hgpatch = user = snippet = None
604 ui.debug('text:\n')
604 ui.debug('text:\n')
605 for t in text.splitlines():
605 for t in text.splitlines():
606 ui.debug(t,'\n')
606 ui.debug(t,'\n')
607 if t == '# HG changeset patch' or hgpatch == True:
607 if t == '# HG changeset patch' or hgpatch == True:
608 hgpatch = True
608 hgpatch = True
609 if t[:7] == "# User ":
609 if t[:7] == "# User ":
610 user = t[7:]
610 user = t[7:]
611 ui.debug('User: %s\n' % user)
611 ui.debug('User: %s\n' % user)
612 if t[:2] <> "# " and t.strip() and not snippet: snippet = t
612 if t[:2] <> "# " and t.strip() and not snippet: snippet = t
613 if snippet: text = snippet + '\n' + text
613 if snippet: text = snippet + '\n' + text
614 ui.debug('text:\n%s\n' % text)
614 ui.debug('text:\n%s\n' % text)
615
615
616 # make sure text isn't empty
616 # make sure text isn't empty
617 if not text: text = "imported patch %s\n" % patch
617 if not text: text = "imported patch %s\n" % patch
618
618
619 f = os.popen("patch -p%d < %s" % (strip, pf))
619 f = os.popen("patch -p%d < %s" % (strip, pf))
620 files = []
620 files = []
621 for l in f.read().splitlines():
621 for l in f.read().splitlines():
622 l.rstrip('\r\n');
622 l.rstrip('\r\n');
623 ui.status("%s\n" % l)
623 ui.status("%s\n" % l)
624 if l[:14] == 'patching file ':
624 if l[:14] == 'patching file ':
625 pf = l[14:]
625 pf = l[14:]
626 if pf not in files:
626 if pf not in files:
627 files.append(pf)
627 files.append(pf)
628 patcherr = f.close()
628 patcherr = f.close()
629 if patcherr:
629 if patcherr:
630 sys.stderr.write("patch failed")
630 sys.stderr.write("patch failed")
631 sys.exit(1)
631 sys.exit(1)
632
632
633 if len(files) > 0:
633 if len(files) > 0:
634 addremove(ui, repo, *files)
634 addremove(ui, repo, *files)
635 repo.commit(files, text, user)
635 repo.commit(files, text, user)
636
636
637 def init(ui, source=None):
637 def init(ui, source=None):
638 """create a new repository in the current directory"""
638 """create a new repository in the current directory"""
639
639
640 if source:
640 if source:
641 ui.warn("no longer supported: use \"hg clone\" instead\n")
641 ui.warn("no longer supported: use \"hg clone\" instead\n")
642 sys.exit(1)
642 sys.exit(1)
643 repo = hg.repository(ui, ".", create=1)
643 repo = hg.repository(ui, ".", create=1)
644
644
645 def locate(ui, repo, *pats, **opts):
645 def locate(ui, repo, *pats, **opts):
646 """locate files matching specific patterns"""
646 """locate files matching specific patterns"""
647 if [p for p in pats if os.sep in p]:
647 if [p for p in pats if os.sep in p]:
648 ui.warn("error: patterns may not contain '%s'\n" % os.sep)
648 ui.warn("error: patterns may not contain '%s'\n" % os.sep)
649 ui.warn("use '-i <dir>' instead\n")
649 ui.warn("use '-i <dir>' instead\n")
650 sys.exit(1)
650 sys.exit(1)
651 def compile(pats, head = '^', tail = os.sep, on_empty = True):
651 def compile(pats, head = '^', tail = os.sep, on_empty = True):
652 if not pats:
652 if not pats:
653 class c:
653 class c:
654 def match(self, x): return on_empty
654 def match(self, x): return on_empty
655 return c()
655 return c()
656 regexp = r'%s(?:%s)%s' % (
656 regexp = r'%s(?:%s)%s' % (
657 head,
657 head,
658 '|'.join([fnmatch.translate(os.path.normpath(os.path.normcase(p)))[:-1]
658 '|'.join([fnmatch.translate(os.path.normpath(os.path.normcase(p)))[:-1]
659 for p in pats]),
659 for p in pats]),
660 tail)
660 tail)
661 return re.compile(regexp)
661 return re.compile(regexp)
662 exclude = compile(opts['exclude'], on_empty = False)
662 exclude = compile(opts['exclude'], on_empty = False)
663 include = compile(opts['include'])
663 include = compile(opts['include'])
664 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 = '$')
665 end = '\n'
665 end = '\n'
666 if opts['print0']: end = '\0'
666 if opts['print0']: end = '\0'
667 if opts['rev']: node = repo.manifest.lookup(opts['rev'])
667 if opts['rev']: node = repo.manifest.lookup(opts['rev'])
668 else: node = repo.manifest.tip()
668 else: node = repo.manifest.tip()
669 manifest = repo.manifest.read(node)
669 manifest = repo.manifest.read(node)
670 cwd = repo.getcwd()
670 cwd = repo.getcwd()
671 cwd_plus = cwd and (cwd + os.sep)
671 cwd_plus = cwd and (cwd + os.sep)
672 found = []
672 found = []
673 for f in manifest:
673 for f in manifest:
674 f = os.path.normcase(f)
674 f = os.path.normcase(f)
675 if exclude.match(f) or not(include.match(f) and
675 if exclude.match(f) or not(include.match(f) and
676 f.startswith(cwd_plus) and
676 f.startswith(cwd_plus) and
677 pat.match(os.path.basename(f))): continue
677 pat.match(os.path.basename(f))): continue
678 if opts['fullpath']: f = os.path.join(repo.root, f)
678 if opts['fullpath']: f = os.path.join(repo.root, f)
679 elif cwd: f = f[len(cwd_plus):]
679 elif cwd: f = f[len(cwd_plus):]
680 found.append(f)
680 found.append(f)
681 found.sort()
681 found.sort()
682 for f in found: ui.write(f, end)
682 for f in found: ui.write(f, end)
683
683
684 def log(ui, repo, f=None, **opts):
684 def log(ui, repo, f=None, **opts):
685 """show the revision history of the repository or a single file"""
685 """show the revision history of the repository or a single file"""
686 if f:
686 if f:
687 files = relpath(repo, [f])
687 files = relpath(repo, [f])
688 filelog = repo.file(files[0])
688 filelog = repo.file(files[0])
689 log = filelog
689 log = filelog
690 lookup = filelog.lookup
690 lookup = filelog.lookup
691 else:
691 else:
692 files = None
692 files = None
693 filelog = None
693 filelog = None
694 log = repo.changelog
694 log = repo.changelog
695 lookup = repo.lookup
695 lookup = repo.lookup
696 revlist = []
696 revlist = []
697 revs = [log.rev(lookup(rev)) for rev in opts['rev']]
697 revs = [log.rev(lookup(rev)) for rev in opts['rev']]
698 while revs:
698 while revs:
699 if len(revs) == 1:
699 if len(revs) == 1:
700 revlist.append(revs.pop(0))
700 revlist.append(revs.pop(0))
701 else:
701 else:
702 a = revs.pop(0)
702 a = revs.pop(0)
703 b = revs.pop(0)
703 b = revs.pop(0)
704 off = a > b and -1 or 1
704 off = a > b and -1 or 1
705 revlist.extend(range(a, b + off, off))
705 revlist.extend(range(a, b + off, off))
706
706
707 for i in revlist or range(log.count() - 1, -1, -1):
707 for i in revlist or range(log.count() - 1, -1, -1):
708 show_changeset(ui, repo, filelog=filelog, rev=i)
708 show_changeset(ui, repo, filelog=filelog, rev=i)
709 if opts['patch']:
709 if opts['patch']:
710 if filelog:
710 if filelog:
711 filenode = filelog.node(i)
711 filenode = filelog.node(i)
712 i = filelog.linkrev(filenode)
712 i = filelog.linkrev(filenode)
713 changenode = repo.changelog.node(i)
713 changenode = repo.changelog.node(i)
714 prev, other = repo.changelog.parents(changenode)
714 prev, other = repo.changelog.parents(changenode)
715 dodiff(sys.stdout, ui, repo, files, prev, changenode)
715 dodiff(sys.stdout, ui, repo, files, prev, changenode)
716 ui.write("\n")
716 ui.write("\n")
717 ui.write("\n")
717 ui.write("\n")
718
718
719 def manifest(ui, repo, rev = []):
719 def manifest(ui, repo, rev = []):
720 """output the latest or given revision of the project manifest"""
720 """output the latest or given revision of the project manifest"""
721 n = repo.manifest.tip()
721 n = repo.manifest.tip()
722 if rev:
722 if rev:
723 n = repo.manifest.lookup(rev)
723 n = repo.manifest.lookup(rev)
724 m = repo.manifest.read(n)
724 m = repo.manifest.read(n)
725 mf = repo.manifest.readflags(n)
725 mf = repo.manifest.readflags(n)
726 files = m.keys()
726 files = m.keys()
727 files.sort()
727 files.sort()
728
728
729 for f in files:
729 for f in files:
730 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))
731
731
732 def parents(ui, repo, node = None):
732 def parents(ui, repo, node = None):
733 '''show the parents of the current working dir'''
733 '''show the parents of the current working dir'''
734 if node:
734 if node:
735 p = repo.changelog.parents(repo.lookup(hg.bin(node)))
735 p = repo.changelog.parents(repo.lookup(hg.bin(node)))
736 else:
736 else:
737 p = repo.dirstate.parents()
737 p = repo.dirstate.parents()
738
738
739 for n in p:
739 for n in p:
740 if n != hg.nullid:
740 if n != hg.nullid:
741 show_changeset(ui, repo, changenode=n)
741 show_changeset(ui, repo, changenode=n)
742
742
743 def pull(ui, repo, source="default", **opts):
743 def pull(ui, repo, source="default", **opts):
744 """pull changes from the specified source"""
744 """pull changes from the specified source"""
745 source = ui.expandpath(source)
745 source = ui.expandpath(source)
746 ui.status('pulling from %s\n' % (source))
746 ui.status('pulling from %s\n' % (source))
747
747
748 other = hg.repository(ui, source)
748 other = hg.repository(ui, source)
749 r = repo.pull(other)
749 r = repo.pull(other)
750 if not r:
750 if not r:
751 if opts['update']:
751 if opts['update']:
752 return update(ui, repo)
752 return update(ui, repo)
753 else:
753 else:
754 ui.status("(run 'hg update' to get a working copy)\n")
754 ui.status("(run 'hg update' to get a working copy)\n")
755
755
756 return r
756 return r
757
757
758 def push(ui, repo, dest="default-push"):
758 def push(ui, repo, dest="default-push"):
759 """push changes to the specified destination"""
759 """push changes to the specified destination"""
760 dest = ui.expandpath(dest)
760 dest = ui.expandpath(dest)
761
761
762 if not dest.startswith("ssh://"):
762 if not dest.startswith("ssh://"):
763 ui.warn("abort: can only push to ssh:// destinations currently\n")
763 ui.warn("abort: can only push to ssh:// destinations currently\n")
764 return 1
764 return 1
765
765
766 m = re.match(r'ssh://(([^@]+)@)?([^:/]+)(:(\d+))?(/(.*))?', dest)
766 m = re.match(r'ssh://(([^@]+)@)?([^:/]+)(:(\d+))?(/(.*))?', dest)
767 if not m:
767 if not m:
768 ui.warn("abort: couldn't parse destination %s\n" % dest)
768 ui.warn("abort: couldn't parse destination %s\n" % dest)
769 return 1
769 return 1
770
770
771 user, host, port, path = map(m.group, (2, 3, 5, 7))
771 user, host, port, path = map(m.group, (2, 3, 5, 7))
772 uhost = user and ("%s@%s" % (user, host)) or host
772 uhost = user and ("%s@%s" % (user, host)) or host
773 port = port and (" -p %s") % port or ""
773 port = port and (" -p %s") % port or ""
774 path = path or ""
774 path = path or ""
775
775
776 sport = random.randrange(30000, 60000)
776 sport = random.randrange(30000, 60000)
777 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/'"
778 cmd = cmd % (uhost, port, sport+1, sport, path, sport+1)
778 cmd = cmd % (uhost, port, sport+1, sport, path, sport+1)
779
779
780 child = os.fork()
780 child = os.fork()
781 if not child:
781 if not child:
782 sys.stdout = file("/dev/null", "w")
782 sys.stdout = file("/dev/null", "w")
783 sys.stderr = sys.stdout
783 sys.stderr = sys.stdout
784 hgweb.server(repo.root, "pull", "", "localhost", sport)
784 hgweb.server(repo.root, "pull", "", "localhost", sport)
785 else:
785 else:
786 ui.status("connecting to %s\n" % host)
786 ui.status("connecting to %s\n" % host)
787 r = os.system(cmd)
787 r = os.system(cmd)
788 os.kill(child, signal.SIGTERM)
788 os.kill(child, signal.SIGTERM)
789 return r
789 return r
790
790
791 def rawcommit(ui, repo, *flist, **rc):
791 def rawcommit(ui, repo, *flist, **rc):
792 "raw commit interface"
792 "raw commit interface"
793
793
794 text = rc['text']
794 text = rc['text']
795 if not text and rc['logfile']:
795 if not text and rc['logfile']:
796 try: text = open(rc['logfile']).read()
796 try: text = open(rc['logfile']).read()
797 except IOError: pass
797 except IOError: pass
798 if not text and not rc['logfile']:
798 if not text and not rc['logfile']:
799 ui.warn("abort: missing commit text\n")
799 ui.warn("abort: missing commit text\n")
800 return 1
800 return 1
801
801
802 files = relpath(repo, list(flist))
802 files = relpath(repo, list(flist))
803 if rc['files']:
803 if rc['files']:
804 files += open(rc['files']).read().splitlines()
804 files += open(rc['files']).read().splitlines()
805
805
806 rc['parent'] = map(repo.lookup, rc['parent'])
806 rc['parent'] = map(repo.lookup, rc['parent'])
807
807
808 repo.rawcommit(files, text, rc['user'], rc['date'], *rc['parent'])
808 repo.rawcommit(files, text, rc['user'], rc['date'], *rc['parent'])
809
809
810 def recover(ui, repo):
810 def recover(ui, repo):
811 """roll back an interrupted transaction"""
811 """roll back an interrupted transaction"""
812 repo.recover()
812 repo.recover()
813
813
814 def remove(ui, repo, file, *files):
814 def remove(ui, repo, file, *files):
815 """remove the specified files on the next commit"""
815 """remove the specified files on the next commit"""
816 repo.remove(relpath(repo, (file,) + files))
816 repo.remove(relpath(repo, (file,) + files))
817
817
818 def revert(ui, repo, *names, **opts):
818 def revert(ui, repo, *names, **opts):
819 """revert modified files or dirs back to their unmodified states"""
819 """revert modified files or dirs back to their unmodified states"""
820 node = opts['rev'] and repo.lookup(opts['rev']) or \
820 node = opts['rev'] and repo.lookup(opts['rev']) or \
821 repo.dirstate.parents()[0]
821 repo.dirstate.parents()[0]
822 root = os.path.realpath(repo.root)
822 root = os.path.realpath(repo.root)
823
823
824 def trimpath(p):
824 def trimpath(p):
825 p = os.path.realpath(p)
825 p = os.path.realpath(p)
826 if p.startswith(root):
826 if p.startswith(root):
827 rest = p[len(root):]
827 rest = p[len(root):]
828 if not rest:
828 if not rest:
829 return rest
829 return rest
830 if p.startswith(os.sep):
830 if p.startswith(os.sep):
831 return rest[1:]
831 return rest[1:]
832 return p
832 return p
833
833
834 relnames = map(trimpath, names or [os.getcwd()])
834 relnames = map(trimpath, names or [os.getcwd()])
835 chosen = {}
835 chosen = {}
836
836
837 def choose(name):
837 def choose(name):
838 def body(name):
838 def body(name):
839 for r in relnames:
839 for r in relnames:
840 if not name.startswith(r): continue
840 if not name.startswith(r): continue
841 rest = name[len(r):]
841 rest = name[len(r):]
842 if not rest: return r, True
842 if not rest: return r, True
843 depth = rest.count(os.sep)
843 depth = rest.count(os.sep)
844 if not r:
844 if not r:
845 if depth == 0 or not opts['nonrecursive']: return r, True
845 if depth == 0 or not opts['nonrecursive']: return r, True
846 elif rest[0] == os.sep:
846 elif rest[0] == os.sep:
847 if depth == 1 or not opts['nonrecursive']: return r, True
847 if depth == 1 or not opts['nonrecursive']: return r, True
848 return None, False
848 return None, False
849 relname, ret = body(name)
849 relname, ret = body(name)
850 if ret:
850 if ret:
851 chosen[relname] = 1
851 chosen[relname] = 1
852 return ret
852 return ret
853
853
854 r = repo.update(node, False, True, choose, False)
854 r = repo.update(node, False, True, choose, False)
855 for n in relnames:
855 for n in relnames:
856 if n not in chosen:
856 if n not in chosen:
857 ui.warn('error: no matches for %s\n' % n)
857 ui.warn('error: no matches for %s\n' % n)
858 r = 1
858 r = 1
859 sys.stdout.flush()
859 sys.stdout.flush()
860 return r
860 return r
861
861
862 def root(ui, repo):
862 def root(ui, repo):
863 """print the root (top) of the current working dir"""
863 """print the root (top) of the current working dir"""
864 ui.write(repo.root + "\n")
864 ui.write(repo.root + "\n")
865
865
866 def serve(ui, repo, **opts):
866 def serve(ui, repo, **opts):
867 """export the repository via HTTP"""
867 """export the repository via HTTP"""
868
868
869 if opts["stdio"]:
869 if opts["stdio"]:
870 fin, fout = sys.stdin, sys.stdout
870 fin, fout = sys.stdin, sys.stdout
871 sys.stdout = sys.stderr
871 sys.stdout = sys.stderr
872
872
873 def getarg():
873 def getarg():
874 argline = fin.readline()[:-1]
874 argline = fin.readline()[:-1]
875 arg, l = argline.split()
875 arg, l = argline.split()
876 val = fin.read(int(l))
876 val = fin.read(int(l))
877 return arg, val
877 return arg, val
878 def respond(v):
878 def respond(v):
879 fout.write("%d\n" % len(v))
879 fout.write("%d\n" % len(v))
880 fout.write(v)
880 fout.write(v)
881 fout.flush()
881 fout.flush()
882
882
883 lock = None
884
883 while 1:
885 while 1:
884 cmd = fin.readline()[:-1]
886 cmd = fin.readline()[:-1]
885 if cmd == '':
887 if cmd == '':
886 return
888 return
887 if cmd == "heads":
889 if cmd == "heads":
888 h = repo.heads()
890 h = repo.heads()
889 respond(" ".join(map(hg.hex, h)) + "\n")
891 respond(" ".join(map(hg.hex, h)) + "\n")
892 if cmd == "lock":
893 lock = repo.lock()
894 respond("")
895 if cmd == "unlock":
896 if lock: lock.release()
897 lock = None
898 respond("")
890 elif cmd == "branches":
899 elif cmd == "branches":
891 arg, nodes = getarg()
900 arg, nodes = getarg()
892 nodes = map(hg.bin, nodes.split(" "))
901 nodes = map(hg.bin, nodes.split(" "))
893 r = []
902 r = []
894 for b in repo.branches(nodes):
903 for b in repo.branches(nodes):
895 r.append(" ".join(map(hg.hex, b)) + "\n")
904 r.append(" ".join(map(hg.hex, b)) + "\n")
896 respond("".join(r))
905 respond("".join(r))
897 elif cmd == "between":
906 elif cmd == "between":
898 arg, pairs = getarg()
907 arg, pairs = getarg()
899 pairs = [ map(hg.bin, p.split("-")) for p in pairs.split(" ") ]
908 pairs = [ map(hg.bin, p.split("-")) for p in pairs.split(" ") ]
900 r = []
909 r = []
901 for b in repo.between(pairs):
910 for b in repo.between(pairs):
902 r.append(" ".join(map(hg.hex, b)) + "\n")
911 r.append(" ".join(map(hg.hex, b)) + "\n")
903 respond("".join(r))
912 respond("".join(r))
904 elif cmd == "changegroup":
913 elif cmd == "changegroup":
905 nodes = []
914 nodes = []
906 arg, roots = getarg()
915 arg, roots = getarg()
907 nodes = map(hg.bin, roots.split(" "))
916 nodes = map(hg.bin, roots.split(" "))
908
917
909 cg = repo.changegroup(nodes)
918 cg = repo.changegroup(nodes)
910 while 1:
919 while 1:
911 d = cg.read(4096)
920 d = cg.read(4096)
912 if not d: break
921 if not d: break
913 fout.write(d)
922 fout.write(d)
914
923
915 out.flush()
924 out.flush()
916
925
917 def openlog(opt, default):
926 def openlog(opt, default):
918 if opts[opt] and opts[opt] != '-': return open(opts[opt], 'w')
927 if opts[opt] and opts[opt] != '-': return open(opts[opt], 'w')
919 else: return default
928 else: return default
920
929
921 httpd = hgweb.create_server(repo.root, opts["name"], opts["templates"],
930 httpd = hgweb.create_server(repo.root, opts["name"], opts["templates"],
922 opts["address"], opts["port"],
931 opts["address"], opts["port"],
923 openlog('accesslog', sys.stdout),
932 openlog('accesslog', sys.stdout),
924 openlog('errorlog', sys.stderr))
933 openlog('errorlog', sys.stderr))
925 if ui.verbose:
934 if ui.verbose:
926 addr, port = httpd.socket.getsockname()
935 addr, port = httpd.socket.getsockname()
927 if addr == '0.0.0.0':
936 if addr == '0.0.0.0':
928 addr = socket.gethostname()
937 addr = socket.gethostname()
929 else:
938 else:
930 try:
939 try:
931 addr = socket.gethostbyaddr(addr)[0]
940 addr = socket.gethostbyaddr(addr)[0]
932 except: pass
941 except: pass
933 if port != 80:
942 if port != 80:
934 ui.status('listening at http://%s:%d/\n' % (addr, port))
943 ui.status('listening at http://%s:%d/\n' % (addr, port))
935 else:
944 else:
936 ui.status('listening at http://%s/\n' % addr)
945 ui.status('listening at http://%s/\n' % addr)
937 httpd.serve_forever()
946 httpd.serve_forever()
938
947
939 def status(ui, repo):
948 def status(ui, repo):
940 '''show changed files in the working directory
949 '''show changed files in the working directory
941
950
942 C = changed
951 C = changed
943 A = added
952 A = added
944 R = removed
953 R = removed
945 ? = not tracked'''
954 ? = not tracked'''
946
955
947 (c, a, d, u) = repo.changes(None, None)
956 (c, a, d, u) = repo.changes(None, None)
948 (c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u))
957 (c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u))
949
958
950 for f in c: ui.write("C ", f, "\n")
959 for f in c: ui.write("C ", f, "\n")
951 for f in a: ui.write("A ", f, "\n")
960 for f in a: ui.write("A ", f, "\n")
952 for f in d: ui.write("R ", f, "\n")
961 for f in d: ui.write("R ", f, "\n")
953 for f in u: ui.write("? ", f, "\n")
962 for f in u: ui.write("? ", f, "\n")
954
963
955 def tag(ui, repo, name, rev = None, **opts):
964 def tag(ui, repo, name, rev = None, **opts):
956 """add a tag for the current tip or a given revision"""
965 """add a tag for the current tip or a given revision"""
957
966
958 if name == "tip":
967 if name == "tip":
959 ui.warn("abort: 'tip' is a reserved name!\n")
968 ui.warn("abort: 'tip' is a reserved name!\n")
960 return -1
969 return -1
961 if rev:
970 if rev:
962 r = hg.hex(repo.lookup(rev))
971 r = hg.hex(repo.lookup(rev))
963 else:
972 else:
964 r = hg.hex(repo.changelog.tip())
973 r = hg.hex(repo.changelog.tip())
965
974
966 if name.find(revrangesep) >= 0:
975 if name.find(revrangesep) >= 0:
967 ui.warn("abort: '%s' cannot be used in a tag name\n" % revrangesep)
976 ui.warn("abort: '%s' cannot be used in a tag name\n" % revrangesep)
968 return -1
977 return -1
969
978
970 if opts['local']:
979 if opts['local']:
971 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
980 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
972 return
981 return
973
982
974 (c, a, d, u) = repo.changes(None, None)
983 (c, a, d, u) = repo.changes(None, None)
975 for x in (c, a, d, u):
984 for x in (c, a, d, u):
976 if ".hgtags" in x:
985 if ".hgtags" in x:
977 ui.warn("abort: working copy of .hgtags is changed!\n")
986 ui.warn("abort: working copy of .hgtags is changed!\n")
978 ui.status("(please commit .hgtags manually)\n")
987 ui.status("(please commit .hgtags manually)\n")
979 return -1
988 return -1
980
989
981 add = 0
990 add = 0
982 if not os.path.exists(repo.wjoin(".hgtags")): add = 1
991 if not os.path.exists(repo.wjoin(".hgtags")): add = 1
983 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
992 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
984 if add: repo.add([".hgtags"])
993 if add: repo.add([".hgtags"])
985
994
986 if not opts['text']:
995 if not opts['text']:
987 opts['text'] = "Added tag %s for changeset %s" % (name, r)
996 opts['text'] = "Added tag %s for changeset %s" % (name, r)
988
997
989 repo.commit([".hgtags"], opts['text'], opts['user'], opts['date'])
998 repo.commit([".hgtags"], opts['text'], opts['user'], opts['date'])
990
999
991 def tags(ui, repo):
1000 def tags(ui, repo):
992 """list repository tags"""
1001 """list repository tags"""
993
1002
994 l = repo.tagslist()
1003 l = repo.tagslist()
995 l.reverse()
1004 l.reverse()
996 for t, n in l:
1005 for t, n in l:
997 try:
1006 try:
998 r = "%5d:%s" % (repo.changelog.rev(n), hg.hex(n))
1007 r = "%5d:%s" % (repo.changelog.rev(n), hg.hex(n))
999 except KeyError:
1008 except KeyError:
1000 r = " ?:?"
1009 r = " ?:?"
1001 ui.write("%-30s %s\n" % (t, r))
1010 ui.write("%-30s %s\n" % (t, r))
1002
1011
1003 def tip(ui, repo):
1012 def tip(ui, repo):
1004 """show the tip revision"""
1013 """show the tip revision"""
1005 n = repo.changelog.tip()
1014 n = repo.changelog.tip()
1006 show_changeset(ui, repo, changenode=n)
1015 show_changeset(ui, repo, changenode=n)
1007
1016
1008 def undo(ui, repo):
1017 def undo(ui, repo):
1009 """undo the last commit or pull
1018 """undo the last commit or pull
1010
1019
1011 Roll back the last pull or commit transaction on the
1020 Roll back the last pull or commit transaction on the
1012 repository, restoring the project to its earlier state.
1021 repository, restoring the project to its earlier state.
1013
1022
1014 This command should be used with care. There is only one level of
1023 This command should be used with care. There is only one level of
1015 undo and there is no redo.
1024 undo and there is no redo.
1016
1025
1017 This command is not intended for use on public repositories. Once
1026 This command is not intended for use on public repositories. Once
1018 a change is visible for pull by other users, undoing it locally is
1027 a change is visible for pull by other users, undoing it locally is
1019 ineffective.
1028 ineffective.
1020 """
1029 """
1021 repo.undo()
1030 repo.undo()
1022
1031
1023 def update(ui, repo, node=None, merge=False, clean=False):
1032 def update(ui, repo, node=None, merge=False, clean=False):
1024 '''update or merge working directory
1033 '''update or merge working directory
1025
1034
1026 If there are no outstanding changes in the working directory and
1035 If there are no outstanding changes in the working directory and
1027 there is a linear relationship between the current version and the
1036 there is a linear relationship between the current version and the
1028 requested version, the result is the requested version.
1037 requested version, the result is the requested version.
1029
1038
1030 Otherwise the result is a merge between the contents of the
1039 Otherwise the result is a merge between the contents of the
1031 current working directory and the requested version. Files that
1040 current working directory and the requested version. Files that
1032 changed between either parent are marked as changed for the next
1041 changed between either parent are marked as changed for the next
1033 commit and a commit must be performed before any further updates
1042 commit and a commit must be performed before any further updates
1034 are allowed.
1043 are allowed.
1035 '''
1044 '''
1036 node = node and repo.lookup(node) or repo.changelog.tip()
1045 node = node and repo.lookup(node) or repo.changelog.tip()
1037 return repo.update(node, allow=merge, force=clean)
1046 return repo.update(node, allow=merge, force=clean)
1038
1047
1039 def verify(ui, repo):
1048 def verify(ui, repo):
1040 """verify the integrity of the repository"""
1049 """verify the integrity of the repository"""
1041 return repo.verify()
1050 return repo.verify()
1042
1051
1043 # Command options and aliases are listed here, alphabetically
1052 # Command options and aliases are listed here, alphabetically
1044
1053
1045 table = {
1054 table = {
1046 "^add": (add, [], "hg add [files]"),
1055 "^add": (add, [], "hg add [files]"),
1047 "addremove": (addremove, [], "hg addremove [files]"),
1056 "addremove": (addremove, [], "hg addremove [files]"),
1048 "^annotate": (annotate,
1057 "^annotate": (annotate,
1049 [('r', 'revision', '', 'revision'),
1058 [('r', 'revision', '', 'revision'),
1050 ('u', 'user', None, 'show user'),
1059 ('u', 'user', None, 'show user'),
1051 ('n', 'number', None, 'show revision number'),
1060 ('n', 'number', None, 'show revision number'),
1052 ('c', 'changeset', None, 'show changeset')],
1061 ('c', 'changeset', None, 'show changeset')],
1053 'hg annotate [-u] [-c] [-n] [-r id] [files]'),
1062 'hg annotate [-u] [-c] [-n] [-r id] [files]'),
1054 "cat": (cat, [('o', 'output', "", 'output to file')], 'hg cat [-o outfile] file> [rev]'),
1063 "cat": (cat, [('o', 'output', "", 'output to file')], 'hg cat [-o outfile] file> [rev]'),
1055 "^clone": (clone, [('U', 'noupdate', None, 'skip update after cloning')],
1064 "^clone": (clone, [('U', 'noupdate', None, 'skip update after cloning')],
1056 'hg clone [options] <source> [dest]'),
1065 'hg clone [options] <source> [dest]'),
1057 "^commit|ci": (commit,
1066 "^commit|ci": (commit,
1058 [('t', 'text', "", 'commit text'),
1067 [('t', 'text', "", 'commit text'),
1059 ('A', 'addremove', None, 'run add/remove during commit'),
1068 ('A', 'addremove', None, 'run add/remove during commit'),
1060 ('l', 'logfile', "", 'commit text file'),
1069 ('l', 'logfile', "", 'commit text file'),
1061 ('d', 'date', "", 'date code'),
1070 ('d', 'date', "", 'date code'),
1062 ('u', 'user', "", 'user')],
1071 ('u', 'user', "", 'user')],
1063 'hg commit [files]'),
1072 'hg commit [files]'),
1064 "copy": (copy, [], 'hg copy <source> <dest>'),
1073 "copy": (copy, [], 'hg copy <source> <dest>'),
1065 "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
1074 "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
1066 "debugstate": (debugstate, [], 'debugstate'),
1075 "debugstate": (debugstate, [], 'debugstate'),
1067 "debugindex": (debugindex, [], 'debugindex <file>'),
1076 "debugindex": (debugindex, [], 'debugindex <file>'),
1068 "debugindexdot": (debugindexdot, [], 'debugindexdot <file>'),
1077 "debugindexdot": (debugindexdot, [], 'debugindexdot <file>'),
1069 "^diff": (diff, [('r', 'rev', [], 'revision')],
1078 "^diff": (diff, [('r', 'rev', [], 'revision')],
1070 'hg diff [-r A] [-r B] [files]'),
1079 'hg diff [-r A] [-r B] [files]'),
1071 "^export": (export, [('o', 'output', "", 'output to file')],
1080 "^export": (export, [('o', 'output', "", 'output to file')],
1072 "hg export [-o file] <changeset> ..."),
1081 "hg export [-o file] <changeset> ..."),
1073 "forget": (forget, [], "hg forget [files]"),
1082 "forget": (forget, [], "hg forget [files]"),
1074 "heads": (heads, [], 'hg heads'),
1083 "heads": (heads, [], 'hg heads'),
1075 "help": (help, [], 'hg help [command]'),
1084 "help": (help, [], 'hg help [command]'),
1076 "identify|id": (identify, [], 'hg identify'),
1085 "identify|id": (identify, [], 'hg identify'),
1077 "import|patch": (import_,
1086 "import|patch": (import_,
1078 [('p', 'strip', 1, 'path strip'),
1087 [('p', 'strip', 1, 'path strip'),
1079 ('b', 'base', "", 'base path')],
1088 ('b', 'base', "", 'base path')],
1080 "hg import [options] <patches>"),
1089 "hg import [options] <patches>"),
1081 "^init": (init, [], 'hg init'),
1090 "^init": (init, [], 'hg init'),
1082 "locate": (locate,
1091 "locate": (locate,
1083 [('0', 'print0', None, 'end records with NUL'),
1092 [('0', 'print0', None, 'end records with NUL'),
1084 ('f', 'fullpath', None, 'print complete paths'),
1093 ('f', 'fullpath', None, 'print complete paths'),
1085 ('i', 'include', [], 'include path in search'),
1094 ('i', 'include', [], 'include path in search'),
1086 ('r', 'rev', '', 'revision'),
1095 ('r', 'rev', '', 'revision'),
1087 ('x', 'exclude', [], 'exclude path from search')],
1096 ('x', 'exclude', [], 'exclude path from search')],
1088 'hg locate [options] [files]'),
1097 'hg locate [options] [files]'),
1089 "^log|history": (log,
1098 "^log|history": (log,
1090 [('r', 'rev', [], 'revision'),
1099 [('r', 'rev', [], 'revision'),
1091 ('p', 'patch', None, 'show patch')],
1100 ('p', 'patch', None, 'show patch')],
1092 'hg log [-r A] [-r B] [-p] [file]'),
1101 'hg log [-r A] [-r B] [-p] [file]'),
1093 "manifest": (manifest, [], 'hg manifest [rev]'),
1102 "manifest": (manifest, [], 'hg manifest [rev]'),
1094 "parents": (parents, [], 'hg parents [node]'),
1103 "parents": (parents, [], 'hg parents [node]'),
1095 "^pull": (pull,
1104 "^pull": (pull,
1096 [('u', 'update', None, 'update working directory')],
1105 [('u', 'update', None, 'update working directory')],
1097 'hg pull [options] [source]'),
1106 'hg pull [options] [source]'),
1098 "^push": (push, [], 'hg push <destination>'),
1107 "^push": (push, [], 'hg push <destination>'),
1099 "rawcommit": (rawcommit,
1108 "rawcommit": (rawcommit,
1100 [('p', 'parent', [], 'parent'),
1109 [('p', 'parent', [], 'parent'),
1101 ('d', 'date', "", 'date code'),
1110 ('d', 'date', "", 'date code'),
1102 ('u', 'user', "", 'user'),
1111 ('u', 'user', "", 'user'),
1103 ('F', 'files', "", 'file list'),
1112 ('F', 'files', "", 'file list'),
1104 ('t', 'text', "", 'commit text'),
1113 ('t', 'text', "", 'commit text'),
1105 ('l', 'logfile', "", 'commit text file')],
1114 ('l', 'logfile', "", 'commit text file')],
1106 'hg rawcommit [options] [files]'),
1115 'hg rawcommit [options] [files]'),
1107 "recover": (recover, [], "hg recover"),
1116 "recover": (recover, [], "hg recover"),
1108 "^remove|rm": (remove, [], "hg remove [files]"),
1117 "^remove|rm": (remove, [], "hg remove [files]"),
1109 "^revert": (revert,
1118 "^revert": (revert,
1110 [("n", "nonrecursive", None, "don't recurse into subdirs"),
1119 [("n", "nonrecursive", None, "don't recurse into subdirs"),
1111 ("r", "rev", "", "revision")],
1120 ("r", "rev", "", "revision")],
1112 "hg revert [files|dirs]"),
1121 "hg revert [files|dirs]"),
1113 "root": (root, [], "hg root"),
1122 "root": (root, [], "hg root"),
1114 "^serve": (serve, [('A', 'accesslog', '', 'access log file'),
1123 "^serve": (serve, [('A', 'accesslog', '', 'access log file'),
1115 ('E', 'errorlog', '', 'error log file'),
1124 ('E', 'errorlog', '', 'error log file'),
1116 ('p', 'port', 8000, 'listen port'),
1125 ('p', 'port', 8000, 'listen port'),
1117 ('a', 'address', '', 'interface address'),
1126 ('a', 'address', '', 'interface address'),
1118 ('n', 'name', os.getcwd(), 'repository name'),
1127 ('n', 'name', os.getcwd(), 'repository name'),
1119 ('', 'stdio', None, 'for remote clients'),
1128 ('', 'stdio', None, 'for remote clients'),
1120 ('t', 'templates', "", 'template map')],
1129 ('t', 'templates', "", 'template map')],
1121 "hg serve [options]"),
1130 "hg serve [options]"),
1122 "^status": (status, [], 'hg status'),
1131 "^status": (status, [], 'hg status'),
1123 "tag": (tag, [('l', 'local', None, 'make the tag local'),
1132 "tag": (tag, [('l', 'local', None, 'make the tag local'),
1124 ('t', 'text', "", 'commit text'),
1133 ('t', 'text', "", 'commit text'),
1125 ('d', 'date', "", 'date code'),
1134 ('d', 'date', "", 'date code'),
1126 ('u', 'user', "", 'user')],
1135 ('u', 'user', "", 'user')],
1127 'hg tag [options] <name> [rev]'),
1136 'hg tag [options] <name> [rev]'),
1128 "tags": (tags, [], 'hg tags'),
1137 "tags": (tags, [], 'hg tags'),
1129 "tip": (tip, [], 'hg tip'),
1138 "tip": (tip, [], 'hg tip'),
1130 "undo": (undo, [], 'hg undo'),
1139 "undo": (undo, [], 'hg undo'),
1131 "^update|up|checkout|co":
1140 "^update|up|checkout|co":
1132 (update,
1141 (update,
1133 [('m', 'merge', None, 'allow merging of conflicts'),
1142 [('m', 'merge', None, 'allow merging of conflicts'),
1134 ('C', 'clean', None, 'overwrite locally modified files')],
1143 ('C', 'clean', None, 'overwrite locally modified files')],
1135 'hg update [options] [node]'),
1144 'hg update [options] [node]'),
1136 "verify": (verify, [], 'hg verify'),
1145 "verify": (verify, [], 'hg verify'),
1137 "version": (show_version, [], 'hg version'),
1146 "version": (show_version, [], 'hg version'),
1138 }
1147 }
1139
1148
1140 globalopts = [('v', 'verbose', None, 'verbose'),
1149 globalopts = [('v', 'verbose', None, 'verbose'),
1141 ('', 'debug', None, 'debug'),
1150 ('', 'debug', None, 'debug'),
1142 ('q', 'quiet', None, 'quiet'),
1151 ('q', 'quiet', None, 'quiet'),
1143 ('', 'profile', None, 'profile'),
1152 ('', 'profile', None, 'profile'),
1144 ('R', 'repository', "", 'repository root directory'),
1153 ('R', 'repository', "", 'repository root directory'),
1145 ('', 'traceback', None, 'print traceback on exception'),
1154 ('', 'traceback', None, 'print traceback on exception'),
1146 ('y', 'noninteractive', None, 'run non-interactively'),
1155 ('y', 'noninteractive', None, 'run non-interactively'),
1147 ('', 'version', None, 'output version information and exit'),
1156 ('', 'version', None, 'output version information and exit'),
1148 ]
1157 ]
1149
1158
1150 norepo = "clone init version help debugindex debugindexdot"
1159 norepo = "clone init version help debugindex debugindexdot"
1151
1160
1152 def find(cmd):
1161 def find(cmd):
1153 for e in table.keys():
1162 for e in table.keys():
1154 if re.match("(%s)$" % e, cmd):
1163 if re.match("(%s)$" % e, cmd):
1155 return table[e]
1164 return table[e]
1156
1165
1157 raise UnknownCommand(cmd)
1166 raise UnknownCommand(cmd)
1158
1167
1159 class SignalInterrupt(Exception): pass
1168 class SignalInterrupt(Exception): pass
1160
1169
1161 def catchterm(*args):
1170 def catchterm(*args):
1162 raise SignalInterrupt
1171 raise SignalInterrupt
1163
1172
1164 def run():
1173 def run():
1165 sys.exit(dispatch(sys.argv[1:]))
1174 sys.exit(dispatch(sys.argv[1:]))
1166
1175
1167 class ParseError(Exception): pass
1176 class ParseError(Exception): pass
1168
1177
1169 def parse(args):
1178 def parse(args):
1170 options = {}
1179 options = {}
1171 cmdoptions = {}
1180 cmdoptions = {}
1172
1181
1173 try:
1182 try:
1174 args = fancyopts.fancyopts(args, globalopts, options)
1183 args = fancyopts.fancyopts(args, globalopts, options)
1175 except fancyopts.getopt.GetoptError, inst:
1184 except fancyopts.getopt.GetoptError, inst:
1176 raise ParseError(None, inst)
1185 raise ParseError(None, inst)
1177
1186
1178 if options["version"]:
1187 if options["version"]:
1179 return ("version", show_version, [], options, cmdoptions)
1188 return ("version", show_version, [], options, cmdoptions)
1180 elif not args:
1189 elif not args:
1181 return ("help", help, [], options, cmdoptions)
1190 return ("help", help, [], options, cmdoptions)
1182 else:
1191 else:
1183 cmd, args = args[0], args[1:]
1192 cmd, args = args[0], args[1:]
1184
1193
1185 i = find(cmd)
1194 i = find(cmd)
1186
1195
1187 # combine global options into local
1196 # combine global options into local
1188 c = list(i[1])
1197 c = list(i[1])
1189 l = len(c)
1198 l = len(c)
1190 for o in globalopts:
1199 for o in globalopts:
1191 c.append((o[0], o[1], options[o[1]], o[3]))
1200 c.append((o[0], o[1], options[o[1]], o[3]))
1192
1201
1193 try:
1202 try:
1194 args = fancyopts.fancyopts(args, c, cmdoptions)
1203 args = fancyopts.fancyopts(args, c, cmdoptions)
1195 except fancyopts.getopt.GetoptError, inst:
1204 except fancyopts.getopt.GetoptError, inst:
1196 raise ParseError(cmd, inst)
1205 raise ParseError(cmd, inst)
1197
1206
1198 # separate global options back out
1207 # separate global options back out
1199 for o in globalopts:
1208 for o in globalopts:
1200 n = o[1]
1209 n = o[1]
1201 options[n] = cmdoptions[n]
1210 options[n] = cmdoptions[n]
1202 del cmdoptions[n]
1211 del cmdoptions[n]
1203
1212
1204 return (cmd, i[0], args, options, cmdoptions)
1213 return (cmd, i[0], args, options, cmdoptions)
1205
1214
1206 def dispatch(args):
1215 def dispatch(args):
1207 signal.signal(signal.SIGTERM, catchterm)
1216 signal.signal(signal.SIGTERM, catchterm)
1208
1217
1209 try:
1218 try:
1210 cmd, func, args, options, cmdoptions = parse(args)
1219 cmd, func, args, options, cmdoptions = parse(args)
1211 except ParseError, inst:
1220 except ParseError, inst:
1212 u = ui.ui()
1221 u = ui.ui()
1213 if inst.args[0]:
1222 if inst.args[0]:
1214 u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
1223 u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
1215 help(u, inst.args[0])
1224 help(u, inst.args[0])
1216 else:
1225 else:
1217 u.warn("hg: %s\n" % inst.args[1])
1226 u.warn("hg: %s\n" % inst.args[1])
1218 help(u)
1227 help(u)
1219 sys.exit(-1)
1228 sys.exit(-1)
1220 except UnknownCommand, inst:
1229 except UnknownCommand, inst:
1221 u = ui.ui()
1230 u = ui.ui()
1222 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1231 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1223 help(u)
1232 help(u)
1224 sys.exit(1)
1233 sys.exit(1)
1225
1234
1226 u = ui.ui(options["verbose"], options["debug"], options["quiet"],
1235 u = ui.ui(options["verbose"], options["debug"], options["quiet"],
1227 not options["noninteractive"])
1236 not options["noninteractive"])
1228
1237
1229 try:
1238 try:
1230 try:
1239 try:
1231 if cmd not in norepo.split():
1240 if cmd not in norepo.split():
1232 path = options["repository"] or ""
1241 path = options["repository"] or ""
1233 repo = hg.repository(ui=u, path=path)
1242 repo = hg.repository(ui=u, path=path)
1234 d = lambda: func(u, repo, *args, **cmdoptions)
1243 d = lambda: func(u, repo, *args, **cmdoptions)
1235 else:
1244 else:
1236 d = lambda: func(u, *args, **cmdoptions)
1245 d = lambda: func(u, *args, **cmdoptions)
1237
1246
1238 if options['profile']:
1247 if options['profile']:
1239 import hotshot, hotshot.stats
1248 import hotshot, hotshot.stats
1240 prof = hotshot.Profile("hg.prof")
1249 prof = hotshot.Profile("hg.prof")
1241 r = prof.runcall(d)
1250 r = prof.runcall(d)
1242 prof.close()
1251 prof.close()
1243 stats = hotshot.stats.load("hg.prof")
1252 stats = hotshot.stats.load("hg.prof")
1244 stats.strip_dirs()
1253 stats.strip_dirs()
1245 stats.sort_stats('time', 'calls')
1254 stats.sort_stats('time', 'calls')
1246 stats.print_stats(40)
1255 stats.print_stats(40)
1247 return r
1256 return r
1248 else:
1257 else:
1249 return d()
1258 return d()
1250 except:
1259 except:
1251 if options['traceback']:
1260 if options['traceback']:
1252 traceback.print_exc()
1261 traceback.print_exc()
1253 raise
1262 raise
1254 except util.CommandError, inst:
1263 except util.CommandError, inst:
1255 u.warn("abort: %s\n" % inst.args)
1264 u.warn("abort: %s\n" % inst.args)
1256 except hg.RepoError, inst:
1265 except hg.RepoError, inst:
1257 u.warn("abort: ", inst, "!\n")
1266 u.warn("abort: ", inst, "!\n")
1258 except SignalInterrupt:
1267 except SignalInterrupt:
1259 u.warn("killed!\n")
1268 u.warn("killed!\n")
1260 except KeyboardInterrupt:
1269 except KeyboardInterrupt:
1261 u.warn("interrupted!\n")
1270 u.warn("interrupted!\n")
1262 except IOError, inst:
1271 except IOError, inst:
1263 if hasattr(inst, "code"):
1272 if hasattr(inst, "code"):
1264 u.warn("abort: %s\n" % inst)
1273 u.warn("abort: %s\n" % inst)
1265 elif hasattr(inst, "reason"):
1274 elif hasattr(inst, "reason"):
1266 u.warn("abort: error %d: %s\n" % (inst.reason[0], inst.reason[1]))
1275 u.warn("abort: error %d: %s\n" % (inst.reason[0], inst.reason[1]))
1267 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
1276 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
1268 u.warn("broken pipe\n")
1277 u.warn("broken pipe\n")
1269 else:
1278 else:
1270 raise
1279 raise
1271 except OSError, inst:
1280 except OSError, inst:
1272 if hasattr(inst, "filename"):
1281 if hasattr(inst, "filename"):
1273 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
1282 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
1274 else:
1283 else:
1275 u.warn("abort: %s\n" % inst.strerror)
1284 u.warn("abort: %s\n" % inst.strerror)
1276 except TypeError, inst:
1285 except TypeError, inst:
1277 # was this an argument error?
1286 # was this an argument error?
1278 tb = traceback.extract_tb(sys.exc_info()[2])
1287 tb = traceback.extract_tb(sys.exc_info()[2])
1279 if len(tb) > 2: # no
1288 if len(tb) > 2: # no
1280 raise
1289 raise
1281 u.debug(inst, "\n")
1290 u.debug(inst, "\n")
1282 u.warn("%s: invalid arguments\n" % cmd)
1291 u.warn("%s: invalid arguments\n" % cmd)
1283 help(u, cmd)
1292 help(u, cmd)
1284
1293
1285 sys.exit(-1)
1294 sys.exit(-1)
@@ -1,1713 +1,1729 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):
501 def dev(self):
502 if self.remote: return -1
502 if self.remote: return -1
503 return os.stat(self.path).st_dev
503 return os.stat(self.path).st_dev
504
504
505 def join(self, f):
505 def join(self, f):
506 return os.path.join(self.path, f)
506 return os.path.join(self.path, f)
507
507
508 def wjoin(self, f):
508 def wjoin(self, f):
509 return os.path.join(self.root, f)
509 return os.path.join(self.root, f)
510
510
511 def file(self, f):
511 def file(self, f):
512 if f[0] == '/': f = f[1:]
512 if f[0] == '/': f = f[1:]
513 return filelog(self.opener, f)
513 return filelog(self.opener, f)
514
514
515 def getcwd(self):
515 def getcwd(self):
516 cwd = os.getcwd()
516 cwd = os.getcwd()
517 if cwd == self.root: return ''
517 if cwd == self.root: return ''
518 return cwd[len(self.root) + 1:]
518 return cwd[len(self.root) + 1:]
519
519
520 def wfile(self, f, mode='r'):
520 def wfile(self, f, mode='r'):
521 return self.wopener(f, mode)
521 return self.wopener(f, mode)
522
522
523 def transaction(self):
523 def transaction(self):
524 # save dirstate for undo
524 # save dirstate for undo
525 try:
525 try:
526 ds = self.opener("dirstate").read()
526 ds = self.opener("dirstate").read()
527 except IOError:
527 except IOError:
528 ds = ""
528 ds = ""
529 self.opener("undo.dirstate", "w").write(ds)
529 self.opener("undo.dirstate", "w").write(ds)
530
530
531 return transaction.transaction(self.ui.warn,
531 return transaction.transaction(self.ui.warn,
532 self.opener, self.join("journal"),
532 self.opener, self.join("journal"),
533 self.join("undo"))
533 self.join("undo"))
534
534
535 def recover(self):
535 def recover(self):
536 lock = self.lock()
536 lock = self.lock()
537 if os.path.exists(self.join("journal")):
537 if os.path.exists(self.join("journal")):
538 self.ui.status("rolling back interrupted transaction\n")
538 self.ui.status("rolling back interrupted transaction\n")
539 return transaction.rollback(self.opener, self.join("journal"))
539 return transaction.rollback(self.opener, self.join("journal"))
540 else:
540 else:
541 self.ui.warn("no interrupted transaction available\n")
541 self.ui.warn("no interrupted transaction available\n")
542
542
543 def undo(self):
543 def undo(self):
544 lock = self.lock()
544 lock = self.lock()
545 if os.path.exists(self.join("undo")):
545 if os.path.exists(self.join("undo")):
546 self.ui.status("rolling back last transaction\n")
546 self.ui.status("rolling back last transaction\n")
547 transaction.rollback(self.opener, self.join("undo"))
547 transaction.rollback(self.opener, self.join("undo"))
548 self.dirstate = None
548 self.dirstate = None
549 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
549 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
550 self.dirstate = dirstate(self.opener, self.ui, self.root)
550 self.dirstate = dirstate(self.opener, self.ui, self.root)
551 else:
551 else:
552 self.ui.warn("no undo information available\n")
552 self.ui.warn("no undo information available\n")
553
553
554 def lock(self, wait = 1):
554 def lock(self, wait = 1):
555 try:
555 try:
556 return lock.lock(self.join("lock"), 0)
556 return lock.lock(self.join("lock"), 0)
557 except lock.LockHeld, inst:
557 except lock.LockHeld, inst:
558 if wait:
558 if wait:
559 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])
560 return lock.lock(self.join("lock"), wait)
560 return lock.lock(self.join("lock"), wait)
561 raise inst
561 raise inst
562
562
563 def rawcommit(self, files, text, user, date, p1=None, p2=None):
563 def rawcommit(self, files, text, user, date, p1=None, p2=None):
564 orig_parent = self.dirstate.parents()[0] or nullid
564 orig_parent = self.dirstate.parents()[0] or nullid
565 p1 = p1 or self.dirstate.parents()[0] or nullid
565 p1 = p1 or self.dirstate.parents()[0] or nullid
566 p2 = p2 or self.dirstate.parents()[1] or nullid
566 p2 = p2 or self.dirstate.parents()[1] or nullid
567 c1 = self.changelog.read(p1)
567 c1 = self.changelog.read(p1)
568 c2 = self.changelog.read(p2)
568 c2 = self.changelog.read(p2)
569 m1 = self.manifest.read(c1[0])
569 m1 = self.manifest.read(c1[0])
570 mf1 = self.manifest.readflags(c1[0])
570 mf1 = self.manifest.readflags(c1[0])
571 m2 = self.manifest.read(c2[0])
571 m2 = self.manifest.read(c2[0])
572
572
573 if orig_parent == p1:
573 if orig_parent == p1:
574 update_dirstate = 1
574 update_dirstate = 1
575 else:
575 else:
576 update_dirstate = 0
576 update_dirstate = 0
577
577
578 tr = self.transaction()
578 tr = self.transaction()
579 mm = m1.copy()
579 mm = m1.copy()
580 mfm = mf1.copy()
580 mfm = mf1.copy()
581 linkrev = self.changelog.count()
581 linkrev = self.changelog.count()
582 for f in files:
582 for f in files:
583 try:
583 try:
584 t = self.wfile(f).read()
584 t = self.wfile(f).read()
585 tm = util.is_exec(self.wjoin(f), mfm.get(f, False))
585 tm = util.is_exec(self.wjoin(f), mfm.get(f, False))
586 r = self.file(f)
586 r = self.file(f)
587 mfm[f] = tm
587 mfm[f] = tm
588 mm[f] = r.add(t, {}, tr, linkrev,
588 mm[f] = r.add(t, {}, tr, linkrev,
589 m1.get(f, nullid), m2.get(f, nullid))
589 m1.get(f, nullid), m2.get(f, nullid))
590 if update_dirstate:
590 if update_dirstate:
591 self.dirstate.update([f], "n")
591 self.dirstate.update([f], "n")
592 except IOError:
592 except IOError:
593 try:
593 try:
594 del mm[f]
594 del mm[f]
595 del mfm[f]
595 del mfm[f]
596 if update_dirstate:
596 if update_dirstate:
597 self.dirstate.forget([f])
597 self.dirstate.forget([f])
598 except:
598 except:
599 # deleted from p2?
599 # deleted from p2?
600 pass
600 pass
601
601
602 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])
603 user = user or self.ui.username()
603 user = user or self.ui.username()
604 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)
605 tr.close()
605 tr.close()
606 if update_dirstate:
606 if update_dirstate:
607 self.dirstate.setparents(n, nullid)
607 self.dirstate.setparents(n, nullid)
608
608
609 def commit(self, files = None, text = "", user = None, date = None):
609 def commit(self, files = None, text = "", user = None, date = None):
610 commit = []
610 commit = []
611 remove = []
611 remove = []
612 if files:
612 if files:
613 for f in files:
613 for f in files:
614 s = self.dirstate.state(f)
614 s = self.dirstate.state(f)
615 if s in 'nmai':
615 if s in 'nmai':
616 commit.append(f)
616 commit.append(f)
617 elif s == 'r':
617 elif s == 'r':
618 remove.append(f)
618 remove.append(f)
619 else:
619 else:
620 self.ui.warn("%s not tracked!\n" % f)
620 self.ui.warn("%s not tracked!\n" % f)
621 else:
621 else:
622 (c, a, d, u) = self.changes(None, None)
622 (c, a, d, u) = self.changes(None, None)
623 commit = c + a
623 commit = c + a
624 remove = d
624 remove = d
625
625
626 if not commit and not remove:
626 if not commit and not remove:
627 self.ui.status("nothing changed\n")
627 self.ui.status("nothing changed\n")
628 return
628 return
629
629
630 if not self.hook("precommit"):
630 if not self.hook("precommit"):
631 return 1
631 return 1
632
632
633 p1, p2 = self.dirstate.parents()
633 p1, p2 = self.dirstate.parents()
634 c1 = self.changelog.read(p1)
634 c1 = self.changelog.read(p1)
635 c2 = self.changelog.read(p2)
635 c2 = self.changelog.read(p2)
636 m1 = self.manifest.read(c1[0])
636 m1 = self.manifest.read(c1[0])
637 mf1 = self.manifest.readflags(c1[0])
637 mf1 = self.manifest.readflags(c1[0])
638 m2 = self.manifest.read(c2[0])
638 m2 = self.manifest.read(c2[0])
639 lock = self.lock()
639 lock = self.lock()
640 tr = self.transaction()
640 tr = self.transaction()
641
641
642 # check in files
642 # check in files
643 new = {}
643 new = {}
644 linkrev = self.changelog.count()
644 linkrev = self.changelog.count()
645 commit.sort()
645 commit.sort()
646 for f in commit:
646 for f in commit:
647 self.ui.note(f + "\n")
647 self.ui.note(f + "\n")
648 try:
648 try:
649 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))
650 t = self.wfile(f).read()
650 t = self.wfile(f).read()
651 except IOError:
651 except IOError:
652 self.warn("trouble committing %s!\n" % f)
652 self.warn("trouble committing %s!\n" % f)
653 raise
653 raise
654
654
655 meta = {}
655 meta = {}
656 cp = self.dirstate.copied(f)
656 cp = self.dirstate.copied(f)
657 if cp:
657 if cp:
658 meta["copy"] = cp
658 meta["copy"] = cp
659 meta["copyrev"] = hex(m1.get(cp, m2.get(cp, nullid)))
659 meta["copyrev"] = hex(m1.get(cp, m2.get(cp, nullid)))
660 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"]))
661
661
662 r = self.file(f)
662 r = self.file(f)
663 fp1 = m1.get(f, nullid)
663 fp1 = m1.get(f, nullid)
664 fp2 = m2.get(f, nullid)
664 fp2 = m2.get(f, nullid)
665 new[f] = r.add(t, meta, tr, linkrev, fp1, fp2)
665 new[f] = r.add(t, meta, tr, linkrev, fp1, fp2)
666
666
667 # update manifest
667 # update manifest
668 m1.update(new)
668 m1.update(new)
669 for f in remove:
669 for f in remove:
670 if f in m1:
670 if f in m1:
671 del m1[f]
671 del m1[f]
672 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])
673
673
674 # add changeset
674 # add changeset
675 new = new.keys()
675 new = new.keys()
676 new.sort()
676 new.sort()
677
677
678 if not text:
678 if not text:
679 edittext = "\n" + "HG: manifest hash %s\n" % hex(mn)
679 edittext = "\n" + "HG: manifest hash %s\n" % hex(mn)
680 edittext += "".join(["HG: changed %s\n" % f for f in new])
680 edittext += "".join(["HG: changed %s\n" % f for f in new])
681 edittext += "".join(["HG: removed %s\n" % f for f in remove])
681 edittext += "".join(["HG: removed %s\n" % f for f in remove])
682 edittext = self.ui.edit(edittext)
682 edittext = self.ui.edit(edittext)
683 if not edittext.rstrip():
683 if not edittext.rstrip():
684 return 1
684 return 1
685 text = edittext
685 text = edittext
686
686
687 user = user or self.ui.username()
687 user = user or self.ui.username()
688 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)
689
689
690 if not self.hook("commit", node=hex(n)):
690 if not self.hook("commit", node=hex(n)):
691 return 1
691 return 1
692
692
693 tr.close()
693 tr.close()
694
694
695 self.dirstate.setparents(n)
695 self.dirstate.setparents(n)
696 self.dirstate.update(new, "n")
696 self.dirstate.update(new, "n")
697 self.dirstate.forget(remove)
697 self.dirstate.forget(remove)
698
698
699 def changes(self, node1, node2, files=None):
699 def changes(self, node1, node2, files=None):
700 mf2, u = None, []
700 mf2, u = None, []
701
701
702 def fcmp(fn, mf):
702 def fcmp(fn, mf):
703 t1 = self.wfile(fn).read()
703 t1 = self.wfile(fn).read()
704 t2 = self.file(fn).revision(mf[fn])
704 t2 = self.file(fn).revision(mf[fn])
705 return cmp(t1, t2)
705 return cmp(t1, t2)
706
706
707 # are we comparing the working directory?
707 # are we comparing the working directory?
708 if not node2:
708 if not node2:
709 l, c, a, d, u = self.dirstate.changes(files, self.ignore)
709 l, c, a, d, u = self.dirstate.changes(files, self.ignore)
710
710
711 # are we comparing working dir against its parent?
711 # are we comparing working dir against its parent?
712 if not node1:
712 if not node1:
713 if l:
713 if l:
714 # do a full compare of any files that might have changed
714 # do a full compare of any files that might have changed
715 change = self.changelog.read(self.dirstate.parents()[0])
715 change = self.changelog.read(self.dirstate.parents()[0])
716 mf2 = self.manifest.read(change[0])
716 mf2 = self.manifest.read(change[0])
717 for f in l:
717 for f in l:
718 if fcmp(f, mf2):
718 if fcmp(f, mf2):
719 c.append(f)
719 c.append(f)
720
720
721 for l in c, a, d, u:
721 for l in c, a, d, u:
722 l.sort()
722 l.sort()
723
723
724 return (c, a, d, u)
724 return (c, a, d, u)
725
725
726 # are we comparing working dir against non-tip?
726 # are we comparing working dir against non-tip?
727 # generate a pseudo-manifest for the working dir
727 # generate a pseudo-manifest for the working dir
728 if not node2:
728 if not node2:
729 if not mf2:
729 if not mf2:
730 change = self.changelog.read(self.dirstate.parents()[0])
730 change = self.changelog.read(self.dirstate.parents()[0])
731 mf2 = self.manifest.read(change[0]).copy()
731 mf2 = self.manifest.read(change[0]).copy()
732 for f in a + c + l:
732 for f in a + c + l:
733 mf2[f] = ""
733 mf2[f] = ""
734 for f in d:
734 for f in d:
735 if f in mf2: del mf2[f]
735 if f in mf2: del mf2[f]
736 else:
736 else:
737 change = self.changelog.read(node2)
737 change = self.changelog.read(node2)
738 mf2 = self.manifest.read(change[0])
738 mf2 = self.manifest.read(change[0])
739
739
740 # flush lists from dirstate before comparing manifests
740 # flush lists from dirstate before comparing manifests
741 c, a = [], []
741 c, a = [], []
742
742
743 change = self.changelog.read(node1)
743 change = self.changelog.read(node1)
744 mf1 = self.manifest.read(change[0]).copy()
744 mf1 = self.manifest.read(change[0]).copy()
745
745
746 for fn in mf2:
746 for fn in mf2:
747 if mf1.has_key(fn):
747 if mf1.has_key(fn):
748 if mf1[fn] != mf2[fn]:
748 if mf1[fn] != mf2[fn]:
749 if mf2[fn] != "" or fcmp(fn, mf1):
749 if mf2[fn] != "" or fcmp(fn, mf1):
750 c.append(fn)
750 c.append(fn)
751 del mf1[fn]
751 del mf1[fn]
752 else:
752 else:
753 a.append(fn)
753 a.append(fn)
754
754
755 d = mf1.keys()
755 d = mf1.keys()
756
756
757 for l in c, a, d, u:
757 for l in c, a, d, u:
758 l.sort()
758 l.sort()
759
759
760 return (c, a, d, u)
760 return (c, a, d, u)
761
761
762 def add(self, list):
762 def add(self, list):
763 for f in list:
763 for f in list:
764 p = self.wjoin(f)
764 p = self.wjoin(f)
765 if not os.path.exists(p):
765 if not os.path.exists(p):
766 self.ui.warn("%s does not exist!\n" % f)
766 self.ui.warn("%s does not exist!\n" % f)
767 elif not os.path.isfile(p):
767 elif not os.path.isfile(p):
768 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)
769 elif self.dirstate.state(f) == 'n':
769 elif self.dirstate.state(f) == 'n':
770 self.ui.warn("%s already tracked!\n" % f)
770 self.ui.warn("%s already tracked!\n" % f)
771 else:
771 else:
772 self.dirstate.update([f], "a")
772 self.dirstate.update([f], "a")
773
773
774 def forget(self, list):
774 def forget(self, list):
775 for f in list:
775 for f in list:
776 if self.dirstate.state(f) not in 'ai':
776 if self.dirstate.state(f) not in 'ai':
777 self.ui.warn("%s not added!\n" % f)
777 self.ui.warn("%s not added!\n" % f)
778 else:
778 else:
779 self.dirstate.forget([f])
779 self.dirstate.forget([f])
780
780
781 def remove(self, list):
781 def remove(self, list):
782 for f in list:
782 for f in list:
783 p = self.wjoin(f)
783 p = self.wjoin(f)
784 if os.path.exists(p):
784 if os.path.exists(p):
785 self.ui.warn("%s still exists!\n" % f)
785 self.ui.warn("%s still exists!\n" % f)
786 elif self.dirstate.state(f) == 'a':
786 elif self.dirstate.state(f) == 'a':
787 self.ui.warn("%s never committed!\n" % f)
787 self.ui.warn("%s never committed!\n" % f)
788 self.dirstate.forget(f)
788 self.dirstate.forget(f)
789 elif f not in self.dirstate:
789 elif f not in self.dirstate:
790 self.ui.warn("%s not tracked!\n" % f)
790 self.ui.warn("%s not tracked!\n" % f)
791 else:
791 else:
792 self.dirstate.update([f], "r")
792 self.dirstate.update([f], "r")
793
793
794 def copy(self, source, dest):
794 def copy(self, source, dest):
795 p = self.wjoin(dest)
795 p = self.wjoin(dest)
796 if not os.path.exists(dest):
796 if not os.path.exists(dest):
797 self.ui.warn("%s does not exist!\n" % dest)
797 self.ui.warn("%s does not exist!\n" % dest)
798 elif not os.path.isfile(dest):
798 elif not os.path.isfile(dest):
799 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)
800 else:
800 else:
801 if self.dirstate.state(dest) == '?':
801 if self.dirstate.state(dest) == '?':
802 self.dirstate.update([dest], "a")
802 self.dirstate.update([dest], "a")
803 self.dirstate.copy(source, dest)
803 self.dirstate.copy(source, dest)
804
804
805 def heads(self):
805 def heads(self):
806 return self.changelog.heads()
806 return self.changelog.heads()
807
807
808 def branches(self, nodes):
808 def branches(self, nodes):
809 if not nodes: nodes = [self.changelog.tip()]
809 if not nodes: nodes = [self.changelog.tip()]
810 b = []
810 b = []
811 for n in nodes:
811 for n in nodes:
812 t = n
812 t = n
813 while n:
813 while n:
814 p = self.changelog.parents(n)
814 p = self.changelog.parents(n)
815 if p[1] != nullid or p[0] == nullid:
815 if p[1] != nullid or p[0] == nullid:
816 b.append((t, n, p[0], p[1]))
816 b.append((t, n, p[0], p[1]))
817 break
817 break
818 n = p[0]
818 n = p[0]
819 return b
819 return b
820
820
821 def between(self, pairs):
821 def between(self, pairs):
822 r = []
822 r = []
823
823
824 for top, bottom in pairs:
824 for top, bottom in pairs:
825 n, l, i = top, [], 0
825 n, l, i = top, [], 0
826 f = 1
826 f = 1
827
827
828 while n != bottom:
828 while n != bottom:
829 p = self.changelog.parents(n)[0]
829 p = self.changelog.parents(n)[0]
830 if i == f:
830 if i == f:
831 l.append(n)
831 l.append(n)
832 f = f * 2
832 f = f * 2
833 n = p
833 n = p
834 i += 1
834 i += 1
835
835
836 r.append(l)
836 r.append(l)
837
837
838 return r
838 return r
839
839
840 def newer(self, nodes):
840 def newer(self, nodes):
841 m = {}
841 m = {}
842 nl = []
842 nl = []
843 pm = {}
843 pm = {}
844 cl = self.changelog
844 cl = self.changelog
845 t = l = cl.count()
845 t = l = cl.count()
846
846
847 # find the lowest numbered node
847 # find the lowest numbered node
848 for n in nodes:
848 for n in nodes:
849 l = min(l, cl.rev(n))
849 l = min(l, cl.rev(n))
850 m[n] = 1
850 m[n] = 1
851
851
852 for i in xrange(l, t):
852 for i in xrange(l, t):
853 n = cl.node(i)
853 n = cl.node(i)
854 if n in m: # explicitly listed
854 if n in m: # explicitly listed
855 pm[n] = 1
855 pm[n] = 1
856 nl.append(n)
856 nl.append(n)
857 continue
857 continue
858 for p in cl.parents(n):
858 for p in cl.parents(n):
859 if p in pm: # parent listed
859 if p in pm: # parent listed
860 pm[n] = 1
860 pm[n] = 1
861 nl.append(n)
861 nl.append(n)
862 break
862 break
863
863
864 return nl
864 return nl
865
865
866 def findincoming(self, remote, base={}):
866 def findincoming(self, remote, base={}):
867 m = self.changelog.nodemap
867 m = self.changelog.nodemap
868 search = []
868 search = []
869 fetch = []
869 fetch = []
870 seen = {}
870 seen = {}
871 seenbranch = {}
871 seenbranch = {}
872
872
873 # assume we're closer to the tip than the root
873 # assume we're closer to the tip than the root
874 # and start by examining the heads
874 # and start by examining the heads
875 self.ui.status("searching for changes\n")
875 self.ui.status("searching for changes\n")
876 heads = remote.heads()
876 heads = remote.heads()
877 unknown = []
877 unknown = []
878 for h in heads:
878 for h in heads:
879 if h not in m:
879 if h not in m:
880 unknown.append(h)
880 unknown.append(h)
881 else:
881 else:
882 base[h] = 1
882 base[h] = 1
883
883
884 if not unknown:
884 if not unknown:
885 return None
885 return None
886
886
887 rep = {}
887 rep = {}
888 reqcnt = 0
888 reqcnt = 0
889
889
890 # search through remote branches
890 # search through remote branches
891 # a 'branch' here is a linear segment of history, with four parts:
891 # a 'branch' here is a linear segment of history, with four parts:
892 # head, root, first parent, second parent
892 # head, root, first parent, second parent
893 # (a branch always has two parents (or none) by definition)
893 # (a branch always has two parents (or none) by definition)
894 unknown = remote.branches(unknown)
894 unknown = remote.branches(unknown)
895 while unknown:
895 while unknown:
896 r = []
896 r = []
897 while unknown:
897 while unknown:
898 n = unknown.pop(0)
898 n = unknown.pop(0)
899 if n[0] in seen:
899 if n[0] in seen:
900 continue
900 continue
901
901
902 self.ui.debug("examining %s:%s\n" % (short(n[0]), short(n[1])))
902 self.ui.debug("examining %s:%s\n" % (short(n[0]), short(n[1])))
903 if n[0] == nullid:
903 if n[0] == nullid:
904 break
904 break
905 if n in seenbranch:
905 if n in seenbranch:
906 self.ui.debug("branch already found\n")
906 self.ui.debug("branch already found\n")
907 continue
907 continue
908 if n[1] and n[1] in m: # do we know the base?
908 if n[1] and n[1] in m: # do we know the base?
909 self.ui.debug("found incomplete branch %s:%s\n"
909 self.ui.debug("found incomplete branch %s:%s\n"
910 % (short(n[0]), short(n[1])))
910 % (short(n[0]), short(n[1])))
911 search.append(n) # schedule branch range for scanning
911 search.append(n) # schedule branch range for scanning
912 seenbranch[n] = 1
912 seenbranch[n] = 1
913 else:
913 else:
914 if n[1] not in seen and n[1] not in fetch:
914 if n[1] not in seen and n[1] not in fetch:
915 if n[2] in m and n[3] in m:
915 if n[2] in m and n[3] in m:
916 self.ui.debug("found new changeset %s\n" %
916 self.ui.debug("found new changeset %s\n" %
917 short(n[1]))
917 short(n[1]))
918 fetch.append(n[1]) # earliest unknown
918 fetch.append(n[1]) # earliest unknown
919 base[n[2]] = 1 # latest known
919 base[n[2]] = 1 # latest known
920 continue
920 continue
921
921
922 for a in n[2:4]:
922 for a in n[2:4]:
923 if a not in rep:
923 if a not in rep:
924 r.append(a)
924 r.append(a)
925 rep[a] = 1
925 rep[a] = 1
926
926
927 seen[n[0]] = 1
927 seen[n[0]] = 1
928
928
929 if r:
929 if r:
930 reqcnt += 1
930 reqcnt += 1
931 self.ui.debug("request %d: %s\n" %
931 self.ui.debug("request %d: %s\n" %
932 (reqcnt, " ".join(map(short, r))))
932 (reqcnt, " ".join(map(short, r))))
933 for p in range(0, len(r), 10):
933 for p in range(0, len(r), 10):
934 for b in remote.branches(r[p:p+10]):
934 for b in remote.branches(r[p:p+10]):
935 self.ui.debug("received %s:%s\n" %
935 self.ui.debug("received %s:%s\n" %
936 (short(b[0]), short(b[1])))
936 (short(b[0]), short(b[1])))
937 if b[0] not in m and b[0] not in seen:
937 if b[0] not in m and b[0] not in seen:
938 unknown.append(b)
938 unknown.append(b)
939
939
940 # do binary search on the branches we found
940 # do binary search on the branches we found
941 while search:
941 while search:
942 n = search.pop(0)
942 n = search.pop(0)
943 reqcnt += 1
943 reqcnt += 1
944 l = remote.between([(n[0], n[1])])[0]
944 l = remote.between([(n[0], n[1])])[0]
945 l.append(n[1])
945 l.append(n[1])
946 p = n[0]
946 p = n[0]
947 f = 1
947 f = 1
948 for i in l:
948 for i in l:
949 self.ui.debug("narrowing %d:%d %s\n" % (f, len(l), short(i)))
949 self.ui.debug("narrowing %d:%d %s\n" % (f, len(l), short(i)))
950 if i in m:
950 if i in m:
951 if f <= 2:
951 if f <= 2:
952 self.ui.debug("found new branch changeset %s\n" %
952 self.ui.debug("found new branch changeset %s\n" %
953 short(p))
953 short(p))
954 fetch.append(p)
954 fetch.append(p)
955 base[i] = 1
955 base[i] = 1
956 else:
956 else:
957 self.ui.debug("narrowed branch search to %s:%s\n"
957 self.ui.debug("narrowed branch search to %s:%s\n"
958 % (short(p), short(i)))
958 % (short(p), short(i)))
959 search.append((p, i))
959 search.append((p, i))
960 break
960 break
961 p, f = i, f * 2
961 p, f = i, f * 2
962
962
963 # sanity check our fetch list
963 # sanity check our fetch list
964 for f in fetch:
964 for f in fetch:
965 if f in m:
965 if f in m:
966 raise RepoError("already have changeset " + short(f[:4]))
966 raise RepoError("already have changeset " + short(f[:4]))
967
967
968 if base.keys() == [nullid]:
968 if base.keys() == [nullid]:
969 self.ui.warn("warning: pulling from an unrelated repository!\n")
969 self.ui.warn("warning: pulling from an unrelated repository!\n")
970
970
971 self.ui.note("adding new changesets starting at " +
971 self.ui.note("adding new changesets starting at " +
972 " ".join([short(f) for f in fetch]) + "\n")
972 " ".join([short(f) for f in fetch]) + "\n")
973
973
974 self.ui.debug("%d total queries\n" % reqcnt)
974 self.ui.debug("%d total queries\n" % reqcnt)
975
975
976 return fetch
976 return fetch
977
977
978 def findoutgoing(self, remote):
978 def findoutgoing(self, remote):
979 base = {}
979 base = {}
980 self.findincoming(remote, base)
980 self.findincoming(remote, base)
981 remain = dict.fromkeys(self.changelog.nodemap)
981 remain = dict.fromkeys(self.changelog.nodemap)
982
982
983 # prune everything remote has from the tree
983 # prune everything remote has from the tree
984 del remain[nullid]
984 del remain[nullid]
985 remove = base.keys()
985 remove = base.keys()
986 while remove:
986 while remove:
987 n = remove.pop(0)
987 n = remove.pop(0)
988 if n in remain:
988 if n in remain:
989 del remain[n]
989 del remain[n]
990 for p in self.changelog.parents(n):
990 for p in self.changelog.parents(n):
991 remove.append(p)
991 remove.append(p)
992
992
993 # find every node whose parents have been pruned
993 # find every node whose parents have been pruned
994 subset = []
994 subset = []
995 for n in remain:
995 for n in remain:
996 p1, p2 = self.changelog.parents(n)
996 p1, p2 = self.changelog.parents(n)
997 if p1 not in remain and p2 not in remain:
997 if p1 not in remain and p2 not in remain:
998 subset.append(n)
998 subset.append(n)
999
999
1000 # this is the set of all roots we have to push
1000 # this is the set of all roots we have to push
1001 return subset
1001 return subset
1002
1002
1003 def pull(self, remote):
1003 def pull(self, remote):
1004 lock = self.lock()
1004 lock = self.lock()
1005
1005
1006 # if we have an empty repo, fetch everything
1006 # if we have an empty repo, fetch everything
1007 if self.changelog.tip() == nullid:
1007 if self.changelog.tip() == nullid:
1008 self.ui.status("requesting all changes\n")
1008 self.ui.status("requesting all changes\n")
1009 fetch = [nullid]
1009 fetch = [nullid]
1010 else:
1010 else:
1011 fetch = self.findincoming(remote)
1011 fetch = self.findincoming(remote)
1012
1012
1013 if not fetch:
1013 if not fetch:
1014 self.ui.status("no changes found\n")
1014 self.ui.status("no changes found\n")
1015 return 1
1015 return 1
1016
1016
1017 cg = remote.changegroup(fetch)
1017 cg = remote.changegroup(fetch)
1018 return self.addchangegroup(cg)
1018 return self.addchangegroup(cg)
1019
1019
1020 def push(self, remote):
1020 def push(self, remote):
1021 lock = remote.lock()
1021 lock = remote.lock()
1022 update = self.findoutgoing(remote)
1022 update = self.findoutgoing(remote)
1023 if not update:
1023 if not update:
1024 self.ui.status("no changes found\n")
1024 self.ui.status("no changes found\n")
1025 return 1
1025 return 1
1026
1026
1027 cg = self.changegroup(update)
1027 cg = self.changegroup(update)
1028 return remote.addchangegroup(cg)
1028 return remote.addchangegroup(cg)
1029
1029
1030 def changegroup(self, basenodes):
1030 def changegroup(self, basenodes):
1031 class genread:
1031 class genread:
1032 def __init__(self, generator):
1032 def __init__(self, generator):
1033 self.g = generator
1033 self.g = generator
1034 self.buf = ""
1034 self.buf = ""
1035 def read(self, l):
1035 def read(self, l):
1036 while l > len(self.buf):
1036 while l > len(self.buf):
1037 try:
1037 try:
1038 self.buf += self.g.next()
1038 self.buf += self.g.next()
1039 except StopIteration:
1039 except StopIteration:
1040 break
1040 break
1041 d, self.buf = self.buf[:l], self.buf[l:]
1041 d, self.buf = self.buf[:l], self.buf[l:]
1042 return d
1042 return d
1043
1043
1044 def gengroup():
1044 def gengroup():
1045 nodes = self.newer(basenodes)
1045 nodes = self.newer(basenodes)
1046
1046
1047 # construct the link map
1047 # construct the link map
1048 linkmap = {}
1048 linkmap = {}
1049 for n in nodes:
1049 for n in nodes:
1050 linkmap[self.changelog.rev(n)] = n
1050 linkmap[self.changelog.rev(n)] = n
1051
1051
1052 # construct a list of all changed files
1052 # construct a list of all changed files
1053 changed = {}
1053 changed = {}
1054 for n in nodes:
1054 for n in nodes:
1055 c = self.changelog.read(n)
1055 c = self.changelog.read(n)
1056 for f in c[3]:
1056 for f in c[3]:
1057 changed[f] = 1
1057 changed[f] = 1
1058 changed = changed.keys()
1058 changed = changed.keys()
1059 changed.sort()
1059 changed.sort()
1060
1060
1061 # the changegroup is changesets + manifests + all file revs
1061 # the changegroup is changesets + manifests + all file revs
1062 revs = [ self.changelog.rev(n) for n in nodes ]
1062 revs = [ self.changelog.rev(n) for n in nodes ]
1063
1063
1064 for y in self.changelog.group(linkmap): yield y
1064 for y in self.changelog.group(linkmap): yield y
1065 for y in self.manifest.group(linkmap): yield y
1065 for y in self.manifest.group(linkmap): yield y
1066 for f in changed:
1066 for f in changed:
1067 yield struct.pack(">l", len(f) + 4) + f
1067 yield struct.pack(">l", len(f) + 4) + f
1068 g = self.file(f).group(linkmap)
1068 g = self.file(f).group(linkmap)
1069 for y in g:
1069 for y in g:
1070 yield y
1070 yield y
1071
1071
1072 yield struct.pack(">l", 0)
1072 yield struct.pack(">l", 0)
1073
1073
1074 return genread(gengroup())
1074 return genread(gengroup())
1075
1075
1076 def addchangegroup(self, source):
1076 def addchangegroup(self, source):
1077
1077
1078 def getchunk():
1078 def getchunk():
1079 d = source.read(4)
1079 d = source.read(4)
1080 if not d: return ""
1080 if not d: return ""
1081 l = struct.unpack(">l", d)[0]
1081 l = struct.unpack(">l", d)[0]
1082 if l <= 4: return ""
1082 if l <= 4: return ""
1083 return source.read(l - 4)
1083 return source.read(l - 4)
1084
1084
1085 def getgroup():
1085 def getgroup():
1086 while 1:
1086 while 1:
1087 c = getchunk()
1087 c = getchunk()
1088 if not c: break
1088 if not c: break
1089 yield c
1089 yield c
1090
1090
1091 def csmap(x):
1091 def csmap(x):
1092 self.ui.debug("add changeset %s\n" % short(x))
1092 self.ui.debug("add changeset %s\n" % short(x))
1093 return self.changelog.count()
1093 return self.changelog.count()
1094
1094
1095 def revmap(x):
1095 def revmap(x):
1096 return self.changelog.rev(x)
1096 return self.changelog.rev(x)
1097
1097
1098 if not source: return
1098 if not source: return
1099 changesets = files = revisions = 0
1099 changesets = files = revisions = 0
1100
1100
1101 tr = self.transaction()
1101 tr = self.transaction()
1102
1102
1103 # pull off the changeset group
1103 # pull off the changeset group
1104 self.ui.status("adding changesets\n")
1104 self.ui.status("adding changesets\n")
1105 co = self.changelog.tip()
1105 co = self.changelog.tip()
1106 cn = self.changelog.addgroup(getgroup(), csmap, tr, 1) # unique
1106 cn = self.changelog.addgroup(getgroup(), csmap, tr, 1) # unique
1107 changesets = self.changelog.rev(cn) - self.changelog.rev(co)
1107 changesets = self.changelog.rev(cn) - self.changelog.rev(co)
1108
1108
1109 # pull off the manifest group
1109 # pull off the manifest group
1110 self.ui.status("adding manifests\n")
1110 self.ui.status("adding manifests\n")
1111 mm = self.manifest.tip()
1111 mm = self.manifest.tip()
1112 mo = self.manifest.addgroup(getgroup(), revmap, tr)
1112 mo = self.manifest.addgroup(getgroup(), revmap, tr)
1113
1113
1114 # process the files
1114 # process the files
1115 self.ui.status("adding file revisions\n")
1115 self.ui.status("adding file revisions\n")
1116 while 1:
1116 while 1:
1117 f = getchunk()
1117 f = getchunk()
1118 if not f: break
1118 if not f: break
1119 self.ui.debug("adding %s revisions\n" % f)
1119 self.ui.debug("adding %s revisions\n" % f)
1120 fl = self.file(f)
1120 fl = self.file(f)
1121 o = fl.count()
1121 o = fl.count()
1122 n = fl.addgroup(getgroup(), revmap, tr)
1122 n = fl.addgroup(getgroup(), revmap, tr)
1123 revisions += fl.count() - o
1123 revisions += fl.count() - o
1124 files += 1
1124 files += 1
1125
1125
1126 self.ui.status(("modified %d files, added %d changesets" +
1126 self.ui.status(("modified %d files, added %d changesets" +
1127 " and %d new revisions\n")
1127 " and %d new revisions\n")
1128 % (files, changesets, revisions))
1128 % (files, changesets, revisions))
1129
1129
1130 tr.close()
1130 tr.close()
1131 return
1131 return
1132
1132
1133 def update(self, node, allow=False, force=False, choose=None,
1133 def update(self, node, allow=False, force=False, choose=None,
1134 moddirstate=True):
1134 moddirstate=True):
1135 pl = self.dirstate.parents()
1135 pl = self.dirstate.parents()
1136 if not force and pl[1] != nullid:
1136 if not force and pl[1] != nullid:
1137 self.ui.warn("aborting: outstanding uncommitted merges\n")
1137 self.ui.warn("aborting: outstanding uncommitted merges\n")
1138 return
1138 return
1139
1139
1140 p1, p2 = pl[0], node
1140 p1, p2 = pl[0], node
1141 pa = self.changelog.ancestor(p1, p2)
1141 pa = self.changelog.ancestor(p1, p2)
1142 m1n = self.changelog.read(p1)[0]
1142 m1n = self.changelog.read(p1)[0]
1143 m2n = self.changelog.read(p2)[0]
1143 m2n = self.changelog.read(p2)[0]
1144 man = self.manifest.ancestor(m1n, m2n)
1144 man = self.manifest.ancestor(m1n, m2n)
1145 m1 = self.manifest.read(m1n)
1145 m1 = self.manifest.read(m1n)
1146 mf1 = self.manifest.readflags(m1n)
1146 mf1 = self.manifest.readflags(m1n)
1147 m2 = self.manifest.read(m2n)
1147 m2 = self.manifest.read(m2n)
1148 mf2 = self.manifest.readflags(m2n)
1148 mf2 = self.manifest.readflags(m2n)
1149 ma = self.manifest.read(man)
1149 ma = self.manifest.read(man)
1150 mfa = self.manifest.readflags(man)
1150 mfa = self.manifest.readflags(man)
1151
1151
1152 (c, a, d, u) = self.changes(None, None)
1152 (c, a, d, u) = self.changes(None, None)
1153
1153
1154 # is this a jump, or a merge? i.e. is there a linear path
1154 # is this a jump, or a merge? i.e. is there a linear path
1155 # from p1 to p2?
1155 # from p1 to p2?
1156 linear_path = (pa == p1 or pa == p2)
1156 linear_path = (pa == p1 or pa == p2)
1157
1157
1158 # resolve the manifest to determine which files
1158 # resolve the manifest to determine which files
1159 # we care about merging
1159 # we care about merging
1160 self.ui.note("resolving manifests\n")
1160 self.ui.note("resolving manifests\n")
1161 self.ui.debug(" ancestor %s local %s remote %s\n" %
1161 self.ui.debug(" ancestor %s local %s remote %s\n" %
1162 (short(man), short(m1n), short(m2n)))
1162 (short(man), short(m1n), short(m2n)))
1163
1163
1164 merge = {}
1164 merge = {}
1165 get = {}
1165 get = {}
1166 remove = []
1166 remove = []
1167 mark = {}
1167 mark = {}
1168
1168
1169 # construct a working dir manifest
1169 # construct a working dir manifest
1170 mw = m1.copy()
1170 mw = m1.copy()
1171 mfw = mf1.copy()
1171 mfw = mf1.copy()
1172 umap = dict.fromkeys(u)
1172 umap = dict.fromkeys(u)
1173
1173
1174 for f in a + c + u:
1174 for f in a + c + u:
1175 mw[f] = ""
1175 mw[f] = ""
1176 mfw[f] = util.is_exec(self.wjoin(f), mfw.get(f, False))
1176 mfw[f] = util.is_exec(self.wjoin(f), mfw.get(f, False))
1177
1177
1178 for f in d:
1178 for f in d:
1179 if f in mw: del mw[f]
1179 if f in mw: del mw[f]
1180
1180
1181 # If we're jumping between revisions (as opposed to merging),
1181 # If we're jumping between revisions (as opposed to merging),
1182 # and if neither the working directory nor the target rev has
1182 # and if neither the working directory nor the target rev has
1183 # the file, then we need to remove it from the dirstate, to
1183 # the file, then we need to remove it from the dirstate, to
1184 # prevent the dirstate from listing the file when it is no
1184 # prevent the dirstate from listing the file when it is no
1185 # longer in the manifest.
1185 # longer in the manifest.
1186 if moddirstate and linear_path and f not in m2:
1186 if moddirstate and linear_path and f not in m2:
1187 self.dirstate.forget((f,))
1187 self.dirstate.forget((f,))
1188
1188
1189 # Compare manifests
1189 # Compare manifests
1190 for f, n in mw.iteritems():
1190 for f, n in mw.iteritems():
1191 if choose and not choose(f): continue
1191 if choose and not choose(f): continue
1192 if f in m2:
1192 if f in m2:
1193 s = 0
1193 s = 0
1194
1194
1195 # is the wfile new since m1, and match m2?
1195 # is the wfile new since m1, and match m2?
1196 if f not in m1:
1196 if f not in m1:
1197 t1 = self.wfile(f).read()
1197 t1 = self.wfile(f).read()
1198 t2 = self.file(f).revision(m2[f])
1198 t2 = self.file(f).revision(m2[f])
1199 if cmp(t1, t2) == 0:
1199 if cmp(t1, t2) == 0:
1200 mark[f] = 1
1200 mark[f] = 1
1201 n = m2[f]
1201 n = m2[f]
1202 del t1, t2
1202 del t1, t2
1203
1203
1204 # are files different?
1204 # are files different?
1205 if n != m2[f]:
1205 if n != m2[f]:
1206 a = ma.get(f, nullid)
1206 a = ma.get(f, nullid)
1207 # are both different from the ancestor?
1207 # are both different from the ancestor?
1208 if n != a and m2[f] != a:
1208 if n != a and m2[f] != a:
1209 self.ui.debug(" %s versions differ, resolve\n" % f)
1209 self.ui.debug(" %s versions differ, resolve\n" % f)
1210 # merge executable bits
1210 # merge executable bits
1211 # "if we changed or they changed, change in merge"
1211 # "if we changed or they changed, change in merge"
1212 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1212 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1213 mode = ((a^b) | (a^c)) ^ a
1213 mode = ((a^b) | (a^c)) ^ a
1214 merge[f] = (m1.get(f, nullid), m2[f], mode)
1214 merge[f] = (m1.get(f, nullid), m2[f], mode)
1215 s = 1
1215 s = 1
1216 # are we clobbering?
1216 # are we clobbering?
1217 # is remote's version newer?
1217 # is remote's version newer?
1218 # or are we going back in time?
1218 # or are we going back in time?
1219 elif force or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
1219 elif force or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
1220 self.ui.debug(" remote %s is newer, get\n" % f)
1220 self.ui.debug(" remote %s is newer, get\n" % f)
1221 get[f] = m2[f]
1221 get[f] = m2[f]
1222 s = 1
1222 s = 1
1223 else:
1223 else:
1224 mark[f] = 1
1224 mark[f] = 1
1225 elif f in umap:
1225 elif f in umap:
1226 # this unknown file is the same as the checkout
1226 # this unknown file is the same as the checkout
1227 get[f] = m2[f]
1227 get[f] = m2[f]
1228
1228
1229 if not s and mfw[f] != mf2[f]:
1229 if not s and mfw[f] != mf2[f]:
1230 if force:
1230 if force:
1231 self.ui.debug(" updating permissions for %s\n" % f)
1231 self.ui.debug(" updating permissions for %s\n" % f)
1232 util.set_exec(self.wjoin(f), mf2[f])
1232 util.set_exec(self.wjoin(f), mf2[f])
1233 else:
1233 else:
1234 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1234 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1235 mode = ((a^b) | (a^c)) ^ a
1235 mode = ((a^b) | (a^c)) ^ a
1236 if mode != b:
1236 if mode != b:
1237 self.ui.debug(" updating permissions for %s\n" % f)
1237 self.ui.debug(" updating permissions for %s\n" % f)
1238 util.set_exec(self.wjoin(f), mode)
1238 util.set_exec(self.wjoin(f), mode)
1239 mark[f] = 1
1239 mark[f] = 1
1240 del m2[f]
1240 del m2[f]
1241 elif f in ma:
1241 elif f in ma:
1242 if n != ma[f]:
1242 if n != ma[f]:
1243 r = "d"
1243 r = "d"
1244 if not force and (linear_path or allow):
1244 if not force and (linear_path or allow):
1245 r = self.ui.prompt(
1245 r = self.ui.prompt(
1246 (" local changed %s which remote deleted\n" % f) +
1246 (" local changed %s which remote deleted\n" % f) +
1247 "(k)eep or (d)elete?", "[kd]", "k")
1247 "(k)eep or (d)elete?", "[kd]", "k")
1248 if r == "d":
1248 if r == "d":
1249 remove.append(f)
1249 remove.append(f)
1250 else:
1250 else:
1251 self.ui.debug("other deleted %s\n" % f)
1251 self.ui.debug("other deleted %s\n" % f)
1252 remove.append(f) # other deleted it
1252 remove.append(f) # other deleted it
1253 else:
1253 else:
1254 if n == m1.get(f, nullid): # same as parent
1254 if n == m1.get(f, nullid): # same as parent
1255 if p2 == pa: # going backwards?
1255 if p2 == pa: # going backwards?
1256 self.ui.debug("remote deleted %s\n" % f)
1256 self.ui.debug("remote deleted %s\n" % f)
1257 remove.append(f)
1257 remove.append(f)
1258 else:
1258 else:
1259 self.ui.debug("local created %s, keeping\n" % f)
1259 self.ui.debug("local created %s, keeping\n" % f)
1260 else:
1260 else:
1261 self.ui.debug("working dir created %s, keeping\n" % f)
1261 self.ui.debug("working dir created %s, keeping\n" % f)
1262
1262
1263 for f, n in m2.iteritems():
1263 for f, n in m2.iteritems():
1264 if choose and not choose(f): continue
1264 if choose and not choose(f): continue
1265 if f[0] == "/": continue
1265 if f[0] == "/": continue
1266 if f in ma and n != ma[f]:
1266 if f in ma and n != ma[f]:
1267 r = "k"
1267 r = "k"
1268 if not force and (linear_path or allow):
1268 if not force and (linear_path or allow):
1269 r = self.ui.prompt(
1269 r = self.ui.prompt(
1270 ("remote changed %s which local deleted\n" % f) +
1270 ("remote changed %s which local deleted\n" % f) +
1271 "(k)eep or (d)elete?", "[kd]", "k")
1271 "(k)eep or (d)elete?", "[kd]", "k")
1272 if r == "k": get[f] = n
1272 if r == "k": get[f] = n
1273 elif f not in ma:
1273 elif f not in ma:
1274 self.ui.debug("remote created %s\n" % f)
1274 self.ui.debug("remote created %s\n" % f)
1275 get[f] = n
1275 get[f] = n
1276 else:
1276 else:
1277 self.ui.debug("local deleted %s\n" % f)
1277 self.ui.debug("local deleted %s\n" % f)
1278
1278
1279 del mw, m1, m2, ma
1279 del mw, m1, m2, ma
1280
1280
1281 if force:
1281 if force:
1282 for f in merge:
1282 for f in merge:
1283 get[f] = merge[f][1]
1283 get[f] = merge[f][1]
1284 merge = {}
1284 merge = {}
1285
1285
1286 if linear_path:
1286 if linear_path:
1287 # we don't need to do any magic, just jump to the new rev
1287 # we don't need to do any magic, just jump to the new rev
1288 mode = 'n'
1288 mode = 'n'
1289 p1, p2 = p2, nullid
1289 p1, p2 = p2, nullid
1290 else:
1290 else:
1291 if not allow:
1291 if not allow:
1292 self.ui.status("this update spans a branch" +
1292 self.ui.status("this update spans a branch" +
1293 " affecting the following files:\n")
1293 " affecting the following files:\n")
1294 fl = merge.keys() + get.keys()
1294 fl = merge.keys() + get.keys()
1295 fl.sort()
1295 fl.sort()
1296 for f in fl:
1296 for f in fl:
1297 cf = ""
1297 cf = ""
1298 if f in merge: cf = " (resolve)"
1298 if f in merge: cf = " (resolve)"
1299 self.ui.status(" %s%s\n" % (f, cf))
1299 self.ui.status(" %s%s\n" % (f, cf))
1300 self.ui.warn("aborting update spanning branches!\n")
1300 self.ui.warn("aborting update spanning branches!\n")
1301 self.ui.status("(use update -m to perform a branch merge)\n")
1301 self.ui.status("(use update -m to perform a branch merge)\n")
1302 return 1
1302 return 1
1303 # we have to remember what files we needed to get/change
1303 # we have to remember what files we needed to get/change
1304 # because any file that's different from either one of its
1304 # because any file that's different from either one of its
1305 # parents must be in the changeset
1305 # parents must be in the changeset
1306 mode = 'm'
1306 mode = 'm'
1307 if moddirstate:
1307 if moddirstate:
1308 self.dirstate.update(mark.keys(), "m")
1308 self.dirstate.update(mark.keys(), "m")
1309
1309
1310 if moddirstate:
1310 if moddirstate:
1311 self.dirstate.setparents(p1, p2)
1311 self.dirstate.setparents(p1, p2)
1312
1312
1313 # get the files we don't need to change
1313 # get the files we don't need to change
1314 files = get.keys()
1314 files = get.keys()
1315 files.sort()
1315 files.sort()
1316 for f in files:
1316 for f in files:
1317 if f[0] == "/": continue
1317 if f[0] == "/": continue
1318 self.ui.note("getting %s\n" % f)
1318 self.ui.note("getting %s\n" % f)
1319 t = self.file(f).read(get[f])
1319 t = self.file(f).read(get[f])
1320 try:
1320 try:
1321 self.wfile(f, "w").write(t)
1321 self.wfile(f, "w").write(t)
1322 except IOError:
1322 except IOError:
1323 os.makedirs(os.path.dirname(self.wjoin(f)))
1323 os.makedirs(os.path.dirname(self.wjoin(f)))
1324 self.wfile(f, "w").write(t)
1324 self.wfile(f, "w").write(t)
1325 util.set_exec(self.wjoin(f), mf2[f])
1325 util.set_exec(self.wjoin(f), mf2[f])
1326 if moddirstate:
1326 if moddirstate:
1327 self.dirstate.update([f], mode)
1327 self.dirstate.update([f], mode)
1328
1328
1329 # merge the tricky bits
1329 # merge the tricky bits
1330 files = merge.keys()
1330 files = merge.keys()
1331 files.sort()
1331 files.sort()
1332 for f in files:
1332 for f in files:
1333 self.ui.status("merging %s\n" % f)
1333 self.ui.status("merging %s\n" % f)
1334 m, o, flag = merge[f]
1334 m, o, flag = merge[f]
1335 self.merge3(f, m, o)
1335 self.merge3(f, m, o)
1336 util.set_exec(self.wjoin(f), flag)
1336 util.set_exec(self.wjoin(f), flag)
1337 if moddirstate:
1337 if moddirstate:
1338 self.dirstate.update([f], 'm')
1338 self.dirstate.update([f], 'm')
1339
1339
1340 for f in remove:
1340 for f in remove:
1341 self.ui.note("removing %s\n" % f)
1341 self.ui.note("removing %s\n" % f)
1342 os.unlink(f)
1342 os.unlink(f)
1343 # try removing directories that might now be empty
1343 # try removing directories that might now be empty
1344 try: os.removedirs(os.path.dirname(f))
1344 try: os.removedirs(os.path.dirname(f))
1345 except: pass
1345 except: pass
1346 if moddirstate:
1346 if moddirstate:
1347 if mode == 'n':
1347 if mode == 'n':
1348 self.dirstate.forget(remove)
1348 self.dirstate.forget(remove)
1349 else:
1349 else:
1350 self.dirstate.update(remove, 'r')
1350 self.dirstate.update(remove, 'r')
1351
1351
1352 def merge3(self, fn, my, other):
1352 def merge3(self, fn, my, other):
1353 """perform a 3-way merge in the working directory"""
1353 """perform a 3-way merge in the working directory"""
1354
1354
1355 def temp(prefix, node):
1355 def temp(prefix, node):
1356 pre = "%s~%s." % (os.path.basename(fn), prefix)
1356 pre = "%s~%s." % (os.path.basename(fn), prefix)
1357 (fd, name) = tempfile.mkstemp("", pre)
1357 (fd, name) = tempfile.mkstemp("", pre)
1358 f = os.fdopen(fd, "wb")
1358 f = os.fdopen(fd, "wb")
1359 f.write(fl.revision(node))
1359 f.write(fl.revision(node))
1360 f.close()
1360 f.close()
1361 return name
1361 return name
1362
1362
1363 fl = self.file(fn)
1363 fl = self.file(fn)
1364 base = fl.ancestor(my, other)
1364 base = fl.ancestor(my, other)
1365 a = self.wjoin(fn)
1365 a = self.wjoin(fn)
1366 b = temp("base", base)
1366 b = temp("base", base)
1367 c = temp("other", other)
1367 c = temp("other", other)
1368
1368
1369 self.ui.note("resolving %s\n" % fn)
1369 self.ui.note("resolving %s\n" % fn)
1370 self.ui.debug("file %s: other %s ancestor %s\n" %
1370 self.ui.debug("file %s: other %s ancestor %s\n" %
1371 (fn, short(other), short(base)))
1371 (fn, short(other), short(base)))
1372
1372
1373 cmd = self.ui.config("ui", "merge") or \
1373 cmd = self.ui.config("ui", "merge") or \
1374 os.environ.get("HGMERGE", "hgmerge")
1374 os.environ.get("HGMERGE", "hgmerge")
1375 r = os.system("%s %s %s %s" % (cmd, a, b, c))
1375 r = os.system("%s %s %s %s" % (cmd, a, b, c))
1376 if r:
1376 if r:
1377 self.ui.warn("merging %s failed!\n" % fn)
1377 self.ui.warn("merging %s failed!\n" % fn)
1378
1378
1379 os.unlink(b)
1379 os.unlink(b)
1380 os.unlink(c)
1380 os.unlink(c)
1381
1381
1382 def verify(self):
1382 def verify(self):
1383 filelinkrevs = {}
1383 filelinkrevs = {}
1384 filenodes = {}
1384 filenodes = {}
1385 changesets = revisions = files = 0
1385 changesets = revisions = files = 0
1386 errors = 0
1386 errors = 0
1387
1387
1388 seen = {}
1388 seen = {}
1389 self.ui.status("checking changesets\n")
1389 self.ui.status("checking changesets\n")
1390 for i in range(self.changelog.count()):
1390 for i in range(self.changelog.count()):
1391 changesets += 1
1391 changesets += 1
1392 n = self.changelog.node(i)
1392 n = self.changelog.node(i)
1393 if n in seen:
1393 if n in seen:
1394 self.ui.warn("duplicate changeset at revision %d\n" % i)
1394 self.ui.warn("duplicate changeset at revision %d\n" % i)
1395 errors += 1
1395 errors += 1
1396 seen[n] = 1
1396 seen[n] = 1
1397
1397
1398 for p in self.changelog.parents(n):
1398 for p in self.changelog.parents(n):
1399 if p not in self.changelog.nodemap:
1399 if p not in self.changelog.nodemap:
1400 self.ui.warn("changeset %s has unknown parent %s\n" %
1400 self.ui.warn("changeset %s has unknown parent %s\n" %
1401 (short(n), short(p)))
1401 (short(n), short(p)))
1402 errors += 1
1402 errors += 1
1403 try:
1403 try:
1404 changes = self.changelog.read(n)
1404 changes = self.changelog.read(n)
1405 except Exception, inst:
1405 except Exception, inst:
1406 self.ui.warn("unpacking changeset %s: %s\n" % (short(n), inst))
1406 self.ui.warn("unpacking changeset %s: %s\n" % (short(n), inst))
1407 errors += 1
1407 errors += 1
1408
1408
1409 for f in changes[3]:
1409 for f in changes[3]:
1410 filelinkrevs.setdefault(f, []).append(i)
1410 filelinkrevs.setdefault(f, []).append(i)
1411
1411
1412 seen = {}
1412 seen = {}
1413 self.ui.status("checking manifests\n")
1413 self.ui.status("checking manifests\n")
1414 for i in range(self.manifest.count()):
1414 for i in range(self.manifest.count()):
1415 n = self.manifest.node(i)
1415 n = self.manifest.node(i)
1416 if n in seen:
1416 if n in seen:
1417 self.ui.warn("duplicate manifest at revision %d\n" % i)
1417 self.ui.warn("duplicate manifest at revision %d\n" % i)
1418 errors += 1
1418 errors += 1
1419 seen[n] = 1
1419 seen[n] = 1
1420
1420
1421 for p in self.manifest.parents(n):
1421 for p in self.manifest.parents(n):
1422 if p not in self.manifest.nodemap:
1422 if p not in self.manifest.nodemap:
1423 self.ui.warn("manifest %s has unknown parent %s\n" %
1423 self.ui.warn("manifest %s has unknown parent %s\n" %
1424 (short(n), short(p)))
1424 (short(n), short(p)))
1425 errors += 1
1425 errors += 1
1426
1426
1427 try:
1427 try:
1428 delta = mdiff.patchtext(self.manifest.delta(n))
1428 delta = mdiff.patchtext(self.manifest.delta(n))
1429 except KeyboardInterrupt:
1429 except KeyboardInterrupt:
1430 self.ui.warn("aborted")
1430 self.ui.warn("aborted")
1431 sys.exit(0)
1431 sys.exit(0)
1432 except Exception, inst:
1432 except Exception, inst:
1433 self.ui.warn("unpacking manifest %s: %s\n"
1433 self.ui.warn("unpacking manifest %s: %s\n"
1434 % (short(n), inst))
1434 % (short(n), inst))
1435 errors += 1
1435 errors += 1
1436
1436
1437 ff = [ l.split('\0') for l in delta.splitlines() ]
1437 ff = [ l.split('\0') for l in delta.splitlines() ]
1438 for f, fn in ff:
1438 for f, fn in ff:
1439 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
1439 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
1440
1440
1441 self.ui.status("crosschecking files in changesets and manifests\n")
1441 self.ui.status("crosschecking files in changesets and manifests\n")
1442 for f in filenodes:
1442 for f in filenodes:
1443 if f not in filelinkrevs:
1443 if f not in filelinkrevs:
1444 self.ui.warn("file %s in manifest but not in changesets\n" % f)
1444 self.ui.warn("file %s in manifest but not in changesets\n" % f)
1445 errors += 1
1445 errors += 1
1446
1446
1447 for f in filelinkrevs:
1447 for f in filelinkrevs:
1448 if f not in filenodes:
1448 if f not in filenodes:
1449 self.ui.warn("file %s in changeset but not in manifest\n" % f)
1449 self.ui.warn("file %s in changeset but not in manifest\n" % f)
1450 errors += 1
1450 errors += 1
1451
1451
1452 self.ui.status("checking files\n")
1452 self.ui.status("checking files\n")
1453 ff = filenodes.keys()
1453 ff = filenodes.keys()
1454 ff.sort()
1454 ff.sort()
1455 for f in ff:
1455 for f in ff:
1456 if f == "/dev/null": continue
1456 if f == "/dev/null": continue
1457 files += 1
1457 files += 1
1458 fl = self.file(f)
1458 fl = self.file(f)
1459 nodes = { nullid: 1 }
1459 nodes = { nullid: 1 }
1460 seen = {}
1460 seen = {}
1461 for i in range(fl.count()):
1461 for i in range(fl.count()):
1462 revisions += 1
1462 revisions += 1
1463 n = fl.node(i)
1463 n = fl.node(i)
1464
1464
1465 if n in seen:
1465 if n in seen:
1466 self.ui.warn("%s: duplicate revision %d\n" % (f, i))
1466 self.ui.warn("%s: duplicate revision %d\n" % (f, i))
1467 errors += 1
1467 errors += 1
1468
1468
1469 if n not in filenodes[f]:
1469 if n not in filenodes[f]:
1470 self.ui.warn("%s: %d:%s not in manifests\n"
1470 self.ui.warn("%s: %d:%s not in manifests\n"
1471 % (f, i, short(n)))
1471 % (f, i, short(n)))
1472 errors += 1
1472 errors += 1
1473 else:
1473 else:
1474 del filenodes[f][n]
1474 del filenodes[f][n]
1475
1475
1476 flr = fl.linkrev(n)
1476 flr = fl.linkrev(n)
1477 if flr not in filelinkrevs[f]:
1477 if flr not in filelinkrevs[f]:
1478 self.ui.warn("%s:%s points to unexpected changeset %d\n"
1478 self.ui.warn("%s:%s points to unexpected changeset %d\n"
1479 % (f, short(n), fl.linkrev(n)))
1479 % (f, short(n), fl.linkrev(n)))
1480 errors += 1
1480 errors += 1
1481 else:
1481 else:
1482 filelinkrevs[f].remove(flr)
1482 filelinkrevs[f].remove(flr)
1483
1483
1484 # verify contents
1484 # verify contents
1485 try:
1485 try:
1486 t = fl.read(n)
1486 t = fl.read(n)
1487 except Exception, inst:
1487 except Exception, inst:
1488 self.ui.warn("unpacking file %s %s: %s\n"
1488 self.ui.warn("unpacking file %s %s: %s\n"
1489 % (f, short(n), inst))
1489 % (f, short(n), inst))
1490 errors += 1
1490 errors += 1
1491
1491
1492 # verify parents
1492 # verify parents
1493 (p1, p2) = fl.parents(n)
1493 (p1, p2) = fl.parents(n)
1494 if p1 not in nodes:
1494 if p1 not in nodes:
1495 self.ui.warn("file %s:%s unknown parent 1 %s" %
1495 self.ui.warn("file %s:%s unknown parent 1 %s" %
1496 (f, short(n), short(p1)))
1496 (f, short(n), short(p1)))
1497 errors += 1
1497 errors += 1
1498 if p2 not in nodes:
1498 if p2 not in nodes:
1499 self.ui.warn("file %s:%s unknown parent 2 %s" %
1499 self.ui.warn("file %s:%s unknown parent 2 %s" %
1500 (f, short(n), short(p1)))
1500 (f, short(n), short(p1)))
1501 errors += 1
1501 errors += 1
1502 nodes[n] = 1
1502 nodes[n] = 1
1503
1503
1504 # cross-check
1504 # cross-check
1505 for node in filenodes[f]:
1505 for node in filenodes[f]:
1506 self.ui.warn("node %s in manifests not in %s\n"
1506 self.ui.warn("node %s in manifests not in %s\n"
1507 % (hex(n), f))
1507 % (hex(n), f))
1508 errors += 1
1508 errors += 1
1509
1509
1510 self.ui.status("%d files, %d changesets, %d total revisions\n" %
1510 self.ui.status("%d files, %d changesets, %d total revisions\n" %
1511 (files, changesets, revisions))
1511 (files, changesets, revisions))
1512
1512
1513 if errors:
1513 if errors:
1514 self.ui.warn("%d integrity errors encountered!\n" % errors)
1514 self.ui.warn("%d integrity errors encountered!\n" % errors)
1515 return 1
1515 return 1
1516
1516
1517 class httprepository:
1517 class httprepository:
1518 def __init__(self, ui, path):
1518 def __init__(self, ui, path):
1519 self.url = path
1519 self.url = path
1520 self.ui = ui
1520 self.ui = ui
1521 no_list = [ "localhost", "127.0.0.1" ]
1521 no_list = [ "localhost", "127.0.0.1" ]
1522 host = ui.config("http_proxy", "host")
1522 host = ui.config("http_proxy", "host")
1523 if host is None:
1523 if host is None:
1524 host = os.environ.get("http_proxy")
1524 host = os.environ.get("http_proxy")
1525 if host and host.startswith('http://'):
1525 if host and host.startswith('http://'):
1526 host = host[7:]
1526 host = host[7:]
1527 user = ui.config("http_proxy", "user")
1527 user = ui.config("http_proxy", "user")
1528 passwd = ui.config("http_proxy", "passwd")
1528 passwd = ui.config("http_proxy", "passwd")
1529 no = ui.config("http_proxy", "no")
1529 no = ui.config("http_proxy", "no")
1530 if no is None:
1530 if no is None:
1531 no = os.environ.get("no_proxy")
1531 no = os.environ.get("no_proxy")
1532 if no:
1532 if no:
1533 no_list = no_list + no.split(",")
1533 no_list = no_list + no.split(",")
1534
1534
1535 no_proxy = 0
1535 no_proxy = 0
1536 for h in no_list:
1536 for h in no_list:
1537 if (path.startswith("http://" + h + "/") or
1537 if (path.startswith("http://" + h + "/") or
1538 path.startswith("http://" + h + ":") or
1538 path.startswith("http://" + h + ":") or
1539 path == "http://" + h):
1539 path == "http://" + h):
1540 no_proxy = 1
1540 no_proxy = 1
1541
1541
1542 # Note: urllib2 takes proxy values from the environment and those will
1542 # Note: urllib2 takes proxy values from the environment and those will
1543 # take precedence
1543 # take precedence
1544 for env in ["HTTP_PROXY", "http_proxy", "no_proxy"]:
1544 for env in ["HTTP_PROXY", "http_proxy", "no_proxy"]:
1545 if os.environ.has_key(env):
1545 if os.environ.has_key(env):
1546 del os.environ[env]
1546 del os.environ[env]
1547
1547
1548 proxy_handler = urllib2.BaseHandler()
1548 proxy_handler = urllib2.BaseHandler()
1549 if host and not no_proxy:
1549 if host and not no_proxy:
1550 proxy_handler = urllib2.ProxyHandler({"http" : "http://" + host})
1550 proxy_handler = urllib2.ProxyHandler({"http" : "http://" + host})
1551
1551
1552 authinfo = None
1552 authinfo = None
1553 if user and passwd:
1553 if user and passwd:
1554 passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
1554 passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
1555 passmgr.add_password(None, host, user, passwd)
1555 passmgr.add_password(None, host, user, passwd)
1556 authinfo = urllib2.ProxyBasicAuthHandler(passmgr)
1556 authinfo = urllib2.ProxyBasicAuthHandler(passmgr)
1557
1557
1558 opener = urllib2.build_opener(proxy_handler, authinfo)
1558 opener = urllib2.build_opener(proxy_handler, authinfo)
1559 urllib2.install_opener(opener)
1559 urllib2.install_opener(opener)
1560
1560
1561 def dev(self):
1561 def dev(self):
1562 return -1
1562 return -1
1563
1563
1564 def do_cmd(self, cmd, **args):
1564 def do_cmd(self, cmd, **args):
1565 self.ui.debug("sending %s command\n" % cmd)
1565 self.ui.debug("sending %s command\n" % cmd)
1566 q = {"cmd": cmd}
1566 q = {"cmd": cmd}
1567 q.update(args)
1567 q.update(args)
1568 qs = urllib.urlencode(q)
1568 qs = urllib.urlencode(q)
1569 cu = "%s?%s" % (self.url, qs)
1569 cu = "%s?%s" % (self.url, qs)
1570 return urllib2.urlopen(cu)
1570 return urllib2.urlopen(cu)
1571
1571
1572 def heads(self):
1572 def heads(self):
1573 d = self.do_cmd("heads").read()
1573 d = self.do_cmd("heads").read()
1574 try:
1574 try:
1575 return map(bin, d[:-1].split(" "))
1575 return map(bin, d[:-1].split(" "))
1576 except:
1576 except:
1577 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1577 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1578 raise
1578 raise
1579
1579
1580 def branches(self, nodes):
1580 def branches(self, nodes):
1581 n = " ".join(map(hex, nodes))
1581 n = " ".join(map(hex, nodes))
1582 d = self.do_cmd("branches", nodes=n).read()
1582 d = self.do_cmd("branches", nodes=n).read()
1583 try:
1583 try:
1584 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
1584 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
1585 return br
1585 return br
1586 except:
1586 except:
1587 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1587 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1588 raise
1588 raise
1589
1589
1590 def between(self, pairs):
1590 def between(self, pairs):
1591 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
1591 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
1592 d = self.do_cmd("between", pairs=n).read()
1592 d = self.do_cmd("between", pairs=n).read()
1593 try:
1593 try:
1594 p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
1594 p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
1595 return p
1595 return p
1596 except:
1596 except:
1597 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1597 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1598 raise
1598 raise
1599
1599
1600 def changegroup(self, nodes):
1600 def changegroup(self, nodes):
1601 n = " ".join(map(hex, nodes))
1601 n = " ".join(map(hex, nodes))
1602 f = self.do_cmd("changegroup", roots=n)
1602 f = self.do_cmd("changegroup", roots=n)
1603 bytes = 0
1603 bytes = 0
1604
1604
1605 class zread:
1605 class zread:
1606 def __init__(self, f):
1606 def __init__(self, f):
1607 self.zd = zlib.decompressobj()
1607 self.zd = zlib.decompressobj()
1608 self.f = f
1608 self.f = f
1609 self.buf = ""
1609 self.buf = ""
1610 def read(self, l):
1610 def read(self, l):
1611 while l > len(self.buf):
1611 while l > len(self.buf):
1612 r = f.read(4096)
1612 r = f.read(4096)
1613 if r:
1613 if r:
1614 self.buf += self.zd.decompress(r)
1614 self.buf += self.zd.decompress(r)
1615 else:
1615 else:
1616 self.buf += self.zd.flush()
1616 self.buf += self.zd.flush()
1617 break
1617 break
1618 d, self.buf = self.buf[:l], self.buf[l:]
1618 d, self.buf = self.buf[:l], self.buf[l:]
1619 return d
1619 return d
1620
1620
1621 return zread(f)
1621 return zread(f)
1622
1622
1623 class remotelock:
1624 def __init__(self, repo):
1625 self.repo = repo
1626 def release(self):
1627 self.repo.unlock()
1628 self.repo = None
1629 def __del__(self):
1630 if self.repo:
1631 self.release()
1623
1632
1624 class sshrepository:
1633 class sshrepository:
1625 def __init__(self, ui, path):
1634 def __init__(self, ui, path):
1626 self.url = path
1635 self.url = path
1627 self.ui = ui
1636 self.ui = ui
1628
1637
1629 m = re.match(r'ssh://(([^@]+)@)?([^:/]+)(:(\d+))?(/(.*))?', path)
1638 m = re.match(r'ssh://(([^@]+)@)?([^:/]+)(:(\d+))?(/(.*))?', path)
1630 if not m:
1639 if not m:
1631 raise RepoError("couldn't parse destination %s\n" % path)
1640 raise RepoError("couldn't parse destination %s\n" % path)
1632
1641
1633 self.user = m.group(2)
1642 self.user = m.group(2)
1634 self.host = m.group(3)
1643 self.host = m.group(3)
1635 self.port = m.group(5)
1644 self.port = m.group(5)
1636 self.path = m.group(7)
1645 self.path = m.group(7)
1637
1646
1638 args = self.user and ("%s@%s" % (self.user, self.host)) or self.host
1647 args = self.user and ("%s@%s" % (self.user, self.host)) or self.host
1639 args = self.port and ("%s -p %s") % (args, self.port) or args
1648 args = self.port and ("%s -p %s") % (args, self.port) or args
1640 path = self.path or ""
1649 path = self.path or ""
1641
1650
1642 cmd = "ssh %s 'hg -R %s serve --stdio'"
1651 cmd = "ssh %s 'hg -R %s serve --stdio'"
1643 cmd = cmd % (args, path)
1652 cmd = cmd % (args, path)
1644
1653
1645 self.pipeo, self.pipei = os.popen2(cmd)
1654 self.pipeo, self.pipei = os.popen2(cmd)
1646
1655
1647 def __del__(self):
1656 def __del__(self):
1648 self.pipeo.close()
1657 self.pipeo.close()
1649 self.pipei.close()
1658 self.pipei.close()
1650
1659
1651 def dev(self):
1660 def dev(self):
1652 return -1
1661 return -1
1653
1662
1654 def do_cmd(self, cmd, **args):
1663 def do_cmd(self, cmd, **args):
1655 self.ui.debug("sending %s command\n" % cmd)
1664 self.ui.debug("sending %s command\n" % cmd)
1656 self.pipeo.write("%s\n" % cmd)
1665 self.pipeo.write("%s\n" % cmd)
1657 for k, v in args.items():
1666 for k, v in args.items():
1658 self.pipeo.write("%s %d\n" % (k, len(v)))
1667 self.pipeo.write("%s %d\n" % (k, len(v)))
1659 self.pipeo.write(v)
1668 self.pipeo.write(v)
1660 self.pipeo.flush()
1669 self.pipeo.flush()
1661
1670
1662 return self.pipei
1671 return self.pipei
1663
1672
1664 def call(self, cmd, **args):
1673 def call(self, cmd, **args):
1665 r = self.do_cmd(cmd, **args)
1674 r = self.do_cmd(cmd, **args)
1666 l = int(r.readline())
1675 l = int(r.readline())
1667 return r.read(l)
1676 return r.read(l)
1668
1677
1678 def lock(self):
1679 self.call("lock")
1680 return remotelock(self)
1681
1682 def unlock(self):
1683 self.call("unlock")
1684
1669 def heads(self):
1685 def heads(self):
1670 d = self.call("heads")
1686 d = self.call("heads")
1671 try:
1687 try:
1672 return map(bin, d[:-1].split(" "))
1688 return map(bin, d[:-1].split(" "))
1673 except:
1689 except:
1674 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1690 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1675 raise
1691 raise
1676
1692
1677 def branches(self, nodes):
1693 def branches(self, nodes):
1678 n = " ".join(map(hex, nodes))
1694 n = " ".join(map(hex, nodes))
1679 d = self.call("branches", nodes=n)
1695 d = self.call("branches", nodes=n)
1680 try:
1696 try:
1681 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
1697 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
1682 return br
1698 return br
1683 except:
1699 except:
1684 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1700 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1685 raise
1701 raise
1686
1702
1687 def between(self, pairs):
1703 def between(self, pairs):
1688 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
1704 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
1689 d = self.call("between", pairs=n)
1705 d = self.call("between", pairs=n)
1690 try:
1706 try:
1691 p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
1707 p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
1692 return p
1708 return p
1693 except:
1709 except:
1694 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1710 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1695 raise
1711 raise
1696
1712
1697 def changegroup(self, nodes):
1713 def changegroup(self, nodes):
1698 n = " ".join(map(hex, nodes))
1714 n = " ".join(map(hex, nodes))
1699 f = self.do_cmd("changegroup", roots=n)
1715 f = self.do_cmd("changegroup", roots=n)
1700 return self.pipei
1716 return self.pipei
1701
1717
1702 def repository(ui, path=None, create=0):
1718 def repository(ui, path=None, create=0):
1703 if path:
1719 if path:
1704 if path.startswith("http://"):
1720 if path.startswith("http://"):
1705 return httprepository(ui, path)
1721 return httprepository(ui, path)
1706 if path.startswith("hg://"):
1722 if path.startswith("hg://"):
1707 return httprepository(ui, path.replace("hg://", "http://"))
1723 return httprepository(ui, path.replace("hg://", "http://"))
1708 if path.startswith("old-http://"):
1724 if path.startswith("old-http://"):
1709 return localrepository(ui, path.replace("old-http://", "http://"))
1725 return localrepository(ui, path.replace("old-http://", "http://"))
1710 if path.startswith("ssh://"):
1726 if path.startswith("ssh://"):
1711 return sshrepository(ui, path)
1727 return sshrepository(ui, path)
1712
1728
1713 return localrepository(ui, path, create)
1729 return localrepository(ui, path, create)
General Comments 0
You need to be logged in to leave comments. Login now