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