##// END OF EJS Templates
Merge with upstream
Thomas Arendsen Hein -
r793:445970cc merge default
parent child Browse files
Show More
@@ -1,1368 +1,1410
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from demandload import demandload
8 from demandload import demandload
9 demandload(globals(), "os re sys signal shutil")
9 demandload(globals(), "os re sys signal shutil")
10 demandload(globals(), "fancyopts ui hg util")
10 demandload(globals(), "fancyopts ui hg util")
11 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
11 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
12 demandload(globals(), "errno socket version struct")
12 demandload(globals(), "errno socket version struct atexit")
13
13
14 class UnknownCommand(Exception):
14 class UnknownCommand(Exception):
15 """Exception raised if command is not in the command table."""
15 """Exception raised if command is not in the command table."""
16
16
17 class Abort(Exception):
17 class Abort(Exception):
18 """Raised if a command needs to print an error and exit."""
18 """Raised if a command needs to print an error and exit."""
19
19
20 def filterfiles(filters, files):
20 def filterfiles(filters, files):
21 l = [x for x in files if x in filters]
21 l = [x for x in files if x in filters]
22
22
23 for t in filters:
23 for t in filters:
24 if t and t[-1] != "/":
24 if t and t[-1] != "/":
25 t += "/"
25 t += "/"
26 l += [x for x in files if x.startswith(t)]
26 l += [x for x in files if x.startswith(t)]
27 return l
27 return l
28
28
29 def relfilter(repo, files):
29 def relfilter(repo, files):
30 cwd = repo.getcwd()
30 cwd = repo.getcwd()
31 if cwd:
31 if cwd:
32 return filterfiles([util.pconvert(cwd)], files)
32 return filterfiles([util.pconvert(cwd)], files)
33 return files
33 return files
34
34
35 def relpath(repo, args):
35 def relpath(repo, args):
36 cwd = repo.getcwd()
36 cwd = repo.getcwd()
37 if cwd:
37 if cwd:
38 return [util.pconvert(os.path.normpath(os.path.join(cwd, x)))
38 return [util.pconvert(os.path.normpath(os.path.join(cwd, x)))
39 for x in args]
39 for x in args]
40 return args
40 return args
41
41
42 def matchpats(cwd, pats = [], opts = {}, head = ''):
42 def matchpats(cwd, pats = [], opts = {}, head = ''):
43 return util.matcher(cwd, pats, opts.get('include'),
43 return util.matcher(cwd, pats, opts.get('include'),
44 opts.get('exclude'), head)
44 opts.get('exclude'), head)
45
45
46 def walk(repo, pats, opts, head = ''):
46 def walk(repo, pats, opts, head = ''):
47 cwd = repo.getcwd()
47 cwd = repo.getcwd()
48 c = 0
48 c = 0
49 if cwd: c = len(cwd) + 1
49 if cwd: c = len(cwd) + 1
50 for src, fn in repo.walk(match = matchpats(cwd, pats, opts, head)):
50 for src, fn in repo.walk(match = matchpats(cwd, pats, opts, head)):
51 yield src, fn, fn[c:]
51 yield src, fn, fn[c:]
52
52
53 revrangesep = ':'
53 revrangesep = ':'
54
54
55 def revrange(ui, repo, revs, revlog=None):
55 def revrange(ui, repo, revs, revlog=None):
56 if revlog is None:
56 if revlog is None:
57 revlog = repo.changelog
57 revlog = repo.changelog
58 revcount = revlog.count()
58 revcount = revlog.count()
59 def fix(val, defval):
59 def fix(val, defval):
60 if not val:
60 if not val:
61 return defval
61 return defval
62 try:
62 try:
63 num = int(val)
63 num = int(val)
64 if str(num) != val:
64 if str(num) != val:
65 raise ValueError
65 raise ValueError
66 if num < 0:
66 if num < 0:
67 num += revcount
67 num += revcount
68 if not (0 <= num < revcount):
68 if not (0 <= num < revcount):
69 raise ValueError
69 raise ValueError
70 except ValueError:
70 except ValueError:
71 try:
71 try:
72 num = repo.changelog.rev(repo.lookup(val))
72 num = repo.changelog.rev(repo.lookup(val))
73 except KeyError:
73 except KeyError:
74 try:
74 try:
75 num = revlog.rev(revlog.lookup(val))
75 num = revlog.rev(revlog.lookup(val))
76 except KeyError:
76 except KeyError:
77 raise Abort('invalid revision identifier %s', val)
77 raise Abort('invalid revision identifier %s', val)
78 return num
78 return num
79 for spec in revs:
79 for spec in revs:
80 if spec.find(revrangesep) >= 0:
80 if spec.find(revrangesep) >= 0:
81 start, end = spec.split(revrangesep, 1)
81 start, end = spec.split(revrangesep, 1)
82 start = fix(start, 0)
82 start = fix(start, 0)
83 end = fix(end, revcount - 1)
83 end = fix(end, revcount - 1)
84 if end > start:
84 if end > start:
85 end += 1
85 end += 1
86 step = 1
86 step = 1
87 else:
87 else:
88 end -= 1
88 end -= 1
89 step = -1
89 step = -1
90 for rev in xrange(start, end, step):
90 for rev in xrange(start, end, step):
91 yield str(rev)
91 yield str(rev)
92 else:
92 else:
93 yield spec
93 yield spec
94
94
95 def make_filename(repo, r, pat, node=None,
95 def make_filename(repo, r, pat, node=None,
96 total=None, seqno=None, revwidth=None):
96 total=None, seqno=None, revwidth=None):
97 node_expander = {
97 node_expander = {
98 'H': lambda: hg.hex(node),
98 'H': lambda: hg.hex(node),
99 'R': lambda: str(r.rev(node)),
99 'R': lambda: str(r.rev(node)),
100 'h': lambda: hg.short(node),
100 'h': lambda: hg.short(node),
101 }
101 }
102 expander = {
102 expander = {
103 '%': lambda: '%',
103 '%': lambda: '%',
104 'b': lambda: os.path.basename(repo.root),
104 'b': lambda: os.path.basename(repo.root),
105 }
105 }
106
106
107 try:
107 try:
108 if node:
108 if node:
109 expander.update(node_expander)
109 expander.update(node_expander)
110 if node and revwidth is not None:
110 if node and revwidth is not None:
111 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
111 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
112 if total is not None:
112 if total is not None:
113 expander['N'] = lambda: str(total)
113 expander['N'] = lambda: str(total)
114 if seqno is not None:
114 if seqno is not None:
115 expander['n'] = lambda: str(seqno)
115 expander['n'] = lambda: str(seqno)
116 if total is not None and seqno is not None:
116 if total is not None and seqno is not None:
117 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
117 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
118
118
119 newname = []
119 newname = []
120 patlen = len(pat)
120 patlen = len(pat)
121 i = 0
121 i = 0
122 while i < patlen:
122 while i < patlen:
123 c = pat[i]
123 c = pat[i]
124 if c == '%':
124 if c == '%':
125 i += 1
125 i += 1
126 c = pat[i]
126 c = pat[i]
127 c = expander[c]()
127 c = expander[c]()
128 newname.append(c)
128 newname.append(c)
129 i += 1
129 i += 1
130 return ''.join(newname)
130 return ''.join(newname)
131 except KeyError, inst:
131 except KeyError, inst:
132 raise Abort("invalid format spec '%%%s' in output file name",
132 raise Abort("invalid format spec '%%%s' in output file name",
133 inst.args[0])
133 inst.args[0])
134
134
135 def make_file(repo, r, pat, node=None,
135 def make_file(repo, r, pat, node=None,
136 total=None, seqno=None, revwidth=None, mode='wb'):
136 total=None, seqno=None, revwidth=None, mode='wb'):
137 if not pat or pat == '-':
137 if not pat or pat == '-':
138 if 'w' in mode: return sys.stdout
138 if 'w' in mode: return sys.stdout
139 else: return sys.stdin
139 else: return sys.stdin
140 if hasattr(pat, 'write') and 'w' in mode:
140 if hasattr(pat, 'write') and 'w' in mode:
141 return pat
141 return pat
142 if hasattr(pat, 'read') and 'r' in mode:
142 if hasattr(pat, 'read') and 'r' in mode:
143 return pat
143 return pat
144 return open(make_filename(repo, r, pat, node, total, seqno, revwidth),
144 return open(make_filename(repo, r, pat, node, total, seqno, revwidth),
145 mode)
145 mode)
146
146
147 def dodiff(fp, ui, repo, files=None, node1=None, node2=None):
147 def dodiff(fp, ui, repo, files=None, node1=None, node2=None):
148 def date(c):
148 def date(c):
149 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
149 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
150
150
151 (c, a, d, u) = repo.changes(node1, node2, files)
151 (c, a, d, u) = repo.changes(node1, node2, files)
152 if files:
152 if files:
153 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
153 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
154
154
155 if not c and not a and not d:
155 if not c and not a and not d:
156 return
156 return
157
157
158 if node2:
158 if node2:
159 change = repo.changelog.read(node2)
159 change = repo.changelog.read(node2)
160 mmap2 = repo.manifest.read(change[0])
160 mmap2 = repo.manifest.read(change[0])
161 date2 = date(change)
161 date2 = date(change)
162 def read(f):
162 def read(f):
163 return repo.file(f).read(mmap2[f])
163 return repo.file(f).read(mmap2[f])
164 else:
164 else:
165 date2 = time.asctime()
165 date2 = time.asctime()
166 if not node1:
166 if not node1:
167 node1 = repo.dirstate.parents()[0]
167 node1 = repo.dirstate.parents()[0]
168 def read(f):
168 def read(f):
169 return repo.wfile(f).read()
169 return repo.wfile(f).read()
170
170
171 if ui.quiet:
171 if ui.quiet:
172 r = None
172 r = None
173 else:
173 else:
174 hexfunc = ui.verbose and hg.hex or hg.short
174 hexfunc = ui.verbose and hg.hex or hg.short
175 r = [hexfunc(node) for node in [node1, node2] if node]
175 r = [hexfunc(node) for node in [node1, node2] if node]
176
176
177 change = repo.changelog.read(node1)
177 change = repo.changelog.read(node1)
178 mmap = repo.manifest.read(change[0])
178 mmap = repo.manifest.read(change[0])
179 date1 = date(change)
179 date1 = date(change)
180
180
181 for f in c:
181 for f in c:
182 to = None
182 to = None
183 if f in mmap:
183 if f in mmap:
184 to = repo.file(f).read(mmap[f])
184 to = repo.file(f).read(mmap[f])
185 tn = read(f)
185 tn = read(f)
186 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
186 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
187 for f in a:
187 for f in a:
188 to = None
188 to = None
189 tn = read(f)
189 tn = read(f)
190 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
190 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
191 for f in d:
191 for f in d:
192 to = repo.file(f).read(mmap[f])
192 to = repo.file(f).read(mmap[f])
193 tn = None
193 tn = None
194 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
194 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
195
195
196 def show_changeset(ui, repo, rev=0, changenode=None, filelog=None):
196 def show_changeset(ui, repo, rev=0, changenode=None, filelog=None):
197 """show a single changeset or file revision"""
197 """show a single changeset or file revision"""
198 changelog = repo.changelog
198 changelog = repo.changelog
199 if filelog:
199 if filelog:
200 log = filelog
200 log = filelog
201 filerev = rev
201 filerev = rev
202 node = filenode = filelog.node(filerev)
202 node = filenode = filelog.node(filerev)
203 changerev = filelog.linkrev(filenode)
203 changerev = filelog.linkrev(filenode)
204 changenode = changenode or changelog.node(changerev)
204 changenode = changenode or changelog.node(changerev)
205 else:
205 else:
206 log = changelog
206 log = changelog
207 changerev = rev
207 changerev = rev
208 if changenode is None:
208 if changenode is None:
209 changenode = changelog.node(changerev)
209 changenode = changelog.node(changerev)
210 elif not changerev:
210 elif not changerev:
211 rev = changerev = changelog.rev(changenode)
211 rev = changerev = changelog.rev(changenode)
212 node = changenode
212 node = changenode
213
213
214 if ui.quiet:
214 if ui.quiet:
215 ui.write("%d:%s\n" % (rev, hg.hex(node)))
215 ui.write("%d:%s\n" % (rev, hg.hex(node)))
216 return
216 return
217
217
218 changes = changelog.read(changenode)
218 changes = changelog.read(changenode)
219
219
220 parents = [(log.rev(parent), hg.hex(parent))
220 parents = [(log.rev(p), ui.verbose and hg.hex(p) or hg.short(p))
221 for parent in log.parents(node)
221 for p in log.parents(node)
222 if ui.debugflag or parent != hg.nullid]
222 if ui.debugflag or p != hg.nullid]
223 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
223 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
224 parents = []
224 parents = []
225
225
226 ui.write("changeset: %d:%s\n" % (changerev, hg.hex(changenode)))
226 if ui.verbose:
227 ui.write("changeset: %d:%s\n" % (changerev, hg.hex(changenode)))
228 else:
229 ui.write("changeset: %d:%s\n" % (changerev, hg.short(changenode)))
230
227 for tag in repo.nodetags(changenode):
231 for tag in repo.nodetags(changenode):
228 ui.status("tag: %s\n" % tag)
232 ui.status("tag: %s\n" % tag)
229 for parent in parents:
233 for parent in parents:
230 ui.write("parent: %d:%s\n" % parent)
234 ui.write("parent: %d:%s\n" % parent)
231 if filelog:
235 if filelog:
232 ui.debug("file rev: %d:%s\n" % (filerev, hg.hex(filenode)))
236 ui.debug("file rev: %d:%s\n" % (filerev, hg.hex(filenode)))
233 ui.note("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
237
238 ui.debug("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
234 hg.hex(changes[0])))
239 hg.hex(changes[0])))
235 ui.status("user: %s\n" % changes[1])
240 ui.status("user: %s\n" % changes[1])
236 ui.status("date: %s\n" % time.asctime(
241 ui.status("date: %s\n" % time.asctime(
237 time.localtime(float(changes[2].split(' ')[0]))))
242 time.localtime(float(changes[2].split(' ')[0]))))
243
238 if ui.debugflag:
244 if ui.debugflag:
239 files = repo.changes(changelog.parents(changenode)[0], changenode)
245 files = repo.changes(changelog.parents(changenode)[0], changenode)
240 for key, value in zip(["files:", "files+:", "files-:"], files):
246 for key, value in zip(["files:", "files+:", "files-:"], files):
241 if value:
247 if value:
242 ui.note("%-12s %s\n" % (key, " ".join(value)))
248 ui.note("%-12s %s\n" % (key, " ".join(value)))
243 else:
249 else:
244 ui.note("files: %s\n" % " ".join(changes[3]))
250 ui.note("files: %s\n" % " ".join(changes[3]))
251
245 description = changes[4].strip()
252 description = changes[4].strip()
246 if description:
253 if description:
247 if ui.verbose:
254 if ui.verbose:
248 ui.status("description:\n")
255 ui.status("description:\n")
249 ui.status(description)
256 ui.status(description)
250 ui.status("\n\n")
257 ui.status("\n\n")
251 else:
258 else:
252 ui.status("summary: %s\n" % description.splitlines()[0])
259 ui.status("summary: %s\n" % description.splitlines()[0])
253 ui.status("\n")
260 ui.status("\n")
254
261
255 def show_version(ui):
262 def show_version(ui):
256 """output version and copyright information"""
263 """output version and copyright information"""
257 ui.write("Mercurial version %s\n" % version.get_version())
264 ui.write("Mercurial version %s\n" % version.get_version())
258 ui.status(
265 ui.status(
259 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
266 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
260 "This is free software; see the source for copying conditions. "
267 "This is free software; see the source for copying conditions. "
261 "There is NO\nwarranty; "
268 "There is NO\nwarranty; "
262 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
269 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
263 )
270 )
264
271
265 def help_(ui, cmd=None):
272 def help_(ui, cmd=None):
266 """show help for a given command or all commands"""
273 """show help for a given command or all commands"""
267 if cmd:
274 if cmd:
268 try:
275 try:
269 i = find(cmd)
276 i = find(cmd)
270 ui.write("%s\n\n" % i[2])
277 ui.write("%s\n\n" % i[2])
271
278
272 if i[1]:
279 if i[1]:
273 for s, l, d, c in i[1]:
280 for s, l, d, c in i[1]:
274 opt = ' '
281 opt = ' '
275 if s:
282 if s:
276 opt = opt + '-' + s + ' '
283 opt = opt + '-' + s + ' '
277 if l:
284 if l:
278 opt = opt + '--' + l + ' '
285 opt = opt + '--' + l + ' '
279 if d:
286 if d:
280 opt = opt + '(' + str(d) + ')'
287 opt = opt + '(' + str(d) + ')'
281 ui.write(opt, "\n")
288 ui.write(opt, "\n")
282 if c:
289 if c:
283 ui.write(' %s\n' % c)
290 ui.write(' %s\n' % c)
284 ui.write("\n")
291 ui.write("\n")
285
292
286 ui.write(i[0].__doc__, "\n")
293 ui.write(i[0].__doc__, "\n")
287 except UnknownCommand:
294 except UnknownCommand:
288 ui.warn("hg: unknown command %s\n" % cmd)
295 ui.warn("hg: unknown command %s\n" % cmd)
289 sys.exit(0)
296 sys.exit(0)
290 else:
297 else:
291 if ui.verbose:
298 if ui.verbose:
292 show_version(ui)
299 show_version(ui)
293 ui.write('\n')
300 ui.write('\n')
294 if ui.verbose:
301 if ui.verbose:
295 ui.write('hg commands:\n\n')
302 ui.write('hg commands:\n\n')
296 else:
303 else:
297 ui.write('basic hg commands (use "hg help -v" for more):\n\n')
304 ui.write('basic hg commands (use "hg help -v" for more):\n\n')
298
305
299 h = {}
306 h = {}
300 for c, e in table.items():
307 for c, e in table.items():
301 f = c.split("|")[0]
308 f = c.split("|")[0]
302 if not ui.verbose and not f.startswith("^"):
309 if not ui.verbose and not f.startswith("^"):
303 continue
310 continue
304 if not ui.debugflag and f.startswith("debug"):
311 if not ui.debugflag and f.startswith("debug"):
305 continue
312 continue
306 f = f.lstrip("^")
313 f = f.lstrip("^")
307 d = ""
314 d = ""
308 if e[0].__doc__:
315 if e[0].__doc__:
309 d = e[0].__doc__.splitlines(0)[0].rstrip()
316 d = e[0].__doc__.splitlines(0)[0].rstrip()
310 h[f] = d
317 h[f] = d
311
318
312 fns = h.keys()
319 fns = h.keys()
313 fns.sort()
320 fns.sort()
314 m = max(map(len, fns))
321 m = max(map(len, fns))
315 for f in fns:
322 for f in fns:
316 ui.write(' %-*s %s\n' % (m, f, h[f]))
323 ui.write(' %-*s %s\n' % (m, f, h[f]))
317
324
318 # Commands start here, listed alphabetically
325 # Commands start here, listed alphabetically
319
326
320 def add(ui, repo, *pats, **opts):
327 def add(ui, repo, *pats, **opts):
321 '''add the specified files on the next commit'''
328 '''add the specified files on the next commit'''
322 names = []
329 names = []
323 q = dict(zip(pats, pats))
330 q = dict(zip(pats, pats))
324 for src, abs, rel in walk(repo, pats, opts):
331 for src, abs, rel in walk(repo, pats, opts):
325 if rel in q or abs in q:
332 if rel in q or abs in q:
326 names.append(abs)
333 names.append(abs)
327 elif repo.dirstate.state(abs) == '?':
334 elif repo.dirstate.state(abs) == '?':
328 ui.status('adding %s\n' % rel)
335 ui.status('adding %s\n' % rel)
329 names.append(abs)
336 names.append(abs)
330 repo.add(names)
337 repo.add(names)
331
338
332 def addremove(ui, repo, *pats, **opts):
339 def addremove(ui, repo, *pats, **opts):
333 """add all new files, delete all missing files"""
340 """add all new files, delete all missing files"""
334 q = dict(zip(pats, pats))
341 q = dict(zip(pats, pats))
335 cwd = repo.getcwd()
342 cwd = repo.getcwd()
336 n = (cwd and len(cwd) + 1) or 0
343 n = (cwd and len(cwd) + 1) or 0
337 c, a, d, u = repo.changes(match = matchpats(cwd, pats, opts))
344 c, a, d, u = repo.changes(match = matchpats(cwd, pats, opts))
338 for f in u:
345 for f in u:
339 if f not in q:
346 if f not in q:
340 ui.status('adding %s\n' % f[n:])
347 ui.status('adding %s\n' % f[n:])
341 repo.add(u)
348 repo.add(u)
342 for f in d:
349 for f in d:
343 if f not in q:
350 if f not in q:
344 ui.status('removing %s\n' % f[n:])
351 ui.status('removing %s\n' % f[n:])
345 repo.remove(d)
352 repo.remove(d)
346
353
347 def annotate(ui, repo, *pats, **opts):
354 def annotate(ui, repo, *pats, **opts):
348 """show changeset information per file line"""
355 """show changeset information per file line"""
349 def getnode(rev):
356 def getnode(rev):
350 return hg.short(repo.changelog.node(rev))
357 return hg.short(repo.changelog.node(rev))
351
358
352 def getname(rev):
359 def getname(rev):
353 try:
360 try:
354 return bcache[rev]
361 return bcache[rev]
355 except KeyError:
362 except KeyError:
356 cl = repo.changelog.read(repo.changelog.node(rev))
363 cl = repo.changelog.read(repo.changelog.node(rev))
357 name = cl[1]
364 name = cl[1]
358 f = name.find('@')
365 f = name.find('@')
359 if f >= 0:
366 if f >= 0:
360 name = name[:f]
367 name = name[:f]
361 f = name.find('<')
368 f = name.find('<')
362 if f >= 0:
369 if f >= 0:
363 name = name[f+1:]
370 name = name[f+1:]
364 bcache[rev] = name
371 bcache[rev] = name
365 return name
372 return name
366
373
367 if not pats:
374 if not pats:
368 raise Abort('at least one file name or pattern required')
375 raise Abort('at least one file name or pattern required')
369
376
370 bcache = {}
377 bcache = {}
371 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
378 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
372 if not opts['user'] and not opts['changeset']:
379 if not opts['user'] and not opts['changeset']:
373 opts['number'] = 1
380 opts['number'] = 1
374
381
375 if opts['rev']:
382 if opts['rev']:
376 node = repo.changelog.lookup(opts['rev'])
383 node = repo.changelog.lookup(opts['rev'])
377 else:
384 else:
378 node = repo.dirstate.parents()[0]
385 node = repo.dirstate.parents()[0]
379 change = repo.changelog.read(node)
386 change = repo.changelog.read(node)
380 mmap = repo.manifest.read(change[0])
387 mmap = repo.manifest.read(change[0])
381 for src, abs, rel in walk(repo, pats, opts):
388 for src, abs, rel in walk(repo, pats, opts):
389 if abs not in mmap:
390 ui.warn("warning: %s is not in the repository!\n" % rel)
391 continue
392
382 lines = repo.file(abs).annotate(mmap[abs])
393 lines = repo.file(abs).annotate(mmap[abs])
383 pieces = []
394 pieces = []
384
395
385 for o, f in opmap:
396 for o, f in opmap:
386 if opts[o]:
397 if opts[o]:
387 l = [f(n) for n, dummy in lines]
398 l = [f(n) for n, dummy in lines]
388 m = max(map(len, l))
399 if l:
389 pieces.append(["%*s" % (m, x) for x in l])
400 m = max(map(len, l))
401 pieces.append(["%*s" % (m, x) for x in l])
390
402
391 for p, l in zip(zip(*pieces), lines):
403 if pieces:
392 ui.write("%s: %s" % (" ".join(p), l[1]))
404 for p, l in zip(zip(*pieces), lines):
405 ui.write("%s: %s" % (" ".join(p), l[1]))
393
406
394 def cat(ui, repo, file1, rev=None, **opts):
407 def cat(ui, repo, file1, rev=None, **opts):
395 """output the latest or given revision of a file"""
408 """output the latest or given revision of a file"""
396 r = repo.file(relpath(repo, [file1])[0])
409 r = repo.file(relpath(repo, [file1])[0])
397 if rev:
410 if rev:
398 n = r.lookup(rev)
411 n = r.lookup(rev)
399 else:
412 else:
400 n = r.tip()
413 n = r.tip()
401 fp = make_file(repo, r, opts['output'], node=n)
414 fp = make_file(repo, r, opts['output'], node=n)
402 fp.write(r.read(n))
415 fp.write(r.read(n))
403
416
404 def clone(ui, source, dest=None, **opts):
417 def clone(ui, source, dest=None, **opts):
405 """make a copy of an existing repository"""
418 """make a copy of an existing repository"""
406 if dest is None:
419 if dest is None:
407 dest = os.path.basename(os.path.normpath(source))
420 dest = os.path.basename(os.path.normpath(source))
408
421
409 if os.path.exists(dest):
422 if os.path.exists(dest):
410 ui.warn("abort: destination '%s' already exists\n" % dest)
423 ui.warn("abort: destination '%s' already exists\n" % dest)
411 return 1
424 return 1
412
425
413 class Dircleanup:
426 class Dircleanup:
414 def __init__(self, dir_):
427 def __init__(self, dir_):
415 self.rmtree = shutil.rmtree
428 self.rmtree = shutil.rmtree
416 self.dir_ = dir_
429 self.dir_ = dir_
417 os.mkdir(dir_)
430 os.mkdir(dir_)
418 def close(self):
431 def close(self):
419 self.dir_ = None
432 self.dir_ = None
420 def __del__(self):
433 def __del__(self):
421 if self.dir_:
434 if self.dir_:
422 self.rmtree(self.dir_, True)
435 self.rmtree(self.dir_, True)
423
436
424 d = Dircleanup(dest)
437 d = Dircleanup(dest)
425 abspath = source
438 abspath = source
426 source = ui.expandpath(source)
439 source = ui.expandpath(source)
427 other = hg.repository(ui, source)
440 other = hg.repository(ui, source)
428
441
429 if other.dev() != -1:
442 if other.dev() != -1:
430 abspath = os.path.abspath(source)
443 abspath = os.path.abspath(source)
431 copyfile = (os.stat(dest).st_dev == other.dev()
444 copyfile = (os.stat(dest).st_dev == other.dev()
432 and getattr(os, 'link', None) or shutil.copy2)
445 and getattr(os, 'link', None) or shutil.copy2)
433 if copyfile is not shutil.copy2:
446 if copyfile is not shutil.copy2:
434 ui.note("cloning by hardlink\n")
447 ui.note("cloning by hardlink\n")
435 util.copytree(os.path.join(source, ".hg"), os.path.join(dest, ".hg"),
448 util.copytree(os.path.join(source, ".hg"), os.path.join(dest, ".hg"),
436 copyfile)
449 copyfile)
437 try:
450 try:
438 os.unlink(os.path.join(dest, ".hg", "dirstate"))
451 os.unlink(os.path.join(dest, ".hg", "dirstate"))
439 except OSError:
452 except OSError:
440 pass
453 pass
441
454
442 repo = hg.repository(ui, dest)
455 repo = hg.repository(ui, dest)
443
456
444 else:
457 else:
445 repo = hg.repository(ui, dest, create=1)
458 repo = hg.repository(ui, dest, create=1)
446 repo.pull(other)
459 repo.pull(other)
447
460
448 f = repo.opener("hgrc", "w")
461 f = repo.opener("hgrc", "w")
449 f.write("[paths]\n")
462 f.write("[paths]\n")
450 f.write("default = %s\n" % abspath)
463 f.write("default = %s\n" % abspath)
451
464
452 if not opts['noupdate']:
465 if not opts['noupdate']:
453 update(ui, repo)
466 update(ui, repo)
454
467
455 d.close()
468 d.close()
456
469
457 def commit(ui, repo, *files, **opts):
470 def commit(ui, repo, *files, **opts):
458 """commit the specified files or all outstanding changes"""
471 """commit the specified files or all outstanding changes"""
459 if opts['text']:
472 if opts['text']:
460 ui.warn("Warning: -t and --text is deprecated,"
473 ui.warn("Warning: -t and --text is deprecated,"
461 " please use -m or --message instead.\n")
474 " please use -m or --message instead.\n")
462 message = opts['message'] or opts['text']
475 message = opts['message'] or opts['text']
463 logfile = opts['logfile']
476 logfile = opts['logfile']
464 if not message and logfile:
477 if not message and logfile:
465 try:
478 try:
466 message = open(logfile).read()
479 message = open(logfile).read()
467 except IOError, why:
480 except IOError, why:
468 ui.warn("Can't read commit message %s: %s\n" % (logfile, why))
481 ui.warn("Can't read commit message %s: %s\n" % (logfile, why))
469
482
470 if opts['addremove']:
483 if opts['addremove']:
471 addremove(ui, repo, *files)
484 addremove(ui, repo, *files)
472 repo.commit(relpath(repo, files), message, opts['user'], opts['date'])
485 repo.commit(relpath(repo, files), message, opts['user'], opts['date'])
473
486
474 def copy(ui, repo, source, dest):
487 def copy(ui, repo, source, dest):
475 """mark a file as copied or renamed for the next commit"""
488 """mark a file as copied or renamed for the next commit"""
476 return repo.copy(*relpath(repo, (source, dest)))
489 return repo.copy(*relpath(repo, (source, dest)))
477
490
478 def debugcheckstate(ui, repo):
491 def debugcheckstate(ui, repo):
479 """validate the correctness of the current dirstate"""
492 """validate the correctness of the current dirstate"""
480 parent1, parent2 = repo.dirstate.parents()
493 parent1, parent2 = repo.dirstate.parents()
481 repo.dirstate.read()
494 repo.dirstate.read()
482 dc = repo.dirstate.map
495 dc = repo.dirstate.map
483 keys = dc.keys()
496 keys = dc.keys()
484 keys.sort()
497 keys.sort()
485 m1n = repo.changelog.read(parent1)[0]
498 m1n = repo.changelog.read(parent1)[0]
486 m2n = repo.changelog.read(parent2)[0]
499 m2n = repo.changelog.read(parent2)[0]
487 m1 = repo.manifest.read(m1n)
500 m1 = repo.manifest.read(m1n)
488 m2 = repo.manifest.read(m2n)
501 m2 = repo.manifest.read(m2n)
489 errors = 0
502 errors = 0
490 for f in dc:
503 for f in dc:
491 state = repo.dirstate.state(f)
504 state = repo.dirstate.state(f)
492 if state in "nr" and f not in m1:
505 if state in "nr" and f not in m1:
493 ui.warn("%s in state %s, but not in manifest1\n" % (f, state))
506 ui.warn("%s in state %s, but not in manifest1\n" % (f, state))
494 errors += 1
507 errors += 1
495 if state in "a" and f in m1:
508 if state in "a" and f in m1:
496 ui.warn("%s in state %s, but also in manifest1\n" % (f, state))
509 ui.warn("%s in state %s, but also in manifest1\n" % (f, state))
497 errors += 1
510 errors += 1
498 if state in "m" and f not in m1 and f not in m2:
511 if state in "m" and f not in m1 and f not in m2:
499 ui.warn("%s in state %s, but not in either manifest\n" %
512 ui.warn("%s in state %s, but not in either manifest\n" %
500 (f, state))
513 (f, state))
501 errors += 1
514 errors += 1
502 for f in m1:
515 for f in m1:
503 state = repo.dirstate.state(f)
516 state = repo.dirstate.state(f)
504 if state not in "nrm":
517 if state not in "nrm":
505 ui.warn("%s in manifest1, but listed as state %s" % (f, state))
518 ui.warn("%s in manifest1, but listed as state %s" % (f, state))
506 errors += 1
519 errors += 1
507 if errors:
520 if errors:
508 raise Abort(".hg/dirstate inconsistent with current parent's manifest")
521 raise Abort(".hg/dirstate inconsistent with current parent's manifest")
509
522
510 def debugstate(ui, repo):
523 def debugstate(ui, repo):
511 """show the contents of the current dirstate"""
524 """show the contents of the current dirstate"""
512 repo.dirstate.read()
525 repo.dirstate.read()
513 dc = repo.dirstate.map
526 dc = repo.dirstate.map
514 keys = dc.keys()
527 keys = dc.keys()
515 keys.sort()
528 keys.sort()
516 for file_ in keys:
529 for file_ in keys:
517 ui.write("%c %3o %10d %s %s\n"
530 ui.write("%c %3o %10d %s %s\n"
518 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
531 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
519 time.strftime("%x %X",
532 time.strftime("%x %X",
520 time.localtime(dc[file_][3])), file_))
533 time.localtime(dc[file_][3])), file_))
521
534
522 def debugindex(ui, file_):
535 def debugindex(ui, file_):
523 """dump the contents of an index file"""
536 """dump the contents of an index file"""
524 r = hg.revlog(hg.opener(""), file_, "")
537 r = hg.revlog(hg.opener(""), file_, "")
525 ui.write(" rev offset length base linkrev" +
538 ui.write(" rev offset length base linkrev" +
526 " p1 p2 nodeid\n")
539 " p1 p2 nodeid\n")
527 for i in range(r.count()):
540 for i in range(r.count()):
528 e = r.index[i]
541 e = r.index[i]
529 ui.write("% 6d % 9d % 7d % 6d % 7d %s.. %s.. %s..\n" % (
542 ui.write("% 6d % 9d % 7d % 6d % 7d %s.. %s.. %s..\n" % (
530 i, e[0], e[1], e[2], e[3],
543 i, e[0], e[1], e[2], e[3],
531 hg.hex(e[4][:5]), hg.hex(e[5][:5]), hg.hex(e[6][:5])))
544 hg.hex(e[4][:5]), hg.hex(e[5][:5]), hg.hex(e[6][:5])))
532
545
533 def debugindexdot(ui, file_):
546 def debugindexdot(ui, file_):
534 """dump an index DAG as a .dot file"""
547 """dump an index DAG as a .dot file"""
535 r = hg.revlog(hg.opener(""), file_, "")
548 r = hg.revlog(hg.opener(""), file_, "")
536 ui.write("digraph G {\n")
549 ui.write("digraph G {\n")
537 for i in range(r.count()):
550 for i in range(r.count()):
538 e = r.index[i]
551 e = r.index[i]
539 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
552 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
540 if e[5] != hg.nullid:
553 if e[5] != hg.nullid:
541 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
554 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
542 ui.write("}\n")
555 ui.write("}\n")
543
556
544 def diff(ui, repo, *pats, **opts):
557 def diff(ui, repo, *pats, **opts):
545 """diff working directory (or selected files)"""
558 """diff working directory (or selected files)"""
546 revs = []
559 revs = []
547 if opts['rev']:
560 if opts['rev']:
548 revs = map(lambda x: repo.lookup(x), opts['rev'])
561 revs = map(lambda x: repo.lookup(x), opts['rev'])
549
562
550 if len(revs) > 2:
563 if len(revs) > 2:
551 raise Abort("too many revisions to diff")
564 raise Abort("too many revisions to diff")
552
565
553 files = []
566 files = []
554 for src, abs, rel in walk(repo, pats, opts):
567 for src, abs, rel in walk(repo, pats, opts):
555 files.append(abs)
568 files.append(abs)
556 dodiff(sys.stdout, ui, repo, files, *revs)
569 dodiff(sys.stdout, ui, repo, files, *revs)
557
570
558 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
571 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
559 node = repo.lookup(changeset)
572 node = repo.lookup(changeset)
560 prev, other = repo.changelog.parents(node)
573 prev, other = repo.changelog.parents(node)
561 change = repo.changelog.read(node)
574 change = repo.changelog.read(node)
562
575
563 fp = make_file(repo, repo.changelog, opts['output'],
576 fp = make_file(repo, repo.changelog, opts['output'],
564 node=node, total=total, seqno=seqno,
577 node=node, total=total, seqno=seqno,
565 revwidth=revwidth)
578 revwidth=revwidth)
566 if fp != sys.stdout:
579 if fp != sys.stdout:
567 ui.note("%s\n" % fp.name)
580 ui.note("%s\n" % fp.name)
568
581
569 fp.write("# HG changeset patch\n")
582 fp.write("# HG changeset patch\n")
570 fp.write("# User %s\n" % change[1])
583 fp.write("# User %s\n" % change[1])
571 fp.write("# Node ID %s\n" % hg.hex(node))
584 fp.write("# Node ID %s\n" % hg.hex(node))
572 fp.write("# Parent %s\n" % hg.hex(prev))
585 fp.write("# Parent %s\n" % hg.hex(prev))
573 if other != hg.nullid:
586 if other != hg.nullid:
574 fp.write("# Parent %s\n" % hg.hex(other))
587 fp.write("# Parent %s\n" % hg.hex(other))
575 fp.write(change[4].rstrip())
588 fp.write(change[4].rstrip())
576 fp.write("\n\n")
589 fp.write("\n\n")
577
590
578 dodiff(fp, ui, repo, None, prev, node)
591 dodiff(fp, ui, repo, None, prev, node)
579 if fp != sys.stdout: fp.close()
592 if fp != sys.stdout: fp.close()
580
593
581 def export(ui, repo, *changesets, **opts):
594 def export(ui, repo, *changesets, **opts):
582 """dump the header and diffs for one or more changesets"""
595 """dump the header and diffs for one or more changesets"""
583 if not changesets:
596 if not changesets:
584 raise Abort("export requires at least one changeset")
597 raise Abort("export requires at least one changeset")
585 seqno = 0
598 seqno = 0
586 revs = list(revrange(ui, repo, changesets))
599 revs = list(revrange(ui, repo, changesets))
587 total = len(revs)
600 total = len(revs)
588 revwidth = max(len(revs[0]), len(revs[-1]))
601 revwidth = max(len(revs[0]), len(revs[-1]))
589 ui.note(len(revs) > 1 and "Exporting patches:\n" or "Exporting patch:\n")
602 ui.note(len(revs) > 1 and "Exporting patches:\n" or "Exporting patch:\n")
590 for cset in revs:
603 for cset in revs:
591 seqno += 1
604 seqno += 1
592 doexport(ui, repo, cset, seqno, total, revwidth, opts)
605 doexport(ui, repo, cset, seqno, total, revwidth, opts)
593
606
594 def forget(ui, repo, file1, *files):
607 def forget(ui, repo, file1, *files):
595 """don't add the specified files on the next commit"""
608 """don't add the specified files on the next commit"""
596 repo.forget(relpath(repo, (file1,) + files))
609 repo.forget(relpath(repo, (file1,) + files))
597
610
598 def heads(ui, repo):
611 def heads(ui, repo):
599 """show current repository heads"""
612 """show current repository heads"""
600 for n in repo.changelog.heads():
613 for n in repo.changelog.heads():
601 show_changeset(ui, repo, changenode=n)
614 show_changeset(ui, repo, changenode=n)
602
615
603 def identify(ui, repo):
616 def identify(ui, repo):
604 """print information about the working copy"""
617 """print information about the working copy"""
605 parents = [p for p in repo.dirstate.parents() if p != hg.nullid]
618 parents = [p for p in repo.dirstate.parents() if p != hg.nullid]
606 if not parents:
619 if not parents:
607 ui.write("unknown\n")
620 ui.write("unknown\n")
608 return
621 return
609
622
610 hexfunc = ui.verbose and hg.hex or hg.short
623 hexfunc = ui.verbose and hg.hex or hg.short
611 (c, a, d, u) = repo.changes()
624 (c, a, d, u) = repo.changes()
612 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
625 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
613 (c or a or d) and "+" or "")]
626 (c or a or d) and "+" or "")]
614
627
615 if not ui.quiet:
628 if not ui.quiet:
616 # multiple tags for a single parent separated by '/'
629 # multiple tags for a single parent separated by '/'
617 parenttags = ['/'.join(tags)
630 parenttags = ['/'.join(tags)
618 for tags in map(repo.nodetags, parents) if tags]
631 for tags in map(repo.nodetags, parents) if tags]
619 # tags for multiple parents separated by ' + '
632 # tags for multiple parents separated by ' + '
620 if parenttags:
633 if parenttags:
621 output.append(' + '.join(parenttags))
634 output.append(' + '.join(parenttags))
622
635
623 ui.write("%s\n" % ' '.join(output))
636 ui.write("%s\n" % ' '.join(output))
624
637
625 def import_(ui, repo, patch1, *patches, **opts):
638 def import_(ui, repo, patch1, *patches, **opts):
626 """import an ordered set of patches"""
639 """import an ordered set of patches"""
627 try:
640 try:
628 import psyco
641 import psyco
629 psyco.full()
642 psyco.full()
630 except ImportError:
643 except ImportError:
631 pass
644 pass
632
645
633 patches = (patch1,) + patches
646 patches = (patch1,) + patches
634
647
635 d = opts["base"]
648 d = opts["base"]
636 strip = opts["strip"]
649 strip = opts["strip"]
637
650
638 for patch in patches:
651 for patch in patches:
639 ui.status("applying %s\n" % patch)
652 ui.status("applying %s\n" % patch)
640 pf = os.path.join(d, patch)
653 pf = os.path.join(d, patch)
641
654
642 message = []
655 message = []
643 user = None
656 user = None
644 hgpatch = False
657 hgpatch = False
645 for line in file(pf):
658 for line in file(pf):
646 line = line.rstrip()
659 line = line.rstrip()
647 if line.startswith("--- ") or line.startswith("diff -r"):
660 if line.startswith("--- ") or line.startswith("diff -r"):
648 break
661 break
649 elif hgpatch:
662 elif hgpatch:
650 # parse values when importing the result of an hg export
663 # parse values when importing the result of an hg export
651 if line.startswith("# User "):
664 if line.startswith("# User "):
652 user = line[7:]
665 user = line[7:]
653 ui.debug('User: %s\n' % user)
666 ui.debug('User: %s\n' % user)
654 elif not line.startswith("# ") and line:
667 elif not line.startswith("# ") and line:
655 message.append(line)
668 message.append(line)
656 hgpatch = False
669 hgpatch = False
657 elif line == '# HG changeset patch':
670 elif line == '# HG changeset patch':
658 hgpatch = True
671 hgpatch = True
659 else:
672 else:
660 message.append(line)
673 message.append(line)
661
674
662 # make sure message isn't empty
675 # make sure message isn't empty
663 if not message:
676 if not message:
664 message = "imported patch %s\n" % patch
677 message = "imported patch %s\n" % patch
665 else:
678 else:
666 message = "%s\n" % '\n'.join(message)
679 message = "%s\n" % '\n'.join(message)
667 ui.debug('message:\n%s\n' % message)
680 ui.debug('message:\n%s\n' % message)
668
681
669 f = os.popen("patch -p%d < %s" % (strip, pf))
682 f = os.popen("patch -p%d < %s" % (strip, pf))
670 files = []
683 files = []
671 for l in f.read().splitlines():
684 for l in f.read().splitlines():
672 l.rstrip('\r\n');
685 l.rstrip('\r\n');
673 ui.status("%s\n" % l)
686 ui.status("%s\n" % l)
674 if l.startswith('patching file '):
687 if l.startswith('patching file '):
675 pf = l[14:]
688 pf = l[14:]
676 if pf not in files:
689 if pf not in files:
677 files.append(pf)
690 files.append(pf)
678 patcherr = f.close()
691 patcherr = f.close()
679 if patcherr:
692 if patcherr:
680 raise Abort("patch failed")
693 raise Abort("patch failed")
681
694
682 if len(files) > 0:
695 if len(files) > 0:
683 addremove(ui, repo, *files)
696 addremove(ui, repo, *files)
684 repo.commit(files, message, user)
697 repo.commit(files, message, user)
685
698
686 def init(ui, source=None):
699 def init(ui, source=None):
687 """create a new repository in the current directory"""
700 """create a new repository in the current directory"""
688
701
689 if source:
702 if source:
690 raise Abort("no longer supported: use \"hg clone\" instead")
703 raise Abort("no longer supported: use \"hg clone\" instead")
691 hg.repository(ui, ".", create=1)
704 hg.repository(ui, ".", create=1)
692
705
693 def locate(ui, repo, *pats, **opts):
706 def locate(ui, repo, *pats, **opts):
694 """locate files matching specific patterns"""
707 """locate files matching specific patterns"""
695 end = '\n'
708 end = '\n'
696 if opts['print0']: end = '\0'
709 if opts['print0']: end = '\0'
697
710
698 for src, abs, rel in walk(repo, pats, opts, '(?:.*/|)'):
711 for src, abs, rel in walk(repo, pats, opts, '(?:.*/|)'):
699 if repo.dirstate.state(abs) == '?': continue
712 if repo.dirstate.state(abs) == '?': continue
700 if opts['fullpath']:
713 if opts['fullpath']:
701 ui.write(os.path.join(repo.root, abs), end)
714 ui.write(os.path.join(repo.root, abs), end)
702 else:
715 else:
703 ui.write(rel, end)
716 ui.write(rel, end)
704
717
705 def log(ui, repo, f=None, **opts):
718 def log(ui, repo, f=None, **opts):
706 """show the revision history of the repository or a single file"""
719 """show the revision history of the repository or a single file"""
707 if f:
720 if f:
708 files = relpath(repo, [f])
721 files = relpath(repo, [f])
709 filelog = repo.file(files[0])
722 filelog = repo.file(files[0])
710 log = filelog
723 log = filelog
711 lookup = filelog.lookup
724 lookup = filelog.lookup
712 else:
725 else:
713 files = None
726 files = None
714 filelog = None
727 filelog = None
715 log = repo.changelog
728 log = repo.changelog
716 lookup = repo.lookup
729 lookup = repo.lookup
717 revlist = []
730 revlist = []
718 revs = [log.rev(lookup(rev)) for rev in opts['rev']]
731 revs = [log.rev(lookup(rev)) for rev in opts['rev']]
719 while revs:
732 while revs:
720 if len(revs) == 1:
733 if len(revs) == 1:
721 revlist.append(revs.pop(0))
734 revlist.append(revs.pop(0))
722 else:
735 else:
723 a = revs.pop(0)
736 a = revs.pop(0)
724 b = revs.pop(0)
737 b = revs.pop(0)
725 off = a > b and -1 or 1
738 off = a > b and -1 or 1
726 revlist.extend(range(a, b + off, off))
739 revlist.extend(range(a, b + off, off))
727
740
728 for i in revlist or range(log.count() - 1, -1, -1):
741 for i in revlist or range(log.count() - 1, -1, -1):
729 show_changeset(ui, repo, filelog=filelog, rev=i)
742 show_changeset(ui, repo, filelog=filelog, rev=i)
730 if opts['patch']:
743 if opts['patch']:
731 if filelog:
744 if filelog:
732 filenode = filelog.node(i)
745 filenode = filelog.node(i)
733 i = filelog.linkrev(filenode)
746 i = filelog.linkrev(filenode)
734 changenode = repo.changelog.node(i)
747 changenode = repo.changelog.node(i)
735 prev, other = repo.changelog.parents(changenode)
748 prev, other = repo.changelog.parents(changenode)
736 dodiff(sys.stdout, ui, repo, files, prev, changenode)
749 dodiff(sys.stdout, ui, repo, files, prev, changenode)
737 ui.write("\n\n")
750 ui.write("\n\n")
738
751
739 def manifest(ui, repo, rev=None):
752 def manifest(ui, repo, rev=None):
740 """output the latest or given revision of the project manifest"""
753 """output the latest or given revision of the project manifest"""
741 if rev:
754 if rev:
742 try:
755 try:
743 # assume all revision numbers are for changesets
756 # assume all revision numbers are for changesets
744 n = repo.lookup(rev)
757 n = repo.lookup(rev)
745 change = repo.changelog.read(n)
758 change = repo.changelog.read(n)
746 n = change[0]
759 n = change[0]
747 except hg.RepoError:
760 except hg.RepoError:
748 n = repo.manifest.lookup(rev)
761 n = repo.manifest.lookup(rev)
749 else:
762 else:
750 n = repo.manifest.tip()
763 n = repo.manifest.tip()
751 m = repo.manifest.read(n)
764 m = repo.manifest.read(n)
752 mf = repo.manifest.readflags(n)
765 mf = repo.manifest.readflags(n)
753 files = m.keys()
766 files = m.keys()
754 files.sort()
767 files.sort()
755
768
756 for f in files:
769 for f in files:
757 ui.write("%40s %3s %s\n" % (hg.hex(m[f]), mf[f] and "755" or "644", f))
770 ui.write("%40s %3s %s\n" % (hg.hex(m[f]), mf[f] and "755" or "644", f))
758
771
759 def parents(ui, repo, rev=None):
772 def parents(ui, repo, rev=None):
760 """show the parents of the working dir or revision"""
773 """show the parents of the working dir or revision"""
761 if rev:
774 if rev:
762 p = repo.changelog.parents(repo.lookup(rev))
775 p = repo.changelog.parents(repo.lookup(rev))
763 else:
776 else:
764 p = repo.dirstate.parents()
777 p = repo.dirstate.parents()
765
778
766 for n in p:
779 for n in p:
767 if n != hg.nullid:
780 if n != hg.nullid:
768 show_changeset(ui, repo, changenode=n)
781 show_changeset(ui, repo, changenode=n)
769
782
783 def paths(ui, repo, search = None):
784 """show path or list of available paths"""
785 if search:
786 for name, path in ui.configitems("paths"):
787 if name == search:
788 ui.write("%s\n" % path)
789 return
790 ui.warn("not found!\n")
791 return 1
792 else:
793 for name, path in ui.configitems("paths"):
794 ui.write("%s = %s\n" % (name, path))
795
770 def pull(ui, repo, source="default", **opts):
796 def pull(ui, repo, source="default", **opts):
771 """pull changes from the specified source"""
797 """pull changes from the specified source"""
772 source = ui.expandpath(source)
798 source = ui.expandpath(source)
773 ui.status('pulling from %s\n' % (source))
799 ui.status('pulling from %s\n' % (source))
774
800
775 other = hg.repository(ui, source)
801 other = hg.repository(ui, source)
776 r = repo.pull(other)
802 r = repo.pull(other)
777 if not r:
803 if not r:
778 if opts['update']:
804 if opts['update']:
779 return update(ui, repo)
805 return update(ui, repo)
780 else:
806 else:
781 ui.status("(run 'hg update' to get a working copy)\n")
807 ui.status("(run 'hg update' to get a working copy)\n")
782
808
783 return r
809 return r
784
810
785 def push(ui, repo, dest="default-push"):
811 def push(ui, repo, dest="default-push"):
786 """push changes to the specified destination"""
812 """push changes to the specified destination"""
787 dest = ui.expandpath(dest)
813 dest = ui.expandpath(dest)
788 ui.status('pushing to %s\n' % (dest))
814 ui.status('pushing to %s\n' % (dest))
789
815
790 other = hg.repository(ui, dest)
816 other = hg.repository(ui, dest)
791 r = repo.push(other)
817 r = repo.push(other)
792 return r
818 return r
793
819
794 def rawcommit(ui, repo, *flist, **rc):
820 def rawcommit(ui, repo, *flist, **rc):
795 "raw commit interface"
821 "raw commit interface"
796 if rc['text']:
822 if rc['text']:
797 ui.warn("Warning: -t and --text is deprecated,"
823 ui.warn("Warning: -t and --text is deprecated,"
798 " please use -m or --message instead.\n")
824 " please use -m or --message instead.\n")
799 message = rc['message'] or rc['text']
825 message = rc['message'] or rc['text']
800 if not message and rc['logfile']:
826 if not message and rc['logfile']:
801 try:
827 try:
802 message = open(rc['logfile']).read()
828 message = open(rc['logfile']).read()
803 except IOError:
829 except IOError:
804 pass
830 pass
805 if not message and not rc['logfile']:
831 if not message and not rc['logfile']:
806 ui.warn("abort: missing commit message\n")
832 ui.warn("abort: missing commit message\n")
807 return 1
833 return 1
808
834
809 files = relpath(repo, list(flist))
835 files = relpath(repo, list(flist))
810 if rc['files']:
836 if rc['files']:
811 files += open(rc['files']).read().splitlines()
837 files += open(rc['files']).read().splitlines()
812
838
813 rc['parent'] = map(repo.lookup, rc['parent'])
839 rc['parent'] = map(repo.lookup, rc['parent'])
814
840
815 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
841 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
816
842
817 def recover(ui, repo):
843 def recover(ui, repo):
818 """roll back an interrupted transaction"""
844 """roll back an interrupted transaction"""
819 repo.recover()
845 repo.recover()
820
846
821 def remove(ui, repo, file1, *files):
847 def remove(ui, repo, file1, *files):
822 """remove the specified files on the next commit"""
848 """remove the specified files on the next commit"""
823 repo.remove(relpath(repo, (file1,) + files))
849 repo.remove(relpath(repo, (file1,) + files))
824
850
825 def revert(ui, repo, *names, **opts):
851 def revert(ui, repo, *names, **opts):
826 """revert modified files or dirs back to their unmodified states"""
852 """revert modified files or dirs back to their unmodified states"""
827 node = opts['rev'] and repo.lookup(opts['rev']) or \
853 node = opts['rev'] and repo.lookup(opts['rev']) or \
828 repo.dirstate.parents()[0]
854 repo.dirstate.parents()[0]
829 root = os.path.realpath(repo.root)
855 root = os.path.realpath(repo.root)
830
856
831 def trimpath(p):
857 def trimpath(p):
832 p = os.path.realpath(p)
858 p = os.path.realpath(p)
833 if p.startswith(root):
859 if p.startswith(root):
834 rest = p[len(root):]
860 rest = p[len(root):]
835 if not rest:
861 if not rest:
836 return rest
862 return rest
837 if p.startswith(os.sep):
863 if p.startswith(os.sep):
838 return rest[1:]
864 return rest[1:]
839 return p
865 return p
840
866
841 relnames = map(trimpath, names or [os.getcwd()])
867 relnames = map(trimpath, names or [os.getcwd()])
842 chosen = {}
868 chosen = {}
843
869
844 def choose(name):
870 def choose(name):
845 def body(name):
871 def body(name):
846 for r in relnames:
872 for r in relnames:
847 if not name.startswith(r):
873 if not name.startswith(r):
848 continue
874 continue
849 rest = name[len(r):]
875 rest = name[len(r):]
850 if not rest:
876 if not rest:
851 return r, True
877 return r, True
852 depth = rest.count(os.sep)
878 depth = rest.count(os.sep)
853 if not r:
879 if not r:
854 if depth == 0 or not opts['nonrecursive']:
880 if depth == 0 or not opts['nonrecursive']:
855 return r, True
881 return r, True
856 elif rest[0] == os.sep:
882 elif rest[0] == os.sep:
857 if depth == 1 or not opts['nonrecursive']:
883 if depth == 1 or not opts['nonrecursive']:
858 return r, True
884 return r, True
859 return None, False
885 return None, False
860 relname, ret = body(name)
886 relname, ret = body(name)
861 if ret:
887 if ret:
862 chosen[relname] = 1
888 chosen[relname] = 1
863 return ret
889 return ret
864
890
865 r = repo.update(node, False, True, choose, False)
891 r = repo.update(node, False, True, choose, False)
866 for n in relnames:
892 for n in relnames:
867 if n not in chosen:
893 if n not in chosen:
868 ui.warn('error: no matches for %s\n' % n)
894 ui.warn('error: no matches for %s\n' % n)
869 r = 1
895 r = 1
870 sys.stdout.flush()
896 sys.stdout.flush()
871 return r
897 return r
872
898
873 def root(ui, repo):
899 def root(ui, repo):
874 """print the root (top) of the current working dir"""
900 """print the root (top) of the current working dir"""
875 ui.write(repo.root + "\n")
901 ui.write(repo.root + "\n")
876
902
877 def serve(ui, repo, **opts):
903 def serve(ui, repo, **opts):
878 """export the repository via HTTP"""
904 """export the repository via HTTP"""
879
905
880 if opts["stdio"]:
906 if opts["stdio"]:
881 fin, fout = sys.stdin, sys.stdout
907 fin, fout = sys.stdin, sys.stdout
882 sys.stdout = sys.stderr
908 sys.stdout = sys.stderr
883
909
884 def getarg():
910 def getarg():
885 argline = fin.readline()[:-1]
911 argline = fin.readline()[:-1]
886 arg, l = argline.split()
912 arg, l = argline.split()
887 val = fin.read(int(l))
913 val = fin.read(int(l))
888 return arg, val
914 return arg, val
889 def respond(v):
915 def respond(v):
890 fout.write("%d\n" % len(v))
916 fout.write("%d\n" % len(v))
891 fout.write(v)
917 fout.write(v)
892 fout.flush()
918 fout.flush()
893
919
894 lock = None
920 lock = None
895
921
896 while 1:
922 while 1:
897 cmd = fin.readline()[:-1]
923 cmd = fin.readline()[:-1]
898 if cmd == '':
924 if cmd == '':
899 return
925 return
900 if cmd == "heads":
926 if cmd == "heads":
901 h = repo.heads()
927 h = repo.heads()
902 respond(" ".join(map(hg.hex, h)) + "\n")
928 respond(" ".join(map(hg.hex, h)) + "\n")
903 if cmd == "lock":
929 if cmd == "lock":
904 lock = repo.lock()
930 lock = repo.lock()
905 respond("")
931 respond("")
906 if cmd == "unlock":
932 if cmd == "unlock":
907 if lock:
933 if lock:
908 lock.release()
934 lock.release()
909 lock = None
935 lock = None
910 respond("")
936 respond("")
911 elif cmd == "branches":
937 elif cmd == "branches":
912 arg, nodes = getarg()
938 arg, nodes = getarg()
913 nodes = map(hg.bin, nodes.split(" "))
939 nodes = map(hg.bin, nodes.split(" "))
914 r = []
940 r = []
915 for b in repo.branches(nodes):
941 for b in repo.branches(nodes):
916 r.append(" ".join(map(hg.hex, b)) + "\n")
942 r.append(" ".join(map(hg.hex, b)) + "\n")
917 respond("".join(r))
943 respond("".join(r))
918 elif cmd == "between":
944 elif cmd == "between":
919 arg, pairs = getarg()
945 arg, pairs = getarg()
920 pairs = [map(hg.bin, p.split("-")) for p in pairs.split(" ")]
946 pairs = [map(hg.bin, p.split("-")) for p in pairs.split(" ")]
921 r = []
947 r = []
922 for b in repo.between(pairs):
948 for b in repo.between(pairs):
923 r.append(" ".join(map(hg.hex, b)) + "\n")
949 r.append(" ".join(map(hg.hex, b)) + "\n")
924 respond("".join(r))
950 respond("".join(r))
925 elif cmd == "changegroup":
951 elif cmd == "changegroup":
926 nodes = []
952 nodes = []
927 arg, roots = getarg()
953 arg, roots = getarg()
928 nodes = map(hg.bin, roots.split(" "))
954 nodes = map(hg.bin, roots.split(" "))
929
955
930 cg = repo.changegroup(nodes)
956 cg = repo.changegroup(nodes)
931 while 1:
957 while 1:
932 d = cg.read(4096)
958 d = cg.read(4096)
933 if not d:
959 if not d:
934 break
960 break
935 fout.write(d)
961 fout.write(d)
936
962
937 fout.flush()
963 fout.flush()
938
964
939 elif cmd == "addchangegroup":
965 elif cmd == "addchangegroup":
940 if not lock:
966 if not lock:
941 respond("not locked")
967 respond("not locked")
942 continue
968 continue
943 respond("")
969 respond("")
944
970
945 r = repo.addchangegroup(fin)
971 r = repo.addchangegroup(fin)
946 respond("")
972 respond("")
947
973
948 def openlog(opt, default):
974 def openlog(opt, default):
949 if opts[opt] and opts[opt] != '-':
975 if opts[opt] and opts[opt] != '-':
950 return open(opts[opt], 'w')
976 return open(opts[opt], 'w')
951 else:
977 else:
952 return default
978 return default
953
979
954 httpd = hgweb.create_server(repo.root, opts["name"], opts["templates"],
980 httpd = hgweb.create_server(repo.root, opts["name"], opts["templates"],
955 opts["address"], opts["port"],
981 opts["address"], opts["port"],
956 openlog('accesslog', sys.stdout),
982 openlog('accesslog', sys.stdout),
957 openlog('errorlog', sys.stderr))
983 openlog('errorlog', sys.stderr))
958 if ui.verbose:
984 if ui.verbose:
959 addr, port = httpd.socket.getsockname()
985 addr, port = httpd.socket.getsockname()
960 if addr == '0.0.0.0':
986 if addr == '0.0.0.0':
961 addr = socket.gethostname()
987 addr = socket.gethostname()
962 else:
988 else:
963 try:
989 try:
964 addr = socket.gethostbyaddr(addr)[0]
990 addr = socket.gethostbyaddr(addr)[0]
965 except socket.error:
991 except socket.error:
966 pass
992 pass
967 if port != 80:
993 if port != 80:
968 ui.status('listening at http://%s:%d/\n' % (addr, port))
994 ui.status('listening at http://%s:%d/\n' % (addr, port))
969 else:
995 else:
970 ui.status('listening at http://%s/\n' % addr)
996 ui.status('listening at http://%s/\n' % addr)
971 httpd.serve_forever()
997 httpd.serve_forever()
972
998
973 def status(ui, repo, *pats, **opts):
999 def status(ui, repo, *pats, **opts):
974 '''show changed files in the working directory
1000 '''show changed files in the working directory
975
1001
976 M = modified
1002 M = modified
977 A = added
1003 A = added
978 R = removed
1004 R = removed
979 ? = not tracked'''
1005 ? = not tracked'''
980
1006
981 (c, a, d, u) = repo.changes(match = matchpats(repo.getcwd(), pats, opts))
1007 (c, a, d, u) = repo.changes(match = matchpats(repo.getcwd(), pats, opts))
982 (c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u))
1008 (c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u))
983
1009
984 for f in c:
1010 for f in c:
985 ui.write("M ", f, "\n")
1011 ui.write("M ", f, "\n")
986 for f in a:
1012 for f in a:
987 ui.write("A ", f, "\n")
1013 ui.write("A ", f, "\n")
988 for f in d:
1014 for f in d:
989 ui.write("R ", f, "\n")
1015 ui.write("R ", f, "\n")
990 for f in u:
1016 for f in u:
991 ui.write("? ", f, "\n")
1017 ui.write("? ", f, "\n")
992
1018
993 def tag(ui, repo, name, rev=None, **opts):
1019 def tag(ui, repo, name, rev=None, **opts):
994 """add a tag for the current tip or a given revision"""
1020 """add a tag for the current tip or a given revision"""
995 if opts['text']:
1021 if opts['text']:
996 ui.warn("Warning: -t and --text is deprecated,"
1022 ui.warn("Warning: -t and --text is deprecated,"
997 " please use -m or --message instead.\n")
1023 " please use -m or --message instead.\n")
998 if name == "tip":
1024 if name == "tip":
999 ui.warn("abort: 'tip' is a reserved name!\n")
1025 ui.warn("abort: 'tip' is a reserved name!\n")
1000 return -1
1026 return -1
1001 if rev:
1027 if rev:
1002 r = hg.hex(repo.lookup(rev))
1028 r = hg.hex(repo.lookup(rev))
1003 else:
1029 else:
1004 r = hg.hex(repo.changelog.tip())
1030 r = hg.hex(repo.changelog.tip())
1005
1031
1006 if name.find(revrangesep) >= 0:
1032 if name.find(revrangesep) >= 0:
1007 ui.warn("abort: '%s' cannot be used in a tag name\n" % revrangesep)
1033 ui.warn("abort: '%s' cannot be used in a tag name\n" % revrangesep)
1008 return -1
1034 return -1
1009
1035
1010 if opts['local']:
1036 if opts['local']:
1011 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1037 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1012 return
1038 return
1013
1039
1014 (c, a, d, u) = repo.changes()
1040 (c, a, d, u) = repo.changes()
1015 for x in (c, a, d, u):
1041 for x in (c, a, d, u):
1016 if ".hgtags" in x:
1042 if ".hgtags" in x:
1017 ui.warn("abort: working copy of .hgtags is changed!\n")
1043 ui.warn("abort: working copy of .hgtags is changed!\n")
1018 ui.status("(please commit .hgtags manually)\n")
1044 ui.status("(please commit .hgtags manually)\n")
1019 return -1
1045 return -1
1020
1046
1021 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
1047 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
1022 if repo.dirstate.state(".hgtags") == '?':
1048 if repo.dirstate.state(".hgtags") == '?':
1023 repo.add([".hgtags"])
1049 repo.add([".hgtags"])
1024
1050
1025 message = (opts['message'] or opts['text'] or
1051 message = (opts['message'] or opts['text'] or
1026 "Added tag %s for changeset %s" % (name, r))
1052 "Added tag %s for changeset %s" % (name, r))
1027 repo.commit([".hgtags"], message, opts['user'], opts['date'])
1053 repo.commit([".hgtags"], message, opts['user'], opts['date'])
1028
1054
1029 def tags(ui, repo):
1055 def tags(ui, repo):
1030 """list repository tags"""
1056 """list repository tags"""
1031
1057
1032 l = repo.tagslist()
1058 l = repo.tagslist()
1033 l.reverse()
1059 l.reverse()
1034 for t, n in l:
1060 for t, n in l:
1035 try:
1061 try:
1036 r = "%5d:%s" % (repo.changelog.rev(n), hg.hex(n))
1062 r = "%5d:%s" % (repo.changelog.rev(n), hg.hex(n))
1037 except KeyError:
1063 except KeyError:
1038 r = " ?:?"
1064 r = " ?:?"
1039 ui.write("%-30s %s\n" % (t, r))
1065 ui.write("%-30s %s\n" % (t, r))
1040
1066
1041 def tip(ui, repo):
1067 def tip(ui, repo):
1042 """show the tip revision"""
1068 """show the tip revision"""
1043 n = repo.changelog.tip()
1069 n = repo.changelog.tip()
1044 show_changeset(ui, repo, changenode=n)
1070 show_changeset(ui, repo, changenode=n)
1045
1071
1046 def undo(ui, repo):
1072 def undo(ui, repo):
1047 """undo the last commit or pull
1073 """undo the last commit or pull
1048
1074
1049 Roll back the last pull or commit transaction on the
1075 Roll back the last pull or commit transaction on the
1050 repository, restoring the project to its earlier state.
1076 repository, restoring the project to its earlier state.
1051
1077
1052 This command should be used with care. There is only one level of
1078 This command should be used with care. There is only one level of
1053 undo and there is no redo.
1079 undo and there is no redo.
1054
1080
1055 This command is not intended for use on public repositories. Once
1081 This command is not intended for use on public repositories. Once
1056 a change is visible for pull by other users, undoing it locally is
1082 a change is visible for pull by other users, undoing it locally is
1057 ineffective.
1083 ineffective.
1058 """
1084 """
1059 repo.undo()
1085 repo.undo()
1060
1086
1061 def update(ui, repo, node=None, merge=False, clean=False):
1087 def update(ui, repo, node=None, merge=False, clean=False):
1062 '''update or merge working directory
1088 '''update or merge working directory
1063
1089
1064 If there are no outstanding changes in the working directory and
1090 If there are no outstanding changes in the working directory and
1065 there is a linear relationship between the current version and the
1091 there is a linear relationship between the current version and the
1066 requested version, the result is the requested version.
1092 requested version, the result is the requested version.
1067
1093
1068 Otherwise the result is a merge between the contents of the
1094 Otherwise the result is a merge between the contents of the
1069 current working directory and the requested version. Files that
1095 current working directory and the requested version. Files that
1070 changed between either parent are marked as changed for the next
1096 changed between either parent are marked as changed for the next
1071 commit and a commit must be performed before any further updates
1097 commit and a commit must be performed before any further updates
1072 are allowed.
1098 are allowed.
1073 '''
1099 '''
1074 node = node and repo.lookup(node) or repo.changelog.tip()
1100 node = node and repo.lookup(node) or repo.changelog.tip()
1075 return repo.update(node, allow=merge, force=clean)
1101 return repo.update(node, allow=merge, force=clean)
1076
1102
1077 def verify(ui, repo):
1103 def verify(ui, repo):
1078 """verify the integrity of the repository"""
1104 """verify the integrity of the repository"""
1079 return repo.verify()
1105 return repo.verify()
1080
1106
1081 # Command options and aliases are listed here, alphabetically
1107 # Command options and aliases are listed here, alphabetically
1082
1108
1083 table = {
1109 table = {
1084 "^add": (add,
1110 "^add": (add,
1085 [('I', 'include', [], 'include path in search'),
1111 [('I', 'include', [], 'include path in search'),
1086 ('X', 'exclude', [], 'exclude path from search')],
1112 ('X', 'exclude', [], 'exclude path from search')],
1087 "hg add [FILE]..."),
1113 "hg add [FILE]..."),
1088 "addremove": (addremove,
1114 "addremove": (addremove,
1089 [('I', 'include', [], 'include path in search'),
1115 [('I', 'include', [], 'include path in search'),
1090 ('X', 'exclude', [], 'exclude path from search')],
1116 ('X', 'exclude', [], 'exclude path from search')],
1091 "hg addremove [OPTION]... [FILE]..."),
1117 "hg addremove [OPTION]... [FILE]..."),
1092 "^annotate":
1118 "^annotate":
1093 (annotate,
1119 (annotate,
1094 [('r', 'rev', '', 'revision'),
1120 [('r', 'rev', '', 'revision'),
1095 ('u', 'user', None, 'show user'),
1121 ('u', 'user', None, 'show user'),
1096 ('n', 'number', None, 'show revision number'),
1122 ('n', 'number', None, 'show revision number'),
1097 ('c', 'changeset', None, 'show changeset'),
1123 ('c', 'changeset', None, 'show changeset'),
1098 ('I', 'include', [], 'include path in search'),
1124 ('I', 'include', [], 'include path in search'),
1099 ('X', 'exclude', [], 'exclude path from search')],
1125 ('X', 'exclude', [], 'exclude path from search')],
1100 'hg annotate [-r REV] [-u] [-n] [-c] FILE...'),
1126 'hg annotate [-r REV] [-u] [-n] [-c] FILE...'),
1101 "cat":
1127 "cat":
1102 (cat,
1128 (cat,
1103 [('o', 'output', "", 'output to file')],
1129 [('o', 'output', "", 'output to file')],
1104 'hg cat [-o OUTFILE] FILE [REV]'),
1130 'hg cat [-o OUTFILE] FILE [REV]'),
1105 "^clone":
1131 "^clone":
1106 (clone,
1132 (clone,
1107 [('U', 'noupdate', None, 'skip update after cloning')],
1133 [('U', 'noupdate', None, 'skip update after cloning')],
1108 'hg clone [-U] SOURCE [DEST]'),
1134 'hg clone [-U] SOURCE [DEST]'),
1109 "^commit|ci":
1135 "^commit|ci":
1110 (commit,
1136 (commit,
1111 [('A', 'addremove', None, 'run add/remove during commit'),
1137 [('A', 'addremove', None, 'run add/remove during commit'),
1112 ('m', 'message', "", 'commit message'),
1138 ('m', 'message', "", 'commit message'),
1113 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1139 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1114 ('l', 'logfile', "", 'commit message file'),
1140 ('l', 'logfile', "", 'commit message file'),
1115 ('d', 'date', "", 'date code'),
1141 ('d', 'date', "", 'date code'),
1116 ('u', 'user', "", 'user')],
1142 ('u', 'user', "", 'user')],
1117 'hg commit [OPTION]... [FILE]...'),
1143 'hg commit [OPTION]... [FILE]...'),
1118 "copy": (copy, [], 'hg copy SOURCE DEST'),
1144 "copy": (copy, [], 'hg copy SOURCE DEST'),
1119 "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
1145 "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
1120 "debugstate": (debugstate, [], 'debugstate'),
1146 "debugstate": (debugstate, [], 'debugstate'),
1121 "debugindex": (debugindex, [], 'debugindex FILE'),
1147 "debugindex": (debugindex, [], 'debugindex FILE'),
1122 "debugindexdot": (debugindexdot, [], 'debugindexdot FILE'),
1148 "debugindexdot": (debugindexdot, [], 'debugindexdot FILE'),
1123 "^diff":
1149 "^diff":
1124 (diff,
1150 (diff,
1125 [('r', 'rev', [], 'revision'),
1151 [('r', 'rev', [], 'revision'),
1126 ('I', 'include', [], 'include path in search'),
1152 ('I', 'include', [], 'include path in search'),
1127 ('X', 'exclude', [], 'exclude path from search')],
1153 ('X', 'exclude', [], 'exclude path from search')],
1128 'hg diff [-r REV1 [-r REV2]] [FILE]...'),
1154 'hg diff [-r REV1 [-r REV2]] [FILE]...'),
1129 "^export":
1155 "^export":
1130 (export,
1156 (export,
1131 [('o', 'output', "", 'output to file')],
1157 [('o', 'output', "", 'output to file')],
1132 "hg export [-o OUTFILE] REV..."),
1158 "hg export [-o OUTFILE] REV..."),
1133 "forget": (forget, [], "hg forget FILE..."),
1159 "forget": (forget, [], "hg forget FILE..."),
1134 "heads": (heads, [], 'hg heads'),
1160 "heads": (heads, [], 'hg heads'),
1135 "help": (help_, [], 'hg help [COMMAND]'),
1161 "help": (help_, [], 'hg help [COMMAND]'),
1136 "identify|id": (identify, [], 'hg identify'),
1162 "identify|id": (identify, [], 'hg identify'),
1137 "import|patch":
1163 "import|patch":
1138 (import_,
1164 (import_,
1139 [('p', 'strip', 1, 'path strip'),
1165 [('p', 'strip', 1, 'path strip'),
1140 ('b', 'base', "", 'base path')],
1166 ('b', 'base', "", 'base path')],
1141 "hg import [-p NUM] [-b BASE] PATCH..."),
1167 "hg import [-p NUM] [-b BASE] PATCH..."),
1142 "^init": (init, [], 'hg init'),
1168 "^init": (init, [], 'hg init'),
1143 "locate":
1169 "locate":
1144 (locate,
1170 (locate,
1145 [('r', 'rev', '', 'revision'),
1171 [('r', 'rev', '', 'revision'),
1146 ('0', 'print0', None, 'end records with NUL'),
1172 ('0', 'print0', None, 'end records with NUL'),
1147 ('f', 'fullpath', None, 'print complete paths'),
1173 ('f', 'fullpath', None, 'print complete paths'),
1148 ('I', 'include', [], 'include path in search'),
1174 ('I', 'include', [], 'include path in search'),
1149 ('X', 'exclude', [], 'exclude path from search')],
1175 ('X', 'exclude', [], 'exclude path from search')],
1150 'hg locate [-r REV] [-f] [-0] [PATTERN]...'),
1176 'hg locate [-r REV] [-f] [-0] [PATTERN]...'),
1151 "^log|history":
1177 "^log|history":
1152 (log,
1178 (log,
1153 [('r', 'rev', [], 'revision'),
1179 [('r', 'rev', [], 'revision'),
1154 ('p', 'patch', None, 'show patch')],
1180 ('p', 'patch', None, 'show patch')],
1155 'hg log [-r REV1 [-r REV2]] [-p] [FILE]'),
1181 'hg log [-r REV1 [-r REV2]] [-p] [FILE]'),
1156 "manifest": (manifest, [], 'hg manifest [REV]'),
1182 "manifest": (manifest, [], 'hg manifest [REV]'),
1157 "parents": (parents, [], 'hg parents [REV]'),
1183 "parents": (parents, [], 'hg parents [REV]'),
1184 "paths": (paths, [], 'hg paths [name]'),
1158 "^pull":
1185 "^pull":
1159 (pull,
1186 (pull,
1160 [('u', 'update', None, 'update working directory')],
1187 [('u', 'update', None, 'update working directory')],
1161 'hg pull [-u] [SOURCE]'),
1188 'hg pull [-u] [SOURCE]'),
1162 "^push": (push, [], 'hg push [DEST]'),
1189 "^push": (push, [], 'hg push [DEST]'),
1163 "rawcommit":
1190 "rawcommit":
1164 (rawcommit,
1191 (rawcommit,
1165 [('p', 'parent', [], 'parent'),
1192 [('p', 'parent', [], 'parent'),
1166 ('d', 'date', "", 'date code'),
1193 ('d', 'date', "", 'date code'),
1167 ('u', 'user', "", 'user'),
1194 ('u', 'user', "", 'user'),
1168 ('F', 'files', "", 'file list'),
1195 ('F', 'files', "", 'file list'),
1169 ('m', 'message', "", 'commit message'),
1196 ('m', 'message', "", 'commit message'),
1170 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1197 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1171 ('l', 'logfile', "", 'commit message file')],
1198 ('l', 'logfile', "", 'commit message file')],
1172 'hg rawcommit [OPTION]... [FILE]...'),
1199 'hg rawcommit [OPTION]... [FILE]...'),
1173 "recover": (recover, [], "hg recover"),
1200 "recover": (recover, [], "hg recover"),
1174 "^remove|rm": (remove, [], "hg remove FILE..."),
1201 "^remove|rm": (remove, [], "hg remove FILE..."),
1175 "^revert":
1202 "^revert":
1176 (revert,
1203 (revert,
1177 [("n", "nonrecursive", None, "don't recurse into subdirs"),
1204 [("n", "nonrecursive", None, "don't recurse into subdirs"),
1178 ("r", "rev", "", "revision")],
1205 ("r", "rev", "", "revision")],
1179 "hg revert [-n] [-r REV] [NAME]..."),
1206 "hg revert [-n] [-r REV] [NAME]..."),
1180 "root": (root, [], "hg root"),
1207 "root": (root, [], "hg root"),
1181 "^serve":
1208 "^serve":
1182 (serve,
1209 (serve,
1183 [('A', 'accesslog', '', 'access log file'),
1210 [('A', 'accesslog', '', 'access log file'),
1184 ('E', 'errorlog', '', 'error log file'),
1211 ('E', 'errorlog', '', 'error log file'),
1185 ('p', 'port', 8000, 'listen port'),
1212 ('p', 'port', 8000, 'listen port'),
1186 ('a', 'address', '', 'interface address'),
1213 ('a', 'address', '', 'interface address'),
1187 ('n', 'name', os.getcwd(), 'repository name'),
1214 ('n', 'name', os.getcwd(), 'repository name'),
1188 ('', 'stdio', None, 'for remote clients'),
1215 ('', 'stdio', None, 'for remote clients'),
1189 ('t', 'templates', "", 'template map')],
1216 ('t', 'templates', "", 'template map')],
1190 "hg serve [OPTION]..."),
1217 "hg serve [OPTION]..."),
1191 "^status": (status,
1218 "^status": (status,
1192 [('I', 'include', [], 'include path in search'),
1219 [('I', 'include', [], 'include path in search'),
1193 ('X', 'exclude', [], 'exclude path from search')],
1220 ('X', 'exclude', [], 'exclude path from search')],
1194 'hg status [FILE]...'),
1221 'hg status [FILE]...'),
1195 "tag":
1222 "tag":
1196 (tag,
1223 (tag,
1197 [('l', 'local', None, 'make the tag local'),
1224 [('l', 'local', None, 'make the tag local'),
1198 ('m', 'message', "", 'commit message'),
1225 ('m', 'message', "", 'commit message'),
1199 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1226 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1200 ('d', 'date', "", 'date code'),
1227 ('d', 'date', "", 'date code'),
1201 ('u', 'user', "", 'user')],
1228 ('u', 'user', "", 'user')],
1202 'hg tag [OPTION]... NAME [REV]'),
1229 'hg tag [OPTION]... NAME [REV]'),
1203 "tags": (tags, [], 'hg tags'),
1230 "tags": (tags, [], 'hg tags'),
1204 "tip": (tip, [], 'hg tip'),
1231 "tip": (tip, [], 'hg tip'),
1205 "undo": (undo, [], 'hg undo'),
1232 "undo": (undo, [], 'hg undo'),
1206 "^update|up|checkout|co":
1233 "^update|up|checkout|co":
1207 (update,
1234 (update,
1208 [('m', 'merge', None, 'allow merging of conflicts'),
1235 [('m', 'merge', None, 'allow merging of conflicts'),
1209 ('C', 'clean', None, 'overwrite locally modified files')],
1236 ('C', 'clean', None, 'overwrite locally modified files')],
1210 'hg update [-m] [-C] [REV]'),
1237 'hg update [-m] [-C] [REV]'),
1211 "verify": (verify, [], 'hg verify'),
1238 "verify": (verify, [], 'hg verify'),
1212 "version": (show_version, [], 'hg version'),
1239 "version": (show_version, [], 'hg version'),
1213 }
1240 }
1214
1241
1215 globalopts = [('v', 'verbose', None, 'verbose'),
1242 globalopts = [('v', 'verbose', None, 'verbose'),
1216 ('', 'debug', None, 'debug'),
1243 ('', 'debug', None, 'debug'),
1217 ('q', 'quiet', None, 'quiet'),
1244 ('q', 'quiet', None, 'quiet'),
1218 ('', 'profile', None, 'profile'),
1245 ('', 'profile', None, 'profile'),
1219 ('R', 'repository', "", 'repository root directory'),
1246 ('R', 'repository', "", 'repository root directory'),
1220 ('', 'traceback', None, 'print traceback on exception'),
1247 ('', 'traceback', None, 'print traceback on exception'),
1221 ('y', 'noninteractive', None, 'run non-interactively'),
1248 ('y', 'noninteractive', None, 'run non-interactively'),
1222 ('', 'version', None, 'output version information and exit'),
1249 ('', 'version', None, 'output version information and exit'),
1250 ('', 'time', None, 'time how long the command takes'),
1223 ]
1251 ]
1224
1252
1225 norepo = "clone init version help debugindex debugindexdot"
1253 norepo = "clone init version help debugindex debugindexdot"
1226
1254
1227 def find(cmd):
1255 def find(cmd):
1228 for e in table.keys():
1256 for e in table.keys():
1229 if re.match("(%s)$" % e, cmd):
1257 if re.match("(%s)$" % e, cmd):
1230 return table[e]
1258 return table[e]
1231
1259
1232 raise UnknownCommand(cmd)
1260 raise UnknownCommand(cmd)
1233
1261
1234 class SignalInterrupt(Exception):
1262 class SignalInterrupt(Exception):
1235 """Exception raised on SIGTERM and SIGHUP."""
1263 """Exception raised on SIGTERM and SIGHUP."""
1236
1264
1237 def catchterm(*args):
1265 def catchterm(*args):
1238 raise SignalInterrupt
1266 raise SignalInterrupt
1239
1267
1240 def run():
1268 def run():
1241 sys.exit(dispatch(sys.argv[1:]))
1269 sys.exit(dispatch(sys.argv[1:]))
1242
1270
1243 class ParseError(Exception):
1271 class ParseError(Exception):
1244 """Exception raised on errors in parsing the command line."""
1272 """Exception raised on errors in parsing the command line."""
1245
1273
1246 def parse(args):
1274 def parse(args):
1247 options = {}
1275 options = {}
1248 cmdoptions = {}
1276 cmdoptions = {}
1249
1277
1250 try:
1278 try:
1251 args = fancyopts.fancyopts(args, globalopts, options)
1279 args = fancyopts.fancyopts(args, globalopts, options)
1252 except fancyopts.getopt.GetoptError, inst:
1280 except fancyopts.getopt.GetoptError, inst:
1253 raise ParseError(None, inst)
1281 raise ParseError(None, inst)
1254
1282
1255 if options["version"]:
1283 if options["version"]:
1256 return ("version", show_version, [], options, cmdoptions)
1284 return ("version", show_version, [], options, cmdoptions)
1257 elif not args:
1285 elif not args:
1258 return ("help", help_, [], options, cmdoptions)
1286 return ("help", help_, [], options, cmdoptions)
1259 else:
1287 else:
1260 cmd, args = args[0], args[1:]
1288 cmd, args = args[0], args[1:]
1261
1289
1262 i = find(cmd)
1290 i = find(cmd)
1263
1291
1264 # combine global options into local
1292 # combine global options into local
1265 c = list(i[1])
1293 c = list(i[1])
1266 for o in globalopts:
1294 for o in globalopts:
1267 c.append((o[0], o[1], options[o[1]], o[3]))
1295 c.append((o[0], o[1], options[o[1]], o[3]))
1268
1296
1269 try:
1297 try:
1270 args = fancyopts.fancyopts(args, c, cmdoptions)
1298 args = fancyopts.fancyopts(args, c, cmdoptions)
1271 except fancyopts.getopt.GetoptError, inst:
1299 except fancyopts.getopt.GetoptError, inst:
1272 raise ParseError(cmd, inst)
1300 raise ParseError(cmd, inst)
1273
1301
1274 # separate global options back out
1302 # separate global options back out
1275 for o in globalopts:
1303 for o in globalopts:
1276 n = o[1]
1304 n = o[1]
1277 options[n] = cmdoptions[n]
1305 options[n] = cmdoptions[n]
1278 del cmdoptions[n]
1306 del cmdoptions[n]
1279
1307
1280 return (cmd, i[0], args, options, cmdoptions)
1308 return (cmd, i[0], args, options, cmdoptions)
1281
1309
1282 def dispatch(args):
1310 def dispatch(args):
1283 signal.signal(signal.SIGTERM, catchterm)
1311 signal.signal(signal.SIGTERM, catchterm)
1284 try:
1312 try:
1285 signal.signal(signal.SIGHUP, catchterm)
1313 signal.signal(signal.SIGHUP, catchterm)
1286 except AttributeError:
1314 except AttributeError:
1287 pass
1315 pass
1288
1316
1289 try:
1317 try:
1290 cmd, func, args, options, cmdoptions = parse(args)
1318 cmd, func, args, options, cmdoptions = parse(args)
1291 except ParseError, inst:
1319 except ParseError, inst:
1292 u = ui.ui()
1320 u = ui.ui()
1293 if inst.args[0]:
1321 if inst.args[0]:
1294 u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
1322 u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
1295 help_(u, inst.args[0])
1323 help_(u, inst.args[0])
1296 else:
1324 else:
1297 u.warn("hg: %s\n" % inst.args[1])
1325 u.warn("hg: %s\n" % inst.args[1])
1298 help_(u)
1326 help_(u)
1299 sys.exit(-1)
1327 sys.exit(-1)
1300 except UnknownCommand, inst:
1328 except UnknownCommand, inst:
1301 u = ui.ui()
1329 u = ui.ui()
1302 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1330 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1303 help_(u)
1331 help_(u)
1304 sys.exit(1)
1332 sys.exit(1)
1305
1333
1334 if options["time"]:
1335 def get_times():
1336 t = os.times()
1337 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
1338 t = (t[0], t[1], t[2], t[3], time.clock())
1339 return t
1340 s = get_times()
1341 def print_time():
1342 t = get_times()
1343 u = ui.ui()
1344 u.warn("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n" %
1345 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
1346 atexit.register(print_time)
1347
1306 u = ui.ui(options["verbose"], options["debug"], options["quiet"],
1348 u = ui.ui(options["verbose"], options["debug"], options["quiet"],
1307 not options["noninteractive"])
1349 not options["noninteractive"])
1308
1350
1309 try:
1351 try:
1310 try:
1352 try:
1311 if cmd not in norepo.split():
1353 if cmd not in norepo.split():
1312 path = options["repository"] or ""
1354 path = options["repository"] or ""
1313 repo = hg.repository(ui=u, path=path)
1355 repo = hg.repository(ui=u, path=path)
1314 d = lambda: func(u, repo, *args, **cmdoptions)
1356 d = lambda: func(u, repo, *args, **cmdoptions)
1315 else:
1357 else:
1316 d = lambda: func(u, *args, **cmdoptions)
1358 d = lambda: func(u, *args, **cmdoptions)
1317
1359
1318 if options['profile']:
1360 if options['profile']:
1319 import hotshot, hotshot.stats
1361 import hotshot, hotshot.stats
1320 prof = hotshot.Profile("hg.prof")
1362 prof = hotshot.Profile("hg.prof")
1321 r = prof.runcall(d)
1363 r = prof.runcall(d)
1322 prof.close()
1364 prof.close()
1323 stats = hotshot.stats.load("hg.prof")
1365 stats = hotshot.stats.load("hg.prof")
1324 stats.strip_dirs()
1366 stats.strip_dirs()
1325 stats.sort_stats('time', 'calls')
1367 stats.sort_stats('time', 'calls')
1326 stats.print_stats(40)
1368 stats.print_stats(40)
1327 return r
1369 return r
1328 else:
1370 else:
1329 return d()
1371 return d()
1330 except:
1372 except:
1331 if options['traceback']:
1373 if options['traceback']:
1332 traceback.print_exc()
1374 traceback.print_exc()
1333 raise
1375 raise
1334 except util.CommandError, inst:
1376 except util.CommandError, inst:
1335 u.warn("abort: %s\n" % inst.args)
1377 u.warn("abort: %s\n" % inst.args)
1336 except hg.RepoError, inst:
1378 except hg.RepoError, inst:
1337 u.warn("abort: ", inst, "!\n")
1379 u.warn("abort: ", inst, "!\n")
1338 except SignalInterrupt:
1380 except SignalInterrupt:
1339 u.warn("killed!\n")
1381 u.warn("killed!\n")
1340 except KeyboardInterrupt:
1382 except KeyboardInterrupt:
1341 u.warn("interrupted!\n")
1383 u.warn("interrupted!\n")
1342 except IOError, inst:
1384 except IOError, inst:
1343 if hasattr(inst, "code"):
1385 if hasattr(inst, "code"):
1344 u.warn("abort: %s\n" % inst)
1386 u.warn("abort: %s\n" % inst)
1345 elif hasattr(inst, "reason"):
1387 elif hasattr(inst, "reason"):
1346 u.warn("abort: error %d: %s\n" % (inst.reason[0], inst.reason[1]))
1388 u.warn("abort: error: %s\n" % inst.reason[1])
1347 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
1389 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
1348 if u.debugflag: u.warn("broken pipe\n")
1390 if u.debugflag: u.warn("broken pipe\n")
1349 else:
1391 else:
1350 raise
1392 raise
1351 except OSError, inst:
1393 except OSError, inst:
1352 if hasattr(inst, "filename"):
1394 if hasattr(inst, "filename"):
1353 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
1395 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
1354 else:
1396 else:
1355 u.warn("abort: %s\n" % inst.strerror)
1397 u.warn("abort: %s\n" % inst.strerror)
1356 except Abort, inst:
1398 except Abort, inst:
1357 u.warn('abort: ', inst.args[0] % inst.args[1:], '\n')
1399 u.warn('abort: ', inst.args[0] % inst.args[1:], '\n')
1358 sys.exit(1)
1400 sys.exit(1)
1359 except TypeError, inst:
1401 except TypeError, inst:
1360 # was this an argument error?
1402 # was this an argument error?
1361 tb = traceback.extract_tb(sys.exc_info()[2])
1403 tb = traceback.extract_tb(sys.exc_info()[2])
1362 if len(tb) > 2: # no
1404 if len(tb) > 2: # no
1363 raise
1405 raise
1364 u.debug(inst, "\n")
1406 u.debug(inst, "\n")
1365 u.warn("%s: invalid arguments\n" % cmd)
1407 u.warn("%s: invalid arguments\n" % cmd)
1366 help_(u, cmd)
1408 help_(u, cmd)
1367
1409
1368 sys.exit(-1)
1410 sys.exit(-1)
@@ -1,1957 +1,1979
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 urlparse")
13 demandload(globals(), "tempfile httprangereader bdiff urlparse")
14 demandload(globals(), "bisect select")
14 demandload(globals(), "bisect select")
15
15
16 class filelog(revlog):
16 class filelog(revlog):
17 def __init__(self, opener, path):
17 def __init__(self, opener, path):
18 revlog.__init__(self, opener,
18 revlog.__init__(self, opener,
19 os.path.join("data", path + ".i"),
19 os.path.join("data", self.encodedir(path + ".i")),
20 os.path.join("data", path + ".d"))
20 os.path.join("data", self.encodedir(path + ".d")))
21
22 # This avoids a collision between a file named foo and a dir named
23 # foo.i or foo.d
24 def encodedir(self, path):
25 path.replace(".hg/", ".hg.hg/")
26 path.replace(".i/", ".i.hg/")
27 path.replace(".d/", ".i.hg/")
28 return path
29
30 def decodedir(self, path):
31 path.replace(".d.hg/", ".d/")
32 path.replace(".i.hg/", ".i/")
33 path.replace(".hg.hg/", ".hg/")
34 return path
21
35
22 def read(self, node):
36 def read(self, node):
23 t = self.revision(node)
37 t = self.revision(node)
24 if not t.startswith('\1\n'):
38 if not t.startswith('\1\n'):
25 return t
39 return t
26 s = t.find('\1\n', 2)
40 s = t.find('\1\n', 2)
27 return t[s+2:]
41 return t[s+2:]
28
42
29 def readmeta(self, node):
43 def readmeta(self, node):
30 t = self.revision(node)
44 t = self.revision(node)
31 if not t.startswith('\1\n'):
45 if not t.startswith('\1\n'):
32 return t
46 return t
33 s = t.find('\1\n', 2)
47 s = t.find('\1\n', 2)
34 mt = t[2:s]
48 mt = t[2:s]
35 for l in mt.splitlines():
49 for l in mt.splitlines():
36 k, v = l.split(": ", 1)
50 k, v = l.split(": ", 1)
37 m[k] = v
51 m[k] = v
38 return m
52 return m
39
53
40 def add(self, text, meta, transaction, link, p1=None, p2=None):
54 def add(self, text, meta, transaction, link, p1=None, p2=None):
41 if meta or text.startswith('\1\n'):
55 if meta or text.startswith('\1\n'):
42 mt = ""
56 mt = ""
43 if meta:
57 if meta:
44 mt = [ "%s: %s\n" % (k, v) for k,v in meta.items() ]
58 mt = [ "%s: %s\n" % (k, v) for k,v in meta.items() ]
45 text = "\1\n" + "".join(mt) + "\1\n" + text
59 text = "\1\n" + "".join(mt) + "\1\n" + text
46 return self.addrevision(text, transaction, link, p1, p2)
60 return self.addrevision(text, transaction, link, p1, p2)
47
61
48 def annotate(self, node):
62 def annotate(self, node):
49
63
50 def decorate(text, rev):
64 def decorate(text, rev):
51 return ([rev] * len(text.splitlines()), text)
65 return ([rev] * len(text.splitlines()), text)
52
66
53 def pair(parent, child):
67 def pair(parent, child):
54 for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]):
68 for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]):
55 child[0][b1:b2] = parent[0][a1:a2]
69 child[0][b1:b2] = parent[0][a1:a2]
56 return child
70 return child
57
71
58 # find all ancestors
72 # find all ancestors
59 needed = {node:1}
73 needed = {node:1}
60 visit = [node]
74 visit = [node]
61 while visit:
75 while visit:
62 n = visit.pop(0)
76 n = visit.pop(0)
63 for p in self.parents(n):
77 for p in self.parents(n):
64 if p not in needed:
78 if p not in needed:
65 needed[p] = 1
79 needed[p] = 1
66 visit.append(p)
80 visit.append(p)
67 else:
81 else:
68 # count how many times we'll use this
82 # count how many times we'll use this
69 needed[p] += 1
83 needed[p] += 1
70
84
71 # sort by revision which is a topological order
85 # sort by revision which is a topological order
72 visit = [ (self.rev(n), n) for n in needed.keys() ]
86 visit = [ (self.rev(n), n) for n in needed.keys() ]
73 visit.sort()
87 visit.sort()
74 hist = {}
88 hist = {}
75
89
76 for r,n in visit:
90 for r,n in visit:
77 curr = decorate(self.read(n), self.linkrev(n))
91 curr = decorate(self.read(n), self.linkrev(n))
78 for p in self.parents(n):
92 for p in self.parents(n):
79 if p != nullid:
93 if p != nullid:
80 curr = pair(hist[p], curr)
94 curr = pair(hist[p], curr)
81 # trim the history of unneeded revs
95 # trim the history of unneeded revs
82 needed[p] -= 1
96 needed[p] -= 1
83 if not needed[p]:
97 if not needed[p]:
84 del hist[p]
98 del hist[p]
85 hist[n] = curr
99 hist[n] = curr
86
100
87 return zip(hist[n][0], hist[n][1].splitlines(1))
101 return zip(hist[n][0], hist[n][1].splitlines(1))
88
102
89 class manifest(revlog):
103 class manifest(revlog):
90 def __init__(self, opener):
104 def __init__(self, opener):
91 self.mapcache = None
105 self.mapcache = None
92 self.listcache = None
106 self.listcache = None
93 self.addlist = None
107 self.addlist = None
94 revlog.__init__(self, opener, "00manifest.i", "00manifest.d")
108 revlog.__init__(self, opener, "00manifest.i", "00manifest.d")
95
109
96 def read(self, node):
110 def read(self, node):
97 if node == nullid: return {} # don't upset local cache
111 if node == nullid: return {} # don't upset local cache
98 if self.mapcache and self.mapcache[0] == node:
112 if self.mapcache and self.mapcache[0] == node:
99 return self.mapcache[1]
113 return self.mapcache[1]
100 text = self.revision(node)
114 text = self.revision(node)
101 map = {}
115 map = {}
102 flag = {}
116 flag = {}
103 self.listcache = (text, text.splitlines(1))
117 self.listcache = (text, text.splitlines(1))
104 for l in self.listcache[1]:
118 for l in self.listcache[1]:
105 (f, n) = l.split('\0')
119 (f, n) = l.split('\0')
106 map[f] = bin(n[:40])
120 map[f] = bin(n[:40])
107 flag[f] = (n[40:-1] == "x")
121 flag[f] = (n[40:-1] == "x")
108 self.mapcache = (node, map, flag)
122 self.mapcache = (node, map, flag)
109 return map
123 return map
110
124
111 def readflags(self, node):
125 def readflags(self, node):
112 if node == nullid: return {} # don't upset local cache
126 if node == nullid: return {} # don't upset local cache
113 if not self.mapcache or self.mapcache[0] != node:
127 if not self.mapcache or self.mapcache[0] != node:
114 self.read(node)
128 self.read(node)
115 return self.mapcache[2]
129 return self.mapcache[2]
116
130
117 def diff(self, a, b):
131 def diff(self, a, b):
118 # this is sneaky, as we're not actually using a and b
132 # this is sneaky, as we're not actually using a and b
119 if self.listcache and self.addlist and self.listcache[0] == a:
133 if self.listcache and self.addlist and self.listcache[0] == a:
120 d = mdiff.diff(self.listcache[1], self.addlist, 1)
134 d = mdiff.diff(self.listcache[1], self.addlist, 1)
121 if mdiff.patch(a, d) != b:
135 if mdiff.patch(a, d) != b:
122 sys.stderr.write("*** sortdiff failed, falling back ***\n")
136 sys.stderr.write("*** sortdiff failed, falling back ***\n")
123 return mdiff.textdiff(a, b)
137 return mdiff.textdiff(a, b)
124 return d
138 return d
125 else:
139 else:
126 return mdiff.textdiff(a, b)
140 return mdiff.textdiff(a, b)
127
141
128 def add(self, map, flags, transaction, link, p1=None, p2=None,
142 def add(self, map, flags, transaction, link, p1=None, p2=None,
129 changed=None):
143 changed=None):
130 # directly generate the mdiff delta from the data collected during
144 # directly generate the mdiff delta from the data collected during
131 # the bisect loop below
145 # the bisect loop below
132 def gendelta(delta):
146 def gendelta(delta):
133 i = 0
147 i = 0
134 result = []
148 result = []
135 while i < len(delta):
149 while i < len(delta):
136 start = delta[i][2]
150 start = delta[i][2]
137 end = delta[i][3]
151 end = delta[i][3]
138 l = delta[i][4]
152 l = delta[i][4]
139 if l == None:
153 if l == None:
140 l = ""
154 l = ""
141 while i < len(delta) - 1 and start <= delta[i+1][2] \
155 while i < len(delta) - 1 and start <= delta[i+1][2] \
142 and end >= delta[i+1][2]:
156 and end >= delta[i+1][2]:
143 if delta[i+1][3] > end:
157 if delta[i+1][3] > end:
144 end = delta[i+1][3]
158 end = delta[i+1][3]
145 if delta[i+1][4]:
159 if delta[i+1][4]:
146 l += delta[i+1][4]
160 l += delta[i+1][4]
147 i += 1
161 i += 1
148 result.append(struct.pack(">lll", start, end, len(l)) + l)
162 result.append(struct.pack(">lll", start, end, len(l)) + l)
149 i += 1
163 i += 1
150 return result
164 return result
151
165
152 # apply the changes collected during the bisect loop to our addlist
166 # apply the changes collected during the bisect loop to our addlist
153 def addlistdelta(addlist, delta):
167 def addlistdelta(addlist, delta):
154 # apply the deltas to the addlist. start from the bottom up
168 # apply the deltas to the addlist. start from the bottom up
155 # so changes to the offsets don't mess things up.
169 # so changes to the offsets don't mess things up.
156 i = len(delta)
170 i = len(delta)
157 while i > 0:
171 while i > 0:
158 i -= 1
172 i -= 1
159 start = delta[i][0]
173 start = delta[i][0]
160 end = delta[i][1]
174 end = delta[i][1]
161 if delta[i][4]:
175 if delta[i][4]:
162 addlist[start:end] = [delta[i][4]]
176 addlist[start:end] = [delta[i][4]]
163 else:
177 else:
164 del addlist[start:end]
178 del addlist[start:end]
165 return addlist
179 return addlist
166
180
167 # calculate the byte offset of the start of each line in the
181 # calculate the byte offset of the start of each line in the
168 # manifest
182 # manifest
169 def calcoffsets(addlist):
183 def calcoffsets(addlist):
170 offsets = [0] * (len(addlist) + 1)
184 offsets = [0] * (len(addlist) + 1)
171 offset = 0
185 offset = 0
172 i = 0
186 i = 0
173 while i < len(addlist):
187 while i < len(addlist):
174 offsets[i] = offset
188 offsets[i] = offset
175 offset += len(addlist[i])
189 offset += len(addlist[i])
176 i += 1
190 i += 1
177 offsets[i] = offset
191 offsets[i] = offset
178 return offsets
192 return offsets
179
193
180 # if we're using the listcache, make sure it is valid and
194 # if we're using the listcache, make sure it is valid and
181 # parented by the same node we're diffing against
195 # parented by the same node we're diffing against
182 if not changed or not self.listcache or not p1 or \
196 if not changed or not self.listcache or not p1 or \
183 self.mapcache[0] != p1:
197 self.mapcache[0] != p1:
184 files = map.keys()
198 files = map.keys()
185 files.sort()
199 files.sort()
186
200
187 self.addlist = ["%s\000%s%s\n" %
201 self.addlist = ["%s\000%s%s\n" %
188 (f, hex(map[f]), flags[f] and "x" or '')
202 (f, hex(map[f]), flags[f] and "x" or '')
189 for f in files]
203 for f in files]
190 cachedelta = None
204 cachedelta = None
191 else:
205 else:
192 addlist = self.listcache[1]
206 addlist = self.listcache[1]
193
207
194 # find the starting offset for each line in the add list
208 # find the starting offset for each line in the add list
195 offsets = calcoffsets(addlist)
209 offsets = calcoffsets(addlist)
196
210
197 # combine the changed lists into one list for sorting
211 # combine the changed lists into one list for sorting
198 work = [[x, 0] for x in changed[0]]
212 work = [[x, 0] for x in changed[0]]
199 work[len(work):] = [[x, 1] for x in changed[1]]
213 work[len(work):] = [[x, 1] for x in changed[1]]
200 work.sort()
214 work.sort()
201
215
202 delta = []
216 delta = []
203 bs = 0
217 bs = 0
204
218
205 for w in work:
219 for w in work:
206 f = w[0]
220 f = w[0]
207 # bs will either be the index of the item or the insert point
221 # bs will either be the index of the item or the insert point
208 bs = bisect.bisect(addlist, f, bs)
222 bs = bisect.bisect(addlist, f, bs)
209 if bs < len(addlist):
223 if bs < len(addlist):
210 fn = addlist[bs][:addlist[bs].index('\0')]
224 fn = addlist[bs][:addlist[bs].index('\0')]
211 else:
225 else:
212 fn = None
226 fn = None
213 if w[1] == 0:
227 if w[1] == 0:
214 l = "%s\000%s%s\n" % (f, hex(map[f]),
228 l = "%s\000%s%s\n" % (f, hex(map[f]),
215 flags[f] and "x" or '')
229 flags[f] and "x" or '')
216 else:
230 else:
217 l = None
231 l = None
218 start = bs
232 start = bs
219 if fn != f:
233 if fn != f:
220 # item not found, insert a new one
234 # item not found, insert a new one
221 end = bs
235 end = bs
222 if w[1] == 1:
236 if w[1] == 1:
223 sys.stderr.write("failed to remove %s from manifest\n"
237 sys.stderr.write("failed to remove %s from manifest\n"
224 % f)
238 % f)
225 sys.exit(1)
239 sys.exit(1)
226 else:
240 else:
227 # item is found, replace/delete the existing line
241 # item is found, replace/delete the existing line
228 end = bs + 1
242 end = bs + 1
229 delta.append([start, end, offsets[start], offsets[end], l])
243 delta.append([start, end, offsets[start], offsets[end], l])
230
244
231 self.addlist = addlistdelta(addlist, delta)
245 self.addlist = addlistdelta(addlist, delta)
232 if self.mapcache[0] == self.tip():
246 if self.mapcache[0] == self.tip():
233 cachedelta = "".join(gendelta(delta))
247 cachedelta = "".join(gendelta(delta))
234 else:
248 else:
235 cachedelta = None
249 cachedelta = None
236
250
237 text = "".join(self.addlist)
251 text = "".join(self.addlist)
238 if cachedelta and mdiff.patch(self.listcache[0], cachedelta) != text:
252 if cachedelta and mdiff.patch(self.listcache[0], cachedelta) != text:
239 sys.stderr.write("manifest delta failure\n")
253 sys.stderr.write("manifest delta failure\n")
240 sys.exit(1)
254 sys.exit(1)
241 n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
255 n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
242 self.mapcache = (n, map, flags)
256 self.mapcache = (n, map, flags)
243 self.listcache = (text, self.addlist)
257 self.listcache = (text, self.addlist)
244 self.addlist = None
258 self.addlist = None
245
259
246 return n
260 return n
247
261
248 class changelog(revlog):
262 class changelog(revlog):
249 def __init__(self, opener):
263 def __init__(self, opener):
250 revlog.__init__(self, opener, "00changelog.i", "00changelog.d")
264 revlog.__init__(self, opener, "00changelog.i", "00changelog.d")
251
265
252 def extract(self, text):
266 def extract(self, text):
253 if not text:
267 if not text:
254 return (nullid, "", "0", [], "")
268 return (nullid, "", "0", [], "")
255 last = text.index("\n\n")
269 last = text.index("\n\n")
256 desc = text[last + 2:]
270 desc = text[last + 2:]
257 l = text[:last].splitlines()
271 l = text[:last].splitlines()
258 manifest = bin(l[0])
272 manifest = bin(l[0])
259 user = l[1]
273 user = l[1]
260 date = l[2]
274 date = l[2]
261 files = l[3:]
275 files = l[3:]
262 return (manifest, user, date, files, desc)
276 return (manifest, user, date, files, desc)
263
277
264 def read(self, node):
278 def read(self, node):
265 return self.extract(self.revision(node))
279 return self.extract(self.revision(node))
266
280
267 def add(self, manifest, list, desc, transaction, p1=None, p2=None,
281 def add(self, manifest, list, desc, transaction, p1=None, p2=None,
268 user=None, date=None):
282 user=None, date=None):
269 date = date or "%d %d" % (time.time(), time.timezone)
283 date = date or "%d %d" % (time.time(), time.timezone)
270 list.sort()
284 list.sort()
271 l = [hex(manifest), user, date] + list + ["", desc]
285 l = [hex(manifest), user, date] + list + ["", desc]
272 text = "\n".join(l)
286 text = "\n".join(l)
273 return self.addrevision(text, transaction, self.count(), p1, p2)
287 return self.addrevision(text, transaction, self.count(), p1, p2)
274
288
275 class dirstate:
289 class dirstate:
276 def __init__(self, opener, ui, root):
290 def __init__(self, opener, ui, root):
277 self.opener = opener
291 self.opener = opener
278 self.root = root
292 self.root = root
279 self.dirty = 0
293 self.dirty = 0
280 self.ui = ui
294 self.ui = ui
281 self.map = None
295 self.map = None
282 self.pl = None
296 self.pl = None
283 self.copies = {}
297 self.copies = {}
284 self.ignorefunc = None
298 self.ignorefunc = None
285
299
286 def wjoin(self, f):
300 def wjoin(self, f):
287 return os.path.join(self.root, f)
301 return os.path.join(self.root, f)
288
302
289 def ignore(self, f):
303 def ignore(self, f):
290 if not self.ignorefunc:
304 if not self.ignorefunc:
291 bigpat = []
305 bigpat = []
292 try:
306 try:
293 l = file(self.wjoin(".hgignore"))
307 l = file(self.wjoin(".hgignore"))
294 for pat in l:
308 for pat in l:
295 if pat != "\n":
309 if pat != "\n":
296 p = util.pconvert(pat[:-1])
310 p = util.pconvert(pat[:-1])
297 try:
311 try:
298 r = re.compile(p)
312 r = re.compile(p)
299 except:
313 except:
300 self.ui.warn("ignoring invalid ignore"
314 self.ui.warn("ignoring invalid ignore"
301 + " regular expression '%s'\n" % p)
315 + " regular expression '%s'\n" % p)
302 else:
316 else:
303 bigpat.append(util.pconvert(pat[:-1]))
317 bigpat.append(util.pconvert(pat[:-1]))
304 except IOError: pass
318 except IOError: pass
305
319
306 if bigpat:
320 if bigpat:
307 s = "(?:%s)" % (")|(?:".join(bigpat))
321 s = "(?:%s)" % (")|(?:".join(bigpat))
308 r = re.compile(s)
322 r = re.compile(s)
309 self.ignorefunc = r.search
323 self.ignorefunc = r.search
310 else:
324 else:
311 self.ignorefunc = util.never
325 self.ignorefunc = util.never
312
326
313 return self.ignorefunc(f)
327 return self.ignorefunc(f)
314
328
315 def __del__(self):
329 def __del__(self):
316 if self.dirty:
330 if self.dirty:
317 self.write()
331 self.write()
318
332
319 def __getitem__(self, key):
333 def __getitem__(self, key):
320 try:
334 try:
321 return self.map[key]
335 return self.map[key]
322 except TypeError:
336 except TypeError:
323 self.read()
337 self.read()
324 return self[key]
338 return self[key]
325
339
326 def __contains__(self, key):
340 def __contains__(self, key):
327 if not self.map: self.read()
341 if not self.map: self.read()
328 return key in self.map
342 return key in self.map
329
343
330 def parents(self):
344 def parents(self):
331 if not self.pl:
345 if not self.pl:
332 self.read()
346 self.read()
333 return self.pl
347 return self.pl
334
348
335 def markdirty(self):
349 def markdirty(self):
336 if not self.dirty:
350 if not self.dirty:
337 self.dirty = 1
351 self.dirty = 1
338
352
339 def setparents(self, p1, p2 = nullid):
353 def setparents(self, p1, p2 = nullid):
340 self.markdirty()
354 self.markdirty()
341 self.pl = p1, p2
355 self.pl = p1, p2
342
356
343 def state(self, key):
357 def state(self, key):
344 try:
358 try:
345 return self[key][0]
359 return self[key][0]
346 except KeyError:
360 except KeyError:
347 return "?"
361 return "?"
348
362
349 def read(self):
363 def read(self):
350 if self.map is not None: return self.map
364 if self.map is not None: return self.map
351
365
352 self.map = {}
366 self.map = {}
353 self.pl = [nullid, nullid]
367 self.pl = [nullid, nullid]
354 try:
368 try:
355 st = self.opener("dirstate").read()
369 st = self.opener("dirstate").read()
356 if not st: return
370 if not st: return
357 except: return
371 except: return
358
372
359 self.pl = [st[:20], st[20: 40]]
373 self.pl = [st[:20], st[20: 40]]
360
374
361 pos = 40
375 pos = 40
362 while pos < len(st):
376 while pos < len(st):
363 e = struct.unpack(">cllll", st[pos:pos+17])
377 e = struct.unpack(">cllll", st[pos:pos+17])
364 l = e[4]
378 l = e[4]
365 pos += 17
379 pos += 17
366 f = st[pos:pos + l]
380 f = st[pos:pos + l]
367 if '\0' in f:
381 if '\0' in f:
368 f, c = f.split('\0')
382 f, c = f.split('\0')
369 self.copies[f] = c
383 self.copies[f] = c
370 self.map[f] = e[:4]
384 self.map[f] = e[:4]
371 pos += l
385 pos += l
372
386
373 def copy(self, source, dest):
387 def copy(self, source, dest):
374 self.read()
388 self.read()
375 self.markdirty()
389 self.markdirty()
376 self.copies[dest] = source
390 self.copies[dest] = source
377
391
378 def copied(self, file):
392 def copied(self, file):
379 return self.copies.get(file, None)
393 return self.copies.get(file, None)
380
394
381 def update(self, files, state):
395 def update(self, files, state):
382 ''' current states:
396 ''' current states:
383 n normal
397 n normal
384 m needs merging
398 m needs merging
385 r marked for removal
399 r marked for removal
386 a marked for addition'''
400 a marked for addition'''
387
401
388 if not files: return
402 if not files: return
389 self.read()
403 self.read()
390 self.markdirty()
404 self.markdirty()
391 for f in files:
405 for f in files:
392 if state == "r":
406 if state == "r":
393 self.map[f] = ('r', 0, 0, 0)
407 self.map[f] = ('r', 0, 0, 0)
394 else:
408 else:
395 s = os.stat(os.path.join(self.root, f))
409 s = os.stat(os.path.join(self.root, f))
396 self.map[f] = (state, s.st_mode, s.st_size, s.st_mtime)
410 self.map[f] = (state, s.st_mode, s.st_size, s.st_mtime)
397
411
398 def forget(self, files):
412 def forget(self, files):
399 if not files: return
413 if not files: return
400 self.read()
414 self.read()
401 self.markdirty()
415 self.markdirty()
402 for f in files:
416 for f in files:
403 try:
417 try:
404 del self.map[f]
418 del self.map[f]
405 except KeyError:
419 except KeyError:
406 self.ui.warn("not in dirstate: %s!\n" % f)
420 self.ui.warn("not in dirstate: %s!\n" % f)
407 pass
421 pass
408
422
409 def clear(self):
423 def clear(self):
410 self.map = {}
424 self.map = {}
411 self.markdirty()
425 self.markdirty()
412
426
413 def write(self):
427 def write(self):
414 st = self.opener("dirstate", "w")
428 st = self.opener("dirstate", "w")
415 st.write("".join(self.pl))
429 st.write("".join(self.pl))
416 for f, e in self.map.items():
430 for f, e in self.map.items():
417 c = self.copied(f)
431 c = self.copied(f)
418 if c:
432 if c:
419 f = f + "\0" + c
433 f = f + "\0" + c
420 e = struct.pack(">cllll", e[0], e[1], e[2], e[3], len(f))
434 e = struct.pack(">cllll", e[0], e[1], e[2], e[3], len(f))
421 st.write(e + f)
435 st.write(e + f)
422 self.dirty = 0
436 self.dirty = 0
423
437
424 def walk(self, files = None, match = util.always):
438 def walk(self, files = None, match = util.always):
425 self.read()
439 self.read()
426 dc = self.map.copy()
440 dc = self.map.copy()
427 # walk all files by default
441 # walk all files by default
428 if not files: files = [self.root]
442 if not files: files = [self.root]
429 def traverse():
443 def traverse():
430 for f in util.unique(files):
444 for f in util.unique(files):
431 f = os.path.join(self.root, f)
445 f = os.path.join(self.root, f)
432 if os.path.isdir(f):
446 if os.path.isdir(f):
433 for dir, subdirs, fl in os.walk(f):
447 for dir, subdirs, fl in os.walk(f):
434 d = dir[len(self.root) + 1:]
448 d = dir[len(self.root) + 1:]
435 if d == '.hg':
449 if d == '.hg':
436 subdirs[:] = []
450 subdirs[:] = []
437 continue
451 continue
438 for sd in subdirs:
452 for sd in subdirs:
439 ds = os.path.join(d, sd +'/')
453 ds = os.path.join(d, sd +'/')
440 if self.ignore(ds) or not match(ds):
454 if self.ignore(ds) or not match(ds):
441 subdirs.remove(sd)
455 subdirs.remove(sd)
442 for fn in fl:
456 for fn in fl:
443 fn = util.pconvert(os.path.join(d, fn))
457 fn = util.pconvert(os.path.join(d, fn))
444 yield 'f', fn
458 yield 'f', fn
445 else:
459 else:
446 yield 'f', f[len(self.root) + 1:]
460 yield 'f', f[len(self.root) + 1:]
447
461
448 for k in dc.keys():
462 for k in dc.keys():
449 yield 'm', k
463 yield 'm', k
450
464
451 # yield only files that match: all in dirstate, others only if
465 # yield only files that match: all in dirstate, others only if
452 # not in .hgignore
466 # not in .hgignore
453
467
454 for src, fn in util.unique(traverse()):
468 for src, fn in util.unique(traverse()):
455 if fn in dc:
469 if fn in dc:
456 del dc[fn]
470 del dc[fn]
457 elif self.ignore(fn):
471 elif self.ignore(fn):
458 continue
472 continue
459 if match(fn):
473 if match(fn):
460 yield src, fn
474 yield src, fn
461
475
462 def changes(self, files = None, match = util.always):
476 def changes(self, files = None, match = util.always):
463 self.read()
477 self.read()
464 dc = self.map.copy()
478 dc = self.map.copy()
465 lookup, changed, added, unknown = [], [], [], []
479 lookup, changed, added, unknown = [], [], [], []
466
480
467 for src, fn in self.walk(files, match):
481 for src, fn in self.walk(files, match):
468 try: s = os.stat(os.path.join(self.root, fn))
482 try: s = os.stat(os.path.join(self.root, fn))
469 except: continue
483 except: continue
470
484
471 if fn in dc:
485 if fn in dc:
472 c = dc[fn]
486 c = dc[fn]
473 del dc[fn]
487 del dc[fn]
474
488
475 if c[0] == 'm':
489 if c[0] == 'm':
476 changed.append(fn)
490 changed.append(fn)
477 elif c[0] == 'a':
491 elif c[0] == 'a':
478 added.append(fn)
492 added.append(fn)
479 elif c[0] == 'r':
493 elif c[0] == 'r':
480 unknown.append(fn)
494 unknown.append(fn)
481 elif c[2] != s.st_size or (c[1] ^ s.st_mode) & 0100:
495 elif c[2] != s.st_size or (c[1] ^ s.st_mode) & 0100:
482 changed.append(fn)
496 changed.append(fn)
483 elif c[1] != s.st_mode or c[3] != s.st_mtime:
497 elif c[1] != s.st_mode or c[3] != s.st_mtime:
484 lookup.append(fn)
498 lookup.append(fn)
485 else:
499 else:
486 if match(fn): unknown.append(fn)
500 if match(fn): unknown.append(fn)
487
501
488 return (lookup, changed, added, filter(match, dc.keys()), unknown)
502 return (lookup, changed, added, filter(match, dc.keys()), unknown)
489
503
490 # used to avoid circular references so destructors work
504 # used to avoid circular references so destructors work
491 def opener(base):
505 def opener(base):
492 p = base
506 p = base
493 def o(path, mode="r"):
507 def o(path, mode="r"):
494 if p.startswith("http://"):
508 if p.startswith("http://"):
495 f = os.path.join(p, urllib.quote(path))
509 f = os.path.join(p, urllib.quote(path))
496 return httprangereader.httprangereader(f)
510 return httprangereader.httprangereader(f)
497
511
498 f = os.path.join(p, path)
512 f = os.path.join(p, path)
499
513
500 mode += "b" # for that other OS
514 mode += "b" # for that other OS
501
515
502 if mode[0] != "r":
516 if mode[0] != "r":
503 try:
517 try:
504 s = os.stat(f)
518 s = os.stat(f)
505 except OSError:
519 except OSError:
506 d = os.path.dirname(f)
520 d = os.path.dirname(f)
507 if not os.path.isdir(d):
521 if not os.path.isdir(d):
508 os.makedirs(d)
522 os.makedirs(d)
509 else:
523 else:
510 if s.st_nlink > 1:
524 if s.st_nlink > 1:
511 file(f + ".tmp", "wb").write(file(f, "rb").read())
525 file(f + ".tmp", "wb").write(file(f, "rb").read())
512 util.rename(f+".tmp", f)
526 util.rename(f+".tmp", f)
513
527
514 return file(f, mode)
528 return file(f, mode)
515
529
516 return o
530 return o
517
531
518 class RepoError(Exception): pass
532 class RepoError(Exception): pass
519
533
520 class localrepository:
534 class localrepository:
521 def __init__(self, ui, path=None, create=0):
535 def __init__(self, ui, path=None, create=0):
522 self.remote = 0
536 self.remote = 0
523 if path and path.startswith("http://"):
537 if path and path.startswith("http://"):
524 self.remote = 1
538 self.remote = 1
525 self.path = path
539 self.path = path
526 else:
540 else:
527 if not path:
541 if not path:
528 p = os.getcwd()
542 p = os.getcwd()
529 while not os.path.isdir(os.path.join(p, ".hg")):
543 while not os.path.isdir(os.path.join(p, ".hg")):
530 oldp = p
544 oldp = p
531 p = os.path.dirname(p)
545 p = os.path.dirname(p)
532 if p == oldp: raise RepoError("no repo found")
546 if p == oldp: raise RepoError("no repo found")
533 path = p
547 path = p
534 self.path = os.path.join(path, ".hg")
548 self.path = os.path.join(path, ".hg")
535
549
536 if not create and not os.path.isdir(self.path):
550 if not create and not os.path.isdir(self.path):
537 raise RepoError("repository %s not found" % self.path)
551 raise RepoError("repository %s not found" % self.path)
538
552
539 self.root = path
553 self.root = path
540 self.ui = ui
554 self.ui = ui
541
555
542 if create:
556 if create:
543 os.mkdir(self.path)
557 os.mkdir(self.path)
544 os.mkdir(self.join("data"))
558 os.mkdir(self.join("data"))
545
559
546 self.opener = opener(self.path)
560 self.opener = opener(self.path)
547 self.wopener = opener(self.root)
561 self.wopener = opener(self.root)
548 self.manifest = manifest(self.opener)
562 self.manifest = manifest(self.opener)
549 self.changelog = changelog(self.opener)
563 self.changelog = changelog(self.opener)
550 self.tagscache = None
564 self.tagscache = None
551 self.nodetagscache = None
565 self.nodetagscache = None
552
566
553 if not self.remote:
567 if not self.remote:
554 self.dirstate = dirstate(self.opener, ui, self.root)
568 self.dirstate = dirstate(self.opener, ui, self.root)
555 try:
569 try:
556 self.ui.readconfig(self.opener("hgrc"))
570 self.ui.readconfig(self.opener("hgrc"))
557 except IOError: pass
571 except IOError: pass
558
572
559 def hook(self, name, **args):
573 def hook(self, name, **args):
560 s = self.ui.config("hooks", name)
574 s = self.ui.config("hooks", name)
561 if s:
575 if s:
562 self.ui.note("running hook %s: %s\n" % (name, s))
576 self.ui.note("running hook %s: %s\n" % (name, s))
563 old = {}
577 old = {}
564 for k, v in args.items():
578 for k, v in args.items():
565 k = k.upper()
579 k = k.upper()
566 old[k] = os.environ.get(k, None)
580 old[k] = os.environ.get(k, None)
567 os.environ[k] = v
581 os.environ[k] = v
568
582
569 r = os.system(s)
583 r = os.system(s)
570
584
571 for k, v in old.items():
585 for k, v in old.items():
572 if v != None:
586 if v != None:
573 os.environ[k] = v
587 os.environ[k] = v
574 else:
588 else:
575 del os.environ[k]
589 del os.environ[k]
576
590
577 if r:
591 if r:
578 self.ui.warn("abort: %s hook failed with status %d!\n" %
592 self.ui.warn("abort: %s hook failed with status %d!\n" %
579 (name, r))
593 (name, r))
580 return False
594 return False
581 return True
595 return True
582
596
583 def tags(self):
597 def tags(self):
584 '''return a mapping of tag to node'''
598 '''return a mapping of tag to node'''
585 if not self.tagscache:
599 if not self.tagscache:
586 self.tagscache = {}
600 self.tagscache = {}
587 def addtag(self, k, n):
601 def addtag(self, k, n):
588 try:
602 try:
589 bin_n = bin(n)
603 bin_n = bin(n)
590 except TypeError:
604 except TypeError:
591 bin_n = ''
605 bin_n = ''
592 self.tagscache[k.strip()] = bin_n
606 self.tagscache[k.strip()] = bin_n
593
607
594 try:
608 try:
595 # read each head of the tags file, ending with the tip
609 # read each head of the tags file, ending with the tip
596 # and add each tag found to the map, with "newer" ones
610 # and add each tag found to the map, with "newer" ones
597 # taking precedence
611 # taking precedence
598 fl = self.file(".hgtags")
612 fl = self.file(".hgtags")
599 h = fl.heads()
613 h = fl.heads()
600 h.reverse()
614 h.reverse()
601 for r in h:
615 for r in h:
602 for l in fl.revision(r).splitlines():
616 for l in fl.revision(r).splitlines():
603 if l:
617 if l:
604 n, k = l.split(" ", 1)
618 n, k = l.split(" ", 1)
605 addtag(self, k, n)
619 addtag(self, k, n)
606 except KeyError:
620 except KeyError:
607 pass
621 pass
608
622
609 try:
623 try:
610 f = self.opener("localtags")
624 f = self.opener("localtags")
611 for l in f:
625 for l in f:
612 n, k = l.split(" ", 1)
626 n, k = l.split(" ", 1)
613 addtag(self, k, n)
627 addtag(self, k, n)
614 except IOError:
628 except IOError:
615 pass
629 pass
616
630
617 self.tagscache['tip'] = self.changelog.tip()
631 self.tagscache['tip'] = self.changelog.tip()
618
632
619 return self.tagscache
633 return self.tagscache
620
634
621 def tagslist(self):
635 def tagslist(self):
622 '''return a list of tags ordered by revision'''
636 '''return a list of tags ordered by revision'''
623 l = []
637 l = []
624 for t, n in self.tags().items():
638 for t, n in self.tags().items():
625 try:
639 try:
626 r = self.changelog.rev(n)
640 r = self.changelog.rev(n)
627 except:
641 except:
628 r = -2 # sort to the beginning of the list if unknown
642 r = -2 # sort to the beginning of the list if unknown
629 l.append((r,t,n))
643 l.append((r,t,n))
630 l.sort()
644 l.sort()
631 return [(t,n) for r,t,n in l]
645 return [(t,n) for r,t,n in l]
632
646
633 def nodetags(self, node):
647 def nodetags(self, node):
634 '''return the tags associated with a node'''
648 '''return the tags associated with a node'''
635 if not self.nodetagscache:
649 if not self.nodetagscache:
636 self.nodetagscache = {}
650 self.nodetagscache = {}
637 for t,n in self.tags().items():
651 for t,n in self.tags().items():
638 self.nodetagscache.setdefault(n,[]).append(t)
652 self.nodetagscache.setdefault(n,[]).append(t)
639 return self.nodetagscache.get(node, [])
653 return self.nodetagscache.get(node, [])
640
654
641 def lookup(self, key):
655 def lookup(self, key):
642 try:
656 try:
643 return self.tags()[key]
657 return self.tags()[key]
644 except KeyError:
658 except KeyError:
645 try:
659 try:
646 return self.changelog.lookup(key)
660 return self.changelog.lookup(key)
647 except:
661 except:
648 raise RepoError("unknown revision '%s'" % key)
662 raise RepoError("unknown revision '%s'" % key)
649
663
650 def dev(self):
664 def dev(self):
651 if self.remote: return -1
665 if self.remote: return -1
652 return os.stat(self.path).st_dev
666 return os.stat(self.path).st_dev
653
667
654 def join(self, f):
668 def join(self, f):
655 return os.path.join(self.path, f)
669 return os.path.join(self.path, f)
656
670
657 def wjoin(self, f):
671 def wjoin(self, f):
658 return os.path.join(self.root, f)
672 return os.path.join(self.root, f)
659
673
660 def file(self, f):
674 def file(self, f):
661 if f[0] == '/': f = f[1:]
675 if f[0] == '/': f = f[1:]
662 return filelog(self.opener, f)
676 return filelog(self.opener, f)
663
677
664 def getcwd(self):
678 def getcwd(self):
665 cwd = os.getcwd()
679 cwd = os.getcwd()
666 if cwd == self.root: return ''
680 if cwd == self.root: return ''
667 return cwd[len(self.root) + 1:]
681 return cwd[len(self.root) + 1:]
668
682
669 def wfile(self, f, mode='r'):
683 def wfile(self, f, mode='r'):
670 return self.wopener(f, mode)
684 return self.wopener(f, mode)
671
685
672 def transaction(self):
686 def transaction(self):
673 # save dirstate for undo
687 # save dirstate for undo
674 try:
688 try:
675 ds = self.opener("dirstate").read()
689 ds = self.opener("dirstate").read()
676 except IOError:
690 except IOError:
677 ds = ""
691 ds = ""
678 self.opener("undo.dirstate", "w").write(ds)
692 self.opener("journal.dirstate", "w").write(ds)
679
693
680 return transaction.transaction(self.ui.warn,
694 def after():
681 self.opener, self.join("journal"),
695 util.rename(self.join("journal"), self.join("undo"))
682 self.join("undo"))
696 util.rename(self.join("journal.dirstate"),
697 self.join("undo.dirstate"))
698
699 return transaction.transaction(self.ui.warn, self.opener,
700 self.join("journal"), after)
683
701
684 def recover(self):
702 def recover(self):
685 lock = self.lock()
703 lock = self.lock()
686 if os.path.exists(self.join("journal")):
704 if os.path.exists(self.join("journal")):
687 self.ui.status("rolling back interrupted transaction\n")
705 self.ui.status("rolling back interrupted transaction\n")
688 return transaction.rollback(self.opener, self.join("journal"))
706 return transaction.rollback(self.opener, self.join("journal"))
689 else:
707 else:
690 self.ui.warn("no interrupted transaction available\n")
708 self.ui.warn("no interrupted transaction available\n")
691
709
692 def undo(self):
710 def undo(self):
693 lock = self.lock()
711 lock = self.lock()
694 if os.path.exists(self.join("undo")):
712 if os.path.exists(self.join("undo")):
695 self.ui.status("rolling back last transaction\n")
713 self.ui.status("rolling back last transaction\n")
696 transaction.rollback(self.opener, self.join("undo"))
714 transaction.rollback(self.opener, self.join("undo"))
697 self.dirstate = None
715 self.dirstate = None
698 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
716 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
699 self.dirstate = dirstate(self.opener, self.ui, self.root)
717 self.dirstate = dirstate(self.opener, self.ui, self.root)
700 else:
718 else:
701 self.ui.warn("no undo information available\n")
719 self.ui.warn("no undo information available\n")
702
720
703 def lock(self, wait = 1):
721 def lock(self, wait = 1):
704 try:
722 try:
705 return lock.lock(self.join("lock"), 0)
723 return lock.lock(self.join("lock"), 0)
706 except lock.LockHeld, inst:
724 except lock.LockHeld, inst:
707 if wait:
725 if wait:
708 self.ui.warn("waiting for lock held by %s\n" % inst.args[0])
726 self.ui.warn("waiting for lock held by %s\n" % inst.args[0])
709 return lock.lock(self.join("lock"), wait)
727 return lock.lock(self.join("lock"), wait)
710 raise inst
728 raise inst
711
729
712 def rawcommit(self, files, text, user, date, p1=None, p2=None):
730 def rawcommit(self, files, text, user, date, p1=None, p2=None):
713 orig_parent = self.dirstate.parents()[0] or nullid
731 orig_parent = self.dirstate.parents()[0] or nullid
714 p1 = p1 or self.dirstate.parents()[0] or nullid
732 p1 = p1 or self.dirstate.parents()[0] or nullid
715 p2 = p2 or self.dirstate.parents()[1] or nullid
733 p2 = p2 or self.dirstate.parents()[1] or nullid
716 c1 = self.changelog.read(p1)
734 c1 = self.changelog.read(p1)
717 c2 = self.changelog.read(p2)
735 c2 = self.changelog.read(p2)
718 m1 = self.manifest.read(c1[0])
736 m1 = self.manifest.read(c1[0])
719 mf1 = self.manifest.readflags(c1[0])
737 mf1 = self.manifest.readflags(c1[0])
720 m2 = self.manifest.read(c2[0])
738 m2 = self.manifest.read(c2[0])
721
739
722 if orig_parent == p1:
740 if orig_parent == p1:
723 update_dirstate = 1
741 update_dirstate = 1
724 else:
742 else:
725 update_dirstate = 0
743 update_dirstate = 0
726
744
727 tr = self.transaction()
745 tr = self.transaction()
728 mm = m1.copy()
746 mm = m1.copy()
729 mfm = mf1.copy()
747 mfm = mf1.copy()
730 linkrev = self.changelog.count()
748 linkrev = self.changelog.count()
731 for f in files:
749 for f in files:
732 try:
750 try:
733 t = self.wfile(f).read()
751 t = self.wfile(f).read()
734 tm = util.is_exec(self.wjoin(f), mfm.get(f, False))
752 tm = util.is_exec(self.wjoin(f), mfm.get(f, False))
735 r = self.file(f)
753 r = self.file(f)
736 mfm[f] = tm
754 mfm[f] = tm
737 mm[f] = r.add(t, {}, tr, linkrev,
755 mm[f] = r.add(t, {}, tr, linkrev,
738 m1.get(f, nullid), m2.get(f, nullid))
756 m1.get(f, nullid), m2.get(f, nullid))
739 if update_dirstate:
757 if update_dirstate:
740 self.dirstate.update([f], "n")
758 self.dirstate.update([f], "n")
741 except IOError:
759 except IOError:
742 try:
760 try:
743 del mm[f]
761 del mm[f]
744 del mfm[f]
762 del mfm[f]
745 if update_dirstate:
763 if update_dirstate:
746 self.dirstate.forget([f])
764 self.dirstate.forget([f])
747 except:
765 except:
748 # deleted from p2?
766 # deleted from p2?
749 pass
767 pass
750
768
751 mnode = self.manifest.add(mm, mfm, tr, linkrev, c1[0], c2[0])
769 mnode = self.manifest.add(mm, mfm, tr, linkrev, c1[0], c2[0])
752 user = user or self.ui.username()
770 user = user or self.ui.username()
753 n = self.changelog.add(mnode, files, text, tr, p1, p2, user, date)
771 n = self.changelog.add(mnode, files, text, tr, p1, p2, user, date)
754 tr.close()
772 tr.close()
755 if update_dirstate:
773 if update_dirstate:
756 self.dirstate.setparents(n, nullid)
774 self.dirstate.setparents(n, nullid)
757
775
758 def commit(self, files = None, text = "", user = None, date = None):
776 def commit(self, files = None, text = "", user = None, date = None):
759 commit = []
777 commit = []
760 remove = []
778 remove = []
761 if files:
779 if files:
762 for f in files:
780 for f in files:
763 s = self.dirstate.state(f)
781 s = self.dirstate.state(f)
764 if s in 'nmai':
782 if s in 'nmai':
765 commit.append(f)
783 commit.append(f)
766 elif s == 'r':
784 elif s == 'r':
767 remove.append(f)
785 remove.append(f)
768 else:
786 else:
769 self.ui.warn("%s not tracked!\n" % f)
787 self.ui.warn("%s not tracked!\n" % f)
770 else:
788 else:
771 (c, a, d, u) = self.changes()
789 (c, a, d, u) = self.changes()
772 commit = c + a
790 commit = c + a
773 remove = d
791 remove = d
774
792
775 if not commit and not remove:
793 if not commit and not remove:
776 self.ui.status("nothing changed\n")
794 self.ui.status("nothing changed\n")
777 return
795 return
778
796
779 if not self.hook("precommit"):
797 if not self.hook("precommit"):
780 return 1
798 return 1
781
799
782 p1, p2 = self.dirstate.parents()
800 p1, p2 = self.dirstate.parents()
783 c1 = self.changelog.read(p1)
801 c1 = self.changelog.read(p1)
784 c2 = self.changelog.read(p2)
802 c2 = self.changelog.read(p2)
785 m1 = self.manifest.read(c1[0])
803 m1 = self.manifest.read(c1[0])
786 mf1 = self.manifest.readflags(c1[0])
804 mf1 = self.manifest.readflags(c1[0])
787 m2 = self.manifest.read(c2[0])
805 m2 = self.manifest.read(c2[0])
788 lock = self.lock()
806 lock = self.lock()
789 tr = self.transaction()
807 tr = self.transaction()
790
808
791 # check in files
809 # check in files
792 new = {}
810 new = {}
793 linkrev = self.changelog.count()
811 linkrev = self.changelog.count()
794 commit.sort()
812 commit.sort()
795 for f in commit:
813 for f in commit:
796 self.ui.note(f + "\n")
814 self.ui.note(f + "\n")
797 try:
815 try:
798 mf1[f] = util.is_exec(self.wjoin(f), mf1.get(f, False))
816 mf1[f] = util.is_exec(self.wjoin(f), mf1.get(f, False))
799 t = self.wfile(f).read()
817 t = self.wfile(f).read()
800 except IOError:
818 except IOError:
801 self.ui.warn("trouble committing %s!\n" % f)
819 self.ui.warn("trouble committing %s!\n" % f)
802 raise
820 raise
803
821
804 meta = {}
822 meta = {}
805 cp = self.dirstate.copied(f)
823 cp = self.dirstate.copied(f)
806 if cp:
824 if cp:
807 meta["copy"] = cp
825 meta["copy"] = cp
808 meta["copyrev"] = hex(m1.get(cp, m2.get(cp, nullid)))
826 meta["copyrev"] = hex(m1.get(cp, m2.get(cp, nullid)))
809 self.ui.debug(" %s: copy %s:%s\n" % (f, cp, meta["copyrev"]))
827 self.ui.debug(" %s: copy %s:%s\n" % (f, cp, meta["copyrev"]))
810
828
811 r = self.file(f)
829 r = self.file(f)
812 fp1 = m1.get(f, nullid)
830 fp1 = m1.get(f, nullid)
813 fp2 = m2.get(f, nullid)
831 fp2 = m2.get(f, nullid)
814 new[f] = r.add(t, meta, tr, linkrev, fp1, fp2)
832 new[f] = r.add(t, meta, tr, linkrev, fp1, fp2)
815
833
816 # update manifest
834 # update manifest
817 m1.update(new)
835 m1.update(new)
818 for f in remove:
836 for f in remove:
819 if f in m1:
837 if f in m1:
820 del m1[f]
838 del m1[f]
821 mn = self.manifest.add(m1, mf1, tr, linkrev, c1[0], c2[0],
839 mn = self.manifest.add(m1, mf1, tr, linkrev, c1[0], c2[0],
822 (new, remove))
840 (new, remove))
823
841
824 # add changeset
842 # add changeset
825 new = new.keys()
843 new = new.keys()
826 new.sort()
844 new.sort()
827
845
828 if not text:
846 if not text:
829 edittext = "\n" + "HG: manifest hash %s\n" % hex(mn)
847 edittext = "\n" + "HG: manifest hash %s\n" % hex(mn)
830 edittext += "".join(["HG: changed %s\n" % f for f in new])
848 edittext += "".join(["HG: changed %s\n" % f for f in new])
831 edittext += "".join(["HG: removed %s\n" % f for f in remove])
849 edittext += "".join(["HG: removed %s\n" % f for f in remove])
832 edittext = self.ui.edit(edittext)
850 edittext = self.ui.edit(edittext)
833 if not edittext.rstrip():
851 if not edittext.rstrip():
834 return 1
852 return 1
835 text = edittext
853 text = edittext
836
854
837 user = user or self.ui.username()
855 user = user or self.ui.username()
838 n = self.changelog.add(mn, new, text, tr, p1, p2, user, date)
856 n = self.changelog.add(mn, new, text, tr, p1, p2, user, date)
839
857
840 tr.close()
858 tr.close()
841
859
842 self.dirstate.setparents(n)
860 self.dirstate.setparents(n)
843 self.dirstate.update(new, "n")
861 self.dirstate.update(new, "n")
844 self.dirstate.forget(remove)
862 self.dirstate.forget(remove)
845
863
846 if not self.hook("commit", node=hex(n)):
864 if not self.hook("commit", node=hex(n)):
847 return 1
865 return 1
848
866
849 def walk(self, node = None, files = [], match = util.always):
867 def walk(self, node = None, files = [], match = util.always):
850 if node:
868 if node:
851 for fn in self.manifest.read(self.changelog.read(node)[0]):
869 for fn in self.manifest.read(self.changelog.read(node)[0]):
852 yield 'm', fn
870 yield 'm', fn
853 else:
871 else:
854 for src, fn in self.dirstate.walk(files, match):
872 for src, fn in self.dirstate.walk(files, match):
855 yield src, fn
873 yield src, fn
856
874
857 def changes(self, node1 = None, node2 = None, files = [],
875 def changes(self, node1 = None, node2 = None, files = [],
858 match = util.always):
876 match = util.always):
859 mf2, u = None, []
877 mf2, u = None, []
860
878
861 def fcmp(fn, mf):
879 def fcmp(fn, mf):
862 t1 = self.wfile(fn).read()
880 t1 = self.wfile(fn).read()
863 t2 = self.file(fn).revision(mf[fn])
881 t2 = self.file(fn).revision(mf[fn])
864 return cmp(t1, t2)
882 return cmp(t1, t2)
865
883
866 def mfmatches(node):
884 def mfmatches(node):
867 mf = dict(self.manifest.read(node))
885 mf = dict(self.manifest.read(node))
868 for fn in mf.keys():
886 for fn in mf.keys():
869 if not match(fn):
887 if not match(fn):
870 del mf[fn]
888 del mf[fn]
871 return mf
889 return mf
872
890
873 # are we comparing the working directory?
891 # are we comparing the working directory?
874 if not node2:
892 if not node2:
875 l, c, a, d, u = self.dirstate.changes(files, match)
893 l, c, a, d, u = self.dirstate.changes(files, match)
876
894
877 # are we comparing working dir against its parent?
895 # are we comparing working dir against its parent?
878 if not node1:
896 if not node1:
879 if l:
897 if l:
880 # do a full compare of any files that might have changed
898 # do a full compare of any files that might have changed
881 change = self.changelog.read(self.dirstate.parents()[0])
899 change = self.changelog.read(self.dirstate.parents()[0])
882 mf2 = mfmatches(change[0])
900 mf2 = mfmatches(change[0])
883 for f in l:
901 for f in l:
884 if fcmp(f, mf2):
902 if fcmp(f, mf2):
885 c.append(f)
903 c.append(f)
886
904
887 for l in c, a, d, u:
905 for l in c, a, d, u:
888 l.sort()
906 l.sort()
889
907
890 return (c, a, d, u)
908 return (c, a, d, u)
891
909
892 # are we comparing working dir against non-tip?
910 # are we comparing working dir against non-tip?
893 # generate a pseudo-manifest for the working dir
911 # generate a pseudo-manifest for the working dir
894 if not node2:
912 if not node2:
895 if not mf2:
913 if not mf2:
896 change = self.changelog.read(self.dirstate.parents()[0])
914 change = self.changelog.read(self.dirstate.parents()[0])
897 mf2 = mfmatches(change[0])
915 mf2 = mfmatches(change[0])
898 for f in a + c + l:
916 for f in a + c + l:
899 mf2[f] = ""
917 mf2[f] = ""
900 for f in d:
918 for f in d:
901 if f in mf2: del mf2[f]
919 if f in mf2: del mf2[f]
902 else:
920 else:
903 change = self.changelog.read(node2)
921 change = self.changelog.read(node2)
904 mf2 = mfmatches(change[0])
922 mf2 = mfmatches(change[0])
905
923
906 # flush lists from dirstate before comparing manifests
924 # flush lists from dirstate before comparing manifests
907 c, a = [], []
925 c, a = [], []
908
926
909 change = self.changelog.read(node1)
927 change = self.changelog.read(node1)
910 mf1 = mfmatches(change[0])
928 mf1 = mfmatches(change[0])
911
929
912 for fn in mf2:
930 for fn in mf2:
913 if mf1.has_key(fn):
931 if mf1.has_key(fn):
914 if mf1[fn] != mf2[fn]:
932 if mf1[fn] != mf2[fn]:
915 if mf2[fn] != "" or fcmp(fn, mf1):
933 if mf2[fn] != "" or fcmp(fn, mf1):
916 c.append(fn)
934 c.append(fn)
917 del mf1[fn]
935 del mf1[fn]
918 else:
936 else:
919 a.append(fn)
937 a.append(fn)
920
938
921 d = mf1.keys()
939 d = mf1.keys()
922
940
923 for l in c, a, d, u:
941 for l in c, a, d, u:
924 l.sort()
942 l.sort()
925
943
926 return (c, a, d, u)
944 return (c, a, d, u)
927
945
928 def add(self, list):
946 def add(self, list):
929 for f in list:
947 for f in list:
930 p = self.wjoin(f)
948 p = self.wjoin(f)
931 if not os.path.exists(p):
949 if not os.path.exists(p):
932 self.ui.warn("%s does not exist!\n" % f)
950 self.ui.warn("%s does not exist!\n" % f)
933 elif not os.path.isfile(p):
951 elif not os.path.isfile(p):
934 self.ui.warn("%s not added: only files supported currently\n" % f)
952 self.ui.warn("%s not added: only files supported currently\n" % f)
935 elif self.dirstate.state(f) in 'an':
953 elif self.dirstate.state(f) in 'an':
936 self.ui.warn("%s already tracked!\n" % f)
954 self.ui.warn("%s already tracked!\n" % f)
937 else:
955 else:
938 self.dirstate.update([f], "a")
956 self.dirstate.update([f], "a")
939
957
940 def forget(self, list):
958 def forget(self, list):
941 for f in list:
959 for f in list:
942 if self.dirstate.state(f) not in 'ai':
960 if self.dirstate.state(f) not in 'ai':
943 self.ui.warn("%s not added!\n" % f)
961 self.ui.warn("%s not added!\n" % f)
944 else:
962 else:
945 self.dirstate.forget([f])
963 self.dirstate.forget([f])
946
964
947 def remove(self, list):
965 def remove(self, list):
948 for f in list:
966 for f in list:
949 p = self.wjoin(f)
967 p = self.wjoin(f)
950 if os.path.exists(p):
968 if os.path.exists(p):
951 self.ui.warn("%s still exists!\n" % f)
969 self.ui.warn("%s still exists!\n" % f)
952 elif self.dirstate.state(f) == 'a':
970 elif self.dirstate.state(f) == 'a':
953 self.ui.warn("%s never committed!\n" % f)
971 self.ui.warn("%s never committed!\n" % f)
954 self.dirstate.forget([f])
972 self.dirstate.forget([f])
955 elif f not in self.dirstate:
973 elif f not in self.dirstate:
956 self.ui.warn("%s not tracked!\n" % f)
974 self.ui.warn("%s not tracked!\n" % f)
957 else:
975 else:
958 self.dirstate.update([f], "r")
976 self.dirstate.update([f], "r")
959
977
960 def copy(self, source, dest):
978 def copy(self, source, dest):
961 p = self.wjoin(dest)
979 p = self.wjoin(dest)
962 if not os.path.exists(dest):
980 if not os.path.exists(p):
963 self.ui.warn("%s does not exist!\n" % dest)
981 self.ui.warn("%s does not exist!\n" % dest)
964 elif not os.path.isfile(dest):
982 elif not os.path.isfile(p):
965 self.ui.warn("copy failed: %s is not a file\n" % dest)
983 self.ui.warn("copy failed: %s is not a file\n" % dest)
966 else:
984 else:
967 if self.dirstate.state(dest) == '?':
985 if self.dirstate.state(dest) == '?':
968 self.dirstate.update([dest], "a")
986 self.dirstate.update([dest], "a")
969 self.dirstate.copy(source, dest)
987 self.dirstate.copy(source, dest)
970
988
971 def heads(self):
989 def heads(self):
972 return self.changelog.heads()
990 return self.changelog.heads()
973
991
974 def branches(self, nodes):
992 def branches(self, nodes):
975 if not nodes: nodes = [self.changelog.tip()]
993 if not nodes: nodes = [self.changelog.tip()]
976 b = []
994 b = []
977 for n in nodes:
995 for n in nodes:
978 t = n
996 t = n
979 while n:
997 while n:
980 p = self.changelog.parents(n)
998 p = self.changelog.parents(n)
981 if p[1] != nullid or p[0] == nullid:
999 if p[1] != nullid or p[0] == nullid:
982 b.append((t, n, p[0], p[1]))
1000 b.append((t, n, p[0], p[1]))
983 break
1001 break
984 n = p[0]
1002 n = p[0]
985 return b
1003 return b
986
1004
987 def between(self, pairs):
1005 def between(self, pairs):
988 r = []
1006 r = []
989
1007
990 for top, bottom in pairs:
1008 for top, bottom in pairs:
991 n, l, i = top, [], 0
1009 n, l, i = top, [], 0
992 f = 1
1010 f = 1
993
1011
994 while n != bottom:
1012 while n != bottom:
995 p = self.changelog.parents(n)[0]
1013 p = self.changelog.parents(n)[0]
996 if i == f:
1014 if i == f:
997 l.append(n)
1015 l.append(n)
998 f = f * 2
1016 f = f * 2
999 n = p
1017 n = p
1000 i += 1
1018 i += 1
1001
1019
1002 r.append(l)
1020 r.append(l)
1003
1021
1004 return r
1022 return r
1005
1023
1006 def newer(self, nodes):
1024 def newer(self, nodes):
1007 m = {}
1025 m = {}
1008 nl = []
1026 nl = []
1009 pm = {}
1027 pm = {}
1010 cl = self.changelog
1028 cl = self.changelog
1011 t = l = cl.count()
1029 t = l = cl.count()
1012
1030
1013 # find the lowest numbered node
1031 # find the lowest numbered node
1014 for n in nodes:
1032 for n in nodes:
1015 l = min(l, cl.rev(n))
1033 l = min(l, cl.rev(n))
1016 m[n] = 1
1034 m[n] = 1
1017
1035
1018 for i in xrange(l, t):
1036 for i in xrange(l, t):
1019 n = cl.node(i)
1037 n = cl.node(i)
1020 if n in m: # explicitly listed
1038 if n in m: # explicitly listed
1021 pm[n] = 1
1039 pm[n] = 1
1022 nl.append(n)
1040 nl.append(n)
1023 continue
1041 continue
1024 for p in cl.parents(n):
1042 for p in cl.parents(n):
1025 if p in pm: # parent listed
1043 if p in pm: # parent listed
1026 pm[n] = 1
1044 pm[n] = 1
1027 nl.append(n)
1045 nl.append(n)
1028 break
1046 break
1029
1047
1030 return nl
1048 return nl
1031
1049
1032 def findincoming(self, remote, base={}):
1050 def findincoming(self, remote, base={}):
1033 m = self.changelog.nodemap
1051 m = self.changelog.nodemap
1034 search = []
1052 search = []
1035 fetch = []
1053 fetch = []
1036 seen = {}
1054 seen = {}
1037 seenbranch = {}
1055 seenbranch = {}
1038
1056
1039 # assume we're closer to the tip than the root
1057 # assume we're closer to the tip than the root
1040 # and start by examining the heads
1058 # and start by examining the heads
1041 self.ui.status("searching for changes\n")
1059 self.ui.status("searching for changes\n")
1042 heads = remote.heads()
1060 heads = remote.heads()
1043 unknown = []
1061 unknown = []
1044 for h in heads:
1062 for h in heads:
1045 if h not in m:
1063 if h not in m:
1046 unknown.append(h)
1064 unknown.append(h)
1047 else:
1065 else:
1048 base[h] = 1
1066 base[h] = 1
1049
1067
1050 if not unknown:
1068 if not unknown:
1051 return None
1069 return None
1052
1070
1053 rep = {}
1071 rep = {}
1054 reqcnt = 0
1072 reqcnt = 0
1055
1073
1056 # search through remote branches
1074 # search through remote branches
1057 # a 'branch' here is a linear segment of history, with four parts:
1075 # a 'branch' here is a linear segment of history, with four parts:
1058 # head, root, first parent, second parent
1076 # head, root, first parent, second parent
1059 # (a branch always has two parents (or none) by definition)
1077 # (a branch always has two parents (or none) by definition)
1060 unknown = remote.branches(unknown)
1078 unknown = remote.branches(unknown)
1061 while unknown:
1079 while unknown:
1062 r = []
1080 r = []
1063 while unknown:
1081 while unknown:
1064 n = unknown.pop(0)
1082 n = unknown.pop(0)
1065 if n[0] in seen:
1083 if n[0] in seen:
1066 continue
1084 continue
1067
1085
1068 self.ui.debug("examining %s:%s\n" % (short(n[0]), short(n[1])))
1086 self.ui.debug("examining %s:%s\n" % (short(n[0]), short(n[1])))
1069 if n[0] == nullid:
1087 if n[0] == nullid:
1070 break
1088 break
1071 if n in seenbranch:
1089 if n in seenbranch:
1072 self.ui.debug("branch already found\n")
1090 self.ui.debug("branch already found\n")
1073 continue
1091 continue
1074 if n[1] and n[1] in m: # do we know the base?
1092 if n[1] and n[1] in m: # do we know the base?
1075 self.ui.debug("found incomplete branch %s:%s\n"
1093 self.ui.debug("found incomplete branch %s:%s\n"
1076 % (short(n[0]), short(n[1])))
1094 % (short(n[0]), short(n[1])))
1077 search.append(n) # schedule branch range for scanning
1095 search.append(n) # schedule branch range for scanning
1078 seenbranch[n] = 1
1096 seenbranch[n] = 1
1079 else:
1097 else:
1080 if n[1] not in seen and n[1] not in fetch:
1098 if n[1] not in seen and n[1] not in fetch:
1081 if n[2] in m and n[3] in m:
1099 if n[2] in m and n[3] in m:
1082 self.ui.debug("found new changeset %s\n" %
1100 self.ui.debug("found new changeset %s\n" %
1083 short(n[1]))
1101 short(n[1]))
1084 fetch.append(n[1]) # earliest unknown
1102 fetch.append(n[1]) # earliest unknown
1085 base[n[2]] = 1 # latest known
1103 base[n[2]] = 1 # latest known
1086 continue
1104 continue
1087
1105
1088 for a in n[2:4]:
1106 for a in n[2:4]:
1089 if a not in rep:
1107 if a not in rep:
1090 r.append(a)
1108 r.append(a)
1091 rep[a] = 1
1109 rep[a] = 1
1092
1110
1093 seen[n[0]] = 1
1111 seen[n[0]] = 1
1094
1112
1095 if r:
1113 if r:
1096 reqcnt += 1
1114 reqcnt += 1
1097 self.ui.debug("request %d: %s\n" %
1115 self.ui.debug("request %d: %s\n" %
1098 (reqcnt, " ".join(map(short, r))))
1116 (reqcnt, " ".join(map(short, r))))
1099 for p in range(0, len(r), 10):
1117 for p in range(0, len(r), 10):
1100 for b in remote.branches(r[p:p+10]):
1118 for b in remote.branches(r[p:p+10]):
1101 self.ui.debug("received %s:%s\n" %
1119 self.ui.debug("received %s:%s\n" %
1102 (short(b[0]), short(b[1])))
1120 (short(b[0]), short(b[1])))
1103 if b[0] not in m and b[0] not in seen:
1121 if b[0] not in m and b[0] not in seen:
1104 unknown.append(b)
1122 unknown.append(b)
1105
1123
1106 # do binary search on the branches we found
1124 # do binary search on the branches we found
1107 while search:
1125 while search:
1108 n = search.pop(0)
1126 n = search.pop(0)
1109 reqcnt += 1
1127 reqcnt += 1
1110 l = remote.between([(n[0], n[1])])[0]
1128 l = remote.between([(n[0], n[1])])[0]
1111 l.append(n[1])
1129 l.append(n[1])
1112 p = n[0]
1130 p = n[0]
1113 f = 1
1131 f = 1
1114 for i in l:
1132 for i in l:
1115 self.ui.debug("narrowing %d:%d %s\n" % (f, len(l), short(i)))
1133 self.ui.debug("narrowing %d:%d %s\n" % (f, len(l), short(i)))
1116 if i in m:
1134 if i in m:
1117 if f <= 2:
1135 if f <= 2:
1118 self.ui.debug("found new branch changeset %s\n" %
1136 self.ui.debug("found new branch changeset %s\n" %
1119 short(p))
1137 short(p))
1120 fetch.append(p)
1138 fetch.append(p)
1121 base[i] = 1
1139 base[i] = 1
1122 else:
1140 else:
1123 self.ui.debug("narrowed branch search to %s:%s\n"
1141 self.ui.debug("narrowed branch search to %s:%s\n"
1124 % (short(p), short(i)))
1142 % (short(p), short(i)))
1125 search.append((p, i))
1143 search.append((p, i))
1126 break
1144 break
1127 p, f = i, f * 2
1145 p, f = i, f * 2
1128
1146
1129 # sanity check our fetch list
1147 # sanity check our fetch list
1130 for f in fetch:
1148 for f in fetch:
1131 if f in m:
1149 if f in m:
1132 raise RepoError("already have changeset " + short(f[:4]))
1150 raise RepoError("already have changeset " + short(f[:4]))
1133
1151
1134 if base.keys() == [nullid]:
1152 if base.keys() == [nullid]:
1135 self.ui.warn("warning: pulling from an unrelated repository!\n")
1153 self.ui.warn("warning: pulling from an unrelated repository!\n")
1136
1154
1137 self.ui.note("adding new changesets starting at " +
1155 self.ui.note("adding new changesets starting at " +
1138 " ".join([short(f) for f in fetch]) + "\n")
1156 " ".join([short(f) for f in fetch]) + "\n")
1139
1157
1140 self.ui.debug("%d total queries\n" % reqcnt)
1158 self.ui.debug("%d total queries\n" % reqcnt)
1141
1159
1142 return fetch
1160 return fetch
1143
1161
1144 def findoutgoing(self, remote):
1162 def findoutgoing(self, remote):
1145 base = {}
1163 base = {}
1146 self.findincoming(remote, base)
1164 self.findincoming(remote, base)
1147 remain = dict.fromkeys(self.changelog.nodemap)
1165 remain = dict.fromkeys(self.changelog.nodemap)
1148
1166
1149 # prune everything remote has from the tree
1167 # prune everything remote has from the tree
1150 del remain[nullid]
1168 del remain[nullid]
1151 remove = base.keys()
1169 remove = base.keys()
1152 while remove:
1170 while remove:
1153 n = remove.pop(0)
1171 n = remove.pop(0)
1154 if n in remain:
1172 if n in remain:
1155 del remain[n]
1173 del remain[n]
1156 for p in self.changelog.parents(n):
1174 for p in self.changelog.parents(n):
1157 remove.append(p)
1175 remove.append(p)
1158
1176
1159 # find every node whose parents have been pruned
1177 # find every node whose parents have been pruned
1160 subset = []
1178 subset = []
1161 for n in remain:
1179 for n in remain:
1162 p1, p2 = self.changelog.parents(n)
1180 p1, p2 = self.changelog.parents(n)
1163 if p1 not in remain and p2 not in remain:
1181 if p1 not in remain and p2 not in remain:
1164 subset.append(n)
1182 subset.append(n)
1165
1183
1166 # this is the set of all roots we have to push
1184 # this is the set of all roots we have to push
1167 return subset
1185 return subset
1168
1186
1169 def pull(self, remote):
1187 def pull(self, remote):
1170 lock = self.lock()
1188 lock = self.lock()
1171
1189
1172 # if we have an empty repo, fetch everything
1190 # if we have an empty repo, fetch everything
1173 if self.changelog.tip() == nullid:
1191 if self.changelog.tip() == nullid:
1174 self.ui.status("requesting all changes\n")
1192 self.ui.status("requesting all changes\n")
1175 fetch = [nullid]
1193 fetch = [nullid]
1176 else:
1194 else:
1177 fetch = self.findincoming(remote)
1195 fetch = self.findincoming(remote)
1178
1196
1179 if not fetch:
1197 if not fetch:
1180 self.ui.status("no changes found\n")
1198 self.ui.status("no changes found\n")
1181 return 1
1199 return 1
1182
1200
1183 cg = remote.changegroup(fetch)
1201 cg = remote.changegroup(fetch)
1184 return self.addchangegroup(cg)
1202 return self.addchangegroup(cg)
1185
1203
1186 def push(self, remote):
1204 def push(self, remote):
1187 lock = remote.lock()
1205 lock = remote.lock()
1188 update = self.findoutgoing(remote)
1206 update = self.findoutgoing(remote)
1189 if not update:
1207 if not update:
1190 self.ui.status("no changes found\n")
1208 self.ui.status("no changes found\n")
1191 return 1
1209 return 1
1192
1210
1193 cg = self.changegroup(update)
1211 cg = self.changegroup(update)
1194 return remote.addchangegroup(cg)
1212 return remote.addchangegroup(cg)
1195
1213
1196 def changegroup(self, basenodes):
1214 def changegroup(self, basenodes):
1197 class genread:
1215 class genread:
1198 def __init__(self, generator):
1216 def __init__(self, generator):
1199 self.g = generator
1217 self.g = generator
1200 self.buf = ""
1218 self.buf = ""
1201 def read(self, l):
1219 def read(self, l):
1202 while l > len(self.buf):
1220 while l > len(self.buf):
1203 try:
1221 try:
1204 self.buf += self.g.next()
1222 self.buf += self.g.next()
1205 except StopIteration:
1223 except StopIteration:
1206 break
1224 break
1207 d, self.buf = self.buf[:l], self.buf[l:]
1225 d, self.buf = self.buf[:l], self.buf[l:]
1208 return d
1226 return d
1209
1227
1210 def gengroup():
1228 def gengroup():
1211 nodes = self.newer(basenodes)
1229 nodes = self.newer(basenodes)
1212
1230
1213 # construct the link map
1231 # construct the link map
1214 linkmap = {}
1232 linkmap = {}
1215 for n in nodes:
1233 for n in nodes:
1216 linkmap[self.changelog.rev(n)] = n
1234 linkmap[self.changelog.rev(n)] = n
1217
1235
1218 # construct a list of all changed files
1236 # construct a list of all changed files
1219 changed = {}
1237 changed = {}
1220 for n in nodes:
1238 for n in nodes:
1221 c = self.changelog.read(n)
1239 c = self.changelog.read(n)
1222 for f in c[3]:
1240 for f in c[3]:
1223 changed[f] = 1
1241 changed[f] = 1
1224 changed = changed.keys()
1242 changed = changed.keys()
1225 changed.sort()
1243 changed.sort()
1226
1244
1227 # the changegroup is changesets + manifests + all file revs
1245 # the changegroup is changesets + manifests + all file revs
1228 revs = [ self.changelog.rev(n) for n in nodes ]
1246 revs = [ self.changelog.rev(n) for n in nodes ]
1229
1247
1230 for y in self.changelog.group(linkmap): yield y
1248 for y in self.changelog.group(linkmap): yield y
1231 for y in self.manifest.group(linkmap): yield y
1249 for y in self.manifest.group(linkmap): yield y
1232 for f in changed:
1250 for f in changed:
1233 yield struct.pack(">l", len(f) + 4) + f
1251 yield struct.pack(">l", len(f) + 4) + f
1234 g = self.file(f).group(linkmap)
1252 g = self.file(f).group(linkmap)
1235 for y in g:
1253 for y in g:
1236 yield y
1254 yield y
1237
1255
1238 yield struct.pack(">l", 0)
1256 yield struct.pack(">l", 0)
1239
1257
1240 return genread(gengroup())
1258 return genread(gengroup())
1241
1259
1242 def addchangegroup(self, source):
1260 def addchangegroup(self, source):
1243
1261
1244 def getchunk():
1262 def getchunk():
1245 d = source.read(4)
1263 d = source.read(4)
1246 if not d: return ""
1264 if not d: return ""
1247 l = struct.unpack(">l", d)[0]
1265 l = struct.unpack(">l", d)[0]
1248 if l <= 4: return ""
1266 if l <= 4: return ""
1249 return source.read(l - 4)
1267 return source.read(l - 4)
1250
1268
1251 def getgroup():
1269 def getgroup():
1252 while 1:
1270 while 1:
1253 c = getchunk()
1271 c = getchunk()
1254 if not c: break
1272 if not c: break
1255 yield c
1273 yield c
1256
1274
1257 def csmap(x):
1275 def csmap(x):
1258 self.ui.debug("add changeset %s\n" % short(x))
1276 self.ui.debug("add changeset %s\n" % short(x))
1259 return self.changelog.count()
1277 return self.changelog.count()
1260
1278
1261 def revmap(x):
1279 def revmap(x):
1262 return self.changelog.rev(x)
1280 return self.changelog.rev(x)
1263
1281
1264 if not source: return
1282 if not source: return
1265 changesets = files = revisions = 0
1283 changesets = files = revisions = 0
1266
1284
1267 tr = self.transaction()
1285 tr = self.transaction()
1268
1286
1269 # pull off the changeset group
1287 # pull off the changeset group
1270 self.ui.status("adding changesets\n")
1288 self.ui.status("adding changesets\n")
1271 co = self.changelog.tip()
1289 co = self.changelog.tip()
1272 cn = self.changelog.addgroup(getgroup(), csmap, tr, 1) # unique
1290 cn = self.changelog.addgroup(getgroup(), csmap, tr, 1) # unique
1273 changesets = self.changelog.rev(cn) - self.changelog.rev(co)
1291 changesets = self.changelog.rev(cn) - self.changelog.rev(co)
1274
1292
1275 # pull off the manifest group
1293 # pull off the manifest group
1276 self.ui.status("adding manifests\n")
1294 self.ui.status("adding manifests\n")
1277 mm = self.manifest.tip()
1295 mm = self.manifest.tip()
1278 mo = self.manifest.addgroup(getgroup(), revmap, tr)
1296 mo = self.manifest.addgroup(getgroup(), revmap, tr)
1279
1297
1280 # process the files
1298 # process the files
1281 self.ui.status("adding file revisions\n")
1299 self.ui.status("adding file changes\n")
1282 while 1:
1300 while 1:
1283 f = getchunk()
1301 f = getchunk()
1284 if not f: break
1302 if not f: break
1285 self.ui.debug("adding %s revisions\n" % f)
1303 self.ui.debug("adding %s revisions\n" % f)
1286 fl = self.file(f)
1304 fl = self.file(f)
1287 o = fl.count()
1305 o = fl.count()
1288 n = fl.addgroup(getgroup(), revmap, tr)
1306 n = fl.addgroup(getgroup(), revmap, tr)
1289 revisions += fl.count() - o
1307 revisions += fl.count() - o
1290 files += 1
1308 files += 1
1291
1309
1292 self.ui.status(("modified %d files, added %d changesets" +
1310 self.ui.status(("added %d changesets" +
1293 " and %d new revisions\n")
1311 " with %d changes to %d files\n")
1294 % (files, changesets, revisions))
1312 % (changesets, revisions, files))
1295
1313
1296 tr.close()
1314 tr.close()
1315
1316 if not self.hook("changegroup"):
1317 return 1
1318
1297 return
1319 return
1298
1320
1299 def update(self, node, allow=False, force=False, choose=None,
1321 def update(self, node, allow=False, force=False, choose=None,
1300 moddirstate=True):
1322 moddirstate=True):
1301 pl = self.dirstate.parents()
1323 pl = self.dirstate.parents()
1302 if not force and pl[1] != nullid:
1324 if not force and pl[1] != nullid:
1303 self.ui.warn("aborting: outstanding uncommitted merges\n")
1325 self.ui.warn("aborting: outstanding uncommitted merges\n")
1304 return 1
1326 return 1
1305
1327
1306 p1, p2 = pl[0], node
1328 p1, p2 = pl[0], node
1307 pa = self.changelog.ancestor(p1, p2)
1329 pa = self.changelog.ancestor(p1, p2)
1308 m1n = self.changelog.read(p1)[0]
1330 m1n = self.changelog.read(p1)[0]
1309 m2n = self.changelog.read(p2)[0]
1331 m2n = self.changelog.read(p2)[0]
1310 man = self.manifest.ancestor(m1n, m2n)
1332 man = self.manifest.ancestor(m1n, m2n)
1311 m1 = self.manifest.read(m1n)
1333 m1 = self.manifest.read(m1n)
1312 mf1 = self.manifest.readflags(m1n)
1334 mf1 = self.manifest.readflags(m1n)
1313 m2 = self.manifest.read(m2n)
1335 m2 = self.manifest.read(m2n)
1314 mf2 = self.manifest.readflags(m2n)
1336 mf2 = self.manifest.readflags(m2n)
1315 ma = self.manifest.read(man)
1337 ma = self.manifest.read(man)
1316 mfa = self.manifest.readflags(man)
1338 mfa = self.manifest.readflags(man)
1317
1339
1318 (c, a, d, u) = self.changes()
1340 (c, a, d, u) = self.changes()
1319
1341
1320 # is this a jump, or a merge? i.e. is there a linear path
1342 # is this a jump, or a merge? i.e. is there a linear path
1321 # from p1 to p2?
1343 # from p1 to p2?
1322 linear_path = (pa == p1 or pa == p2)
1344 linear_path = (pa == p1 or pa == p2)
1323
1345
1324 # resolve the manifest to determine which files
1346 # resolve the manifest to determine which files
1325 # we care about merging
1347 # we care about merging
1326 self.ui.note("resolving manifests\n")
1348 self.ui.note("resolving manifests\n")
1327 self.ui.debug(" force %s allow %s moddirstate %s linear %s\n" %
1349 self.ui.debug(" force %s allow %s moddirstate %s linear %s\n" %
1328 (force, allow, moddirstate, linear_path))
1350 (force, allow, moddirstate, linear_path))
1329 self.ui.debug(" ancestor %s local %s remote %s\n" %
1351 self.ui.debug(" ancestor %s local %s remote %s\n" %
1330 (short(man), short(m1n), short(m2n)))
1352 (short(man), short(m1n), short(m2n)))
1331
1353
1332 merge = {}
1354 merge = {}
1333 get = {}
1355 get = {}
1334 remove = []
1356 remove = []
1335 mark = {}
1357 mark = {}
1336
1358
1337 # construct a working dir manifest
1359 # construct a working dir manifest
1338 mw = m1.copy()
1360 mw = m1.copy()
1339 mfw = mf1.copy()
1361 mfw = mf1.copy()
1340 umap = dict.fromkeys(u)
1362 umap = dict.fromkeys(u)
1341
1363
1342 for f in a + c + u:
1364 for f in a + c + u:
1343 mw[f] = ""
1365 mw[f] = ""
1344 mfw[f] = util.is_exec(self.wjoin(f), mfw.get(f, False))
1366 mfw[f] = util.is_exec(self.wjoin(f), mfw.get(f, False))
1345
1367
1346 for f in d:
1368 for f in d:
1347 if f in mw: del mw[f]
1369 if f in mw: del mw[f]
1348
1370
1349 # If we're jumping between revisions (as opposed to merging),
1371 # If we're jumping between revisions (as opposed to merging),
1350 # and if neither the working directory nor the target rev has
1372 # and if neither the working directory nor the target rev has
1351 # the file, then we need to remove it from the dirstate, to
1373 # the file, then we need to remove it from the dirstate, to
1352 # prevent the dirstate from listing the file when it is no
1374 # prevent the dirstate from listing the file when it is no
1353 # longer in the manifest.
1375 # longer in the manifest.
1354 if moddirstate and linear_path and f not in m2:
1376 if moddirstate and linear_path and f not in m2:
1355 self.dirstate.forget((f,))
1377 self.dirstate.forget((f,))
1356
1378
1357 # Compare manifests
1379 # Compare manifests
1358 for f, n in mw.iteritems():
1380 for f, n in mw.iteritems():
1359 if choose and not choose(f): continue
1381 if choose and not choose(f): continue
1360 if f in m2:
1382 if f in m2:
1361 s = 0
1383 s = 0
1362
1384
1363 # is the wfile new since m1, and match m2?
1385 # is the wfile new since m1, and match m2?
1364 if f not in m1:
1386 if f not in m1:
1365 t1 = self.wfile(f).read()
1387 t1 = self.wfile(f).read()
1366 t2 = self.file(f).revision(m2[f])
1388 t2 = self.file(f).revision(m2[f])
1367 if cmp(t1, t2) == 0:
1389 if cmp(t1, t2) == 0:
1368 mark[f] = 1
1390 mark[f] = 1
1369 n = m2[f]
1391 n = m2[f]
1370 del t1, t2
1392 del t1, t2
1371
1393
1372 # are files different?
1394 # are files different?
1373 if n != m2[f]:
1395 if n != m2[f]:
1374 a = ma.get(f, nullid)
1396 a = ma.get(f, nullid)
1375 # are both different from the ancestor?
1397 # are both different from the ancestor?
1376 if n != a and m2[f] != a:
1398 if n != a and m2[f] != a:
1377 self.ui.debug(" %s versions differ, resolve\n" % f)
1399 self.ui.debug(" %s versions differ, resolve\n" % f)
1378 # merge executable bits
1400 # merge executable bits
1379 # "if we changed or they changed, change in merge"
1401 # "if we changed or they changed, change in merge"
1380 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1402 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1381 mode = ((a^b) | (a^c)) ^ a
1403 mode = ((a^b) | (a^c)) ^ a
1382 merge[f] = (m1.get(f, nullid), m2[f], mode)
1404 merge[f] = (m1.get(f, nullid), m2[f], mode)
1383 s = 1
1405 s = 1
1384 # are we clobbering?
1406 # are we clobbering?
1385 # is remote's version newer?
1407 # is remote's version newer?
1386 # or are we going back in time?
1408 # or are we going back in time?
1387 elif force or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
1409 elif force or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
1388 self.ui.debug(" remote %s is newer, get\n" % f)
1410 self.ui.debug(" remote %s is newer, get\n" % f)
1389 get[f] = m2[f]
1411 get[f] = m2[f]
1390 s = 1
1412 s = 1
1391 else:
1413 else:
1392 mark[f] = 1
1414 mark[f] = 1
1393 elif f in umap:
1415 elif f in umap:
1394 # this unknown file is the same as the checkout
1416 # this unknown file is the same as the checkout
1395 get[f] = m2[f]
1417 get[f] = m2[f]
1396
1418
1397 if not s and mfw[f] != mf2[f]:
1419 if not s and mfw[f] != mf2[f]:
1398 if force:
1420 if force:
1399 self.ui.debug(" updating permissions for %s\n" % f)
1421 self.ui.debug(" updating permissions for %s\n" % f)
1400 util.set_exec(self.wjoin(f), mf2[f])
1422 util.set_exec(self.wjoin(f), mf2[f])
1401 else:
1423 else:
1402 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1424 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1403 mode = ((a^b) | (a^c)) ^ a
1425 mode = ((a^b) | (a^c)) ^ a
1404 if mode != b:
1426 if mode != b:
1405 self.ui.debug(" updating permissions for %s\n" % f)
1427 self.ui.debug(" updating permissions for %s\n" % f)
1406 util.set_exec(self.wjoin(f), mode)
1428 util.set_exec(self.wjoin(f), mode)
1407 mark[f] = 1
1429 mark[f] = 1
1408 del m2[f]
1430 del m2[f]
1409 elif f in ma:
1431 elif f in ma:
1410 if n != ma[f]:
1432 if n != ma[f]:
1411 r = "d"
1433 r = "d"
1412 if not force and (linear_path or allow):
1434 if not force and (linear_path or allow):
1413 r = self.ui.prompt(
1435 r = self.ui.prompt(
1414 (" local changed %s which remote deleted\n" % f) +
1436 (" local changed %s which remote deleted\n" % f) +
1415 "(k)eep or (d)elete?", "[kd]", "k")
1437 "(k)eep or (d)elete?", "[kd]", "k")
1416 if r == "d":
1438 if r == "d":
1417 remove.append(f)
1439 remove.append(f)
1418 else:
1440 else:
1419 self.ui.debug("other deleted %s\n" % f)
1441 self.ui.debug("other deleted %s\n" % f)
1420 remove.append(f) # other deleted it
1442 remove.append(f) # other deleted it
1421 else:
1443 else:
1422 if n == m1.get(f, nullid): # same as parent
1444 if n == m1.get(f, nullid): # same as parent
1423 if p2 == pa: # going backwards?
1445 if p2 == pa: # going backwards?
1424 self.ui.debug("remote deleted %s\n" % f)
1446 self.ui.debug("remote deleted %s\n" % f)
1425 remove.append(f)
1447 remove.append(f)
1426 else:
1448 else:
1427 self.ui.debug("local created %s, keeping\n" % f)
1449 self.ui.debug("local created %s, keeping\n" % f)
1428 else:
1450 else:
1429 self.ui.debug("working dir created %s, keeping\n" % f)
1451 self.ui.debug("working dir created %s, keeping\n" % f)
1430
1452
1431 for f, n in m2.iteritems():
1453 for f, n in m2.iteritems():
1432 if choose and not choose(f): continue
1454 if choose and not choose(f): continue
1433 if f[0] == "/": continue
1455 if f[0] == "/": continue
1434 if f in ma and n != ma[f]:
1456 if f in ma and n != ma[f]:
1435 r = "k"
1457 r = "k"
1436 if not force and (linear_path or allow):
1458 if not force and (linear_path or allow):
1437 r = self.ui.prompt(
1459 r = self.ui.prompt(
1438 ("remote changed %s which local deleted\n" % f) +
1460 ("remote changed %s which local deleted\n" % f) +
1439 "(k)eep or (d)elete?", "[kd]", "k")
1461 "(k)eep or (d)elete?", "[kd]", "k")
1440 if r == "k": get[f] = n
1462 if r == "k": get[f] = n
1441 elif f not in ma:
1463 elif f not in ma:
1442 self.ui.debug("remote created %s\n" % f)
1464 self.ui.debug("remote created %s\n" % f)
1443 get[f] = n
1465 get[f] = n
1444 else:
1466 else:
1445 if force or p2 == pa: # going backwards?
1467 if force or p2 == pa: # going backwards?
1446 self.ui.debug("local deleted %s, recreating\n" % f)
1468 self.ui.debug("local deleted %s, recreating\n" % f)
1447 get[f] = n
1469 get[f] = n
1448 else:
1470 else:
1449 self.ui.debug("local deleted %s\n" % f)
1471 self.ui.debug("local deleted %s\n" % f)
1450
1472
1451 del mw, m1, m2, ma
1473 del mw, m1, m2, ma
1452
1474
1453 if force:
1475 if force:
1454 for f in merge:
1476 for f in merge:
1455 get[f] = merge[f][1]
1477 get[f] = merge[f][1]
1456 merge = {}
1478 merge = {}
1457
1479
1458 if linear_path or force:
1480 if linear_path or force:
1459 # we don't need to do any magic, just jump to the new rev
1481 # we don't need to do any magic, just jump to the new rev
1460 mode = 'n'
1482 mode = 'n'
1461 p1, p2 = p2, nullid
1483 p1, p2 = p2, nullid
1462 else:
1484 else:
1463 if not allow:
1485 if not allow:
1464 self.ui.status("this update spans a branch" +
1486 self.ui.status("this update spans a branch" +
1465 " affecting the following files:\n")
1487 " affecting the following files:\n")
1466 fl = merge.keys() + get.keys()
1488 fl = merge.keys() + get.keys()
1467 fl.sort()
1489 fl.sort()
1468 for f in fl:
1490 for f in fl:
1469 cf = ""
1491 cf = ""
1470 if f in merge: cf = " (resolve)"
1492 if f in merge: cf = " (resolve)"
1471 self.ui.status(" %s%s\n" % (f, cf))
1493 self.ui.status(" %s%s\n" % (f, cf))
1472 self.ui.warn("aborting update spanning branches!\n")
1494 self.ui.warn("aborting update spanning branches!\n")
1473 self.ui.status("(use update -m to perform a branch merge)\n")
1495 self.ui.status("(use update -m to perform a branch merge)\n")
1474 return 1
1496 return 1
1475 # we have to remember what files we needed to get/change
1497 # we have to remember what files we needed to get/change
1476 # because any file that's different from either one of its
1498 # because any file that's different from either one of its
1477 # parents must be in the changeset
1499 # parents must be in the changeset
1478 mode = 'm'
1500 mode = 'm'
1479 if moddirstate:
1501 if moddirstate:
1480 self.dirstate.update(mark.keys(), "m")
1502 self.dirstate.update(mark.keys(), "m")
1481
1503
1482 if moddirstate:
1504 if moddirstate:
1483 self.dirstate.setparents(p1, p2)
1505 self.dirstate.setparents(p1, p2)
1484
1506
1485 # get the files we don't need to change
1507 # get the files we don't need to change
1486 files = get.keys()
1508 files = get.keys()
1487 files.sort()
1509 files.sort()
1488 for f in files:
1510 for f in files:
1489 if f[0] == "/": continue
1511 if f[0] == "/": continue
1490 self.ui.note("getting %s\n" % f)
1512 self.ui.note("getting %s\n" % f)
1491 t = self.file(f).read(get[f])
1513 t = self.file(f).read(get[f])
1492 try:
1514 try:
1493 self.wfile(f, "w").write(t)
1515 self.wfile(f, "w").write(t)
1494 except IOError:
1516 except IOError:
1495 os.makedirs(os.path.dirname(self.wjoin(f)))
1517 os.makedirs(os.path.dirname(self.wjoin(f)))
1496 self.wfile(f, "w").write(t)
1518 self.wfile(f, "w").write(t)
1497 util.set_exec(self.wjoin(f), mf2[f])
1519 util.set_exec(self.wjoin(f), mf2[f])
1498 if moddirstate:
1520 if moddirstate:
1499 self.dirstate.update([f], mode)
1521 self.dirstate.update([f], mode)
1500
1522
1501 # merge the tricky bits
1523 # merge the tricky bits
1502 files = merge.keys()
1524 files = merge.keys()
1503 files.sort()
1525 files.sort()
1504 for f in files:
1526 for f in files:
1505 self.ui.status("merging %s\n" % f)
1527 self.ui.status("merging %s\n" % f)
1506 m, o, flag = merge[f]
1528 m, o, flag = merge[f]
1507 self.merge3(f, m, o)
1529 self.merge3(f, m, o)
1508 util.set_exec(self.wjoin(f), flag)
1530 util.set_exec(self.wjoin(f), flag)
1509 if moddirstate and mode == 'm':
1531 if moddirstate and mode == 'm':
1510 # only update dirstate on branch merge, otherwise we
1532 # only update dirstate on branch merge, otherwise we
1511 # could mark files with changes as unchanged
1533 # could mark files with changes as unchanged
1512 self.dirstate.update([f], mode)
1534 self.dirstate.update([f], mode)
1513
1535
1514 remove.sort()
1536 remove.sort()
1515 for f in remove:
1537 for f in remove:
1516 self.ui.note("removing %s\n" % f)
1538 self.ui.note("removing %s\n" % f)
1517 try:
1539 try:
1518 os.unlink(f)
1540 os.unlink(f)
1519 except OSError, inst:
1541 except OSError, inst:
1520 self.ui.warn("update failed to remove %s: %s!\n" % (f, inst))
1542 self.ui.warn("update failed to remove %s: %s!\n" % (f, inst))
1521 # try removing directories that might now be empty
1543 # try removing directories that might now be empty
1522 try: os.removedirs(os.path.dirname(f))
1544 try: os.removedirs(os.path.dirname(f))
1523 except: pass
1545 except: pass
1524 if moddirstate:
1546 if moddirstate:
1525 if mode == 'n':
1547 if mode == 'n':
1526 self.dirstate.forget(remove)
1548 self.dirstate.forget(remove)
1527 else:
1549 else:
1528 self.dirstate.update(remove, 'r')
1550 self.dirstate.update(remove, 'r')
1529
1551
1530 def merge3(self, fn, my, other):
1552 def merge3(self, fn, my, other):
1531 """perform a 3-way merge in the working directory"""
1553 """perform a 3-way merge in the working directory"""
1532
1554
1533 def temp(prefix, node):
1555 def temp(prefix, node):
1534 pre = "%s~%s." % (os.path.basename(fn), prefix)
1556 pre = "%s~%s." % (os.path.basename(fn), prefix)
1535 (fd, name) = tempfile.mkstemp("", pre)
1557 (fd, name) = tempfile.mkstemp("", pre)
1536 f = os.fdopen(fd, "wb")
1558 f = os.fdopen(fd, "wb")
1537 f.write(fl.revision(node))
1559 f.write(fl.revision(node))
1538 f.close()
1560 f.close()
1539 return name
1561 return name
1540
1562
1541 fl = self.file(fn)
1563 fl = self.file(fn)
1542 base = fl.ancestor(my, other)
1564 base = fl.ancestor(my, other)
1543 a = self.wjoin(fn)
1565 a = self.wjoin(fn)
1544 b = temp("base", base)
1566 b = temp("base", base)
1545 c = temp("other", other)
1567 c = temp("other", other)
1546
1568
1547 self.ui.note("resolving %s\n" % fn)
1569 self.ui.note("resolving %s\n" % fn)
1548 self.ui.debug("file %s: other %s ancestor %s\n" %
1570 self.ui.debug("file %s: other %s ancestor %s\n" %
1549 (fn, short(other), short(base)))
1571 (fn, short(other), short(base)))
1550
1572
1551 cmd = (os.environ.get("HGMERGE") or self.ui.config("ui", "merge")
1573 cmd = (os.environ.get("HGMERGE") or self.ui.config("ui", "merge")
1552 or "hgmerge")
1574 or "hgmerge")
1553 r = os.system("%s %s %s %s" % (cmd, a, b, c))
1575 r = os.system("%s %s %s %s" % (cmd, a, b, c))
1554 if r:
1576 if r:
1555 self.ui.warn("merging %s failed!\n" % fn)
1577 self.ui.warn("merging %s failed!\n" % fn)
1556
1578
1557 os.unlink(b)
1579 os.unlink(b)
1558 os.unlink(c)
1580 os.unlink(c)
1559
1581
1560 def verify(self):
1582 def verify(self):
1561 filelinkrevs = {}
1583 filelinkrevs = {}
1562 filenodes = {}
1584 filenodes = {}
1563 changesets = revisions = files = 0
1585 changesets = revisions = files = 0
1564 errors = 0
1586 errors = 0
1565
1587
1566 seen = {}
1588 seen = {}
1567 self.ui.status("checking changesets\n")
1589 self.ui.status("checking changesets\n")
1568 for i in range(self.changelog.count()):
1590 for i in range(self.changelog.count()):
1569 changesets += 1
1591 changesets += 1
1570 n = self.changelog.node(i)
1592 n = self.changelog.node(i)
1571 if n in seen:
1593 if n in seen:
1572 self.ui.warn("duplicate changeset at revision %d\n" % i)
1594 self.ui.warn("duplicate changeset at revision %d\n" % i)
1573 errors += 1
1595 errors += 1
1574 seen[n] = 1
1596 seen[n] = 1
1575
1597
1576 for p in self.changelog.parents(n):
1598 for p in self.changelog.parents(n):
1577 if p not in self.changelog.nodemap:
1599 if p not in self.changelog.nodemap:
1578 self.ui.warn("changeset %s has unknown parent %s\n" %
1600 self.ui.warn("changeset %s has unknown parent %s\n" %
1579 (short(n), short(p)))
1601 (short(n), short(p)))
1580 errors += 1
1602 errors += 1
1581 try:
1603 try:
1582 changes = self.changelog.read(n)
1604 changes = self.changelog.read(n)
1583 except Exception, inst:
1605 except Exception, inst:
1584 self.ui.warn("unpacking changeset %s: %s\n" % (short(n), inst))
1606 self.ui.warn("unpacking changeset %s: %s\n" % (short(n), inst))
1585 errors += 1
1607 errors += 1
1586
1608
1587 for f in changes[3]:
1609 for f in changes[3]:
1588 filelinkrevs.setdefault(f, []).append(i)
1610 filelinkrevs.setdefault(f, []).append(i)
1589
1611
1590 seen = {}
1612 seen = {}
1591 self.ui.status("checking manifests\n")
1613 self.ui.status("checking manifests\n")
1592 for i in range(self.manifest.count()):
1614 for i in range(self.manifest.count()):
1593 n = self.manifest.node(i)
1615 n = self.manifest.node(i)
1594 if n in seen:
1616 if n in seen:
1595 self.ui.warn("duplicate manifest at revision %d\n" % i)
1617 self.ui.warn("duplicate manifest at revision %d\n" % i)
1596 errors += 1
1618 errors += 1
1597 seen[n] = 1
1619 seen[n] = 1
1598
1620
1599 for p in self.manifest.parents(n):
1621 for p in self.manifest.parents(n):
1600 if p not in self.manifest.nodemap:
1622 if p not in self.manifest.nodemap:
1601 self.ui.warn("manifest %s has unknown parent %s\n" %
1623 self.ui.warn("manifest %s has unknown parent %s\n" %
1602 (short(n), short(p)))
1624 (short(n), short(p)))
1603 errors += 1
1625 errors += 1
1604
1626
1605 try:
1627 try:
1606 delta = mdiff.patchtext(self.manifest.delta(n))
1628 delta = mdiff.patchtext(self.manifest.delta(n))
1607 except KeyboardInterrupt:
1629 except KeyboardInterrupt:
1608 self.ui.warn("aborted")
1630 self.ui.warn("aborted")
1609 sys.exit(0)
1631 sys.exit(0)
1610 except Exception, inst:
1632 except Exception, inst:
1611 self.ui.warn("unpacking manifest %s: %s\n"
1633 self.ui.warn("unpacking manifest %s: %s\n"
1612 % (short(n), inst))
1634 % (short(n), inst))
1613 errors += 1
1635 errors += 1
1614
1636
1615 ff = [ l.split('\0') for l in delta.splitlines() ]
1637 ff = [ l.split('\0') for l in delta.splitlines() ]
1616 for f, fn in ff:
1638 for f, fn in ff:
1617 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
1639 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
1618
1640
1619 self.ui.status("crosschecking files in changesets and manifests\n")
1641 self.ui.status("crosschecking files in changesets and manifests\n")
1620 for f in filenodes:
1642 for f in filenodes:
1621 if f not in filelinkrevs:
1643 if f not in filelinkrevs:
1622 self.ui.warn("file %s in manifest but not in changesets\n" % f)
1644 self.ui.warn("file %s in manifest but not in changesets\n" % f)
1623 errors += 1
1645 errors += 1
1624
1646
1625 for f in filelinkrevs:
1647 for f in filelinkrevs:
1626 if f not in filenodes:
1648 if f not in filenodes:
1627 self.ui.warn("file %s in changeset but not in manifest\n" % f)
1649 self.ui.warn("file %s in changeset but not in manifest\n" % f)
1628 errors += 1
1650 errors += 1
1629
1651
1630 self.ui.status("checking files\n")
1652 self.ui.status("checking files\n")
1631 ff = filenodes.keys()
1653 ff = filenodes.keys()
1632 ff.sort()
1654 ff.sort()
1633 for f in ff:
1655 for f in ff:
1634 if f == "/dev/null": continue
1656 if f == "/dev/null": continue
1635 files += 1
1657 files += 1
1636 fl = self.file(f)
1658 fl = self.file(f)
1637 nodes = { nullid: 1 }
1659 nodes = { nullid: 1 }
1638 seen = {}
1660 seen = {}
1639 for i in range(fl.count()):
1661 for i in range(fl.count()):
1640 revisions += 1
1662 revisions += 1
1641 n = fl.node(i)
1663 n = fl.node(i)
1642
1664
1643 if n in seen:
1665 if n in seen:
1644 self.ui.warn("%s: duplicate revision %d\n" % (f, i))
1666 self.ui.warn("%s: duplicate revision %d\n" % (f, i))
1645 errors += 1
1667 errors += 1
1646
1668
1647 if n not in filenodes[f]:
1669 if n not in filenodes[f]:
1648 self.ui.warn("%s: %d:%s not in manifests\n"
1670 self.ui.warn("%s: %d:%s not in manifests\n"
1649 % (f, i, short(n)))
1671 % (f, i, short(n)))
1650 errors += 1
1672 errors += 1
1651 else:
1673 else:
1652 del filenodes[f][n]
1674 del filenodes[f][n]
1653
1675
1654 flr = fl.linkrev(n)
1676 flr = fl.linkrev(n)
1655 if flr not in filelinkrevs[f]:
1677 if flr not in filelinkrevs[f]:
1656 self.ui.warn("%s:%s points to unexpected changeset %d\n"
1678 self.ui.warn("%s:%s points to unexpected changeset %d\n"
1657 % (f, short(n), fl.linkrev(n)))
1679 % (f, short(n), fl.linkrev(n)))
1658 errors += 1
1680 errors += 1
1659 else:
1681 else:
1660 filelinkrevs[f].remove(flr)
1682 filelinkrevs[f].remove(flr)
1661
1683
1662 # verify contents
1684 # verify contents
1663 try:
1685 try:
1664 t = fl.read(n)
1686 t = fl.read(n)
1665 except Exception, inst:
1687 except Exception, inst:
1666 self.ui.warn("unpacking file %s %s: %s\n"
1688 self.ui.warn("unpacking file %s %s: %s\n"
1667 % (f, short(n), inst))
1689 % (f, short(n), inst))
1668 errors += 1
1690 errors += 1
1669
1691
1670 # verify parents
1692 # verify parents
1671 (p1, p2) = fl.parents(n)
1693 (p1, p2) = fl.parents(n)
1672 if p1 not in nodes:
1694 if p1 not in nodes:
1673 self.ui.warn("file %s:%s unknown parent 1 %s" %
1695 self.ui.warn("file %s:%s unknown parent 1 %s" %
1674 (f, short(n), short(p1)))
1696 (f, short(n), short(p1)))
1675 errors += 1
1697 errors += 1
1676 if p2 not in nodes:
1698 if p2 not in nodes:
1677 self.ui.warn("file %s:%s unknown parent 2 %s" %
1699 self.ui.warn("file %s:%s unknown parent 2 %s" %
1678 (f, short(n), short(p1)))
1700 (f, short(n), short(p1)))
1679 errors += 1
1701 errors += 1
1680 nodes[n] = 1
1702 nodes[n] = 1
1681
1703
1682 # cross-check
1704 # cross-check
1683 for node in filenodes[f]:
1705 for node in filenodes[f]:
1684 self.ui.warn("node %s in manifests not in %s\n"
1706 self.ui.warn("node %s in manifests not in %s\n"
1685 % (hex(node), f))
1707 % (hex(node), f))
1686 errors += 1
1708 errors += 1
1687
1709
1688 self.ui.status("%d files, %d changesets, %d total revisions\n" %
1710 self.ui.status("%d files, %d changesets, %d total revisions\n" %
1689 (files, changesets, revisions))
1711 (files, changesets, revisions))
1690
1712
1691 if errors:
1713 if errors:
1692 self.ui.warn("%d integrity errors encountered!\n" % errors)
1714 self.ui.warn("%d integrity errors encountered!\n" % errors)
1693 return 1
1715 return 1
1694
1716
1695 class httprepository:
1717 class httprepository:
1696 def __init__(self, ui, path):
1718 def __init__(self, ui, path):
1697 # fix missing / after hostname
1719 # fix missing / after hostname
1698 s = urlparse.urlsplit(path)
1720 s = urlparse.urlsplit(path)
1699 partial = s[2]
1721 partial = s[2]
1700 if not partial: partial = "/"
1722 if not partial: partial = "/"
1701 self.url = urlparse.urlunsplit((s[0], s[1], partial, '', ''))
1723 self.url = urlparse.urlunsplit((s[0], s[1], partial, '', ''))
1702 self.ui = ui
1724 self.ui = ui
1703 no_list = [ "localhost", "127.0.0.1" ]
1725 no_list = [ "localhost", "127.0.0.1" ]
1704 host = ui.config("http_proxy", "host")
1726 host = ui.config("http_proxy", "host")
1705 if host is None:
1727 if host is None:
1706 host = os.environ.get("http_proxy")
1728 host = os.environ.get("http_proxy")
1707 if host and host.startswith('http://'):
1729 if host and host.startswith('http://'):
1708 host = host[7:]
1730 host = host[7:]
1709 user = ui.config("http_proxy", "user")
1731 user = ui.config("http_proxy", "user")
1710 passwd = ui.config("http_proxy", "passwd")
1732 passwd = ui.config("http_proxy", "passwd")
1711 no = ui.config("http_proxy", "no")
1733 no = ui.config("http_proxy", "no")
1712 if no is None:
1734 if no is None:
1713 no = os.environ.get("no_proxy")
1735 no = os.environ.get("no_proxy")
1714 if no:
1736 if no:
1715 no_list = no_list + no.split(",")
1737 no_list = no_list + no.split(",")
1716
1738
1717 no_proxy = 0
1739 no_proxy = 0
1718 for h in no_list:
1740 for h in no_list:
1719 if (path.startswith("http://" + h + "/") or
1741 if (path.startswith("http://" + h + "/") or
1720 path.startswith("http://" + h + ":") or
1742 path.startswith("http://" + h + ":") or
1721 path == "http://" + h):
1743 path == "http://" + h):
1722 no_proxy = 1
1744 no_proxy = 1
1723
1745
1724 # Note: urllib2 takes proxy values from the environment and those will
1746 # Note: urllib2 takes proxy values from the environment and those will
1725 # take precedence
1747 # take precedence
1726 for env in ["HTTP_PROXY", "http_proxy", "no_proxy"]:
1748 for env in ["HTTP_PROXY", "http_proxy", "no_proxy"]:
1727 if os.environ.has_key(env):
1749 if os.environ.has_key(env):
1728 del os.environ[env]
1750 del os.environ[env]
1729
1751
1730 proxy_handler = urllib2.BaseHandler()
1752 proxy_handler = urllib2.BaseHandler()
1731 if host and not no_proxy:
1753 if host and not no_proxy:
1732 proxy_handler = urllib2.ProxyHandler({"http" : "http://" + host})
1754 proxy_handler = urllib2.ProxyHandler({"http" : "http://" + host})
1733
1755
1734 authinfo = None
1756 authinfo = None
1735 if user and passwd:
1757 if user and passwd:
1736 passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
1758 passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
1737 passmgr.add_password(None, host, user, passwd)
1759 passmgr.add_password(None, host, user, passwd)
1738 authinfo = urllib2.ProxyBasicAuthHandler(passmgr)
1760 authinfo = urllib2.ProxyBasicAuthHandler(passmgr)
1739
1761
1740 opener = urllib2.build_opener(proxy_handler, authinfo)
1762 opener = urllib2.build_opener(proxy_handler, authinfo)
1741 urllib2.install_opener(opener)
1763 urllib2.install_opener(opener)
1742
1764
1743 def dev(self):
1765 def dev(self):
1744 return -1
1766 return -1
1745
1767
1746 def do_cmd(self, cmd, **args):
1768 def do_cmd(self, cmd, **args):
1747 self.ui.debug("sending %s command\n" % cmd)
1769 self.ui.debug("sending %s command\n" % cmd)
1748 q = {"cmd": cmd}
1770 q = {"cmd": cmd}
1749 q.update(args)
1771 q.update(args)
1750 qs = urllib.urlencode(q)
1772 qs = urllib.urlencode(q)
1751 cu = "%s?%s" % (self.url, qs)
1773 cu = "%s?%s" % (self.url, qs)
1752 resp = urllib2.urlopen(cu)
1774 resp = urllib2.urlopen(cu)
1753 proto = resp.headers['content-type']
1775 proto = resp.headers['content-type']
1754
1776
1755 # accept old "text/plain" and "application/hg-changegroup" for now
1777 # accept old "text/plain" and "application/hg-changegroup" for now
1756 if not proto.startswith('application/mercurial') and \
1778 if not proto.startswith('application/mercurial') and \
1757 not proto.startswith('text/plain') and \
1779 not proto.startswith('text/plain') and \
1758 not proto.startswith('application/hg-changegroup'):
1780 not proto.startswith('application/hg-changegroup'):
1759 raise RepoError("'%s' does not appear to be an hg repository"
1781 raise RepoError("'%s' does not appear to be an hg repository"
1760 % self.url)
1782 % self.url)
1761
1783
1762 if proto.startswith('application/mercurial'):
1784 if proto.startswith('application/mercurial'):
1763 version = proto[22:]
1785 version = proto[22:]
1764 if float(version) > 0.1:
1786 if float(version) > 0.1:
1765 raise RepoError("'%s' uses newer protocol %s" %
1787 raise RepoError("'%s' uses newer protocol %s" %
1766 (self.url, version))
1788 (self.url, version))
1767
1789
1768 return resp
1790 return resp
1769
1791
1770 def heads(self):
1792 def heads(self):
1771 d = self.do_cmd("heads").read()
1793 d = self.do_cmd("heads").read()
1772 try:
1794 try:
1773 return map(bin, d[:-1].split(" "))
1795 return map(bin, d[:-1].split(" "))
1774 except:
1796 except:
1775 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1797 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1776 raise
1798 raise
1777
1799
1778 def branches(self, nodes):
1800 def branches(self, nodes):
1779 n = " ".join(map(hex, nodes))
1801 n = " ".join(map(hex, nodes))
1780 d = self.do_cmd("branches", nodes=n).read()
1802 d = self.do_cmd("branches", nodes=n).read()
1781 try:
1803 try:
1782 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
1804 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
1783 return br
1805 return br
1784 except:
1806 except:
1785 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1807 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1786 raise
1808 raise
1787
1809
1788 def between(self, pairs):
1810 def between(self, pairs):
1789 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
1811 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
1790 d = self.do_cmd("between", pairs=n).read()
1812 d = self.do_cmd("between", pairs=n).read()
1791 try:
1813 try:
1792 p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
1814 p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
1793 return p
1815 return p
1794 except:
1816 except:
1795 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1817 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1796 raise
1818 raise
1797
1819
1798 def changegroup(self, nodes):
1820 def changegroup(self, nodes):
1799 n = " ".join(map(hex, nodes))
1821 n = " ".join(map(hex, nodes))
1800 f = self.do_cmd("changegroup", roots=n)
1822 f = self.do_cmd("changegroup", roots=n)
1801 bytes = 0
1823 bytes = 0
1802
1824
1803 class zread:
1825 class zread:
1804 def __init__(self, f):
1826 def __init__(self, f):
1805 self.zd = zlib.decompressobj()
1827 self.zd = zlib.decompressobj()
1806 self.f = f
1828 self.f = f
1807 self.buf = ""
1829 self.buf = ""
1808 def read(self, l):
1830 def read(self, l):
1809 while l > len(self.buf):
1831 while l > len(self.buf):
1810 r = self.f.read(4096)
1832 r = self.f.read(4096)
1811 if r:
1833 if r:
1812 self.buf += self.zd.decompress(r)
1834 self.buf += self.zd.decompress(r)
1813 else:
1835 else:
1814 self.buf += self.zd.flush()
1836 self.buf += self.zd.flush()
1815 break
1837 break
1816 d, self.buf = self.buf[:l], self.buf[l:]
1838 d, self.buf = self.buf[:l], self.buf[l:]
1817 return d
1839 return d
1818
1840
1819 return zread(f)
1841 return zread(f)
1820
1842
1821 class remotelock:
1843 class remotelock:
1822 def __init__(self, repo):
1844 def __init__(self, repo):
1823 self.repo = repo
1845 self.repo = repo
1824 def release(self):
1846 def release(self):
1825 self.repo.unlock()
1847 self.repo.unlock()
1826 self.repo = None
1848 self.repo = None
1827 def __del__(self):
1849 def __del__(self):
1828 if self.repo:
1850 if self.repo:
1829 self.release()
1851 self.release()
1830
1852
1831 class sshrepository:
1853 class sshrepository:
1832 def __init__(self, ui, path):
1854 def __init__(self, ui, path):
1833 self.url = path
1855 self.url = path
1834 self.ui = ui
1856 self.ui = ui
1835
1857
1836 m = re.match(r'ssh://(([^@]+)@)?([^:/]+)(:(\d+))?(/(.*))?', path)
1858 m = re.match(r'ssh://(([^@]+)@)?([^:/]+)(:(\d+))?(/(.*))?', path)
1837 if not m:
1859 if not m:
1838 raise RepoError("couldn't parse destination %s\n" % path)
1860 raise RepoError("couldn't parse destination %s\n" % path)
1839
1861
1840 self.user = m.group(2)
1862 self.user = m.group(2)
1841 self.host = m.group(3)
1863 self.host = m.group(3)
1842 self.port = m.group(5)
1864 self.port = m.group(5)
1843 self.path = m.group(7)
1865 self.path = m.group(7)
1844
1866
1845 args = self.user and ("%s@%s" % (self.user, self.host)) or self.host
1867 args = self.user and ("%s@%s" % (self.user, self.host)) or self.host
1846 args = self.port and ("%s -p %s") % (args, self.port) or args
1868 args = self.port and ("%s -p %s") % (args, self.port) or args
1847 path = self.path or ""
1869 path = self.path or ""
1848
1870
1849 cmd = "ssh %s 'hg -R %s serve --stdio'"
1871 cmd = "ssh %s 'hg -R %s serve --stdio'"
1850 cmd = cmd % (args, path)
1872 cmd = cmd % (args, path)
1851
1873
1852 self.pipeo, self.pipei, self.pipee = os.popen3(cmd)
1874 self.pipeo, self.pipei, self.pipee = os.popen3(cmd)
1853
1875
1854 def readerr(self):
1876 def readerr(self):
1855 while 1:
1877 while 1:
1856 r,w,x = select.select([self.pipee], [], [], 0)
1878 r,w,x = select.select([self.pipee], [], [], 0)
1857 if not r: break
1879 if not r: break
1858 l = self.pipee.readline()
1880 l = self.pipee.readline()
1859 if not l: break
1881 if not l: break
1860 self.ui.status("remote: ", l)
1882 self.ui.status("remote: ", l)
1861
1883
1862 def __del__(self):
1884 def __del__(self):
1863 self.pipeo.close()
1885 self.pipeo.close()
1864 self.pipei.close()
1886 self.pipei.close()
1865 for l in self.pipee:
1887 for l in self.pipee:
1866 self.ui.status("remote: ", l)
1888 self.ui.status("remote: ", l)
1867 self.pipee.close()
1889 self.pipee.close()
1868
1890
1869 def dev(self):
1891 def dev(self):
1870 return -1
1892 return -1
1871
1893
1872 def do_cmd(self, cmd, **args):
1894 def do_cmd(self, cmd, **args):
1873 self.ui.debug("sending %s command\n" % cmd)
1895 self.ui.debug("sending %s command\n" % cmd)
1874 self.pipeo.write("%s\n" % cmd)
1896 self.pipeo.write("%s\n" % cmd)
1875 for k, v in args.items():
1897 for k, v in args.items():
1876 self.pipeo.write("%s %d\n" % (k, len(v)))
1898 self.pipeo.write("%s %d\n" % (k, len(v)))
1877 self.pipeo.write(v)
1899 self.pipeo.write(v)
1878 self.pipeo.flush()
1900 self.pipeo.flush()
1879
1901
1880 return self.pipei
1902 return self.pipei
1881
1903
1882 def call(self, cmd, **args):
1904 def call(self, cmd, **args):
1883 r = self.do_cmd(cmd, **args)
1905 r = self.do_cmd(cmd, **args)
1884 l = r.readline()
1906 l = r.readline()
1885 self.readerr()
1907 self.readerr()
1886 try:
1908 try:
1887 l = int(l)
1909 l = int(l)
1888 except:
1910 except:
1889 raise RepoError("unexpected response '%s'" % l)
1911 raise RepoError("unexpected response '%s'" % l)
1890 return r.read(l)
1912 return r.read(l)
1891
1913
1892 def lock(self):
1914 def lock(self):
1893 self.call("lock")
1915 self.call("lock")
1894 return remotelock(self)
1916 return remotelock(self)
1895
1917
1896 def unlock(self):
1918 def unlock(self):
1897 self.call("unlock")
1919 self.call("unlock")
1898
1920
1899 def heads(self):
1921 def heads(self):
1900 d = self.call("heads")
1922 d = self.call("heads")
1901 try:
1923 try:
1902 return map(bin, d[:-1].split(" "))
1924 return map(bin, d[:-1].split(" "))
1903 except:
1925 except:
1904 raise RepoError("unexpected response '%s'" % (d[:400] + "..."))
1926 raise RepoError("unexpected response '%s'" % (d[:400] + "..."))
1905
1927
1906 def branches(self, nodes):
1928 def branches(self, nodes):
1907 n = " ".join(map(hex, nodes))
1929 n = " ".join(map(hex, nodes))
1908 d = self.call("branches", nodes=n)
1930 d = self.call("branches", nodes=n)
1909 try:
1931 try:
1910 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
1932 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
1911 return br
1933 return br
1912 except:
1934 except:
1913 raise RepoError("unexpected response '%s'" % (d[:400] + "..."))
1935 raise RepoError("unexpected response '%s'" % (d[:400] + "..."))
1914
1936
1915 def between(self, pairs):
1937 def between(self, pairs):
1916 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
1938 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
1917 d = self.call("between", pairs=n)
1939 d = self.call("between", pairs=n)
1918 try:
1940 try:
1919 p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
1941 p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
1920 return p
1942 return p
1921 except:
1943 except:
1922 raise RepoError("unexpected response '%s'" % (d[:400] + "..."))
1944 raise RepoError("unexpected response '%s'" % (d[:400] + "..."))
1923
1945
1924 def changegroup(self, nodes):
1946 def changegroup(self, nodes):
1925 n = " ".join(map(hex, nodes))
1947 n = " ".join(map(hex, nodes))
1926 f = self.do_cmd("changegroup", roots=n)
1948 f = self.do_cmd("changegroup", roots=n)
1927 return self.pipei
1949 return self.pipei
1928
1950
1929 def addchangegroup(self, cg):
1951 def addchangegroup(self, cg):
1930 d = self.call("addchangegroup")
1952 d = self.call("addchangegroup")
1931 if d:
1953 if d:
1932 raise RepoError("push refused: %s", d)
1954 raise RepoError("push refused: %s", d)
1933
1955
1934 while 1:
1956 while 1:
1935 d = cg.read(4096)
1957 d = cg.read(4096)
1936 if not d: break
1958 if not d: break
1937 self.pipeo.write(d)
1959 self.pipeo.write(d)
1938 self.readerr()
1960 self.readerr()
1939
1961
1940 self.pipeo.flush()
1962 self.pipeo.flush()
1941
1963
1942 self.readerr()
1964 self.readerr()
1943 l = int(self.pipei.readline())
1965 l = int(self.pipei.readline())
1944 return self.pipei.read(l) != ""
1966 return self.pipei.read(l) != ""
1945
1967
1946 def repository(ui, path=None, create=0):
1968 def repository(ui, path=None, create=0):
1947 if path:
1969 if path:
1948 if path.startswith("http://"):
1970 if path.startswith("http://"):
1949 return httprepository(ui, path)
1971 return httprepository(ui, path)
1950 if path.startswith("hg://"):
1972 if path.startswith("hg://"):
1951 return httprepository(ui, path.replace("hg://", "http://"))
1973 return httprepository(ui, path.replace("hg://", "http://"))
1952 if path.startswith("old-http://"):
1974 if path.startswith("old-http://"):
1953 return localrepository(ui, path.replace("old-http://", "http://"))
1975 return localrepository(ui, path.replace("old-http://", "http://"))
1954 if path.startswith("ssh://"):
1976 if path.startswith("ssh://"):
1955 return sshrepository(ui, path)
1977 return sshrepository(ui, path)
1956
1978
1957 return localrepository(ui, path, create)
1979 return localrepository(ui, path, create)
@@ -1,78 +1,78
1 # transaction.py - simple journalling scheme for mercurial
1 # transaction.py - simple journalling scheme for mercurial
2 #
2 #
3 # This transaction scheme is intended to gracefully handle program
3 # This transaction scheme is intended to gracefully handle program
4 # errors and interruptions. More serious failures like system crashes
4 # errors and interruptions. More serious failures like system crashes
5 # can be recovered with an fsck-like tool. As the whole repository is
5 # can be recovered with an fsck-like tool. As the whole repository is
6 # effectively log-structured, this should amount to simply truncating
6 # effectively log-structured, this should amount to simply truncating
7 # anything that isn't referenced in the changelog.
7 # anything that isn't referenced in the changelog.
8 #
8 #
9 # Copyright 2005 Matt Mackall <mpm@selenic.com>
9 # Copyright 2005 Matt Mackall <mpm@selenic.com>
10 #
10 #
11 # This software may be used and distributed according to the terms
11 # This software may be used and distributed according to the terms
12 # of the GNU General Public License, incorporated herein by reference.
12 # of the GNU General Public License, incorporated herein by reference.
13
13
14 import os
14 import os
15 import util
15 import util
16
16
17 class transaction:
17 class transaction:
18 def __init__(self, report, opener, journal, after = None):
18 def __init__(self, report, opener, journal, after = None):
19 self.journal = None
19 self.journal = None
20
20
21 # abort here if the journal already exists
21 # abort here if the journal already exists
22 if os.path.exists(journal):
22 if os.path.exists(journal):
23 raise "journal already exists - run hg recover"
23 raise "journal already exists - run hg recover"
24
24
25 self.report = report
25 self.report = report
26 self.opener = opener
26 self.opener = opener
27 self.after = after
27 self.after = after
28 self.entries = []
28 self.entries = []
29 self.map = {}
29 self.map = {}
30 self.journal = journal
30 self.journal = journal
31
31
32 self.file = open(self.journal, "w")
32 self.file = open(self.journal, "w")
33
33
34 def __del__(self):
34 def __del__(self):
35 if self.journal:
35 if self.journal:
36 if self.entries: self.abort()
36 if self.entries: self.abort()
37 self.file.close()
37 self.file.close()
38 try: os.unlink(self.journal)
38 try: os.unlink(self.journal)
39 except: pass
39 except: pass
40
40
41 def add(self, file, offset):
41 def add(self, file, offset):
42 if file in self.map: return
42 if file in self.map: return
43 self.entries.append((file, offset))
43 self.entries.append((file, offset))
44 self.map[file] = 1
44 self.map[file] = 1
45 # add enough data to the journal to do the truncate
45 # add enough data to the journal to do the truncate
46 self.file.write("%s\0%d\n" % (file, offset))
46 self.file.write("%s\0%d\n" % (file, offset))
47 self.file.flush()
47 self.file.flush()
48
48
49 def close(self):
49 def close(self):
50 self.file.close()
50 self.file.close()
51 self.entries = []
51 self.entries = []
52 if self.after:
52 if self.after:
53 util.rename(self.journal, self.after)
53 self.after()
54 else:
54 else:
55 os.unlink(self.journal)
55 os.unlink(self.journal)
56 self.journal = None
56 self.journal = None
57
57
58 def abort(self):
58 def abort(self):
59 if not self.entries: return
59 if not self.entries: return
60
60
61 self.report("transaction abort!\n")
61 self.report("transaction abort!\n")
62
62
63 for f, o in self.entries:
63 for f, o in self.entries:
64 try:
64 try:
65 self.opener(f, "a").truncate(o)
65 self.opener(f, "a").truncate(o)
66 except:
66 except:
67 self.report("failed to truncate %s\n" % f)
67 self.report("failed to truncate %s\n" % f)
68
68
69 self.entries = []
69 self.entries = []
70
70
71 self.report("rollback completed\n")
71 self.report("rollback completed\n")
72
72
73 def rollback(opener, file):
73 def rollback(opener, file):
74 for l in open(file).readlines():
74 for l in open(file).readlines():
75 f, o = l.split('\0')
75 f, o = l.split('\0')
76 opener(f, "a").truncate(int(o))
76 opener(f, "a").truncate(int(o))
77 os.unlink(file)
77 os.unlink(file)
78
78
@@ -1,207 +1,210
1 # util.py - utility functions and platform specfic implementations
1 # util.py - utility functions and platform specfic implementations
2 #
2 #
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.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, errno
8 import os, errno
9 from demandload import *
9 from demandload import *
10 demandload(globals(), "re")
10 demandload(globals(), "re")
11
11
12 def unique(g):
12 def unique(g):
13 seen = {}
13 seen = {}
14 for f in g:
14 for f in g:
15 if f not in seen:
15 if f not in seen:
16 seen[f] = 1
16 seen[f] = 1
17 yield f
17 yield f
18
18
19 class CommandError(Exception): pass
19 class CommandError(Exception): pass
20
20
21 def explain_exit(code):
22 """return a 2-tuple (desc, code) describing a process's status"""
23 if os.WIFEXITED(code):
24 val = os.WEXITSTATUS(code)
25 return "exited with status %d" % val, val
26 elif os.WIFSIGNALED(code):
27 val = os.WTERMSIG(code)
28 return "killed by signal %d" % val, val
29 elif os.WIFSTOPPED(code):
30 val = os.WSTOPSIG(code)
31 return "stopped by signal %d" % val, val
32 raise ValueError("invalid exit code")
33
34 def always(fn): return True
21 def always(fn): return True
35 def never(fn): return False
22 def never(fn): return False
36
23
37 def globre(pat, head = '^', tail = '$'):
24 def globre(pat, head = '^', tail = '$'):
38 "convert a glob pattern into a regexp"
25 "convert a glob pattern into a regexp"
39 i, n = 0, len(pat)
26 i, n = 0, len(pat)
40 res = ''
27 res = ''
41 group = False
28 group = False
42 def peek(): return i < n and pat[i]
29 def peek(): return i < n and pat[i]
43 while i < n:
30 while i < n:
44 c = pat[i]
31 c = pat[i]
45 i = i+1
32 i = i+1
46 if c == '*':
33 if c == '*':
47 if peek() == '*':
34 if peek() == '*':
48 i += 1
35 i += 1
49 res += '.*'
36 res += '.*'
50 else:
37 else:
51 res += '[^/]*'
38 res += '[^/]*'
52 elif c == '?':
39 elif c == '?':
53 res += '.'
40 res += '.'
54 elif c == '[':
41 elif c == '[':
55 j = i
42 j = i
56 if j < n and pat[j] in '!]':
43 if j < n and pat[j] in '!]':
57 j += 1
44 j += 1
58 while j < n and pat[j] != ']':
45 while j < n and pat[j] != ']':
59 j += 1
46 j += 1
60 if j >= n:
47 if j >= n:
61 res += '\\['
48 res += '\\['
62 else:
49 else:
63 stuff = pat[i:j].replace('\\','\\\\')
50 stuff = pat[i:j].replace('\\','\\\\')
64 i = j + 1
51 i = j + 1
65 if stuff[0] == '!':
52 if stuff[0] == '!':
66 stuff = '^' + stuff[1:]
53 stuff = '^' + stuff[1:]
67 elif stuff[0] == '^':
54 elif stuff[0] == '^':
68 stuff = '\\' + stuff
55 stuff = '\\' + stuff
69 res = '%s[%s]' % (res, stuff)
56 res = '%s[%s]' % (res, stuff)
70 elif c == '{':
57 elif c == '{':
71 group = True
58 group = True
72 res += '(?:'
59 res += '(?:'
73 elif c == '}' and group:
60 elif c == '}' and group:
74 res += ')'
61 res += ')'
75 group = False
62 group = False
76 elif c == ',' and group:
63 elif c == ',' and group:
77 res += '|'
64 res += '|'
78 else:
65 else:
79 res += re.escape(c)
66 res += re.escape(c)
80 return head + res + tail
67 return head + res + tail
81
68
82 def matcher(cwd, pats, inc, exc, head = ''):
69 def matcher(cwd, pats, inc, exc, head = ''):
83 def regex(name, tail):
70 def regex(name, tail):
84 '''convert a pattern into a regular expression'''
71 '''convert a pattern into a regular expression'''
85 if name.startswith('re:'):
72 if name.startswith('re:'):
86 return name[3:]
73 return name[3:]
87 elif name.startswith('path:'):
74 elif name.startswith('path:'):
88 return '^' + re.escape(name[5:]) + '$'
75 return '^' + re.escape(name[5:]) + '$'
89 elif name.startswith('glob:'):
76 elif name.startswith('glob:'):
90 return head + globre(name[5:], '', tail)
77 return head + globre(name[5:], '', tail)
91 return head + globre(name, '', tail)
78 return head + globre(name, '', tail)
92
79
93 def under(fn):
80 def under(fn):
94 """check if fn is under our cwd"""
81 """check if fn is under our cwd"""
95 return not cwd or fn.startswith(cwdsep)
82 return not cwd or fn.startswith(cwdsep)
96
83
97 def matchfn(pats, tail):
84 def matchfn(pats, tail):
98 """build a matching function from a set of patterns"""
85 """build a matching function from a set of patterns"""
99 if pats:
86 if pats:
100 pat = '(?:%s)' % '|'.join([regex(p, tail) for p in pats])
87 pat = '(?:%s)' % '|'.join([regex(p, tail) for p in pats])
101 if cwd:
88 if cwd:
102 pat = re.escape(cwd + os.sep) + pat
89 pat = re.escape(cwd + os.sep) + pat
103 return re.compile(pat).match
90 return re.compile(pat).match
104
91
105 cwdsep = cwd + os.sep
92 cwdsep = cwd + os.sep
106 patmatch = matchfn(pats, '$') or (lambda fn: True)
93 patmatch = matchfn(pats, '$') or (lambda fn: True)
107 incmatch = matchfn(inc, '(?:/|$)') or under
94 incmatch = matchfn(inc, '(?:/|$)') or under
108 excmatch = matchfn(exc, '(?:/|$)') or (lambda fn: False)
95 excmatch = matchfn(exc, '(?:/|$)') or (lambda fn: False)
109
96
110 return lambda fn: (incmatch(fn) and not excmatch(fn) and
97 return lambda fn: (incmatch(fn) and not excmatch(fn) and
111 (fn.endswith('/') or patmatch(fn)))
98 (fn.endswith('/') or patmatch(fn)))
112
99
113 def system(cmd, errprefix=None):
100 def system(cmd, errprefix=None):
114 """execute a shell command that must succeed"""
101 """execute a shell command that must succeed"""
115 rc = os.system(cmd)
102 rc = os.system(cmd)
116 if rc:
103 if rc:
117 errmsg = "%s %s" % (os.path.basename(cmd.split(None, 1)[0]),
104 errmsg = "%s %s" % (os.path.basename(cmd.split(None, 1)[0]),
118 explain_exit(rc)[0])
105 explain_exit(rc)[0])
119 if errprefix:
106 if errprefix:
120 errmsg = "%s: %s" % (errprefix, errmsg)
107 errmsg = "%s: %s" % (errprefix, errmsg)
121 raise CommandError(errmsg)
108 raise CommandError(errmsg)
122
109
123 def rename(src, dst):
110 def rename(src, dst):
124 try:
111 try:
125 os.rename(src, dst)
112 os.rename(src, dst)
126 except:
113 except:
127 os.unlink(dst)
114 os.unlink(dst)
128 os.rename(src, dst)
115 os.rename(src, dst)
129
116
130 def copytree(src, dst, copyfile):
117 def copytree(src, dst, copyfile):
131 """Copy a directory tree, files are copied using 'copyfile'."""
118 """Copy a directory tree, files are copied using 'copyfile'."""
132 names = os.listdir(src)
119 names = os.listdir(src)
133 os.mkdir(dst)
120 os.mkdir(dst)
134
121
135 for name in names:
122 for name in names:
136 srcname = os.path.join(src, name)
123 srcname = os.path.join(src, name)
137 dstname = os.path.join(dst, name)
124 dstname = os.path.join(dst, name)
138 if os.path.isdir(srcname):
125 if os.path.isdir(srcname):
139 copytree(srcname, dstname, copyfile)
126 copytree(srcname, dstname, copyfile)
140 elif os.path.isfile(srcname):
127 elif os.path.isfile(srcname):
141 copyfile(srcname, dstname)
128 copyfile(srcname, dstname)
142 else:
129 else:
143 raise IOError("Not a regular file: %r" % srcname)
130 raise IOError("Not a regular file: %r" % srcname)
144
131
145 def _makelock_file(info, pathname):
132 def _makelock_file(info, pathname):
146 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
133 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
147 os.write(ld, info)
134 os.write(ld, info)
148 os.close(ld)
135 os.close(ld)
149
136
150 def _readlock_file(pathname):
137 def _readlock_file(pathname):
151 return file(pathname).read()
138 return file(pathname).read()
152
139
153 # Platfor specific varients
140 # Platfor specific varients
154 if os.name == 'nt':
141 if os.name == 'nt':
155 nulldev = 'NUL:'
142 nulldev = 'NUL:'
156
143
157 def is_exec(f, last):
144 def is_exec(f, last):
158 return last
145 return last
159
146
160 def set_exec(f, mode):
147 def set_exec(f, mode):
161 pass
148 pass
162
149
163 def pconvert(path):
150 def pconvert(path):
164 return path.replace("\\", "/")
151 return path.replace("\\", "/")
165
152
166 makelock = _makelock_file
153 makelock = _makelock_file
167 readlock = _readlock_file
154 readlock = _readlock_file
168
155
156 def explain_exit(code):
157 return "exited with status %d" % code, code
158
169 else:
159 else:
170 nulldev = '/dev/null'
160 nulldev = '/dev/null'
171
161
172 def is_exec(f, last):
162 def is_exec(f, last):
173 return (os.stat(f).st_mode & 0100 != 0)
163 return (os.stat(f).st_mode & 0100 != 0)
174
164
175 def set_exec(f, mode):
165 def set_exec(f, mode):
176 s = os.stat(f).st_mode
166 s = os.stat(f).st_mode
177 if (s & 0100 != 0) == mode:
167 if (s & 0100 != 0) == mode:
178 return
168 return
179 if mode:
169 if mode:
180 # Turn on +x for every +r bit when making a file executable
170 # Turn on +x for every +r bit when making a file executable
181 # and obey umask.
171 # and obey umask.
182 umask = os.umask(0)
172 umask = os.umask(0)
183 os.umask(umask)
173 os.umask(umask)
184 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
174 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
185 else:
175 else:
186 os.chmod(f, s & 0666)
176 os.chmod(f, s & 0666)
187
177
188 def pconvert(path):
178 def pconvert(path):
189 return path
179 return path
190
180
191 def makelock(info, pathname):
181 def makelock(info, pathname):
192 try:
182 try:
193 os.symlink(info, pathname)
183 os.symlink(info, pathname)
194 except OSError, why:
184 except OSError, why:
195 if why.errno == errno.EEXIST:
185 if why.errno == errno.EEXIST:
196 raise
186 raise
197 else:
187 else:
198 _makelock_file(info, pathname)
188 _makelock_file(info, pathname)
199
189
200 def readlock(pathname):
190 def readlock(pathname):
201 try:
191 try:
202 return os.readlink(pathname)
192 return os.readlink(pathname)
203 except OSError, why:
193 except OSError, why:
204 if why.errno == errno.EINVAL:
194 if why.errno == errno.EINVAL:
205 return _readlock_file(pathname)
195 return _readlock_file(pathname)
206 else:
196 else:
207 raise
197 raise
198
199 def explain_exit(code):
200 """return a 2-tuple (desc, code) describing a process's status"""
201 if os.WIFEXITED(code):
202 val = os.WEXITSTATUS(code)
203 return "exited with status %d" % val, val
204 elif os.WIFSIGNALED(code):
205 val = os.WTERMSIG(code)
206 return "killed by signal %d" % val, val
207 elif os.WIFSTOPPED(code):
208 val = os.STOPSIG(code)
209 return "stopped by signal %d" % val, val
210 raise ValueError("invalid exit code")
@@ -1,7 +1,7
1 <item>
1 <item>
2 <title>#desc|firstline|escape#</title>
2 <title>#desc|firstline|escape#</title>
3 <link>#url#?cmd=changeset;node=#node#</link>
3 <link>#url#?cmd=changeset;node=#node#</link>
4 <description>#desc|escape|addbreaks#</description>
4 <description><![CDATA[#desc|escape|addbreaks#]]></description>
5 <author>#author|obfuscate#</author>
5 <author>#author|obfuscate#</author>
6 <pubDate>#date|rfc822date#</pubDate>
6 <pubDate>#date|rfc822date#</pubDate>
7 </item>
7 </item>
@@ -1,7 +1,7
1 <item>
1 <item>
2 <title>#desc|firstline|escape#</title>
2 <title>#desc|firstline|escape#</title>
3 <link>#url#?cmd=file;file=#file#;filenode=#filenode#</link>
3 <link>#url#?cmd=file;file=#file#;filenode=#filenode#</link>
4 <description>#desc|escape|addbreaks#</description>
4 <description><![CDATA[#desc|escape|addbreaks#]]></description>
5 <author>#author|obfuscate#</author>
5 <author>#author|obfuscate#</author>
6 <pubDate>#date|rfc822date#</pubDate>>
6 <pubDate>#date|rfc822date#</pubDate>>
7 </item>
7 </item>
@@ -1,36 +1,37
1 A simple testing framework
1 A simple testing framework
2
2
3 To run the tests, do:
3 To run the tests, do:
4
4
5 cd tests/
5 cd tests/
6 ./run-tests
6 ./run-tests
7
7
8 This finds all scripts in the test directory named test-* and executes
8 This finds all scripts in the test directory named test-* and executes
9 them. The scripts can be either shell scripts or Python. Each test is
9 them. The scripts can be either shell scripts or Python. Each test is
10 run in a temporary directory that is removed when the test is complete.
10 run in a temporary directory that is removed when the test is complete.
11
11
12 A test-<x> succeeds if the script returns success and its output
12 A test-<x> succeeds if the script returns success and its output
13 matches test-<x>.out. If the new output doesn't match, it is stored in
13 matches test-<x>.out. If the new output doesn't match, it is stored in
14 test-<x>.err.
14 test-<x>.err.
15
15
16 There are some tricky points here that you should be aware of when
16 There are some tricky points here that you should be aware of when
17 writing tests:
17 writing tests:
18
18
19 - hg commit and hg up -m want user interaction
19 - hg commit and hg up -m want user interaction
20
20
21 for commit use -m "text"
21 for commit use -m "text"
22 for hg up -m, set HGMERGE to something noninteractive (like true or merge)
22 for hg up -m, set HGMERGE to something noninteractive (like true or merge)
23
23
24 - changeset hashes will change based on user and date which make
24 - changeset hashes will change based on user and date which make
25 things like hg history output change
25 things like hg history output change
26
26
27 use commit -m "test" -u test -d "0 0"
27 use commit -m "test" -u test -d "0 0"
28
28
29 - diff will show the current time
29 - diff will show the current time
30
30
31 use hg diff | sed "s/\(\(---\|+++\).*\)\t.*/\1/" to strip dates
31 use hg diff | sed "s/\(\(---\|+++\) [^ \t]*\)[ \t].*/\1/" to strip
32 dates
32
33
33 - set -x and pipelines don't generate stable output
34 - set -x and pipelines don't generate stable output
34
35
35 turn off set -x or break pipelines into pieces
36 turn off set -x or break pipelines into pieces
36
37
@@ -1,102 +1,102
1 #!/bin/sh -e
1 #!/bin/sh -e
2
2
3 export LANG=C
3 export LANG=C
4 export LC_CTYPE="C"
4 export LC_CTYPE="C"
5 export LC_NUMERIC="C"
5 export LC_NUMERIC="C"
6 export LC_TIME="C"
6 export LC_TIME="C"
7 export LC_COLLATE="C"
7 export LC_COLLATE="C"
8 export LC_MONETARY="C"
8 export LC_MONETARY="C"
9 export LC_MESSAGES="C"
9 export LC_MESSAGES="C"
10 export LC_PAPER="C"
10 export LC_PAPER="C"
11 export LC_NAME="C"
11 export LC_NAME="C"
12 export LC_ADDRESS="C"
12 export LC_ADDRESS="C"
13 export LC_TELEPHONE="C"
13 export LC_TELEPHONE="C"
14 export LC_MEASUREMENT="C"
14 export LC_MEASUREMENT="C"
15 export LC_IDENTIFICATION="C"
15 export LC_IDENTIFICATION="C"
16 export LC_ALL=""
16 export LC_ALL=""
17 export HGEDITOR=true
17 export HGEDITOR=true
18 export HGMERGE=true
18 export HGMERGE=true
19 export HGUSER=test
19 export HGUSER=test
20
20
21 umask 022
21 umask 022
22
22
23 tests=0
23 tests=0
24 failed=0
24 failed=0
25 H=$PWD
25 H=$PWD
26
26
27 if [ -d /usr/lib64 ]; then
27 if [ -d /usr/lib64 ]; then
28 lib=lib64
28 lib=lib64
29 else
29 else
30 lib=lib
30 lib=lib
31 fi
31 fi
32
32
33 TESTPATH=$PWD/install/bin
33 TESTPATH=$PWD/install/bin
34 export PATH=$TESTPATH:$PATH
34 export PATH=$TESTPATH:$PATH
35 export PYTHONPATH=$PWD/install/$lib/python
35 export PYTHONPATH=$PWD/install/$lib/python
36
36
37 rm -rf install
37 rm -rf install
38 cd ..
38 cd ..
39 ${PYTHON:-python} setup.py install --home=tests/install > tests/install.err
39 ${PYTHON:-python} setup.py install --home=tests/install > tests/install.err
40 if [ $? != 0 ] ; then
40 if [ $? != 0 ] ; then
41 cat tests/install.err
41 cat tests/install.err
42 fi
42 fi
43 cd $H
43 cd $H
44 rm install.err
44 rm install.err
45
45
46 function run_one
46 function run_one
47 {
47 {
48 rm -f $1.err
48 rm -f $1.err
49 export TZ=GMT
49 export TZ=GMT
50 D=`mktemp -d`
50 D=`mktemp -d ${TMP-/tmp}/tmp.XXXXXX`
51 if [ "$D" = "" ] ; then
51 if [ "$D" = "" ] ; then
52 echo mktemp failed!
52 echo mktemp failed!
53 fi
53 fi
54
54
55 cd $D
55 cd $D
56 fail=0
56 fail=0
57 export HOME=$D
57 export HOME=$D
58
58
59 if ! $H/$1 > .out 2>&1 ; then
59 if ! $H/$1 > .out 2>&1 ; then
60 echo $1 failed with error code $?
60 echo $1 failed with error code $?
61 fail=1
61 fail=1
62 fi
62 fi
63
63
64 if [ -s .out -a ! -r $H/$1.out ] ; then
64 if [ -s .out -a ! -r $H/$1.out ] ; then
65 echo $1 generated unexpected output:
65 echo $1 generated unexpected output:
66 cat .out
66 cat .out
67 cp .out $H/$1.err
67 cp .out $H/$1.err
68 fail=1
68 fail=1
69 elif [ -r $H/$1.out ] && ! diff -u $H/$1.out .out > /dev/null ; then
69 elif [ -r $H/$1.out ] && ! diff -u $H/$1.out .out > /dev/null ; then
70 echo $1 output changed:
70 echo $1 output changed:
71 diff -u $H/$1.out .out && true
71 diff -u $H/$1.out .out && true
72 cp .out $H/$1.err
72 cp .out $H/$1.err
73 fail=1
73 fail=1
74 fi
74 fi
75
75
76 cd $H
76 cd $H
77 rm -r $D
77 rm -r $D
78 return $fail
78 return $fail
79 }
79 }
80
80
81 TESTS=$@
81 TESTS=$@
82 if [ "$TESTS" = "" ] ; then
82 if [ "$TESTS" = "" ] ; then
83 TESTS=`ls test-* | grep -Ev "\.|~"`
83 TESTS=`ls test-* | grep -Ev "\.|~"`
84 fi
84 fi
85
85
86 for f in $TESTS ; do
86 for f in $TESTS ; do
87 echo -n "."
87 echo -n "."
88 if ! run_one $f ; then
88 if ! run_one $f ; then
89 failed=$[$failed + 1]
89 failed=$[$failed + 1]
90 fi
90 fi
91 tests=$[$tests + 1]
91 tests=$[$tests + 1]
92 done
92 done
93
93
94 rm -rf install
94 rm -rf install
95
95
96 echo
96 echo
97 echo Ran $tests tests, $failed failed
97 echo Ran $tests tests, $failed failed
98
98
99 if [ $failed -gt 0 ] ; then
99 if [ $failed -gt 0 ] ; then
100 exit 1
100 exit 1
101 fi
101 fi
102
102
@@ -1,15 +1,15
1 + hg clone http://localhost:20059/ copy
1 + hg clone http://localhost:20059/ copy
2 requesting all changes
2 requesting all changes
3 abort: error 111: Connection refused
3 abort: error: Connection refused
4 + echo 255
4 + echo 255
5 255
5 255
6 + ls copy
6 + ls copy
7 ls: copy: No such file or directory
7 ls: copy: No such file or directory
8 + cat
8 + cat
9 + set +x
9 + set +x
10 + hg clone http://localhost:20059/foo copy2
10 + hg clone http://localhost:20059/foo copy2
11 requesting all changes
11 requesting all changes
12 abort: HTTP Error 404: File not found
12 abort: HTTP Error 404: File not found
13 + echo 255
13 + echo 255
14 255
14 255
15 + set +x
15 + set +x
@@ -1,23 +1,23
1 + mkdir t
1 + mkdir t
2 + cd t
2 + cd t
3 + hg init
3 + hg init
4 + echo a
4 + echo a
5 + hg add a
5 + hg add a
6 + hg commit -m test -d '0 0'
6 + hg commit -m test -d '0 0'
7 + hg history
7 + hg history
8 changeset: 0:acb14030fe0a21b60322c440ad2d20cf7685a376
8 changeset: 0:acb14030fe0a
9 tag: tip
9 tag: tip
10 user: test
10 user: test
11 date: Thu Jan 1 00:00:00 1970
11 date: Thu Jan 1 00:00:00 1970
12 summary: test
12 summary: test
13
13
14 + hg manifest
14 + hg manifest
15 b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3 644 a
15 b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3 644 a
16 + hg cat a
16 + hg cat a
17 a
17 a
18 + hg verify
18 + hg verify
19 checking changesets
19 checking changesets
20 checking manifests
20 checking manifests
21 crosschecking files in changesets and manifests
21 crosschecking files in changesets and manifests
22 checking files
22 checking files
23 1 files, 1 changesets, 1 total revisions
23 1 files, 1 changesets, 1 total revisions
@@ -1,39 +1,39
1 + hg clone a b
1 + hg clone a b
2 abort: repository a/.hg not found!
2 abort: repository a/.hg not found!
3 + echo 255
3 + echo 255
4 255
4 255
5 + hg clone http://127.0.0.1:3121/a b
5 + hg clone http://127.0.0.1:3121/a b
6 requesting all changes
6 requesting all changes
7 abort: error 111: Connection refused
7 abort: error: Connection refused
8 + echo 255
8 + echo 255
9 255
9 255
10 + rm -rf b
10 + rm -rf b
11 + mkdir a
11 + mkdir a
12 + chmod 000 a
12 + chmod 000 a
13 + hg clone a b
13 + hg clone a b
14 abort: repository a/.hg not found!
14 abort: repository a/.hg not found!
15 + echo 255
15 + echo 255
16 255
16 255
17 + mkdir b
17 + mkdir b
18 + cd b
18 + cd b
19 + hg init
19 + hg init
20 + hg clone . ../a
20 + hg clone . ../a
21 abort: destination '../a' already exists
21 abort: destination '../a' already exists
22 + echo 1
22 + echo 1
23 1
23 1
24 + cd ..
24 + cd ..
25 + chmod 700 a
25 + chmod 700 a
26 + rm -rf a b
26 + rm -rf a b
27 + mkfifo a
27 + mkfifo a
28 + hg clone a b
28 + hg clone a b
29 abort: repository a/.hg not found!
29 abort: repository a/.hg not found!
30 + echo 255
30 + echo 255
31 255
31 255
32 + rm a
32 + rm a
33 + mkdir q
33 + mkdir q
34 + cd q
34 + cd q
35 + hg init
35 + hg init
36 + cd ..
36 + cd ..
37 + hg clone q
37 + hg clone q
38 abort: destination 'q' already exists
38 abort: destination 'q' already exists
39 + true
39 + true
@@ -1,51 +1,51
1 + hg init
1 + hg init
2 + echo a
2 + echo a
3 + hg add a
3 + hg add a
4 + hg commit -m 1 -d '0 0'
4 + hg commit -m 1 -d '0 0'
5 + hg status
5 + hg status
6 ? .out
6 ? .out
7 + cp a b
7 + cp a b
8 + hg copy a b
8 + hg copy a b
9 + hg status
9 + hg status
10 A b
10 A b
11 ? .out
11 ? .out
12 + hg --debug commit -m 2 -d '0 0'
12 + hg --debug commit -m 2 -d '0 0'
13 b
13 b
14 b: copy a:b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3
14 b: copy a:b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3
15 + hg history
15 + hg history
16 changeset: 1:3b5b84850bbed12e8ff8c1b87b32dc93c59ae6d8
16 changeset: 1:3b5b84850bbe
17 tag: tip
17 tag: tip
18 user: test
18 user: test
19 date: Thu Jan 1 00:00:00 1970
19 date: Thu Jan 1 00:00:00 1970
20 summary: 2
20 summary: 2
21
21
22 changeset: 0:c19d34741b0a4ced8e4ba74bb834597d5193851e
22 changeset: 0:c19d34741b0a
23 user: test
23 user: test
24 date: Thu Jan 1 00:00:00 1970
24 date: Thu Jan 1 00:00:00 1970
25 summary: 1
25 summary: 1
26
26
27 + hg log a
27 + hg log a
28 changeset: 0:c19d34741b0a4ced8e4ba74bb834597d5193851e
28 changeset: 0:c19d34741b0a
29 user: test
29 user: test
30 date: Thu Jan 1 00:00:00 1970
30 date: Thu Jan 1 00:00:00 1970
31 summary: 1
31 summary: 1
32
32
33 + hexdump -C .hg/data/b.d
33 + hexdump -C .hg/data/b.d
34 00000000 75 01 0a 63 6f 70 79 72 65 76 3a 20 62 37 38 39 |u..copyrev: b789|
34 00000000 75 01 0a 63 6f 70 79 72 65 76 3a 20 62 37 38 39 |u..copyrev: b789|
35 00000010 66 64 64 39 36 64 63 32 66 33 62 64 32 32 39 63 |fdd96dc2f3bd229c|
35 00000010 66 64 64 39 36 64 63 32 66 33 62 64 32 32 39 63 |fdd96dc2f3bd229c|
36 00000020 31 64 64 38 65 65 64 66 30 66 63 36 30 65 32 62 |1dd8eedf0fc60e2b|
36 00000020 31 64 64 38 65 65 64 66 30 66 63 36 30 65 32 62 |1dd8eedf0fc60e2b|
37 00000030 36 38 65 33 0a 63 6f 70 79 3a 20 61 0a 01 0a 61 |68e3.copy: a...a|
37 00000030 36 38 65 33 0a 63 6f 70 79 3a 20 61 0a 01 0a 61 |68e3.copy: a...a|
38 00000040 0a |.|
38 00000040 0a |.|
39 00000041
39 00000041
40 + hg cat b
40 + hg cat b
41 + md5sum bsum
41 + md5sum bsum
42 60b725f10c9c85c70d97880dfe8191b3 bsum
42 60b725f10c9c85c70d97880dfe8191b3 bsum
43 + hg cat a
43 + hg cat a
44 + md5sum asum
44 + md5sum asum
45 60b725f10c9c85c70d97880dfe8191b3 asum
45 60b725f10c9c85c70d97880dfe8191b3 asum
46 + hg verify
46 + hg verify
47 checking changesets
47 checking changesets
48 checking manifests
48 checking manifests
49 crosschecking files in changesets and manifests
49 crosschecking files in changesets and manifests
50 checking files
50 checking files
51 2 files, 2 changesets, 2 total revisions
51 2 files, 2 changesets, 2 total revisions
@@ -1,12 +1,12
1 #!/bin/sh
1 #!/bin/sh
2
2
3 hg init
3 hg init
4 touch a
4 touch a
5 hg add a
5 hg add a
6 hg ci -m "a" -d "0 0"
6 hg ci -m "a" -d "0 0"
7
7
8 echo 123 > b
8 echo 123 > b
9 hg add b
9 hg add b
10 hg diff | sed "s/\(\(---\|+++\).*\)\t.*/\1/"
10 hg diff | sed "s/\(\(---\|+++\) [^ \t]*\)[ \t].*/\1/"
11
11
12 hg diff -r tip | sed "s/\(\(---\|+++\).*\)\t.*/\1/"
12 hg diff -r tip | sed "s/\(\(---\|+++\) [^ \t]*\)[ \t].*/\1/"
@@ -1,72 +1,72
1 + umask 027
1 + umask 027
2 + mkdir test1
2 + mkdir test1
3 + cd test1
3 + cd test1
4 + hg init
4 + hg init
5 + touch a b
5 + touch a b
6 + hg add a b
6 + hg add a b
7 + hg ci -m 'added a b' -d '0 0'
7 + hg ci -m 'added a b' -d '0 0'
8 + cd ..
8 + cd ..
9 + mkdir test2
9 + mkdir test2
10 + cd test2
10 + cd test2
11 + hg init
11 + hg init
12 + hg pull ../test1
12 + hg pull ../test1
13 pulling from ../test1
13 pulling from ../test1
14 requesting all changes
14 requesting all changes
15 adding changesets
15 adding changesets
16 adding manifests
16 adding manifests
17 adding file revisions
17 adding file changes
18 modified 2 files, added 1 changesets and 2 new revisions
18 added 1 changesets with 2 changes to 2 files
19 (run 'hg update' to get a working copy)
19 (run 'hg update' to get a working copy)
20 + hg co
20 + hg co
21 + chmod +x a
21 + chmod +x a
22 + hg ci -m 'chmod +x a' -d '0 0'
22 + hg ci -m 'chmod +x a' -d '0 0'
23 + cd ../test1
23 + cd ../test1
24 + echo 123
24 + echo 123
25 + hg ci -m 'a updated' -d '0 0'
25 + hg ci -m 'a updated' -d '0 0'
26 + hg pull ../test2
26 + hg pull ../test2
27 pulling from ../test2
27 pulling from ../test2
28 searching for changes
28 searching for changes
29 adding changesets
29 adding changesets
30 adding manifests
30 adding manifests
31 adding file revisions
31 adding file changes
32 modified 1 files, added 1 changesets and 1 new revisions
32 added 1 changesets with 1 changes to 1 files
33 (run 'hg update' to get a working copy)
33 (run 'hg update' to get a working copy)
34 + hg heads
34 + hg heads
35 changeset: 2:3ef54330565526bebf37a0d9bf540c283fd133a1
35 changeset: 2:3ef543305655
36 tag: tip
36 tag: tip
37 parent: 0:22a449e20da501ca558394c083ca470e9c81b9f7
37 parent: 0:22a449e20da5
38 user: test
38 user: test
39 date: Thu Jan 1 00:00:00 1970
39 date: Thu Jan 1 00:00:00 1970
40 summary: chmod +x a
40 summary: chmod +x a
41
41
42 changeset: 1:c6ecefc45368ed556d965f1c1086c6561a8b2ac5
42 changeset: 1:c6ecefc45368
43 user: test
43 user: test
44 date: Thu Jan 1 00:00:00 1970
44 date: Thu Jan 1 00:00:00 1970
45 summary: a updated
45 summary: a updated
46
46
47 + hg history
47 + hg history
48 changeset: 2:3ef54330565526bebf37a0d9bf540c283fd133a1
48 changeset: 2:3ef543305655
49 tag: tip
49 tag: tip
50 parent: 0:22a449e20da501ca558394c083ca470e9c81b9f7
50 parent: 0:22a449e20da5
51 user: test
51 user: test
52 date: Thu Jan 1 00:00:00 1970
52 date: Thu Jan 1 00:00:00 1970
53 summary: chmod +x a
53 summary: chmod +x a
54
54
55 changeset: 1:c6ecefc45368ed556d965f1c1086c6561a8b2ac5
55 changeset: 1:c6ecefc45368
56 user: test
56 user: test
57 date: Thu Jan 1 00:00:00 1970
57 date: Thu Jan 1 00:00:00 1970
58 summary: a updated
58 summary: a updated
59
59
60 changeset: 0:22a449e20da501ca558394c083ca470e9c81b9f7
60 changeset: 0:22a449e20da5
61 user: test
61 user: test
62 date: Thu Jan 1 00:00:00 1970
62 date: Thu Jan 1 00:00:00 1970
63 summary: added a b
63 summary: added a b
64
64
65 + hg -v co -m
65 + hg -v co -m
66 resolving manifests
66 resolving manifests
67 merging a
67 merging a
68 resolving a
68 resolving a
69 + ls -l ../test1/a ../test2/a
69 + ls -l ../test1/a ../test2/a
70 + cut -b 0-10
70 + cut -b 0-10
71 -rwxr-x---
71 -rwxr-x---
72 -rwxr-x---
72 -rwxr-x---
@@ -1,10 +1,10
1 0
1 0
2 0
2 0
3 adding changesets
3 adding changesets
4 killed!
4 killed!
5 transaction abort!
5 transaction abort!
6 rollback completed
6 rollback completed
7 00changelog.d
7 00changelog.d
8 00changelog.i
8 00changelog.i
9 data
9 data
10 undo.dirstate
10 journal.dirstate
@@ -1,53 +1,53
1 + cat
1 + cat
2 + chmod +x merge
2 + chmod +x merge
3 + export HGMERGE=./merge
3 + export HGMERGE=./merge
4 + HGMERGE=./merge
4 + HGMERGE=./merge
5 + mkdir A1
5 + mkdir A1
6 + cd A1
6 + cd A1
7 + hg init
7 + hg init
8 + echo This is file foo1
8 + echo This is file foo1
9 + echo This is file bar1
9 + echo This is file bar1
10 + hg add foo bar
10 + hg add foo bar
11 + hg commit -m 'commit text' -d '0 0'
11 + hg commit -m 'commit text' -d '0 0'
12 + cd ..
12 + cd ..
13 + hg clone A1 B1
13 + hg clone A1 B1
14 + cd A1
14 + cd A1
15 + rm bar
15 + rm bar
16 + hg remove bar
16 + hg remove bar
17 + hg commit -m 'commit test' -d '0 0'
17 + hg commit -m 'commit test' -d '0 0'
18 + cd ../B1
18 + cd ../B1
19 + echo This is file foo22
19 + echo This is file foo22
20 + hg commit -m 'commit test' -d '0 0'
20 + hg commit -m 'commit test' -d '0 0'
21 + cd ..
21 + cd ..
22 + hg clone A1 A2
22 + hg clone A1 A2
23 + hg clone B1 B2
23 + hg clone B1 B2
24 + cd A1
24 + cd A1
25 + hg pull ../B1
25 + hg pull ../B1
26 pulling from ../B1
26 pulling from ../B1
27 searching for changes
27 searching for changes
28 adding changesets
28 adding changesets
29 adding manifests
29 adding manifests
30 adding file revisions
30 adding file changes
31 modified 1 files, added 1 changesets and 1 new revisions
31 added 1 changesets with 1 changes to 1 files
32 (run 'hg update' to get a working copy)
32 (run 'hg update' to get a working copy)
33 + hg update -m
33 + hg update -m
34 + hg commit -m 'commit test' -d '0 0'
34 + hg commit -m 'commit test' -d '0 0'
35 + echo bar should remain deleted.
35 + echo bar should remain deleted.
36 bar should remain deleted.
36 bar should remain deleted.
37 + hg manifest
37 + hg manifest
38 6b70e9e451a5a33faad7bbebe627e46b937b7364 644 foo
38 6b70e9e451a5a33faad7bbebe627e46b937b7364 644 foo
39 + cd ../B2
39 + cd ../B2
40 + hg pull ../A2
40 + hg pull ../A2
41 pulling from ../A2
41 pulling from ../A2
42 searching for changes
42 searching for changes
43 adding changesets
43 adding changesets
44 adding manifests
44 adding manifests
45 adding file revisions
45 adding file changes
46 modified 0 files, added 1 changesets and 0 new revisions
46 added 1 changesets with 0 changes to 0 files
47 (run 'hg update' to get a working copy)
47 (run 'hg update' to get a working copy)
48 + hg update -m
48 + hg update -m
49 + hg commit -m 'commit test' -d '0 0'
49 + hg commit -m 'commit test' -d '0 0'
50 + echo bar should remain deleted.
50 + echo bar should remain deleted.
51 bar should remain deleted.
51 bar should remain deleted.
52 + hg manifest
52 + hg manifest
53 6b70e9e451a5a33faad7bbebe627e46b937b7364 644 foo
53 6b70e9e451a5a33faad7bbebe627e46b937b7364 644 foo
@@ -1,22 +1,22
1 adding foo
1 adding foo
2 checking changesets
2 checking changesets
3 checking manifests
3 checking manifests
4 crosschecking files in changesets and manifests
4 crosschecking files in changesets and manifests
5 checking files
5 checking files
6 1 files, 1 changesets, 1 total revisions
6 1 files, 1 changesets, 1 total revisions
7 requesting all changes
7 requesting all changes
8 adding changesets
8 adding changesets
9 adding manifests
9 adding manifests
10 adding file revisions
10 adding file changes
11 modified 1 files, added 1 changesets and 1 new revisions
11 added 1 changesets with 1 changes to 1 files
12 checking changesets
12 checking changesets
13 checking manifests
13 checking manifests
14 crosschecking files in changesets and manifests
14 crosschecking files in changesets and manifests
15 checking files
15 checking files
16 1 files, 1 changesets, 1 total revisions
16 1 files, 1 changesets, 1 total revisions
17 foo
17 foo
18 2ed2a3912a0b24502043eae84ee4b279c18b90dd 644 foo
18 2ed2a3912a0b24502043eae84ee4b279c18b90dd 644 foo
19 pulling from http://localhost:20059/
19 pulling from http://localhost:20059/
20 searching for changes
20 searching for changes
21 no changes found
21 no changes found
22 killed!
22 killed!
@@ -1,83 +1,83
1 + hg --debug init
1 + hg --debug init
2 + echo this is a1
2 + echo this is a1
3 + hg add a
3 + hg add a
4 + hg commit -m0 -d '0 0'
4 + hg commit -m0 -d '0 0'
5 + echo this is b1
5 + echo this is b1
6 + hg add b
6 + hg add b
7 + hg commit -m1 -d '0 0'
7 + hg commit -m1 -d '0 0'
8 + hg manifest 1
8 + hg manifest 1
9 05f9e54f4c9b86b09099803d8b49a50edcb4eaab 644 a
9 05f9e54f4c9b86b09099803d8b49a50edcb4eaab 644 a
10 54837d97f2932a8194e69745a280a2c11e61ff9c 644 b
10 54837d97f2932a8194e69745a280a2c11e61ff9c 644 b
11 + echo this is c1
11 + echo this is c1
12 + hg rawcommit -p 1 -d '0 0' -m2 c
12 + hg rawcommit -p 1 -d '0 0' -m2 c
13 + hg manifest 2
13 + hg manifest 2
14 05f9e54f4c9b86b09099803d8b49a50edcb4eaab 644 a
14 05f9e54f4c9b86b09099803d8b49a50edcb4eaab 644 a
15 54837d97f2932a8194e69745a280a2c11e61ff9c 644 b
15 54837d97f2932a8194e69745a280a2c11e61ff9c 644 b
16 76d5e637cbec1bcc04a5a3fa4bcc7d13f6847c00 644 c
16 76d5e637cbec1bcc04a5a3fa4bcc7d13f6847c00 644 c
17 + hg parents
17 + hg parents
18 changeset: 2:9f827976dae422d883af3cedc7a849c3e41a9b96
18 changeset: 2:9f827976dae4
19 tag: tip
19 tag: tip
20 user: test
20 user: test
21 date: Thu Jan 1 00:00:00 1970
21 date: Thu Jan 1 00:00:00 1970
22 summary: 2
22 summary: 2
23
23
24 + rm b
24 + rm b
25 + hg rawcommit -p 2 -d '0 0' -m3 b
25 + hg rawcommit -p 2 -d '0 0' -m3 b
26 + hg manifest 3
26 + hg manifest 3
27 05f9e54f4c9b86b09099803d8b49a50edcb4eaab 644 a
27 05f9e54f4c9b86b09099803d8b49a50edcb4eaab 644 a
28 76d5e637cbec1bcc04a5a3fa4bcc7d13f6847c00 644 c
28 76d5e637cbec1bcc04a5a3fa4bcc7d13f6847c00 644 c
29 + hg parents
29 + hg parents
30 changeset: 3:c8225a10618652ed2048e5ec0e917a92e50b9032
30 changeset: 3:c8225a106186
31 tag: tip
31 tag: tip
32 user: test
32 user: test
33 date: Thu Jan 1 00:00:00 1970
33 date: Thu Jan 1 00:00:00 1970
34 summary: 3
34 summary: 3
35
35
36 + echo this is a22
36 + echo this is a22
37 + hg rawcommit -p 3 -d '0 0' -m4 a
37 + hg rawcommit -p 3 -d '0 0' -m4 a
38 + hg manifest 4
38 + hg manifest 4
39 d6e3c4976c13feb1728cd3ac851abaf7256a5c23 644 a
39 d6e3c4976c13feb1728cd3ac851abaf7256a5c23 644 a
40 76d5e637cbec1bcc04a5a3fa4bcc7d13f6847c00 644 c
40 76d5e637cbec1bcc04a5a3fa4bcc7d13f6847c00 644 c
41 + hg parents
41 + hg parents
42 changeset: 4:8dfeee82a94bbe13e5f3ca5eee08058269af87c1
42 changeset: 4:8dfeee82a94b
43 tag: tip
43 tag: tip
44 user: test
44 user: test
45 date: Thu Jan 1 00:00:00 1970
45 date: Thu Jan 1 00:00:00 1970
46 summary: 4
46 summary: 4
47
47
48 + echo this is c22
48 + echo this is c22
49 + hg rawcommit -p 1 -d '0 0' -m5 c
49 + hg rawcommit -p 1 -d '0 0' -m5 c
50 + hg manifest 5
50 + hg manifest 5
51 05f9e54f4c9b86b09099803d8b49a50edcb4eaab 644 a
51 05f9e54f4c9b86b09099803d8b49a50edcb4eaab 644 a
52 54837d97f2932a8194e69745a280a2c11e61ff9c 644 b
52 54837d97f2932a8194e69745a280a2c11e61ff9c 644 b
53 3570202ceac2b52517df64ebd0a062cb0d8fe33a 644 c
53 3570202ceac2b52517df64ebd0a062cb0d8fe33a 644 c
54 + hg parents
54 + hg parents
55 changeset: 4:8dfeee82a94bbe13e5f3ca5eee08058269af87c1
55 changeset: 4:8dfeee82a94b
56 user: test
56 user: test
57 date: Thu Jan 1 00:00:00 1970
57 date: Thu Jan 1 00:00:00 1970
58 summary: 4
58 summary: 4
59
59
60 + hg rawcommit -p 4 -p 5 -d '0 0' -m6
60 + hg rawcommit -p 4 -p 5 -d '0 0' -m6
61 + hg manifest 6
61 + hg manifest 6
62 d6e3c4976c13feb1728cd3ac851abaf7256a5c23 644 a
62 d6e3c4976c13feb1728cd3ac851abaf7256a5c23 644 a
63 76d5e637cbec1bcc04a5a3fa4bcc7d13f6847c00 644 c
63 76d5e637cbec1bcc04a5a3fa4bcc7d13f6847c00 644 c
64 + hg parents
64 + hg parents
65 changeset: 6:c0e932ecae5eb7d8d2af2659f3ab03dbe4a9ff7c
65 changeset: 6:c0e932ecae5e
66 tag: tip
66 tag: tip
67 parent: 4:8dfeee82a94bbe13e5f3ca5eee08058269af87c1
67 parent: 4:8dfeee82a94b
68 parent: 5:a7925a42d0df7b35e14ecd7bf12ed6bbc776e9df
68 parent: 5:a7925a42d0df
69 user: test
69 user: test
70 date: Thu Jan 1 00:00:00 1970
70 date: Thu Jan 1 00:00:00 1970
71 summary: 6
71 summary: 6
72
72
73 + hg rawcommit -p 6 -d '0 0' -m7
73 + hg rawcommit -p 6 -d '0 0' -m7
74 + hg manifest 7
74 + hg manifest 7
75 d6e3c4976c13feb1728cd3ac851abaf7256a5c23 644 a
75 d6e3c4976c13feb1728cd3ac851abaf7256a5c23 644 a
76 76d5e637cbec1bcc04a5a3fa4bcc7d13f6847c00 644 c
76 76d5e637cbec1bcc04a5a3fa4bcc7d13f6847c00 644 c
77 + hg parents
77 + hg parents
78 changeset: 7:3a157da4365dc1966cf9a032b0113fd8613d7865
78 changeset: 7:3a157da4365d
79 tag: tip
79 tag: tip
80 user: test
80 user: test
81 date: Thu Jan 1 00:00:00 1970
81 date: Thu Jan 1 00:00:00 1970
82 summary: 7
82 summary: 7
83
83
@@ -1,39 +1,39
1 + mkdir test
1 + mkdir test
2 + cd test
2 + cd test
3 + echo foo
3 + echo foo
4 + hg init
4 + hg init
5 + hg addremove
5 + hg addremove
6 adding foo
6 adding foo
7 + hg commit -m 1
7 + hg commit -m 1
8 + hg verify
8 + hg verify
9 checking changesets
9 checking changesets
10 checking manifests
10 checking manifests
11 crosschecking files in changesets and manifests
11 crosschecking files in changesets and manifests
12 checking files
12 checking files
13 1 files, 1 changesets, 1 total revisions
13 1 files, 1 changesets, 1 total revisions
14 + hg clone . ../branch
14 + hg clone . ../branch
15 + cd ../branch
15 + cd ../branch
16 + hg co
16 + hg co
17 + echo bar
17 + echo bar
18 + hg commit -m 2
18 + hg commit -m 2
19 + cd ../test
19 + cd ../test
20 + hg pull ../branch
20 + hg pull ../branch
21 pulling from ../branch
21 pulling from ../branch
22 searching for changes
22 searching for changes
23 adding changesets
23 adding changesets
24 adding manifests
24 adding manifests
25 adding file revisions
25 adding file changes
26 modified 1 files, added 1 changesets and 1 new revisions
26 added 1 changesets with 1 changes to 1 files
27 (run 'hg update' to get a working copy)
27 (run 'hg update' to get a working copy)
28 + hg verify
28 + hg verify
29 checking changesets
29 checking changesets
30 checking manifests
30 checking manifests
31 crosschecking files in changesets and manifests
31 crosschecking files in changesets and manifests
32 checking files
32 checking files
33 1 files, 2 changesets, 2 total revisions
33 1 files, 2 changesets, 2 total revisions
34 + hg co
34 + hg co
35 + cat foo
35 + cat foo
36 foo
36 foo
37 bar
37 bar
38 + hg manifest
38 + hg manifest
39 6f4310b00b9a147241b071a60c28a650827fb03d 644 foo
39 6f4310b00b9a147241b071a60c28a650827fb03d 644 foo
@@ -1,31 +1,31
1 + hg init
1 + hg init
2 + echo a
2 + echo a
3 + hg add a
3 + hg add a
4 + hg commit -m test -d '0 0'
4 + hg commit -m test -d '0 0'
5 + hg history
5 + hg history
6 changeset: 0:acb14030fe0a21b60322c440ad2d20cf7685a376
6 changeset: 0:acb14030fe0a
7 tag: tip
7 tag: tip
8 user: test
8 user: test
9 date: Thu Jan 1 00:00:00 1970
9 date: Thu Jan 1 00:00:00 1970
10 summary: test
10 summary: test
11
11
12 + hg tag -d '0 0' bleah
12 + hg tag -d '0 0' bleah
13 + hg history
13 + hg history
14 changeset: 1:863197ef03781c4fc00276d83eb66c4cb9cd91df
14 changeset: 1:863197ef0378
15 tag: tip
15 tag: tip
16 user: test
16 user: test
17 date: Thu Jan 1 00:00:00 1970
17 date: Thu Jan 1 00:00:00 1970
18 summary: Added tag bleah for changeset acb14030fe0a21b60322c440ad2d20cf7685a376
18 summary: Added tag bleah for changeset acb14030fe0a21b60322c440ad2d20cf7685a376
19
19
20 changeset: 0:acb14030fe0a21b60322c440ad2d20cf7685a376
20 changeset: 0:acb14030fe0a
21 tag: bleah
21 tag: bleah
22 user: test
22 user: test
23 date: Thu Jan 1 00:00:00 1970
23 date: Thu Jan 1 00:00:00 1970
24 summary: test
24 summary: test
25
25
26 + echo foo
26 + echo foo
27 + hg tag -d '0 0' bleah2
27 + hg tag -d '0 0' bleah2
28 abort: working copy of .hgtags is changed!
28 abort: working copy of .hgtags is changed!
29 (please commit .hgtags manually)
29 (please commit .hgtags manually)
30 + echo failed
30 + echo failed
31 failed
31 failed
@@ -1,31 +1,31
1 + mkdir t
1 + mkdir t
2 + cd t
2 + cd t
3 + hg init
3 + hg init
4 + echo a
4 + echo a
5 + hg add a
5 + hg add a
6 + hg commit -m test -d '0 0'
6 + hg commit -m test -d '0 0'
7 + hg verify
7 + hg verify
8 checking changesets
8 checking changesets
9 checking manifests
9 checking manifests
10 crosschecking files in changesets and manifests
10 crosschecking files in changesets and manifests
11 checking files
11 checking files
12 1 files, 1 changesets, 1 total revisions
12 1 files, 1 changesets, 1 total revisions
13 + hg parents
13 + hg parents
14 changeset: 0:acb14030fe0a21b60322c440ad2d20cf7685a376
14 changeset: 0:acb14030fe0a
15 tag: tip
15 tag: tip
16 user: test
16 user: test
17 date: Thu Jan 1 00:00:00 1970
17 date: Thu Jan 1 00:00:00 1970
18 summary: test
18 summary: test
19
19
20 + hg status
20 + hg status
21 + hg undo
21 + hg undo
22 rolling back last transaction
22 rolling back last transaction
23 + hg verify
23 + hg verify
24 checking changesets
24 checking changesets
25 checking manifests
25 checking manifests
26 crosschecking files in changesets and manifests
26 crosschecking files in changesets and manifests
27 checking files
27 checking files
28 0 files, 0 changesets, 0 total revisions
28 0 files, 0 changesets, 0 total revisions
29 + hg parents
29 + hg parents
30 + hg status
30 + hg status
31 A a
31 A a
@@ -1,19 +1,19
1 pulling from ../a
1 pulling from ../a
2 searching for changes
2 searching for changes
3 warning: pulling from an unrelated repository!
3 warning: pulling from an unrelated repository!
4 adding changesets
4 adding changesets
5 adding manifests
5 adding manifests
6 adding file revisions
6 adding file changes
7 modified 1 files, added 1 changesets and 1 new revisions
7 added 1 changesets with 1 changes to 1 files
8 (run 'hg update' to get a working copy)
8 (run 'hg update' to get a working copy)
9 changeset: 1:9a79c33a9db37480e40fbd2a65d62ebd2a3c441c
9 changeset: 1:9a79c33a9db3
10 tag: tip
10 tag: tip
11 user: a
11 user: a
12 date: Thu Jan 1 00:00:00 1970
12 date: Thu Jan 1 00:00:00 1970
13 summary: a
13 summary: a
14
14
15 changeset: 0:01f8062b2de51c0fa6428c5db1d1b3ea780189df
15 changeset: 0:01f8062b2de5
16 user: b
16 user: b
17 date: Thu Jan 1 00:00:00 1970
17 date: Thu Jan 1 00:00:00 1970
18 summary: b
18 summary: b
19
19
@@ -1,33 +1,33
1 #!/bin/sh
1 #!/bin/sh
2
2
3 set -ex
3 set -ex
4 mkdir r1
4 mkdir r1
5 cd r1
5 cd r1
6 hg init
6 hg init
7 echo a > a
7 echo a > a
8 hg addremove
8 hg addremove
9 hg commit -m "1" -d "0 0"
9 hg commit -m "1" -d "0 0"
10
10
11 hg clone . ../r2
11 hg clone . ../r2
12 cd ../r2
12 cd ../r2
13 hg up
13 hg up
14 echo abc > a
14 echo abc > a
15 hg diff > ../d
15 hg diff > ../d
16 sed "s/\(\(---\|+++\).*\)\t.*/\1/" < ../d
16 sed "s/\(\(---\|+++\) [^ \t]*\)[ \t].*/\1/" < ../d
17
17
18 cd ../r1
18 cd ../r1
19 echo b > b
19 echo b > b
20 echo a2 > a
20 echo a2 > a
21 hg addremove
21 hg addremove
22 hg commit -m "2" -d "0 0"
22 hg commit -m "2" -d "0 0"
23
23
24 cd ../r2
24 cd ../r2
25 hg -q pull ../r1
25 hg -q pull ../r1
26 hg status
26 hg status
27 hg --debug up
27 hg --debug up
28 hg --debug up -m
28 hg --debug up -m
29 hg parents
29 hg parents
30 hg -v history
30 hg -v history
31 hg diff > ../d
31 hg diff > ../d
32 sed "s/\(\(---\|+++\).*\)\t.*/\1/" < ../d
32 sed "s/\(\(---\|+++\) [^ \t]*\)[ \t].*/\1/" < ../d
33
33
@@ -1,78 +1,76
1 + mkdir r1
1 + mkdir r1
2 + cd r1
2 + cd r1
3 + hg init
3 + hg init
4 + echo a
4 + echo a
5 + hg addremove
5 + hg addremove
6 adding a
6 adding a
7 + hg commit -m 1 -d '0 0'
7 + hg commit -m 1 -d '0 0'
8 + hg clone . ../r2
8 + hg clone . ../r2
9 + cd ../r2
9 + cd ../r2
10 + hg up
10 + hg up
11 + echo abc
11 + echo abc
12 + hg diff
12 + hg diff
13 + sed 's/\(\(---\|+++\).*\)\t.*/\1/'
13 + sed 's/\(\(---\|+++\) [^ \t]*\)[ \t].*/\1/'
14 diff -r c19d34741b0a a
14 diff -r c19d34741b0a a
15 --- a/a
15 --- a/a
16 +++ b/a
16 +++ b/a
17 @@ -1,1 +1,1 @@
17 @@ -1,1 +1,1 @@
18 -a
18 -a
19 +abc
19 +abc
20 + cd ../r1
20 + cd ../r1
21 + echo b
21 + echo b
22 + echo a2
22 + echo a2
23 + hg addremove
23 + hg addremove
24 adding b
24 adding b
25 + hg commit -m 2 -d '0 0'
25 + hg commit -m 2 -d '0 0'
26 + cd ../r2
26 + cd ../r2
27 + hg -q pull ../r1
27 + hg -q pull ../r1
28 + hg status
28 + hg status
29 M a
29 M a
30 + hg --debug up
30 + hg --debug up
31 resolving manifests
31 resolving manifests
32 force None allow None moddirstate True linear True
32 force None allow None moddirstate True linear True
33 ancestor a0c8bcbbb45c local a0c8bcbbb45c remote 1165e8bd193e
33 ancestor a0c8bcbbb45c local a0c8bcbbb45c remote 1165e8bd193e
34 a versions differ, resolve
34 a versions differ, resolve
35 remote created b
35 remote created b
36 getting b
36 getting b
37 merging a
37 merging a
38 resolving a
38 resolving a
39 file a: other d730145abbf9 ancestor b789fdd96dc2
39 file a: other d730145abbf9 ancestor b789fdd96dc2
40 + hg --debug up -m
40 + hg --debug up -m
41 resolving manifests
41 resolving manifests
42 force None allow 1 moddirstate True linear True
42 force None allow 1 moddirstate True linear True
43 ancestor 1165e8bd193e local 1165e8bd193e remote 1165e8bd193e
43 ancestor 1165e8bd193e local 1165e8bd193e remote 1165e8bd193e
44 + hg parents
44 + hg parents
45 changeset: 1:1e71731e6fbb5b35fae293120dea6964371c13c6
45 changeset: 1:1e71731e6fbb
46 tag: tip
46 tag: tip
47 user: test
47 user: test
48 date: Thu Jan 1 00:00:00 1970
48 date: Thu Jan 1 00:00:00 1970
49 summary: 2
49 summary: 2
50
50
51 + hg -v history
51 + hg -v history
52 changeset: 1:1e71731e6fbb5b35fae293120dea6964371c13c6
52 changeset: 1:1e71731e6fbb5b35fae293120dea6964371c13c6
53 tag: tip
53 tag: tip
54 manifest: 1:1165e8bd193e17ad7d321d846fcf27ff3f412758
55 user: test
54 user: test
56 date: Thu Jan 1 00:00:00 1970
55 date: Thu Jan 1 00:00:00 1970
57 files: a b
56 files: a b
58 description:
57 description:
59 2
58 2
60
59
61
60
62 changeset: 0:c19d34741b0a4ced8e4ba74bb834597d5193851e
61 changeset: 0:c19d34741b0a4ced8e4ba74bb834597d5193851e
63 manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
64 user: test
62 user: test
65 date: Thu Jan 1 00:00:00 1970
63 date: Thu Jan 1 00:00:00 1970
66 files: a
64 files: a
67 description:
65 description:
68 1
66 1
69
67
70
68
71 + hg diff
69 + hg diff
72 + sed 's/\(\(---\|+++\).*\)\t.*/\1/'
70 + sed 's/\(\(---\|+++\) [^ \t]*\)[ \t].*/\1/'
73 diff -r 1e71731e6fbb a
71 diff -r 1e71731e6fbb a
74 --- a/a
72 --- a/a
75 +++ b/a
73 +++ b/a
76 @@ -1,1 +1,1 @@
74 @@ -1,1 +1,1 @@
77 -a2
75 -a2
78 +abc
76 +abc
General Comments 0
You need to be logged in to leave comments. Login now