##// END OF EJS Templates
bundler: make parsechunk return the base revision of the delta
Benoit Boissinot -
r14141:bd1cbfe5 default
parent child Browse files
Show More
@@ -1,213 +1,227 b''
1 1 # changegroup.py - Mercurial changegroup manipulation functions
2 2 #
3 3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from i18n import _
9 9 import util
10 10 import struct, os, bz2, zlib, tempfile
11 11
12 _BUNDLE10_DELTA_HEADER = "20s20s20s20s"
13
12 14 def readexactly(stream, n):
13 15 '''read n bytes from stream.read and abort if less was available'''
14 16 s = stream.read(n)
15 17 if len(s) < n:
16 18 raise util.Abort(_("stream ended unexpectedly"
17 19 " (got %d bytes, expected %d)")
18 20 % (len(s), n))
19 21 return s
20 22
21 23 def getchunk(stream):
22 24 """return the next chunk from stream as a string"""
23 25 d = readexactly(stream, 4)
24 26 l = struct.unpack(">l", d)[0]
25 27 if l <= 4:
26 28 if l:
27 29 raise util.Abort(_("invalid chunk length %d") % l)
28 30 return ""
29 31 return readexactly(stream, l - 4)
30 32
31 33 def chunkheader(length):
32 34 """return a changegroup chunk header (string)"""
33 35 return struct.pack(">l", length + 4)
34 36
35 37 def closechunk():
36 38 """return a changegroup chunk header (string) for a zero-length chunk"""
37 39 return struct.pack(">l", 0)
38 40
39 41 class nocompress(object):
40 42 def compress(self, x):
41 43 return x
42 44 def flush(self):
43 45 return ""
44 46
45 47 bundletypes = {
46 48 "": ("", nocompress), # only when using unbundle on ssh and old http servers
47 49 # since the unification ssh accepts a header but there
48 50 # is no capability signaling it.
49 51 "HG10UN": ("HG10UN", nocompress),
50 52 "HG10BZ": ("HG10", lambda: bz2.BZ2Compressor()),
51 53 "HG10GZ": ("HG10GZ", lambda: zlib.compressobj()),
52 54 }
53 55
54 56 # hgweb uses this list to communicate its preferred type
55 57 bundlepriority = ['HG10GZ', 'HG10BZ', 'HG10UN']
56 58
57 59 def writebundle(cg, filename, bundletype):
58 60 """Write a bundle file and return its filename.
59 61
60 62 Existing files will not be overwritten.
61 63 If no filename is specified, a temporary file is created.
62 64 bz2 compression can be turned off.
63 65 The bundle file will be deleted in case of errors.
64 66 """
65 67
66 68 fh = None
67 69 cleanup = None
68 70 try:
69 71 if filename:
70 72 fh = open(filename, "wb")
71 73 else:
72 74 fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
73 75 fh = os.fdopen(fd, "wb")
74 76 cleanup = filename
75 77
76 78 header, compressor = bundletypes[bundletype]
77 79 fh.write(header)
78 80 z = compressor()
79 81
80 82 # parse the changegroup data, otherwise we will block
81 83 # in case of sshrepo because we don't know the end of the stream
82 84
83 85 # an empty chunkgroup is the end of the changegroup
84 86 # a changegroup has at least 2 chunkgroups (changelog and manifest).
85 87 # after that, an empty chunkgroup is the end of the changegroup
86 88 empty = False
87 89 count = 0
88 90 while not empty or count <= 2:
89 91 empty = True
90 92 count += 1
91 93 while 1:
92 94 chunk = getchunk(cg)
93 95 if not chunk:
94 96 break
95 97 empty = False
96 98 fh.write(z.compress(chunkheader(len(chunk))))
97 99 pos = 0
98 100 while pos < len(chunk):
99 101 next = pos + 2**20
100 102 fh.write(z.compress(chunk[pos:next]))
101 103 pos = next
102 104 fh.write(z.compress(closechunk()))
103 105 fh.write(z.flush())
104 106 cleanup = None
105 107 return filename
106 108 finally:
107 109 if fh is not None:
108 110 fh.close()
109 111 if cleanup is not None:
110 112 os.unlink(cleanup)
111 113
112 114 def decompressor(fh, alg):
113 115 if alg == 'UN':
114 116 return fh
115 117 elif alg == 'GZ':
116 118 def generator(f):
117 119 zd = zlib.decompressobj()
118 120 for chunk in f:
119 121 yield zd.decompress(chunk)
120 122 elif alg == 'BZ':
121 123 def generator(f):
122 124 zd = bz2.BZ2Decompressor()
123 125 zd.decompress("BZ")
124 126 for chunk in util.filechunkiter(f, 4096):
125 127 yield zd.decompress(chunk)
126 128 else:
127 129 raise util.Abort("unknown bundle compression '%s'" % alg)
128 130 return util.chunkbuffer(generator(fh))
129 131
130 132 class unbundle10(object):
133 deltaheader = _BUNDLE10_DELTA_HEADER
134 deltaheadersize = struct.calcsize(deltaheader)
131 135 def __init__(self, fh, alg):
132 136 self._stream = decompressor(fh, alg)
133 137 self._type = alg
134 138 self.callback = None
135 139 def compressed(self):
136 140 return self._type != 'UN'
137 141 def read(self, l):
138 142 return self._stream.read(l)
139 143 def seek(self, pos):
140 144 return self._stream.seek(pos)
141 145 def tell(self):
142 146 return self._stream.tell()
143 147 def close(self):
144 148 return self._stream.close()
145 149
146 150 def chunklength(self):
147 151 d = readexactly(self._stream, 4)
148 152 l = struct.unpack(">l", d)[0]
149 153 if l <= 4:
150 154 if l:
151 155 raise util.Abort(_("invalid chunk length %d") % l)
152 156 return 0
153 157 if self.callback:
154 158 self.callback()
155 159 return l - 4
156 160
157 161 def chunk(self):
158 162 """return the next chunk from changegroup 'source' as a string"""
159 163 l = self.chunklength()
160 164 return readexactly(self._stream, l)
161 165
162 def parsechunk(self):
166 def _deltaheader(self, headertuple, prevnode):
167 node, p1, p2, cs = headertuple
168 if prevnode is None:
169 deltabase = p1
170 else:
171 deltabase = prevnode
172 return node, p1, p2, deltabase, cs
173
174 def parsechunk(self, prevnode):
163 175 l = self.chunklength()
164 176 if not l:
165 177 return {}
166 h = readexactly(self._stream, 80)
167 node, p1, p2, cs = struct.unpack("20s20s20s20s", h)
168 data = readexactly(self._stream, l - 80)
169 return dict(node=node, p1=p1, p2=p2, cs=cs, data=data)
178 headerdata = readexactly(self._stream, self.deltaheadersize)
179 header = struct.unpack(self.deltaheader, headerdata)
180 delta = readexactly(self._stream, l - self.deltaheadersize)
181 node, p1, p2, deltabase, cs = self._deltaheader(header, prevnode)
182 return dict(node=node, p1=p1, p2=p2, cs=cs,
183 deltabase=deltabase, delta=delta)
170 184
171 185 class headerlessfixup(object):
172 186 def __init__(self, fh, h):
173 187 self._h = h
174 188 self._fh = fh
175 189 def read(self, n):
176 190 if self._h:
177 191 d, self._h = self._h[:n], self._h[n:]
178 192 if len(d) < n:
179 193 d += readexactly(self._fh, n - len(d))
180 194 return d
181 195 return readexactly(self._fh, n)
182 196
183 197 def readbundle(fh, fname):
184 198 header = readexactly(fh, 6)
185 199
186 200 if not fname:
187 201 fname = "stream"
188 202 if not header.startswith('HG') and header.startswith('\0'):
189 203 fh = headerlessfixup(fh, header)
190 204 header = "HG10UN"
191 205
192 206 magic, version, alg = header[0:2], header[2:4], header[4:6]
193 207
194 208 if magic != 'HG':
195 209 raise util.Abort(_('%s: not a Mercurial bundle') % fname)
196 210 if version != '10':
197 211 raise util.Abort(_('%s: unknown bundle version %s') % (fname, version))
198 212 return unbundle10(fh, alg)
199 213
200 214 class bundle10(object):
201 215 def __init__(self, lookup):
202 216 self._lookup = lookup
203 217 def close(self):
204 218 return closechunk()
205 219 def fileheader(self, fname):
206 220 return chunkheader(len(fname)) + fname
207 221 def revchunk(self, revlog, node='', p1='', p2='', prefix='', data=''):
208 222 linknode = self._lookup(revlog, node)
209 223 meta = node + p1 + p2 + linknode + prefix
210 224 l = len(meta) + len(data)
211 225 yield chunkheader(l)
212 226 yield meta
213 227 yield data
@@ -1,4875 +1,4880 b''
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from node import hex, bin, nullid, nullrev, short
9 9 from lock import release
10 10 from i18n import _, gettext
11 11 import os, re, sys, difflib, time, tempfile
12 12 import hg, scmutil, util, revlog, extensions, copies, error, bookmarks
13 13 import patch, help, url, encoding, templatekw, discovery
14 14 import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
15 15 import merge as mergemod
16 16 import minirst, revset, templatefilters
17 17 import dagparser
18 18
19 19 # Commands start here, listed alphabetically
20 20
21 21 def add(ui, repo, *pats, **opts):
22 22 """add the specified files on the next commit
23 23
24 24 Schedule files to be version controlled and added to the
25 25 repository.
26 26
27 27 The files will be added to the repository at the next commit. To
28 28 undo an add before that, see :hg:`forget`.
29 29
30 30 If no names are given, add all files to the repository.
31 31
32 32 .. container:: verbose
33 33
34 34 An example showing how new (unknown) files are added
35 35 automatically by :hg:`add`::
36 36
37 37 $ ls
38 38 foo.c
39 39 $ hg status
40 40 ? foo.c
41 41 $ hg add
42 42 adding foo.c
43 43 $ hg status
44 44 A foo.c
45 45
46 46 Returns 0 if all files are successfully added.
47 47 """
48 48
49 49 m = cmdutil.match(repo, pats, opts)
50 50 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
51 51 opts.get('subrepos'), prefix="")
52 52 return rejected and 1 or 0
53 53
54 54 def addremove(ui, repo, *pats, **opts):
55 55 """add all new files, delete all missing files
56 56
57 57 Add all new files and remove all missing files from the
58 58 repository.
59 59
60 60 New files are ignored if they match any of the patterns in
61 61 ``.hgignore``. As with add, these changes take effect at the next
62 62 commit.
63 63
64 64 Use the -s/--similarity option to detect renamed files. With a
65 65 parameter greater than 0, this compares every removed file with
66 66 every added file and records those similar enough as renames. This
67 67 option takes a percentage between 0 (disabled) and 100 (files must
68 68 be identical) as its parameter. Detecting renamed files this way
69 69 can be expensive. After using this option, :hg:`status -C` can be
70 70 used to check which files were identified as moved or renamed.
71 71
72 72 Returns 0 if all files are successfully added.
73 73 """
74 74 try:
75 75 sim = float(opts.get('similarity') or 100)
76 76 except ValueError:
77 77 raise util.Abort(_('similarity must be a number'))
78 78 if sim < 0 or sim > 100:
79 79 raise util.Abort(_('similarity must be between 0 and 100'))
80 80 return cmdutil.addremove(repo, pats, opts, similarity=sim / 100.0)
81 81
82 82 def annotate(ui, repo, *pats, **opts):
83 83 """show changeset information by line for each file
84 84
85 85 List changes in files, showing the revision id responsible for
86 86 each line
87 87
88 88 This command is useful for discovering when a change was made and
89 89 by whom.
90 90
91 91 Without the -a/--text option, annotate will avoid processing files
92 92 it detects as binary. With -a, annotate will annotate the file
93 93 anyway, although the results will probably be neither useful
94 94 nor desirable.
95 95
96 96 Returns 0 on success.
97 97 """
98 98 if opts.get('follow'):
99 99 # --follow is deprecated and now just an alias for -f/--file
100 100 # to mimic the behavior of Mercurial before version 1.5
101 101 opts['file'] = 1
102 102
103 103 datefunc = ui.quiet and util.shortdate or util.datestr
104 104 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
105 105
106 106 if not pats:
107 107 raise util.Abort(_('at least one filename or pattern is required'))
108 108
109 109 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
110 110 ('number', lambda x: str(x[0].rev())),
111 111 ('changeset', lambda x: short(x[0].node())),
112 112 ('date', getdate),
113 113 ('file', lambda x: x[0].path()),
114 114 ]
115 115
116 116 if (not opts.get('user') and not opts.get('changeset')
117 117 and not opts.get('date') and not opts.get('file')):
118 118 opts['number'] = 1
119 119
120 120 linenumber = opts.get('line_number') is not None
121 121 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
122 122 raise util.Abort(_('at least one of -n/-c is required for -l'))
123 123
124 124 funcmap = [func for op, func in opmap if opts.get(op)]
125 125 if linenumber:
126 126 lastfunc = funcmap[-1]
127 127 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
128 128
129 129 def bad(x, y):
130 130 raise util.Abort("%s: %s" % (x, y))
131 131
132 132 ctx = cmdutil.revsingle(repo, opts.get('rev'))
133 133 m = cmdutil.match(repo, pats, opts)
134 134 m.bad = bad
135 135 follow = not opts.get('no_follow')
136 136 for abs in ctx.walk(m):
137 137 fctx = ctx[abs]
138 138 if not opts.get('text') and util.binary(fctx.data()):
139 139 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
140 140 continue
141 141
142 142 lines = fctx.annotate(follow=follow, linenumber=linenumber)
143 143 pieces = []
144 144
145 145 for f in funcmap:
146 146 l = [f(n) for n, dummy in lines]
147 147 if l:
148 148 sized = [(x, encoding.colwidth(x)) for x in l]
149 149 ml = max([w for x, w in sized])
150 150 pieces.append(["%s%s" % (' ' * (ml - w), x) for x, w in sized])
151 151
152 152 if pieces:
153 153 for p, l in zip(zip(*pieces), lines):
154 154 ui.write("%s: %s" % (" ".join(p), l[1]))
155 155
156 156 def archive(ui, repo, dest, **opts):
157 157 '''create an unversioned archive of a repository revision
158 158
159 159 By default, the revision used is the parent of the working
160 160 directory; use -r/--rev to specify a different revision.
161 161
162 162 The archive type is automatically detected based on file
163 163 extension (or override using -t/--type).
164 164
165 165 Valid types are:
166 166
167 167 :``files``: a directory full of files (default)
168 168 :``tar``: tar archive, uncompressed
169 169 :``tbz2``: tar archive, compressed using bzip2
170 170 :``tgz``: tar archive, compressed using gzip
171 171 :``uzip``: zip archive, uncompressed
172 172 :``zip``: zip archive, compressed using deflate
173 173
174 174 The exact name of the destination archive or directory is given
175 175 using a format string; see :hg:`help export` for details.
176 176
177 177 Each member added to an archive file has a directory prefix
178 178 prepended. Use -p/--prefix to specify a format string for the
179 179 prefix. The default is the basename of the archive, with suffixes
180 180 removed.
181 181
182 182 Returns 0 on success.
183 183 '''
184 184
185 185 ctx = cmdutil.revsingle(repo, opts.get('rev'))
186 186 if not ctx:
187 187 raise util.Abort(_('no working directory: please specify a revision'))
188 188 node = ctx.node()
189 189 dest = cmdutil.make_filename(repo, dest, node)
190 190 if os.path.realpath(dest) == repo.root:
191 191 raise util.Abort(_('repository root cannot be destination'))
192 192
193 193 kind = opts.get('type') or archival.guesskind(dest) or 'files'
194 194 prefix = opts.get('prefix')
195 195
196 196 if dest == '-':
197 197 if kind == 'files':
198 198 raise util.Abort(_('cannot archive plain files to stdout'))
199 199 dest = sys.stdout
200 200 if not prefix:
201 201 prefix = os.path.basename(repo.root) + '-%h'
202 202
203 203 prefix = cmdutil.make_filename(repo, prefix, node)
204 204 matchfn = cmdutil.match(repo, [], opts)
205 205 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
206 206 matchfn, prefix, subrepos=opts.get('subrepos'))
207 207
208 208 def backout(ui, repo, node=None, rev=None, **opts):
209 209 '''reverse effect of earlier changeset
210 210
211 211 Prepare a new changeset with the effect of REV undone in the
212 212 current working directory.
213 213
214 214 If REV is the parent of the working directory, then this new changeset
215 215 is committed automatically. Otherwise, hg needs to merge the
216 216 changes and the merged result is left uncommitted.
217 217
218 218 By default, the pending changeset will have one parent,
219 219 maintaining a linear history. With --merge, the pending changeset
220 220 will instead have two parents: the old parent of the working
221 221 directory and a new child of REV that simply undoes REV.
222 222
223 223 Before version 1.7, the behavior without --merge was equivalent to
224 224 specifying --merge followed by :hg:`update --clean .` to cancel
225 225 the merge and leave the child of REV as a head to be merged
226 226 separately.
227 227
228 228 See :hg:`help dates` for a list of formats valid for -d/--date.
229 229
230 230 Returns 0 on success.
231 231 '''
232 232 if rev and node:
233 233 raise util.Abort(_("please specify just one revision"))
234 234
235 235 if not rev:
236 236 rev = node
237 237
238 238 if not rev:
239 239 raise util.Abort(_("please specify a revision to backout"))
240 240
241 241 date = opts.get('date')
242 242 if date:
243 243 opts['date'] = util.parsedate(date)
244 244
245 245 cmdutil.bail_if_changed(repo)
246 246 node = cmdutil.revsingle(repo, rev).node()
247 247
248 248 op1, op2 = repo.dirstate.parents()
249 249 a = repo.changelog.ancestor(op1, node)
250 250 if a != node:
251 251 raise util.Abort(_('cannot backout change on a different branch'))
252 252
253 253 p1, p2 = repo.changelog.parents(node)
254 254 if p1 == nullid:
255 255 raise util.Abort(_('cannot backout a change with no parents'))
256 256 if p2 != nullid:
257 257 if not opts.get('parent'):
258 258 raise util.Abort(_('cannot backout a merge changeset without '
259 259 '--parent'))
260 260 p = repo.lookup(opts['parent'])
261 261 if p not in (p1, p2):
262 262 raise util.Abort(_('%s is not a parent of %s') %
263 263 (short(p), short(node)))
264 264 parent = p
265 265 else:
266 266 if opts.get('parent'):
267 267 raise util.Abort(_('cannot use --parent on non-merge changeset'))
268 268 parent = p1
269 269
270 270 # the backout should appear on the same branch
271 271 branch = repo.dirstate.branch()
272 272 hg.clean(repo, node, show_stats=False)
273 273 repo.dirstate.setbranch(branch)
274 274 revert_opts = opts.copy()
275 275 revert_opts['date'] = None
276 276 revert_opts['all'] = True
277 277 revert_opts['rev'] = hex(parent)
278 278 revert_opts['no_backup'] = None
279 279 revert(ui, repo, **revert_opts)
280 280 if not opts.get('merge') and op1 != node:
281 281 try:
282 282 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
283 283 return hg.update(repo, op1)
284 284 finally:
285 285 ui.setconfig('ui', 'forcemerge', '')
286 286
287 287 commit_opts = opts.copy()
288 288 commit_opts['addremove'] = False
289 289 if not commit_opts['message'] and not commit_opts['logfile']:
290 290 # we don't translate commit messages
291 291 commit_opts['message'] = "Backed out changeset %s" % short(node)
292 292 commit_opts['force_editor'] = True
293 293 commit(ui, repo, **commit_opts)
294 294 def nice(node):
295 295 return '%d:%s' % (repo.changelog.rev(node), short(node))
296 296 ui.status(_('changeset %s backs out changeset %s\n') %
297 297 (nice(repo.changelog.tip()), nice(node)))
298 298 if opts.get('merge') and op1 != node:
299 299 hg.clean(repo, op1, show_stats=False)
300 300 ui.status(_('merging with changeset %s\n')
301 301 % nice(repo.changelog.tip()))
302 302 try:
303 303 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
304 304 return hg.merge(repo, hex(repo.changelog.tip()))
305 305 finally:
306 306 ui.setconfig('ui', 'forcemerge', '')
307 307 return 0
308 308
309 309 def bisect(ui, repo, rev=None, extra=None, command=None,
310 310 reset=None, good=None, bad=None, skip=None, extend=None,
311 311 noupdate=None):
312 312 """subdivision search of changesets
313 313
314 314 This command helps to find changesets which introduce problems. To
315 315 use, mark the earliest changeset you know exhibits the problem as
316 316 bad, then mark the latest changeset which is free from the problem
317 317 as good. Bisect will update your working directory to a revision
318 318 for testing (unless the -U/--noupdate option is specified). Once
319 319 you have performed tests, mark the working directory as good or
320 320 bad, and bisect will either update to another candidate changeset
321 321 or announce that it has found the bad revision.
322 322
323 323 As a shortcut, you can also use the revision argument to mark a
324 324 revision as good or bad without checking it out first.
325 325
326 326 If you supply a command, it will be used for automatic bisection.
327 327 Its exit status will be used to mark revisions as good or bad:
328 328 status 0 means good, 125 means to skip the revision, 127
329 329 (command not found) will abort the bisection, and any other
330 330 non-zero exit status means the revision is bad.
331 331
332 332 Returns 0 on success.
333 333 """
334 334 def extendbisectrange(nodes, good):
335 335 # bisect is incomplete when it ends on a merge node and
336 336 # one of the parent was not checked.
337 337 parents = repo[nodes[0]].parents()
338 338 if len(parents) > 1:
339 339 side = good and state['bad'] or state['good']
340 340 num = len(set(i.node() for i in parents) & set(side))
341 341 if num == 1:
342 342 return parents[0].ancestor(parents[1])
343 343 return None
344 344
345 345 def print_result(nodes, good):
346 346 displayer = cmdutil.show_changeset(ui, repo, {})
347 347 if len(nodes) == 1:
348 348 # narrowed it down to a single revision
349 349 if good:
350 350 ui.write(_("The first good revision is:\n"))
351 351 else:
352 352 ui.write(_("The first bad revision is:\n"))
353 353 displayer.show(repo[nodes[0]])
354 354 extendnode = extendbisectrange(nodes, good)
355 355 if extendnode is not None:
356 356 ui.write(_('Not all ancestors of this changeset have been'
357 357 ' checked.\nUse bisect --extend to continue the '
358 358 'bisection from\nthe common ancestor, %s.\n')
359 359 % extendnode)
360 360 else:
361 361 # multiple possible revisions
362 362 if good:
363 363 ui.write(_("Due to skipped revisions, the first "
364 364 "good revision could be any of:\n"))
365 365 else:
366 366 ui.write(_("Due to skipped revisions, the first "
367 367 "bad revision could be any of:\n"))
368 368 for n in nodes:
369 369 displayer.show(repo[n])
370 370 displayer.close()
371 371
372 372 def check_state(state, interactive=True):
373 373 if not state['good'] or not state['bad']:
374 374 if (good or bad or skip or reset) and interactive:
375 375 return
376 376 if not state['good']:
377 377 raise util.Abort(_('cannot bisect (no known good revisions)'))
378 378 else:
379 379 raise util.Abort(_('cannot bisect (no known bad revisions)'))
380 380 return True
381 381
382 382 # backward compatibility
383 383 if rev in "good bad reset init".split():
384 384 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
385 385 cmd, rev, extra = rev, extra, None
386 386 if cmd == "good":
387 387 good = True
388 388 elif cmd == "bad":
389 389 bad = True
390 390 else:
391 391 reset = True
392 392 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
393 393 raise util.Abort(_('incompatible arguments'))
394 394
395 395 if reset:
396 396 p = repo.join("bisect.state")
397 397 if os.path.exists(p):
398 398 os.unlink(p)
399 399 return
400 400
401 401 state = hbisect.load_state(repo)
402 402
403 403 if command:
404 404 changesets = 1
405 405 try:
406 406 while changesets:
407 407 # update state
408 408 status = util.system(command)
409 409 if status == 125:
410 410 transition = "skip"
411 411 elif status == 0:
412 412 transition = "good"
413 413 # status < 0 means process was killed
414 414 elif status == 127:
415 415 raise util.Abort(_("failed to execute %s") % command)
416 416 elif status < 0:
417 417 raise util.Abort(_("%s killed") % command)
418 418 else:
419 419 transition = "bad"
420 420 ctx = cmdutil.revsingle(repo, rev)
421 421 rev = None # clear for future iterations
422 422 state[transition].append(ctx.node())
423 423 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
424 424 check_state(state, interactive=False)
425 425 # bisect
426 426 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
427 427 # update to next check
428 428 cmdutil.bail_if_changed(repo)
429 429 hg.clean(repo, nodes[0], show_stats=False)
430 430 finally:
431 431 hbisect.save_state(repo, state)
432 432 print_result(nodes, good)
433 433 return
434 434
435 435 # update state
436 436
437 437 if rev:
438 438 nodes = [repo.lookup(i) for i in cmdutil.revrange(repo, [rev])]
439 439 else:
440 440 nodes = [repo.lookup('.')]
441 441
442 442 if good or bad or skip:
443 443 if good:
444 444 state['good'] += nodes
445 445 elif bad:
446 446 state['bad'] += nodes
447 447 elif skip:
448 448 state['skip'] += nodes
449 449 hbisect.save_state(repo, state)
450 450
451 451 if not check_state(state):
452 452 return
453 453
454 454 # actually bisect
455 455 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
456 456 if extend:
457 457 if not changesets:
458 458 extendnode = extendbisectrange(nodes, good)
459 459 if extendnode is not None:
460 460 ui.write(_("Extending search to changeset %d:%s\n"
461 461 % (extendnode.rev(), extendnode)))
462 462 if noupdate:
463 463 return
464 464 cmdutil.bail_if_changed(repo)
465 465 return hg.clean(repo, extendnode.node())
466 466 raise util.Abort(_("nothing to extend"))
467 467
468 468 if changesets == 0:
469 469 print_result(nodes, good)
470 470 else:
471 471 assert len(nodes) == 1 # only a single node can be tested next
472 472 node = nodes[0]
473 473 # compute the approximate number of remaining tests
474 474 tests, size = 0, 2
475 475 while size <= changesets:
476 476 tests, size = tests + 1, size * 2
477 477 rev = repo.changelog.rev(node)
478 478 ui.write(_("Testing changeset %d:%s "
479 479 "(%d changesets remaining, ~%d tests)\n")
480 480 % (rev, short(node), changesets, tests))
481 481 if not noupdate:
482 482 cmdutil.bail_if_changed(repo)
483 483 return hg.clean(repo, node)
484 484
485 485 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False, rename=None):
486 486 '''track a line of development with movable markers
487 487
488 488 Bookmarks are pointers to certain commits that move when
489 489 committing. Bookmarks are local. They can be renamed, copied and
490 490 deleted. It is possible to use bookmark names in :hg:`merge` and
491 491 :hg:`update` to merge and update respectively to a given bookmark.
492 492
493 493 You can use :hg:`bookmark NAME` to set a bookmark on the working
494 494 directory's parent revision with the given name. If you specify
495 495 a revision using -r REV (where REV may be an existing bookmark),
496 496 the bookmark is assigned to that revision.
497 497
498 498 Bookmarks can be pushed and pulled between repositories (see :hg:`help
499 499 push` and :hg:`help pull`). This requires both the local and remote
500 500 repositories to support bookmarks. For versions prior to 1.8, this means
501 501 the bookmarks extension must be enabled.
502 502 '''
503 503 hexfn = ui.debugflag and hex or short
504 504 marks = repo._bookmarks
505 505 cur = repo.changectx('.').node()
506 506
507 507 if rename:
508 508 if rename not in marks:
509 509 raise util.Abort(_("bookmark '%s' does not exist") % rename)
510 510 if mark in marks and not force:
511 511 raise util.Abort(_("bookmark '%s' already exists "
512 512 "(use -f to force)") % mark)
513 513 if mark is None:
514 514 raise util.Abort(_("new bookmark name required"))
515 515 marks[mark] = marks[rename]
516 516 if repo._bookmarkcurrent == rename:
517 517 bookmarks.setcurrent(repo, mark)
518 518 del marks[rename]
519 519 bookmarks.write(repo)
520 520 return
521 521
522 522 if delete:
523 523 if mark is None:
524 524 raise util.Abort(_("bookmark name required"))
525 525 if mark not in marks:
526 526 raise util.Abort(_("bookmark '%s' does not exist") % mark)
527 527 if mark == repo._bookmarkcurrent:
528 528 bookmarks.setcurrent(repo, None)
529 529 del marks[mark]
530 530 bookmarks.write(repo)
531 531 return
532 532
533 533 if mark is not None:
534 534 if "\n" in mark:
535 535 raise util.Abort(_("bookmark name cannot contain newlines"))
536 536 mark = mark.strip()
537 537 if not mark:
538 538 raise util.Abort(_("bookmark names cannot consist entirely of "
539 539 "whitespace"))
540 540 if mark in marks and not force:
541 541 raise util.Abort(_("bookmark '%s' already exists "
542 542 "(use -f to force)") % mark)
543 543 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
544 544 and not force):
545 545 raise util.Abort(
546 546 _("a bookmark cannot have the name of an existing branch"))
547 547 if rev:
548 548 marks[mark] = repo.lookup(rev)
549 549 else:
550 550 marks[mark] = repo.changectx('.').node()
551 551 if repo.changectx('.').node() == marks[mark]:
552 552 bookmarks.setcurrent(repo, mark)
553 553 bookmarks.write(repo)
554 554 return
555 555
556 556 if mark is None:
557 557 if rev:
558 558 raise util.Abort(_("bookmark name required"))
559 559 if len(marks) == 0:
560 560 ui.status(_("no bookmarks set\n"))
561 561 else:
562 562 for bmark, n in sorted(marks.iteritems()):
563 563 current = repo._bookmarkcurrent
564 564 if bmark == current and n == cur:
565 565 prefix, label = '*', 'bookmarks.current'
566 566 else:
567 567 prefix, label = ' ', ''
568 568
569 569 if ui.quiet:
570 570 ui.write("%s\n" % bmark, label=label)
571 571 else:
572 572 ui.write(" %s %-25s %d:%s\n" % (
573 573 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
574 574 label=label)
575 575 return
576 576
577 577 def branch(ui, repo, label=None, **opts):
578 578 """set or show the current branch name
579 579
580 580 With no argument, show the current branch name. With one argument,
581 581 set the working directory branch name (the branch will not exist
582 582 in the repository until the next commit). Standard practice
583 583 recommends that primary development take place on the 'default'
584 584 branch.
585 585
586 586 Unless -f/--force is specified, branch will not let you set a
587 587 branch name that already exists, even if it's inactive.
588 588
589 589 Use -C/--clean to reset the working directory branch to that of
590 590 the parent of the working directory, negating a previous branch
591 591 change.
592 592
593 593 Use the command :hg:`update` to switch to an existing branch. Use
594 594 :hg:`commit --close-branch` to mark this branch as closed.
595 595
596 596 Returns 0 on success.
597 597 """
598 598
599 599 if opts.get('clean'):
600 600 label = repo[None].p1().branch()
601 601 repo.dirstate.setbranch(label)
602 602 ui.status(_('reset working directory to branch %s\n') % label)
603 603 elif label:
604 604 if not opts.get('force') and label in repo.branchtags():
605 605 if label not in [p.branch() for p in repo.parents()]:
606 606 raise util.Abort(_('a branch of the same name already exists'
607 607 " (use 'hg update' to switch to it)"))
608 608 repo.dirstate.setbranch(label)
609 609 ui.status(_('marked working directory as branch %s\n') % label)
610 610 else:
611 611 ui.write("%s\n" % repo.dirstate.branch())
612 612
613 613 def branches(ui, repo, active=False, closed=False):
614 614 """list repository named branches
615 615
616 616 List the repository's named branches, indicating which ones are
617 617 inactive. If -c/--closed is specified, also list branches which have
618 618 been marked closed (see :hg:`commit --close-branch`).
619 619
620 620 If -a/--active is specified, only show active branches. A branch
621 621 is considered active if it contains repository heads.
622 622
623 623 Use the command :hg:`update` to switch to an existing branch.
624 624
625 625 Returns 0.
626 626 """
627 627
628 628 hexfunc = ui.debugflag and hex or short
629 629 activebranches = [repo[n].branch() for n in repo.heads()]
630 630 def testactive(tag, node):
631 631 realhead = tag in activebranches
632 632 open = node in repo.branchheads(tag, closed=False)
633 633 return realhead and open
634 634 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
635 635 for tag, node in repo.branchtags().items()],
636 636 reverse=True)
637 637
638 638 for isactive, node, tag in branches:
639 639 if (not active) or isactive:
640 640 if ui.quiet:
641 641 ui.write("%s\n" % tag)
642 642 else:
643 643 hn = repo.lookup(node)
644 644 if isactive:
645 645 label = 'branches.active'
646 646 notice = ''
647 647 elif hn not in repo.branchheads(tag, closed=False):
648 648 if not closed:
649 649 continue
650 650 label = 'branches.closed'
651 651 notice = _(' (closed)')
652 652 else:
653 653 label = 'branches.inactive'
654 654 notice = _(' (inactive)')
655 655 if tag == repo.dirstate.branch():
656 656 label = 'branches.current'
657 657 rev = str(node).rjust(31 - encoding.colwidth(tag))
658 658 rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
659 659 tag = ui.label(tag, label)
660 660 ui.write("%s %s%s\n" % (tag, rev, notice))
661 661
662 662 def bundle(ui, repo, fname, dest=None, **opts):
663 663 """create a changegroup file
664 664
665 665 Generate a compressed changegroup file collecting changesets not
666 666 known to be in another repository.
667 667
668 668 If you omit the destination repository, then hg assumes the
669 669 destination will have all the nodes you specify with --base
670 670 parameters. To create a bundle containing all changesets, use
671 671 -a/--all (or --base null).
672 672
673 673 You can change compression method with the -t/--type option.
674 674 The available compression methods are: none, bzip2, and
675 675 gzip (by default, bundles are compressed using bzip2).
676 676
677 677 The bundle file can then be transferred using conventional means
678 678 and applied to another repository with the unbundle or pull
679 679 command. This is useful when direct push and pull are not
680 680 available or when exporting an entire repository is undesirable.
681 681
682 682 Applying bundles preserves all changeset contents including
683 683 permissions, copy/rename information, and revision history.
684 684
685 685 Returns 0 on success, 1 if no changes found.
686 686 """
687 687 revs = None
688 688 if 'rev' in opts:
689 689 revs = cmdutil.revrange(repo, opts['rev'])
690 690
691 691 if opts.get('all'):
692 692 base = ['null']
693 693 else:
694 694 base = cmdutil.revrange(repo, opts.get('base'))
695 695 if base:
696 696 if dest:
697 697 raise util.Abort(_("--base is incompatible with specifying "
698 698 "a destination"))
699 699 common = [repo.lookup(rev) for rev in base]
700 700 else:
701 701 dest = ui.expandpath(dest or 'default-push', dest or 'default')
702 702 dest, branches = hg.parseurl(dest, opts.get('branch'))
703 703 other = hg.repository(hg.remoteui(repo, opts), dest)
704 704 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
705 705 inc = discovery.findcommonincoming(repo, other, force=opts.get('force'))
706 706 common, _anyinc, _heads = inc
707 707
708 708 nodes = revs and map(repo.lookup, revs) or revs
709 709 cg = repo.getbundle('bundle', common=common, heads=nodes)
710 710 if not cg:
711 711 ui.status(_("no changes found\n"))
712 712 return 1
713 713
714 714 bundletype = opts.get('type', 'bzip2').lower()
715 715 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
716 716 bundletype = btypes.get(bundletype)
717 717 if bundletype not in changegroup.bundletypes:
718 718 raise util.Abort(_('unknown bundle type specified with --type'))
719 719
720 720 changegroup.writebundle(cg, fname, bundletype)
721 721
722 722 def cat(ui, repo, file1, *pats, **opts):
723 723 """output the current or given revision of files
724 724
725 725 Print the specified files as they were at the given revision. If
726 726 no revision is given, the parent of the working directory is used,
727 727 or tip if no revision is checked out.
728 728
729 729 Output may be to a file, in which case the name of the file is
730 730 given using a format string. The formatting rules are the same as
731 731 for the export command, with the following additions:
732 732
733 733 :``%s``: basename of file being printed
734 734 :``%d``: dirname of file being printed, or '.' if in repository root
735 735 :``%p``: root-relative path name of file being printed
736 736
737 737 Returns 0 on success.
738 738 """
739 739 ctx = cmdutil.revsingle(repo, opts.get('rev'))
740 740 err = 1
741 741 m = cmdutil.match(repo, (file1,) + pats, opts)
742 742 for abs in ctx.walk(m):
743 743 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
744 744 data = ctx[abs].data()
745 745 if opts.get('decode'):
746 746 data = repo.wwritedata(abs, data)
747 747 fp.write(data)
748 748 fp.close()
749 749 err = 0
750 750 return err
751 751
752 752 def clone(ui, source, dest=None, **opts):
753 753 """make a copy of an existing repository
754 754
755 755 Create a copy of an existing repository in a new directory.
756 756
757 757 If no destination directory name is specified, it defaults to the
758 758 basename of the source.
759 759
760 760 The location of the source is added to the new repository's
761 761 ``.hg/hgrc`` file, as the default to be used for future pulls.
762 762
763 763 See :hg:`help urls` for valid source format details.
764 764
765 765 It is possible to specify an ``ssh://`` URL as the destination, but no
766 766 ``.hg/hgrc`` and working directory will be created on the remote side.
767 767 Please see :hg:`help urls` for important details about ``ssh://`` URLs.
768 768
769 769 A set of changesets (tags, or branch names) to pull may be specified
770 770 by listing each changeset (tag, or branch name) with -r/--rev.
771 771 If -r/--rev is used, the cloned repository will contain only a subset
772 772 of the changesets of the source repository. Only the set of changesets
773 773 defined by all -r/--rev options (including all their ancestors)
774 774 will be pulled into the destination repository.
775 775 No subsequent changesets (including subsequent tags) will be present
776 776 in the destination.
777 777
778 778 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
779 779 local source repositories.
780 780
781 781 For efficiency, hardlinks are used for cloning whenever the source
782 782 and destination are on the same filesystem (note this applies only
783 783 to the repository data, not to the working directory). Some
784 784 filesystems, such as AFS, implement hardlinking incorrectly, but
785 785 do not report errors. In these cases, use the --pull option to
786 786 avoid hardlinking.
787 787
788 788 In some cases, you can clone repositories and the working directory
789 789 using full hardlinks with ::
790 790
791 791 $ cp -al REPO REPOCLONE
792 792
793 793 This is the fastest way to clone, but it is not always safe. The
794 794 operation is not atomic (making sure REPO is not modified during
795 795 the operation is up to you) and you have to make sure your editor
796 796 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
797 797 this is not compatible with certain extensions that place their
798 798 metadata under the .hg directory, such as mq.
799 799
800 800 Mercurial will update the working directory to the first applicable
801 801 revision from this list:
802 802
803 803 a) null if -U or the source repository has no changesets
804 804 b) if -u . and the source repository is local, the first parent of
805 805 the source repository's working directory
806 806 c) the changeset specified with -u (if a branch name, this means the
807 807 latest head of that branch)
808 808 d) the changeset specified with -r
809 809 e) the tipmost head specified with -b
810 810 f) the tipmost head specified with the url#branch source syntax
811 811 g) the tipmost head of the default branch
812 812 h) tip
813 813
814 814 Returns 0 on success.
815 815 """
816 816 if opts.get('noupdate') and opts.get('updaterev'):
817 817 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
818 818
819 819 r = hg.clone(hg.remoteui(ui, opts), source, dest,
820 820 pull=opts.get('pull'),
821 821 stream=opts.get('uncompressed'),
822 822 rev=opts.get('rev'),
823 823 update=opts.get('updaterev') or not opts.get('noupdate'),
824 824 branch=opts.get('branch'))
825 825
826 826 return r is None
827 827
828 828 def commit(ui, repo, *pats, **opts):
829 829 """commit the specified files or all outstanding changes
830 830
831 831 Commit changes to the given files into the repository. Unlike a
832 832 centralized SCM, this operation is a local operation. See
833 833 :hg:`push` for a way to actively distribute your changes.
834 834
835 835 If a list of files is omitted, all changes reported by :hg:`status`
836 836 will be committed.
837 837
838 838 If you are committing the result of a merge, do not provide any
839 839 filenames or -I/-X filters.
840 840
841 841 If no commit message is specified, Mercurial starts your
842 842 configured editor where you can enter a message. In case your
843 843 commit fails, you will find a backup of your message in
844 844 ``.hg/last-message.txt``.
845 845
846 846 See :hg:`help dates` for a list of formats valid for -d/--date.
847 847
848 848 Returns 0 on success, 1 if nothing changed.
849 849 """
850 850 extra = {}
851 851 if opts.get('close_branch'):
852 852 if repo['.'].node() not in repo.branchheads():
853 853 # The topo heads set is included in the branch heads set of the
854 854 # current branch, so it's sufficient to test branchheads
855 855 raise util.Abort(_('can only close branch heads'))
856 856 extra['close'] = 1
857 857 e = cmdutil.commiteditor
858 858 if opts.get('force_editor'):
859 859 e = cmdutil.commitforceeditor
860 860
861 861 def commitfunc(ui, repo, message, match, opts):
862 862 return repo.commit(message, opts.get('user'), opts.get('date'), match,
863 863 editor=e, extra=extra)
864 864
865 865 branch = repo[None].branch()
866 866 bheads = repo.branchheads(branch)
867 867
868 868 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
869 869 if not node:
870 870 stat = repo.status(match=cmdutil.match(repo, pats, opts))
871 871 if stat[3]:
872 872 ui.status(_("nothing changed (%d missing files, see 'hg status')\n")
873 873 % len(stat[3]))
874 874 else:
875 875 ui.status(_("nothing changed\n"))
876 876 return 1
877 877
878 878 ctx = repo[node]
879 879 parents = ctx.parents()
880 880
881 881 if bheads and not [x for x in parents
882 882 if x.node() in bheads and x.branch() == branch]:
883 883 ui.status(_('created new head\n'))
884 884 # The message is not printed for initial roots. For the other
885 885 # changesets, it is printed in the following situations:
886 886 #
887 887 # Par column: for the 2 parents with ...
888 888 # N: null or no parent
889 889 # B: parent is on another named branch
890 890 # C: parent is a regular non head changeset
891 891 # H: parent was a branch head of the current branch
892 892 # Msg column: whether we print "created new head" message
893 893 # In the following, it is assumed that there already exists some
894 894 # initial branch heads of the current branch, otherwise nothing is
895 895 # printed anyway.
896 896 #
897 897 # Par Msg Comment
898 898 # NN y additional topo root
899 899 #
900 900 # BN y additional branch root
901 901 # CN y additional topo head
902 902 # HN n usual case
903 903 #
904 904 # BB y weird additional branch root
905 905 # CB y branch merge
906 906 # HB n merge with named branch
907 907 #
908 908 # CC y additional head from merge
909 909 # CH n merge with a head
910 910 #
911 911 # HH n head merge: head count decreases
912 912
913 913 if not opts.get('close_branch'):
914 914 for r in parents:
915 915 if r.extra().get('close') and r.branch() == branch:
916 916 ui.status(_('reopening closed branch head %d\n') % r)
917 917
918 918 if ui.debugflag:
919 919 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
920 920 elif ui.verbose:
921 921 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
922 922
923 923 def copy(ui, repo, *pats, **opts):
924 924 """mark files as copied for the next commit
925 925
926 926 Mark dest as having copies of source files. If dest is a
927 927 directory, copies are put in that directory. If dest is a file,
928 928 the source must be a single file.
929 929
930 930 By default, this command copies the contents of files as they
931 931 exist in the working directory. If invoked with -A/--after, the
932 932 operation is recorded, but no copying is performed.
933 933
934 934 This command takes effect with the next commit. To undo a copy
935 935 before that, see :hg:`revert`.
936 936
937 937 Returns 0 on success, 1 if errors are encountered.
938 938 """
939 939 wlock = repo.wlock(False)
940 940 try:
941 941 return cmdutil.copy(ui, repo, pats, opts)
942 942 finally:
943 943 wlock.release()
944 944
945 945 def debugancestor(ui, repo, *args):
946 946 """find the ancestor revision of two revisions in a given index"""
947 947 if len(args) == 3:
948 948 index, rev1, rev2 = args
949 949 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
950 950 lookup = r.lookup
951 951 elif len(args) == 2:
952 952 if not repo:
953 953 raise util.Abort(_("there is no Mercurial repository here "
954 954 "(.hg not found)"))
955 955 rev1, rev2 = args
956 956 r = repo.changelog
957 957 lookup = repo.lookup
958 958 else:
959 959 raise util.Abort(_('either two or three arguments required'))
960 960 a = r.ancestor(lookup(rev1), lookup(rev2))
961 961 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
962 962
963 963 def debugbuilddag(ui, repo, text,
964 964 mergeable_file=False,
965 965 appended_file=False,
966 966 overwritten_file=False,
967 967 new_file=False):
968 968 """builds a repo with a given dag from scratch in the current empty repo
969 969
970 970 Elements:
971 971
972 972 - "+n" is a linear run of n nodes based on the current default parent
973 973 - "." is a single node based on the current default parent
974 974 - "$" resets the default parent to null (implied at the start);
975 975 otherwise the default parent is always the last node created
976 976 - "<p" sets the default parent to the backref p
977 977 - "*p" is a fork at parent p, which is a backref
978 978 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
979 979 - "/p2" is a merge of the preceding node and p2
980 980 - ":tag" defines a local tag for the preceding node
981 981 - "@branch" sets the named branch for subsequent nodes
982 982 - "!command" runs the command using your shell
983 983 - "!!my command\\n" is like "!", but to the end of the line
984 984 - "#...\\n" is a comment up to the end of the line
985 985
986 986 Whitespace between the above elements is ignored.
987 987
988 988 A backref is either
989 989
990 990 - a number n, which references the node curr-n, where curr is the current
991 991 node, or
992 992 - the name of a local tag you placed earlier using ":tag", or
993 993 - empty to denote the default parent.
994 994
995 995 All string valued-elements are either strictly alphanumeric, or must
996 996 be enclosed in double quotes ("..."), with "\\" as escape character.
997 997
998 998 Note that the --overwritten-file and --appended-file options imply the
999 999 use of "HGMERGE=internal:local" during DAG buildup.
1000 1000 """
1001 1001
1002 1002 if not (mergeable_file or appended_file or overwritten_file or new_file):
1003 1003 raise util.Abort(_('need at least one of -m, -a, -o, -n'))
1004 1004
1005 1005 if len(repo.changelog) > 0:
1006 1006 raise util.Abort(_('repository is not empty'))
1007 1007
1008 1008 if overwritten_file or appended_file:
1009 1009 # we don't want to fail in merges during buildup
1010 1010 os.environ['HGMERGE'] = 'internal:local'
1011 1011
1012 1012 def writefile(fname, text, fmode="wb"):
1013 1013 f = open(fname, fmode)
1014 1014 try:
1015 1015 f.write(text)
1016 1016 finally:
1017 1017 f.close()
1018 1018
1019 1019 if mergeable_file:
1020 1020 linesperrev = 2
1021 1021 # determine number of revs in DAG
1022 1022 n = 0
1023 1023 for type, data in dagparser.parsedag(text):
1024 1024 if type == 'n':
1025 1025 n += 1
1026 1026 # make a file with k lines per rev
1027 1027 writefile("mf", "\n".join(str(i) for i in xrange(0, n * linesperrev))
1028 1028 + "\n")
1029 1029
1030 1030 at = -1
1031 1031 atbranch = 'default'
1032 1032 for type, data in dagparser.parsedag(text):
1033 1033 if type == 'n':
1034 1034 ui.status('node %s\n' % str(data))
1035 1035 id, ps = data
1036 1036 p1 = ps[0]
1037 1037 if p1 != at:
1038 1038 update(ui, repo, node=str(p1), clean=True)
1039 1039 at = p1
1040 1040 if repo.dirstate.branch() != atbranch:
1041 1041 branch(ui, repo, atbranch, force=True)
1042 1042 if len(ps) > 1:
1043 1043 p2 = ps[1]
1044 1044 merge(ui, repo, node=p2)
1045 1045
1046 1046 if mergeable_file:
1047 1047 f = open("mf", "rb+")
1048 1048 try:
1049 1049 lines = f.read().split("\n")
1050 1050 lines[id * linesperrev] += " r%i" % id
1051 1051 f.seek(0)
1052 1052 f.write("\n".join(lines))
1053 1053 finally:
1054 1054 f.close()
1055 1055
1056 1056 if appended_file:
1057 1057 writefile("af", "r%i\n" % id, "ab")
1058 1058
1059 1059 if overwritten_file:
1060 1060 writefile("of", "r%i\n" % id)
1061 1061
1062 1062 if new_file:
1063 1063 writefile("nf%i" % id, "r%i\n" % id)
1064 1064
1065 1065 commit(ui, repo, addremove=True, message="r%i" % id, date=(id, 0))
1066 1066 at = id
1067 1067 elif type == 'l':
1068 1068 id, name = data
1069 1069 ui.status('tag %s\n' % name)
1070 1070 tag(ui, repo, name, local=True)
1071 1071 elif type == 'a':
1072 1072 ui.status('branch %s\n' % data)
1073 1073 atbranch = data
1074 1074 elif type in 'cC':
1075 1075 r = util.system(data, cwd=repo.root)
1076 1076 if r:
1077 1077 desc, r = util.explain_exit(r)
1078 1078 raise util.Abort(_('%s command %s') % (data, desc))
1079 1079
1080 1080 def debugcommands(ui, cmd='', *args):
1081 1081 """list all available commands and options"""
1082 1082 for cmd, vals in sorted(table.iteritems()):
1083 1083 cmd = cmd.split('|')[0].strip('^')
1084 1084 opts = ', '.join([i[1] for i in vals[1]])
1085 1085 ui.write('%s: %s\n' % (cmd, opts))
1086 1086
1087 1087 def debugcomplete(ui, cmd='', **opts):
1088 1088 """returns the completion list associated with the given command"""
1089 1089
1090 1090 if opts.get('options'):
1091 1091 options = []
1092 1092 otables = [globalopts]
1093 1093 if cmd:
1094 1094 aliases, entry = cmdutil.findcmd(cmd, table, False)
1095 1095 otables.append(entry[1])
1096 1096 for t in otables:
1097 1097 for o in t:
1098 1098 if "(DEPRECATED)" in o[3]:
1099 1099 continue
1100 1100 if o[0]:
1101 1101 options.append('-%s' % o[0])
1102 1102 options.append('--%s' % o[1])
1103 1103 ui.write("%s\n" % "\n".join(options))
1104 1104 return
1105 1105
1106 1106 cmdlist = cmdutil.findpossible(cmd, table)
1107 1107 if ui.verbose:
1108 1108 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1109 1109 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1110 1110
1111 1111 def debugfsinfo(ui, path = "."):
1112 1112 """show information detected about current filesystem"""
1113 1113 open('.debugfsinfo', 'w').write('')
1114 1114 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1115 1115 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1116 1116 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1117 1117 and 'yes' or 'no'))
1118 1118 os.unlink('.debugfsinfo')
1119 1119
1120 1120 def debugrebuildstate(ui, repo, rev="tip"):
1121 1121 """rebuild the dirstate as it would look like for the given revision"""
1122 1122 ctx = cmdutil.revsingle(repo, rev)
1123 1123 wlock = repo.wlock()
1124 1124 try:
1125 1125 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1126 1126 finally:
1127 1127 wlock.release()
1128 1128
1129 1129 def debugcheckstate(ui, repo):
1130 1130 """validate the correctness of the current dirstate"""
1131 1131 parent1, parent2 = repo.dirstate.parents()
1132 1132 m1 = repo[parent1].manifest()
1133 1133 m2 = repo[parent2].manifest()
1134 1134 errors = 0
1135 1135 for f in repo.dirstate:
1136 1136 state = repo.dirstate[f]
1137 1137 if state in "nr" and f not in m1:
1138 1138 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1139 1139 errors += 1
1140 1140 if state in "a" and f in m1:
1141 1141 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1142 1142 errors += 1
1143 1143 if state in "m" and f not in m1 and f not in m2:
1144 1144 ui.warn(_("%s in state %s, but not in either manifest\n") %
1145 1145 (f, state))
1146 1146 errors += 1
1147 1147 for f in m1:
1148 1148 state = repo.dirstate[f]
1149 1149 if state not in "nrm":
1150 1150 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1151 1151 errors += 1
1152 1152 if errors:
1153 1153 error = _(".hg/dirstate inconsistent with current parent's manifest")
1154 1154 raise util.Abort(error)
1155 1155
1156 1156 def showconfig(ui, repo, *values, **opts):
1157 1157 """show combined config settings from all hgrc files
1158 1158
1159 1159 With no arguments, print names and values of all config items.
1160 1160
1161 1161 With one argument of the form section.name, print just the value
1162 1162 of that config item.
1163 1163
1164 1164 With multiple arguments, print names and values of all config
1165 1165 items with matching section names.
1166 1166
1167 1167 With --debug, the source (filename and line number) is printed
1168 1168 for each config item.
1169 1169
1170 1170 Returns 0 on success.
1171 1171 """
1172 1172
1173 1173 for f in scmutil.rcpath():
1174 1174 ui.debug(_('read config from: %s\n') % f)
1175 1175 untrusted = bool(opts.get('untrusted'))
1176 1176 if values:
1177 1177 sections = [v for v in values if '.' not in v]
1178 1178 items = [v for v in values if '.' in v]
1179 1179 if len(items) > 1 or items and sections:
1180 1180 raise util.Abort(_('only one config item permitted'))
1181 1181 for section, name, value in ui.walkconfig(untrusted=untrusted):
1182 1182 value = str(value).replace('\n', '\\n')
1183 1183 sectname = section + '.' + name
1184 1184 if values:
1185 1185 for v in values:
1186 1186 if v == section:
1187 1187 ui.debug('%s: ' %
1188 1188 ui.configsource(section, name, untrusted))
1189 1189 ui.write('%s=%s\n' % (sectname, value))
1190 1190 elif v == sectname:
1191 1191 ui.debug('%s: ' %
1192 1192 ui.configsource(section, name, untrusted))
1193 1193 ui.write(value, '\n')
1194 1194 else:
1195 1195 ui.debug('%s: ' %
1196 1196 ui.configsource(section, name, untrusted))
1197 1197 ui.write('%s=%s\n' % (sectname, value))
1198 1198
1199 1199 def debugknown(ui, repopath, *ids, **opts):
1200 1200 """test whether node ids are known to a repo
1201 1201
1202 1202 Every ID must be a full-length hex node id string. Returns a list of 0s and 1s
1203 1203 indicating unknown/known.
1204 1204 """
1205 1205 repo = hg.repository(ui, repopath)
1206 1206 if not repo.capable('known'):
1207 1207 raise util.Abort("known() not supported by target repository")
1208 1208 flags = repo.known([bin(s) for s in ids])
1209 1209 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
1210 1210
1211 1211 def debugbundle(ui, bundlepath, all=None, **opts):
1212 1212 """lists the contents of a bundle"""
1213 1213 f = url.open(ui, bundlepath)
1214 1214 try:
1215 1215 gen = changegroup.readbundle(f, bundlepath)
1216 1216 if all:
1217 ui.write("format: id, p1, p2, cset, len(delta)\n")
1217 ui.write("format: id, p1, p2, cset, delta base, len(delta)\n")
1218 1218
1219 1219 def showchunks(named):
1220 1220 ui.write("\n%s\n" % named)
1221 chain = None
1221 1222 while 1:
1222 chunkdata = gen.parsechunk()
1223 chunkdata = gen.parsechunk(chain)
1223 1224 if not chunkdata:
1224 1225 break
1225 1226 node = chunkdata['node']
1226 1227 p1 = chunkdata['p1']
1227 1228 p2 = chunkdata['p2']
1228 1229 cs = chunkdata['cs']
1229 delta = chunkdata['data']
1230 ui.write("%s %s %s %s %s\n" %
1230 deltabase = chunkdata['deltabase']
1231 delta = chunkdata['delta']
1232 ui.write("%s %s %s %s %s %s\n" %
1231 1233 (hex(node), hex(p1), hex(p2),
1232 hex(cs), len(delta)))
1234 hex(cs), hex(deltabase), len(delta)))
1235 chain = node
1233 1236
1234 1237 showchunks("changelog")
1235 1238 showchunks("manifest")
1236 1239 while 1:
1237 1240 fname = gen.chunk()
1238 1241 if not fname:
1239 1242 break
1240 1243 showchunks(fname)
1241 1244 else:
1245 chain = None
1242 1246 while 1:
1243 chunkdata = gen.parsechunk()
1247 chunkdata = gen.parsechunk(chain)
1244 1248 if not chunkdata:
1245 1249 break
1246 1250 node = chunkdata['node']
1247 1251 ui.write("%s\n" % hex(node))
1252 chain = node
1248 1253 finally:
1249 1254 f.close()
1250 1255
1251 1256 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
1252 1257 """retrieves a bundle from a repo
1253 1258
1254 1259 Every ID must be a full-length hex node id string. Saves the bundle to the
1255 1260 given file.
1256 1261 """
1257 1262 repo = hg.repository(ui, repopath)
1258 1263 if not repo.capable('getbundle'):
1259 1264 raise util.Abort("getbundle() not supported by target repository")
1260 1265 args = {}
1261 1266 if common:
1262 1267 args['common'] = [bin(s) for s in common]
1263 1268 if head:
1264 1269 args['heads'] = [bin(s) for s in head]
1265 1270 bundle = repo.getbundle('debug', **args)
1266 1271
1267 1272 bundletype = opts.get('type', 'bzip2').lower()
1268 1273 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1269 1274 bundletype = btypes.get(bundletype)
1270 1275 if bundletype not in changegroup.bundletypes:
1271 1276 raise util.Abort(_('unknown bundle type specified with --type'))
1272 1277 changegroup.writebundle(bundle, bundlepath, bundletype)
1273 1278
1274 1279 def debugpushkey(ui, repopath, namespace, *keyinfo):
1275 1280 '''access the pushkey key/value protocol
1276 1281
1277 1282 With two args, list the keys in the given namespace.
1278 1283
1279 1284 With five args, set a key to new if it currently is set to old.
1280 1285 Reports success or failure.
1281 1286 '''
1282 1287
1283 1288 target = hg.repository(ui, repopath)
1284 1289 if keyinfo:
1285 1290 key, old, new = keyinfo
1286 1291 r = target.pushkey(namespace, key, old, new)
1287 1292 ui.status(str(r) + '\n')
1288 1293 return not r
1289 1294 else:
1290 1295 for k, v in target.listkeys(namespace).iteritems():
1291 1296 ui.write("%s\t%s\n" % (k.encode('string-escape'),
1292 1297 v.encode('string-escape')))
1293 1298
1294 1299 def debugrevspec(ui, repo, expr):
1295 1300 '''parse and apply a revision specification'''
1296 1301 if ui.verbose:
1297 1302 tree = revset.parse(expr)[0]
1298 1303 ui.note(tree, "\n")
1299 1304 newtree = revset.findaliases(ui, tree)
1300 1305 if newtree != tree:
1301 1306 ui.note(newtree, "\n")
1302 1307 func = revset.match(ui, expr)
1303 1308 for c in func(repo, range(len(repo))):
1304 1309 ui.write("%s\n" % c)
1305 1310
1306 1311 def debugsetparents(ui, repo, rev1, rev2=None):
1307 1312 """manually set the parents of the current working directory
1308 1313
1309 1314 This is useful for writing repository conversion tools, but should
1310 1315 be used with care.
1311 1316
1312 1317 Returns 0 on success.
1313 1318 """
1314 1319
1315 1320 r1 = cmdutil.revsingle(repo, rev1).node()
1316 1321 r2 = cmdutil.revsingle(repo, rev2, 'null').node()
1317 1322
1318 1323 wlock = repo.wlock()
1319 1324 try:
1320 1325 repo.dirstate.setparents(r1, r2)
1321 1326 finally:
1322 1327 wlock.release()
1323 1328
1324 1329 def debugstate(ui, repo, nodates=None, datesort=None):
1325 1330 """show the contents of the current dirstate"""
1326 1331 timestr = ""
1327 1332 showdate = not nodates
1328 1333 if datesort:
1329 1334 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
1330 1335 else:
1331 1336 keyfunc = None # sort by filename
1332 1337 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
1333 1338 if showdate:
1334 1339 if ent[3] == -1:
1335 1340 # Pad or slice to locale representation
1336 1341 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
1337 1342 time.localtime(0)))
1338 1343 timestr = 'unset'
1339 1344 timestr = (timestr[:locale_len] +
1340 1345 ' ' * (locale_len - len(timestr)))
1341 1346 else:
1342 1347 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
1343 1348 time.localtime(ent[3]))
1344 1349 if ent[1] & 020000:
1345 1350 mode = 'lnk'
1346 1351 else:
1347 1352 mode = '%3o' % (ent[1] & 0777)
1348 1353 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
1349 1354 for f in repo.dirstate.copies():
1350 1355 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
1351 1356
1352 1357 def debugsub(ui, repo, rev=None):
1353 1358 ctx = cmdutil.revsingle(repo, rev, None)
1354 1359 for k, v in sorted(ctx.substate.items()):
1355 1360 ui.write('path %s\n' % k)
1356 1361 ui.write(' source %s\n' % v[0])
1357 1362 ui.write(' revision %s\n' % v[1])
1358 1363
1359 1364 def debugdag(ui, repo, file_=None, *revs, **opts):
1360 1365 """format the changelog or an index DAG as a concise textual description
1361 1366
1362 1367 If you pass a revlog index, the revlog's DAG is emitted. If you list
1363 1368 revision numbers, they get labelled in the output as rN.
1364 1369
1365 1370 Otherwise, the changelog DAG of the current repo is emitted.
1366 1371 """
1367 1372 spaces = opts.get('spaces')
1368 1373 dots = opts.get('dots')
1369 1374 if file_:
1370 1375 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1371 1376 revs = set((int(r) for r in revs))
1372 1377 def events():
1373 1378 for r in rlog:
1374 1379 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1375 1380 if r in revs:
1376 1381 yield 'l', (r, "r%i" % r)
1377 1382 elif repo:
1378 1383 cl = repo.changelog
1379 1384 tags = opts.get('tags')
1380 1385 branches = opts.get('branches')
1381 1386 if tags:
1382 1387 labels = {}
1383 1388 for l, n in repo.tags().items():
1384 1389 labels.setdefault(cl.rev(n), []).append(l)
1385 1390 def events():
1386 1391 b = "default"
1387 1392 for r in cl:
1388 1393 if branches:
1389 1394 newb = cl.read(cl.node(r))[5]['branch']
1390 1395 if newb != b:
1391 1396 yield 'a', newb
1392 1397 b = newb
1393 1398 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1394 1399 if tags:
1395 1400 ls = labels.get(r)
1396 1401 if ls:
1397 1402 for l in ls:
1398 1403 yield 'l', (r, l)
1399 1404 else:
1400 1405 raise util.Abort(_('need repo for changelog dag'))
1401 1406
1402 1407 for line in dagparser.dagtextlines(events(),
1403 1408 addspaces=spaces,
1404 1409 wraplabels=True,
1405 1410 wrapannotations=True,
1406 1411 wrapnonlinear=dots,
1407 1412 usedots=dots,
1408 1413 maxlinewidth=70):
1409 1414 ui.write(line)
1410 1415 ui.write("\n")
1411 1416
1412 1417 def debugdata(ui, repo, file_, rev):
1413 1418 """dump the contents of a data file revision"""
1414 1419 r = None
1415 1420 if repo:
1416 1421 filelog = repo.file(file_)
1417 1422 if len(filelog):
1418 1423 r = filelog
1419 1424 if not r:
1420 1425 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
1421 1426 file_[:-2] + ".i")
1422 1427 try:
1423 1428 ui.write(r.revision(r.lookup(rev)))
1424 1429 except KeyError:
1425 1430 raise util.Abort(_('invalid revision identifier %s') % rev)
1426 1431
1427 1432 def debugdate(ui, date, range=None, **opts):
1428 1433 """parse and display a date"""
1429 1434 if opts["extended"]:
1430 1435 d = util.parsedate(date, util.extendeddateformats)
1431 1436 else:
1432 1437 d = util.parsedate(date)
1433 1438 ui.write("internal: %s %s\n" % d)
1434 1439 ui.write("standard: %s\n" % util.datestr(d))
1435 1440 if range:
1436 1441 m = util.matchdate(range)
1437 1442 ui.write("match: %s\n" % m(d[0]))
1438 1443
1439 1444 def debugignore(ui, repo, *values, **opts):
1440 1445 """display the combined ignore pattern"""
1441 1446 ignore = repo.dirstate._ignore
1442 1447 if hasattr(ignore, 'includepat'):
1443 1448 ui.write("%s\n" % ignore.includepat)
1444 1449 else:
1445 1450 raise util.Abort(_("no ignore patterns found"))
1446 1451
1447 1452 def debugindex(ui, repo, file_, **opts):
1448 1453 """dump the contents of an index file"""
1449 1454 r = None
1450 1455 if repo:
1451 1456 filelog = repo.file(file_)
1452 1457 if len(filelog):
1453 1458 r = filelog
1454 1459
1455 1460 format = opts.get('format', 0)
1456 1461 if format not in (0, 1):
1457 1462 raise util.Abort(_("unknown format %d") % format)
1458 1463
1459 1464 if not r:
1460 1465 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1461 1466
1462 1467 if format == 0:
1463 1468 ui.write(" rev offset length base linkrev"
1464 1469 " nodeid p1 p2\n")
1465 1470 elif format == 1:
1466 1471 ui.write(" rev flag offset length"
1467 1472 " size base link p1 p2 nodeid\n")
1468 1473
1469 1474 for i in r:
1470 1475 node = r.node(i)
1471 1476 if format == 0:
1472 1477 try:
1473 1478 pp = r.parents(node)
1474 1479 except:
1475 1480 pp = [nullid, nullid]
1476 1481 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1477 1482 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
1478 1483 short(node), short(pp[0]), short(pp[1])))
1479 1484 elif format == 1:
1480 1485 pr = r.parentrevs(i)
1481 1486 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1482 1487 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1483 1488 r.base(i), r.linkrev(i), pr[0], pr[1], short(node)))
1484 1489
1485 1490 def debugindexdot(ui, repo, file_):
1486 1491 """dump an index DAG as a graphviz dot file"""
1487 1492 r = None
1488 1493 if repo:
1489 1494 filelog = repo.file(file_)
1490 1495 if len(filelog):
1491 1496 r = filelog
1492 1497 if not r:
1493 1498 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1494 1499 ui.write("digraph G {\n")
1495 1500 for i in r:
1496 1501 node = r.node(i)
1497 1502 pp = r.parents(node)
1498 1503 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1499 1504 if pp[1] != nullid:
1500 1505 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1501 1506 ui.write("}\n")
1502 1507
1503 1508 def debuginstall(ui):
1504 1509 '''test Mercurial installation
1505 1510
1506 1511 Returns 0 on success.
1507 1512 '''
1508 1513
1509 1514 def writetemp(contents):
1510 1515 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1511 1516 f = os.fdopen(fd, "wb")
1512 1517 f.write(contents)
1513 1518 f.close()
1514 1519 return name
1515 1520
1516 1521 problems = 0
1517 1522
1518 1523 # encoding
1519 1524 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1520 1525 try:
1521 1526 encoding.fromlocal("test")
1522 1527 except util.Abort, inst:
1523 1528 ui.write(" %s\n" % inst)
1524 1529 ui.write(_(" (check that your locale is properly set)\n"))
1525 1530 problems += 1
1526 1531
1527 1532 # compiled modules
1528 1533 ui.status(_("Checking installed modules (%s)...\n")
1529 1534 % os.path.dirname(__file__))
1530 1535 try:
1531 1536 import bdiff, mpatch, base85, osutil
1532 1537 except Exception, inst:
1533 1538 ui.write(" %s\n" % inst)
1534 1539 ui.write(_(" One or more extensions could not be found"))
1535 1540 ui.write(_(" (check that you compiled the extensions)\n"))
1536 1541 problems += 1
1537 1542
1538 1543 # templates
1539 1544 ui.status(_("Checking templates...\n"))
1540 1545 try:
1541 1546 import templater
1542 1547 templater.templater(templater.templatepath("map-cmdline.default"))
1543 1548 except Exception, inst:
1544 1549 ui.write(" %s\n" % inst)
1545 1550 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1546 1551 problems += 1
1547 1552
1548 1553 # editor
1549 1554 ui.status(_("Checking commit editor...\n"))
1550 1555 editor = ui.geteditor()
1551 1556 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1552 1557 if not cmdpath:
1553 1558 if editor == 'vi':
1554 1559 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1555 1560 ui.write(_(" (specify a commit editor in your configuration"
1556 1561 " file)\n"))
1557 1562 else:
1558 1563 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1559 1564 ui.write(_(" (specify a commit editor in your configuration"
1560 1565 " file)\n"))
1561 1566 problems += 1
1562 1567
1563 1568 # check username
1564 1569 ui.status(_("Checking username...\n"))
1565 1570 try:
1566 1571 ui.username()
1567 1572 except util.Abort, e:
1568 1573 ui.write(" %s\n" % e)
1569 1574 ui.write(_(" (specify a username in your configuration file)\n"))
1570 1575 problems += 1
1571 1576
1572 1577 if not problems:
1573 1578 ui.status(_("No problems detected\n"))
1574 1579 else:
1575 1580 ui.write(_("%s problems detected,"
1576 1581 " please check your install!\n") % problems)
1577 1582
1578 1583 return problems
1579 1584
1580 1585 def debugrename(ui, repo, file1, *pats, **opts):
1581 1586 """dump rename information"""
1582 1587
1583 1588 ctx = cmdutil.revsingle(repo, opts.get('rev'))
1584 1589 m = cmdutil.match(repo, (file1,) + pats, opts)
1585 1590 for abs in ctx.walk(m):
1586 1591 fctx = ctx[abs]
1587 1592 o = fctx.filelog().renamed(fctx.filenode())
1588 1593 rel = m.rel(abs)
1589 1594 if o:
1590 1595 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1591 1596 else:
1592 1597 ui.write(_("%s not renamed\n") % rel)
1593 1598
1594 1599 def debugwalk(ui, repo, *pats, **opts):
1595 1600 """show how files match on given patterns"""
1596 1601 m = cmdutil.match(repo, pats, opts)
1597 1602 items = list(repo.walk(m))
1598 1603 if not items:
1599 1604 return
1600 1605 fmt = 'f %%-%ds %%-%ds %%s' % (
1601 1606 max([len(abs) for abs in items]),
1602 1607 max([len(m.rel(abs)) for abs in items]))
1603 1608 for abs in items:
1604 1609 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1605 1610 ui.write("%s\n" % line.rstrip())
1606 1611
1607 1612 def debugwireargs(ui, repopath, *vals, **opts):
1608 1613 repo = hg.repository(hg.remoteui(ui, opts), repopath)
1609 1614 for opt in remoteopts:
1610 1615 del opts[opt[1]]
1611 1616 args = {}
1612 1617 for k, v in opts.iteritems():
1613 1618 if v:
1614 1619 args[k] = v
1615 1620 # run twice to check that we don't mess up the stream for the next command
1616 1621 res1 = repo.debugwireargs(*vals, **args)
1617 1622 res2 = repo.debugwireargs(*vals, **args)
1618 1623 ui.write("%s\n" % res1)
1619 1624 if res1 != res2:
1620 1625 ui.warn("%s\n" % res2)
1621 1626
1622 1627 def diff(ui, repo, *pats, **opts):
1623 1628 """diff repository (or selected files)
1624 1629
1625 1630 Show differences between revisions for the specified files.
1626 1631
1627 1632 Differences between files are shown using the unified diff format.
1628 1633
1629 1634 .. note::
1630 1635 diff may generate unexpected results for merges, as it will
1631 1636 default to comparing against the working directory's first
1632 1637 parent changeset if no revisions are specified.
1633 1638
1634 1639 When two revision arguments are given, then changes are shown
1635 1640 between those revisions. If only one revision is specified then
1636 1641 that revision is compared to the working directory, and, when no
1637 1642 revisions are specified, the working directory files are compared
1638 1643 to its parent.
1639 1644
1640 1645 Alternatively you can specify -c/--change with a revision to see
1641 1646 the changes in that changeset relative to its first parent.
1642 1647
1643 1648 Without the -a/--text option, diff will avoid generating diffs of
1644 1649 files it detects as binary. With -a, diff will generate a diff
1645 1650 anyway, probably with undesirable results.
1646 1651
1647 1652 Use the -g/--git option to generate diffs in the git extended diff
1648 1653 format. For more information, read :hg:`help diffs`.
1649 1654
1650 1655 Returns 0 on success.
1651 1656 """
1652 1657
1653 1658 revs = opts.get('rev')
1654 1659 change = opts.get('change')
1655 1660 stat = opts.get('stat')
1656 1661 reverse = opts.get('reverse')
1657 1662
1658 1663 if revs and change:
1659 1664 msg = _('cannot specify --rev and --change at the same time')
1660 1665 raise util.Abort(msg)
1661 1666 elif change:
1662 1667 node2 = cmdutil.revsingle(repo, change, None).node()
1663 1668 node1 = repo[node2].p1().node()
1664 1669 else:
1665 1670 node1, node2 = cmdutil.revpair(repo, revs)
1666 1671
1667 1672 if reverse:
1668 1673 node1, node2 = node2, node1
1669 1674
1670 1675 diffopts = patch.diffopts(ui, opts)
1671 1676 m = cmdutil.match(repo, pats, opts)
1672 1677 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1673 1678 listsubrepos=opts.get('subrepos'))
1674 1679
1675 1680 def export(ui, repo, *changesets, **opts):
1676 1681 """dump the header and diffs for one or more changesets
1677 1682
1678 1683 Print the changeset header and diffs for one or more revisions.
1679 1684
1680 1685 The information shown in the changeset header is: author, date,
1681 1686 branch name (if non-default), changeset hash, parent(s) and commit
1682 1687 comment.
1683 1688
1684 1689 .. note::
1685 1690 export may generate unexpected diff output for merge
1686 1691 changesets, as it will compare the merge changeset against its
1687 1692 first parent only.
1688 1693
1689 1694 Output may be to a file, in which case the name of the file is
1690 1695 given using a format string. The formatting rules are as follows:
1691 1696
1692 1697 :``%%``: literal "%" character
1693 1698 :``%H``: changeset hash (40 hexadecimal digits)
1694 1699 :``%N``: number of patches being generated
1695 1700 :``%R``: changeset revision number
1696 1701 :``%b``: basename of the exporting repository
1697 1702 :``%h``: short-form changeset hash (12 hexadecimal digits)
1698 1703 :``%n``: zero-padded sequence number, starting at 1
1699 1704 :``%r``: zero-padded changeset revision number
1700 1705
1701 1706 Without the -a/--text option, export will avoid generating diffs
1702 1707 of files it detects as binary. With -a, export will generate a
1703 1708 diff anyway, probably with undesirable results.
1704 1709
1705 1710 Use the -g/--git option to generate diffs in the git extended diff
1706 1711 format. See :hg:`help diffs` for more information.
1707 1712
1708 1713 With the --switch-parent option, the diff will be against the
1709 1714 second parent. It can be useful to review a merge.
1710 1715
1711 1716 Returns 0 on success.
1712 1717 """
1713 1718 changesets += tuple(opts.get('rev', []))
1714 1719 if not changesets:
1715 1720 raise util.Abort(_("export requires at least one changeset"))
1716 1721 revs = cmdutil.revrange(repo, changesets)
1717 1722 if len(revs) > 1:
1718 1723 ui.note(_('exporting patches:\n'))
1719 1724 else:
1720 1725 ui.note(_('exporting patch:\n'))
1721 1726 cmdutil.export(repo, revs, template=opts.get('output'),
1722 1727 switch_parent=opts.get('switch_parent'),
1723 1728 opts=patch.diffopts(ui, opts))
1724 1729
1725 1730 def forget(ui, repo, *pats, **opts):
1726 1731 """forget the specified files on the next commit
1727 1732
1728 1733 Mark the specified files so they will no longer be tracked
1729 1734 after the next commit.
1730 1735
1731 1736 This only removes files from the current branch, not from the
1732 1737 entire project history, and it does not delete them from the
1733 1738 working directory.
1734 1739
1735 1740 To undo a forget before the next commit, see :hg:`add`.
1736 1741
1737 1742 Returns 0 on success.
1738 1743 """
1739 1744
1740 1745 if not pats:
1741 1746 raise util.Abort(_('no files specified'))
1742 1747
1743 1748 m = cmdutil.match(repo, pats, opts)
1744 1749 s = repo.status(match=m, clean=True)
1745 1750 forget = sorted(s[0] + s[1] + s[3] + s[6])
1746 1751 errs = 0
1747 1752
1748 1753 for f in m.files():
1749 1754 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1750 1755 ui.warn(_('not removing %s: file is already untracked\n')
1751 1756 % m.rel(f))
1752 1757 errs = 1
1753 1758
1754 1759 for f in forget:
1755 1760 if ui.verbose or not m.exact(f):
1756 1761 ui.status(_('removing %s\n') % m.rel(f))
1757 1762
1758 1763 repo[None].remove(forget, unlink=False)
1759 1764 return errs
1760 1765
1761 1766 def grep(ui, repo, pattern, *pats, **opts):
1762 1767 """search for a pattern in specified files and revisions
1763 1768
1764 1769 Search revisions of files for a regular expression.
1765 1770
1766 1771 This command behaves differently than Unix grep. It only accepts
1767 1772 Python/Perl regexps. It searches repository history, not the
1768 1773 working directory. It always prints the revision number in which a
1769 1774 match appears.
1770 1775
1771 1776 By default, grep only prints output for the first revision of a
1772 1777 file in which it finds a match. To get it to print every revision
1773 1778 that contains a change in match status ("-" for a match that
1774 1779 becomes a non-match, or "+" for a non-match that becomes a match),
1775 1780 use the --all flag.
1776 1781
1777 1782 Returns 0 if a match is found, 1 otherwise.
1778 1783 """
1779 1784 reflags = 0
1780 1785 if opts.get('ignore_case'):
1781 1786 reflags |= re.I
1782 1787 try:
1783 1788 regexp = re.compile(pattern, reflags)
1784 1789 except re.error, inst:
1785 1790 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1786 1791 return 1
1787 1792 sep, eol = ':', '\n'
1788 1793 if opts.get('print0'):
1789 1794 sep = eol = '\0'
1790 1795
1791 1796 getfile = util.lrucachefunc(repo.file)
1792 1797
1793 1798 def matchlines(body):
1794 1799 begin = 0
1795 1800 linenum = 0
1796 1801 while True:
1797 1802 match = regexp.search(body, begin)
1798 1803 if not match:
1799 1804 break
1800 1805 mstart, mend = match.span()
1801 1806 linenum += body.count('\n', begin, mstart) + 1
1802 1807 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1803 1808 begin = body.find('\n', mend) + 1 or len(body)
1804 1809 lend = begin - 1
1805 1810 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1806 1811
1807 1812 class linestate(object):
1808 1813 def __init__(self, line, linenum, colstart, colend):
1809 1814 self.line = line
1810 1815 self.linenum = linenum
1811 1816 self.colstart = colstart
1812 1817 self.colend = colend
1813 1818
1814 1819 def __hash__(self):
1815 1820 return hash((self.linenum, self.line))
1816 1821
1817 1822 def __eq__(self, other):
1818 1823 return self.line == other.line
1819 1824
1820 1825 matches = {}
1821 1826 copies = {}
1822 1827 def grepbody(fn, rev, body):
1823 1828 matches[rev].setdefault(fn, [])
1824 1829 m = matches[rev][fn]
1825 1830 for lnum, cstart, cend, line in matchlines(body):
1826 1831 s = linestate(line, lnum, cstart, cend)
1827 1832 m.append(s)
1828 1833
1829 1834 def difflinestates(a, b):
1830 1835 sm = difflib.SequenceMatcher(None, a, b)
1831 1836 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1832 1837 if tag == 'insert':
1833 1838 for i in xrange(blo, bhi):
1834 1839 yield ('+', b[i])
1835 1840 elif tag == 'delete':
1836 1841 for i in xrange(alo, ahi):
1837 1842 yield ('-', a[i])
1838 1843 elif tag == 'replace':
1839 1844 for i in xrange(alo, ahi):
1840 1845 yield ('-', a[i])
1841 1846 for i in xrange(blo, bhi):
1842 1847 yield ('+', b[i])
1843 1848
1844 1849 def display(fn, ctx, pstates, states):
1845 1850 rev = ctx.rev()
1846 1851 datefunc = ui.quiet and util.shortdate or util.datestr
1847 1852 found = False
1848 1853 filerevmatches = {}
1849 1854 def binary():
1850 1855 flog = getfile(fn)
1851 1856 return util.binary(flog.read(ctx.filenode(fn)))
1852 1857
1853 1858 if opts.get('all'):
1854 1859 iter = difflinestates(pstates, states)
1855 1860 else:
1856 1861 iter = [('', l) for l in states]
1857 1862 for change, l in iter:
1858 1863 cols = [fn, str(rev)]
1859 1864 before, match, after = None, None, None
1860 1865 if opts.get('line_number'):
1861 1866 cols.append(str(l.linenum))
1862 1867 if opts.get('all'):
1863 1868 cols.append(change)
1864 1869 if opts.get('user'):
1865 1870 cols.append(ui.shortuser(ctx.user()))
1866 1871 if opts.get('date'):
1867 1872 cols.append(datefunc(ctx.date()))
1868 1873 if opts.get('files_with_matches'):
1869 1874 c = (fn, rev)
1870 1875 if c in filerevmatches:
1871 1876 continue
1872 1877 filerevmatches[c] = 1
1873 1878 else:
1874 1879 before = l.line[:l.colstart]
1875 1880 match = l.line[l.colstart:l.colend]
1876 1881 after = l.line[l.colend:]
1877 1882 ui.write(sep.join(cols))
1878 1883 if before is not None:
1879 1884 if not opts.get('text') and binary():
1880 1885 ui.write(sep + " Binary file matches")
1881 1886 else:
1882 1887 ui.write(sep + before)
1883 1888 ui.write(match, label='grep.match')
1884 1889 ui.write(after)
1885 1890 ui.write(eol)
1886 1891 found = True
1887 1892 return found
1888 1893
1889 1894 skip = {}
1890 1895 revfiles = {}
1891 1896 matchfn = cmdutil.match(repo, pats, opts)
1892 1897 found = False
1893 1898 follow = opts.get('follow')
1894 1899
1895 1900 def prep(ctx, fns):
1896 1901 rev = ctx.rev()
1897 1902 pctx = ctx.p1()
1898 1903 parent = pctx.rev()
1899 1904 matches.setdefault(rev, {})
1900 1905 matches.setdefault(parent, {})
1901 1906 files = revfiles.setdefault(rev, [])
1902 1907 for fn in fns:
1903 1908 flog = getfile(fn)
1904 1909 try:
1905 1910 fnode = ctx.filenode(fn)
1906 1911 except error.LookupError:
1907 1912 continue
1908 1913
1909 1914 copied = flog.renamed(fnode)
1910 1915 copy = follow and copied and copied[0]
1911 1916 if copy:
1912 1917 copies.setdefault(rev, {})[fn] = copy
1913 1918 if fn in skip:
1914 1919 if copy:
1915 1920 skip[copy] = True
1916 1921 continue
1917 1922 files.append(fn)
1918 1923
1919 1924 if fn not in matches[rev]:
1920 1925 grepbody(fn, rev, flog.read(fnode))
1921 1926
1922 1927 pfn = copy or fn
1923 1928 if pfn not in matches[parent]:
1924 1929 try:
1925 1930 fnode = pctx.filenode(pfn)
1926 1931 grepbody(pfn, parent, flog.read(fnode))
1927 1932 except error.LookupError:
1928 1933 pass
1929 1934
1930 1935 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
1931 1936 rev = ctx.rev()
1932 1937 parent = ctx.p1().rev()
1933 1938 for fn in sorted(revfiles.get(rev, [])):
1934 1939 states = matches[rev][fn]
1935 1940 copy = copies.get(rev, {}).get(fn)
1936 1941 if fn in skip:
1937 1942 if copy:
1938 1943 skip[copy] = True
1939 1944 continue
1940 1945 pstates = matches.get(parent, {}).get(copy or fn, [])
1941 1946 if pstates or states:
1942 1947 r = display(fn, ctx, pstates, states)
1943 1948 found = found or r
1944 1949 if r and not opts.get('all'):
1945 1950 skip[fn] = True
1946 1951 if copy:
1947 1952 skip[copy] = True
1948 1953 del matches[rev]
1949 1954 del revfiles[rev]
1950 1955
1951 1956 return not found
1952 1957
1953 1958 def heads(ui, repo, *branchrevs, **opts):
1954 1959 """show current repository heads or show branch heads
1955 1960
1956 1961 With no arguments, show all repository branch heads.
1957 1962
1958 1963 Repository "heads" are changesets with no child changesets. They are
1959 1964 where development generally takes place and are the usual targets
1960 1965 for update and merge operations. Branch heads are changesets that have
1961 1966 no child changeset on the same branch.
1962 1967
1963 1968 If one or more REVs are given, only branch heads on the branches
1964 1969 associated with the specified changesets are shown.
1965 1970
1966 1971 If -c/--closed is specified, also show branch heads marked closed
1967 1972 (see :hg:`commit --close-branch`).
1968 1973
1969 1974 If STARTREV is specified, only those heads that are descendants of
1970 1975 STARTREV will be displayed.
1971 1976
1972 1977 If -t/--topo is specified, named branch mechanics will be ignored and only
1973 1978 changesets without children will be shown.
1974 1979
1975 1980 Returns 0 if matching heads are found, 1 if not.
1976 1981 """
1977 1982
1978 1983 start = None
1979 1984 if 'rev' in opts:
1980 1985 start = cmdutil.revsingle(repo, opts['rev'], None).node()
1981 1986
1982 1987 if opts.get('topo'):
1983 1988 heads = [repo[h] for h in repo.heads(start)]
1984 1989 else:
1985 1990 heads = []
1986 1991 for b, ls in repo.branchmap().iteritems():
1987 1992 if start is None:
1988 1993 heads += [repo[h] for h in ls]
1989 1994 continue
1990 1995 startrev = repo.changelog.rev(start)
1991 1996 descendants = set(repo.changelog.descendants(startrev))
1992 1997 descendants.add(startrev)
1993 1998 rev = repo.changelog.rev
1994 1999 heads += [repo[h] for h in ls if rev(h) in descendants]
1995 2000
1996 2001 if branchrevs:
1997 2002 branches = set(repo[br].branch() for br in branchrevs)
1998 2003 heads = [h for h in heads if h.branch() in branches]
1999 2004
2000 2005 if not opts.get('closed'):
2001 2006 heads = [h for h in heads if not h.extra().get('close')]
2002 2007
2003 2008 if opts.get('active') and branchrevs:
2004 2009 dagheads = repo.heads(start)
2005 2010 heads = [h for h in heads if h.node() in dagheads]
2006 2011
2007 2012 if branchrevs:
2008 2013 haveheads = set(h.branch() for h in heads)
2009 2014 if branches - haveheads:
2010 2015 headless = ', '.join(b for b in branches - haveheads)
2011 2016 msg = _('no open branch heads found on branches %s')
2012 2017 if opts.get('rev'):
2013 2018 msg += _(' (started at %s)' % opts['rev'])
2014 2019 ui.warn((msg + '\n') % headless)
2015 2020
2016 2021 if not heads:
2017 2022 return 1
2018 2023
2019 2024 heads = sorted(heads, key=lambda x: -x.rev())
2020 2025 displayer = cmdutil.show_changeset(ui, repo, opts)
2021 2026 for ctx in heads:
2022 2027 displayer.show(ctx)
2023 2028 displayer.close()
2024 2029
2025 2030 def help_(ui, name=None, with_version=False, unknowncmd=False, full=True):
2026 2031 """show help for a given topic or a help overview
2027 2032
2028 2033 With no arguments, print a list of commands with short help messages.
2029 2034
2030 2035 Given a topic, extension, or command name, print help for that
2031 2036 topic.
2032 2037
2033 2038 Returns 0 if successful.
2034 2039 """
2035 2040 option_lists = []
2036 2041 textwidth = min(ui.termwidth(), 80) - 2
2037 2042
2038 2043 def addglobalopts(aliases):
2039 2044 if ui.verbose:
2040 2045 option_lists.append((_("global options:"), globalopts))
2041 2046 if name == 'shortlist':
2042 2047 option_lists.append((_('use "hg help" for the full list '
2043 2048 'of commands'), ()))
2044 2049 else:
2045 2050 if name == 'shortlist':
2046 2051 msg = _('use "hg help" for the full list of commands '
2047 2052 'or "hg -v" for details')
2048 2053 elif name and not full:
2049 2054 msg = _('use "hg help %s" to show the full help text' % name)
2050 2055 elif aliases:
2051 2056 msg = _('use "hg -v help%s" to show builtin aliases and '
2052 2057 'global options') % (name and " " + name or "")
2053 2058 else:
2054 2059 msg = _('use "hg -v help %s" to show global options') % name
2055 2060 option_lists.append((msg, ()))
2056 2061
2057 2062 def helpcmd(name):
2058 2063 if with_version:
2059 2064 version_(ui)
2060 2065 ui.write('\n')
2061 2066
2062 2067 try:
2063 2068 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
2064 2069 except error.AmbiguousCommand, inst:
2065 2070 # py3k fix: except vars can't be used outside the scope of the
2066 2071 # except block, nor can be used inside a lambda. python issue4617
2067 2072 prefix = inst.args[0]
2068 2073 select = lambda c: c.lstrip('^').startswith(prefix)
2069 2074 helplist(_('list of commands:\n\n'), select)
2070 2075 return
2071 2076
2072 2077 # check if it's an invalid alias and display its error if it is
2073 2078 if getattr(entry[0], 'badalias', False):
2074 2079 if not unknowncmd:
2075 2080 entry[0](ui)
2076 2081 return
2077 2082
2078 2083 # synopsis
2079 2084 if len(entry) > 2:
2080 2085 if entry[2].startswith('hg'):
2081 2086 ui.write("%s\n" % entry[2])
2082 2087 else:
2083 2088 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
2084 2089 else:
2085 2090 ui.write('hg %s\n' % aliases[0])
2086 2091
2087 2092 # aliases
2088 2093 if full and not ui.quiet and len(aliases) > 1:
2089 2094 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
2090 2095
2091 2096 # description
2092 2097 doc = gettext(entry[0].__doc__)
2093 2098 if not doc:
2094 2099 doc = _("(no help text available)")
2095 2100 if hasattr(entry[0], 'definition'): # aliased command
2096 2101 if entry[0].definition.startswith('!'): # shell alias
2097 2102 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
2098 2103 else:
2099 2104 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
2100 2105 if ui.quiet or not full:
2101 2106 doc = doc.splitlines()[0]
2102 2107 keep = ui.verbose and ['verbose'] or []
2103 2108 formatted, pruned = minirst.format(doc, textwidth, keep=keep)
2104 2109 ui.write("\n%s\n" % formatted)
2105 2110 if pruned:
2106 2111 ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
2107 2112
2108 2113 if not ui.quiet:
2109 2114 # options
2110 2115 if entry[1]:
2111 2116 option_lists.append((_("options:\n"), entry[1]))
2112 2117
2113 2118 addglobalopts(False)
2114 2119
2115 2120 def helplist(header, select=None):
2116 2121 h = {}
2117 2122 cmds = {}
2118 2123 for c, e in table.iteritems():
2119 2124 f = c.split("|", 1)[0]
2120 2125 if select and not select(f):
2121 2126 continue
2122 2127 if (not select and name != 'shortlist' and
2123 2128 e[0].__module__ != __name__):
2124 2129 continue
2125 2130 if name == "shortlist" and not f.startswith("^"):
2126 2131 continue
2127 2132 f = f.lstrip("^")
2128 2133 if not ui.debugflag and f.startswith("debug"):
2129 2134 continue
2130 2135 doc = e[0].__doc__
2131 2136 if doc and 'DEPRECATED' in doc and not ui.verbose:
2132 2137 continue
2133 2138 doc = gettext(doc)
2134 2139 if not doc:
2135 2140 doc = _("(no help text available)")
2136 2141 h[f] = doc.splitlines()[0].rstrip()
2137 2142 cmds[f] = c.lstrip("^")
2138 2143
2139 2144 if not h:
2140 2145 ui.status(_('no commands defined\n'))
2141 2146 return
2142 2147
2143 2148 ui.status(header)
2144 2149 fns = sorted(h)
2145 2150 m = max(map(len, fns))
2146 2151 for f in fns:
2147 2152 if ui.verbose:
2148 2153 commands = cmds[f].replace("|",", ")
2149 2154 ui.write(" %s:\n %s\n"%(commands, h[f]))
2150 2155 else:
2151 2156 ui.write('%s\n' % (util.wrap(h[f], textwidth,
2152 2157 initindent=' %-*s ' % (m, f),
2153 2158 hangindent=' ' * (m + 4))))
2154 2159
2155 2160 if not ui.quiet:
2156 2161 addglobalopts(True)
2157 2162
2158 2163 def helptopic(name):
2159 2164 for names, header, doc in help.helptable:
2160 2165 if name in names:
2161 2166 break
2162 2167 else:
2163 2168 raise error.UnknownCommand(name)
2164 2169
2165 2170 # description
2166 2171 if not doc:
2167 2172 doc = _("(no help text available)")
2168 2173 if hasattr(doc, '__call__'):
2169 2174 doc = doc()
2170 2175
2171 2176 ui.write("%s\n\n" % header)
2172 2177 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
2173 2178
2174 2179 def helpext(name):
2175 2180 try:
2176 2181 mod = extensions.find(name)
2177 2182 doc = gettext(mod.__doc__) or _('no help text available')
2178 2183 except KeyError:
2179 2184 mod = None
2180 2185 doc = extensions.disabledext(name)
2181 2186 if not doc:
2182 2187 raise error.UnknownCommand(name)
2183 2188
2184 2189 if '\n' not in doc:
2185 2190 head, tail = doc, ""
2186 2191 else:
2187 2192 head, tail = doc.split('\n', 1)
2188 2193 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
2189 2194 if tail:
2190 2195 ui.write(minirst.format(tail, textwidth))
2191 2196 ui.status('\n\n')
2192 2197
2193 2198 if mod:
2194 2199 try:
2195 2200 ct = mod.cmdtable
2196 2201 except AttributeError:
2197 2202 ct = {}
2198 2203 modcmds = set([c.split('|', 1)[0] for c in ct])
2199 2204 helplist(_('list of commands:\n\n'), modcmds.__contains__)
2200 2205 else:
2201 2206 ui.write(_('use "hg help extensions" for information on enabling '
2202 2207 'extensions\n'))
2203 2208
2204 2209 def helpextcmd(name):
2205 2210 cmd, ext, mod = extensions.disabledcmd(ui, name, ui.config('ui', 'strict'))
2206 2211 doc = gettext(mod.__doc__).splitlines()[0]
2207 2212
2208 2213 msg = help.listexts(_("'%s' is provided by the following "
2209 2214 "extension:") % cmd, {ext: doc}, len(ext),
2210 2215 indent=4)
2211 2216 ui.write(minirst.format(msg, textwidth))
2212 2217 ui.write('\n\n')
2213 2218 ui.write(_('use "hg help extensions" for information on enabling '
2214 2219 'extensions\n'))
2215 2220
2216 2221 help.addtopichook('revsets', revset.makedoc)
2217 2222 help.addtopichook('templates', templatekw.makedoc)
2218 2223 help.addtopichook('templates', templatefilters.makedoc)
2219 2224
2220 2225 if name and name != 'shortlist':
2221 2226 i = None
2222 2227 if unknowncmd:
2223 2228 queries = (helpextcmd,)
2224 2229 else:
2225 2230 queries = (helptopic, helpcmd, helpext, helpextcmd)
2226 2231 for f in queries:
2227 2232 try:
2228 2233 f(name)
2229 2234 i = None
2230 2235 break
2231 2236 except error.UnknownCommand, inst:
2232 2237 i = inst
2233 2238 if i:
2234 2239 raise i
2235 2240
2236 2241 else:
2237 2242 # program name
2238 2243 if ui.verbose or with_version:
2239 2244 version_(ui)
2240 2245 else:
2241 2246 ui.status(_("Mercurial Distributed SCM\n"))
2242 2247 ui.status('\n')
2243 2248
2244 2249 # list of commands
2245 2250 if name == "shortlist":
2246 2251 header = _('basic commands:\n\n')
2247 2252 else:
2248 2253 header = _('list of commands:\n\n')
2249 2254
2250 2255 helplist(header)
2251 2256 if name != 'shortlist':
2252 2257 exts, maxlength = extensions.enabled()
2253 2258 text = help.listexts(_('enabled extensions:'), exts, maxlength)
2254 2259 if text:
2255 2260 ui.write("\n%s\n" % minirst.format(text, textwidth))
2256 2261
2257 2262 # list all option lists
2258 2263 opt_output = []
2259 2264 multioccur = False
2260 2265 for title, options in option_lists:
2261 2266 opt_output.append(("\n%s" % title, None))
2262 2267 for option in options:
2263 2268 if len(option) == 5:
2264 2269 shortopt, longopt, default, desc, optlabel = option
2265 2270 else:
2266 2271 shortopt, longopt, default, desc = option
2267 2272 optlabel = _("VALUE") # default label
2268 2273
2269 2274 if _("DEPRECATED") in desc and not ui.verbose:
2270 2275 continue
2271 2276 if isinstance(default, list):
2272 2277 numqualifier = " %s [+]" % optlabel
2273 2278 multioccur = True
2274 2279 elif (default is not None) and not isinstance(default, bool):
2275 2280 numqualifier = " %s" % optlabel
2276 2281 else:
2277 2282 numqualifier = ""
2278 2283 opt_output.append(("%2s%s" %
2279 2284 (shortopt and "-%s" % shortopt,
2280 2285 longopt and " --%s%s" %
2281 2286 (longopt, numqualifier)),
2282 2287 "%s%s" % (desc,
2283 2288 default
2284 2289 and _(" (default: %s)") % default
2285 2290 or "")))
2286 2291 if multioccur:
2287 2292 msg = _("\n[+] marked option can be specified multiple times")
2288 2293 if ui.verbose and name != 'shortlist':
2289 2294 opt_output.append((msg, None))
2290 2295 else:
2291 2296 opt_output.insert(-1, (msg, None))
2292 2297
2293 2298 if not name:
2294 2299 ui.write(_("\nadditional help topics:\n\n"))
2295 2300 topics = []
2296 2301 for names, header, doc in help.helptable:
2297 2302 topics.append((sorted(names, key=len, reverse=True)[0], header))
2298 2303 topics_len = max([len(s[0]) for s in topics])
2299 2304 for t, desc in topics:
2300 2305 ui.write(" %-*s %s\n" % (topics_len, t, desc))
2301 2306
2302 2307 if opt_output:
2303 2308 colwidth = encoding.colwidth
2304 2309 # normalize: (opt or message, desc or None, width of opt)
2305 2310 entries = [desc and (opt, desc, colwidth(opt)) or (opt, None, 0)
2306 2311 for opt, desc in opt_output]
2307 2312 hanging = max([e[2] for e in entries])
2308 2313 for opt, desc, width in entries:
2309 2314 if desc:
2310 2315 initindent = ' %s%s ' % (opt, ' ' * (hanging - width))
2311 2316 hangindent = ' ' * (hanging + 3)
2312 2317 ui.write('%s\n' % (util.wrap(desc, textwidth,
2313 2318 initindent=initindent,
2314 2319 hangindent=hangindent)))
2315 2320 else:
2316 2321 ui.write("%s\n" % opt)
2317 2322
2318 2323 def identify(ui, repo, source=None, rev=None,
2319 2324 num=None, id=None, branch=None, tags=None, bookmarks=None):
2320 2325 """identify the working copy or specified revision
2321 2326
2322 2327 Print a summary identifying the repository state at REV using one or
2323 2328 two parent hash identifiers, followed by a "+" if the working
2324 2329 directory has uncommitted changes, the branch name (if not default),
2325 2330 a list of tags, and a list of bookmarks.
2326 2331
2327 2332 When REV is not given, print a summary of the current state of the
2328 2333 repository.
2329 2334
2330 2335 Specifying a path to a repository root or Mercurial bundle will
2331 2336 cause lookup to operate on that repository/bundle.
2332 2337
2333 2338 Returns 0 if successful.
2334 2339 """
2335 2340
2336 2341 if not repo and not source:
2337 2342 raise util.Abort(_("there is no Mercurial repository here "
2338 2343 "(.hg not found)"))
2339 2344
2340 2345 hexfunc = ui.debugflag and hex or short
2341 2346 default = not (num or id or branch or tags or bookmarks)
2342 2347 output = []
2343 2348 revs = []
2344 2349
2345 2350 if source:
2346 2351 source, branches = hg.parseurl(ui.expandpath(source))
2347 2352 repo = hg.repository(ui, source)
2348 2353 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
2349 2354
2350 2355 if not repo.local():
2351 2356 if num or branch or tags:
2352 2357 raise util.Abort(
2353 2358 _("can't query remote revision number, branch, or tags"))
2354 2359 if not rev and revs:
2355 2360 rev = revs[0]
2356 2361 if not rev:
2357 2362 rev = "tip"
2358 2363
2359 2364 remoterev = repo.lookup(rev)
2360 2365 if default or id:
2361 2366 output = [hexfunc(remoterev)]
2362 2367
2363 2368 def getbms():
2364 2369 bms = []
2365 2370
2366 2371 if 'bookmarks' in repo.listkeys('namespaces'):
2367 2372 hexremoterev = hex(remoterev)
2368 2373 bms = [bm for bm, bmr in repo.listkeys('bookmarks').iteritems()
2369 2374 if bmr == hexremoterev]
2370 2375
2371 2376 return bms
2372 2377
2373 2378 if bookmarks:
2374 2379 output.extend(getbms())
2375 2380 elif default and not ui.quiet:
2376 2381 # multiple bookmarks for a single parent separated by '/'
2377 2382 bm = '/'.join(getbms())
2378 2383 if bm:
2379 2384 output.append(bm)
2380 2385 else:
2381 2386 if not rev:
2382 2387 ctx = repo[None]
2383 2388 parents = ctx.parents()
2384 2389 changed = ""
2385 2390 if default or id or num:
2386 2391 changed = util.any(repo.status()) and "+" or ""
2387 2392 if default or id:
2388 2393 output = ["%s%s" %
2389 2394 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
2390 2395 if num:
2391 2396 output.append("%s%s" %
2392 2397 ('+'.join([str(p.rev()) for p in parents]), changed))
2393 2398 else:
2394 2399 ctx = cmdutil.revsingle(repo, rev)
2395 2400 if default or id:
2396 2401 output = [hexfunc(ctx.node())]
2397 2402 if num:
2398 2403 output.append(str(ctx.rev()))
2399 2404
2400 2405 if default and not ui.quiet:
2401 2406 b = ctx.branch()
2402 2407 if b != 'default':
2403 2408 output.append("(%s)" % b)
2404 2409
2405 2410 # multiple tags for a single parent separated by '/'
2406 2411 t = '/'.join(ctx.tags())
2407 2412 if t:
2408 2413 output.append(t)
2409 2414
2410 2415 # multiple bookmarks for a single parent separated by '/'
2411 2416 bm = '/'.join(ctx.bookmarks())
2412 2417 if bm:
2413 2418 output.append(bm)
2414 2419 else:
2415 2420 if branch:
2416 2421 output.append(ctx.branch())
2417 2422
2418 2423 if tags:
2419 2424 output.extend(ctx.tags())
2420 2425
2421 2426 if bookmarks:
2422 2427 output.extend(ctx.bookmarks())
2423 2428
2424 2429 ui.write("%s\n" % ' '.join(output))
2425 2430
2426 2431 def import_(ui, repo, patch1, *patches, **opts):
2427 2432 """import an ordered set of patches
2428 2433
2429 2434 Import a list of patches and commit them individually (unless
2430 2435 --no-commit is specified).
2431 2436
2432 2437 If there are outstanding changes in the working directory, import
2433 2438 will abort unless given the -f/--force flag.
2434 2439
2435 2440 You can import a patch straight from a mail message. Even patches
2436 2441 as attachments work (to use the body part, it must have type
2437 2442 text/plain or text/x-patch). From and Subject headers of email
2438 2443 message are used as default committer and commit message. All
2439 2444 text/plain body parts before first diff are added to commit
2440 2445 message.
2441 2446
2442 2447 If the imported patch was generated by :hg:`export`, user and
2443 2448 description from patch override values from message headers and
2444 2449 body. Values given on command line with -m/--message and -u/--user
2445 2450 override these.
2446 2451
2447 2452 If --exact is specified, import will set the working directory to
2448 2453 the parent of each patch before applying it, and will abort if the
2449 2454 resulting changeset has a different ID than the one recorded in
2450 2455 the patch. This may happen due to character set problems or other
2451 2456 deficiencies in the text patch format.
2452 2457
2453 2458 With -s/--similarity, hg will attempt to discover renames and
2454 2459 copies in the patch in the same way as 'addremove'.
2455 2460
2456 2461 To read a patch from standard input, use "-" as the patch name. If
2457 2462 a URL is specified, the patch will be downloaded from it.
2458 2463 See :hg:`help dates` for a list of formats valid for -d/--date.
2459 2464
2460 2465 Returns 0 on success.
2461 2466 """
2462 2467 patches = (patch1,) + patches
2463 2468
2464 2469 date = opts.get('date')
2465 2470 if date:
2466 2471 opts['date'] = util.parsedate(date)
2467 2472
2468 2473 try:
2469 2474 sim = float(opts.get('similarity') or 0)
2470 2475 except ValueError:
2471 2476 raise util.Abort(_('similarity must be a number'))
2472 2477 if sim < 0 or sim > 100:
2473 2478 raise util.Abort(_('similarity must be between 0 and 100'))
2474 2479
2475 2480 if opts.get('exact') or not opts.get('force'):
2476 2481 cmdutil.bail_if_changed(repo)
2477 2482
2478 2483 d = opts["base"]
2479 2484 strip = opts["strip"]
2480 2485 wlock = lock = None
2481 2486 msgs = []
2482 2487
2483 2488 def tryone(ui, hunk):
2484 2489 tmpname, message, user, date, branch, nodeid, p1, p2 = \
2485 2490 patch.extract(ui, hunk)
2486 2491
2487 2492 if not tmpname:
2488 2493 return None
2489 2494 commitid = _('to working directory')
2490 2495
2491 2496 try:
2492 2497 cmdline_message = cmdutil.logmessage(opts)
2493 2498 if cmdline_message:
2494 2499 # pickup the cmdline msg
2495 2500 message = cmdline_message
2496 2501 elif message:
2497 2502 # pickup the patch msg
2498 2503 message = message.strip()
2499 2504 else:
2500 2505 # launch the editor
2501 2506 message = None
2502 2507 ui.debug('message:\n%s\n' % message)
2503 2508
2504 2509 wp = repo.parents()
2505 2510 if opts.get('exact'):
2506 2511 if not nodeid or not p1:
2507 2512 raise util.Abort(_('not a Mercurial patch'))
2508 2513 p1 = repo.lookup(p1)
2509 2514 p2 = repo.lookup(p2 or hex(nullid))
2510 2515
2511 2516 if p1 != wp[0].node():
2512 2517 hg.clean(repo, p1)
2513 2518 repo.dirstate.setparents(p1, p2)
2514 2519 elif p2:
2515 2520 try:
2516 2521 p1 = repo.lookup(p1)
2517 2522 p2 = repo.lookup(p2)
2518 2523 if p1 == wp[0].node():
2519 2524 repo.dirstate.setparents(p1, p2)
2520 2525 except error.RepoError:
2521 2526 pass
2522 2527 if opts.get('exact') or opts.get('import_branch'):
2523 2528 repo.dirstate.setbranch(branch or 'default')
2524 2529
2525 2530 files = {}
2526 2531 try:
2527 2532 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
2528 2533 files=files, eolmode=None)
2529 2534 finally:
2530 2535 files = cmdutil.updatedir(ui, repo, files,
2531 2536 similarity=sim / 100.0)
2532 2537 if opts.get('no_commit'):
2533 2538 if message:
2534 2539 msgs.append(message)
2535 2540 else:
2536 2541 if opts.get('exact'):
2537 2542 m = None
2538 2543 else:
2539 2544 m = cmdutil.matchfiles(repo, files or [])
2540 2545 n = repo.commit(message, opts.get('user') or user,
2541 2546 opts.get('date') or date, match=m,
2542 2547 editor=cmdutil.commiteditor)
2543 2548 if opts.get('exact'):
2544 2549 if hex(n) != nodeid:
2545 2550 repo.rollback()
2546 2551 raise util.Abort(_('patch is damaged'
2547 2552 ' or loses information'))
2548 2553 # Force a dirstate write so that the next transaction
2549 2554 # backups an up-do-date file.
2550 2555 repo.dirstate.write()
2551 2556 if n:
2552 2557 commitid = short(n)
2553 2558
2554 2559 return commitid
2555 2560 finally:
2556 2561 os.unlink(tmpname)
2557 2562
2558 2563 try:
2559 2564 wlock = repo.wlock()
2560 2565 lock = repo.lock()
2561 2566 lastcommit = None
2562 2567 for p in patches:
2563 2568 pf = os.path.join(d, p)
2564 2569
2565 2570 if pf == '-':
2566 2571 ui.status(_("applying patch from stdin\n"))
2567 2572 pf = sys.stdin
2568 2573 else:
2569 2574 ui.status(_("applying %s\n") % p)
2570 2575 pf = url.open(ui, pf)
2571 2576
2572 2577 haspatch = False
2573 2578 for hunk in patch.split(pf):
2574 2579 commitid = tryone(ui, hunk)
2575 2580 if commitid:
2576 2581 haspatch = True
2577 2582 if lastcommit:
2578 2583 ui.status(_('applied %s\n') % lastcommit)
2579 2584 lastcommit = commitid
2580 2585
2581 2586 if not haspatch:
2582 2587 raise util.Abort(_('no diffs found'))
2583 2588
2584 2589 if msgs:
2585 2590 repo.opener('last-message.txt', 'wb').write('\n* * *\n'.join(msgs))
2586 2591 finally:
2587 2592 release(lock, wlock)
2588 2593
2589 2594 def incoming(ui, repo, source="default", **opts):
2590 2595 """show new changesets found in source
2591 2596
2592 2597 Show new changesets found in the specified path/URL or the default
2593 2598 pull location. These are the changesets that would have been pulled
2594 2599 if a pull at the time you issued this command.
2595 2600
2596 2601 For remote repository, using --bundle avoids downloading the
2597 2602 changesets twice if the incoming is followed by a pull.
2598 2603
2599 2604 See pull for valid source format details.
2600 2605
2601 2606 Returns 0 if there are incoming changes, 1 otherwise.
2602 2607 """
2603 2608 if opts.get('bundle') and opts.get('subrepos'):
2604 2609 raise util.Abort(_('cannot combine --bundle and --subrepos'))
2605 2610
2606 2611 if opts.get('bookmarks'):
2607 2612 source, branches = hg.parseurl(ui.expandpath(source),
2608 2613 opts.get('branch'))
2609 2614 other = hg.repository(hg.remoteui(repo, opts), source)
2610 2615 if 'bookmarks' not in other.listkeys('namespaces'):
2611 2616 ui.warn(_("remote doesn't support bookmarks\n"))
2612 2617 return 0
2613 2618 ui.status(_('comparing with %s\n') % util.hidepassword(source))
2614 2619 return bookmarks.diff(ui, repo, other)
2615 2620
2616 2621 ret = hg.incoming(ui, repo, source, opts)
2617 2622 return ret
2618 2623
2619 2624 def init(ui, dest=".", **opts):
2620 2625 """create a new repository in the given directory
2621 2626
2622 2627 Initialize a new repository in the given directory. If the given
2623 2628 directory does not exist, it will be created.
2624 2629
2625 2630 If no directory is given, the current directory is used.
2626 2631
2627 2632 It is possible to specify an ``ssh://`` URL as the destination.
2628 2633 See :hg:`help urls` for more information.
2629 2634
2630 2635 Returns 0 on success.
2631 2636 """
2632 2637 hg.repository(hg.remoteui(ui, opts), ui.expandpath(dest), create=1)
2633 2638
2634 2639 def locate(ui, repo, *pats, **opts):
2635 2640 """locate files matching specific patterns
2636 2641
2637 2642 Print files under Mercurial control in the working directory whose
2638 2643 names match the given patterns.
2639 2644
2640 2645 By default, this command searches all directories in the working
2641 2646 directory. To search just the current directory and its
2642 2647 subdirectories, use "--include .".
2643 2648
2644 2649 If no patterns are given to match, this command prints the names
2645 2650 of all files under Mercurial control in the working directory.
2646 2651
2647 2652 If you want to feed the output of this command into the "xargs"
2648 2653 command, use the -0 option to both this command and "xargs". This
2649 2654 will avoid the problem of "xargs" treating single filenames that
2650 2655 contain whitespace as multiple filenames.
2651 2656
2652 2657 Returns 0 if a match is found, 1 otherwise.
2653 2658 """
2654 2659 end = opts.get('print0') and '\0' or '\n'
2655 2660 rev = cmdutil.revsingle(repo, opts.get('rev'), None).node()
2656 2661
2657 2662 ret = 1
2658 2663 m = cmdutil.match(repo, pats, opts, default='relglob')
2659 2664 m.bad = lambda x, y: False
2660 2665 for abs in repo[rev].walk(m):
2661 2666 if not rev and abs not in repo.dirstate:
2662 2667 continue
2663 2668 if opts.get('fullpath'):
2664 2669 ui.write(repo.wjoin(abs), end)
2665 2670 else:
2666 2671 ui.write(((pats and m.rel(abs)) or abs), end)
2667 2672 ret = 0
2668 2673
2669 2674 return ret
2670 2675
2671 2676 def log(ui, repo, *pats, **opts):
2672 2677 """show revision history of entire repository or files
2673 2678
2674 2679 Print the revision history of the specified files or the entire
2675 2680 project.
2676 2681
2677 2682 File history is shown without following rename or copy history of
2678 2683 files. Use -f/--follow with a filename to follow history across
2679 2684 renames and copies. --follow without a filename will only show
2680 2685 ancestors or descendants of the starting revision. --follow-first
2681 2686 only follows the first parent of merge revisions.
2682 2687
2683 2688 If no revision range is specified, the default is ``tip:0`` unless
2684 2689 --follow is set, in which case the working directory parent is
2685 2690 used as the starting revision. You can specify a revision set for
2686 2691 log, see :hg:`help revsets` for more information.
2687 2692
2688 2693 See :hg:`help dates` for a list of formats valid for -d/--date.
2689 2694
2690 2695 By default this command prints revision number and changeset id,
2691 2696 tags, non-trivial parents, user, date and time, and a summary for
2692 2697 each commit. When the -v/--verbose switch is used, the list of
2693 2698 changed files and full commit message are shown.
2694 2699
2695 2700 .. note::
2696 2701 log -p/--patch may generate unexpected diff output for merge
2697 2702 changesets, as it will only compare the merge changeset against
2698 2703 its first parent. Also, only files different from BOTH parents
2699 2704 will appear in files:.
2700 2705
2701 2706 Returns 0 on success.
2702 2707 """
2703 2708
2704 2709 matchfn = cmdutil.match(repo, pats, opts)
2705 2710 limit = cmdutil.loglimit(opts)
2706 2711 count = 0
2707 2712
2708 2713 endrev = None
2709 2714 if opts.get('copies') and opts.get('rev'):
2710 2715 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
2711 2716
2712 2717 df = False
2713 2718 if opts["date"]:
2714 2719 df = util.matchdate(opts["date"])
2715 2720
2716 2721 branches = opts.get('branch', []) + opts.get('only_branch', [])
2717 2722 opts['branch'] = [repo.lookupbranch(b) for b in branches]
2718 2723
2719 2724 displayer = cmdutil.show_changeset(ui, repo, opts, True)
2720 2725 def prep(ctx, fns):
2721 2726 rev = ctx.rev()
2722 2727 parents = [p for p in repo.changelog.parentrevs(rev)
2723 2728 if p != nullrev]
2724 2729 if opts.get('no_merges') and len(parents) == 2:
2725 2730 return
2726 2731 if opts.get('only_merges') and len(parents) != 2:
2727 2732 return
2728 2733 if opts.get('branch') and ctx.branch() not in opts['branch']:
2729 2734 return
2730 2735 if df and not df(ctx.date()[0]):
2731 2736 return
2732 2737 if opts['user'] and not [k for k in opts['user']
2733 2738 if k.lower() in ctx.user().lower()]:
2734 2739 return
2735 2740 if opts.get('keyword'):
2736 2741 for k in [kw.lower() for kw in opts['keyword']]:
2737 2742 if (k in ctx.user().lower() or
2738 2743 k in ctx.description().lower() or
2739 2744 k in " ".join(ctx.files()).lower()):
2740 2745 break
2741 2746 else:
2742 2747 return
2743 2748
2744 2749 copies = None
2745 2750 if opts.get('copies') and rev:
2746 2751 copies = []
2747 2752 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2748 2753 for fn in ctx.files():
2749 2754 rename = getrenamed(fn, rev)
2750 2755 if rename:
2751 2756 copies.append((fn, rename[0]))
2752 2757
2753 2758 revmatchfn = None
2754 2759 if opts.get('patch') or opts.get('stat'):
2755 2760 if opts.get('follow') or opts.get('follow_first'):
2756 2761 # note: this might be wrong when following through merges
2757 2762 revmatchfn = cmdutil.match(repo, fns, default='path')
2758 2763 else:
2759 2764 revmatchfn = matchfn
2760 2765
2761 2766 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2762 2767
2763 2768 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2764 2769 if count == limit:
2765 2770 break
2766 2771 if displayer.flush(ctx.rev()):
2767 2772 count += 1
2768 2773 displayer.close()
2769 2774
2770 2775 def manifest(ui, repo, node=None, rev=None):
2771 2776 """output the current or given revision of the project manifest
2772 2777
2773 2778 Print a list of version controlled files for the given revision.
2774 2779 If no revision is given, the first parent of the working directory
2775 2780 is used, or the null revision if no revision is checked out.
2776 2781
2777 2782 With -v, print file permissions, symlink and executable bits.
2778 2783 With --debug, print file revision hashes.
2779 2784
2780 2785 Returns 0 on success.
2781 2786 """
2782 2787
2783 2788 if rev and node:
2784 2789 raise util.Abort(_("please specify just one revision"))
2785 2790
2786 2791 if not node:
2787 2792 node = rev
2788 2793
2789 2794 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2790 2795 ctx = cmdutil.revsingle(repo, node)
2791 2796 for f in ctx:
2792 2797 if ui.debugflag:
2793 2798 ui.write("%40s " % hex(ctx.manifest()[f]))
2794 2799 if ui.verbose:
2795 2800 ui.write(decor[ctx.flags(f)])
2796 2801 ui.write("%s\n" % f)
2797 2802
2798 2803 def merge(ui, repo, node=None, **opts):
2799 2804 """merge working directory with another revision
2800 2805
2801 2806 The current working directory is updated with all changes made in
2802 2807 the requested revision since the last common predecessor revision.
2803 2808
2804 2809 Files that changed between either parent are marked as changed for
2805 2810 the next commit and a commit must be performed before any further
2806 2811 updates to the repository are allowed. The next commit will have
2807 2812 two parents.
2808 2813
2809 2814 ``--tool`` can be used to specify the merge tool used for file
2810 2815 merges. It overrides the HGMERGE environment variable and your
2811 2816 configuration files. See :hg:`help merge-tools` for options.
2812 2817
2813 2818 If no revision is specified, the working directory's parent is a
2814 2819 head revision, and the current branch contains exactly one other
2815 2820 head, the other head is merged with by default. Otherwise, an
2816 2821 explicit revision with which to merge with must be provided.
2817 2822
2818 2823 :hg:`resolve` must be used to resolve unresolved files.
2819 2824
2820 2825 To undo an uncommitted merge, use :hg:`update --clean .` which
2821 2826 will check out a clean copy of the original merge parent, losing
2822 2827 all changes.
2823 2828
2824 2829 Returns 0 on success, 1 if there are unresolved files.
2825 2830 """
2826 2831
2827 2832 if opts.get('rev') and node:
2828 2833 raise util.Abort(_("please specify just one revision"))
2829 2834 if not node:
2830 2835 node = opts.get('rev')
2831 2836
2832 2837 if not node:
2833 2838 branch = repo[None].branch()
2834 2839 bheads = repo.branchheads(branch)
2835 2840 if len(bheads) > 2:
2836 2841 raise util.Abort(_(
2837 2842 'branch \'%s\' has %d heads - '
2838 2843 'please merge with an explicit rev\n'
2839 2844 '(run \'hg heads .\' to see heads)')
2840 2845 % (branch, len(bheads)))
2841 2846
2842 2847 parent = repo.dirstate.p1()
2843 2848 if len(bheads) == 1:
2844 2849 if len(repo.heads()) > 1:
2845 2850 raise util.Abort(_(
2846 2851 'branch \'%s\' has one head - '
2847 2852 'please merge with an explicit rev\n'
2848 2853 '(run \'hg heads\' to see all heads)')
2849 2854 % branch)
2850 2855 msg = _('there is nothing to merge')
2851 2856 if parent != repo.lookup(repo[None].branch()):
2852 2857 msg = _('%s - use "hg update" instead') % msg
2853 2858 raise util.Abort(msg)
2854 2859
2855 2860 if parent not in bheads:
2856 2861 raise util.Abort(_('working dir not at a head rev - '
2857 2862 'use "hg update" or merge with an explicit rev'))
2858 2863 node = parent == bheads[0] and bheads[-1] or bheads[0]
2859 2864 else:
2860 2865 node = cmdutil.revsingle(repo, node).node()
2861 2866
2862 2867 if opts.get('preview'):
2863 2868 # find nodes that are ancestors of p2 but not of p1
2864 2869 p1 = repo.lookup('.')
2865 2870 p2 = repo.lookup(node)
2866 2871 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
2867 2872
2868 2873 displayer = cmdutil.show_changeset(ui, repo, opts)
2869 2874 for node in nodes:
2870 2875 displayer.show(repo[node])
2871 2876 displayer.close()
2872 2877 return 0
2873 2878
2874 2879 try:
2875 2880 # ui.forcemerge is an internal variable, do not document
2876 2881 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
2877 2882 return hg.merge(repo, node, force=opts.get('force'))
2878 2883 finally:
2879 2884 ui.setconfig('ui', 'forcemerge', '')
2880 2885
2881 2886 def outgoing(ui, repo, dest=None, **opts):
2882 2887 """show changesets not found in the destination
2883 2888
2884 2889 Show changesets not found in the specified destination repository
2885 2890 or the default push location. These are the changesets that would
2886 2891 be pushed if a push was requested.
2887 2892
2888 2893 See pull for details of valid destination formats.
2889 2894
2890 2895 Returns 0 if there are outgoing changes, 1 otherwise.
2891 2896 """
2892 2897
2893 2898 if opts.get('bookmarks'):
2894 2899 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2895 2900 dest, branches = hg.parseurl(dest, opts.get('branch'))
2896 2901 other = hg.repository(hg.remoteui(repo, opts), dest)
2897 2902 if 'bookmarks' not in other.listkeys('namespaces'):
2898 2903 ui.warn(_("remote doesn't support bookmarks\n"))
2899 2904 return 0
2900 2905 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
2901 2906 return bookmarks.diff(ui, other, repo)
2902 2907
2903 2908 ret = hg.outgoing(ui, repo, dest, opts)
2904 2909 return ret
2905 2910
2906 2911 def parents(ui, repo, file_=None, **opts):
2907 2912 """show the parents of the working directory or revision
2908 2913
2909 2914 Print the working directory's parent revisions. If a revision is
2910 2915 given via -r/--rev, the parent of that revision will be printed.
2911 2916 If a file argument is given, the revision in which the file was
2912 2917 last changed (before the working directory revision or the
2913 2918 argument to --rev if given) is printed.
2914 2919
2915 2920 Returns 0 on success.
2916 2921 """
2917 2922
2918 2923 ctx = cmdutil.revsingle(repo, opts.get('rev'), None)
2919 2924
2920 2925 if file_:
2921 2926 m = cmdutil.match(repo, (file_,), opts)
2922 2927 if m.anypats() or len(m.files()) != 1:
2923 2928 raise util.Abort(_('can only specify an explicit filename'))
2924 2929 file_ = m.files()[0]
2925 2930 filenodes = []
2926 2931 for cp in ctx.parents():
2927 2932 if not cp:
2928 2933 continue
2929 2934 try:
2930 2935 filenodes.append(cp.filenode(file_))
2931 2936 except error.LookupError:
2932 2937 pass
2933 2938 if not filenodes:
2934 2939 raise util.Abort(_("'%s' not found in manifest!") % file_)
2935 2940 fl = repo.file(file_)
2936 2941 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2937 2942 else:
2938 2943 p = [cp.node() for cp in ctx.parents()]
2939 2944
2940 2945 displayer = cmdutil.show_changeset(ui, repo, opts)
2941 2946 for n in p:
2942 2947 if n != nullid:
2943 2948 displayer.show(repo[n])
2944 2949 displayer.close()
2945 2950
2946 2951 def paths(ui, repo, search=None):
2947 2952 """show aliases for remote repositories
2948 2953
2949 2954 Show definition of symbolic path name NAME. If no name is given,
2950 2955 show definition of all available names.
2951 2956
2952 2957 Path names are defined in the [paths] section of your
2953 2958 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
2954 2959 repository, ``.hg/hgrc`` is used, too.
2955 2960
2956 2961 The path names ``default`` and ``default-push`` have a special
2957 2962 meaning. When performing a push or pull operation, they are used
2958 2963 as fallbacks if no location is specified on the command-line.
2959 2964 When ``default-push`` is set, it will be used for push and
2960 2965 ``default`` will be used for pull; otherwise ``default`` is used
2961 2966 as the fallback for both. When cloning a repository, the clone
2962 2967 source is written as ``default`` in ``.hg/hgrc``. Note that
2963 2968 ``default`` and ``default-push`` apply to all inbound (e.g.
2964 2969 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
2965 2970 :hg:`bundle`) operations.
2966 2971
2967 2972 See :hg:`help urls` for more information.
2968 2973
2969 2974 Returns 0 on success.
2970 2975 """
2971 2976 if search:
2972 2977 for name, path in ui.configitems("paths"):
2973 2978 if name == search:
2974 2979 ui.write("%s\n" % util.hidepassword(path))
2975 2980 return
2976 2981 ui.warn(_("not found!\n"))
2977 2982 return 1
2978 2983 else:
2979 2984 for name, path in ui.configitems("paths"):
2980 2985 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
2981 2986
2982 2987 def postincoming(ui, repo, modheads, optupdate, checkout):
2983 2988 if modheads == 0:
2984 2989 return
2985 2990 if optupdate:
2986 2991 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2987 2992 return hg.update(repo, checkout)
2988 2993 else:
2989 2994 ui.status(_("not updating, since new heads added\n"))
2990 2995 if modheads > 1:
2991 2996 currentbranchheads = len(repo.branchheads())
2992 2997 if currentbranchheads == modheads:
2993 2998 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2994 2999 elif currentbranchheads > 1:
2995 3000 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to merge)\n"))
2996 3001 else:
2997 3002 ui.status(_("(run 'hg heads' to see heads)\n"))
2998 3003 else:
2999 3004 ui.status(_("(run 'hg update' to get a working copy)\n"))
3000 3005
3001 3006 def pull(ui, repo, source="default", **opts):
3002 3007 """pull changes from the specified source
3003 3008
3004 3009 Pull changes from a remote repository to a local one.
3005 3010
3006 3011 This finds all changes from the repository at the specified path
3007 3012 or URL and adds them to a local repository (the current one unless
3008 3013 -R is specified). By default, this does not update the copy of the
3009 3014 project in the working directory.
3010 3015
3011 3016 Use :hg:`incoming` if you want to see what would have been added
3012 3017 by a pull at the time you issued this command. If you then decide
3013 3018 to add those changes to the repository, you should use :hg:`pull
3014 3019 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
3015 3020
3016 3021 If SOURCE is omitted, the 'default' path will be used.
3017 3022 See :hg:`help urls` for more information.
3018 3023
3019 3024 Returns 0 on success, 1 if an update had unresolved files.
3020 3025 """
3021 3026 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
3022 3027 other = hg.repository(hg.remoteui(repo, opts), source)
3023 3028 ui.status(_('pulling from %s\n') % util.hidepassword(source))
3024 3029 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3025 3030
3026 3031 if opts.get('bookmark'):
3027 3032 if not revs:
3028 3033 revs = []
3029 3034 rb = other.listkeys('bookmarks')
3030 3035 for b in opts['bookmark']:
3031 3036 if b not in rb:
3032 3037 raise util.Abort(_('remote bookmark %s not found!') % b)
3033 3038 revs.append(rb[b])
3034 3039
3035 3040 if revs:
3036 3041 try:
3037 3042 revs = [other.lookup(rev) for rev in revs]
3038 3043 except error.CapabilityError:
3039 3044 err = _("other repository doesn't support revision lookup, "
3040 3045 "so a rev cannot be specified.")
3041 3046 raise util.Abort(err)
3042 3047
3043 3048 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
3044 3049 bookmarks.updatefromremote(ui, repo, other)
3045 3050 if checkout:
3046 3051 checkout = str(repo.changelog.rev(other.lookup(checkout)))
3047 3052 repo._subtoppath = source
3048 3053 try:
3049 3054 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
3050 3055
3051 3056 finally:
3052 3057 del repo._subtoppath
3053 3058
3054 3059 # update specified bookmarks
3055 3060 if opts.get('bookmark'):
3056 3061 for b in opts['bookmark']:
3057 3062 # explicit pull overrides local bookmark if any
3058 3063 ui.status(_("importing bookmark %s\n") % b)
3059 3064 repo._bookmarks[b] = repo[rb[b]].node()
3060 3065 bookmarks.write(repo)
3061 3066
3062 3067 return ret
3063 3068
3064 3069 def push(ui, repo, dest=None, **opts):
3065 3070 """push changes to the specified destination
3066 3071
3067 3072 Push changesets from the local repository to the specified
3068 3073 destination.
3069 3074
3070 3075 This operation is symmetrical to pull: it is identical to a pull
3071 3076 in the destination repository from the current one.
3072 3077
3073 3078 By default, push will not allow creation of new heads at the
3074 3079 destination, since multiple heads would make it unclear which head
3075 3080 to use. In this situation, it is recommended to pull and merge
3076 3081 before pushing.
3077 3082
3078 3083 Use --new-branch if you want to allow push to create a new named
3079 3084 branch that is not present at the destination. This allows you to
3080 3085 only create a new branch without forcing other changes.
3081 3086
3082 3087 Use -f/--force to override the default behavior and push all
3083 3088 changesets on all branches.
3084 3089
3085 3090 If -r/--rev is used, the specified revision and all its ancestors
3086 3091 will be pushed to the remote repository.
3087 3092
3088 3093 Please see :hg:`help urls` for important details about ``ssh://``
3089 3094 URLs. If DESTINATION is omitted, a default path will be used.
3090 3095
3091 3096 Returns 0 if push was successful, 1 if nothing to push.
3092 3097 """
3093 3098
3094 3099 if opts.get('bookmark'):
3095 3100 for b in opts['bookmark']:
3096 3101 # translate -B options to -r so changesets get pushed
3097 3102 if b in repo._bookmarks:
3098 3103 opts.setdefault('rev', []).append(b)
3099 3104 else:
3100 3105 # if we try to push a deleted bookmark, translate it to null
3101 3106 # this lets simultaneous -r, -b options continue working
3102 3107 opts.setdefault('rev', []).append("null")
3103 3108
3104 3109 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3105 3110 dest, branches = hg.parseurl(dest, opts.get('branch'))
3106 3111 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
3107 3112 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
3108 3113 other = hg.repository(hg.remoteui(repo, opts), dest)
3109 3114 if revs:
3110 3115 revs = [repo.lookup(rev) for rev in revs]
3111 3116
3112 3117 repo._subtoppath = dest
3113 3118 try:
3114 3119 # push subrepos depth-first for coherent ordering
3115 3120 c = repo['']
3116 3121 subs = c.substate # only repos that are committed
3117 3122 for s in sorted(subs):
3118 3123 if not c.sub(s).push(opts.get('force')):
3119 3124 return False
3120 3125 finally:
3121 3126 del repo._subtoppath
3122 3127 result = repo.push(other, opts.get('force'), revs=revs,
3123 3128 newbranch=opts.get('new_branch'))
3124 3129
3125 3130 result = (result == 0)
3126 3131
3127 3132 if opts.get('bookmark'):
3128 3133 rb = other.listkeys('bookmarks')
3129 3134 for b in opts['bookmark']:
3130 3135 # explicit push overrides remote bookmark if any
3131 3136 if b in repo._bookmarks:
3132 3137 ui.status(_("exporting bookmark %s\n") % b)
3133 3138 new = repo[b].hex()
3134 3139 elif b in rb:
3135 3140 ui.status(_("deleting remote bookmark %s\n") % b)
3136 3141 new = '' # delete
3137 3142 else:
3138 3143 ui.warn(_('bookmark %s does not exist on the local '
3139 3144 'or remote repository!\n') % b)
3140 3145 return 2
3141 3146 old = rb.get(b, '')
3142 3147 r = other.pushkey('bookmarks', b, old, new)
3143 3148 if not r:
3144 3149 ui.warn(_('updating bookmark %s failed!\n') % b)
3145 3150 if not result:
3146 3151 result = 2
3147 3152
3148 3153 return result
3149 3154
3150 3155 def recover(ui, repo):
3151 3156 """roll back an interrupted transaction
3152 3157
3153 3158 Recover from an interrupted commit or pull.
3154 3159
3155 3160 This command tries to fix the repository status after an
3156 3161 interrupted operation. It should only be necessary when Mercurial
3157 3162 suggests it.
3158 3163
3159 3164 Returns 0 if successful, 1 if nothing to recover or verify fails.
3160 3165 """
3161 3166 if repo.recover():
3162 3167 return hg.verify(repo)
3163 3168 return 1
3164 3169
3165 3170 def remove(ui, repo, *pats, **opts):
3166 3171 """remove the specified files on the next commit
3167 3172
3168 3173 Schedule the indicated files for removal from the repository.
3169 3174
3170 3175 This only removes files from the current branch, not from the
3171 3176 entire project history. -A/--after can be used to remove only
3172 3177 files that have already been deleted, -f/--force can be used to
3173 3178 force deletion, and -Af can be used to remove files from the next
3174 3179 revision without deleting them from the working directory.
3175 3180
3176 3181 The following table details the behavior of remove for different
3177 3182 file states (columns) and option combinations (rows). The file
3178 3183 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
3179 3184 reported by :hg:`status`). The actions are Warn, Remove (from
3180 3185 branch) and Delete (from disk)::
3181 3186
3182 3187 A C M !
3183 3188 none W RD W R
3184 3189 -f R RD RD R
3185 3190 -A W W W R
3186 3191 -Af R R R R
3187 3192
3188 3193 This command schedules the files to be removed at the next commit.
3189 3194 To undo a remove before that, see :hg:`revert`.
3190 3195
3191 3196 Returns 0 on success, 1 if any warnings encountered.
3192 3197 """
3193 3198
3194 3199 ret = 0
3195 3200 after, force = opts.get('after'), opts.get('force')
3196 3201 if not pats and not after:
3197 3202 raise util.Abort(_('no files specified'))
3198 3203
3199 3204 m = cmdutil.match(repo, pats, opts)
3200 3205 s = repo.status(match=m, clean=True)
3201 3206 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
3202 3207
3203 3208 for f in m.files():
3204 3209 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
3205 3210 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
3206 3211 ret = 1
3207 3212
3208 3213 if force:
3209 3214 remove, forget = modified + deleted + clean, added
3210 3215 elif after:
3211 3216 remove, forget = deleted, []
3212 3217 for f in modified + added + clean:
3213 3218 ui.warn(_('not removing %s: file still exists (use -f'
3214 3219 ' to force removal)\n') % m.rel(f))
3215 3220 ret = 1
3216 3221 else:
3217 3222 remove, forget = deleted + clean, []
3218 3223 for f in modified:
3219 3224 ui.warn(_('not removing %s: file is modified (use -f'
3220 3225 ' to force removal)\n') % m.rel(f))
3221 3226 ret = 1
3222 3227 for f in added:
3223 3228 ui.warn(_('not removing %s: file has been marked for add (use -f'
3224 3229 ' to force removal)\n') % m.rel(f))
3225 3230 ret = 1
3226 3231
3227 3232 for f in sorted(remove + forget):
3228 3233 if ui.verbose or not m.exact(f):
3229 3234 ui.status(_('removing %s\n') % m.rel(f))
3230 3235
3231 3236 repo[None].forget(forget)
3232 3237 repo[None].remove(remove, unlink=not after)
3233 3238 return ret
3234 3239
3235 3240 def rename(ui, repo, *pats, **opts):
3236 3241 """rename files; equivalent of copy + remove
3237 3242
3238 3243 Mark dest as copies of sources; mark sources for deletion. If dest
3239 3244 is a directory, copies are put in that directory. If dest is a
3240 3245 file, there can only be one source.
3241 3246
3242 3247 By default, this command copies the contents of files as they
3243 3248 exist in the working directory. If invoked with -A/--after, the
3244 3249 operation is recorded, but no copying is performed.
3245 3250
3246 3251 This command takes effect at the next commit. To undo a rename
3247 3252 before that, see :hg:`revert`.
3248 3253
3249 3254 Returns 0 on success, 1 if errors are encountered.
3250 3255 """
3251 3256 wlock = repo.wlock(False)
3252 3257 try:
3253 3258 return cmdutil.copy(ui, repo, pats, opts, rename=True)
3254 3259 finally:
3255 3260 wlock.release()
3256 3261
3257 3262 def resolve(ui, repo, *pats, **opts):
3258 3263 """redo merges or set/view the merge status of files
3259 3264
3260 3265 Merges with unresolved conflicts are often the result of
3261 3266 non-interactive merging using the ``internal:merge`` configuration
3262 3267 setting, or a command-line merge tool like ``diff3``. The resolve
3263 3268 command is used to manage the files involved in a merge, after
3264 3269 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
3265 3270 working directory must have two parents).
3266 3271
3267 3272 The resolve command can be used in the following ways:
3268 3273
3269 3274 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
3270 3275 files, discarding any previous merge attempts. Re-merging is not
3271 3276 performed for files already marked as resolved. Use ``--all/-a``
3272 3277 to selects all unresolved files. ``--tool`` can be used to specify
3273 3278 the merge tool used for the given files. It overrides the HGMERGE
3274 3279 environment variable and your configuration files.
3275 3280
3276 3281 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
3277 3282 (e.g. after having manually fixed-up the files). The default is
3278 3283 to mark all unresolved files.
3279 3284
3280 3285 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
3281 3286 default is to mark all resolved files.
3282 3287
3283 3288 - :hg:`resolve -l`: list files which had or still have conflicts.
3284 3289 In the printed list, ``U`` = unresolved and ``R`` = resolved.
3285 3290
3286 3291 Note that Mercurial will not let you commit files with unresolved
3287 3292 merge conflicts. You must use :hg:`resolve -m ...` before you can
3288 3293 commit after a conflicting merge.
3289 3294
3290 3295 Returns 0 on success, 1 if any files fail a resolve attempt.
3291 3296 """
3292 3297
3293 3298 all, mark, unmark, show, nostatus = \
3294 3299 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
3295 3300
3296 3301 if (show and (mark or unmark)) or (mark and unmark):
3297 3302 raise util.Abort(_("too many options specified"))
3298 3303 if pats and all:
3299 3304 raise util.Abort(_("can't specify --all and patterns"))
3300 3305 if not (all or pats or show or mark or unmark):
3301 3306 raise util.Abort(_('no files or directories specified; '
3302 3307 'use --all to remerge all files'))
3303 3308
3304 3309 ms = mergemod.mergestate(repo)
3305 3310 m = cmdutil.match(repo, pats, opts)
3306 3311 ret = 0
3307 3312
3308 3313 for f in ms:
3309 3314 if m(f):
3310 3315 if show:
3311 3316 if nostatus:
3312 3317 ui.write("%s\n" % f)
3313 3318 else:
3314 3319 ui.write("%s %s\n" % (ms[f].upper(), f),
3315 3320 label='resolve.' +
3316 3321 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
3317 3322 elif mark:
3318 3323 ms.mark(f, "r")
3319 3324 elif unmark:
3320 3325 ms.mark(f, "u")
3321 3326 else:
3322 3327 wctx = repo[None]
3323 3328 mctx = wctx.parents()[-1]
3324 3329
3325 3330 # backup pre-resolve (merge uses .orig for its own purposes)
3326 3331 a = repo.wjoin(f)
3327 3332 util.copyfile(a, a + ".resolve")
3328 3333
3329 3334 try:
3330 3335 # resolve file
3331 3336 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
3332 3337 if ms.resolve(f, wctx, mctx):
3333 3338 ret = 1
3334 3339 finally:
3335 3340 ui.setconfig('ui', 'forcemerge', '')
3336 3341
3337 3342 # replace filemerge's .orig file with our resolve file
3338 3343 util.rename(a + ".resolve", a + ".orig")
3339 3344
3340 3345 ms.commit()
3341 3346 return ret
3342 3347
3343 3348 def revert(ui, repo, *pats, **opts):
3344 3349 """restore individual files or directories to an earlier state
3345 3350
3346 3351 .. note::
3347 3352 This command is most likely not what you are looking for.
3348 3353 Revert will partially overwrite content in the working
3349 3354 directory without changing the working directory parents. Use
3350 3355 :hg:`update -r rev` to check out earlier revisions, or
3351 3356 :hg:`update --clean .` to undo a merge which has added another
3352 3357 parent.
3353 3358
3354 3359 With no revision specified, revert the named files or directories
3355 3360 to the contents they had in the parent of the working directory.
3356 3361 This restores the contents of the affected files to an unmodified
3357 3362 state and unschedules adds, removes, copies, and renames. If the
3358 3363 working directory has two parents, you must explicitly specify a
3359 3364 revision.
3360 3365
3361 3366 Using the -r/--rev option, revert the given files or directories
3362 3367 to their contents as of a specific revision. This can be helpful
3363 3368 to "roll back" some or all of an earlier change. See :hg:`help
3364 3369 dates` for a list of formats valid for -d/--date.
3365 3370
3366 3371 Revert modifies the working directory. It does not commit any
3367 3372 changes, or change the parent of the working directory. If you
3368 3373 revert to a revision other than the parent of the working
3369 3374 directory, the reverted files will thus appear modified
3370 3375 afterwards.
3371 3376
3372 3377 If a file has been deleted, it is restored. Files scheduled for
3373 3378 addition are just unscheduled and left as they are. If the
3374 3379 executable mode of a file was changed, it is reset.
3375 3380
3376 3381 If names are given, all files matching the names are reverted.
3377 3382 If no arguments are given, no files are reverted.
3378 3383
3379 3384 Modified files are saved with a .orig suffix before reverting.
3380 3385 To disable these backups, use --no-backup.
3381 3386
3382 3387 Returns 0 on success.
3383 3388 """
3384 3389
3385 3390 if opts.get("date"):
3386 3391 if opts.get("rev"):
3387 3392 raise util.Abort(_("you can't specify a revision and a date"))
3388 3393 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
3389 3394
3390 3395 parent, p2 = repo.dirstate.parents()
3391 3396 if not opts.get('rev') and p2 != nullid:
3392 3397 raise util.Abort(_('uncommitted merge - '
3393 3398 'use "hg update", see "hg help revert"'))
3394 3399
3395 3400 if not pats and not opts.get('all'):
3396 3401 raise util.Abort(_('no files or directories specified; '
3397 3402 'use --all to revert the whole repo'))
3398 3403
3399 3404 ctx = cmdutil.revsingle(repo, opts.get('rev'))
3400 3405 node = ctx.node()
3401 3406 mf = ctx.manifest()
3402 3407 if node == parent:
3403 3408 pmf = mf
3404 3409 else:
3405 3410 pmf = None
3406 3411
3407 3412 # need all matching names in dirstate and manifest of target rev,
3408 3413 # so have to walk both. do not print errors if files exist in one
3409 3414 # but not other.
3410 3415
3411 3416 names = {}
3412 3417
3413 3418 wlock = repo.wlock()
3414 3419 try:
3415 3420 # walk dirstate.
3416 3421
3417 3422 m = cmdutil.match(repo, pats, opts)
3418 3423 m.bad = lambda x, y: False
3419 3424 for abs in repo.walk(m):
3420 3425 names[abs] = m.rel(abs), m.exact(abs)
3421 3426
3422 3427 # walk target manifest.
3423 3428
3424 3429 def badfn(path, msg):
3425 3430 if path in names:
3426 3431 return
3427 3432 path_ = path + '/'
3428 3433 for f in names:
3429 3434 if f.startswith(path_):
3430 3435 return
3431 3436 ui.warn("%s: %s\n" % (m.rel(path), msg))
3432 3437
3433 3438 m = cmdutil.match(repo, pats, opts)
3434 3439 m.bad = badfn
3435 3440 for abs in repo[node].walk(m):
3436 3441 if abs not in names:
3437 3442 names[abs] = m.rel(abs), m.exact(abs)
3438 3443
3439 3444 m = cmdutil.matchfiles(repo, names)
3440 3445 changes = repo.status(match=m)[:4]
3441 3446 modified, added, removed, deleted = map(set, changes)
3442 3447
3443 3448 # if f is a rename, also revert the source
3444 3449 cwd = repo.getcwd()
3445 3450 for f in added:
3446 3451 src = repo.dirstate.copied(f)
3447 3452 if src and src not in names and repo.dirstate[src] == 'r':
3448 3453 removed.add(src)
3449 3454 names[src] = (repo.pathto(src, cwd), True)
3450 3455
3451 3456 def removeforget(abs):
3452 3457 if repo.dirstate[abs] == 'a':
3453 3458 return _('forgetting %s\n')
3454 3459 return _('removing %s\n')
3455 3460
3456 3461 revert = ([], _('reverting %s\n'))
3457 3462 add = ([], _('adding %s\n'))
3458 3463 remove = ([], removeforget)
3459 3464 undelete = ([], _('undeleting %s\n'))
3460 3465
3461 3466 disptable = (
3462 3467 # dispatch table:
3463 3468 # file state
3464 3469 # action if in target manifest
3465 3470 # action if not in target manifest
3466 3471 # make backup if in target manifest
3467 3472 # make backup if not in target manifest
3468 3473 (modified, revert, remove, True, True),
3469 3474 (added, revert, remove, True, False),
3470 3475 (removed, undelete, None, False, False),
3471 3476 (deleted, revert, remove, False, False),
3472 3477 )
3473 3478
3474 3479 for abs, (rel, exact) in sorted(names.items()):
3475 3480 mfentry = mf.get(abs)
3476 3481 target = repo.wjoin(abs)
3477 3482 def handle(xlist, dobackup):
3478 3483 xlist[0].append(abs)
3479 3484 if (dobackup and not opts.get('no_backup') and
3480 3485 os.path.lexists(target)):
3481 3486 bakname = "%s.orig" % rel
3482 3487 ui.note(_('saving current version of %s as %s\n') %
3483 3488 (rel, bakname))
3484 3489 if not opts.get('dry_run'):
3485 3490 util.rename(target, bakname)
3486 3491 if ui.verbose or not exact:
3487 3492 msg = xlist[1]
3488 3493 if not isinstance(msg, basestring):
3489 3494 msg = msg(abs)
3490 3495 ui.status(msg % rel)
3491 3496 for table, hitlist, misslist, backuphit, backupmiss in disptable:
3492 3497 if abs not in table:
3493 3498 continue
3494 3499 # file has changed in dirstate
3495 3500 if mfentry:
3496 3501 handle(hitlist, backuphit)
3497 3502 elif misslist is not None:
3498 3503 handle(misslist, backupmiss)
3499 3504 break
3500 3505 else:
3501 3506 if abs not in repo.dirstate:
3502 3507 if mfentry:
3503 3508 handle(add, True)
3504 3509 elif exact:
3505 3510 ui.warn(_('file not managed: %s\n') % rel)
3506 3511 continue
3507 3512 # file has not changed in dirstate
3508 3513 if node == parent:
3509 3514 if exact:
3510 3515 ui.warn(_('no changes needed to %s\n') % rel)
3511 3516 continue
3512 3517 if pmf is None:
3513 3518 # only need parent manifest in this unlikely case,
3514 3519 # so do not read by default
3515 3520 pmf = repo[parent].manifest()
3516 3521 if abs in pmf:
3517 3522 if mfentry:
3518 3523 # if version of file is same in parent and target
3519 3524 # manifests, do nothing
3520 3525 if (pmf[abs] != mfentry or
3521 3526 pmf.flags(abs) != mf.flags(abs)):
3522 3527 handle(revert, False)
3523 3528 else:
3524 3529 handle(remove, False)
3525 3530
3526 3531 if not opts.get('dry_run'):
3527 3532 def checkout(f):
3528 3533 fc = ctx[f]
3529 3534 repo.wwrite(f, fc.data(), fc.flags())
3530 3535
3531 3536 audit_path = scmutil.path_auditor(repo.root)
3532 3537 for f in remove[0]:
3533 3538 if repo.dirstate[f] == 'a':
3534 3539 repo.dirstate.forget(f)
3535 3540 continue
3536 3541 audit_path(f)
3537 3542 try:
3538 3543 util.unlinkpath(repo.wjoin(f))
3539 3544 except OSError:
3540 3545 pass
3541 3546 repo.dirstate.remove(f)
3542 3547
3543 3548 normal = None
3544 3549 if node == parent:
3545 3550 # We're reverting to our parent. If possible, we'd like status
3546 3551 # to report the file as clean. We have to use normallookup for
3547 3552 # merges to avoid losing information about merged/dirty files.
3548 3553 if p2 != nullid:
3549 3554 normal = repo.dirstate.normallookup
3550 3555 else:
3551 3556 normal = repo.dirstate.normal
3552 3557 for f in revert[0]:
3553 3558 checkout(f)
3554 3559 if normal:
3555 3560 normal(f)
3556 3561
3557 3562 for f in add[0]:
3558 3563 checkout(f)
3559 3564 repo.dirstate.add(f)
3560 3565
3561 3566 normal = repo.dirstate.normallookup
3562 3567 if node == parent and p2 == nullid:
3563 3568 normal = repo.dirstate.normal
3564 3569 for f in undelete[0]:
3565 3570 checkout(f)
3566 3571 normal(f)
3567 3572
3568 3573 finally:
3569 3574 wlock.release()
3570 3575
3571 3576 def rollback(ui, repo, **opts):
3572 3577 """roll back the last transaction (dangerous)
3573 3578
3574 3579 This command should be used with care. There is only one level of
3575 3580 rollback, and there is no way to undo a rollback. It will also
3576 3581 restore the dirstate at the time of the last transaction, losing
3577 3582 any dirstate changes since that time. This command does not alter
3578 3583 the working directory.
3579 3584
3580 3585 Transactions are used to encapsulate the effects of all commands
3581 3586 that create new changesets or propagate existing changesets into a
3582 3587 repository. For example, the following commands are transactional,
3583 3588 and their effects can be rolled back:
3584 3589
3585 3590 - commit
3586 3591 - import
3587 3592 - pull
3588 3593 - push (with this repository as the destination)
3589 3594 - unbundle
3590 3595
3591 3596 This command is not intended for use on public repositories. Once
3592 3597 changes are visible for pull by other users, rolling a transaction
3593 3598 back locally is ineffective (someone else may already have pulled
3594 3599 the changes). Furthermore, a race is possible with readers of the
3595 3600 repository; for example an in-progress pull from the repository
3596 3601 may fail if a rollback is performed.
3597 3602
3598 3603 Returns 0 on success, 1 if no rollback data is available.
3599 3604 """
3600 3605 return repo.rollback(opts.get('dry_run'))
3601 3606
3602 3607 def root(ui, repo):
3603 3608 """print the root (top) of the current working directory
3604 3609
3605 3610 Print the root directory of the current repository.
3606 3611
3607 3612 Returns 0 on success.
3608 3613 """
3609 3614 ui.write(repo.root + "\n")
3610 3615
3611 3616 def serve(ui, repo, **opts):
3612 3617 """start stand-alone webserver
3613 3618
3614 3619 Start a local HTTP repository browser and pull server. You can use
3615 3620 this for ad-hoc sharing and browsing of repositories. It is
3616 3621 recommended to use a real web server to serve a repository for
3617 3622 longer periods of time.
3618 3623
3619 3624 Please note that the server does not implement access control.
3620 3625 This means that, by default, anybody can read from the server and
3621 3626 nobody can write to it by default. Set the ``web.allow_push``
3622 3627 option to ``*`` to allow everybody to push to the server. You
3623 3628 should use a real web server if you need to authenticate users.
3624 3629
3625 3630 By default, the server logs accesses to stdout and errors to
3626 3631 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
3627 3632 files.
3628 3633
3629 3634 To have the server choose a free port number to listen on, specify
3630 3635 a port number of 0; in this case, the server will print the port
3631 3636 number it uses.
3632 3637
3633 3638 Returns 0 on success.
3634 3639 """
3635 3640
3636 3641 if opts["stdio"]:
3637 3642 if repo is None:
3638 3643 raise error.RepoError(_("There is no Mercurial repository here"
3639 3644 " (.hg not found)"))
3640 3645 s = sshserver.sshserver(ui, repo)
3641 3646 s.serve_forever()
3642 3647
3643 3648 # this way we can check if something was given in the command-line
3644 3649 if opts.get('port'):
3645 3650 opts['port'] = util.getport(opts.get('port'))
3646 3651
3647 3652 baseui = repo and repo.baseui or ui
3648 3653 optlist = ("name templates style address port prefix ipv6"
3649 3654 " accesslog errorlog certificate encoding")
3650 3655 for o in optlist.split():
3651 3656 val = opts.get(o, '')
3652 3657 if val in (None, ''): # should check against default options instead
3653 3658 continue
3654 3659 baseui.setconfig("web", o, val)
3655 3660 if repo and repo.ui != baseui:
3656 3661 repo.ui.setconfig("web", o, val)
3657 3662
3658 3663 o = opts.get('web_conf') or opts.get('webdir_conf')
3659 3664 if not o:
3660 3665 if not repo:
3661 3666 raise error.RepoError(_("There is no Mercurial repository"
3662 3667 " here (.hg not found)"))
3663 3668 o = repo.root
3664 3669
3665 3670 app = hgweb.hgweb(o, baseui=ui)
3666 3671
3667 3672 class service(object):
3668 3673 def init(self):
3669 3674 util.set_signal_handler()
3670 3675 self.httpd = hgweb.server.create_server(ui, app)
3671 3676
3672 3677 if opts['port'] and not ui.verbose:
3673 3678 return
3674 3679
3675 3680 if self.httpd.prefix:
3676 3681 prefix = self.httpd.prefix.strip('/') + '/'
3677 3682 else:
3678 3683 prefix = ''
3679 3684
3680 3685 port = ':%d' % self.httpd.port
3681 3686 if port == ':80':
3682 3687 port = ''
3683 3688
3684 3689 bindaddr = self.httpd.addr
3685 3690 if bindaddr == '0.0.0.0':
3686 3691 bindaddr = '*'
3687 3692 elif ':' in bindaddr: # IPv6
3688 3693 bindaddr = '[%s]' % bindaddr
3689 3694
3690 3695 fqaddr = self.httpd.fqaddr
3691 3696 if ':' in fqaddr:
3692 3697 fqaddr = '[%s]' % fqaddr
3693 3698 if opts['port']:
3694 3699 write = ui.status
3695 3700 else:
3696 3701 write = ui.write
3697 3702 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
3698 3703 (fqaddr, port, prefix, bindaddr, self.httpd.port))
3699 3704
3700 3705 def run(self):
3701 3706 self.httpd.serve_forever()
3702 3707
3703 3708 service = service()
3704 3709
3705 3710 cmdutil.service(opts, initfn=service.init, runfn=service.run)
3706 3711
3707 3712 def status(ui, repo, *pats, **opts):
3708 3713 """show changed files in the working directory
3709 3714
3710 3715 Show status of files in the repository. If names are given, only
3711 3716 files that match are shown. Files that are clean or ignored or
3712 3717 the source of a copy/move operation, are not listed unless
3713 3718 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
3714 3719 Unless options described with "show only ..." are given, the
3715 3720 options -mardu are used.
3716 3721
3717 3722 Option -q/--quiet hides untracked (unknown and ignored) files
3718 3723 unless explicitly requested with -u/--unknown or -i/--ignored.
3719 3724
3720 3725 .. note::
3721 3726 status may appear to disagree with diff if permissions have
3722 3727 changed or a merge has occurred. The standard diff format does
3723 3728 not report permission changes and diff only reports changes
3724 3729 relative to one merge parent.
3725 3730
3726 3731 If one revision is given, it is used as the base revision.
3727 3732 If two revisions are given, the differences between them are
3728 3733 shown. The --change option can also be used as a shortcut to list
3729 3734 the changed files of a revision from its first parent.
3730 3735
3731 3736 The codes used to show the status of files are::
3732 3737
3733 3738 M = modified
3734 3739 A = added
3735 3740 R = removed
3736 3741 C = clean
3737 3742 ! = missing (deleted by non-hg command, but still tracked)
3738 3743 ? = not tracked
3739 3744 I = ignored
3740 3745 = origin of the previous file listed as A (added)
3741 3746
3742 3747 Returns 0 on success.
3743 3748 """
3744 3749
3745 3750 revs = opts.get('rev')
3746 3751 change = opts.get('change')
3747 3752
3748 3753 if revs and change:
3749 3754 msg = _('cannot specify --rev and --change at the same time')
3750 3755 raise util.Abort(msg)
3751 3756 elif change:
3752 3757 node2 = repo.lookup(change)
3753 3758 node1 = repo[node2].p1().node()
3754 3759 else:
3755 3760 node1, node2 = cmdutil.revpair(repo, revs)
3756 3761
3757 3762 cwd = (pats and repo.getcwd()) or ''
3758 3763 end = opts.get('print0') and '\0' or '\n'
3759 3764 copy = {}
3760 3765 states = 'modified added removed deleted unknown ignored clean'.split()
3761 3766 show = [k for k in states if opts.get(k)]
3762 3767 if opts.get('all'):
3763 3768 show += ui.quiet and (states[:4] + ['clean']) or states
3764 3769 if not show:
3765 3770 show = ui.quiet and states[:4] or states[:5]
3766 3771
3767 3772 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
3768 3773 'ignored' in show, 'clean' in show, 'unknown' in show,
3769 3774 opts.get('subrepos'))
3770 3775 changestates = zip(states, 'MAR!?IC', stat)
3771 3776
3772 3777 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
3773 3778 ctxn = repo[nullid]
3774 3779 ctx1 = repo[node1]
3775 3780 ctx2 = repo[node2]
3776 3781 added = stat[1]
3777 3782 if node2 is None:
3778 3783 added = stat[0] + stat[1] # merged?
3779 3784
3780 3785 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
3781 3786 if k in added:
3782 3787 copy[k] = v
3783 3788 elif v in added:
3784 3789 copy[v] = k
3785 3790
3786 3791 for state, char, files in changestates:
3787 3792 if state in show:
3788 3793 format = "%s %%s%s" % (char, end)
3789 3794 if opts.get('no_status'):
3790 3795 format = "%%s%s" % end
3791 3796
3792 3797 for f in files:
3793 3798 ui.write(format % repo.pathto(f, cwd),
3794 3799 label='status.' + state)
3795 3800 if f in copy:
3796 3801 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
3797 3802 label='status.copied')
3798 3803
3799 3804 def summary(ui, repo, **opts):
3800 3805 """summarize working directory state
3801 3806
3802 3807 This generates a brief summary of the working directory state,
3803 3808 including parents, branch, commit status, and available updates.
3804 3809
3805 3810 With the --remote option, this will check the default paths for
3806 3811 incoming and outgoing changes. This can be time-consuming.
3807 3812
3808 3813 Returns 0 on success.
3809 3814 """
3810 3815
3811 3816 ctx = repo[None]
3812 3817 parents = ctx.parents()
3813 3818 pnode = parents[0].node()
3814 3819
3815 3820 for p in parents:
3816 3821 # label with log.changeset (instead of log.parent) since this
3817 3822 # shows a working directory parent *changeset*:
3818 3823 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
3819 3824 label='log.changeset')
3820 3825 ui.write(' '.join(p.tags()), label='log.tag')
3821 3826 if p.bookmarks():
3822 3827 ui.write(' ' + ' '.join(p.bookmarks()), label='log.bookmark')
3823 3828 if p.rev() == -1:
3824 3829 if not len(repo):
3825 3830 ui.write(_(' (empty repository)'))
3826 3831 else:
3827 3832 ui.write(_(' (no revision checked out)'))
3828 3833 ui.write('\n')
3829 3834 if p.description():
3830 3835 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
3831 3836 label='log.summary')
3832 3837
3833 3838 branch = ctx.branch()
3834 3839 bheads = repo.branchheads(branch)
3835 3840 m = _('branch: %s\n') % branch
3836 3841 if branch != 'default':
3837 3842 ui.write(m, label='log.branch')
3838 3843 else:
3839 3844 ui.status(m, label='log.branch')
3840 3845
3841 3846 st = list(repo.status(unknown=True))[:6]
3842 3847
3843 3848 c = repo.dirstate.copies()
3844 3849 copied, renamed = [], []
3845 3850 for d, s in c.iteritems():
3846 3851 if s in st[2]:
3847 3852 st[2].remove(s)
3848 3853 renamed.append(d)
3849 3854 else:
3850 3855 copied.append(d)
3851 3856 if d in st[1]:
3852 3857 st[1].remove(d)
3853 3858 st.insert(3, renamed)
3854 3859 st.insert(4, copied)
3855 3860
3856 3861 ms = mergemod.mergestate(repo)
3857 3862 st.append([f for f in ms if ms[f] == 'u'])
3858 3863
3859 3864 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
3860 3865 st.append(subs)
3861 3866
3862 3867 labels = [ui.label(_('%d modified'), 'status.modified'),
3863 3868 ui.label(_('%d added'), 'status.added'),
3864 3869 ui.label(_('%d removed'), 'status.removed'),
3865 3870 ui.label(_('%d renamed'), 'status.copied'),
3866 3871 ui.label(_('%d copied'), 'status.copied'),
3867 3872 ui.label(_('%d deleted'), 'status.deleted'),
3868 3873 ui.label(_('%d unknown'), 'status.unknown'),
3869 3874 ui.label(_('%d ignored'), 'status.ignored'),
3870 3875 ui.label(_('%d unresolved'), 'resolve.unresolved'),
3871 3876 ui.label(_('%d subrepos'), 'status.modified')]
3872 3877 t = []
3873 3878 for s, l in zip(st, labels):
3874 3879 if s:
3875 3880 t.append(l % len(s))
3876 3881
3877 3882 t = ', '.join(t)
3878 3883 cleanworkdir = False
3879 3884
3880 3885 if len(parents) > 1:
3881 3886 t += _(' (merge)')
3882 3887 elif branch != parents[0].branch():
3883 3888 t += _(' (new branch)')
3884 3889 elif (parents[0].extra().get('close') and
3885 3890 pnode in repo.branchheads(branch, closed=True)):
3886 3891 t += _(' (head closed)')
3887 3892 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
3888 3893 t += _(' (clean)')
3889 3894 cleanworkdir = True
3890 3895 elif pnode not in bheads:
3891 3896 t += _(' (new branch head)')
3892 3897
3893 3898 if cleanworkdir:
3894 3899 ui.status(_('commit: %s\n') % t.strip())
3895 3900 else:
3896 3901 ui.write(_('commit: %s\n') % t.strip())
3897 3902
3898 3903 # all ancestors of branch heads - all ancestors of parent = new csets
3899 3904 new = [0] * len(repo)
3900 3905 cl = repo.changelog
3901 3906 for a in [cl.rev(n) for n in bheads]:
3902 3907 new[a] = 1
3903 3908 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
3904 3909 new[a] = 1
3905 3910 for a in [p.rev() for p in parents]:
3906 3911 if a >= 0:
3907 3912 new[a] = 0
3908 3913 for a in cl.ancestors(*[p.rev() for p in parents]):
3909 3914 new[a] = 0
3910 3915 new = sum(new)
3911 3916
3912 3917 if new == 0:
3913 3918 ui.status(_('update: (current)\n'))
3914 3919 elif pnode not in bheads:
3915 3920 ui.write(_('update: %d new changesets (update)\n') % new)
3916 3921 else:
3917 3922 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
3918 3923 (new, len(bheads)))
3919 3924
3920 3925 if opts.get('remote'):
3921 3926 t = []
3922 3927 source, branches = hg.parseurl(ui.expandpath('default'))
3923 3928 other = hg.repository(hg.remoteui(repo, {}), source)
3924 3929 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3925 3930 ui.debug('comparing with %s\n' % util.hidepassword(source))
3926 3931 repo.ui.pushbuffer()
3927 3932 common, incoming, rheads = discovery.findcommonincoming(repo, other)
3928 3933 repo.ui.popbuffer()
3929 3934 if incoming:
3930 3935 t.append(_('1 or more incoming'))
3931 3936
3932 3937 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
3933 3938 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3934 3939 other = hg.repository(hg.remoteui(repo, {}), dest)
3935 3940 ui.debug('comparing with %s\n' % util.hidepassword(dest))
3936 3941 repo.ui.pushbuffer()
3937 3942 common, _anyinc, _heads = discovery.findcommonincoming(repo, other)
3938 3943 repo.ui.popbuffer()
3939 3944 o = repo.changelog.findmissing(common=common)
3940 3945 if o:
3941 3946 t.append(_('%d outgoing') % len(o))
3942 3947 if 'bookmarks' in other.listkeys('namespaces'):
3943 3948 lmarks = repo.listkeys('bookmarks')
3944 3949 rmarks = other.listkeys('bookmarks')
3945 3950 diff = set(rmarks) - set(lmarks)
3946 3951 if len(diff) > 0:
3947 3952 t.append(_('%d incoming bookmarks') % len(diff))
3948 3953 diff = set(lmarks) - set(rmarks)
3949 3954 if len(diff) > 0:
3950 3955 t.append(_('%d outgoing bookmarks') % len(diff))
3951 3956
3952 3957 if t:
3953 3958 ui.write(_('remote: %s\n') % (', '.join(t)))
3954 3959 else:
3955 3960 ui.status(_('remote: (synced)\n'))
3956 3961
3957 3962 def tag(ui, repo, name1, *names, **opts):
3958 3963 """add one or more tags for the current or given revision
3959 3964
3960 3965 Name a particular revision using <name>.
3961 3966
3962 3967 Tags are used to name particular revisions of the repository and are
3963 3968 very useful to compare different revisions, to go back to significant
3964 3969 earlier versions or to mark branch points as releases, etc. Changing
3965 3970 an existing tag is normally disallowed; use -f/--force to override.
3966 3971
3967 3972 If no revision is given, the parent of the working directory is
3968 3973 used, or tip if no revision is checked out.
3969 3974
3970 3975 To facilitate version control, distribution, and merging of tags,
3971 3976 they are stored as a file named ".hgtags" which is managed similarly
3972 3977 to other project files and can be hand-edited if necessary. This
3973 3978 also means that tagging creates a new commit. The file
3974 3979 ".hg/localtags" is used for local tags (not shared among
3975 3980 repositories).
3976 3981
3977 3982 Tag commits are usually made at the head of a branch. If the parent
3978 3983 of the working directory is not a branch head, :hg:`tag` aborts; use
3979 3984 -f/--force to force the tag commit to be based on a non-head
3980 3985 changeset.
3981 3986
3982 3987 See :hg:`help dates` for a list of formats valid for -d/--date.
3983 3988
3984 3989 Since tag names have priority over branch names during revision
3985 3990 lookup, using an existing branch name as a tag name is discouraged.
3986 3991
3987 3992 Returns 0 on success.
3988 3993 """
3989 3994
3990 3995 rev_ = "."
3991 3996 names = [t.strip() for t in (name1,) + names]
3992 3997 if len(names) != len(set(names)):
3993 3998 raise util.Abort(_('tag names must be unique'))
3994 3999 for n in names:
3995 4000 if n in ['tip', '.', 'null']:
3996 4001 raise util.Abort(_('the name \'%s\' is reserved') % n)
3997 4002 if not n:
3998 4003 raise util.Abort(_('tag names cannot consist entirely of whitespace'))
3999 4004 if opts.get('rev') and opts.get('remove'):
4000 4005 raise util.Abort(_("--rev and --remove are incompatible"))
4001 4006 if opts.get('rev'):
4002 4007 rev_ = opts['rev']
4003 4008 message = opts.get('message')
4004 4009 if opts.get('remove'):
4005 4010 expectedtype = opts.get('local') and 'local' or 'global'
4006 4011 for n in names:
4007 4012 if not repo.tagtype(n):
4008 4013 raise util.Abort(_('tag \'%s\' does not exist') % n)
4009 4014 if repo.tagtype(n) != expectedtype:
4010 4015 if expectedtype == 'global':
4011 4016 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
4012 4017 else:
4013 4018 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
4014 4019 rev_ = nullid
4015 4020 if not message:
4016 4021 # we don't translate commit messages
4017 4022 message = 'Removed tag %s' % ', '.join(names)
4018 4023 elif not opts.get('force'):
4019 4024 for n in names:
4020 4025 if n in repo.tags():
4021 4026 raise util.Abort(_('tag \'%s\' already exists '
4022 4027 '(use -f to force)') % n)
4023 4028 if not opts.get('local'):
4024 4029 p1, p2 = repo.dirstate.parents()
4025 4030 if p2 != nullid:
4026 4031 raise util.Abort(_('uncommitted merge'))
4027 4032 bheads = repo.branchheads()
4028 4033 if not opts.get('force') and bheads and p1 not in bheads:
4029 4034 raise util.Abort(_('not at a branch head (use -f to force)'))
4030 4035 r = cmdutil.revsingle(repo, rev_).node()
4031 4036
4032 4037 if not message:
4033 4038 # we don't translate commit messages
4034 4039 message = ('Added tag %s for changeset %s' %
4035 4040 (', '.join(names), short(r)))
4036 4041
4037 4042 date = opts.get('date')
4038 4043 if date:
4039 4044 date = util.parsedate(date)
4040 4045
4041 4046 if opts.get('edit'):
4042 4047 message = ui.edit(message, ui.username())
4043 4048
4044 4049 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
4045 4050
4046 4051 def tags(ui, repo):
4047 4052 """list repository tags
4048 4053
4049 4054 This lists both regular and local tags. When the -v/--verbose
4050 4055 switch is used, a third column "local" is printed for local tags.
4051 4056
4052 4057 Returns 0 on success.
4053 4058 """
4054 4059
4055 4060 hexfunc = ui.debugflag and hex or short
4056 4061 tagtype = ""
4057 4062
4058 4063 for t, n in reversed(repo.tagslist()):
4059 4064 if ui.quiet:
4060 4065 ui.write("%s\n" % t)
4061 4066 continue
4062 4067
4063 4068 hn = hexfunc(n)
4064 4069 r = "%5d:%s" % (repo.changelog.rev(n), hn)
4065 4070 spaces = " " * (30 - encoding.colwidth(t))
4066 4071
4067 4072 if ui.verbose:
4068 4073 if repo.tagtype(t) == 'local':
4069 4074 tagtype = " local"
4070 4075 else:
4071 4076 tagtype = ""
4072 4077 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
4073 4078
4074 4079 def tip(ui, repo, **opts):
4075 4080 """show the tip revision
4076 4081
4077 4082 The tip revision (usually just called the tip) is the changeset
4078 4083 most recently added to the repository (and therefore the most
4079 4084 recently changed head).
4080 4085
4081 4086 If you have just made a commit, that commit will be the tip. If
4082 4087 you have just pulled changes from another repository, the tip of
4083 4088 that repository becomes the current tip. The "tip" tag is special
4084 4089 and cannot be renamed or assigned to a different changeset.
4085 4090
4086 4091 Returns 0 on success.
4087 4092 """
4088 4093 displayer = cmdutil.show_changeset(ui, repo, opts)
4089 4094 displayer.show(repo[len(repo) - 1])
4090 4095 displayer.close()
4091 4096
4092 4097 def unbundle(ui, repo, fname1, *fnames, **opts):
4093 4098 """apply one or more changegroup files
4094 4099
4095 4100 Apply one or more compressed changegroup files generated by the
4096 4101 bundle command.
4097 4102
4098 4103 Returns 0 on success, 1 if an update has unresolved files.
4099 4104 """
4100 4105 fnames = (fname1,) + fnames
4101 4106
4102 4107 lock = repo.lock()
4103 4108 wc = repo['.']
4104 4109 try:
4105 4110 for fname in fnames:
4106 4111 f = url.open(ui, fname)
4107 4112 gen = changegroup.readbundle(f, fname)
4108 4113 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname,
4109 4114 lock=lock)
4110 4115 bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
4111 4116 finally:
4112 4117 lock.release()
4113 4118 return postincoming(ui, repo, modheads, opts.get('update'), None)
4114 4119
4115 4120 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
4116 4121 """update working directory (or switch revisions)
4117 4122
4118 4123 Update the repository's working directory to the specified
4119 4124 changeset. If no changeset is specified, update to the tip of the
4120 4125 current named branch.
4121 4126
4122 4127 If the changeset is not a descendant of the working directory's
4123 4128 parent, the update is aborted. With the -c/--check option, the
4124 4129 working directory is checked for uncommitted changes; if none are
4125 4130 found, the working directory is updated to the specified
4126 4131 changeset.
4127 4132
4128 4133 The following rules apply when the working directory contains
4129 4134 uncommitted changes:
4130 4135
4131 4136 1. If neither -c/--check nor -C/--clean is specified, and if
4132 4137 the requested changeset is an ancestor or descendant of
4133 4138 the working directory's parent, the uncommitted changes
4134 4139 are merged into the requested changeset and the merged
4135 4140 result is left uncommitted. If the requested changeset is
4136 4141 not an ancestor or descendant (that is, it is on another
4137 4142 branch), the update is aborted and the uncommitted changes
4138 4143 are preserved.
4139 4144
4140 4145 2. With the -c/--check option, the update is aborted and the
4141 4146 uncommitted changes are preserved.
4142 4147
4143 4148 3. With the -C/--clean option, uncommitted changes are discarded and
4144 4149 the working directory is updated to the requested changeset.
4145 4150
4146 4151 Use null as the changeset to remove the working directory (like
4147 4152 :hg:`clone -U`).
4148 4153
4149 4154 If you want to update just one file to an older changeset, use
4150 4155 :hg:`revert`.
4151 4156
4152 4157 See :hg:`help dates` for a list of formats valid for -d/--date.
4153 4158
4154 4159 Returns 0 on success, 1 if there are unresolved files.
4155 4160 """
4156 4161 if rev and node:
4157 4162 raise util.Abort(_("please specify just one revision"))
4158 4163
4159 4164 if rev is None or rev == '':
4160 4165 rev = node
4161 4166
4162 4167 # if we defined a bookmark, we have to remember the original bookmark name
4163 4168 brev = rev
4164 4169 rev = cmdutil.revsingle(repo, rev, rev).rev()
4165 4170
4166 4171 if check and clean:
4167 4172 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
4168 4173
4169 4174 if check:
4170 4175 # we could use dirty() but we can ignore merge and branch trivia
4171 4176 c = repo[None]
4172 4177 if c.modified() or c.added() or c.removed():
4173 4178 raise util.Abort(_("uncommitted local changes"))
4174 4179
4175 4180 if date:
4176 4181 if rev is not None:
4177 4182 raise util.Abort(_("you can't specify a revision and a date"))
4178 4183 rev = cmdutil.finddate(ui, repo, date)
4179 4184
4180 4185 if clean or check:
4181 4186 ret = hg.clean(repo, rev)
4182 4187 else:
4183 4188 ret = hg.update(repo, rev)
4184 4189
4185 4190 if brev in repo._bookmarks:
4186 4191 bookmarks.setcurrent(repo, brev)
4187 4192
4188 4193 return ret
4189 4194
4190 4195 def verify(ui, repo):
4191 4196 """verify the integrity of the repository
4192 4197
4193 4198 Verify the integrity of the current repository.
4194 4199
4195 4200 This will perform an extensive check of the repository's
4196 4201 integrity, validating the hashes and checksums of each entry in
4197 4202 the changelog, manifest, and tracked files, as well as the
4198 4203 integrity of their crosslinks and indices.
4199 4204
4200 4205 Returns 0 on success, 1 if errors are encountered.
4201 4206 """
4202 4207 return hg.verify(repo)
4203 4208
4204 4209 def version_(ui):
4205 4210 """output version and copyright information"""
4206 4211 ui.write(_("Mercurial Distributed SCM (version %s)\n")
4207 4212 % util.version())
4208 4213 ui.status(_(
4209 4214 "(see http://mercurial.selenic.com for more information)\n"
4210 4215 "\nCopyright (C) 2005-2011 Matt Mackall and others\n"
4211 4216 "This is free software; see the source for copying conditions. "
4212 4217 "There is NO\nwarranty; "
4213 4218 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
4214 4219 ))
4215 4220
4216 4221 # Command options and aliases are listed here, alphabetically
4217 4222
4218 4223 globalopts = [
4219 4224 ('R', 'repository', '',
4220 4225 _('repository root directory or name of overlay bundle file'),
4221 4226 _('REPO')),
4222 4227 ('', 'cwd', '',
4223 4228 _('change working directory'), _('DIR')),
4224 4229 ('y', 'noninteractive', None,
4225 4230 _('do not prompt, assume \'yes\' for any required answers')),
4226 4231 ('q', 'quiet', None, _('suppress output')),
4227 4232 ('v', 'verbose', None, _('enable additional output')),
4228 4233 ('', 'config', [],
4229 4234 _('set/override config option (use \'section.name=value\')'),
4230 4235 _('CONFIG')),
4231 4236 ('', 'debug', None, _('enable debugging output')),
4232 4237 ('', 'debugger', None, _('start debugger')),
4233 4238 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
4234 4239 _('ENCODE')),
4235 4240 ('', 'encodingmode', encoding.encodingmode,
4236 4241 _('set the charset encoding mode'), _('MODE')),
4237 4242 ('', 'traceback', None, _('always print a traceback on exception')),
4238 4243 ('', 'time', None, _('time how long the command takes')),
4239 4244 ('', 'profile', None, _('print command execution profile')),
4240 4245 ('', 'version', None, _('output version information and exit')),
4241 4246 ('h', 'help', None, _('display help and exit')),
4242 4247 ]
4243 4248
4244 4249 dryrunopts = [('n', 'dry-run', None,
4245 4250 _('do not perform actions, just print output'))]
4246 4251
4247 4252 remoteopts = [
4248 4253 ('e', 'ssh', '',
4249 4254 _('specify ssh command to use'), _('CMD')),
4250 4255 ('', 'remotecmd', '',
4251 4256 _('specify hg command to run on the remote side'), _('CMD')),
4252 4257 ('', 'insecure', None,
4253 4258 _('do not verify server certificate (ignoring web.cacerts config)')),
4254 4259 ]
4255 4260
4256 4261 walkopts = [
4257 4262 ('I', 'include', [],
4258 4263 _('include names matching the given patterns'), _('PATTERN')),
4259 4264 ('X', 'exclude', [],
4260 4265 _('exclude names matching the given patterns'), _('PATTERN')),
4261 4266 ]
4262 4267
4263 4268 commitopts = [
4264 4269 ('m', 'message', '',
4265 4270 _('use text as commit message'), _('TEXT')),
4266 4271 ('l', 'logfile', '',
4267 4272 _('read commit message from file'), _('FILE')),
4268 4273 ]
4269 4274
4270 4275 commitopts2 = [
4271 4276 ('d', 'date', '',
4272 4277 _('record the specified date as commit date'), _('DATE')),
4273 4278 ('u', 'user', '',
4274 4279 _('record the specified user as committer'), _('USER')),
4275 4280 ]
4276 4281
4277 4282 templateopts = [
4278 4283 ('', 'style', '',
4279 4284 _('display using template map file'), _('STYLE')),
4280 4285 ('', 'template', '',
4281 4286 _('display with template'), _('TEMPLATE')),
4282 4287 ]
4283 4288
4284 4289 logopts = [
4285 4290 ('p', 'patch', None, _('show patch')),
4286 4291 ('g', 'git', None, _('use git extended diff format')),
4287 4292 ('l', 'limit', '',
4288 4293 _('limit number of changes displayed'), _('NUM')),
4289 4294 ('M', 'no-merges', None, _('do not show merges')),
4290 4295 ('', 'stat', None, _('output diffstat-style summary of changes')),
4291 4296 ] + templateopts
4292 4297
4293 4298 diffopts = [
4294 4299 ('a', 'text', None, _('treat all files as text')),
4295 4300 ('g', 'git', None, _('use git extended diff format')),
4296 4301 ('', 'nodates', None, _('omit dates from diff headers'))
4297 4302 ]
4298 4303
4299 4304 diffopts2 = [
4300 4305 ('p', 'show-function', None, _('show which function each change is in')),
4301 4306 ('', 'reverse', None, _('produce a diff that undoes the changes')),
4302 4307 ('w', 'ignore-all-space', None,
4303 4308 _('ignore white space when comparing lines')),
4304 4309 ('b', 'ignore-space-change', None,
4305 4310 _('ignore changes in the amount of white space')),
4306 4311 ('B', 'ignore-blank-lines', None,
4307 4312 _('ignore changes whose lines are all blank')),
4308 4313 ('U', 'unified', '',
4309 4314 _('number of lines of context to show'), _('NUM')),
4310 4315 ('', 'stat', None, _('output diffstat-style summary of changes')),
4311 4316 ]
4312 4317
4313 4318 similarityopts = [
4314 4319 ('s', 'similarity', '',
4315 4320 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
4316 4321 ]
4317 4322
4318 4323 subrepoopts = [
4319 4324 ('S', 'subrepos', None,
4320 4325 _('recurse into subrepositories'))
4321 4326 ]
4322 4327
4323 4328 table = {
4324 4329 "^add": (add, walkopts + subrepoopts + dryrunopts,
4325 4330 _('[OPTION]... [FILE]...')),
4326 4331 "addremove":
4327 4332 (addremove, similarityopts + walkopts + dryrunopts,
4328 4333 _('[OPTION]... [FILE]...')),
4329 4334 "^annotate|blame":
4330 4335 (annotate,
4331 4336 [('r', 'rev', '',
4332 4337 _('annotate the specified revision'), _('REV')),
4333 4338 ('', 'follow', None,
4334 4339 _('follow copies/renames and list the filename (DEPRECATED)')),
4335 4340 ('', 'no-follow', None, _("don't follow copies and renames")),
4336 4341 ('a', 'text', None, _('treat all files as text')),
4337 4342 ('u', 'user', None, _('list the author (long with -v)')),
4338 4343 ('f', 'file', None, _('list the filename')),
4339 4344 ('d', 'date', None, _('list the date (short with -q)')),
4340 4345 ('n', 'number', None, _('list the revision number (default)')),
4341 4346 ('c', 'changeset', None, _('list the changeset')),
4342 4347 ('l', 'line-number', None,
4343 4348 _('show line number at the first appearance'))
4344 4349 ] + walkopts,
4345 4350 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
4346 4351 "archive":
4347 4352 (archive,
4348 4353 [('', 'no-decode', None, _('do not pass files through decoders')),
4349 4354 ('p', 'prefix', '',
4350 4355 _('directory prefix for files in archive'), _('PREFIX')),
4351 4356 ('r', 'rev', '',
4352 4357 _('revision to distribute'), _('REV')),
4353 4358 ('t', 'type', '',
4354 4359 _('type of distribution to create'), _('TYPE')),
4355 4360 ] + subrepoopts + walkopts,
4356 4361 _('[OPTION]... DEST')),
4357 4362 "backout":
4358 4363 (backout,
4359 4364 [('', 'merge', None,
4360 4365 _('merge with old dirstate parent after backout')),
4361 4366 ('', 'parent', '',
4362 4367 _('parent to choose when backing out merge'), _('REV')),
4363 4368 ('t', 'tool', '',
4364 4369 _('specify merge tool')),
4365 4370 ('r', 'rev', '',
4366 4371 _('revision to backout'), _('REV')),
4367 4372 ] + walkopts + commitopts + commitopts2,
4368 4373 _('[OPTION]... [-r] REV')),
4369 4374 "bisect":
4370 4375 (bisect,
4371 4376 [('r', 'reset', False, _('reset bisect state')),
4372 4377 ('g', 'good', False, _('mark changeset good')),
4373 4378 ('b', 'bad', False, _('mark changeset bad')),
4374 4379 ('s', 'skip', False, _('skip testing changeset')),
4375 4380 ('e', 'extend', False, _('extend the bisect range')),
4376 4381 ('c', 'command', '',
4377 4382 _('use command to check changeset state'), _('CMD')),
4378 4383 ('U', 'noupdate', False, _('do not update to target'))],
4379 4384 _("[-gbsr] [-U] [-c CMD] [REV]")),
4380 4385 "bookmarks":
4381 4386 (bookmark,
4382 4387 [('f', 'force', False, _('force')),
4383 4388 ('r', 'rev', '', _('revision'), _('REV')),
4384 4389 ('d', 'delete', False, _('delete a given bookmark')),
4385 4390 ('m', 'rename', '', _('rename a given bookmark'), _('NAME'))],
4386 4391 _('hg bookmarks [-f] [-d] [-m NAME] [-r REV] [NAME]')),
4387 4392 "branch":
4388 4393 (branch,
4389 4394 [('f', 'force', None,
4390 4395 _('set branch name even if it shadows an existing branch')),
4391 4396 ('C', 'clean', None, _('reset branch name to parent branch name'))],
4392 4397 _('[-fC] [NAME]')),
4393 4398 "branches":
4394 4399 (branches,
4395 4400 [('a', 'active', False,
4396 4401 _('show only branches that have unmerged heads')),
4397 4402 ('c', 'closed', False,
4398 4403 _('show normal and closed branches'))],
4399 4404 _('[-ac]')),
4400 4405 "bundle":
4401 4406 (bundle,
4402 4407 [('f', 'force', None,
4403 4408 _('run even when the destination is unrelated')),
4404 4409 ('r', 'rev', [],
4405 4410 _('a changeset intended to be added to the destination'),
4406 4411 _('REV')),
4407 4412 ('b', 'branch', [],
4408 4413 _('a specific branch you would like to bundle'),
4409 4414 _('BRANCH')),
4410 4415 ('', 'base', [],
4411 4416 _('a base changeset assumed to be available at the destination'),
4412 4417 _('REV')),
4413 4418 ('a', 'all', None, _('bundle all changesets in the repository')),
4414 4419 ('t', 'type', 'bzip2',
4415 4420 _('bundle compression type to use'), _('TYPE')),
4416 4421 ] + remoteopts,
4417 4422 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
4418 4423 "cat":
4419 4424 (cat,
4420 4425 [('o', 'output', '',
4421 4426 _('print output to file with formatted name'), _('FORMAT')),
4422 4427 ('r', 'rev', '',
4423 4428 _('print the given revision'), _('REV')),
4424 4429 ('', 'decode', None, _('apply any matching decode filter')),
4425 4430 ] + walkopts,
4426 4431 _('[OPTION]... FILE...')),
4427 4432 "^clone":
4428 4433 (clone,
4429 4434 [('U', 'noupdate', None,
4430 4435 _('the clone will include an empty working copy (only a repository)')),
4431 4436 ('u', 'updaterev', '',
4432 4437 _('revision, tag or branch to check out'), _('REV')),
4433 4438 ('r', 'rev', [],
4434 4439 _('include the specified changeset'), _('REV')),
4435 4440 ('b', 'branch', [],
4436 4441 _('clone only the specified branch'), _('BRANCH')),
4437 4442 ('', 'pull', None, _('use pull protocol to copy metadata')),
4438 4443 ('', 'uncompressed', None,
4439 4444 _('use uncompressed transfer (fast over LAN)')),
4440 4445 ] + remoteopts,
4441 4446 _('[OPTION]... SOURCE [DEST]')),
4442 4447 "^commit|ci":
4443 4448 (commit,
4444 4449 [('A', 'addremove', None,
4445 4450 _('mark new/missing files as added/removed before committing')),
4446 4451 ('', 'close-branch', None,
4447 4452 _('mark a branch as closed, hiding it from the branch list')),
4448 4453 ] + walkopts + commitopts + commitopts2,
4449 4454 _('[OPTION]... [FILE]...')),
4450 4455 "copy|cp":
4451 4456 (copy,
4452 4457 [('A', 'after', None, _('record a copy that has already occurred')),
4453 4458 ('f', 'force', None,
4454 4459 _('forcibly copy over an existing managed file')),
4455 4460 ] + walkopts + dryrunopts,
4456 4461 _('[OPTION]... [SOURCE]... DEST')),
4457 4462 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
4458 4463 "debugbuilddag":
4459 4464 (debugbuilddag,
4460 4465 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
4461 4466 ('a', 'appended-file', None, _('add single file all revs append to')),
4462 4467 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
4463 4468 ('n', 'new-file', None, _('add new file at each rev')),
4464 4469 ],
4465 4470 _('[OPTION]... TEXT')),
4466 4471 "debugbundle":
4467 4472 (debugbundle,
4468 4473 [('a', 'all', None, _('show all details')),
4469 4474 ],
4470 4475 _('FILE')),
4471 4476 "debugcheckstate": (debugcheckstate, [], ''),
4472 4477 "debugcommands": (debugcommands, [], _('[COMMAND]')),
4473 4478 "debugcomplete":
4474 4479 (debugcomplete,
4475 4480 [('o', 'options', None, _('show the command options'))],
4476 4481 _('[-o] CMD')),
4477 4482 "debugdag":
4478 4483 (debugdag,
4479 4484 [('t', 'tags', None, _('use tags as labels')),
4480 4485 ('b', 'branches', None, _('annotate with branch names')),
4481 4486 ('', 'dots', None, _('use dots for runs')),
4482 4487 ('s', 'spaces', None, _('separate elements by spaces')),
4483 4488 ],
4484 4489 _('[OPTION]... [FILE [REV]...]')),
4485 4490 "debugdate":
4486 4491 (debugdate,
4487 4492 [('e', 'extended', None, _('try extended date formats'))],
4488 4493 _('[-e] DATE [RANGE]')),
4489 4494 "debugdata": (debugdata, [], _('FILE REV')),
4490 4495 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
4491 4496 "debuggetbundle":
4492 4497 (debuggetbundle,
4493 4498 [('H', 'head', [], _('id of head node'), _('ID')),
4494 4499 ('C', 'common', [], _('id of common node'), _('ID')),
4495 4500 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
4496 4501 ],
4497 4502 _('REPO FILE [-H|-C ID]...')),
4498 4503 "debugignore": (debugignore, [], ''),
4499 4504 "debugindex": (debugindex,
4500 4505 [('f', 'format', 0, _('revlog format'), _('FORMAT'))],
4501 4506 _('FILE')),
4502 4507 "debugindexdot": (debugindexdot, [], _('FILE')),
4503 4508 "debuginstall": (debuginstall, [], ''),
4504 4509 "debugknown": (debugknown, [], _('REPO ID...')),
4505 4510 "debugpushkey": (debugpushkey, [], _('REPO NAMESPACE [KEY OLD NEW]')),
4506 4511 "debugrebuildstate":
4507 4512 (debugrebuildstate,
4508 4513 [('r', 'rev', '',
4509 4514 _('revision to rebuild to'), _('REV'))],
4510 4515 _('[-r REV] [REV]')),
4511 4516 "debugrename":
4512 4517 (debugrename,
4513 4518 [('r', 'rev', '',
4514 4519 _('revision to debug'), _('REV'))],
4515 4520 _('[-r REV] FILE')),
4516 4521 "debugrevspec":
4517 4522 (debugrevspec, [], ('REVSPEC')),
4518 4523 "debugsetparents":
4519 4524 (debugsetparents, [], _('REV1 [REV2]')),
4520 4525 "debugstate":
4521 4526 (debugstate,
4522 4527 [('', 'nodates', None, _('do not display the saved mtime')),
4523 4528 ('', 'datesort', None, _('sort by saved mtime'))],
4524 4529 _('[OPTION]...')),
4525 4530 "debugsub":
4526 4531 (debugsub,
4527 4532 [('r', 'rev', '',
4528 4533 _('revision to check'), _('REV'))],
4529 4534 _('[-r REV] [REV]')),
4530 4535 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
4531 4536 "debugwireargs":
4532 4537 (debugwireargs,
4533 4538 [('', 'three', '', 'three'),
4534 4539 ('', 'four', '', 'four'),
4535 4540 ('', 'five', '', 'five'),
4536 4541 ] + remoteopts,
4537 4542 _('REPO [OPTIONS]... [ONE [TWO]]')),
4538 4543 "^diff":
4539 4544 (diff,
4540 4545 [('r', 'rev', [],
4541 4546 _('revision'), _('REV')),
4542 4547 ('c', 'change', '',
4543 4548 _('change made by revision'), _('REV'))
4544 4549 ] + diffopts + diffopts2 + walkopts + subrepoopts,
4545 4550 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...')),
4546 4551 "^export":
4547 4552 (export,
4548 4553 [('o', 'output', '',
4549 4554 _('print output to file with formatted name'), _('FORMAT')),
4550 4555 ('', 'switch-parent', None, _('diff against the second parent')),
4551 4556 ('r', 'rev', [],
4552 4557 _('revisions to export'), _('REV')),
4553 4558 ] + diffopts,
4554 4559 _('[OPTION]... [-o OUTFILESPEC] REV...')),
4555 4560 "^forget":
4556 4561 (forget,
4557 4562 [] + walkopts,
4558 4563 _('[OPTION]... FILE...')),
4559 4564 "grep":
4560 4565 (grep,
4561 4566 [('0', 'print0', None, _('end fields with NUL')),
4562 4567 ('', 'all', None, _('print all revisions that match')),
4563 4568 ('a', 'text', None, _('treat all files as text')),
4564 4569 ('f', 'follow', None,
4565 4570 _('follow changeset history,'
4566 4571 ' or file history across copies and renames')),
4567 4572 ('i', 'ignore-case', None, _('ignore case when matching')),
4568 4573 ('l', 'files-with-matches', None,
4569 4574 _('print only filenames and revisions that match')),
4570 4575 ('n', 'line-number', None, _('print matching line numbers')),
4571 4576 ('r', 'rev', [],
4572 4577 _('only search files changed within revision range'), _('REV')),
4573 4578 ('u', 'user', None, _('list the author (long with -v)')),
4574 4579 ('d', 'date', None, _('list the date (short with -q)')),
4575 4580 ] + walkopts,
4576 4581 _('[OPTION]... PATTERN [FILE]...')),
4577 4582 "heads":
4578 4583 (heads,
4579 4584 [('r', 'rev', '',
4580 4585 _('show only heads which are descendants of STARTREV'),
4581 4586 _('STARTREV')),
4582 4587 ('t', 'topo', False, _('show topological heads only')),
4583 4588 ('a', 'active', False,
4584 4589 _('show active branchheads only (DEPRECATED)')),
4585 4590 ('c', 'closed', False,
4586 4591 _('show normal and closed branch heads')),
4587 4592 ] + templateopts,
4588 4593 _('[-ac] [-r STARTREV] [REV]...')),
4589 4594 "help": (help_, [], _('[TOPIC]')),
4590 4595 "identify|id":
4591 4596 (identify,
4592 4597 [('r', 'rev', '',
4593 4598 _('identify the specified revision'), _('REV')),
4594 4599 ('n', 'num', None, _('show local revision number')),
4595 4600 ('i', 'id', None, _('show global revision id')),
4596 4601 ('b', 'branch', None, _('show branch')),
4597 4602 ('t', 'tags', None, _('show tags')),
4598 4603 ('B', 'bookmarks', None, _('show bookmarks'))],
4599 4604 _('[-nibtB] [-r REV] [SOURCE]')),
4600 4605 "import|patch":
4601 4606 (import_,
4602 4607 [('p', 'strip', 1,
4603 4608 _('directory strip option for patch. This has the same '
4604 4609 'meaning as the corresponding patch option'),
4605 4610 _('NUM')),
4606 4611 ('b', 'base', '',
4607 4612 _('base path'), _('PATH')),
4608 4613 ('f', 'force', None,
4609 4614 _('skip check for outstanding uncommitted changes')),
4610 4615 ('', 'no-commit', None,
4611 4616 _("don't commit, just update the working directory")),
4612 4617 ('', 'exact', None,
4613 4618 _('apply patch to the nodes from which it was generated')),
4614 4619 ('', 'import-branch', None,
4615 4620 _('use any branch information in patch (implied by --exact)'))] +
4616 4621 commitopts + commitopts2 + similarityopts,
4617 4622 _('[OPTION]... PATCH...')),
4618 4623 "incoming|in":
4619 4624 (incoming,
4620 4625 [('f', 'force', None,
4621 4626 _('run even if remote repository is unrelated')),
4622 4627 ('n', 'newest-first', None, _('show newest record first')),
4623 4628 ('', 'bundle', '',
4624 4629 _('file to store the bundles into'), _('FILE')),
4625 4630 ('r', 'rev', [],
4626 4631 _('a remote changeset intended to be added'), _('REV')),
4627 4632 ('B', 'bookmarks', False, _("compare bookmarks")),
4628 4633 ('b', 'branch', [],
4629 4634 _('a specific branch you would like to pull'), _('BRANCH')),
4630 4635 ] + logopts + remoteopts + subrepoopts,
4631 4636 _('[-p] [-n] [-M] [-f] [-r REV]...'
4632 4637 ' [--bundle FILENAME] [SOURCE]')),
4633 4638 "^init":
4634 4639 (init,
4635 4640 remoteopts,
4636 4641 _('[-e CMD] [--remotecmd CMD] [DEST]')),
4637 4642 "locate":
4638 4643 (locate,
4639 4644 [('r', 'rev', '',
4640 4645 _('search the repository as it is in REV'), _('REV')),
4641 4646 ('0', 'print0', None,
4642 4647 _('end filenames with NUL, for use with xargs')),
4643 4648 ('f', 'fullpath', None,
4644 4649 _('print complete paths from the filesystem root')),
4645 4650 ] + walkopts,
4646 4651 _('[OPTION]... [PATTERN]...')),
4647 4652 "^log|history":
4648 4653 (log,
4649 4654 [('f', 'follow', None,
4650 4655 _('follow changeset history,'
4651 4656 ' or file history across copies and renames')),
4652 4657 ('', 'follow-first', None,
4653 4658 _('only follow the first parent of merge changesets')),
4654 4659 ('d', 'date', '',
4655 4660 _('show revisions matching date spec'), _('DATE')),
4656 4661 ('C', 'copies', None, _('show copied files')),
4657 4662 ('k', 'keyword', [],
4658 4663 _('do case-insensitive search for a given text'), _('TEXT')),
4659 4664 ('r', 'rev', [],
4660 4665 _('show the specified revision or range'), _('REV')),
4661 4666 ('', 'removed', None, _('include revisions where files were removed')),
4662 4667 ('m', 'only-merges', None, _('show only merges')),
4663 4668 ('u', 'user', [],
4664 4669 _('revisions committed by user'), _('USER')),
4665 4670 ('', 'only-branch', [],
4666 4671 _('show only changesets within the given named branch (DEPRECATED)'),
4667 4672 _('BRANCH')),
4668 4673 ('b', 'branch', [],
4669 4674 _('show changesets within the given named branch'), _('BRANCH')),
4670 4675 ('P', 'prune', [],
4671 4676 _('do not display revision or any of its ancestors'), _('REV')),
4672 4677 ] + logopts + walkopts,
4673 4678 _('[OPTION]... [FILE]')),
4674 4679 "manifest":
4675 4680 (manifest,
4676 4681 [('r', 'rev', '',
4677 4682 _('revision to display'), _('REV'))],
4678 4683 _('[-r REV]')),
4679 4684 "^merge":
4680 4685 (merge,
4681 4686 [('f', 'force', None, _('force a merge with outstanding changes')),
4682 4687 ('t', 'tool', '', _('specify merge tool')),
4683 4688 ('r', 'rev', '',
4684 4689 _('revision to merge'), _('REV')),
4685 4690 ('P', 'preview', None,
4686 4691 _('review revisions to merge (no merge is performed)'))],
4687 4692 _('[-P] [-f] [[-r] REV]')),
4688 4693 "outgoing|out":
4689 4694 (outgoing,
4690 4695 [('f', 'force', None,
4691 4696 _('run even when the destination is unrelated')),
4692 4697 ('r', 'rev', [],
4693 4698 _('a changeset intended to be included in the destination'),
4694 4699 _('REV')),
4695 4700 ('n', 'newest-first', None, _('show newest record first')),
4696 4701 ('B', 'bookmarks', False, _("compare bookmarks")),
4697 4702 ('b', 'branch', [],
4698 4703 _('a specific branch you would like to push'), _('BRANCH')),
4699 4704 ] + logopts + remoteopts + subrepoopts,
4700 4705 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
4701 4706 "parents":
4702 4707 (parents,
4703 4708 [('r', 'rev', '',
4704 4709 _('show parents of the specified revision'), _('REV')),
4705 4710 ] + templateopts,
4706 4711 _('[-r REV] [FILE]')),
4707 4712 "paths": (paths, [], _('[NAME]')),
4708 4713 "^pull":
4709 4714 (pull,
4710 4715 [('u', 'update', None,
4711 4716 _('update to new branch head if changesets were pulled')),
4712 4717 ('f', 'force', None,
4713 4718 _('run even when remote repository is unrelated')),
4714 4719 ('r', 'rev', [],
4715 4720 _('a remote changeset intended to be added'), _('REV')),
4716 4721 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4717 4722 ('b', 'branch', [],
4718 4723 _('a specific branch you would like to pull'), _('BRANCH')),
4719 4724 ] + remoteopts,
4720 4725 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
4721 4726 "^push":
4722 4727 (push,
4723 4728 [('f', 'force', None, _('force push')),
4724 4729 ('r', 'rev', [],
4725 4730 _('a changeset intended to be included in the destination'),
4726 4731 _('REV')),
4727 4732 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4728 4733 ('b', 'branch', [],
4729 4734 _('a specific branch you would like to push'), _('BRANCH')),
4730 4735 ('', 'new-branch', False, _('allow pushing a new branch')),
4731 4736 ] + remoteopts,
4732 4737 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
4733 4738 "recover": (recover, []),
4734 4739 "^remove|rm":
4735 4740 (remove,
4736 4741 [('A', 'after', None, _('record delete for missing files')),
4737 4742 ('f', 'force', None,
4738 4743 _('remove (and delete) file even if added or modified')),
4739 4744 ] + walkopts,
4740 4745 _('[OPTION]... FILE...')),
4741 4746 "rename|move|mv":
4742 4747 (rename,
4743 4748 [('A', 'after', None, _('record a rename that has already occurred')),
4744 4749 ('f', 'force', None,
4745 4750 _('forcibly copy over an existing managed file')),
4746 4751 ] + walkopts + dryrunopts,
4747 4752 _('[OPTION]... SOURCE... DEST')),
4748 4753 "resolve":
4749 4754 (resolve,
4750 4755 [('a', 'all', None, _('select all unresolved files')),
4751 4756 ('l', 'list', None, _('list state of files needing merge')),
4752 4757 ('m', 'mark', None, _('mark files as resolved')),
4753 4758 ('u', 'unmark', None, _('mark files as unresolved')),
4754 4759 ('t', 'tool', '', _('specify merge tool')),
4755 4760 ('n', 'no-status', None, _('hide status prefix'))]
4756 4761 + walkopts,
4757 4762 _('[OPTION]... [FILE]...')),
4758 4763 "revert":
4759 4764 (revert,
4760 4765 [('a', 'all', None, _('revert all changes when no arguments given')),
4761 4766 ('d', 'date', '',
4762 4767 _('tipmost revision matching date'), _('DATE')),
4763 4768 ('r', 'rev', '',
4764 4769 _('revert to the specified revision'), _('REV')),
4765 4770 ('', 'no-backup', None, _('do not save backup copies of files')),
4766 4771 ] + walkopts + dryrunopts,
4767 4772 _('[OPTION]... [-r REV] [NAME]...')),
4768 4773 "rollback": (rollback, dryrunopts),
4769 4774 "root": (root, []),
4770 4775 "^serve":
4771 4776 (serve,
4772 4777 [('A', 'accesslog', '',
4773 4778 _('name of access log file to write to'), _('FILE')),
4774 4779 ('d', 'daemon', None, _('run server in background')),
4775 4780 ('', 'daemon-pipefds', '',
4776 4781 _('used internally by daemon mode'), _('NUM')),
4777 4782 ('E', 'errorlog', '',
4778 4783 _('name of error log file to write to'), _('FILE')),
4779 4784 # use string type, then we can check if something was passed
4780 4785 ('p', 'port', '',
4781 4786 _('port to listen on (default: 8000)'), _('PORT')),
4782 4787 ('a', 'address', '',
4783 4788 _('address to listen on (default: all interfaces)'), _('ADDR')),
4784 4789 ('', 'prefix', '',
4785 4790 _('prefix path to serve from (default: server root)'), _('PREFIX')),
4786 4791 ('n', 'name', '',
4787 4792 _('name to show in web pages (default: working directory)'),
4788 4793 _('NAME')),
4789 4794 ('', 'web-conf', '',
4790 4795 _('name of the hgweb config file (see "hg help hgweb")'),
4791 4796 _('FILE')),
4792 4797 ('', 'webdir-conf', '',
4793 4798 _('name of the hgweb config file (DEPRECATED)'), _('FILE')),
4794 4799 ('', 'pid-file', '',
4795 4800 _('name of file to write process ID to'), _('FILE')),
4796 4801 ('', 'stdio', None, _('for remote clients')),
4797 4802 ('t', 'templates', '',
4798 4803 _('web templates to use'), _('TEMPLATE')),
4799 4804 ('', 'style', '',
4800 4805 _('template style to use'), _('STYLE')),
4801 4806 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4802 4807 ('', 'certificate', '',
4803 4808 _('SSL certificate file'), _('FILE'))],
4804 4809 _('[OPTION]...')),
4805 4810 "showconfig|debugconfig":
4806 4811 (showconfig,
4807 4812 [('u', 'untrusted', None, _('show untrusted configuration options'))],
4808 4813 _('[-u] [NAME]...')),
4809 4814 "^summary|sum":
4810 4815 (summary,
4811 4816 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
4812 4817 "^status|st":
4813 4818 (status,
4814 4819 [('A', 'all', None, _('show status of all files')),
4815 4820 ('m', 'modified', None, _('show only modified files')),
4816 4821 ('a', 'added', None, _('show only added files')),
4817 4822 ('r', 'removed', None, _('show only removed files')),
4818 4823 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4819 4824 ('c', 'clean', None, _('show only files without changes')),
4820 4825 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4821 4826 ('i', 'ignored', None, _('show only ignored files')),
4822 4827 ('n', 'no-status', None, _('hide status prefix')),
4823 4828 ('C', 'copies', None, _('show source of copied files')),
4824 4829 ('0', 'print0', None,
4825 4830 _('end filenames with NUL, for use with xargs')),
4826 4831 ('', 'rev', [],
4827 4832 _('show difference from revision'), _('REV')),
4828 4833 ('', 'change', '',
4829 4834 _('list the changed files of a revision'), _('REV')),
4830 4835 ] + walkopts + subrepoopts,
4831 4836 _('[OPTION]... [FILE]...')),
4832 4837 "tag":
4833 4838 (tag,
4834 4839 [('f', 'force', None, _('force tag')),
4835 4840 ('l', 'local', None, _('make the tag local')),
4836 4841 ('r', 'rev', '',
4837 4842 _('revision to tag'), _('REV')),
4838 4843 ('', 'remove', None, _('remove a tag')),
4839 4844 # -l/--local is already there, commitopts cannot be used
4840 4845 ('e', 'edit', None, _('edit commit message')),
4841 4846 ('m', 'message', '',
4842 4847 _('use <text> as commit message'), _('TEXT')),
4843 4848 ] + commitopts2,
4844 4849 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
4845 4850 "tags": (tags, [], ''),
4846 4851 "tip":
4847 4852 (tip,
4848 4853 [('p', 'patch', None, _('show patch')),
4849 4854 ('g', 'git', None, _('use git extended diff format')),
4850 4855 ] + templateopts,
4851 4856 _('[-p] [-g]')),
4852 4857 "unbundle":
4853 4858 (unbundle,
4854 4859 [('u', 'update', None,
4855 4860 _('update to new branch head if changesets were unbundled'))],
4856 4861 _('[-u] FILE...')),
4857 4862 "^update|up|checkout|co":
4858 4863 (update,
4859 4864 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
4860 4865 ('c', 'check', None,
4861 4866 _('update across branches if no uncommitted changes')),
4862 4867 ('d', 'date', '',
4863 4868 _('tipmost revision matching date'), _('DATE')),
4864 4869 ('r', 'rev', '',
4865 4870 _('revision'), _('REV'))],
4866 4871 _('[-c] [-C] [-d DATE] [[-r] REV]')),
4867 4872 "verify": (verify, []),
4868 4873 "version": (version_, []),
4869 4874 }
4870 4875
4871 4876 norepo = ("clone init version help debugcommands debugcomplete"
4872 4877 " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
4873 4878 " debugknown debuggetbundle debugbundle")
4874 4879 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
4875 4880 " debugdata debugindex debugindexdot")
@@ -1,1275 +1,1274 b''
1 1 # revlog.py - storage back-end for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 """Storage back-end for Mercurial.
9 9
10 10 This provides efficient delta storage with O(1) retrieve and append
11 11 and O(changes) merge between branches.
12 12 """
13 13
14 14 # import stuff from node for others to import from revlog
15 15 from node import bin, hex, nullid, nullrev, short #@UnusedImport
16 16 from i18n import _
17 17 import ancestor, mdiff, parsers, error, util
18 18 import struct, zlib, errno
19 19
20 20 _pack = struct.pack
21 21 _unpack = struct.unpack
22 22 _compress = zlib.compress
23 23 _decompress = zlib.decompress
24 24 _sha = util.sha1
25 25
26 26 # revlog header flags
27 27 REVLOGV0 = 0
28 28 REVLOGNG = 1
29 29 REVLOGNGINLINEDATA = (1 << 16)
30 30 REVLOGSHALLOW = (1 << 17)
31 31 REVLOG_DEFAULT_FLAGS = REVLOGNGINLINEDATA
32 32 REVLOG_DEFAULT_FORMAT = REVLOGNG
33 33 REVLOG_DEFAULT_VERSION = REVLOG_DEFAULT_FORMAT | REVLOG_DEFAULT_FLAGS
34 34 REVLOGNG_FLAGS = REVLOGNGINLINEDATA | REVLOGSHALLOW
35 35
36 36 # revlog index flags
37 37 REVIDX_PARENTDELTA = 1
38 38 REVIDX_PUNCHED_FLAG = 2
39 39 REVIDX_KNOWN_FLAGS = REVIDX_PUNCHED_FLAG | REVIDX_PARENTDELTA
40 40
41 41 # max size of revlog with inline data
42 42 _maxinline = 131072
43 43 _chunksize = 1048576
44 44
45 45 RevlogError = error.RevlogError
46 46 LookupError = error.LookupError
47 47
48 48 def getoffset(q):
49 49 return int(q >> 16)
50 50
51 51 def gettype(q):
52 52 return int(q & 0xFFFF)
53 53
54 54 def offset_type(offset, type):
55 55 return long(long(offset) << 16 | type)
56 56
57 57 nullhash = _sha(nullid)
58 58
59 59 def hash(text, p1, p2):
60 60 """generate a hash from the given text and its parent hashes
61 61
62 62 This hash combines both the current file contents and its history
63 63 in a manner that makes it easy to distinguish nodes with the same
64 64 content in the revision graph.
65 65 """
66 66 # As of now, if one of the parent node is null, p2 is null
67 67 if p2 == nullid:
68 68 # deep copy of a hash is faster than creating one
69 69 s = nullhash.copy()
70 70 s.update(p1)
71 71 else:
72 72 # none of the parent nodes are nullid
73 73 l = [p1, p2]
74 74 l.sort()
75 75 s = _sha(l[0])
76 76 s.update(l[1])
77 77 s.update(text)
78 78 return s.digest()
79 79
80 80 def compress(text):
81 81 """ generate a possibly-compressed representation of text """
82 82 if not text:
83 83 return ("", text)
84 84 l = len(text)
85 85 bin = None
86 86 if l < 44:
87 87 pass
88 88 elif l > 1000000:
89 89 # zlib makes an internal copy, thus doubling memory usage for
90 90 # large files, so lets do this in pieces
91 91 z = zlib.compressobj()
92 92 p = []
93 93 pos = 0
94 94 while pos < l:
95 95 pos2 = pos + 2**20
96 96 p.append(z.compress(text[pos:pos2]))
97 97 pos = pos2
98 98 p.append(z.flush())
99 99 if sum(map(len, p)) < l:
100 100 bin = "".join(p)
101 101 else:
102 102 bin = _compress(text)
103 103 if bin is None or len(bin) > l:
104 104 if text[0] == '\0':
105 105 return ("", text)
106 106 return ('u', text)
107 107 return ("", bin)
108 108
109 109 def decompress(bin):
110 110 """ decompress the given input """
111 111 if not bin:
112 112 return bin
113 113 t = bin[0]
114 114 if t == '\0':
115 115 return bin
116 116 if t == 'x':
117 117 return _decompress(bin)
118 118 if t == 'u':
119 119 return bin[1:]
120 120 raise RevlogError(_("unknown compression type %r") % t)
121 121
122 122 indexformatv0 = ">4l20s20s20s"
123 123 v0shaoffset = 56
124 124
125 125 class revlogoldio(object):
126 126 def __init__(self):
127 127 self.size = struct.calcsize(indexformatv0)
128 128
129 129 def parseindex(self, data, inline):
130 130 s = self.size
131 131 index = []
132 132 nodemap = {nullid: nullrev}
133 133 n = off = 0
134 134 l = len(data)
135 135 while off + s <= l:
136 136 cur = data[off:off + s]
137 137 off += s
138 138 e = _unpack(indexformatv0, cur)
139 139 # transform to revlogv1 format
140 140 e2 = (offset_type(e[0], 0), e[1], -1, e[2], e[3],
141 141 nodemap.get(e[4], nullrev), nodemap.get(e[5], nullrev), e[6])
142 142 index.append(e2)
143 143 nodemap[e[6]] = n
144 144 n += 1
145 145
146 146 # add the magic null revision at -1
147 147 index.append((0, 0, 0, -1, -1, -1, -1, nullid))
148 148
149 149 return index, nodemap, None
150 150
151 151 def packentry(self, entry, node, version, rev):
152 152 if gettype(entry[0]):
153 153 raise RevlogError(_("index entry flags need RevlogNG"))
154 154 e2 = (getoffset(entry[0]), entry[1], entry[3], entry[4],
155 155 node(entry[5]), node(entry[6]), entry[7])
156 156 return _pack(indexformatv0, *e2)
157 157
158 158 # index ng:
159 159 # 6 bytes: offset
160 160 # 2 bytes: flags
161 161 # 4 bytes: compressed length
162 162 # 4 bytes: uncompressed length
163 163 # 4 bytes: base rev
164 164 # 4 bytes: link rev
165 165 # 4 bytes: parent 1 rev
166 166 # 4 bytes: parent 2 rev
167 167 # 32 bytes: nodeid
168 168 indexformatng = ">Qiiiiii20s12x"
169 169 ngshaoffset = 32
170 170 versionformat = ">I"
171 171
172 172 class revlogio(object):
173 173 def __init__(self):
174 174 self.size = struct.calcsize(indexformatng)
175 175
176 176 def parseindex(self, data, inline):
177 177 # call the C implementation to parse the index data
178 178 index, cache = parsers.parse_index2(data, inline)
179 179 return index, None, cache
180 180
181 181 def packentry(self, entry, node, version, rev):
182 182 p = _pack(indexformatng, *entry)
183 183 if rev == 0:
184 184 p = _pack(versionformat, version) + p[4:]
185 185 return p
186 186
187 187 class revlog(object):
188 188 """
189 189 the underlying revision storage object
190 190
191 191 A revlog consists of two parts, an index and the revision data.
192 192
193 193 The index is a file with a fixed record size containing
194 194 information on each revision, including its nodeid (hash), the
195 195 nodeids of its parents, the position and offset of its data within
196 196 the data file, and the revision it's based on. Finally, each entry
197 197 contains a linkrev entry that can serve as a pointer to external
198 198 data.
199 199
200 200 The revision data itself is a linear collection of data chunks.
201 201 Each chunk represents a revision and is usually represented as a
202 202 delta against the previous chunk. To bound lookup time, runs of
203 203 deltas are limited to about 2 times the length of the original
204 204 version data. This makes retrieval of a version proportional to
205 205 its size, or O(1) relative to the number of revisions.
206 206
207 207 Both pieces of the revlog are written to in an append-only
208 208 fashion, which means we never need to rewrite a file to insert or
209 209 remove data, and can use some simple techniques to avoid the need
210 210 for locking while reading.
211 211 """
212 212 def __init__(self, opener, indexfile, shallowroot=None):
213 213 """
214 214 create a revlog object
215 215
216 216 opener is a function that abstracts the file opening operation
217 217 and can be used to implement COW semantics or the like.
218 218 """
219 219 self.indexfile = indexfile
220 220 self.datafile = indexfile[:-2] + ".d"
221 221 self.opener = opener
222 222 self._cache = None
223 223 self._chunkcache = (0, '')
224 224 self.index = []
225 225 self._shallowroot = shallowroot
226 226 self._parentdelta = 0
227 227 self._pcache = {}
228 228 self._nodecache = {nullid: nullrev}
229 229 self._nodepos = None
230 230
231 231 v = REVLOG_DEFAULT_VERSION
232 232 if hasattr(opener, 'options') and 'defversion' in opener.options:
233 233 v = opener.options['defversion']
234 234 if v & REVLOGNG:
235 235 v |= REVLOGNGINLINEDATA
236 236 if v & REVLOGNG and 'parentdelta' in opener.options:
237 237 self._parentdelta = 1
238 238
239 239 if shallowroot:
240 240 v |= REVLOGSHALLOW
241 241
242 242 i = ''
243 243 try:
244 244 f = self.opener(self.indexfile)
245 245 i = f.read()
246 246 f.close()
247 247 if len(i) > 0:
248 248 v = struct.unpack(versionformat, i[:4])[0]
249 249 except IOError, inst:
250 250 if inst.errno != errno.ENOENT:
251 251 raise
252 252
253 253 self.version = v
254 254 self._inline = v & REVLOGNGINLINEDATA
255 255 self._shallow = v & REVLOGSHALLOW
256 256 flags = v & ~0xFFFF
257 257 fmt = v & 0xFFFF
258 258 if fmt == REVLOGV0 and flags:
259 259 raise RevlogError(_("index %s unknown flags %#04x for format v0")
260 260 % (self.indexfile, flags >> 16))
261 261 elif fmt == REVLOGNG and flags & ~REVLOGNG_FLAGS:
262 262 raise RevlogError(_("index %s unknown flags %#04x for revlogng")
263 263 % (self.indexfile, flags >> 16))
264 264 elif fmt > REVLOGNG:
265 265 raise RevlogError(_("index %s unknown format %d")
266 266 % (self.indexfile, fmt))
267 267
268 268 self._io = revlogio()
269 269 if self.version == REVLOGV0:
270 270 self._io = revlogoldio()
271 271 try:
272 272 d = self._io.parseindex(i, self._inline)
273 273 except (ValueError, IndexError):
274 274 raise RevlogError(_("index %s is corrupted") % (self.indexfile))
275 275 self.index, nodemap, self._chunkcache = d
276 276 if nodemap is not None:
277 277 self.nodemap = self._nodecache = nodemap
278 278 if not self._chunkcache:
279 279 self._chunkclear()
280 280
281 281 def tip(self):
282 282 return self.node(len(self.index) - 2)
283 283 def __len__(self):
284 284 return len(self.index) - 1
285 285 def __iter__(self):
286 286 for i in xrange(len(self)):
287 287 yield i
288 288
289 289 @util.propertycache
290 290 def nodemap(self):
291 291 self.rev(self.node(0))
292 292 return self._nodecache
293 293
294 294 def rev(self, node):
295 295 try:
296 296 return self._nodecache[node]
297 297 except KeyError:
298 298 n = self._nodecache
299 299 i = self.index
300 300 p = self._nodepos
301 301 if p is None:
302 302 p = len(i) - 2
303 303 for r in xrange(p, -1, -1):
304 304 v = i[r][7]
305 305 n[v] = r
306 306 if v == node:
307 307 self._nodepos = r - 1
308 308 return r
309 309 raise LookupError(node, self.indexfile, _('no node'))
310 310
311 311 def node(self, rev):
312 312 return self.index[rev][7]
313 313 def linkrev(self, rev):
314 314 return self.index[rev][4]
315 315 def parents(self, node):
316 316 i = self.index
317 317 d = i[self.rev(node)]
318 318 return i[d[5]][7], i[d[6]][7] # map revisions to nodes inline
319 319 def parentrevs(self, rev):
320 320 return self.index[rev][5:7]
321 321 def start(self, rev):
322 322 return int(self.index[rev][0] >> 16)
323 323 def end(self, rev):
324 324 return self.start(rev) + self.length(rev)
325 325 def length(self, rev):
326 326 return self.index[rev][1]
327 327 def base(self, rev):
328 328 return self.index[rev][3]
329 329 def flags(self, rev):
330 330 return self.index[rev][0] & 0xFFFF
331 331 def rawsize(self, rev):
332 332 """return the length of the uncompressed text for a given revision"""
333 333 l = self.index[rev][2]
334 334 if l >= 0:
335 335 return l
336 336
337 337 t = self.revision(self.node(rev))
338 338 return len(t)
339 339 size = rawsize
340 340
341 341 def reachable(self, node, stop=None):
342 342 """return the set of all nodes ancestral to a given node, including
343 343 the node itself, stopping when stop is matched"""
344 344 reachable = set((node,))
345 345 visit = [node]
346 346 if stop:
347 347 stopn = self.rev(stop)
348 348 else:
349 349 stopn = 0
350 350 while visit:
351 351 n = visit.pop(0)
352 352 if n == stop:
353 353 continue
354 354 if n == nullid:
355 355 continue
356 356 for p in self.parents(n):
357 357 if self.rev(p) < stopn:
358 358 continue
359 359 if p not in reachable:
360 360 reachable.add(p)
361 361 visit.append(p)
362 362 return reachable
363 363
364 364 def ancestors(self, *revs):
365 365 """Generate the ancestors of 'revs' in reverse topological order.
366 366
367 367 Yield a sequence of revision numbers starting with the parents
368 368 of each revision in revs, i.e., each revision is *not* considered
369 369 an ancestor of itself. Results are in breadth-first order:
370 370 parents of each rev in revs, then parents of those, etc. Result
371 371 does not include the null revision."""
372 372 visit = list(revs)
373 373 seen = set([nullrev])
374 374 while visit:
375 375 for parent in self.parentrevs(visit.pop(0)):
376 376 if parent not in seen:
377 377 visit.append(parent)
378 378 seen.add(parent)
379 379 yield parent
380 380
381 381 def descendants(self, *revs):
382 382 """Generate the descendants of 'revs' in revision order.
383 383
384 384 Yield a sequence of revision numbers starting with a child of
385 385 some rev in revs, i.e., each revision is *not* considered a
386 386 descendant of itself. Results are ordered by revision number (a
387 387 topological sort)."""
388 388 first = min(revs)
389 389 if first == nullrev:
390 390 for i in self:
391 391 yield i
392 392 return
393 393
394 394 seen = set(revs)
395 395 for i in xrange(first + 1, len(self)):
396 396 for x in self.parentrevs(i):
397 397 if x != nullrev and x in seen:
398 398 seen.add(i)
399 399 yield i
400 400 break
401 401
402 402 def findcommonmissing(self, common=None, heads=None):
403 403 """Return a tuple of the ancestors of common and the ancestors of heads
404 404 that are not ancestors of common.
405 405
406 406 More specifically, the second element is a list of nodes N such that
407 407 every N satisfies the following constraints:
408 408
409 409 1. N is an ancestor of some node in 'heads'
410 410 2. N is not an ancestor of any node in 'common'
411 411
412 412 The list is sorted by revision number, meaning it is
413 413 topologically sorted.
414 414
415 415 'heads' and 'common' are both lists of node IDs. If heads is
416 416 not supplied, uses all of the revlog's heads. If common is not
417 417 supplied, uses nullid."""
418 418 if common is None:
419 419 common = [nullid]
420 420 if heads is None:
421 421 heads = self.heads()
422 422
423 423 common = [self.rev(n) for n in common]
424 424 heads = [self.rev(n) for n in heads]
425 425
426 426 # we want the ancestors, but inclusive
427 427 has = set(self.ancestors(*common))
428 428 has.add(nullrev)
429 429 has.update(common)
430 430
431 431 # take all ancestors from heads that aren't in has
432 432 missing = set()
433 433 visit = [r for r in heads if r not in has]
434 434 while visit:
435 435 r = visit.pop(0)
436 436 if r in missing:
437 437 continue
438 438 else:
439 439 missing.add(r)
440 440 for p in self.parentrevs(r):
441 441 if p not in has:
442 442 visit.append(p)
443 443 missing = list(missing)
444 444 missing.sort()
445 445 return has, [self.node(r) for r in missing]
446 446
447 447 def findmissing(self, common=None, heads=None):
448 448 """Return the ancestors of heads that are not ancestors of common.
449 449
450 450 More specifically, return a list of nodes N such that every N
451 451 satisfies the following constraints:
452 452
453 453 1. N is an ancestor of some node in 'heads'
454 454 2. N is not an ancestor of any node in 'common'
455 455
456 456 The list is sorted by revision number, meaning it is
457 457 topologically sorted.
458 458
459 459 'heads' and 'common' are both lists of node IDs. If heads is
460 460 not supplied, uses all of the revlog's heads. If common is not
461 461 supplied, uses nullid."""
462 462 _common, missing = self.findcommonmissing(common, heads)
463 463 return missing
464 464
465 465 def nodesbetween(self, roots=None, heads=None):
466 466 """Return a topological path from 'roots' to 'heads'.
467 467
468 468 Return a tuple (nodes, outroots, outheads) where 'nodes' is a
469 469 topologically sorted list of all nodes N that satisfy both of
470 470 these constraints:
471 471
472 472 1. N is a descendant of some node in 'roots'
473 473 2. N is an ancestor of some node in 'heads'
474 474
475 475 Every node is considered to be both a descendant and an ancestor
476 476 of itself, so every reachable node in 'roots' and 'heads' will be
477 477 included in 'nodes'.
478 478
479 479 'outroots' is the list of reachable nodes in 'roots', i.e., the
480 480 subset of 'roots' that is returned in 'nodes'. Likewise,
481 481 'outheads' is the subset of 'heads' that is also in 'nodes'.
482 482
483 483 'roots' and 'heads' are both lists of node IDs. If 'roots' is
484 484 unspecified, uses nullid as the only root. If 'heads' is
485 485 unspecified, uses list of all of the revlog's heads."""
486 486 nonodes = ([], [], [])
487 487 if roots is not None:
488 488 roots = list(roots)
489 489 if not roots:
490 490 return nonodes
491 491 lowestrev = min([self.rev(n) for n in roots])
492 492 else:
493 493 roots = [nullid] # Everybody's a descendent of nullid
494 494 lowestrev = nullrev
495 495 if (lowestrev == nullrev) and (heads is None):
496 496 # We want _all_ the nodes!
497 497 return ([self.node(r) for r in self], [nullid], list(self.heads()))
498 498 if heads is None:
499 499 # All nodes are ancestors, so the latest ancestor is the last
500 500 # node.
501 501 highestrev = len(self) - 1
502 502 # Set ancestors to None to signal that every node is an ancestor.
503 503 ancestors = None
504 504 # Set heads to an empty dictionary for later discovery of heads
505 505 heads = {}
506 506 else:
507 507 heads = list(heads)
508 508 if not heads:
509 509 return nonodes
510 510 ancestors = set()
511 511 # Turn heads into a dictionary so we can remove 'fake' heads.
512 512 # Also, later we will be using it to filter out the heads we can't
513 513 # find from roots.
514 514 heads = dict.fromkeys(heads, 0)
515 515 # Start at the top and keep marking parents until we're done.
516 516 nodestotag = set(heads)
517 517 # Remember where the top was so we can use it as a limit later.
518 518 highestrev = max([self.rev(n) for n in nodestotag])
519 519 while nodestotag:
520 520 # grab a node to tag
521 521 n = nodestotag.pop()
522 522 # Never tag nullid
523 523 if n == nullid:
524 524 continue
525 525 # A node's revision number represents its place in a
526 526 # topologically sorted list of nodes.
527 527 r = self.rev(n)
528 528 if r >= lowestrev:
529 529 if n not in ancestors:
530 530 # If we are possibly a descendent of one of the roots
531 531 # and we haven't already been marked as an ancestor
532 532 ancestors.add(n) # Mark as ancestor
533 533 # Add non-nullid parents to list of nodes to tag.
534 534 nodestotag.update([p for p in self.parents(n) if
535 535 p != nullid])
536 536 elif n in heads: # We've seen it before, is it a fake head?
537 537 # So it is, real heads should not be the ancestors of
538 538 # any other heads.
539 539 heads.pop(n)
540 540 if not ancestors:
541 541 return nonodes
542 542 # Now that we have our set of ancestors, we want to remove any
543 543 # roots that are not ancestors.
544 544
545 545 # If one of the roots was nullid, everything is included anyway.
546 546 if lowestrev > nullrev:
547 547 # But, since we weren't, let's recompute the lowest rev to not
548 548 # include roots that aren't ancestors.
549 549
550 550 # Filter out roots that aren't ancestors of heads
551 551 roots = [n for n in roots if n in ancestors]
552 552 # Recompute the lowest revision
553 553 if roots:
554 554 lowestrev = min([self.rev(n) for n in roots])
555 555 else:
556 556 # No more roots? Return empty list
557 557 return nonodes
558 558 else:
559 559 # We are descending from nullid, and don't need to care about
560 560 # any other roots.
561 561 lowestrev = nullrev
562 562 roots = [nullid]
563 563 # Transform our roots list into a set.
564 564 descendents = set(roots)
565 565 # Also, keep the original roots so we can filter out roots that aren't
566 566 # 'real' roots (i.e. are descended from other roots).
567 567 roots = descendents.copy()
568 568 # Our topologically sorted list of output nodes.
569 569 orderedout = []
570 570 # Don't start at nullid since we don't want nullid in our output list,
571 571 # and if nullid shows up in descedents, empty parents will look like
572 572 # they're descendents.
573 573 for r in xrange(max(lowestrev, 0), highestrev + 1):
574 574 n = self.node(r)
575 575 isdescendent = False
576 576 if lowestrev == nullrev: # Everybody is a descendent of nullid
577 577 isdescendent = True
578 578 elif n in descendents:
579 579 # n is already a descendent
580 580 isdescendent = True
581 581 # This check only needs to be done here because all the roots
582 582 # will start being marked is descendents before the loop.
583 583 if n in roots:
584 584 # If n was a root, check if it's a 'real' root.
585 585 p = tuple(self.parents(n))
586 586 # If any of its parents are descendents, it's not a root.
587 587 if (p[0] in descendents) or (p[1] in descendents):
588 588 roots.remove(n)
589 589 else:
590 590 p = tuple(self.parents(n))
591 591 # A node is a descendent if either of its parents are
592 592 # descendents. (We seeded the dependents list with the roots
593 593 # up there, remember?)
594 594 if (p[0] in descendents) or (p[1] in descendents):
595 595 descendents.add(n)
596 596 isdescendent = True
597 597 if isdescendent and ((ancestors is None) or (n in ancestors)):
598 598 # Only include nodes that are both descendents and ancestors.
599 599 orderedout.append(n)
600 600 if (ancestors is not None) and (n in heads):
601 601 # We're trying to figure out which heads are reachable
602 602 # from roots.
603 603 # Mark this head as having been reached
604 604 heads[n] = 1
605 605 elif ancestors is None:
606 606 # Otherwise, we're trying to discover the heads.
607 607 # Assume this is a head because if it isn't, the next step
608 608 # will eventually remove it.
609 609 heads[n] = 1
610 610 # But, obviously its parents aren't.
611 611 for p in self.parents(n):
612 612 heads.pop(p, None)
613 613 heads = [n for n in heads.iterkeys() if heads[n] != 0]
614 614 roots = list(roots)
615 615 assert orderedout
616 616 assert roots
617 617 assert heads
618 618 return (orderedout, roots, heads)
619 619
620 620 def heads(self, start=None, stop=None):
621 621 """return the list of all nodes that have no children
622 622
623 623 if start is specified, only heads that are descendants of
624 624 start will be returned
625 625 if stop is specified, it will consider all the revs from stop
626 626 as if they had no children
627 627 """
628 628 if start is None and stop is None:
629 629 count = len(self)
630 630 if not count:
631 631 return [nullid]
632 632 ishead = [1] * (count + 1)
633 633 index = self.index
634 634 for r in xrange(count):
635 635 e = index[r]
636 636 ishead[e[5]] = ishead[e[6]] = 0
637 637 return [self.node(r) for r in xrange(count) if ishead[r]]
638 638
639 639 if start is None:
640 640 start = nullid
641 641 if stop is None:
642 642 stop = []
643 643 stoprevs = set([self.rev(n) for n in stop])
644 644 startrev = self.rev(start)
645 645 reachable = set((startrev,))
646 646 heads = set((startrev,))
647 647
648 648 parentrevs = self.parentrevs
649 649 for r in xrange(startrev + 1, len(self)):
650 650 for p in parentrevs(r):
651 651 if p in reachable:
652 652 if r not in stoprevs:
653 653 reachable.add(r)
654 654 heads.add(r)
655 655 if p in heads and p not in stoprevs:
656 656 heads.remove(p)
657 657
658 658 return [self.node(r) for r in heads]
659 659
660 660 def children(self, node):
661 661 """find the children of a given node"""
662 662 c = []
663 663 p = self.rev(node)
664 664 for r in range(p + 1, len(self)):
665 665 prevs = [pr for pr in self.parentrevs(r) if pr != nullrev]
666 666 if prevs:
667 667 for pr in prevs:
668 668 if pr == p:
669 669 c.append(self.node(r))
670 670 elif p == nullrev:
671 671 c.append(self.node(r))
672 672 return c
673 673
674 674 def descendant(self, start, end):
675 675 if start == nullrev:
676 676 return True
677 677 for i in self.descendants(start):
678 678 if i == end:
679 679 return True
680 680 elif i > end:
681 681 break
682 682 return False
683 683
684 684 def ancestor(self, a, b):
685 685 """calculate the least common ancestor of nodes a and b"""
686 686
687 687 # fast path, check if it is a descendant
688 688 a, b = self.rev(a), self.rev(b)
689 689 start, end = sorted((a, b))
690 690 if self.descendant(start, end):
691 691 return self.node(start)
692 692
693 693 def parents(rev):
694 694 return [p for p in self.parentrevs(rev) if p != nullrev]
695 695
696 696 c = ancestor.ancestor(a, b, parents)
697 697 if c is None:
698 698 return nullid
699 699
700 700 return self.node(c)
701 701
702 702 def _match(self, id):
703 703 if isinstance(id, (long, int)):
704 704 # rev
705 705 return self.node(id)
706 706 if len(id) == 20:
707 707 # possibly a binary node
708 708 # odds of a binary node being all hex in ASCII are 1 in 10**25
709 709 try:
710 710 node = id
711 711 self.rev(node) # quick search the index
712 712 return node
713 713 except LookupError:
714 714 pass # may be partial hex id
715 715 try:
716 716 # str(rev)
717 717 rev = int(id)
718 718 if str(rev) != id:
719 719 raise ValueError
720 720 if rev < 0:
721 721 rev = len(self) + rev
722 722 if rev < 0 or rev >= len(self):
723 723 raise ValueError
724 724 return self.node(rev)
725 725 except (ValueError, OverflowError):
726 726 pass
727 727 if len(id) == 40:
728 728 try:
729 729 # a full hex nodeid?
730 730 node = bin(id)
731 731 self.rev(node)
732 732 return node
733 733 except (TypeError, LookupError):
734 734 pass
735 735
736 736 def _partialmatch(self, id):
737 737 if id in self._pcache:
738 738 return self._pcache[id]
739 739
740 740 if len(id) < 40:
741 741 try:
742 742 # hex(node)[:...]
743 743 l = len(id) // 2 # grab an even number of digits
744 744 prefix = bin(id[:l * 2])
745 745 nl = [e[7] for e in self.index if e[7].startswith(prefix)]
746 746 nl = [n for n in nl if hex(n).startswith(id)]
747 747 if len(nl) > 0:
748 748 if len(nl) == 1:
749 749 self._pcache[id] = nl[0]
750 750 return nl[0]
751 751 raise LookupError(id, self.indexfile,
752 752 _('ambiguous identifier'))
753 753 return None
754 754 except TypeError:
755 755 pass
756 756
757 757 def lookup(self, id):
758 758 """locate a node based on:
759 759 - revision number or str(revision number)
760 760 - nodeid or subset of hex nodeid
761 761 """
762 762 n = self._match(id)
763 763 if n is not None:
764 764 return n
765 765 n = self._partialmatch(id)
766 766 if n:
767 767 return n
768 768
769 769 raise LookupError(id, self.indexfile, _('no match found'))
770 770
771 771 def cmp(self, node, text):
772 772 """compare text with a given file revision
773 773
774 774 returns True if text is different than what is stored.
775 775 """
776 776 p1, p2 = self.parents(node)
777 777 return hash(text, p1, p2) != node
778 778
779 779 def _addchunk(self, offset, data):
780 780 o, d = self._chunkcache
781 781 # try to add to existing cache
782 782 if o + len(d) == offset and len(d) + len(data) < _chunksize:
783 783 self._chunkcache = o, d + data
784 784 else:
785 785 self._chunkcache = offset, data
786 786
787 787 def _loadchunk(self, offset, length):
788 788 if self._inline:
789 789 df = self.opener(self.indexfile)
790 790 else:
791 791 df = self.opener(self.datafile)
792 792
793 793 readahead = max(65536, length)
794 794 df.seek(offset)
795 795 d = df.read(readahead)
796 796 self._addchunk(offset, d)
797 797 if readahead > length:
798 798 return d[:length]
799 799 return d
800 800
801 801 def _getchunk(self, offset, length):
802 802 o, d = self._chunkcache
803 803 l = len(d)
804 804
805 805 # is it in the cache?
806 806 cachestart = offset - o
807 807 cacheend = cachestart + length
808 808 if cachestart >= 0 and cacheend <= l:
809 809 if cachestart == 0 and cacheend == l:
810 810 return d # avoid a copy
811 811 return d[cachestart:cacheend]
812 812
813 813 return self._loadchunk(offset, length)
814 814
815 815 def _chunkraw(self, startrev, endrev):
816 816 start = self.start(startrev)
817 817 length = self.end(endrev) - start
818 818 if self._inline:
819 819 start += (startrev + 1) * self._io.size
820 820 return self._getchunk(start, length)
821 821
822 822 def _chunk(self, rev):
823 823 return decompress(self._chunkraw(rev, rev))
824 824
825 825 def _chunkbase(self, rev):
826 826 return self._chunk(rev)
827 827
828 828 def _chunkclear(self):
829 829 self._chunkcache = (0, '')
830 830
831 831 def deltaparent(self, rev):
832 832 """return previous revision or parentrev according to flags"""
833 833 if self.flags(rev) & REVIDX_PARENTDELTA:
834 834 return self.parentrevs(rev)[0]
835 835 else:
836 836 return rev - 1
837 837
838 838 def revdiff(self, rev1, rev2):
839 839 """return or calculate a delta between two revisions"""
840 840 if self.base(rev2) != rev2 and self.deltaparent(rev2) == rev1:
841 841 return self._chunk(rev2)
842 842
843 843 return mdiff.textdiff(self.revision(self.node(rev1)),
844 844 self.revision(self.node(rev2)))
845 845
846 846 def revision(self, node):
847 847 """return an uncompressed revision of a given node"""
848 848 cachedrev = None
849 849 if node == nullid:
850 850 return ""
851 851 if self._cache:
852 852 if self._cache[0] == node:
853 853 return self._cache[2]
854 854 cachedrev = self._cache[1]
855 855
856 856 # look up what we need to read
857 857 text = None
858 858 rev = self.rev(node)
859 859 base = self.base(rev)
860 860
861 861 # check rev flags
862 862 if self.flags(rev) & ~REVIDX_KNOWN_FLAGS:
863 863 raise RevlogError(_('incompatible revision flag %x') %
864 864 (self.flags(rev) & ~REVIDX_KNOWN_FLAGS))
865 865
866 866 # build delta chain
867 867 chain = []
868 868 index = self.index # for performance
869 869 iterrev = rev
870 870 e = index[iterrev]
871 871 while iterrev != base and iterrev != cachedrev:
872 872 chain.append(iterrev)
873 873 if e[0] & REVIDX_PARENTDELTA:
874 874 iterrev = e[5]
875 875 else:
876 876 iterrev -= 1
877 877 e = index[iterrev]
878 878 chain.reverse()
879 879 base = iterrev
880 880
881 881 if iterrev == cachedrev:
882 882 # cache hit
883 883 text = self._cache[2]
884 884
885 885 # drop cache to save memory
886 886 self._cache = None
887 887
888 888 self._chunkraw(base, rev)
889 889 if text is None:
890 890 text = self._chunkbase(base)
891 891
892 892 bins = [self._chunk(r) for r in chain]
893 893 text = mdiff.patches(text, bins)
894 894
895 895 text = self._checkhash(text, node, rev)
896 896
897 897 self._cache = (node, rev, text)
898 898 return text
899 899
900 900 def _checkhash(self, text, node, rev):
901 901 p1, p2 = self.parents(node)
902 902 if (node != hash(text, p1, p2) and
903 903 not (self.flags(rev) & REVIDX_PUNCHED_FLAG)):
904 904 raise RevlogError(_("integrity check failed on %s:%d")
905 905 % (self.indexfile, rev))
906 906 return text
907 907
908 908 def checkinlinesize(self, tr, fp=None):
909 909 if not self._inline or (self.start(-2) + self.length(-2)) < _maxinline:
910 910 return
911 911
912 912 trinfo = tr.find(self.indexfile)
913 913 if trinfo is None:
914 914 raise RevlogError(_("%s not found in the transaction")
915 915 % self.indexfile)
916 916
917 917 trindex = trinfo[2]
918 918 dataoff = self.start(trindex)
919 919
920 920 tr.add(self.datafile, dataoff)
921 921
922 922 if fp:
923 923 fp.flush()
924 924 fp.close()
925 925
926 926 df = self.opener(self.datafile, 'w')
927 927 try:
928 928 for r in self:
929 929 df.write(self._chunkraw(r, r))
930 930 finally:
931 931 df.close()
932 932
933 933 fp = self.opener(self.indexfile, 'w', atomictemp=True)
934 934 self.version &= ~(REVLOGNGINLINEDATA)
935 935 self._inline = False
936 936 for i in self:
937 937 e = self._io.packentry(self.index[i], self.node, self.version, i)
938 938 fp.write(e)
939 939
940 940 # if we don't call rename, the temp file will never replace the
941 941 # real index
942 942 fp.rename()
943 943
944 944 tr.replace(self.indexfile, trindex * self._io.size)
945 945 self._chunkclear()
946 946
947 947 def addrevision(self, text, transaction, link, p1, p2, cachedelta=None):
948 948 """add a revision to the log
949 949
950 950 text - the revision data to add
951 951 transaction - the transaction object used for rollback
952 952 link - the linkrev data to add
953 953 p1, p2 - the parent nodeids of the revision
954 954 cachedelta - an optional precomputed delta
955 955 """
956 956 node = hash(text, p1, p2)
957 957 if (node in self.nodemap and
958 958 (not self.flags(self.rev(node)) & REVIDX_PUNCHED_FLAG)):
959 959 return node
960 960
961 961 dfh = None
962 962 if not self._inline:
963 963 dfh = self.opener(self.datafile, "a")
964 964 ifh = self.opener(self.indexfile, "a+")
965 965 try:
966 966 return self._addrevision(node, text, transaction, link, p1, p2,
967 967 cachedelta, ifh, dfh)
968 968 finally:
969 969 if dfh:
970 970 dfh.close()
971 971 ifh.close()
972 972
973 973 def _addrevision(self, node, text, transaction, link, p1, p2,
974 974 cachedelta, ifh, dfh):
975 975
976 976 btext = [text]
977 977 def buildtext():
978 978 if btext[0] is not None:
979 979 return btext[0]
980 980 # flush any pending writes here so we can read it in revision
981 981 if dfh:
982 982 dfh.flush()
983 983 ifh.flush()
984 984 basetext = self.revision(self.node(cachedelta[0]))
985 985 btext[0] = mdiff.patch(basetext, cachedelta[1])
986 986 chk = hash(btext[0], p1, p2)
987 987 if chk != node:
988 988 raise RevlogError(_("consistency error in delta"))
989 989 return btext[0]
990 990
991 991 def builddelta(rev):
992 992 # can we use the cached delta?
993 993 if cachedelta and cachedelta[0] == rev:
994 994 delta = cachedelta[1]
995 995 else:
996 996 t = buildtext()
997 997 ptext = self.revision(self.node(rev))
998 998 delta = mdiff.textdiff(ptext, t)
999 999 data = compress(delta)
1000 1000 l = len(data[1]) + len(data[0])
1001 1001 base = self.base(rev)
1002 1002 dist = l + offset - self.start(base)
1003 1003 return dist, l, data, base
1004 1004
1005 1005 curr = len(self)
1006 1006 prev = curr - 1
1007 1007 base = curr
1008 1008 offset = self.end(prev)
1009 1009 flags = 0
1010 1010 d = None
1011 1011 p1r, p2r = self.rev(p1), self.rev(p2)
1012 1012
1013 1013 # should we try to build a delta?
1014 1014 if prev != nullrev:
1015 1015 d = builddelta(prev)
1016 1016 if self._parentdelta and prev != p1r:
1017 1017 d2 = builddelta(p1r)
1018 1018 if d2 < d:
1019 1019 d = d2
1020 1020 flags = REVIDX_PARENTDELTA
1021 1021 dist, l, data, base = d
1022 1022
1023 1023 # full versions are inserted when the needed deltas
1024 1024 # become comparable to the uncompressed text
1025 1025 # or the base revision is punched
1026 1026 if text is None:
1027 1027 textlen = mdiff.patchedsize(self.rawsize(cachedelta[0]),
1028 1028 cachedelta[1])
1029 1029 else:
1030 1030 textlen = len(text)
1031 1031 if (d is None or dist > textlen * 2 or
1032 1032 (self.flags(base) & REVIDX_PUNCHED_FLAG)):
1033 1033 text = buildtext()
1034 1034 data = compress(text)
1035 1035 l = len(data[1]) + len(data[0])
1036 1036 base = curr
1037 1037
1038 1038 e = (offset_type(offset, flags), l, textlen,
1039 1039 base, link, p1r, p2r, node)
1040 1040 self.index.insert(-1, e)
1041 1041 self.nodemap[node] = curr
1042 1042
1043 1043 entry = self._io.packentry(e, self.node, self.version, curr)
1044 1044 if not self._inline:
1045 1045 transaction.add(self.datafile, offset)
1046 1046 transaction.add(self.indexfile, curr * len(entry))
1047 1047 if data[0]:
1048 1048 dfh.write(data[0])
1049 1049 dfh.write(data[1])
1050 1050 dfh.flush()
1051 1051 ifh.write(entry)
1052 1052 else:
1053 1053 offset += curr * self._io.size
1054 1054 transaction.add(self.indexfile, offset, curr)
1055 1055 ifh.write(entry)
1056 1056 ifh.write(data[0])
1057 1057 ifh.write(data[1])
1058 1058 self.checkinlinesize(transaction, ifh)
1059 1059
1060 1060 if type(text) == str: # only accept immutable objects
1061 1061 self._cache = (node, curr, text)
1062 1062 return node
1063 1063
1064 1064 def group(self, nodelist, bundler):
1065 1065 """Calculate a delta group, yielding a sequence of changegroup chunks
1066 1066 (strings).
1067 1067
1068 1068 Given a list of changeset revs, return a set of deltas and
1069 1069 metadata corresponding to nodes. The first delta is
1070 1070 first parent(nodelist[0]) -> nodelist[0], the receiver is
1071 1071 guaranteed to have this parent as it has all history before
1072 1072 these changesets. In the case firstparent is nullrev the
1073 1073 changegroup starts with a full revision.
1074 1074 """
1075 1075
1076 1076 revs = sorted([self.rev(n) for n in nodelist])
1077 1077
1078 1078 # if we don't have any revisions touched by these changesets, bail
1079 1079 if not revs:
1080 1080 yield bundler.close()
1081 1081 return
1082 1082
1083 1083 # add the parent of the first rev
1084 1084 p = self.parentrevs(revs[0])[0]
1085 1085 revs.insert(0, p)
1086 1086
1087 1087 # build deltas
1088 1088 for r in xrange(len(revs) - 1):
1089 1089 a, b = revs[r], revs[r + 1]
1090 1090 nb = self.node(b)
1091 1091 p1, p2 = self.parents(nb)
1092 1092 prefix = ''
1093 1093
1094 1094 if a == nullrev:
1095 1095 d = self.revision(nb)
1096 1096 prefix = mdiff.trivialdiffheader(len(d))
1097 1097 else:
1098 1098 d = self.revdiff(a, b)
1099 1099 for c in bundler.revchunk(self, nb, p1, p2, prefix, d):
1100 1100 yield c
1101 1101
1102 1102 yield bundler.close()
1103 1103
1104 1104 def addgroup(self, bundle, linkmapper, transaction):
1105 1105 """
1106 1106 add a delta group
1107 1107
1108 1108 given a set of deltas, add them to the revision log. the
1109 1109 first delta is against its parent, which should be in our
1110 1110 log, the rest are against the previous delta.
1111 1111 """
1112 1112
1113 1113 # track the base of the current delta log
1114 1114 node = None
1115 1115
1116 1116 r = len(self)
1117 1117 end = 0
1118 1118 if r:
1119 1119 end = self.end(r - 1)
1120 1120 ifh = self.opener(self.indexfile, "a+")
1121 1121 isize = r * self._io.size
1122 1122 if self._inline:
1123 1123 transaction.add(self.indexfile, end + isize, r)
1124 1124 dfh = None
1125 1125 else:
1126 1126 transaction.add(self.indexfile, isize, r)
1127 1127 transaction.add(self.datafile, end)
1128 1128 dfh = self.opener(self.datafile, "a")
1129 1129
1130 1130 try:
1131 1131 # loop through our set of deltas
1132 1132 chain = None
1133 1133 while 1:
1134 chunkdata = bundle.parsechunk()
1134 chunkdata = bundle.parsechunk(chain)
1135 1135 if not chunkdata:
1136 1136 break
1137 1137 node = chunkdata['node']
1138 1138 p1 = chunkdata['p1']
1139 1139 p2 = chunkdata['p2']
1140 1140 cs = chunkdata['cs']
1141 delta = chunkdata['data']
1141 deltabase = chunkdata['deltabase']
1142 delta = chunkdata['delta']
1142 1143
1143 1144 link = linkmapper(cs)
1144 1145 if (node in self.nodemap and
1145 1146 (not self.flags(self.rev(node)) & REVIDX_PUNCHED_FLAG)):
1146 1147 # this can happen if two branches make the same change
1147 1148 chain = node
1148 1149 continue
1149 1150
1150 1151 for p in (p1, p2):
1151 1152 if not p in self.nodemap:
1152 1153 if self._shallow:
1153 1154 # add null entries for missing parents
1154 1155 # XXX FIXME
1155 1156 #if base == nullrev:
1156 1157 # base = len(self)
1157 1158 #e = (offset_type(end, REVIDX_PUNCHED_FLAG),
1158 1159 # 0, 0, base, nullrev, nullrev, nullrev, p)
1159 1160 #self.index.insert(-1, e)
1160 1161 #self.nodemap[p] = r
1161 1162 #entry = self._io.packentry(e, self.node,
1162 1163 # self.version, r)
1163 1164 #ifh.write(entry)
1164 1165 #t, r = r, r + 1
1165 1166 raise LookupError(p, self.indexfile,
1166 1167 _('unknown parent'))
1167 1168 else:
1168 1169 raise LookupError(p, self.indexfile,
1169 1170 _('unknown parent'))
1170 1171
1171 if not chain:
1172 # retrieve the parent revision of the delta chain
1173 chain = p1
1174 if not chain in self.nodemap:
1175 raise LookupError(chain, self.indexfile, _('unknown base'))
1172 if deltabase not in self.nodemap:
1173 raise LookupError(deltabase, self.indexfile,
1174 _('unknown delta base'))
1176 1175
1177 chainrev = self.rev(chain)
1176 baserev = self.rev(deltabase)
1178 1177 chain = self._addrevision(node, None, transaction, link,
1179 p1, p2, (chainrev, delta), ifh, dfh)
1178 p1, p2, (baserev, delta), ifh, dfh)
1180 1179 if not dfh and not self._inline:
1181 1180 # addrevision switched from inline to conventional
1182 1181 # reopen the index
1183 1182 ifh.close()
1184 1183 dfh = self.opener(self.datafile, "a")
1185 1184 ifh = self.opener(self.indexfile, "a")
1186 1185 finally:
1187 1186 if dfh:
1188 1187 dfh.close()
1189 1188 ifh.close()
1190 1189
1191 1190 return node
1192 1191
1193 1192 def strip(self, minlink, transaction):
1194 1193 """truncate the revlog on the first revision with a linkrev >= minlink
1195 1194
1196 1195 This function is called when we're stripping revision minlink and
1197 1196 its descendants from the repository.
1198 1197
1199 1198 We have to remove all revisions with linkrev >= minlink, because
1200 1199 the equivalent changelog revisions will be renumbered after the
1201 1200 strip.
1202 1201
1203 1202 So we truncate the revlog on the first of these revisions, and
1204 1203 trust that the caller has saved the revisions that shouldn't be
1205 1204 removed and that it'll readd them after this truncation.
1206 1205 """
1207 1206 if len(self) == 0:
1208 1207 return
1209 1208
1210 1209 for rev in self:
1211 1210 if self.index[rev][4] >= minlink:
1212 1211 break
1213 1212 else:
1214 1213 return
1215 1214
1216 1215 # first truncate the files on disk
1217 1216 end = self.start(rev)
1218 1217 if not self._inline:
1219 1218 transaction.add(self.datafile, end)
1220 1219 end = rev * self._io.size
1221 1220 else:
1222 1221 end += rev * self._io.size
1223 1222
1224 1223 transaction.add(self.indexfile, end)
1225 1224
1226 1225 # then reset internal state in memory to forget those revisions
1227 1226 self._cache = None
1228 1227 self._chunkclear()
1229 1228 for x in xrange(rev, len(self)):
1230 1229 del self.nodemap[self.node(x)]
1231 1230
1232 1231 del self.index[rev:-1]
1233 1232
1234 1233 def checksize(self):
1235 1234 expected = 0
1236 1235 if len(self):
1237 1236 expected = max(0, self.end(len(self) - 1))
1238 1237
1239 1238 try:
1240 1239 f = self.opener(self.datafile)
1241 1240 f.seek(0, 2)
1242 1241 actual = f.tell()
1243 1242 f.close()
1244 1243 dd = actual - expected
1245 1244 except IOError, inst:
1246 1245 if inst.errno != errno.ENOENT:
1247 1246 raise
1248 1247 dd = 0
1249 1248
1250 1249 try:
1251 1250 f = self.opener(self.indexfile)
1252 1251 f.seek(0, 2)
1253 1252 actual = f.tell()
1254 1253 f.close()
1255 1254 s = self._io.size
1256 1255 i = max(0, actual // s)
1257 1256 di = actual - (i * s)
1258 1257 if self._inline:
1259 1258 databytes = 0
1260 1259 for r in self:
1261 1260 databytes += max(0, self.length(r))
1262 1261 dd = 0
1263 1262 di = actual - len(self) * s - databytes
1264 1263 except IOError, inst:
1265 1264 if inst.errno != errno.ENOENT:
1266 1265 raise
1267 1266 di = 0
1268 1267
1269 1268 return (dd, di)
1270 1269
1271 1270 def files(self):
1272 1271 res = [self.indexfile]
1273 1272 if not self._inline:
1274 1273 res.append(self.datafile)
1275 1274 return res
@@ -1,36 +1,36 b''
1 1
2 2 Create a test repository:
3 3
4 4 $ hg init repo
5 5 $ cd repo
6 6 $ touch a ; hg add a ; hg ci -ma
7 7 $ touch b ; hg add b ; hg ci -mb
8 8 $ touch c ; hg add c ; hg ci -mc
9 9 $ hg bundle --base 0 --rev tip bundle.hg
10 10 2 changesets found
11 11
12 12 Terse output:
13 13
14 14 $ hg debugbundle bundle.hg
15 15 0e067c57feba1a5694ca4844f05588bb1bf82342
16 16 991a3460af53952d10ec8a295d3d2cc2e5fa9690
17 17
18 18 Verbose output:
19 19
20 20 $ hg debugbundle --all bundle.hg
21 format: id, p1, p2, cset, len(delta)
21 format: id, p1, p2, cset, delta base, len(delta)
22 22
23 23 changelog
24 0e067c57feba1a5694ca4844f05588bb1bf82342 3903775176ed42b1458a6281db4a0ccf4d9f287a 0000000000000000000000000000000000000000 0e067c57feba1a5694ca4844f05588bb1bf82342 80
25 991a3460af53952d10ec8a295d3d2cc2e5fa9690 0e067c57feba1a5694ca4844f05588bb1bf82342 0000000000000000000000000000000000000000 991a3460af53952d10ec8a295d3d2cc2e5fa9690 80
24 0e067c57feba1a5694ca4844f05588bb1bf82342 3903775176ed42b1458a6281db4a0ccf4d9f287a 0000000000000000000000000000000000000000 0e067c57feba1a5694ca4844f05588bb1bf82342 3903775176ed42b1458a6281db4a0ccf4d9f287a 80
25 991a3460af53952d10ec8a295d3d2cc2e5fa9690 0e067c57feba1a5694ca4844f05588bb1bf82342 0000000000000000000000000000000000000000 991a3460af53952d10ec8a295d3d2cc2e5fa9690 0e067c57feba1a5694ca4844f05588bb1bf82342 80
26 26
27 27 manifest
28 686dbf0aeca417636fa26a9121c681eabbb15a20 8515d4bfda768e04af4c13a69a72e28c7effbea7 0000000000000000000000000000000000000000 0e067c57feba1a5694ca4844f05588bb1bf82342 55
29 ae25a31b30b3490a981e7b96a3238cc69583fda1 686dbf0aeca417636fa26a9121c681eabbb15a20 0000000000000000000000000000000000000000 991a3460af53952d10ec8a295d3d2cc2e5fa9690 55
28 686dbf0aeca417636fa26a9121c681eabbb15a20 8515d4bfda768e04af4c13a69a72e28c7effbea7 0000000000000000000000000000000000000000 0e067c57feba1a5694ca4844f05588bb1bf82342 8515d4bfda768e04af4c13a69a72e28c7effbea7 55
29 ae25a31b30b3490a981e7b96a3238cc69583fda1 686dbf0aeca417636fa26a9121c681eabbb15a20 0000000000000000000000000000000000000000 991a3460af53952d10ec8a295d3d2cc2e5fa9690 686dbf0aeca417636fa26a9121c681eabbb15a20 55
30 30
31 31 b
32 b80de5d138758541c5f05265ad144ab9fa86d1db 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 0e067c57feba1a5694ca4844f05588bb1bf82342 12
32 b80de5d138758541c5f05265ad144ab9fa86d1db 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 0e067c57feba1a5694ca4844f05588bb1bf82342 0000000000000000000000000000000000000000 12
33 33
34 34 c
35 b80de5d138758541c5f05265ad144ab9fa86d1db 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 991a3460af53952d10ec8a295d3d2cc2e5fa9690 12
35 b80de5d138758541c5f05265ad144ab9fa86d1db 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 991a3460af53952d10ec8a295d3d2cc2e5fa9690 0000000000000000000000000000000000000000 12
36 36
@@ -1,253 +1,253 b''
1 1
2 2 = Test the getbundle() protocol function =
3 3
4 4 Enable graphlog extension:
5 5
6 6 $ echo "[extensions]" >> $HGRCPATH
7 7 $ echo "graphlog=" >> $HGRCPATH
8 8
9 9 Create a test repository:
10 10
11 11 $ hg init repo
12 12 $ cd repo
13 13 $ hg debugbuilddag -n -m '+2 :fork +5 :p1 *fork +6 :p2 /p1 :m1 +3' > /dev/null
14 14 $ hg glog --template '{node}\n'
15 15 @ 2bba2f40f321484159b395a43f20101d4bb7ead0
16 16 |
17 17 o d9e5488323c782fe684573f3043369d199038b6f
18 18 |
19 19 o 6e9a5adf5437e49c746288cf95c5ac34fa8f2f72
20 20 |
21 21 o 733bf0910832b26b768a09172f325f995b5476e1
22 22 |\
23 23 | o b5af5d6ea56d73ce24c40bc3cd19a862f74888ac
24 24 | |
25 25 | o 6b57ee934bb2996050540f84cdfc8dcad1e7267d
26 26 | |
27 27 | o 2c0ec49482e8abe888b7bd090b5827acfc22b3d7
28 28 | |
29 29 | o c1818a9f5977dd4139a48f93f5425c67d44a9368
30 30 | |
31 31 | o 6c725a58ad10aea441540bfd06c507f63e8b9cdd
32 32 | |
33 33 | o 18063366a155bd56b5618229ae2ac3e91849aa5e
34 34 | |
35 35 | o a21d913c992197a2eb60b298521ec0f045a04799
36 36 | |
37 37 o | b6b2b682253df2ffedc10e9415e4114202b303c5
38 38 | |
39 39 o | 2114148793524fd045998f71a45b0aaf139f752b
40 40 | |
41 41 o | 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc
42 42 | |
43 43 o | ea919464b16e003894c48b6cb68df3cd9411b544
44 44 | |
45 45 o | 0f82d97ec2778746743fbc996740d409558fda22
46 46 |/
47 47 o 6e23b016bc0f0e79c7bd9dd372ccee07055d7fd4
48 48 |
49 49 o 10e64d654571f11577745b4d8372e859d9e4df63
50 50
51 51 $ cd ..
52 52
53 53
54 54 = Test locally =
55 55
56 56 Get everything:
57 57
58 58 $ hg debuggetbundle repo bundle
59 59 $ hg debugbundle bundle
60 60 10e64d654571f11577745b4d8372e859d9e4df63
61 61 6e23b016bc0f0e79c7bd9dd372ccee07055d7fd4
62 62 0f82d97ec2778746743fbc996740d409558fda22
63 63 ea919464b16e003894c48b6cb68df3cd9411b544
64 64 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc
65 65 2114148793524fd045998f71a45b0aaf139f752b
66 66 b6b2b682253df2ffedc10e9415e4114202b303c5
67 67 a21d913c992197a2eb60b298521ec0f045a04799
68 68 18063366a155bd56b5618229ae2ac3e91849aa5e
69 69 6c725a58ad10aea441540bfd06c507f63e8b9cdd
70 70 c1818a9f5977dd4139a48f93f5425c67d44a9368
71 71 2c0ec49482e8abe888b7bd090b5827acfc22b3d7
72 72 6b57ee934bb2996050540f84cdfc8dcad1e7267d
73 73 b5af5d6ea56d73ce24c40bc3cd19a862f74888ac
74 74 733bf0910832b26b768a09172f325f995b5476e1
75 75 6e9a5adf5437e49c746288cf95c5ac34fa8f2f72
76 76 d9e5488323c782fe684573f3043369d199038b6f
77 77 2bba2f40f321484159b395a43f20101d4bb7ead0
78 78
79 79 Get part of linear run:
80 80
81 81 $ hg debuggetbundle repo bundle -H d9e5488323c782fe684573f3043369d199038b6f -C 733bf0910832b26b768a09172f325f995b5476e1
82 82 $ hg debugbundle bundle
83 83 6e9a5adf5437e49c746288cf95c5ac34fa8f2f72
84 84 d9e5488323c782fe684573f3043369d199038b6f
85 85
86 86 Get missing branch and merge:
87 87
88 88 $ hg debuggetbundle repo bundle -H d9e5488323c782fe684573f3043369d199038b6f -C 6b57ee934bb2996050540f84cdfc8dcad1e7267d
89 89 $ hg debugbundle bundle
90 90 0f82d97ec2778746743fbc996740d409558fda22
91 91 ea919464b16e003894c48b6cb68df3cd9411b544
92 92 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc
93 93 2114148793524fd045998f71a45b0aaf139f752b
94 94 b6b2b682253df2ffedc10e9415e4114202b303c5
95 95 b5af5d6ea56d73ce24c40bc3cd19a862f74888ac
96 96 733bf0910832b26b768a09172f325f995b5476e1
97 97 6e9a5adf5437e49c746288cf95c5ac34fa8f2f72
98 98 d9e5488323c782fe684573f3043369d199038b6f
99 99
100 100 Get from only one head:
101 101
102 102 $ hg debuggetbundle repo bundle -H 6c725a58ad10aea441540bfd06c507f63e8b9cdd -C 6e23b016bc0f0e79c7bd9dd372ccee07055d7fd4
103 103 $ hg debugbundle bundle
104 104 a21d913c992197a2eb60b298521ec0f045a04799
105 105 18063366a155bd56b5618229ae2ac3e91849aa5e
106 106 6c725a58ad10aea441540bfd06c507f63e8b9cdd
107 107
108 108 Get parts of two branches:
109 109
110 110 $ hg debuggetbundle repo bundle -H 6b57ee934bb2996050540f84cdfc8dcad1e7267d -C c1818a9f5977dd4139a48f93f5425c67d44a9368 -H 2114148793524fd045998f71a45b0aaf139f752b -C ea919464b16e003894c48b6cb68df3cd9411b544
111 111 $ hg debugbundle bundle
112 112 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc
113 113 2114148793524fd045998f71a45b0aaf139f752b
114 114 2c0ec49482e8abe888b7bd090b5827acfc22b3d7
115 115 6b57ee934bb2996050540f84cdfc8dcad1e7267d
116 116
117 117 Check that we get all needed file changes:
118 118
119 119 $ hg debugbundle bundle --all
120 format: id, p1, p2, cset, len(delta)
120 format: id, p1, p2, cset, delta base, len(delta)
121 121
122 122 changelog
123 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc ea919464b16e003894c48b6cb68df3cd9411b544 0000000000000000000000000000000000000000 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc 99
124 2114148793524fd045998f71a45b0aaf139f752b 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc 0000000000000000000000000000000000000000 2114148793524fd045998f71a45b0aaf139f752b 99
125 2c0ec49482e8abe888b7bd090b5827acfc22b3d7 c1818a9f5977dd4139a48f93f5425c67d44a9368 0000000000000000000000000000000000000000 2c0ec49482e8abe888b7bd090b5827acfc22b3d7 102
126 6b57ee934bb2996050540f84cdfc8dcad1e7267d 2c0ec49482e8abe888b7bd090b5827acfc22b3d7 0000000000000000000000000000000000000000 6b57ee934bb2996050540f84cdfc8dcad1e7267d 102
123 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc ea919464b16e003894c48b6cb68df3cd9411b544 0000000000000000000000000000000000000000 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc ea919464b16e003894c48b6cb68df3cd9411b544 99
124 2114148793524fd045998f71a45b0aaf139f752b 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc 0000000000000000000000000000000000000000 2114148793524fd045998f71a45b0aaf139f752b 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc 99
125 2c0ec49482e8abe888b7bd090b5827acfc22b3d7 c1818a9f5977dd4139a48f93f5425c67d44a9368 0000000000000000000000000000000000000000 2c0ec49482e8abe888b7bd090b5827acfc22b3d7 2114148793524fd045998f71a45b0aaf139f752b 102
126 6b57ee934bb2996050540f84cdfc8dcad1e7267d 2c0ec49482e8abe888b7bd090b5827acfc22b3d7 0000000000000000000000000000000000000000 6b57ee934bb2996050540f84cdfc8dcad1e7267d 2c0ec49482e8abe888b7bd090b5827acfc22b3d7 102
127 127
128 128 manifest
129 dac7984588fc4eea7acbf39693a9c1b06f5b175d 591f732a3faf1fb903815273f3c199a514a61ccb 0000000000000000000000000000000000000000 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc 113
130 0772616e6b48a76afb6c1458e193cbb3dae2e4ff dac7984588fc4eea7acbf39693a9c1b06f5b175d 0000000000000000000000000000000000000000 2114148793524fd045998f71a45b0aaf139f752b 113
131 eb498cd9af6c44108e43041e951ce829e29f6c80 bff2f4817ced57b386caf7c4e3e36a4bc9af7e93 0000000000000000000000000000000000000000 2c0ec49482e8abe888b7bd090b5827acfc22b3d7 295
132 b15709c071ddd2d93188508ba156196ab4f19620 eb498cd9af6c44108e43041e951ce829e29f6c80 0000000000000000000000000000000000000000 6b57ee934bb2996050540f84cdfc8dcad1e7267d 114
129 dac7984588fc4eea7acbf39693a9c1b06f5b175d 591f732a3faf1fb903815273f3c199a514a61ccb 0000000000000000000000000000000000000000 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc 591f732a3faf1fb903815273f3c199a514a61ccb 113
130 0772616e6b48a76afb6c1458e193cbb3dae2e4ff dac7984588fc4eea7acbf39693a9c1b06f5b175d 0000000000000000000000000000000000000000 2114148793524fd045998f71a45b0aaf139f752b dac7984588fc4eea7acbf39693a9c1b06f5b175d 113
131 eb498cd9af6c44108e43041e951ce829e29f6c80 bff2f4817ced57b386caf7c4e3e36a4bc9af7e93 0000000000000000000000000000000000000000 2c0ec49482e8abe888b7bd090b5827acfc22b3d7 0772616e6b48a76afb6c1458e193cbb3dae2e4ff 295
132 b15709c071ddd2d93188508ba156196ab4f19620 eb498cd9af6c44108e43041e951ce829e29f6c80 0000000000000000000000000000000000000000 6b57ee934bb2996050540f84cdfc8dcad1e7267d eb498cd9af6c44108e43041e951ce829e29f6c80 114
133 133
134 134 mf
135 4f73f97080266ab8e0c0561ca8d0da3eaf65b695 301ca08d026bb72cb4258a9d211bdf7ca0bcd810 0000000000000000000000000000000000000000 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc 17
136 c7b583de053293870e145f45bd2d61643563fd06 4f73f97080266ab8e0c0561ca8d0da3eaf65b695 0000000000000000000000000000000000000000 2114148793524fd045998f71a45b0aaf139f752b 18
137 266ee3c0302a5a18f1cf96817ac79a51836179e9 edc0f6b8db80d68ae6aff2b19f7e5347ab68fa63 0000000000000000000000000000000000000000 2c0ec49482e8abe888b7bd090b5827acfc22b3d7 149
138 698c6a36220548cd3903ca7dada27c59aa500c52 266ee3c0302a5a18f1cf96817ac79a51836179e9 0000000000000000000000000000000000000000 6b57ee934bb2996050540f84cdfc8dcad1e7267d 19
135 4f73f97080266ab8e0c0561ca8d0da3eaf65b695 301ca08d026bb72cb4258a9d211bdf7ca0bcd810 0000000000000000000000000000000000000000 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc 301ca08d026bb72cb4258a9d211bdf7ca0bcd810 17
136 c7b583de053293870e145f45bd2d61643563fd06 4f73f97080266ab8e0c0561ca8d0da3eaf65b695 0000000000000000000000000000000000000000 2114148793524fd045998f71a45b0aaf139f752b 4f73f97080266ab8e0c0561ca8d0da3eaf65b695 18
137 266ee3c0302a5a18f1cf96817ac79a51836179e9 edc0f6b8db80d68ae6aff2b19f7e5347ab68fa63 0000000000000000000000000000000000000000 2c0ec49482e8abe888b7bd090b5827acfc22b3d7 c7b583de053293870e145f45bd2d61643563fd06 149
138 698c6a36220548cd3903ca7dada27c59aa500c52 266ee3c0302a5a18f1cf96817ac79a51836179e9 0000000000000000000000000000000000000000 6b57ee934bb2996050540f84cdfc8dcad1e7267d 266ee3c0302a5a18f1cf96817ac79a51836179e9 19
139 139
140 140 nf11
141 33fbc651630ffa7ccbebfe4eb91320a873e7291c 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 2c0ec49482e8abe888b7bd090b5827acfc22b3d7 16
141 33fbc651630ffa7ccbebfe4eb91320a873e7291c 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 2c0ec49482e8abe888b7bd090b5827acfc22b3d7 0000000000000000000000000000000000000000 16
142 142
143 143 nf12
144 ddce0544363f037e9fb889faca058f52dc01c0a5 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 6b57ee934bb2996050540f84cdfc8dcad1e7267d 16
144 ddce0544363f037e9fb889faca058f52dc01c0a5 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 6b57ee934bb2996050540f84cdfc8dcad1e7267d 0000000000000000000000000000000000000000 16
145 145
146 146 nf4
147 3c1407305701051cbed9f9cb9a68bdfb5997c235 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc 15
147 3c1407305701051cbed9f9cb9a68bdfb5997c235 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc 0000000000000000000000000000000000000000 15
148 148
149 149 nf5
150 0dbd89c185f53a1727c54cd1ce256482fa23968e 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 2114148793524fd045998f71a45b0aaf139f752b 15
150 0dbd89c185f53a1727c54cd1ce256482fa23968e 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 2114148793524fd045998f71a45b0aaf139f752b 0000000000000000000000000000000000000000 15
151 151
152 152 Get branch and merge:
153 153
154 154 $ hg debuggetbundle repo bundle -C 10e64d654571f11577745b4d8372e859d9e4df63 -H 6e9a5adf5437e49c746288cf95c5ac34fa8f2f72
155 155 $ hg debugbundle bundle
156 156 6e23b016bc0f0e79c7bd9dd372ccee07055d7fd4
157 157 0f82d97ec2778746743fbc996740d409558fda22
158 158 ea919464b16e003894c48b6cb68df3cd9411b544
159 159 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc
160 160 2114148793524fd045998f71a45b0aaf139f752b
161 161 b6b2b682253df2ffedc10e9415e4114202b303c5
162 162 a21d913c992197a2eb60b298521ec0f045a04799
163 163 18063366a155bd56b5618229ae2ac3e91849aa5e
164 164 6c725a58ad10aea441540bfd06c507f63e8b9cdd
165 165 c1818a9f5977dd4139a48f93f5425c67d44a9368
166 166 2c0ec49482e8abe888b7bd090b5827acfc22b3d7
167 167 6b57ee934bb2996050540f84cdfc8dcad1e7267d
168 168 b5af5d6ea56d73ce24c40bc3cd19a862f74888ac
169 169 733bf0910832b26b768a09172f325f995b5476e1
170 170 6e9a5adf5437e49c746288cf95c5ac34fa8f2f72
171 171
172 172
173 173 = Test via HTTP =
174 174
175 175 Get everything:
176 176
177 177 $ hg serve -R repo -p $HGPORT -d --pid-file=hg.pid -E error.log -A access.log
178 178 $ cat hg.pid >> $DAEMON_PIDS
179 179 $ hg debuggetbundle http://localhost:$HGPORT/ bundle
180 180 $ hg debugbundle bundle
181 181 10e64d654571f11577745b4d8372e859d9e4df63
182 182 6e23b016bc0f0e79c7bd9dd372ccee07055d7fd4
183 183 0f82d97ec2778746743fbc996740d409558fda22
184 184 ea919464b16e003894c48b6cb68df3cd9411b544
185 185 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc
186 186 2114148793524fd045998f71a45b0aaf139f752b
187 187 b6b2b682253df2ffedc10e9415e4114202b303c5
188 188 a21d913c992197a2eb60b298521ec0f045a04799
189 189 18063366a155bd56b5618229ae2ac3e91849aa5e
190 190 6c725a58ad10aea441540bfd06c507f63e8b9cdd
191 191 c1818a9f5977dd4139a48f93f5425c67d44a9368
192 192 2c0ec49482e8abe888b7bd090b5827acfc22b3d7
193 193 6b57ee934bb2996050540f84cdfc8dcad1e7267d
194 194 b5af5d6ea56d73ce24c40bc3cd19a862f74888ac
195 195 733bf0910832b26b768a09172f325f995b5476e1
196 196 6e9a5adf5437e49c746288cf95c5ac34fa8f2f72
197 197 d9e5488323c782fe684573f3043369d199038b6f
198 198 2bba2f40f321484159b395a43f20101d4bb7ead0
199 199
200 200 Get parts of two branches:
201 201
202 202 $ hg debuggetbundle http://localhost:$HGPORT/ bundle -H 6b57ee934bb2996050540f84cdfc8dcad1e7267d -C c1818a9f5977dd4139a48f93f5425c67d44a9368 -H 2114148793524fd045998f71a45b0aaf139f752b -C ea919464b16e003894c48b6cb68df3cd9411b544
203 203 $ hg debugbundle bundle
204 204 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc
205 205 2114148793524fd045998f71a45b0aaf139f752b
206 206 2c0ec49482e8abe888b7bd090b5827acfc22b3d7
207 207 6b57ee934bb2996050540f84cdfc8dcad1e7267d
208 208
209 209 Check that we get all needed file changes:
210 210
211 211 $ hg debugbundle bundle --all
212 format: id, p1, p2, cset, len(delta)
212 format: id, p1, p2, cset, delta base, len(delta)
213 213
214 214 changelog
215 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc ea919464b16e003894c48b6cb68df3cd9411b544 0000000000000000000000000000000000000000 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc 99
216 2114148793524fd045998f71a45b0aaf139f752b 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc 0000000000000000000000000000000000000000 2114148793524fd045998f71a45b0aaf139f752b 99
217 2c0ec49482e8abe888b7bd090b5827acfc22b3d7 c1818a9f5977dd4139a48f93f5425c67d44a9368 0000000000000000000000000000000000000000 2c0ec49482e8abe888b7bd090b5827acfc22b3d7 102
218 6b57ee934bb2996050540f84cdfc8dcad1e7267d 2c0ec49482e8abe888b7bd090b5827acfc22b3d7 0000000000000000000000000000000000000000 6b57ee934bb2996050540f84cdfc8dcad1e7267d 102
215 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc ea919464b16e003894c48b6cb68df3cd9411b544 0000000000000000000000000000000000000000 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc ea919464b16e003894c48b6cb68df3cd9411b544 99
216 2114148793524fd045998f71a45b0aaf139f752b 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc 0000000000000000000000000000000000000000 2114148793524fd045998f71a45b0aaf139f752b 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc 99
217 2c0ec49482e8abe888b7bd090b5827acfc22b3d7 c1818a9f5977dd4139a48f93f5425c67d44a9368 0000000000000000000000000000000000000000 2c0ec49482e8abe888b7bd090b5827acfc22b3d7 2114148793524fd045998f71a45b0aaf139f752b 102
218 6b57ee934bb2996050540f84cdfc8dcad1e7267d 2c0ec49482e8abe888b7bd090b5827acfc22b3d7 0000000000000000000000000000000000000000 6b57ee934bb2996050540f84cdfc8dcad1e7267d 2c0ec49482e8abe888b7bd090b5827acfc22b3d7 102
219 219
220 220 manifest
221 dac7984588fc4eea7acbf39693a9c1b06f5b175d 591f732a3faf1fb903815273f3c199a514a61ccb 0000000000000000000000000000000000000000 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc 113
222 0772616e6b48a76afb6c1458e193cbb3dae2e4ff dac7984588fc4eea7acbf39693a9c1b06f5b175d 0000000000000000000000000000000000000000 2114148793524fd045998f71a45b0aaf139f752b 113
223 eb498cd9af6c44108e43041e951ce829e29f6c80 bff2f4817ced57b386caf7c4e3e36a4bc9af7e93 0000000000000000000000000000000000000000 2c0ec49482e8abe888b7bd090b5827acfc22b3d7 295
224 b15709c071ddd2d93188508ba156196ab4f19620 eb498cd9af6c44108e43041e951ce829e29f6c80 0000000000000000000000000000000000000000 6b57ee934bb2996050540f84cdfc8dcad1e7267d 114
221 dac7984588fc4eea7acbf39693a9c1b06f5b175d 591f732a3faf1fb903815273f3c199a514a61ccb 0000000000000000000000000000000000000000 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc 591f732a3faf1fb903815273f3c199a514a61ccb 113
222 0772616e6b48a76afb6c1458e193cbb3dae2e4ff dac7984588fc4eea7acbf39693a9c1b06f5b175d 0000000000000000000000000000000000000000 2114148793524fd045998f71a45b0aaf139f752b dac7984588fc4eea7acbf39693a9c1b06f5b175d 113
223 eb498cd9af6c44108e43041e951ce829e29f6c80 bff2f4817ced57b386caf7c4e3e36a4bc9af7e93 0000000000000000000000000000000000000000 2c0ec49482e8abe888b7bd090b5827acfc22b3d7 0772616e6b48a76afb6c1458e193cbb3dae2e4ff 295
224 b15709c071ddd2d93188508ba156196ab4f19620 eb498cd9af6c44108e43041e951ce829e29f6c80 0000000000000000000000000000000000000000 6b57ee934bb2996050540f84cdfc8dcad1e7267d eb498cd9af6c44108e43041e951ce829e29f6c80 114
225 225
226 226 mf
227 4f73f97080266ab8e0c0561ca8d0da3eaf65b695 301ca08d026bb72cb4258a9d211bdf7ca0bcd810 0000000000000000000000000000000000000000 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc 17
228 c7b583de053293870e145f45bd2d61643563fd06 4f73f97080266ab8e0c0561ca8d0da3eaf65b695 0000000000000000000000000000000000000000 2114148793524fd045998f71a45b0aaf139f752b 18
229 266ee3c0302a5a18f1cf96817ac79a51836179e9 edc0f6b8db80d68ae6aff2b19f7e5347ab68fa63 0000000000000000000000000000000000000000 2c0ec49482e8abe888b7bd090b5827acfc22b3d7 149
230 698c6a36220548cd3903ca7dada27c59aa500c52 266ee3c0302a5a18f1cf96817ac79a51836179e9 0000000000000000000000000000000000000000 6b57ee934bb2996050540f84cdfc8dcad1e7267d 19
227 4f73f97080266ab8e0c0561ca8d0da3eaf65b695 301ca08d026bb72cb4258a9d211bdf7ca0bcd810 0000000000000000000000000000000000000000 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc 301ca08d026bb72cb4258a9d211bdf7ca0bcd810 17
228 c7b583de053293870e145f45bd2d61643563fd06 4f73f97080266ab8e0c0561ca8d0da3eaf65b695 0000000000000000000000000000000000000000 2114148793524fd045998f71a45b0aaf139f752b 4f73f97080266ab8e0c0561ca8d0da3eaf65b695 18
229 266ee3c0302a5a18f1cf96817ac79a51836179e9 edc0f6b8db80d68ae6aff2b19f7e5347ab68fa63 0000000000000000000000000000000000000000 2c0ec49482e8abe888b7bd090b5827acfc22b3d7 c7b583de053293870e145f45bd2d61643563fd06 149
230 698c6a36220548cd3903ca7dada27c59aa500c52 266ee3c0302a5a18f1cf96817ac79a51836179e9 0000000000000000000000000000000000000000 6b57ee934bb2996050540f84cdfc8dcad1e7267d 266ee3c0302a5a18f1cf96817ac79a51836179e9 19
231 231
232 232 nf11
233 33fbc651630ffa7ccbebfe4eb91320a873e7291c 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 2c0ec49482e8abe888b7bd090b5827acfc22b3d7 16
233 33fbc651630ffa7ccbebfe4eb91320a873e7291c 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 2c0ec49482e8abe888b7bd090b5827acfc22b3d7 0000000000000000000000000000000000000000 16
234 234
235 235 nf12
236 ddce0544363f037e9fb889faca058f52dc01c0a5 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 6b57ee934bb2996050540f84cdfc8dcad1e7267d 16
236 ddce0544363f037e9fb889faca058f52dc01c0a5 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 6b57ee934bb2996050540f84cdfc8dcad1e7267d 0000000000000000000000000000000000000000 16
237 237
238 238 nf4
239 3c1407305701051cbed9f9cb9a68bdfb5997c235 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc 15
239 3c1407305701051cbed9f9cb9a68bdfb5997c235 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc 0000000000000000000000000000000000000000 15
240 240
241 241 nf5
242 0dbd89c185f53a1727c54cd1ce256482fa23968e 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 2114148793524fd045998f71a45b0aaf139f752b 15
242 0dbd89c185f53a1727c54cd1ce256482fa23968e 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 2114148793524fd045998f71a45b0aaf139f752b 0000000000000000000000000000000000000000 15
243 243
244 244 Verify we hit the HTTP server:
245 245
246 246 $ cat access.log
247 247 * - - [*] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
248 248 * - - [*] "GET /?cmd=getbundle HTTP/1.1" 200 - (glob)
249 249 * - - [*] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
250 250 * - - [*] "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:common=c1818a9f5977dd4139a48f93f5425c67d44a9368+ea919464b16e003894c48b6cb68df3cd9411b544&heads=6b57ee934bb2996050540f84cdfc8dcad1e7267d+2114148793524fd045998f71a45b0aaf139f752b (glob)
251 251
252 252 $ cat error.log
253 253
General Comments 0
You need to be logged in to leave comments. Login now