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