##// END OF EJS Templates
diff: make use of output labeling
Brodie Rao -
r10818:d14d45fa default
parent child Browse files
Show More
@@ -1,3899 +1,3901 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from node import hex, nullid, nullrev, short
8 from node import hex, nullid, nullrev, short
9 from lock import release
9 from lock import release
10 from i18n import _, gettext
10 from i18n import _, gettext
11 import os, re, sys, difflib, time, tempfile
11 import os, re, sys, difflib, time, tempfile
12 import hg, util, revlog, bundlerepo, extensions, copies, error
12 import hg, util, revlog, bundlerepo, extensions, copies, error
13 import patch, help, mdiff, url, encoding, templatekw
13 import patch, help, mdiff, url, encoding, templatekw
14 import archival, changegroup, cmdutil, sshserver, hbisect
14 import archival, changegroup, cmdutil, sshserver, hbisect
15 from hgweb import server, hgweb_mod, hgwebdir_mod
15 from hgweb import server, hgweb_mod, hgwebdir_mod
16 import merge as mergemod
16 import merge as mergemod
17 import minirst
17 import minirst
18
18
19 # Commands start here, listed alphabetically
19 # Commands start here, listed alphabetically
20
20
21 def add(ui, repo, *pats, **opts):
21 def add(ui, repo, *pats, **opts):
22 """add the specified files on the next commit
22 """add the specified files on the next commit
23
23
24 Schedule files to be version controlled and added to the
24 Schedule files to be version controlled and added to the
25 repository.
25 repository.
26
26
27 The files will be added to the repository at the next commit. To
27 The files will be added to the repository at the next commit. To
28 undo an add before that, see hg forget.
28 undo an add before that, see hg forget.
29
29
30 If no names are given, add all files to the repository.
30 If no names are given, add all files to the repository.
31
31
32 .. container:: verbose
32 .. container:: verbose
33
33
34 An example showing how new (unknown) files are added
34 An example showing how new (unknown) files are added
35 automatically by ``hg add``::
35 automatically by ``hg add``::
36
36
37 $ ls
37 $ ls
38 foo.c
38 foo.c
39 $ hg status
39 $ hg status
40 ? foo.c
40 ? foo.c
41 $ hg add
41 $ hg add
42 adding foo.c
42 adding foo.c
43 $ hg status
43 $ hg status
44 A foo.c
44 A foo.c
45 """
45 """
46
46
47 bad = []
47 bad = []
48 names = []
48 names = []
49 m = cmdutil.match(repo, pats, opts)
49 m = cmdutil.match(repo, pats, opts)
50 oldbad = m.bad
50 oldbad = m.bad
51 m.bad = lambda x, y: bad.append(x) or oldbad(x, y)
51 m.bad = lambda x, y: bad.append(x) or oldbad(x, y)
52
52
53 for f in repo.walk(m):
53 for f in repo.walk(m):
54 exact = m.exact(f)
54 exact = m.exact(f)
55 if exact or f not in repo.dirstate:
55 if exact or f not in repo.dirstate:
56 names.append(f)
56 names.append(f)
57 if ui.verbose or not exact:
57 if ui.verbose or not exact:
58 ui.status(_('adding %s\n') % m.rel(f))
58 ui.status(_('adding %s\n') % m.rel(f))
59 if not opts.get('dry_run'):
59 if not opts.get('dry_run'):
60 bad += [f for f in repo.add(names) if f in m.files()]
60 bad += [f for f in repo.add(names) if f in m.files()]
61 return bad and 1 or 0
61 return bad and 1 or 0
62
62
63 def addremove(ui, repo, *pats, **opts):
63 def addremove(ui, repo, *pats, **opts):
64 """add all new files, delete all missing files
64 """add all new files, delete all missing files
65
65
66 Add all new files and remove all missing files from the
66 Add all new files and remove all missing files from the
67 repository.
67 repository.
68
68
69 New files are ignored if they match any of the patterns in
69 New files are ignored if they match any of the patterns in
70 .hgignore. As with add, these changes take effect at the next
70 .hgignore. As with add, these changes take effect at the next
71 commit.
71 commit.
72
72
73 Use the -s/--similarity option to detect renamed files. With a
73 Use the -s/--similarity option to detect renamed files. With a
74 parameter greater than 0, this compares every removed file with
74 parameter greater than 0, this compares every removed file with
75 every added file and records those similar enough as renames. This
75 every added file and records those similar enough as renames. This
76 option takes a percentage between 0 (disabled) and 100 (files must
76 option takes a percentage between 0 (disabled) and 100 (files must
77 be identical) as its parameter. Detecting renamed files this way
77 be identical) as its parameter. Detecting renamed files this way
78 can be expensive.
78 can be expensive.
79 """
79 """
80 try:
80 try:
81 sim = float(opts.get('similarity') or 0)
81 sim = float(opts.get('similarity') or 0)
82 except ValueError:
82 except ValueError:
83 raise util.Abort(_('similarity must be a number'))
83 raise util.Abort(_('similarity must be a number'))
84 if sim < 0 or sim > 100:
84 if sim < 0 or sim > 100:
85 raise util.Abort(_('similarity must be between 0 and 100'))
85 raise util.Abort(_('similarity must be between 0 and 100'))
86 return cmdutil.addremove(repo, pats, opts, similarity=sim / 100.0)
86 return cmdutil.addremove(repo, pats, opts, similarity=sim / 100.0)
87
87
88 def annotate(ui, repo, *pats, **opts):
88 def annotate(ui, repo, *pats, **opts):
89 """show changeset information by line for each file
89 """show changeset information by line for each file
90
90
91 List changes in files, showing the revision id responsible for
91 List changes in files, showing the revision id responsible for
92 each line
92 each line
93
93
94 This command is useful for discovering when a change was made and
94 This command is useful for discovering when a change was made and
95 by whom.
95 by whom.
96
96
97 Without the -a/--text option, annotate will avoid processing files
97 Without the -a/--text option, annotate will avoid processing files
98 it detects as binary. With -a, annotate will annotate the file
98 it detects as binary. With -a, annotate will annotate the file
99 anyway, although the results will probably be neither useful
99 anyway, although the results will probably be neither useful
100 nor desirable.
100 nor desirable.
101 """
101 """
102 if opts.get('follow'):
102 if opts.get('follow'):
103 # --follow is deprecated and now just an alias for -f/--file
103 # --follow is deprecated and now just an alias for -f/--file
104 # to mimic the behavior of Mercurial before version 1.5
104 # to mimic the behavior of Mercurial before version 1.5
105 opts['file'] = 1
105 opts['file'] = 1
106
106
107 datefunc = ui.quiet and util.shortdate or util.datestr
107 datefunc = ui.quiet and util.shortdate or util.datestr
108 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
108 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
109
109
110 if not pats:
110 if not pats:
111 raise util.Abort(_('at least one filename or pattern is required'))
111 raise util.Abort(_('at least one filename or pattern is required'))
112
112
113 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
113 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
114 ('number', lambda x: str(x[0].rev())),
114 ('number', lambda x: str(x[0].rev())),
115 ('changeset', lambda x: short(x[0].node())),
115 ('changeset', lambda x: short(x[0].node())),
116 ('date', getdate),
116 ('date', getdate),
117 ('file', lambda x: x[0].path()),
117 ('file', lambda x: x[0].path()),
118 ]
118 ]
119
119
120 if (not opts.get('user') and not opts.get('changeset')
120 if (not opts.get('user') and not opts.get('changeset')
121 and not opts.get('date') and not opts.get('file')):
121 and not opts.get('date') and not opts.get('file')):
122 opts['number'] = 1
122 opts['number'] = 1
123
123
124 linenumber = opts.get('line_number') is not None
124 linenumber = opts.get('line_number') is not None
125 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
125 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
126 raise util.Abort(_('at least one of -n/-c is required for -l'))
126 raise util.Abort(_('at least one of -n/-c is required for -l'))
127
127
128 funcmap = [func for op, func in opmap if opts.get(op)]
128 funcmap = [func for op, func in opmap if opts.get(op)]
129 if linenumber:
129 if linenumber:
130 lastfunc = funcmap[-1]
130 lastfunc = funcmap[-1]
131 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
131 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
132
132
133 ctx = repo[opts.get('rev')]
133 ctx = repo[opts.get('rev')]
134 m = cmdutil.match(repo, pats, opts)
134 m = cmdutil.match(repo, pats, opts)
135 follow = not opts.get('no_follow')
135 follow = not opts.get('no_follow')
136 for abs in ctx.walk(m):
136 for abs in ctx.walk(m):
137 fctx = ctx[abs]
137 fctx = ctx[abs]
138 if not opts.get('text') and util.binary(fctx.data()):
138 if not opts.get('text') and util.binary(fctx.data()):
139 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
139 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
140 continue
140 continue
141
141
142 lines = fctx.annotate(follow=follow, linenumber=linenumber)
142 lines = fctx.annotate(follow=follow, linenumber=linenumber)
143 pieces = []
143 pieces = []
144
144
145 for f in funcmap:
145 for f in funcmap:
146 l = [f(n) for n, dummy in lines]
146 l = [f(n) for n, dummy in lines]
147 if l:
147 if l:
148 ml = max(map(len, l))
148 ml = max(map(len, l))
149 pieces.append(["%*s" % (ml, x) for x in l])
149 pieces.append(["%*s" % (ml, x) for x in l])
150
150
151 if pieces:
151 if pieces:
152 for p, l in zip(zip(*pieces), lines):
152 for p, l in zip(zip(*pieces), lines):
153 ui.write("%s: %s" % (" ".join(p), l[1]))
153 ui.write("%s: %s" % (" ".join(p), l[1]))
154
154
155 def archive(ui, repo, dest, **opts):
155 def archive(ui, repo, dest, **opts):
156 '''create an unversioned archive of a repository revision
156 '''create an unversioned archive of a repository revision
157
157
158 By default, the revision used is the parent of the working
158 By default, the revision used is the parent of the working
159 directory; use -r/--rev to specify a different revision.
159 directory; use -r/--rev to specify a different revision.
160
160
161 The archive type is automatically detected based on file
161 The archive type is automatically detected based on file
162 extension (or override using -t/--type).
162 extension (or override using -t/--type).
163
163
164 Valid types are:
164 Valid types are:
165
165
166 :``files``: a directory full of files (default)
166 :``files``: a directory full of files (default)
167 :``tar``: tar archive, uncompressed
167 :``tar``: tar archive, uncompressed
168 :``tbz2``: tar archive, compressed using bzip2
168 :``tbz2``: tar archive, compressed using bzip2
169 :``tgz``: tar archive, compressed using gzip
169 :``tgz``: tar archive, compressed using gzip
170 :``uzip``: zip archive, uncompressed
170 :``uzip``: zip archive, uncompressed
171 :``zip``: zip archive, compressed using deflate
171 :``zip``: zip archive, compressed using deflate
172
172
173 The exact name of the destination archive or directory is given
173 The exact name of the destination archive or directory is given
174 using a format string; see 'hg help export' for details.
174 using a format string; see 'hg help export' for details.
175
175
176 Each member added to an archive file has a directory prefix
176 Each member added to an archive file has a directory prefix
177 prepended. Use -p/--prefix to specify a format string for the
177 prepended. Use -p/--prefix to specify a format string for the
178 prefix. The default is the basename of the archive, with suffixes
178 prefix. The default is the basename of the archive, with suffixes
179 removed.
179 removed.
180 '''
180 '''
181
181
182 ctx = repo[opts.get('rev')]
182 ctx = repo[opts.get('rev')]
183 if not ctx:
183 if not ctx:
184 raise util.Abort(_('no working directory: please specify a revision'))
184 raise util.Abort(_('no working directory: please specify a revision'))
185 node = ctx.node()
185 node = ctx.node()
186 dest = cmdutil.make_filename(repo, dest, node)
186 dest = cmdutil.make_filename(repo, dest, node)
187 if os.path.realpath(dest) == repo.root:
187 if os.path.realpath(dest) == repo.root:
188 raise util.Abort(_('repository root cannot be destination'))
188 raise util.Abort(_('repository root cannot be destination'))
189
189
190 def guess_type():
190 def guess_type():
191 exttypes = {
191 exttypes = {
192 'tar': ['.tar'],
192 'tar': ['.tar'],
193 'tbz2': ['.tbz2', '.tar.bz2'],
193 'tbz2': ['.tbz2', '.tar.bz2'],
194 'tgz': ['.tgz', '.tar.gz'],
194 'tgz': ['.tgz', '.tar.gz'],
195 'zip': ['.zip'],
195 'zip': ['.zip'],
196 }
196 }
197
197
198 for type, extensions in exttypes.items():
198 for type, extensions in exttypes.items():
199 if util.any(dest.endswith(ext) for ext in extensions):
199 if util.any(dest.endswith(ext) for ext in extensions):
200 return type
200 return type
201 return None
201 return None
202
202
203 kind = opts.get('type') or guess_type() or 'files'
203 kind = opts.get('type') or guess_type() or 'files'
204 prefix = opts.get('prefix')
204 prefix = opts.get('prefix')
205
205
206 if dest == '-':
206 if dest == '-':
207 if kind == 'files':
207 if kind == 'files':
208 raise util.Abort(_('cannot archive plain files to stdout'))
208 raise util.Abort(_('cannot archive plain files to stdout'))
209 dest = sys.stdout
209 dest = sys.stdout
210 if not prefix:
210 if not prefix:
211 prefix = os.path.basename(repo.root) + '-%h'
211 prefix = os.path.basename(repo.root) + '-%h'
212
212
213 prefix = cmdutil.make_filename(repo, prefix, node)
213 prefix = cmdutil.make_filename(repo, prefix, node)
214 matchfn = cmdutil.match(repo, [], opts)
214 matchfn = cmdutil.match(repo, [], opts)
215 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
215 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
216 matchfn, prefix)
216 matchfn, prefix)
217
217
218 def backout(ui, repo, node=None, rev=None, **opts):
218 def backout(ui, repo, node=None, rev=None, **opts):
219 '''reverse effect of earlier changeset
219 '''reverse effect of earlier changeset
220
220
221 Commit the backed out changes as a new changeset. The new
221 Commit the backed out changes as a new changeset. The new
222 changeset is a child of the backed out changeset.
222 changeset is a child of the backed out changeset.
223
223
224 If you backout a changeset other than the tip, a new head is
224 If you backout a changeset other than the tip, a new head is
225 created. This head will be the new tip and you should merge this
225 created. This head will be the new tip and you should merge this
226 backout changeset with another head.
226 backout changeset with another head.
227
227
228 The --merge option remembers the parent of the working directory
228 The --merge option remembers the parent of the working directory
229 before starting the backout, then merges the new head with that
229 before starting the backout, then merges the new head with that
230 changeset afterwards. This saves you from doing the merge by hand.
230 changeset afterwards. This saves you from doing the merge by hand.
231 The result of this merge is not committed, as with a normal merge.
231 The result of this merge is not committed, as with a normal merge.
232
232
233 See 'hg help dates' for a list of formats valid for -d/--date.
233 See 'hg help dates' for a list of formats valid for -d/--date.
234 '''
234 '''
235 if rev and node:
235 if rev and node:
236 raise util.Abort(_("please specify just one revision"))
236 raise util.Abort(_("please specify just one revision"))
237
237
238 if not rev:
238 if not rev:
239 rev = node
239 rev = node
240
240
241 if not rev:
241 if not rev:
242 raise util.Abort(_("please specify a revision to backout"))
242 raise util.Abort(_("please specify a revision to backout"))
243
243
244 date = opts.get('date')
244 date = opts.get('date')
245 if date:
245 if date:
246 opts['date'] = util.parsedate(date)
246 opts['date'] = util.parsedate(date)
247
247
248 cmdutil.bail_if_changed(repo)
248 cmdutil.bail_if_changed(repo)
249 node = repo.lookup(rev)
249 node = repo.lookup(rev)
250
250
251 op1, op2 = repo.dirstate.parents()
251 op1, op2 = repo.dirstate.parents()
252 a = repo.changelog.ancestor(op1, node)
252 a = repo.changelog.ancestor(op1, node)
253 if a != node:
253 if a != node:
254 raise util.Abort(_('cannot backout change on a different branch'))
254 raise util.Abort(_('cannot backout change on a different branch'))
255
255
256 p1, p2 = repo.changelog.parents(node)
256 p1, p2 = repo.changelog.parents(node)
257 if p1 == nullid:
257 if p1 == nullid:
258 raise util.Abort(_('cannot backout a change with no parents'))
258 raise util.Abort(_('cannot backout a change with no parents'))
259 if p2 != nullid:
259 if p2 != nullid:
260 if not opts.get('parent'):
260 if not opts.get('parent'):
261 raise util.Abort(_('cannot backout a merge changeset without '
261 raise util.Abort(_('cannot backout a merge changeset without '
262 '--parent'))
262 '--parent'))
263 p = repo.lookup(opts['parent'])
263 p = repo.lookup(opts['parent'])
264 if p not in (p1, p2):
264 if p not in (p1, p2):
265 raise util.Abort(_('%s is not a parent of %s') %
265 raise util.Abort(_('%s is not a parent of %s') %
266 (short(p), short(node)))
266 (short(p), short(node)))
267 parent = p
267 parent = p
268 else:
268 else:
269 if opts.get('parent'):
269 if opts.get('parent'):
270 raise util.Abort(_('cannot use --parent on non-merge changeset'))
270 raise util.Abort(_('cannot use --parent on non-merge changeset'))
271 parent = p1
271 parent = p1
272
272
273 # the backout should appear on the same branch
273 # the backout should appear on the same branch
274 branch = repo.dirstate.branch()
274 branch = repo.dirstate.branch()
275 hg.clean(repo, node, show_stats=False)
275 hg.clean(repo, node, show_stats=False)
276 repo.dirstate.setbranch(branch)
276 repo.dirstate.setbranch(branch)
277 revert_opts = opts.copy()
277 revert_opts = opts.copy()
278 revert_opts['date'] = None
278 revert_opts['date'] = None
279 revert_opts['all'] = True
279 revert_opts['all'] = True
280 revert_opts['rev'] = hex(parent)
280 revert_opts['rev'] = hex(parent)
281 revert_opts['no_backup'] = None
281 revert_opts['no_backup'] = None
282 revert(ui, repo, **revert_opts)
282 revert(ui, repo, **revert_opts)
283 commit_opts = opts.copy()
283 commit_opts = opts.copy()
284 commit_opts['addremove'] = False
284 commit_opts['addremove'] = False
285 if not commit_opts['message'] and not commit_opts['logfile']:
285 if not commit_opts['message'] and not commit_opts['logfile']:
286 # we don't translate commit messages
286 # we don't translate commit messages
287 commit_opts['message'] = "Backed out changeset %s" % short(node)
287 commit_opts['message'] = "Backed out changeset %s" % short(node)
288 commit_opts['force_editor'] = True
288 commit_opts['force_editor'] = True
289 commit(ui, repo, **commit_opts)
289 commit(ui, repo, **commit_opts)
290 def nice(node):
290 def nice(node):
291 return '%d:%s' % (repo.changelog.rev(node), short(node))
291 return '%d:%s' % (repo.changelog.rev(node), short(node))
292 ui.status(_('changeset %s backs out changeset %s\n') %
292 ui.status(_('changeset %s backs out changeset %s\n') %
293 (nice(repo.changelog.tip()), nice(node)))
293 (nice(repo.changelog.tip()), nice(node)))
294 if op1 != node:
294 if op1 != node:
295 hg.clean(repo, op1, show_stats=False)
295 hg.clean(repo, op1, show_stats=False)
296 if opts.get('merge'):
296 if opts.get('merge'):
297 ui.status(_('merging with changeset %s\n')
297 ui.status(_('merging with changeset %s\n')
298 % nice(repo.changelog.tip()))
298 % nice(repo.changelog.tip()))
299 hg.merge(repo, hex(repo.changelog.tip()))
299 hg.merge(repo, hex(repo.changelog.tip()))
300 else:
300 else:
301 ui.status(_('the backout changeset is a new head - '
301 ui.status(_('the backout changeset is a new head - '
302 'do not forget to merge\n'))
302 'do not forget to merge\n'))
303 ui.status(_('(use "backout --merge" '
303 ui.status(_('(use "backout --merge" '
304 'if you want to auto-merge)\n'))
304 'if you want to auto-merge)\n'))
305
305
306 def bisect(ui, repo, rev=None, extra=None, command=None,
306 def bisect(ui, repo, rev=None, extra=None, command=None,
307 reset=None, good=None, bad=None, skip=None, noupdate=None):
307 reset=None, good=None, bad=None, skip=None, noupdate=None):
308 """subdivision search of changesets
308 """subdivision search of changesets
309
309
310 This command helps to find changesets which introduce problems. To
310 This command helps to find changesets which introduce problems. To
311 use, mark the earliest changeset you know exhibits the problem as
311 use, mark the earliest changeset you know exhibits the problem as
312 bad, then mark the latest changeset which is free from the problem
312 bad, then mark the latest changeset which is free from the problem
313 as good. Bisect will update your working directory to a revision
313 as good. Bisect will update your working directory to a revision
314 for testing (unless the -U/--noupdate option is specified). Once
314 for testing (unless the -U/--noupdate option is specified). Once
315 you have performed tests, mark the working directory as good or
315 you have performed tests, mark the working directory as good or
316 bad, and bisect will either update to another candidate changeset
316 bad, and bisect will either update to another candidate changeset
317 or announce that it has found the bad revision.
317 or announce that it has found the bad revision.
318
318
319 As a shortcut, you can also use the revision argument to mark a
319 As a shortcut, you can also use the revision argument to mark a
320 revision as good or bad without checking it out first.
320 revision as good or bad without checking it out first.
321
321
322 If you supply a command, it will be used for automatic bisection.
322 If you supply a command, it will be used for automatic bisection.
323 Its exit status will be used to mark revisions as good or bad:
323 Its exit status will be used to mark revisions as good or bad:
324 status 0 means good, 125 means to skip the revision, 127
324 status 0 means good, 125 means to skip the revision, 127
325 (command not found) will abort the bisection, and any other
325 (command not found) will abort the bisection, and any other
326 non-zero exit status means the revision is bad.
326 non-zero exit status means the revision is bad.
327 """
327 """
328 def print_result(nodes, good):
328 def print_result(nodes, good):
329 displayer = cmdutil.show_changeset(ui, repo, {})
329 displayer = cmdutil.show_changeset(ui, repo, {})
330 if len(nodes) == 1:
330 if len(nodes) == 1:
331 # narrowed it down to a single revision
331 # narrowed it down to a single revision
332 if good:
332 if good:
333 ui.write(_("The first good revision is:\n"))
333 ui.write(_("The first good revision is:\n"))
334 else:
334 else:
335 ui.write(_("The first bad revision is:\n"))
335 ui.write(_("The first bad revision is:\n"))
336 displayer.show(repo[nodes[0]])
336 displayer.show(repo[nodes[0]])
337 else:
337 else:
338 # multiple possible revisions
338 # multiple possible revisions
339 if good:
339 if good:
340 ui.write(_("Due to skipped revisions, the first "
340 ui.write(_("Due to skipped revisions, the first "
341 "good revision could be any of:\n"))
341 "good revision could be any of:\n"))
342 else:
342 else:
343 ui.write(_("Due to skipped revisions, the first "
343 ui.write(_("Due to skipped revisions, the first "
344 "bad revision could be any of:\n"))
344 "bad revision could be any of:\n"))
345 for n in nodes:
345 for n in nodes:
346 displayer.show(repo[n])
346 displayer.show(repo[n])
347 displayer.close()
347 displayer.close()
348
348
349 def check_state(state, interactive=True):
349 def check_state(state, interactive=True):
350 if not state['good'] or not state['bad']:
350 if not state['good'] or not state['bad']:
351 if (good or bad or skip or reset) and interactive:
351 if (good or bad or skip or reset) and interactive:
352 return
352 return
353 if not state['good']:
353 if not state['good']:
354 raise util.Abort(_('cannot bisect (no known good revisions)'))
354 raise util.Abort(_('cannot bisect (no known good revisions)'))
355 else:
355 else:
356 raise util.Abort(_('cannot bisect (no known bad revisions)'))
356 raise util.Abort(_('cannot bisect (no known bad revisions)'))
357 return True
357 return True
358
358
359 # backward compatibility
359 # backward compatibility
360 if rev in "good bad reset init".split():
360 if rev in "good bad reset init".split():
361 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
361 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
362 cmd, rev, extra = rev, extra, None
362 cmd, rev, extra = rev, extra, None
363 if cmd == "good":
363 if cmd == "good":
364 good = True
364 good = True
365 elif cmd == "bad":
365 elif cmd == "bad":
366 bad = True
366 bad = True
367 else:
367 else:
368 reset = True
368 reset = True
369 elif extra or good + bad + skip + reset + bool(command) > 1:
369 elif extra or good + bad + skip + reset + bool(command) > 1:
370 raise util.Abort(_('incompatible arguments'))
370 raise util.Abort(_('incompatible arguments'))
371
371
372 if reset:
372 if reset:
373 p = repo.join("bisect.state")
373 p = repo.join("bisect.state")
374 if os.path.exists(p):
374 if os.path.exists(p):
375 os.unlink(p)
375 os.unlink(p)
376 return
376 return
377
377
378 state = hbisect.load_state(repo)
378 state = hbisect.load_state(repo)
379
379
380 if command:
380 if command:
381 changesets = 1
381 changesets = 1
382 try:
382 try:
383 while changesets:
383 while changesets:
384 # update state
384 # update state
385 status = util.system(command)
385 status = util.system(command)
386 if status == 125:
386 if status == 125:
387 transition = "skip"
387 transition = "skip"
388 elif status == 0:
388 elif status == 0:
389 transition = "good"
389 transition = "good"
390 # status < 0 means process was killed
390 # status < 0 means process was killed
391 elif status == 127:
391 elif status == 127:
392 raise util.Abort(_("failed to execute %s") % command)
392 raise util.Abort(_("failed to execute %s") % command)
393 elif status < 0:
393 elif status < 0:
394 raise util.Abort(_("%s killed") % command)
394 raise util.Abort(_("%s killed") % command)
395 else:
395 else:
396 transition = "bad"
396 transition = "bad"
397 ctx = repo[rev or '.']
397 ctx = repo[rev or '.']
398 state[transition].append(ctx.node())
398 state[transition].append(ctx.node())
399 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
399 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
400 check_state(state, interactive=False)
400 check_state(state, interactive=False)
401 # bisect
401 # bisect
402 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
402 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
403 # update to next check
403 # update to next check
404 cmdutil.bail_if_changed(repo)
404 cmdutil.bail_if_changed(repo)
405 hg.clean(repo, nodes[0], show_stats=False)
405 hg.clean(repo, nodes[0], show_stats=False)
406 finally:
406 finally:
407 hbisect.save_state(repo, state)
407 hbisect.save_state(repo, state)
408 return print_result(nodes, good)
408 return print_result(nodes, good)
409
409
410 # update state
410 # update state
411 node = repo.lookup(rev or '.')
411 node = repo.lookup(rev or '.')
412 if good or bad or skip:
412 if good or bad or skip:
413 if good:
413 if good:
414 state['good'].append(node)
414 state['good'].append(node)
415 elif bad:
415 elif bad:
416 state['bad'].append(node)
416 state['bad'].append(node)
417 elif skip:
417 elif skip:
418 state['skip'].append(node)
418 state['skip'].append(node)
419 hbisect.save_state(repo, state)
419 hbisect.save_state(repo, state)
420
420
421 if not check_state(state):
421 if not check_state(state):
422 return
422 return
423
423
424 # actually bisect
424 # actually bisect
425 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
425 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
426 if changesets == 0:
426 if changesets == 0:
427 print_result(nodes, good)
427 print_result(nodes, good)
428 else:
428 else:
429 assert len(nodes) == 1 # only a single node can be tested next
429 assert len(nodes) == 1 # only a single node can be tested next
430 node = nodes[0]
430 node = nodes[0]
431 # compute the approximate number of remaining tests
431 # compute the approximate number of remaining tests
432 tests, size = 0, 2
432 tests, size = 0, 2
433 while size <= changesets:
433 while size <= changesets:
434 tests, size = tests + 1, size * 2
434 tests, size = tests + 1, size * 2
435 rev = repo.changelog.rev(node)
435 rev = repo.changelog.rev(node)
436 ui.write(_("Testing changeset %d:%s "
436 ui.write(_("Testing changeset %d:%s "
437 "(%d changesets remaining, ~%d tests)\n")
437 "(%d changesets remaining, ~%d tests)\n")
438 % (rev, short(node), changesets, tests))
438 % (rev, short(node), changesets, tests))
439 if not noupdate:
439 if not noupdate:
440 cmdutil.bail_if_changed(repo)
440 cmdutil.bail_if_changed(repo)
441 return hg.clean(repo, node)
441 return hg.clean(repo, node)
442
442
443 def branch(ui, repo, label=None, **opts):
443 def branch(ui, repo, label=None, **opts):
444 """set or show the current branch name
444 """set or show the current branch name
445
445
446 With no argument, show the current branch name. With one argument,
446 With no argument, show the current branch name. With one argument,
447 set the working directory branch name (the branch will not exist
447 set the working directory branch name (the branch will not exist
448 in the repository until the next commit). Standard practice
448 in the repository until the next commit). Standard practice
449 recommends that primary development take place on the 'default'
449 recommends that primary development take place on the 'default'
450 branch.
450 branch.
451
451
452 Unless -f/--force is specified, branch will not let you set a
452 Unless -f/--force is specified, branch will not let you set a
453 branch name that already exists, even if it's inactive.
453 branch name that already exists, even if it's inactive.
454
454
455 Use -C/--clean to reset the working directory branch to that of
455 Use -C/--clean to reset the working directory branch to that of
456 the parent of the working directory, negating a previous branch
456 the parent of the working directory, negating a previous branch
457 change.
457 change.
458
458
459 Use the command 'hg update' to switch to an existing branch. Use
459 Use the command 'hg update' to switch to an existing branch. Use
460 'hg commit --close-branch' to mark this branch as closed.
460 'hg commit --close-branch' to mark this branch as closed.
461 """
461 """
462
462
463 if opts.get('clean'):
463 if opts.get('clean'):
464 label = repo[None].parents()[0].branch()
464 label = repo[None].parents()[0].branch()
465 repo.dirstate.setbranch(label)
465 repo.dirstate.setbranch(label)
466 ui.status(_('reset working directory to branch %s\n') % label)
466 ui.status(_('reset working directory to branch %s\n') % label)
467 elif label:
467 elif label:
468 utflabel = encoding.fromlocal(label)
468 utflabel = encoding.fromlocal(label)
469 if not opts.get('force') and utflabel in repo.branchtags():
469 if not opts.get('force') and utflabel in repo.branchtags():
470 if label not in [p.branch() for p in repo.parents()]:
470 if label not in [p.branch() for p in repo.parents()]:
471 raise util.Abort(_('a branch of the same name already exists'
471 raise util.Abort(_('a branch of the same name already exists'
472 " (use 'hg update' to switch to it)"))
472 " (use 'hg update' to switch to it)"))
473 repo.dirstate.setbranch(utflabel)
473 repo.dirstate.setbranch(utflabel)
474 ui.status(_('marked working directory as branch %s\n') % label)
474 ui.status(_('marked working directory as branch %s\n') % label)
475 else:
475 else:
476 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
476 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
477
477
478 def branches(ui, repo, active=False, closed=False):
478 def branches(ui, repo, active=False, closed=False):
479 """list repository named branches
479 """list repository named branches
480
480
481 List the repository's named branches, indicating which ones are
481 List the repository's named branches, indicating which ones are
482 inactive. If -c/--closed is specified, also list branches which have
482 inactive. If -c/--closed is specified, also list branches which have
483 been marked closed (see hg commit --close-branch).
483 been marked closed (see hg commit --close-branch).
484
484
485 If -a/--active is specified, only show active branches. A branch
485 If -a/--active is specified, only show active branches. A branch
486 is considered active if it contains repository heads.
486 is considered active if it contains repository heads.
487
487
488 Use the command 'hg update' to switch to an existing branch.
488 Use the command 'hg update' to switch to an existing branch.
489 """
489 """
490
490
491 hexfunc = ui.debugflag and hex or short
491 hexfunc = ui.debugflag and hex or short
492 activebranches = [repo[n].branch() for n in repo.heads()]
492 activebranches = [repo[n].branch() for n in repo.heads()]
493 def testactive(tag, node):
493 def testactive(tag, node):
494 realhead = tag in activebranches
494 realhead = tag in activebranches
495 open = node in repo.branchheads(tag, closed=False)
495 open = node in repo.branchheads(tag, closed=False)
496 return realhead and open
496 return realhead and open
497 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
497 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
498 for tag, node in repo.branchtags().items()],
498 for tag, node in repo.branchtags().items()],
499 reverse=True)
499 reverse=True)
500
500
501 for isactive, node, tag in branches:
501 for isactive, node, tag in branches:
502 if (not active) or isactive:
502 if (not active) or isactive:
503 encodedtag = encoding.tolocal(tag)
503 encodedtag = encoding.tolocal(tag)
504 if ui.quiet:
504 if ui.quiet:
505 ui.write("%s\n" % encodedtag)
505 ui.write("%s\n" % encodedtag)
506 else:
506 else:
507 hn = repo.lookup(node)
507 hn = repo.lookup(node)
508 if isactive:
508 if isactive:
509 notice = ''
509 notice = ''
510 elif hn not in repo.branchheads(tag, closed=False):
510 elif hn not in repo.branchheads(tag, closed=False):
511 if not closed:
511 if not closed:
512 continue
512 continue
513 notice = _(' (closed)')
513 notice = _(' (closed)')
514 else:
514 else:
515 notice = _(' (inactive)')
515 notice = _(' (inactive)')
516 rev = str(node).rjust(31 - encoding.colwidth(encodedtag))
516 rev = str(node).rjust(31 - encoding.colwidth(encodedtag))
517 data = encodedtag, rev, hexfunc(hn), notice
517 data = encodedtag, rev, hexfunc(hn), notice
518 ui.write("%s %s:%s%s\n" % data)
518 ui.write("%s %s:%s%s\n" % data)
519
519
520 def bundle(ui, repo, fname, dest=None, **opts):
520 def bundle(ui, repo, fname, dest=None, **opts):
521 """create a changegroup file
521 """create a changegroup file
522
522
523 Generate a compressed changegroup file collecting changesets not
523 Generate a compressed changegroup file collecting changesets not
524 known to be in another repository.
524 known to be in another repository.
525
525
526 If you omit the destination repository, then hg assumes the
526 If you omit the destination repository, then hg assumes the
527 destination will have all the nodes you specify with --base
527 destination will have all the nodes you specify with --base
528 parameters. To create a bundle containing all changesets, use
528 parameters. To create a bundle containing all changesets, use
529 -a/--all (or --base null).
529 -a/--all (or --base null).
530
530
531 You can change compression method with the -t/--type option.
531 You can change compression method with the -t/--type option.
532 The available compression methods are: none, bzip2, and
532 The available compression methods are: none, bzip2, and
533 gzip (by default, bundles are compressed using bzip2).
533 gzip (by default, bundles are compressed using bzip2).
534
534
535 The bundle file can then be transferred using conventional means
535 The bundle file can then be transferred using conventional means
536 and applied to another repository with the unbundle or pull
536 and applied to another repository with the unbundle or pull
537 command. This is useful when direct push and pull are not
537 command. This is useful when direct push and pull are not
538 available or when exporting an entire repository is undesirable.
538 available or when exporting an entire repository is undesirable.
539
539
540 Applying bundles preserves all changeset contents including
540 Applying bundles preserves all changeset contents including
541 permissions, copy/rename information, and revision history.
541 permissions, copy/rename information, and revision history.
542 """
542 """
543 revs = opts.get('rev') or None
543 revs = opts.get('rev') or None
544 if revs:
544 if revs:
545 revs = [repo.lookup(rev) for rev in revs]
545 revs = [repo.lookup(rev) for rev in revs]
546 if opts.get('all'):
546 if opts.get('all'):
547 base = ['null']
547 base = ['null']
548 else:
548 else:
549 base = opts.get('base')
549 base = opts.get('base')
550 if base:
550 if base:
551 if dest:
551 if dest:
552 raise util.Abort(_("--base is incompatible with specifying "
552 raise util.Abort(_("--base is incompatible with specifying "
553 "a destination"))
553 "a destination"))
554 base = [repo.lookup(rev) for rev in base]
554 base = [repo.lookup(rev) for rev in base]
555 # create the right base
555 # create the right base
556 # XXX: nodesbetween / changegroup* should be "fixed" instead
556 # XXX: nodesbetween / changegroup* should be "fixed" instead
557 o = []
557 o = []
558 has = set((nullid,))
558 has = set((nullid,))
559 for n in base:
559 for n in base:
560 has.update(repo.changelog.reachable(n))
560 has.update(repo.changelog.reachable(n))
561 if revs:
561 if revs:
562 visit = list(revs)
562 visit = list(revs)
563 has.difference_update(revs)
563 has.difference_update(revs)
564 else:
564 else:
565 visit = repo.changelog.heads()
565 visit = repo.changelog.heads()
566 seen = {}
566 seen = {}
567 while visit:
567 while visit:
568 n = visit.pop(0)
568 n = visit.pop(0)
569 parents = [p for p in repo.changelog.parents(n) if p not in has]
569 parents = [p for p in repo.changelog.parents(n) if p not in has]
570 if len(parents) == 0:
570 if len(parents) == 0:
571 if n not in has:
571 if n not in has:
572 o.append(n)
572 o.append(n)
573 else:
573 else:
574 for p in parents:
574 for p in parents:
575 if p not in seen:
575 if p not in seen:
576 seen[p] = 1
576 seen[p] = 1
577 visit.append(p)
577 visit.append(p)
578 else:
578 else:
579 dest = ui.expandpath(dest or 'default-push', dest or 'default')
579 dest = ui.expandpath(dest or 'default-push', dest or 'default')
580 dest, branches = hg.parseurl(dest, opts.get('branch'))
580 dest, branches = hg.parseurl(dest, opts.get('branch'))
581 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
581 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
582 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
582 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
583 o = repo.findoutgoing(other, force=opts.get('force'))
583 o = repo.findoutgoing(other, force=opts.get('force'))
584
584
585 if not o:
585 if not o:
586 ui.status(_("no changes found\n"))
586 ui.status(_("no changes found\n"))
587 return
587 return
588
588
589 if revs:
589 if revs:
590 cg = repo.changegroupsubset(o, revs, 'bundle')
590 cg = repo.changegroupsubset(o, revs, 'bundle')
591 else:
591 else:
592 cg = repo.changegroup(o, 'bundle')
592 cg = repo.changegroup(o, 'bundle')
593
593
594 bundletype = opts.get('type', 'bzip2').lower()
594 bundletype = opts.get('type', 'bzip2').lower()
595 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
595 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
596 bundletype = btypes.get(bundletype)
596 bundletype = btypes.get(bundletype)
597 if bundletype not in changegroup.bundletypes:
597 if bundletype not in changegroup.bundletypes:
598 raise util.Abort(_('unknown bundle type specified with --type'))
598 raise util.Abort(_('unknown bundle type specified with --type'))
599
599
600 changegroup.writebundle(cg, fname, bundletype)
600 changegroup.writebundle(cg, fname, bundletype)
601
601
602 def cat(ui, repo, file1, *pats, **opts):
602 def cat(ui, repo, file1, *pats, **opts):
603 """output the current or given revision of files
603 """output the current or given revision of files
604
604
605 Print the specified files as they were at the given revision. If
605 Print the specified files as they were at the given revision. If
606 no revision is given, the parent of the working directory is used,
606 no revision is given, the parent of the working directory is used,
607 or tip if no revision is checked out.
607 or tip if no revision is checked out.
608
608
609 Output may be to a file, in which case the name of the file is
609 Output may be to a file, in which case the name of the file is
610 given using a format string. The formatting rules are the same as
610 given using a format string. The formatting rules are the same as
611 for the export command, with the following additions:
611 for the export command, with the following additions:
612
612
613 :``%s``: basename of file being printed
613 :``%s``: basename of file being printed
614 :``%d``: dirname of file being printed, or '.' if in repository root
614 :``%d``: dirname of file being printed, or '.' if in repository root
615 :``%p``: root-relative path name of file being printed
615 :``%p``: root-relative path name of file being printed
616 """
616 """
617 ctx = repo[opts.get('rev')]
617 ctx = repo[opts.get('rev')]
618 err = 1
618 err = 1
619 m = cmdutil.match(repo, (file1,) + pats, opts)
619 m = cmdutil.match(repo, (file1,) + pats, opts)
620 for abs in ctx.walk(m):
620 for abs in ctx.walk(m):
621 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
621 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
622 data = ctx[abs].data()
622 data = ctx[abs].data()
623 if opts.get('decode'):
623 if opts.get('decode'):
624 data = repo.wwritedata(abs, data)
624 data = repo.wwritedata(abs, data)
625 fp.write(data)
625 fp.write(data)
626 err = 0
626 err = 0
627 return err
627 return err
628
628
629 def clone(ui, source, dest=None, **opts):
629 def clone(ui, source, dest=None, **opts):
630 """make a copy of an existing repository
630 """make a copy of an existing repository
631
631
632 Create a copy of an existing repository in a new directory.
632 Create a copy of an existing repository in a new directory.
633
633
634 If no destination directory name is specified, it defaults to the
634 If no destination directory name is specified, it defaults to the
635 basename of the source.
635 basename of the source.
636
636
637 The location of the source is added to the new repository's
637 The location of the source is added to the new repository's
638 .hg/hgrc file, as the default to be used for future pulls.
638 .hg/hgrc file, as the default to be used for future pulls.
639
639
640 See 'hg help urls' for valid source format details.
640 See 'hg help urls' for valid source format details.
641
641
642 It is possible to specify an ``ssh://`` URL as the destination, but no
642 It is possible to specify an ``ssh://`` URL as the destination, but no
643 .hg/hgrc and working directory will be created on the remote side.
643 .hg/hgrc and working directory will be created on the remote side.
644 Please see 'hg help urls' for important details about ``ssh://`` URLs.
644 Please see 'hg help urls' for important details about ``ssh://`` URLs.
645
645
646 A set of changesets (tags, or branch names) to pull may be specified
646 A set of changesets (tags, or branch names) to pull may be specified
647 by listing each changeset (tag, or branch name) with -r/--rev.
647 by listing each changeset (tag, or branch name) with -r/--rev.
648 If -r/--rev is used, the cloned repository will contain only a subset
648 If -r/--rev is used, the cloned repository will contain only a subset
649 of the changesets of the source repository. Only the set of changesets
649 of the changesets of the source repository. Only the set of changesets
650 defined by all -r/--rev options (including all their ancestors)
650 defined by all -r/--rev options (including all their ancestors)
651 will be pulled into the destination repository.
651 will be pulled into the destination repository.
652 No subsequent changesets (including subsequent tags) will be present
652 No subsequent changesets (including subsequent tags) will be present
653 in the destination.
653 in the destination.
654
654
655 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
655 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
656 local source repositories.
656 local source repositories.
657
657
658 For efficiency, hardlinks are used for cloning whenever the source
658 For efficiency, hardlinks are used for cloning whenever the source
659 and destination are on the same filesystem (note this applies only
659 and destination are on the same filesystem (note this applies only
660 to the repository data, not to the working directory). Some
660 to the repository data, not to the working directory). Some
661 filesystems, such as AFS, implement hardlinking incorrectly, but
661 filesystems, such as AFS, implement hardlinking incorrectly, but
662 do not report errors. In these cases, use the --pull option to
662 do not report errors. In these cases, use the --pull option to
663 avoid hardlinking.
663 avoid hardlinking.
664
664
665 In some cases, you can clone repositories and the working directory
665 In some cases, you can clone repositories and the working directory
666 using full hardlinks with ::
666 using full hardlinks with ::
667
667
668 $ cp -al REPO REPOCLONE
668 $ cp -al REPO REPOCLONE
669
669
670 This is the fastest way to clone, but it is not always safe. The
670 This is the fastest way to clone, but it is not always safe. The
671 operation is not atomic (making sure REPO is not modified during
671 operation is not atomic (making sure REPO is not modified during
672 the operation is up to you) and you have to make sure your editor
672 the operation is up to you) and you have to make sure your editor
673 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
673 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
674 this is not compatible with certain extensions that place their
674 this is not compatible with certain extensions that place their
675 metadata under the .hg directory, such as mq.
675 metadata under the .hg directory, such as mq.
676
676
677 Mercurial will update the working directory to the first applicable
677 Mercurial will update the working directory to the first applicable
678 revision from this list:
678 revision from this list:
679
679
680 a) null if -U or the source repository has no changesets
680 a) null if -U or the source repository has no changesets
681 b) if -u . and the source repository is local, the first parent of
681 b) if -u . and the source repository is local, the first parent of
682 the source repository's working directory
682 the source repository's working directory
683 c) the changeset specified with -u (if a branch name, this means the
683 c) the changeset specified with -u (if a branch name, this means the
684 latest head of that branch)
684 latest head of that branch)
685 d) the changeset specified with -r
685 d) the changeset specified with -r
686 e) the tipmost head specified with -b
686 e) the tipmost head specified with -b
687 f) the tipmost head specified with the url#branch source syntax
687 f) the tipmost head specified with the url#branch source syntax
688 g) the tipmost head of the default branch
688 g) the tipmost head of the default branch
689 h) tip
689 h) tip
690 """
690 """
691 if opts.get('noupdate') and opts.get('updaterev'):
691 if opts.get('noupdate') and opts.get('updaterev'):
692 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
692 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
693
693
694 hg.clone(cmdutil.remoteui(ui, opts), source, dest,
694 hg.clone(cmdutil.remoteui(ui, opts), source, dest,
695 pull=opts.get('pull'),
695 pull=opts.get('pull'),
696 stream=opts.get('uncompressed'),
696 stream=opts.get('uncompressed'),
697 rev=opts.get('rev'),
697 rev=opts.get('rev'),
698 update=opts.get('updaterev') or not opts.get('noupdate'),
698 update=opts.get('updaterev') or not opts.get('noupdate'),
699 branch=opts.get('branch'))
699 branch=opts.get('branch'))
700
700
701 def commit(ui, repo, *pats, **opts):
701 def commit(ui, repo, *pats, **opts):
702 """commit the specified files or all outstanding changes
702 """commit the specified files or all outstanding changes
703
703
704 Commit changes to the given files into the repository. Unlike a
704 Commit changes to the given files into the repository. Unlike a
705 centralized RCS, this operation is a local operation. See hg push
705 centralized RCS, this operation is a local operation. See hg push
706 for a way to actively distribute your changes.
706 for a way to actively distribute your changes.
707
707
708 If a list of files is omitted, all changes reported by "hg status"
708 If a list of files is omitted, all changes reported by "hg status"
709 will be committed.
709 will be committed.
710
710
711 If you are committing the result of a merge, do not provide any
711 If you are committing the result of a merge, do not provide any
712 filenames or -I/-X filters.
712 filenames or -I/-X filters.
713
713
714 If no commit message is specified, the configured editor is
714 If no commit message is specified, the configured editor is
715 started to prompt you for a message.
715 started to prompt you for a message.
716
716
717 See 'hg help dates' for a list of formats valid for -d/--date.
717 See 'hg help dates' for a list of formats valid for -d/--date.
718 """
718 """
719 extra = {}
719 extra = {}
720 if opts.get('close_branch'):
720 if opts.get('close_branch'):
721 extra['close'] = 1
721 extra['close'] = 1
722 e = cmdutil.commiteditor
722 e = cmdutil.commiteditor
723 if opts.get('force_editor'):
723 if opts.get('force_editor'):
724 e = cmdutil.commitforceeditor
724 e = cmdutil.commitforceeditor
725
725
726 def commitfunc(ui, repo, message, match, opts):
726 def commitfunc(ui, repo, message, match, opts):
727 return repo.commit(message, opts.get('user'), opts.get('date'), match,
727 return repo.commit(message, opts.get('user'), opts.get('date'), match,
728 editor=e, extra=extra)
728 editor=e, extra=extra)
729
729
730 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
730 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
731 if not node:
731 if not node:
732 ui.status(_("nothing changed\n"))
732 ui.status(_("nothing changed\n"))
733 return
733 return
734 cl = repo.changelog
734 cl = repo.changelog
735 rev = cl.rev(node)
735 rev = cl.rev(node)
736 parents = cl.parentrevs(rev)
736 parents = cl.parentrevs(rev)
737 if rev - 1 in parents:
737 if rev - 1 in parents:
738 # one of the parents was the old tip
738 # one of the parents was the old tip
739 pass
739 pass
740 elif (parents == (nullrev, nullrev) or
740 elif (parents == (nullrev, nullrev) or
741 len(cl.heads(cl.node(parents[0]))) > 1 and
741 len(cl.heads(cl.node(parents[0]))) > 1 and
742 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
742 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
743 ui.status(_('created new head\n'))
743 ui.status(_('created new head\n'))
744
744
745 if ui.debugflag:
745 if ui.debugflag:
746 ui.write(_('committed changeset %d:%s\n') % (rev, hex(node)))
746 ui.write(_('committed changeset %d:%s\n') % (rev, hex(node)))
747 elif ui.verbose:
747 elif ui.verbose:
748 ui.write(_('committed changeset %d:%s\n') % (rev, short(node)))
748 ui.write(_('committed changeset %d:%s\n') % (rev, short(node)))
749
749
750 def copy(ui, repo, *pats, **opts):
750 def copy(ui, repo, *pats, **opts):
751 """mark files as copied for the next commit
751 """mark files as copied for the next commit
752
752
753 Mark dest as having copies of source files. If dest is a
753 Mark dest as having copies of source files. If dest is a
754 directory, copies are put in that directory. If dest is a file,
754 directory, copies are put in that directory. If dest is a file,
755 the source must be a single file.
755 the source must be a single file.
756
756
757 By default, this command copies the contents of files as they
757 By default, this command copies the contents of files as they
758 exist in the working directory. If invoked with -A/--after, the
758 exist in the working directory. If invoked with -A/--after, the
759 operation is recorded, but no copying is performed.
759 operation is recorded, but no copying is performed.
760
760
761 This command takes effect with the next commit. To undo a copy
761 This command takes effect with the next commit. To undo a copy
762 before that, see hg revert.
762 before that, see hg revert.
763 """
763 """
764 wlock = repo.wlock(False)
764 wlock = repo.wlock(False)
765 try:
765 try:
766 return cmdutil.copy(ui, repo, pats, opts)
766 return cmdutil.copy(ui, repo, pats, opts)
767 finally:
767 finally:
768 wlock.release()
768 wlock.release()
769
769
770 def debugancestor(ui, repo, *args):
770 def debugancestor(ui, repo, *args):
771 """find the ancestor revision of two revisions in a given index"""
771 """find the ancestor revision of two revisions in a given index"""
772 if len(args) == 3:
772 if len(args) == 3:
773 index, rev1, rev2 = args
773 index, rev1, rev2 = args
774 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
774 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
775 lookup = r.lookup
775 lookup = r.lookup
776 elif len(args) == 2:
776 elif len(args) == 2:
777 if not repo:
777 if not repo:
778 raise util.Abort(_("There is no Mercurial repository here "
778 raise util.Abort(_("There is no Mercurial repository here "
779 "(.hg not found)"))
779 "(.hg not found)"))
780 rev1, rev2 = args
780 rev1, rev2 = args
781 r = repo.changelog
781 r = repo.changelog
782 lookup = repo.lookup
782 lookup = repo.lookup
783 else:
783 else:
784 raise util.Abort(_('either two or three arguments required'))
784 raise util.Abort(_('either two or three arguments required'))
785 a = r.ancestor(lookup(rev1), lookup(rev2))
785 a = r.ancestor(lookup(rev1), lookup(rev2))
786 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
786 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
787
787
788 def debugcommands(ui, cmd='', *args):
788 def debugcommands(ui, cmd='', *args):
789 for cmd, vals in sorted(table.iteritems()):
789 for cmd, vals in sorted(table.iteritems()):
790 cmd = cmd.split('|')[0].strip('^')
790 cmd = cmd.split('|')[0].strip('^')
791 opts = ', '.join([i[1] for i in vals[1]])
791 opts = ', '.join([i[1] for i in vals[1]])
792 ui.write('%s: %s\n' % (cmd, opts))
792 ui.write('%s: %s\n' % (cmd, opts))
793
793
794 def debugcomplete(ui, cmd='', **opts):
794 def debugcomplete(ui, cmd='', **opts):
795 """returns the completion list associated with the given command"""
795 """returns the completion list associated with the given command"""
796
796
797 if opts.get('options'):
797 if opts.get('options'):
798 options = []
798 options = []
799 otables = [globalopts]
799 otables = [globalopts]
800 if cmd:
800 if cmd:
801 aliases, entry = cmdutil.findcmd(cmd, table, False)
801 aliases, entry = cmdutil.findcmd(cmd, table, False)
802 otables.append(entry[1])
802 otables.append(entry[1])
803 for t in otables:
803 for t in otables:
804 for o in t:
804 for o in t:
805 if o[0]:
805 if o[0]:
806 options.append('-%s' % o[0])
806 options.append('-%s' % o[0])
807 options.append('--%s' % o[1])
807 options.append('--%s' % o[1])
808 ui.write("%s\n" % "\n".join(options))
808 ui.write("%s\n" % "\n".join(options))
809 return
809 return
810
810
811 cmdlist = cmdutil.findpossible(cmd, table)
811 cmdlist = cmdutil.findpossible(cmd, table)
812 if ui.verbose:
812 if ui.verbose:
813 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
813 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
814 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
814 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
815
815
816 def debugfsinfo(ui, path = "."):
816 def debugfsinfo(ui, path = "."):
817 open('.debugfsinfo', 'w').write('')
817 open('.debugfsinfo', 'w').write('')
818 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
818 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
819 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
819 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
820 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
820 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
821 and 'yes' or 'no'))
821 and 'yes' or 'no'))
822 os.unlink('.debugfsinfo')
822 os.unlink('.debugfsinfo')
823
823
824 def debugrebuildstate(ui, repo, rev="tip"):
824 def debugrebuildstate(ui, repo, rev="tip"):
825 """rebuild the dirstate as it would look like for the given revision"""
825 """rebuild the dirstate as it would look like for the given revision"""
826 ctx = repo[rev]
826 ctx = repo[rev]
827 wlock = repo.wlock()
827 wlock = repo.wlock()
828 try:
828 try:
829 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
829 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
830 finally:
830 finally:
831 wlock.release()
831 wlock.release()
832
832
833 def debugcheckstate(ui, repo):
833 def debugcheckstate(ui, repo):
834 """validate the correctness of the current dirstate"""
834 """validate the correctness of the current dirstate"""
835 parent1, parent2 = repo.dirstate.parents()
835 parent1, parent2 = repo.dirstate.parents()
836 m1 = repo[parent1].manifest()
836 m1 = repo[parent1].manifest()
837 m2 = repo[parent2].manifest()
837 m2 = repo[parent2].manifest()
838 errors = 0
838 errors = 0
839 for f in repo.dirstate:
839 for f in repo.dirstate:
840 state = repo.dirstate[f]
840 state = repo.dirstate[f]
841 if state in "nr" and f not in m1:
841 if state in "nr" and f not in m1:
842 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
842 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
843 errors += 1
843 errors += 1
844 if state in "a" and f in m1:
844 if state in "a" and f in m1:
845 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
845 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
846 errors += 1
846 errors += 1
847 if state in "m" and f not in m1 and f not in m2:
847 if state in "m" and f not in m1 and f not in m2:
848 ui.warn(_("%s in state %s, but not in either manifest\n") %
848 ui.warn(_("%s in state %s, but not in either manifest\n") %
849 (f, state))
849 (f, state))
850 errors += 1
850 errors += 1
851 for f in m1:
851 for f in m1:
852 state = repo.dirstate[f]
852 state = repo.dirstate[f]
853 if state not in "nrm":
853 if state not in "nrm":
854 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
854 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
855 errors += 1
855 errors += 1
856 if errors:
856 if errors:
857 error = _(".hg/dirstate inconsistent with current parent's manifest")
857 error = _(".hg/dirstate inconsistent with current parent's manifest")
858 raise util.Abort(error)
858 raise util.Abort(error)
859
859
860 def showconfig(ui, repo, *values, **opts):
860 def showconfig(ui, repo, *values, **opts):
861 """show combined config settings from all hgrc files
861 """show combined config settings from all hgrc files
862
862
863 With no arguments, print names and values of all config items.
863 With no arguments, print names and values of all config items.
864
864
865 With one argument of the form section.name, print just the value
865 With one argument of the form section.name, print just the value
866 of that config item.
866 of that config item.
867
867
868 With multiple arguments, print names and values of all config
868 With multiple arguments, print names and values of all config
869 items with matching section names.
869 items with matching section names.
870
870
871 With --debug, the source (filename and line number) is printed
871 With --debug, the source (filename and line number) is printed
872 for each config item.
872 for each config item.
873 """
873 """
874
874
875 untrusted = bool(opts.get('untrusted'))
875 untrusted = bool(opts.get('untrusted'))
876 if values:
876 if values:
877 if len([v for v in values if '.' in v]) > 1:
877 if len([v for v in values if '.' in v]) > 1:
878 raise util.Abort(_('only one config item permitted'))
878 raise util.Abort(_('only one config item permitted'))
879 for section, name, value in ui.walkconfig(untrusted=untrusted):
879 for section, name, value in ui.walkconfig(untrusted=untrusted):
880 sectname = section + '.' + name
880 sectname = section + '.' + name
881 if values:
881 if values:
882 for v in values:
882 for v in values:
883 if v == section:
883 if v == section:
884 ui.debug('%s: ' %
884 ui.debug('%s: ' %
885 ui.configsource(section, name, untrusted))
885 ui.configsource(section, name, untrusted))
886 ui.write('%s=%s\n' % (sectname, value))
886 ui.write('%s=%s\n' % (sectname, value))
887 elif v == sectname:
887 elif v == sectname:
888 ui.debug('%s: ' %
888 ui.debug('%s: ' %
889 ui.configsource(section, name, untrusted))
889 ui.configsource(section, name, untrusted))
890 ui.write(value, '\n')
890 ui.write(value, '\n')
891 else:
891 else:
892 ui.debug('%s: ' %
892 ui.debug('%s: ' %
893 ui.configsource(section, name, untrusted))
893 ui.configsource(section, name, untrusted))
894 ui.write('%s=%s\n' % (sectname, value))
894 ui.write('%s=%s\n' % (sectname, value))
895
895
896 def debugsetparents(ui, repo, rev1, rev2=None):
896 def debugsetparents(ui, repo, rev1, rev2=None):
897 """manually set the parents of the current working directory
897 """manually set the parents of the current working directory
898
898
899 This is useful for writing repository conversion tools, but should
899 This is useful for writing repository conversion tools, but should
900 be used with care.
900 be used with care.
901 """
901 """
902
902
903 if not rev2:
903 if not rev2:
904 rev2 = hex(nullid)
904 rev2 = hex(nullid)
905
905
906 wlock = repo.wlock()
906 wlock = repo.wlock()
907 try:
907 try:
908 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
908 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
909 finally:
909 finally:
910 wlock.release()
910 wlock.release()
911
911
912 def debugstate(ui, repo, nodates=None):
912 def debugstate(ui, repo, nodates=None):
913 """show the contents of the current dirstate"""
913 """show the contents of the current dirstate"""
914 timestr = ""
914 timestr = ""
915 showdate = not nodates
915 showdate = not nodates
916 for file_, ent in sorted(repo.dirstate._map.iteritems()):
916 for file_, ent in sorted(repo.dirstate._map.iteritems()):
917 if showdate:
917 if showdate:
918 if ent[3] == -1:
918 if ent[3] == -1:
919 # Pad or slice to locale representation
919 # Pad or slice to locale representation
920 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
920 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
921 time.localtime(0)))
921 time.localtime(0)))
922 timestr = 'unset'
922 timestr = 'unset'
923 timestr = (timestr[:locale_len] +
923 timestr = (timestr[:locale_len] +
924 ' ' * (locale_len - len(timestr)))
924 ' ' * (locale_len - len(timestr)))
925 else:
925 else:
926 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
926 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
927 time.localtime(ent[3]))
927 time.localtime(ent[3]))
928 if ent[1] & 020000:
928 if ent[1] & 020000:
929 mode = 'lnk'
929 mode = 'lnk'
930 else:
930 else:
931 mode = '%3o' % (ent[1] & 0777)
931 mode = '%3o' % (ent[1] & 0777)
932 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
932 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
933 for f in repo.dirstate.copies():
933 for f in repo.dirstate.copies():
934 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
934 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
935
935
936 def debugsub(ui, repo, rev=None):
936 def debugsub(ui, repo, rev=None):
937 if rev == '':
937 if rev == '':
938 rev = None
938 rev = None
939 for k, v in sorted(repo[rev].substate.items()):
939 for k, v in sorted(repo[rev].substate.items()):
940 ui.write('path %s\n' % k)
940 ui.write('path %s\n' % k)
941 ui.write(' source %s\n' % v[0])
941 ui.write(' source %s\n' % v[0])
942 ui.write(' revision %s\n' % v[1])
942 ui.write(' revision %s\n' % v[1])
943
943
944 def debugdata(ui, file_, rev):
944 def debugdata(ui, file_, rev):
945 """dump the contents of a data file revision"""
945 """dump the contents of a data file revision"""
946 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
946 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
947 try:
947 try:
948 ui.write(r.revision(r.lookup(rev)))
948 ui.write(r.revision(r.lookup(rev)))
949 except KeyError:
949 except KeyError:
950 raise util.Abort(_('invalid revision identifier %s') % rev)
950 raise util.Abort(_('invalid revision identifier %s') % rev)
951
951
952 def debugdate(ui, date, range=None, **opts):
952 def debugdate(ui, date, range=None, **opts):
953 """parse and display a date"""
953 """parse and display a date"""
954 if opts["extended"]:
954 if opts["extended"]:
955 d = util.parsedate(date, util.extendeddateformats)
955 d = util.parsedate(date, util.extendeddateformats)
956 else:
956 else:
957 d = util.parsedate(date)
957 d = util.parsedate(date)
958 ui.write("internal: %s %s\n" % d)
958 ui.write("internal: %s %s\n" % d)
959 ui.write("standard: %s\n" % util.datestr(d))
959 ui.write("standard: %s\n" % util.datestr(d))
960 if range:
960 if range:
961 m = util.matchdate(range)
961 m = util.matchdate(range)
962 ui.write("match: %s\n" % m(d[0]))
962 ui.write("match: %s\n" % m(d[0]))
963
963
964 def debugindex(ui, file_):
964 def debugindex(ui, file_):
965 """dump the contents of an index file"""
965 """dump the contents of an index file"""
966 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
966 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
967 ui.write(" rev offset length base linkrev"
967 ui.write(" rev offset length base linkrev"
968 " nodeid p1 p2\n")
968 " nodeid p1 p2\n")
969 for i in r:
969 for i in r:
970 node = r.node(i)
970 node = r.node(i)
971 try:
971 try:
972 pp = r.parents(node)
972 pp = r.parents(node)
973 except:
973 except:
974 pp = [nullid, nullid]
974 pp = [nullid, nullid]
975 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
975 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
976 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
976 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
977 short(node), short(pp[0]), short(pp[1])))
977 short(node), short(pp[0]), short(pp[1])))
978
978
979 def debugindexdot(ui, file_):
979 def debugindexdot(ui, file_):
980 """dump an index DAG as a graphviz dot file"""
980 """dump an index DAG as a graphviz dot file"""
981 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
981 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
982 ui.write("digraph G {\n")
982 ui.write("digraph G {\n")
983 for i in r:
983 for i in r:
984 node = r.node(i)
984 node = r.node(i)
985 pp = r.parents(node)
985 pp = r.parents(node)
986 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
986 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
987 if pp[1] != nullid:
987 if pp[1] != nullid:
988 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
988 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
989 ui.write("}\n")
989 ui.write("}\n")
990
990
991 def debuginstall(ui):
991 def debuginstall(ui):
992 '''test Mercurial installation'''
992 '''test Mercurial installation'''
993
993
994 def writetemp(contents):
994 def writetemp(contents):
995 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
995 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
996 f = os.fdopen(fd, "wb")
996 f = os.fdopen(fd, "wb")
997 f.write(contents)
997 f.write(contents)
998 f.close()
998 f.close()
999 return name
999 return name
1000
1000
1001 problems = 0
1001 problems = 0
1002
1002
1003 # encoding
1003 # encoding
1004 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1004 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1005 try:
1005 try:
1006 encoding.fromlocal("test")
1006 encoding.fromlocal("test")
1007 except util.Abort, inst:
1007 except util.Abort, inst:
1008 ui.write(" %s\n" % inst)
1008 ui.write(" %s\n" % inst)
1009 ui.write(_(" (check that your locale is properly set)\n"))
1009 ui.write(_(" (check that your locale is properly set)\n"))
1010 problems += 1
1010 problems += 1
1011
1011
1012 # compiled modules
1012 # compiled modules
1013 ui.status(_("Checking extensions...\n"))
1013 ui.status(_("Checking extensions...\n"))
1014 try:
1014 try:
1015 import bdiff, mpatch, base85
1015 import bdiff, mpatch, base85
1016 except Exception, inst:
1016 except Exception, inst:
1017 ui.write(" %s\n" % inst)
1017 ui.write(" %s\n" % inst)
1018 ui.write(_(" One or more extensions could not be found"))
1018 ui.write(_(" One or more extensions could not be found"))
1019 ui.write(_(" (check that you compiled the extensions)\n"))
1019 ui.write(_(" (check that you compiled the extensions)\n"))
1020 problems += 1
1020 problems += 1
1021
1021
1022 # templates
1022 # templates
1023 ui.status(_("Checking templates...\n"))
1023 ui.status(_("Checking templates...\n"))
1024 try:
1024 try:
1025 import templater
1025 import templater
1026 templater.templater(templater.templatepath("map-cmdline.default"))
1026 templater.templater(templater.templatepath("map-cmdline.default"))
1027 except Exception, inst:
1027 except Exception, inst:
1028 ui.write(" %s\n" % inst)
1028 ui.write(" %s\n" % inst)
1029 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1029 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1030 problems += 1
1030 problems += 1
1031
1031
1032 # patch
1032 # patch
1033 ui.status(_("Checking patch...\n"))
1033 ui.status(_("Checking patch...\n"))
1034 patchproblems = 0
1034 patchproblems = 0
1035 a = "1\n2\n3\n4\n"
1035 a = "1\n2\n3\n4\n"
1036 b = "1\n2\n3\ninsert\n4\n"
1036 b = "1\n2\n3\ninsert\n4\n"
1037 fa = writetemp(a)
1037 fa = writetemp(a)
1038 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
1038 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
1039 os.path.basename(fa))
1039 os.path.basename(fa))
1040 fd = writetemp(d)
1040 fd = writetemp(d)
1041
1041
1042 files = {}
1042 files = {}
1043 try:
1043 try:
1044 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
1044 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
1045 except util.Abort, e:
1045 except util.Abort, e:
1046 ui.write(_(" patch call failed:\n"))
1046 ui.write(_(" patch call failed:\n"))
1047 ui.write(" " + str(e) + "\n")
1047 ui.write(" " + str(e) + "\n")
1048 patchproblems += 1
1048 patchproblems += 1
1049 else:
1049 else:
1050 if list(files) != [os.path.basename(fa)]:
1050 if list(files) != [os.path.basename(fa)]:
1051 ui.write(_(" unexpected patch output!\n"))
1051 ui.write(_(" unexpected patch output!\n"))
1052 patchproblems += 1
1052 patchproblems += 1
1053 a = open(fa).read()
1053 a = open(fa).read()
1054 if a != b:
1054 if a != b:
1055 ui.write(_(" patch test failed!\n"))
1055 ui.write(_(" patch test failed!\n"))
1056 patchproblems += 1
1056 patchproblems += 1
1057
1057
1058 if patchproblems:
1058 if patchproblems:
1059 if ui.config('ui', 'patch'):
1059 if ui.config('ui', 'patch'):
1060 ui.write(_(" (Current patch tool may be incompatible with patch,"
1060 ui.write(_(" (Current patch tool may be incompatible with patch,"
1061 " or misconfigured. Please check your .hgrc file)\n"))
1061 " or misconfigured. Please check your .hgrc file)\n"))
1062 else:
1062 else:
1063 ui.write(_(" Internal patcher failure, please report this error"
1063 ui.write(_(" Internal patcher failure, please report this error"
1064 " to http://mercurial.selenic.com/bts/\n"))
1064 " to http://mercurial.selenic.com/bts/\n"))
1065 problems += patchproblems
1065 problems += patchproblems
1066
1066
1067 os.unlink(fa)
1067 os.unlink(fa)
1068 os.unlink(fd)
1068 os.unlink(fd)
1069
1069
1070 # editor
1070 # editor
1071 ui.status(_("Checking commit editor...\n"))
1071 ui.status(_("Checking commit editor...\n"))
1072 editor = ui.geteditor()
1072 editor = ui.geteditor()
1073 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1073 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1074 if not cmdpath:
1074 if not cmdpath:
1075 if editor == 'vi':
1075 if editor == 'vi':
1076 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1076 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1077 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1077 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1078 else:
1078 else:
1079 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1079 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1080 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1080 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1081 problems += 1
1081 problems += 1
1082
1082
1083 # check username
1083 # check username
1084 ui.status(_("Checking username...\n"))
1084 ui.status(_("Checking username...\n"))
1085 try:
1085 try:
1086 user = ui.username()
1086 user = ui.username()
1087 except util.Abort, e:
1087 except util.Abort, e:
1088 ui.write(" %s\n" % e)
1088 ui.write(" %s\n" % e)
1089 ui.write(_(" (specify a username in your .hgrc file)\n"))
1089 ui.write(_(" (specify a username in your .hgrc file)\n"))
1090 problems += 1
1090 problems += 1
1091
1091
1092 if not problems:
1092 if not problems:
1093 ui.status(_("No problems detected\n"))
1093 ui.status(_("No problems detected\n"))
1094 else:
1094 else:
1095 ui.write(_("%s problems detected,"
1095 ui.write(_("%s problems detected,"
1096 " please check your install!\n") % problems)
1096 " please check your install!\n") % problems)
1097
1097
1098 return problems
1098 return problems
1099
1099
1100 def debugrename(ui, repo, file1, *pats, **opts):
1100 def debugrename(ui, repo, file1, *pats, **opts):
1101 """dump rename information"""
1101 """dump rename information"""
1102
1102
1103 ctx = repo[opts.get('rev')]
1103 ctx = repo[opts.get('rev')]
1104 m = cmdutil.match(repo, (file1,) + pats, opts)
1104 m = cmdutil.match(repo, (file1,) + pats, opts)
1105 for abs in ctx.walk(m):
1105 for abs in ctx.walk(m):
1106 fctx = ctx[abs]
1106 fctx = ctx[abs]
1107 o = fctx.filelog().renamed(fctx.filenode())
1107 o = fctx.filelog().renamed(fctx.filenode())
1108 rel = m.rel(abs)
1108 rel = m.rel(abs)
1109 if o:
1109 if o:
1110 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1110 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1111 else:
1111 else:
1112 ui.write(_("%s not renamed\n") % rel)
1112 ui.write(_("%s not renamed\n") % rel)
1113
1113
1114 def debugwalk(ui, repo, *pats, **opts):
1114 def debugwalk(ui, repo, *pats, **opts):
1115 """show how files match on given patterns"""
1115 """show how files match on given patterns"""
1116 m = cmdutil.match(repo, pats, opts)
1116 m = cmdutil.match(repo, pats, opts)
1117 items = list(repo.walk(m))
1117 items = list(repo.walk(m))
1118 if not items:
1118 if not items:
1119 return
1119 return
1120 fmt = 'f %%-%ds %%-%ds %%s' % (
1120 fmt = 'f %%-%ds %%-%ds %%s' % (
1121 max([len(abs) for abs in items]),
1121 max([len(abs) for abs in items]),
1122 max([len(m.rel(abs)) for abs in items]))
1122 max([len(m.rel(abs)) for abs in items]))
1123 for abs in items:
1123 for abs in items:
1124 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1124 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1125 ui.write("%s\n" % line.rstrip())
1125 ui.write("%s\n" % line.rstrip())
1126
1126
1127 def diff(ui, repo, *pats, **opts):
1127 def diff(ui, repo, *pats, **opts):
1128 """diff repository (or selected files)
1128 """diff repository (or selected files)
1129
1129
1130 Show differences between revisions for the specified files.
1130 Show differences between revisions for the specified files.
1131
1131
1132 Differences between files are shown using the unified diff format.
1132 Differences between files are shown using the unified diff format.
1133
1133
1134 NOTE: diff may generate unexpected results for merges, as it will
1134 NOTE: diff may generate unexpected results for merges, as it will
1135 default to comparing against the working directory's first parent
1135 default to comparing against the working directory's first parent
1136 changeset if no revisions are specified.
1136 changeset if no revisions are specified.
1137
1137
1138 When two revision arguments are given, then changes are shown
1138 When two revision arguments are given, then changes are shown
1139 between those revisions. If only one revision is specified then
1139 between those revisions. If only one revision is specified then
1140 that revision is compared to the working directory, and, when no
1140 that revision is compared to the working directory, and, when no
1141 revisions are specified, the working directory files are compared
1141 revisions are specified, the working directory files are compared
1142 to its parent.
1142 to its parent.
1143
1143
1144 Alternatively you can specify -c/--change with a revision to see
1144 Alternatively you can specify -c/--change with a revision to see
1145 the changes in that changeset relative to its first parent.
1145 the changes in that changeset relative to its first parent.
1146
1146
1147 Without the -a/--text option, diff will avoid generating diffs of
1147 Without the -a/--text option, diff will avoid generating diffs of
1148 files it detects as binary. With -a, diff will generate a diff
1148 files it detects as binary. With -a, diff will generate a diff
1149 anyway, probably with undesirable results.
1149 anyway, probably with undesirable results.
1150
1150
1151 Use the -g/--git option to generate diffs in the git extended diff
1151 Use the -g/--git option to generate diffs in the git extended diff
1152 format. For more information, read 'hg help diffs'.
1152 format. For more information, read 'hg help diffs'.
1153 """
1153 """
1154
1154
1155 revs = opts.get('rev')
1155 revs = opts.get('rev')
1156 change = opts.get('change')
1156 change = opts.get('change')
1157 stat = opts.get('stat')
1157 stat = opts.get('stat')
1158 reverse = opts.get('reverse')
1158 reverse = opts.get('reverse')
1159
1159
1160 if revs and change:
1160 if revs and change:
1161 msg = _('cannot specify --rev and --change at the same time')
1161 msg = _('cannot specify --rev and --change at the same time')
1162 raise util.Abort(msg)
1162 raise util.Abort(msg)
1163 elif change:
1163 elif change:
1164 node2 = repo.lookup(change)
1164 node2 = repo.lookup(change)
1165 node1 = repo[node2].parents()[0].node()
1165 node1 = repo[node2].parents()[0].node()
1166 else:
1166 else:
1167 node1, node2 = cmdutil.revpair(repo, revs)
1167 node1, node2 = cmdutil.revpair(repo, revs)
1168
1168
1169 if reverse:
1169 if reverse:
1170 node1, node2 = node2, node1
1170 node1, node2 = node2, node1
1171
1171
1172 if stat:
1172 if stat:
1173 opts['unified'] = '0'
1173 opts['unified'] = '0'
1174 diffopts = patch.diffopts(ui, opts)
1174 diffopts = patch.diffopts(ui, opts)
1175
1175
1176 m = cmdutil.match(repo, pats, opts)
1176 m = cmdutil.match(repo, pats, opts)
1177 if stat:
1177 it = patch.diff(repo, node1, node2, match=m, opts=diffopts)
1178 it = patch.diff(repo, node1, node2, match=m, opts=diffopts)
1178 if stat:
1179 width = ui.interactive() and util.termwidth() or 80
1179 width = ui.interactive() and util.termwidth() or 80
1180 ui.write(patch.diffstat(util.iterlines(it), width=width,
1180 for chunk, label in patch.diffstatui(util.iterlines(it), width=width,
1181 git=diffopts.git))
1181 git=diffopts.git):
1182 ui.write(chunk, label=label)
1182 else:
1183 else:
1183 for chunk in it:
1184 it = patch.diffui(repo, node1, node2, match=m, opts=diffopts)
1184 ui.write(chunk)
1185 for chunk, label in it:
1186 ui.write(chunk, label=label)
1185
1187
1186 def export(ui, repo, *changesets, **opts):
1188 def export(ui, repo, *changesets, **opts):
1187 """dump the header and diffs for one or more changesets
1189 """dump the header and diffs for one or more changesets
1188
1190
1189 Print the changeset header and diffs for one or more revisions.
1191 Print the changeset header and diffs for one or more revisions.
1190
1192
1191 The information shown in the changeset header is: author, date,
1193 The information shown in the changeset header is: author, date,
1192 branch name (if non-default), changeset hash, parent(s) and commit
1194 branch name (if non-default), changeset hash, parent(s) and commit
1193 comment.
1195 comment.
1194
1196
1195 NOTE: export may generate unexpected diff output for merge
1197 NOTE: export may generate unexpected diff output for merge
1196 changesets, as it will compare the merge changeset against its
1198 changesets, as it will compare the merge changeset against its
1197 first parent only.
1199 first parent only.
1198
1200
1199 Output may be to a file, in which case the name of the file is
1201 Output may be to a file, in which case the name of the file is
1200 given using a format string. The formatting rules are as follows:
1202 given using a format string. The formatting rules are as follows:
1201
1203
1202 :``%%``: literal "%" character
1204 :``%%``: literal "%" character
1203 :``%H``: changeset hash (40 bytes of hexadecimal)
1205 :``%H``: changeset hash (40 bytes of hexadecimal)
1204 :``%N``: number of patches being generated
1206 :``%N``: number of patches being generated
1205 :``%R``: changeset revision number
1207 :``%R``: changeset revision number
1206 :``%b``: basename of the exporting repository
1208 :``%b``: basename of the exporting repository
1207 :``%h``: short-form changeset hash (12 bytes of hexadecimal)
1209 :``%h``: short-form changeset hash (12 bytes of hexadecimal)
1208 :``%n``: zero-padded sequence number, starting at 1
1210 :``%n``: zero-padded sequence number, starting at 1
1209 :``%r``: zero-padded changeset revision number
1211 :``%r``: zero-padded changeset revision number
1210
1212
1211 Without the -a/--text option, export will avoid generating diffs
1213 Without the -a/--text option, export will avoid generating diffs
1212 of files it detects as binary. With -a, export will generate a
1214 of files it detects as binary. With -a, export will generate a
1213 diff anyway, probably with undesirable results.
1215 diff anyway, probably with undesirable results.
1214
1216
1215 Use the -g/--git option to generate diffs in the git extended diff
1217 Use the -g/--git option to generate diffs in the git extended diff
1216 format. See 'hg help diffs' for more information.
1218 format. See 'hg help diffs' for more information.
1217
1219
1218 With the --switch-parent option, the diff will be against the
1220 With the --switch-parent option, the diff will be against the
1219 second parent. It can be useful to review a merge.
1221 second parent. It can be useful to review a merge.
1220 """
1222 """
1221 changesets += tuple(opts.get('rev', []))
1223 changesets += tuple(opts.get('rev', []))
1222 if not changesets:
1224 if not changesets:
1223 raise util.Abort(_("export requires at least one changeset"))
1225 raise util.Abort(_("export requires at least one changeset"))
1224 revs = cmdutil.revrange(repo, changesets)
1226 revs = cmdutil.revrange(repo, changesets)
1225 if len(revs) > 1:
1227 if len(revs) > 1:
1226 ui.note(_('exporting patches:\n'))
1228 ui.note(_('exporting patches:\n'))
1227 else:
1229 else:
1228 ui.note(_('exporting patch:\n'))
1230 ui.note(_('exporting patch:\n'))
1229 cmdutil.export(repo, revs, template=opts.get('output'),
1231 cmdutil.export(repo, revs, template=opts.get('output'),
1230 switch_parent=opts.get('switch_parent'),
1232 switch_parent=opts.get('switch_parent'),
1231 opts=patch.diffopts(ui, opts))
1233 opts=patch.diffopts(ui, opts))
1232
1234
1233 def forget(ui, repo, *pats, **opts):
1235 def forget(ui, repo, *pats, **opts):
1234 """forget the specified files on the next commit
1236 """forget the specified files on the next commit
1235
1237
1236 Mark the specified files so they will no longer be tracked
1238 Mark the specified files so they will no longer be tracked
1237 after the next commit.
1239 after the next commit.
1238
1240
1239 This only removes files from the current branch, not from the
1241 This only removes files from the current branch, not from the
1240 entire project history, and it does not delete them from the
1242 entire project history, and it does not delete them from the
1241 working directory.
1243 working directory.
1242
1244
1243 To undo a forget before the next commit, see hg add.
1245 To undo a forget before the next commit, see hg add.
1244 """
1246 """
1245
1247
1246 if not pats:
1248 if not pats:
1247 raise util.Abort(_('no files specified'))
1249 raise util.Abort(_('no files specified'))
1248
1250
1249 m = cmdutil.match(repo, pats, opts)
1251 m = cmdutil.match(repo, pats, opts)
1250 s = repo.status(match=m, clean=True)
1252 s = repo.status(match=m, clean=True)
1251 forget = sorted(s[0] + s[1] + s[3] + s[6])
1253 forget = sorted(s[0] + s[1] + s[3] + s[6])
1252
1254
1253 for f in m.files():
1255 for f in m.files():
1254 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1256 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1255 ui.warn(_('not removing %s: file is already untracked\n')
1257 ui.warn(_('not removing %s: file is already untracked\n')
1256 % m.rel(f))
1258 % m.rel(f))
1257
1259
1258 for f in forget:
1260 for f in forget:
1259 if ui.verbose or not m.exact(f):
1261 if ui.verbose or not m.exact(f):
1260 ui.status(_('removing %s\n') % m.rel(f))
1262 ui.status(_('removing %s\n') % m.rel(f))
1261
1263
1262 repo.remove(forget, unlink=False)
1264 repo.remove(forget, unlink=False)
1263
1265
1264 def grep(ui, repo, pattern, *pats, **opts):
1266 def grep(ui, repo, pattern, *pats, **opts):
1265 """search for a pattern in specified files and revisions
1267 """search for a pattern in specified files and revisions
1266
1268
1267 Search revisions of files for a regular expression.
1269 Search revisions of files for a regular expression.
1268
1270
1269 This command behaves differently than Unix grep. It only accepts
1271 This command behaves differently than Unix grep. It only accepts
1270 Python/Perl regexps. It searches repository history, not the
1272 Python/Perl regexps. It searches repository history, not the
1271 working directory. It always prints the revision number in which a
1273 working directory. It always prints the revision number in which a
1272 match appears.
1274 match appears.
1273
1275
1274 By default, grep only prints output for the first revision of a
1276 By default, grep only prints output for the first revision of a
1275 file in which it finds a match. To get it to print every revision
1277 file in which it finds a match. To get it to print every revision
1276 that contains a change in match status ("-" for a match that
1278 that contains a change in match status ("-" for a match that
1277 becomes a non-match, or "+" for a non-match that becomes a match),
1279 becomes a non-match, or "+" for a non-match that becomes a match),
1278 use the --all flag.
1280 use the --all flag.
1279 """
1281 """
1280 reflags = 0
1282 reflags = 0
1281 if opts.get('ignore_case'):
1283 if opts.get('ignore_case'):
1282 reflags |= re.I
1284 reflags |= re.I
1283 try:
1285 try:
1284 regexp = re.compile(pattern, reflags)
1286 regexp = re.compile(pattern, reflags)
1285 except Exception, inst:
1287 except Exception, inst:
1286 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1288 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1287 return None
1289 return None
1288 sep, eol = ':', '\n'
1290 sep, eol = ':', '\n'
1289 if opts.get('print0'):
1291 if opts.get('print0'):
1290 sep = eol = '\0'
1292 sep = eol = '\0'
1291
1293
1292 getfile = util.lrucachefunc(repo.file)
1294 getfile = util.lrucachefunc(repo.file)
1293
1295
1294 def matchlines(body):
1296 def matchlines(body):
1295 begin = 0
1297 begin = 0
1296 linenum = 0
1298 linenum = 0
1297 while True:
1299 while True:
1298 match = regexp.search(body, begin)
1300 match = regexp.search(body, begin)
1299 if not match:
1301 if not match:
1300 break
1302 break
1301 mstart, mend = match.span()
1303 mstart, mend = match.span()
1302 linenum += body.count('\n', begin, mstart) + 1
1304 linenum += body.count('\n', begin, mstart) + 1
1303 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1305 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1304 begin = body.find('\n', mend) + 1 or len(body)
1306 begin = body.find('\n', mend) + 1 or len(body)
1305 lend = begin - 1
1307 lend = begin - 1
1306 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1308 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1307
1309
1308 class linestate(object):
1310 class linestate(object):
1309 def __init__(self, line, linenum, colstart, colend):
1311 def __init__(self, line, linenum, colstart, colend):
1310 self.line = line
1312 self.line = line
1311 self.linenum = linenum
1313 self.linenum = linenum
1312 self.colstart = colstart
1314 self.colstart = colstart
1313 self.colend = colend
1315 self.colend = colend
1314
1316
1315 def __hash__(self):
1317 def __hash__(self):
1316 return hash((self.linenum, self.line))
1318 return hash((self.linenum, self.line))
1317
1319
1318 def __eq__(self, other):
1320 def __eq__(self, other):
1319 return self.line == other.line
1321 return self.line == other.line
1320
1322
1321 matches = {}
1323 matches = {}
1322 copies = {}
1324 copies = {}
1323 def grepbody(fn, rev, body):
1325 def grepbody(fn, rev, body):
1324 matches[rev].setdefault(fn, [])
1326 matches[rev].setdefault(fn, [])
1325 m = matches[rev][fn]
1327 m = matches[rev][fn]
1326 for lnum, cstart, cend, line in matchlines(body):
1328 for lnum, cstart, cend, line in matchlines(body):
1327 s = linestate(line, lnum, cstart, cend)
1329 s = linestate(line, lnum, cstart, cend)
1328 m.append(s)
1330 m.append(s)
1329
1331
1330 def difflinestates(a, b):
1332 def difflinestates(a, b):
1331 sm = difflib.SequenceMatcher(None, a, b)
1333 sm = difflib.SequenceMatcher(None, a, b)
1332 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1334 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1333 if tag == 'insert':
1335 if tag == 'insert':
1334 for i in xrange(blo, bhi):
1336 for i in xrange(blo, bhi):
1335 yield ('+', b[i])
1337 yield ('+', b[i])
1336 elif tag == 'delete':
1338 elif tag == 'delete':
1337 for i in xrange(alo, ahi):
1339 for i in xrange(alo, ahi):
1338 yield ('-', a[i])
1340 yield ('-', a[i])
1339 elif tag == 'replace':
1341 elif tag == 'replace':
1340 for i in xrange(alo, ahi):
1342 for i in xrange(alo, ahi):
1341 yield ('-', a[i])
1343 yield ('-', a[i])
1342 for i in xrange(blo, bhi):
1344 for i in xrange(blo, bhi):
1343 yield ('+', b[i])
1345 yield ('+', b[i])
1344
1346
1345 def display(fn, ctx, pstates, states):
1347 def display(fn, ctx, pstates, states):
1346 rev = ctx.rev()
1348 rev = ctx.rev()
1347 datefunc = ui.quiet and util.shortdate or util.datestr
1349 datefunc = ui.quiet and util.shortdate or util.datestr
1348 found = False
1350 found = False
1349 filerevmatches = {}
1351 filerevmatches = {}
1350 if opts.get('all'):
1352 if opts.get('all'):
1351 iter = difflinestates(pstates, states)
1353 iter = difflinestates(pstates, states)
1352 else:
1354 else:
1353 iter = [('', l) for l in states]
1355 iter = [('', l) for l in states]
1354 for change, l in iter:
1356 for change, l in iter:
1355 cols = [fn, str(rev)]
1357 cols = [fn, str(rev)]
1356 before, match, after = None, None, None
1358 before, match, after = None, None, None
1357 if opts.get('line_number'):
1359 if opts.get('line_number'):
1358 cols.append(str(l.linenum))
1360 cols.append(str(l.linenum))
1359 if opts.get('all'):
1361 if opts.get('all'):
1360 cols.append(change)
1362 cols.append(change)
1361 if opts.get('user'):
1363 if opts.get('user'):
1362 cols.append(ui.shortuser(ctx.user()))
1364 cols.append(ui.shortuser(ctx.user()))
1363 if opts.get('date'):
1365 if opts.get('date'):
1364 cols.append(datefunc(ctx.date()))
1366 cols.append(datefunc(ctx.date()))
1365 if opts.get('files_with_matches'):
1367 if opts.get('files_with_matches'):
1366 c = (fn, rev)
1368 c = (fn, rev)
1367 if c in filerevmatches:
1369 if c in filerevmatches:
1368 continue
1370 continue
1369 filerevmatches[c] = 1
1371 filerevmatches[c] = 1
1370 else:
1372 else:
1371 before = l.line[:l.colstart]
1373 before = l.line[:l.colstart]
1372 match = l.line[l.colstart:l.colend]
1374 match = l.line[l.colstart:l.colend]
1373 after = l.line[l.colend:]
1375 after = l.line[l.colend:]
1374 ui.write(sep.join(cols))
1376 ui.write(sep.join(cols))
1375 if before is not None:
1377 if before is not None:
1376 ui.write(sep + before)
1378 ui.write(sep + before)
1377 ui.write(match, label='grep.match')
1379 ui.write(match, label='grep.match')
1378 ui.write(after)
1380 ui.write(after)
1379 ui.write(eol)
1381 ui.write(eol)
1380 found = True
1382 found = True
1381 return found
1383 return found
1382
1384
1383 skip = {}
1385 skip = {}
1384 revfiles = {}
1386 revfiles = {}
1385 matchfn = cmdutil.match(repo, pats, opts)
1387 matchfn = cmdutil.match(repo, pats, opts)
1386 found = False
1388 found = False
1387 follow = opts.get('follow')
1389 follow = opts.get('follow')
1388
1390
1389 def prep(ctx, fns):
1391 def prep(ctx, fns):
1390 rev = ctx.rev()
1392 rev = ctx.rev()
1391 pctx = ctx.parents()[0]
1393 pctx = ctx.parents()[0]
1392 parent = pctx.rev()
1394 parent = pctx.rev()
1393 matches.setdefault(rev, {})
1395 matches.setdefault(rev, {})
1394 matches.setdefault(parent, {})
1396 matches.setdefault(parent, {})
1395 files = revfiles.setdefault(rev, [])
1397 files = revfiles.setdefault(rev, [])
1396 for fn in fns:
1398 for fn in fns:
1397 flog = getfile(fn)
1399 flog = getfile(fn)
1398 try:
1400 try:
1399 fnode = ctx.filenode(fn)
1401 fnode = ctx.filenode(fn)
1400 except error.LookupError:
1402 except error.LookupError:
1401 continue
1403 continue
1402
1404
1403 copied = flog.renamed(fnode)
1405 copied = flog.renamed(fnode)
1404 copy = follow and copied and copied[0]
1406 copy = follow and copied and copied[0]
1405 if copy:
1407 if copy:
1406 copies.setdefault(rev, {})[fn] = copy
1408 copies.setdefault(rev, {})[fn] = copy
1407 if fn in skip:
1409 if fn in skip:
1408 if copy:
1410 if copy:
1409 skip[copy] = True
1411 skip[copy] = True
1410 continue
1412 continue
1411 files.append(fn)
1413 files.append(fn)
1412
1414
1413 if fn not in matches[rev]:
1415 if fn not in matches[rev]:
1414 grepbody(fn, rev, flog.read(fnode))
1416 grepbody(fn, rev, flog.read(fnode))
1415
1417
1416 pfn = copy or fn
1418 pfn = copy or fn
1417 if pfn not in matches[parent]:
1419 if pfn not in matches[parent]:
1418 try:
1420 try:
1419 fnode = pctx.filenode(pfn)
1421 fnode = pctx.filenode(pfn)
1420 grepbody(pfn, parent, flog.read(fnode))
1422 grepbody(pfn, parent, flog.read(fnode))
1421 except error.LookupError:
1423 except error.LookupError:
1422 pass
1424 pass
1423
1425
1424 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
1426 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
1425 rev = ctx.rev()
1427 rev = ctx.rev()
1426 parent = ctx.parents()[0].rev()
1428 parent = ctx.parents()[0].rev()
1427 for fn in sorted(revfiles.get(rev, [])):
1429 for fn in sorted(revfiles.get(rev, [])):
1428 states = matches[rev][fn]
1430 states = matches[rev][fn]
1429 copy = copies.get(rev, {}).get(fn)
1431 copy = copies.get(rev, {}).get(fn)
1430 if fn in skip:
1432 if fn in skip:
1431 if copy:
1433 if copy:
1432 skip[copy] = True
1434 skip[copy] = True
1433 continue
1435 continue
1434 pstates = matches.get(parent, {}).get(copy or fn, [])
1436 pstates = matches.get(parent, {}).get(copy or fn, [])
1435 if pstates or states:
1437 if pstates or states:
1436 r = display(fn, ctx, pstates, states)
1438 r = display(fn, ctx, pstates, states)
1437 found = found or r
1439 found = found or r
1438 if r and not opts.get('all'):
1440 if r and not opts.get('all'):
1439 skip[fn] = True
1441 skip[fn] = True
1440 if copy:
1442 if copy:
1441 skip[copy] = True
1443 skip[copy] = True
1442 del matches[rev]
1444 del matches[rev]
1443 del revfiles[rev]
1445 del revfiles[rev]
1444
1446
1445 def heads(ui, repo, *branchrevs, **opts):
1447 def heads(ui, repo, *branchrevs, **opts):
1446 """show current repository heads or show branch heads
1448 """show current repository heads or show branch heads
1447
1449
1448 With no arguments, show all repository branch heads.
1450 With no arguments, show all repository branch heads.
1449
1451
1450 Repository "heads" are changesets with no child changesets. They are
1452 Repository "heads" are changesets with no child changesets. They are
1451 where development generally takes place and are the usual targets
1453 where development generally takes place and are the usual targets
1452 for update and merge operations. Branch heads are changesets that have
1454 for update and merge operations. Branch heads are changesets that have
1453 no child changeset on the same branch.
1455 no child changeset on the same branch.
1454
1456
1455 If one or more REVs are given, only branch heads on the branches
1457 If one or more REVs are given, only branch heads on the branches
1456 associated with the specified changesets are shown.
1458 associated with the specified changesets are shown.
1457
1459
1458 If -c/--closed is specified, also show branch heads marked closed
1460 If -c/--closed is specified, also show branch heads marked closed
1459 (see hg commit --close-branch).
1461 (see hg commit --close-branch).
1460
1462
1461 If STARTREV is specified, only those heads that are descendants of
1463 If STARTREV is specified, only those heads that are descendants of
1462 STARTREV will be displayed.
1464 STARTREV will be displayed.
1463
1465
1464 If -t/--topo is specified, named branch mechanics will be ignored and only
1466 If -t/--topo is specified, named branch mechanics will be ignored and only
1465 changesets without children will be shown.
1467 changesets without children will be shown.
1466 """
1468 """
1467
1469
1468 if opts.get('rev'):
1470 if opts.get('rev'):
1469 start = repo.lookup(opts['rev'])
1471 start = repo.lookup(opts['rev'])
1470 else:
1472 else:
1471 start = None
1473 start = None
1472
1474
1473 if opts.get('topo'):
1475 if opts.get('topo'):
1474 heads = [repo[h] for h in repo.heads(start)]
1476 heads = [repo[h] for h in repo.heads(start)]
1475 else:
1477 else:
1476 heads = []
1478 heads = []
1477 for b, ls in repo.branchmap().iteritems():
1479 for b, ls in repo.branchmap().iteritems():
1478 if start is None:
1480 if start is None:
1479 heads += [repo[h] for h in ls]
1481 heads += [repo[h] for h in ls]
1480 continue
1482 continue
1481 startrev = repo.changelog.rev(start)
1483 startrev = repo.changelog.rev(start)
1482 descendants = set(repo.changelog.descendants(startrev))
1484 descendants = set(repo.changelog.descendants(startrev))
1483 descendants.add(startrev)
1485 descendants.add(startrev)
1484 rev = repo.changelog.rev
1486 rev = repo.changelog.rev
1485 heads += [repo[h] for h in ls if rev(h) in descendants]
1487 heads += [repo[h] for h in ls if rev(h) in descendants]
1486
1488
1487 if branchrevs:
1489 if branchrevs:
1488 decode, encode = encoding.fromlocal, encoding.tolocal
1490 decode, encode = encoding.fromlocal, encoding.tolocal
1489 branches = set(repo[decode(br)].branch() for br in branchrevs)
1491 branches = set(repo[decode(br)].branch() for br in branchrevs)
1490 heads = [h for h in heads if h.branch() in branches]
1492 heads = [h for h in heads if h.branch() in branches]
1491
1493
1492 if not opts.get('closed'):
1494 if not opts.get('closed'):
1493 heads = [h for h in heads if not h.extra().get('close')]
1495 heads = [h for h in heads if not h.extra().get('close')]
1494
1496
1495 if opts.get('active') and branchrevs:
1497 if opts.get('active') and branchrevs:
1496 dagheads = repo.heads(start)
1498 dagheads = repo.heads(start)
1497 heads = [h for h in heads if h.node() in dagheads]
1499 heads = [h for h in heads if h.node() in dagheads]
1498
1500
1499 if branchrevs:
1501 if branchrevs:
1500 haveheads = set(h.branch() for h in heads)
1502 haveheads = set(h.branch() for h in heads)
1501 if branches - haveheads:
1503 if branches - haveheads:
1502 headless = ', '.join(encode(b) for b in branches - haveheads)
1504 headless = ', '.join(encode(b) for b in branches - haveheads)
1503 msg = _('no open branch heads found on branches %s')
1505 msg = _('no open branch heads found on branches %s')
1504 if opts.get('rev'):
1506 if opts.get('rev'):
1505 msg += _(' (started at %s)' % opts['rev'])
1507 msg += _(' (started at %s)' % opts['rev'])
1506 ui.warn((msg + '\n') % headless)
1508 ui.warn((msg + '\n') % headless)
1507
1509
1508 if not heads:
1510 if not heads:
1509 return 1
1511 return 1
1510
1512
1511 heads = sorted(heads, key=lambda x: -x.rev())
1513 heads = sorted(heads, key=lambda x: -x.rev())
1512 displayer = cmdutil.show_changeset(ui, repo, opts)
1514 displayer = cmdutil.show_changeset(ui, repo, opts)
1513 for ctx in heads:
1515 for ctx in heads:
1514 displayer.show(ctx)
1516 displayer.show(ctx)
1515 displayer.close()
1517 displayer.close()
1516
1518
1517 def help_(ui, name=None, with_version=False, unknowncmd=False):
1519 def help_(ui, name=None, with_version=False, unknowncmd=False):
1518 """show help for a given topic or a help overview
1520 """show help for a given topic or a help overview
1519
1521
1520 With no arguments, print a list of commands with short help messages.
1522 With no arguments, print a list of commands with short help messages.
1521
1523
1522 Given a topic, extension, or command name, print help for that
1524 Given a topic, extension, or command name, print help for that
1523 topic."""
1525 topic."""
1524 option_lists = []
1526 option_lists = []
1525 textwidth = util.termwidth() - 2
1527 textwidth = util.termwidth() - 2
1526
1528
1527 def addglobalopts(aliases):
1529 def addglobalopts(aliases):
1528 if ui.verbose:
1530 if ui.verbose:
1529 option_lists.append((_("global options:"), globalopts))
1531 option_lists.append((_("global options:"), globalopts))
1530 if name == 'shortlist':
1532 if name == 'shortlist':
1531 option_lists.append((_('use "hg help" for the full list '
1533 option_lists.append((_('use "hg help" for the full list '
1532 'of commands'), ()))
1534 'of commands'), ()))
1533 else:
1535 else:
1534 if name == 'shortlist':
1536 if name == 'shortlist':
1535 msg = _('use "hg help" for the full list of commands '
1537 msg = _('use "hg help" for the full list of commands '
1536 'or "hg -v" for details')
1538 'or "hg -v" for details')
1537 elif aliases:
1539 elif aliases:
1538 msg = _('use "hg -v help%s" to show aliases and '
1540 msg = _('use "hg -v help%s" to show aliases and '
1539 'global options') % (name and " " + name or "")
1541 'global options') % (name and " " + name or "")
1540 else:
1542 else:
1541 msg = _('use "hg -v help %s" to show global options') % name
1543 msg = _('use "hg -v help %s" to show global options') % name
1542 option_lists.append((msg, ()))
1544 option_lists.append((msg, ()))
1543
1545
1544 def helpcmd(name):
1546 def helpcmd(name):
1545 if with_version:
1547 if with_version:
1546 version_(ui)
1548 version_(ui)
1547 ui.write('\n')
1549 ui.write('\n')
1548
1550
1549 try:
1551 try:
1550 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
1552 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
1551 except error.AmbiguousCommand, inst:
1553 except error.AmbiguousCommand, inst:
1552 # py3k fix: except vars can't be used outside the scope of the
1554 # py3k fix: except vars can't be used outside the scope of the
1553 # except block, nor can be used inside a lambda. python issue4617
1555 # except block, nor can be used inside a lambda. python issue4617
1554 prefix = inst.args[0]
1556 prefix = inst.args[0]
1555 select = lambda c: c.lstrip('^').startswith(prefix)
1557 select = lambda c: c.lstrip('^').startswith(prefix)
1556 helplist(_('list of commands:\n\n'), select)
1558 helplist(_('list of commands:\n\n'), select)
1557 return
1559 return
1558
1560
1559 # check if it's an invalid alias and display its error if it is
1561 # check if it's an invalid alias and display its error if it is
1560 if getattr(entry[0], 'badalias', False):
1562 if getattr(entry[0], 'badalias', False):
1561 if not unknowncmd:
1563 if not unknowncmd:
1562 entry[0](ui)
1564 entry[0](ui)
1563 return
1565 return
1564
1566
1565 # synopsis
1567 # synopsis
1566 if len(entry) > 2:
1568 if len(entry) > 2:
1567 if entry[2].startswith('hg'):
1569 if entry[2].startswith('hg'):
1568 ui.write("%s\n" % entry[2])
1570 ui.write("%s\n" % entry[2])
1569 else:
1571 else:
1570 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
1572 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
1571 else:
1573 else:
1572 ui.write('hg %s\n' % aliases[0])
1574 ui.write('hg %s\n' % aliases[0])
1573
1575
1574 # aliases
1576 # aliases
1575 if not ui.quiet and len(aliases) > 1:
1577 if not ui.quiet and len(aliases) > 1:
1576 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1578 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1577
1579
1578 # description
1580 # description
1579 doc = gettext(entry[0].__doc__)
1581 doc = gettext(entry[0].__doc__)
1580 if not doc:
1582 if not doc:
1581 doc = _("(no help text available)")
1583 doc = _("(no help text available)")
1582 if hasattr(entry[0], 'definition'): # aliased command
1584 if hasattr(entry[0], 'definition'): # aliased command
1583 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
1585 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
1584 if ui.quiet:
1586 if ui.quiet:
1585 doc = doc.splitlines()[0]
1587 doc = doc.splitlines()[0]
1586 keep = ui.verbose and ['verbose'] or []
1588 keep = ui.verbose and ['verbose'] or []
1587 formatted, pruned = minirst.format(doc, textwidth, keep=keep)
1589 formatted, pruned = minirst.format(doc, textwidth, keep=keep)
1588 ui.write("\n%s\n" % formatted)
1590 ui.write("\n%s\n" % formatted)
1589 if pruned:
1591 if pruned:
1590 ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
1592 ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
1591
1593
1592 if not ui.quiet:
1594 if not ui.quiet:
1593 # options
1595 # options
1594 if entry[1]:
1596 if entry[1]:
1595 option_lists.append((_("options:\n"), entry[1]))
1597 option_lists.append((_("options:\n"), entry[1]))
1596
1598
1597 addglobalopts(False)
1599 addglobalopts(False)
1598
1600
1599 def helplist(header, select=None):
1601 def helplist(header, select=None):
1600 h = {}
1602 h = {}
1601 cmds = {}
1603 cmds = {}
1602 for c, e in table.iteritems():
1604 for c, e in table.iteritems():
1603 f = c.split("|", 1)[0]
1605 f = c.split("|", 1)[0]
1604 if select and not select(f):
1606 if select and not select(f):
1605 continue
1607 continue
1606 if (not select and name != 'shortlist' and
1608 if (not select and name != 'shortlist' and
1607 e[0].__module__ != __name__):
1609 e[0].__module__ != __name__):
1608 continue
1610 continue
1609 if name == "shortlist" and not f.startswith("^"):
1611 if name == "shortlist" and not f.startswith("^"):
1610 continue
1612 continue
1611 f = f.lstrip("^")
1613 f = f.lstrip("^")
1612 if not ui.debugflag and f.startswith("debug"):
1614 if not ui.debugflag and f.startswith("debug"):
1613 continue
1615 continue
1614 doc = e[0].__doc__
1616 doc = e[0].__doc__
1615 if doc and 'DEPRECATED' in doc and not ui.verbose:
1617 if doc and 'DEPRECATED' in doc and not ui.verbose:
1616 continue
1618 continue
1617 doc = gettext(doc)
1619 doc = gettext(doc)
1618 if not doc:
1620 if not doc:
1619 doc = _("(no help text available)")
1621 doc = _("(no help text available)")
1620 h[f] = doc.splitlines()[0].rstrip()
1622 h[f] = doc.splitlines()[0].rstrip()
1621 cmds[f] = c.lstrip("^")
1623 cmds[f] = c.lstrip("^")
1622
1624
1623 if not h:
1625 if not h:
1624 ui.status(_('no commands defined\n'))
1626 ui.status(_('no commands defined\n'))
1625 return
1627 return
1626
1628
1627 ui.status(header)
1629 ui.status(header)
1628 fns = sorted(h)
1630 fns = sorted(h)
1629 m = max(map(len, fns))
1631 m = max(map(len, fns))
1630 for f in fns:
1632 for f in fns:
1631 if ui.verbose:
1633 if ui.verbose:
1632 commands = cmds[f].replace("|",", ")
1634 commands = cmds[f].replace("|",", ")
1633 ui.write(" %s:\n %s\n"%(commands, h[f]))
1635 ui.write(" %s:\n %s\n"%(commands, h[f]))
1634 else:
1636 else:
1635 ui.write(' %-*s %s\n' % (m, f, util.wrap(h[f], m + 4)))
1637 ui.write(' %-*s %s\n' % (m, f, util.wrap(h[f], m + 4)))
1636
1638
1637 if not ui.quiet:
1639 if not ui.quiet:
1638 addglobalopts(True)
1640 addglobalopts(True)
1639
1641
1640 def helptopic(name):
1642 def helptopic(name):
1641 for names, header, doc in help.helptable:
1643 for names, header, doc in help.helptable:
1642 if name in names:
1644 if name in names:
1643 break
1645 break
1644 else:
1646 else:
1645 raise error.UnknownCommand(name)
1647 raise error.UnknownCommand(name)
1646
1648
1647 # description
1649 # description
1648 if not doc:
1650 if not doc:
1649 doc = _("(no help text available)")
1651 doc = _("(no help text available)")
1650 if hasattr(doc, '__call__'):
1652 if hasattr(doc, '__call__'):
1651 doc = doc()
1653 doc = doc()
1652
1654
1653 ui.write("%s\n\n" % header)
1655 ui.write("%s\n\n" % header)
1654 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
1656 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
1655
1657
1656 def helpext(name):
1658 def helpext(name):
1657 try:
1659 try:
1658 mod = extensions.find(name)
1660 mod = extensions.find(name)
1659 doc = gettext(mod.__doc__) or _('no help text available')
1661 doc = gettext(mod.__doc__) or _('no help text available')
1660 except KeyError:
1662 except KeyError:
1661 mod = None
1663 mod = None
1662 doc = extensions.disabledext(name)
1664 doc = extensions.disabledext(name)
1663 if not doc:
1665 if not doc:
1664 raise error.UnknownCommand(name)
1666 raise error.UnknownCommand(name)
1665
1667
1666 if '\n' not in doc:
1668 if '\n' not in doc:
1667 head, tail = doc, ""
1669 head, tail = doc, ""
1668 else:
1670 else:
1669 head, tail = doc.split('\n', 1)
1671 head, tail = doc.split('\n', 1)
1670 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
1672 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
1671 if tail:
1673 if tail:
1672 ui.write(minirst.format(tail, textwidth))
1674 ui.write(minirst.format(tail, textwidth))
1673 ui.status('\n\n')
1675 ui.status('\n\n')
1674
1676
1675 if mod:
1677 if mod:
1676 try:
1678 try:
1677 ct = mod.cmdtable
1679 ct = mod.cmdtable
1678 except AttributeError:
1680 except AttributeError:
1679 ct = {}
1681 ct = {}
1680 modcmds = set([c.split('|', 1)[0] for c in ct])
1682 modcmds = set([c.split('|', 1)[0] for c in ct])
1681 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1683 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1682 else:
1684 else:
1683 ui.write(_('use "hg help extensions" for information on enabling '
1685 ui.write(_('use "hg help extensions" for information on enabling '
1684 'extensions\n'))
1686 'extensions\n'))
1685
1687
1686 def helpextcmd(name):
1688 def helpextcmd(name):
1687 cmd, ext, mod = extensions.disabledcmd(name, ui.config('ui', 'strict'))
1689 cmd, ext, mod = extensions.disabledcmd(name, ui.config('ui', 'strict'))
1688 doc = gettext(mod.__doc__).splitlines()[0]
1690 doc = gettext(mod.__doc__).splitlines()[0]
1689
1691
1690 msg = help.listexts(_("'%s' is provided by the following "
1692 msg = help.listexts(_("'%s' is provided by the following "
1691 "extension:") % cmd, {ext: doc}, len(ext),
1693 "extension:") % cmd, {ext: doc}, len(ext),
1692 indent=4)
1694 indent=4)
1693 ui.write(minirst.format(msg, textwidth))
1695 ui.write(minirst.format(msg, textwidth))
1694 ui.write('\n\n')
1696 ui.write('\n\n')
1695 ui.write(_('use "hg help extensions" for information on enabling '
1697 ui.write(_('use "hg help extensions" for information on enabling '
1696 'extensions\n'))
1698 'extensions\n'))
1697
1699
1698 if name and name != 'shortlist':
1700 if name and name != 'shortlist':
1699 i = None
1701 i = None
1700 if unknowncmd:
1702 if unknowncmd:
1701 queries = (helpextcmd,)
1703 queries = (helpextcmd,)
1702 else:
1704 else:
1703 queries = (helptopic, helpcmd, helpext, helpextcmd)
1705 queries = (helptopic, helpcmd, helpext, helpextcmd)
1704 for f in queries:
1706 for f in queries:
1705 try:
1707 try:
1706 f(name)
1708 f(name)
1707 i = None
1709 i = None
1708 break
1710 break
1709 except error.UnknownCommand, inst:
1711 except error.UnknownCommand, inst:
1710 i = inst
1712 i = inst
1711 if i:
1713 if i:
1712 raise i
1714 raise i
1713
1715
1714 else:
1716 else:
1715 # program name
1717 # program name
1716 if ui.verbose or with_version:
1718 if ui.verbose or with_version:
1717 version_(ui)
1719 version_(ui)
1718 else:
1720 else:
1719 ui.status(_("Mercurial Distributed SCM\n"))
1721 ui.status(_("Mercurial Distributed SCM\n"))
1720 ui.status('\n')
1722 ui.status('\n')
1721
1723
1722 # list of commands
1724 # list of commands
1723 if name == "shortlist":
1725 if name == "shortlist":
1724 header = _('basic commands:\n\n')
1726 header = _('basic commands:\n\n')
1725 else:
1727 else:
1726 header = _('list of commands:\n\n')
1728 header = _('list of commands:\n\n')
1727
1729
1728 helplist(header)
1730 helplist(header)
1729 if name != 'shortlist':
1731 if name != 'shortlist':
1730 exts, maxlength = extensions.enabled()
1732 exts, maxlength = extensions.enabled()
1731 text = help.listexts(_('enabled extensions:'), exts, maxlength)
1733 text = help.listexts(_('enabled extensions:'), exts, maxlength)
1732 if text:
1734 if text:
1733 ui.write("\n%s\n" % minirst.format(text, textwidth))
1735 ui.write("\n%s\n" % minirst.format(text, textwidth))
1734
1736
1735 # list all option lists
1737 # list all option lists
1736 opt_output = []
1738 opt_output = []
1737 for title, options in option_lists:
1739 for title, options in option_lists:
1738 opt_output.append(("\n%s" % title, None))
1740 opt_output.append(("\n%s" % title, None))
1739 for shortopt, longopt, default, desc in options:
1741 for shortopt, longopt, default, desc in options:
1740 if _("DEPRECATED") in desc and not ui.verbose:
1742 if _("DEPRECATED") in desc and not ui.verbose:
1741 continue
1743 continue
1742 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1744 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1743 longopt and " --%s" % longopt),
1745 longopt and " --%s" % longopt),
1744 "%s%s" % (desc,
1746 "%s%s" % (desc,
1745 default
1747 default
1746 and _(" (default: %s)") % default
1748 and _(" (default: %s)") % default
1747 or "")))
1749 or "")))
1748
1750
1749 if not name:
1751 if not name:
1750 ui.write(_("\nadditional help topics:\n\n"))
1752 ui.write(_("\nadditional help topics:\n\n"))
1751 topics = []
1753 topics = []
1752 for names, header, doc in help.helptable:
1754 for names, header, doc in help.helptable:
1753 topics.append((sorted(names, key=len, reverse=True)[0], header))
1755 topics.append((sorted(names, key=len, reverse=True)[0], header))
1754 topics_len = max([len(s[0]) for s in topics])
1756 topics_len = max([len(s[0]) for s in topics])
1755 for t, desc in topics:
1757 for t, desc in topics:
1756 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1758 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1757
1759
1758 if opt_output:
1760 if opt_output:
1759 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1761 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1760 for first, second in opt_output:
1762 for first, second in opt_output:
1761 if second:
1763 if second:
1762 second = util.wrap(second, opts_len + 3)
1764 second = util.wrap(second, opts_len + 3)
1763 ui.write(" %-*s %s\n" % (opts_len, first, second))
1765 ui.write(" %-*s %s\n" % (opts_len, first, second))
1764 else:
1766 else:
1765 ui.write("%s\n" % first)
1767 ui.write("%s\n" % first)
1766
1768
1767 def identify(ui, repo, source=None,
1769 def identify(ui, repo, source=None,
1768 rev=None, num=None, id=None, branch=None, tags=None):
1770 rev=None, num=None, id=None, branch=None, tags=None):
1769 """identify the working copy or specified revision
1771 """identify the working copy or specified revision
1770
1772
1771 With no revision, print a summary of the current state of the
1773 With no revision, print a summary of the current state of the
1772 repository.
1774 repository.
1773
1775
1774 Specifying a path to a repository root or Mercurial bundle will
1776 Specifying a path to a repository root or Mercurial bundle will
1775 cause lookup to operate on that repository/bundle.
1777 cause lookup to operate on that repository/bundle.
1776
1778
1777 This summary identifies the repository state using one or two
1779 This summary identifies the repository state using one or two
1778 parent hash identifiers, followed by a "+" if there are
1780 parent hash identifiers, followed by a "+" if there are
1779 uncommitted changes in the working directory, a list of tags for
1781 uncommitted changes in the working directory, a list of tags for
1780 this revision and a branch name for non-default branches.
1782 this revision and a branch name for non-default branches.
1781 """
1783 """
1782
1784
1783 if not repo and not source:
1785 if not repo and not source:
1784 raise util.Abort(_("There is no Mercurial repository here "
1786 raise util.Abort(_("There is no Mercurial repository here "
1785 "(.hg not found)"))
1787 "(.hg not found)"))
1786
1788
1787 hexfunc = ui.debugflag and hex or short
1789 hexfunc = ui.debugflag and hex or short
1788 default = not (num or id or branch or tags)
1790 default = not (num or id or branch or tags)
1789 output = []
1791 output = []
1790
1792
1791 revs = []
1793 revs = []
1792 if source:
1794 if source:
1793 source, branches = hg.parseurl(ui.expandpath(source))
1795 source, branches = hg.parseurl(ui.expandpath(source))
1794 repo = hg.repository(ui, source)
1796 repo = hg.repository(ui, source)
1795 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
1797 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
1796
1798
1797 if not repo.local():
1799 if not repo.local():
1798 if not rev and revs:
1800 if not rev and revs:
1799 rev = revs[0]
1801 rev = revs[0]
1800 if not rev:
1802 if not rev:
1801 rev = "tip"
1803 rev = "tip"
1802 if num or branch or tags:
1804 if num or branch or tags:
1803 raise util.Abort(
1805 raise util.Abort(
1804 "can't query remote revision number, branch, or tags")
1806 "can't query remote revision number, branch, or tags")
1805 output = [hexfunc(repo.lookup(rev))]
1807 output = [hexfunc(repo.lookup(rev))]
1806 elif not rev:
1808 elif not rev:
1807 ctx = repo[None]
1809 ctx = repo[None]
1808 parents = ctx.parents()
1810 parents = ctx.parents()
1809 changed = False
1811 changed = False
1810 if default or id or num:
1812 if default or id or num:
1811 changed = util.any(repo.status())
1813 changed = util.any(repo.status())
1812 if default or id:
1814 if default or id:
1813 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1815 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1814 (changed) and "+" or "")]
1816 (changed) and "+" or "")]
1815 if num:
1817 if num:
1816 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1818 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1817 (changed) and "+" or ""))
1819 (changed) and "+" or ""))
1818 else:
1820 else:
1819 ctx = repo[rev]
1821 ctx = repo[rev]
1820 if default or id:
1822 if default or id:
1821 output = [hexfunc(ctx.node())]
1823 output = [hexfunc(ctx.node())]
1822 if num:
1824 if num:
1823 output.append(str(ctx.rev()))
1825 output.append(str(ctx.rev()))
1824
1826
1825 if repo.local() and default and not ui.quiet:
1827 if repo.local() and default and not ui.quiet:
1826 b = encoding.tolocal(ctx.branch())
1828 b = encoding.tolocal(ctx.branch())
1827 if b != 'default':
1829 if b != 'default':
1828 output.append("(%s)" % b)
1830 output.append("(%s)" % b)
1829
1831
1830 # multiple tags for a single parent separated by '/'
1832 # multiple tags for a single parent separated by '/'
1831 t = "/".join(ctx.tags())
1833 t = "/".join(ctx.tags())
1832 if t:
1834 if t:
1833 output.append(t)
1835 output.append(t)
1834
1836
1835 if branch:
1837 if branch:
1836 output.append(encoding.tolocal(ctx.branch()))
1838 output.append(encoding.tolocal(ctx.branch()))
1837
1839
1838 if tags:
1840 if tags:
1839 output.extend(ctx.tags())
1841 output.extend(ctx.tags())
1840
1842
1841 ui.write("%s\n" % ' '.join(output))
1843 ui.write("%s\n" % ' '.join(output))
1842
1844
1843 def import_(ui, repo, patch1, *patches, **opts):
1845 def import_(ui, repo, patch1, *patches, **opts):
1844 """import an ordered set of patches
1846 """import an ordered set of patches
1845
1847
1846 Import a list of patches and commit them individually (unless
1848 Import a list of patches and commit them individually (unless
1847 --no-commit is specified).
1849 --no-commit is specified).
1848
1850
1849 If there are outstanding changes in the working directory, import
1851 If there are outstanding changes in the working directory, import
1850 will abort unless given the -f/--force flag.
1852 will abort unless given the -f/--force flag.
1851
1853
1852 You can import a patch straight from a mail message. Even patches
1854 You can import a patch straight from a mail message. Even patches
1853 as attachments work (to use the body part, it must have type
1855 as attachments work (to use the body part, it must have type
1854 text/plain or text/x-patch). From and Subject headers of email
1856 text/plain or text/x-patch). From and Subject headers of email
1855 message are used as default committer and commit message. All
1857 message are used as default committer and commit message. All
1856 text/plain body parts before first diff are added to commit
1858 text/plain body parts before first diff are added to commit
1857 message.
1859 message.
1858
1860
1859 If the imported patch was generated by hg export, user and
1861 If the imported patch was generated by hg export, user and
1860 description from patch override values from message headers and
1862 description from patch override values from message headers and
1861 body. Values given on command line with -m/--message and -u/--user
1863 body. Values given on command line with -m/--message and -u/--user
1862 override these.
1864 override these.
1863
1865
1864 If --exact is specified, import will set the working directory to
1866 If --exact is specified, import will set the working directory to
1865 the parent of each patch before applying it, and will abort if the
1867 the parent of each patch before applying it, and will abort if the
1866 resulting changeset has a different ID than the one recorded in
1868 resulting changeset has a different ID than the one recorded in
1867 the patch. This may happen due to character set problems or other
1869 the patch. This may happen due to character set problems or other
1868 deficiencies in the text patch format.
1870 deficiencies in the text patch format.
1869
1871
1870 With -s/--similarity, hg will attempt to discover renames and
1872 With -s/--similarity, hg will attempt to discover renames and
1871 copies in the patch in the same way as 'addremove'.
1873 copies in the patch in the same way as 'addremove'.
1872
1874
1873 To read a patch from standard input, use "-" as the patch name. If
1875 To read a patch from standard input, use "-" as the patch name. If
1874 a URL is specified, the patch will be downloaded from it.
1876 a URL is specified, the patch will be downloaded from it.
1875 See 'hg help dates' for a list of formats valid for -d/--date.
1877 See 'hg help dates' for a list of formats valid for -d/--date.
1876 """
1878 """
1877 patches = (patch1,) + patches
1879 patches = (patch1,) + patches
1878
1880
1879 date = opts.get('date')
1881 date = opts.get('date')
1880 if date:
1882 if date:
1881 opts['date'] = util.parsedate(date)
1883 opts['date'] = util.parsedate(date)
1882
1884
1883 try:
1885 try:
1884 sim = float(opts.get('similarity') or 0)
1886 sim = float(opts.get('similarity') or 0)
1885 except ValueError:
1887 except ValueError:
1886 raise util.Abort(_('similarity must be a number'))
1888 raise util.Abort(_('similarity must be a number'))
1887 if sim < 0 or sim > 100:
1889 if sim < 0 or sim > 100:
1888 raise util.Abort(_('similarity must be between 0 and 100'))
1890 raise util.Abort(_('similarity must be between 0 and 100'))
1889
1891
1890 if opts.get('exact') or not opts.get('force'):
1892 if opts.get('exact') or not opts.get('force'):
1891 cmdutil.bail_if_changed(repo)
1893 cmdutil.bail_if_changed(repo)
1892
1894
1893 d = opts["base"]
1895 d = opts["base"]
1894 strip = opts["strip"]
1896 strip = opts["strip"]
1895 wlock = lock = None
1897 wlock = lock = None
1896
1898
1897 def tryone(ui, hunk):
1899 def tryone(ui, hunk):
1898 tmpname, message, user, date, branch, nodeid, p1, p2 = \
1900 tmpname, message, user, date, branch, nodeid, p1, p2 = \
1899 patch.extract(ui, hunk)
1901 patch.extract(ui, hunk)
1900
1902
1901 if not tmpname:
1903 if not tmpname:
1902 return None
1904 return None
1903 commitid = _('to working directory')
1905 commitid = _('to working directory')
1904
1906
1905 try:
1907 try:
1906 cmdline_message = cmdutil.logmessage(opts)
1908 cmdline_message = cmdutil.logmessage(opts)
1907 if cmdline_message:
1909 if cmdline_message:
1908 # pickup the cmdline msg
1910 # pickup the cmdline msg
1909 message = cmdline_message
1911 message = cmdline_message
1910 elif message:
1912 elif message:
1911 # pickup the patch msg
1913 # pickup the patch msg
1912 message = message.strip()
1914 message = message.strip()
1913 else:
1915 else:
1914 # launch the editor
1916 # launch the editor
1915 message = None
1917 message = None
1916 ui.debug('message:\n%s\n' % message)
1918 ui.debug('message:\n%s\n' % message)
1917
1919
1918 wp = repo.parents()
1920 wp = repo.parents()
1919 if opts.get('exact'):
1921 if opts.get('exact'):
1920 if not nodeid or not p1:
1922 if not nodeid or not p1:
1921 raise util.Abort(_('not a Mercurial patch'))
1923 raise util.Abort(_('not a Mercurial patch'))
1922 p1 = repo.lookup(p1)
1924 p1 = repo.lookup(p1)
1923 p2 = repo.lookup(p2 or hex(nullid))
1925 p2 = repo.lookup(p2 or hex(nullid))
1924
1926
1925 if p1 != wp[0].node():
1927 if p1 != wp[0].node():
1926 hg.clean(repo, p1)
1928 hg.clean(repo, p1)
1927 repo.dirstate.setparents(p1, p2)
1929 repo.dirstate.setparents(p1, p2)
1928 elif p2:
1930 elif p2:
1929 try:
1931 try:
1930 p1 = repo.lookup(p1)
1932 p1 = repo.lookup(p1)
1931 p2 = repo.lookup(p2)
1933 p2 = repo.lookup(p2)
1932 if p1 == wp[0].node():
1934 if p1 == wp[0].node():
1933 repo.dirstate.setparents(p1, p2)
1935 repo.dirstate.setparents(p1, p2)
1934 except error.RepoError:
1936 except error.RepoError:
1935 pass
1937 pass
1936 if opts.get('exact') or opts.get('import_branch'):
1938 if opts.get('exact') or opts.get('import_branch'):
1937 repo.dirstate.setbranch(branch or 'default')
1939 repo.dirstate.setbranch(branch or 'default')
1938
1940
1939 files = {}
1941 files = {}
1940 try:
1942 try:
1941 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1943 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1942 files=files, eolmode=None)
1944 files=files, eolmode=None)
1943 finally:
1945 finally:
1944 files = patch.updatedir(ui, repo, files,
1946 files = patch.updatedir(ui, repo, files,
1945 similarity=sim / 100.0)
1947 similarity=sim / 100.0)
1946 if not opts.get('no_commit'):
1948 if not opts.get('no_commit'):
1947 if opts.get('exact'):
1949 if opts.get('exact'):
1948 m = None
1950 m = None
1949 else:
1951 else:
1950 m = cmdutil.matchfiles(repo, files or [])
1952 m = cmdutil.matchfiles(repo, files or [])
1951 n = repo.commit(message, opts.get('user') or user,
1953 n = repo.commit(message, opts.get('user') or user,
1952 opts.get('date') or date, match=m,
1954 opts.get('date') or date, match=m,
1953 editor=cmdutil.commiteditor)
1955 editor=cmdutil.commiteditor)
1954 if opts.get('exact'):
1956 if opts.get('exact'):
1955 if hex(n) != nodeid:
1957 if hex(n) != nodeid:
1956 repo.rollback()
1958 repo.rollback()
1957 raise util.Abort(_('patch is damaged'
1959 raise util.Abort(_('patch is damaged'
1958 ' or loses information'))
1960 ' or loses information'))
1959 # Force a dirstate write so that the next transaction
1961 # Force a dirstate write so that the next transaction
1960 # backups an up-do-date file.
1962 # backups an up-do-date file.
1961 repo.dirstate.write()
1963 repo.dirstate.write()
1962 if n:
1964 if n:
1963 commitid = short(n)
1965 commitid = short(n)
1964
1966
1965 return commitid
1967 return commitid
1966 finally:
1968 finally:
1967 os.unlink(tmpname)
1969 os.unlink(tmpname)
1968
1970
1969 try:
1971 try:
1970 wlock = repo.wlock()
1972 wlock = repo.wlock()
1971 lock = repo.lock()
1973 lock = repo.lock()
1972 lastcommit = None
1974 lastcommit = None
1973 for p in patches:
1975 for p in patches:
1974 pf = os.path.join(d, p)
1976 pf = os.path.join(d, p)
1975
1977
1976 if pf == '-':
1978 if pf == '-':
1977 ui.status(_("applying patch from stdin\n"))
1979 ui.status(_("applying patch from stdin\n"))
1978 pf = sys.stdin
1980 pf = sys.stdin
1979 else:
1981 else:
1980 ui.status(_("applying %s\n") % p)
1982 ui.status(_("applying %s\n") % p)
1981 pf = url.open(ui, pf)
1983 pf = url.open(ui, pf)
1982
1984
1983 haspatch = False
1985 haspatch = False
1984 for hunk in patch.split(pf):
1986 for hunk in patch.split(pf):
1985 commitid = tryone(ui, hunk)
1987 commitid = tryone(ui, hunk)
1986 if commitid:
1988 if commitid:
1987 haspatch = True
1989 haspatch = True
1988 if lastcommit:
1990 if lastcommit:
1989 ui.status(_('applied %s\n') % lastcommit)
1991 ui.status(_('applied %s\n') % lastcommit)
1990 lastcommit = commitid
1992 lastcommit = commitid
1991
1993
1992 if not haspatch:
1994 if not haspatch:
1993 raise util.Abort(_('no diffs found'))
1995 raise util.Abort(_('no diffs found'))
1994
1996
1995 finally:
1997 finally:
1996 release(lock, wlock)
1998 release(lock, wlock)
1997
1999
1998 def incoming(ui, repo, source="default", **opts):
2000 def incoming(ui, repo, source="default", **opts):
1999 """show new changesets found in source
2001 """show new changesets found in source
2000
2002
2001 Show new changesets found in the specified path/URL or the default
2003 Show new changesets found in the specified path/URL or the default
2002 pull location. These are the changesets that would have been pulled
2004 pull location. These are the changesets that would have been pulled
2003 if a pull at the time you issued this command.
2005 if a pull at the time you issued this command.
2004
2006
2005 For remote repository, using --bundle avoids downloading the
2007 For remote repository, using --bundle avoids downloading the
2006 changesets twice if the incoming is followed by a pull.
2008 changesets twice if the incoming is followed by a pull.
2007
2009
2008 See pull for valid source format details.
2010 See pull for valid source format details.
2009 """
2011 """
2010 limit = cmdutil.loglimit(opts)
2012 limit = cmdutil.loglimit(opts)
2011 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2013 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2012 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2014 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2013 ui.status(_('comparing with %s\n') % url.hidepassword(source))
2015 ui.status(_('comparing with %s\n') % url.hidepassword(source))
2014 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2016 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2015 if revs:
2017 if revs:
2016 revs = [other.lookup(rev) for rev in revs]
2018 revs = [other.lookup(rev) for rev in revs]
2017 common, incoming, rheads = repo.findcommonincoming(other, heads=revs,
2019 common, incoming, rheads = repo.findcommonincoming(other, heads=revs,
2018 force=opts["force"])
2020 force=opts["force"])
2019 if not incoming:
2021 if not incoming:
2020 try:
2022 try:
2021 os.unlink(opts["bundle"])
2023 os.unlink(opts["bundle"])
2022 except:
2024 except:
2023 pass
2025 pass
2024 ui.status(_("no changes found\n"))
2026 ui.status(_("no changes found\n"))
2025 return 1
2027 return 1
2026
2028
2027 cleanup = None
2029 cleanup = None
2028 try:
2030 try:
2029 fname = opts["bundle"]
2031 fname = opts["bundle"]
2030 if fname or not other.local():
2032 if fname or not other.local():
2031 # create a bundle (uncompressed if other repo is not local)
2033 # create a bundle (uncompressed if other repo is not local)
2032
2034
2033 if revs is None and other.capable('changegroupsubset'):
2035 if revs is None and other.capable('changegroupsubset'):
2034 revs = rheads
2036 revs = rheads
2035
2037
2036 if revs is None:
2038 if revs is None:
2037 cg = other.changegroup(incoming, "incoming")
2039 cg = other.changegroup(incoming, "incoming")
2038 else:
2040 else:
2039 cg = other.changegroupsubset(incoming, revs, 'incoming')
2041 cg = other.changegroupsubset(incoming, revs, 'incoming')
2040 bundletype = other.local() and "HG10BZ" or "HG10UN"
2042 bundletype = other.local() and "HG10BZ" or "HG10UN"
2041 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
2043 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
2042 # keep written bundle?
2044 # keep written bundle?
2043 if opts["bundle"]:
2045 if opts["bundle"]:
2044 cleanup = None
2046 cleanup = None
2045 if not other.local():
2047 if not other.local():
2046 # use the created uncompressed bundlerepo
2048 # use the created uncompressed bundlerepo
2047 other = bundlerepo.bundlerepository(ui, repo.root, fname)
2049 other = bundlerepo.bundlerepository(ui, repo.root, fname)
2048
2050
2049 o = other.changelog.nodesbetween(incoming, revs)[0]
2051 o = other.changelog.nodesbetween(incoming, revs)[0]
2050 if opts.get('newest_first'):
2052 if opts.get('newest_first'):
2051 o.reverse()
2053 o.reverse()
2052 displayer = cmdutil.show_changeset(ui, other, opts)
2054 displayer = cmdutil.show_changeset(ui, other, opts)
2053 count = 0
2055 count = 0
2054 for n in o:
2056 for n in o:
2055 if limit is not None and count >= limit:
2057 if limit is not None and count >= limit:
2056 break
2058 break
2057 parents = [p for p in other.changelog.parents(n) if p != nullid]
2059 parents = [p for p in other.changelog.parents(n) if p != nullid]
2058 if opts.get('no_merges') and len(parents) == 2:
2060 if opts.get('no_merges') and len(parents) == 2:
2059 continue
2061 continue
2060 count += 1
2062 count += 1
2061 displayer.show(other[n])
2063 displayer.show(other[n])
2062 displayer.close()
2064 displayer.close()
2063 finally:
2065 finally:
2064 if hasattr(other, 'close'):
2066 if hasattr(other, 'close'):
2065 other.close()
2067 other.close()
2066 if cleanup:
2068 if cleanup:
2067 os.unlink(cleanup)
2069 os.unlink(cleanup)
2068
2070
2069 def init(ui, dest=".", **opts):
2071 def init(ui, dest=".", **opts):
2070 """create a new repository in the given directory
2072 """create a new repository in the given directory
2071
2073
2072 Initialize a new repository in the given directory. If the given
2074 Initialize a new repository in the given directory. If the given
2073 directory does not exist, it will be created.
2075 directory does not exist, it will be created.
2074
2076
2075 If no directory is given, the current directory is used.
2077 If no directory is given, the current directory is used.
2076
2078
2077 It is possible to specify an ``ssh://`` URL as the destination.
2079 It is possible to specify an ``ssh://`` URL as the destination.
2078 See 'hg help urls' for more information.
2080 See 'hg help urls' for more information.
2079 """
2081 """
2080 hg.repository(cmdutil.remoteui(ui, opts), dest, create=1)
2082 hg.repository(cmdutil.remoteui(ui, opts), dest, create=1)
2081
2083
2082 def locate(ui, repo, *pats, **opts):
2084 def locate(ui, repo, *pats, **opts):
2083 """locate files matching specific patterns
2085 """locate files matching specific patterns
2084
2086
2085 Print files under Mercurial control in the working directory whose
2087 Print files under Mercurial control in the working directory whose
2086 names match the given patterns.
2088 names match the given patterns.
2087
2089
2088 By default, this command searches all directories in the working
2090 By default, this command searches all directories in the working
2089 directory. To search just the current directory and its
2091 directory. To search just the current directory and its
2090 subdirectories, use "--include .".
2092 subdirectories, use "--include .".
2091
2093
2092 If no patterns are given to match, this command prints the names
2094 If no patterns are given to match, this command prints the names
2093 of all files under Mercurial control in the working directory.
2095 of all files under Mercurial control in the working directory.
2094
2096
2095 If you want to feed the output of this command into the "xargs"
2097 If you want to feed the output of this command into the "xargs"
2096 command, use the -0 option to both this command and "xargs". This
2098 command, use the -0 option to both this command and "xargs". This
2097 will avoid the problem of "xargs" treating single filenames that
2099 will avoid the problem of "xargs" treating single filenames that
2098 contain whitespace as multiple filenames.
2100 contain whitespace as multiple filenames.
2099 """
2101 """
2100 end = opts.get('print0') and '\0' or '\n'
2102 end = opts.get('print0') and '\0' or '\n'
2101 rev = opts.get('rev') or None
2103 rev = opts.get('rev') or None
2102
2104
2103 ret = 1
2105 ret = 1
2104 m = cmdutil.match(repo, pats, opts, default='relglob')
2106 m = cmdutil.match(repo, pats, opts, default='relglob')
2105 m.bad = lambda x, y: False
2107 m.bad = lambda x, y: False
2106 for abs in repo[rev].walk(m):
2108 for abs in repo[rev].walk(m):
2107 if not rev and abs not in repo.dirstate:
2109 if not rev and abs not in repo.dirstate:
2108 continue
2110 continue
2109 if opts.get('fullpath'):
2111 if opts.get('fullpath'):
2110 ui.write(repo.wjoin(abs), end)
2112 ui.write(repo.wjoin(abs), end)
2111 else:
2113 else:
2112 ui.write(((pats and m.rel(abs)) or abs), end)
2114 ui.write(((pats and m.rel(abs)) or abs), end)
2113 ret = 0
2115 ret = 0
2114
2116
2115 return ret
2117 return ret
2116
2118
2117 def log(ui, repo, *pats, **opts):
2119 def log(ui, repo, *pats, **opts):
2118 """show revision history of entire repository or files
2120 """show revision history of entire repository or files
2119
2121
2120 Print the revision history of the specified files or the entire
2122 Print the revision history of the specified files or the entire
2121 project.
2123 project.
2122
2124
2123 File history is shown without following rename or copy history of
2125 File history is shown without following rename or copy history of
2124 files. Use -f/--follow with a filename to follow history across
2126 files. Use -f/--follow with a filename to follow history across
2125 renames and copies. --follow without a filename will only show
2127 renames and copies. --follow without a filename will only show
2126 ancestors or descendants of the starting revision. --follow-first
2128 ancestors or descendants of the starting revision. --follow-first
2127 only follows the first parent of merge revisions.
2129 only follows the first parent of merge revisions.
2128
2130
2129 If no revision range is specified, the default is tip:0 unless
2131 If no revision range is specified, the default is tip:0 unless
2130 --follow is set, in which case the working directory parent is
2132 --follow is set, in which case the working directory parent is
2131 used as the starting revision.
2133 used as the starting revision.
2132
2134
2133 See 'hg help dates' for a list of formats valid for -d/--date.
2135 See 'hg help dates' for a list of formats valid for -d/--date.
2134
2136
2135 By default this command prints revision number and changeset id,
2137 By default this command prints revision number and changeset id,
2136 tags, non-trivial parents, user, date and time, and a summary for
2138 tags, non-trivial parents, user, date and time, and a summary for
2137 each commit. When the -v/--verbose switch is used, the list of
2139 each commit. When the -v/--verbose switch is used, the list of
2138 changed files and full commit message are shown.
2140 changed files and full commit message are shown.
2139
2141
2140 NOTE: log -p/--patch may generate unexpected diff output for merge
2142 NOTE: log -p/--patch may generate unexpected diff output for merge
2141 changesets, as it will only compare the merge changeset against
2143 changesets, as it will only compare the merge changeset against
2142 its first parent. Also, only files different from BOTH parents
2144 its first parent. Also, only files different from BOTH parents
2143 will appear in files:.
2145 will appear in files:.
2144 """
2146 """
2145
2147
2146 matchfn = cmdutil.match(repo, pats, opts)
2148 matchfn = cmdutil.match(repo, pats, opts)
2147 limit = cmdutil.loglimit(opts)
2149 limit = cmdutil.loglimit(opts)
2148 count = 0
2150 count = 0
2149
2151
2150 endrev = None
2152 endrev = None
2151 if opts.get('copies') and opts.get('rev'):
2153 if opts.get('copies') and opts.get('rev'):
2152 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
2154 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
2153
2155
2154 df = False
2156 df = False
2155 if opts["date"]:
2157 if opts["date"]:
2156 df = util.matchdate(opts["date"])
2158 df = util.matchdate(opts["date"])
2157
2159
2158 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
2160 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
2159 def prep(ctx, fns):
2161 def prep(ctx, fns):
2160 rev = ctx.rev()
2162 rev = ctx.rev()
2161 parents = [p for p in repo.changelog.parentrevs(rev)
2163 parents = [p for p in repo.changelog.parentrevs(rev)
2162 if p != nullrev]
2164 if p != nullrev]
2163 if opts.get('no_merges') and len(parents) == 2:
2165 if opts.get('no_merges') and len(parents) == 2:
2164 return
2166 return
2165 if opts.get('only_merges') and len(parents) != 2:
2167 if opts.get('only_merges') and len(parents) != 2:
2166 return
2168 return
2167 if opts.get('only_branch') and ctx.branch() not in opts['only_branch']:
2169 if opts.get('only_branch') and ctx.branch() not in opts['only_branch']:
2168 return
2170 return
2169 if df and not df(ctx.date()[0]):
2171 if df and not df(ctx.date()[0]):
2170 return
2172 return
2171 if opts['user'] and not [k for k in opts['user'] if k in ctx.user()]:
2173 if opts['user'] and not [k for k in opts['user'] if k in ctx.user()]:
2172 return
2174 return
2173 if opts.get('keyword'):
2175 if opts.get('keyword'):
2174 for k in [kw.lower() for kw in opts['keyword']]:
2176 for k in [kw.lower() for kw in opts['keyword']]:
2175 if (k in ctx.user().lower() or
2177 if (k in ctx.user().lower() or
2176 k in ctx.description().lower() or
2178 k in ctx.description().lower() or
2177 k in " ".join(ctx.files()).lower()):
2179 k in " ".join(ctx.files()).lower()):
2178 break
2180 break
2179 else:
2181 else:
2180 return
2182 return
2181
2183
2182 copies = None
2184 copies = None
2183 if opts.get('copies') and rev:
2185 if opts.get('copies') and rev:
2184 copies = []
2186 copies = []
2185 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2187 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2186 for fn in ctx.files():
2188 for fn in ctx.files():
2187 rename = getrenamed(fn, rev)
2189 rename = getrenamed(fn, rev)
2188 if rename:
2190 if rename:
2189 copies.append((fn, rename[0]))
2191 copies.append((fn, rename[0]))
2190
2192
2191 displayer.show(ctx, copies=copies)
2193 displayer.show(ctx, copies=copies)
2192
2194
2193 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2195 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2194 if count == limit:
2196 if count == limit:
2195 break
2197 break
2196 if displayer.flush(ctx.rev()):
2198 if displayer.flush(ctx.rev()):
2197 count += 1
2199 count += 1
2198 displayer.close()
2200 displayer.close()
2199
2201
2200 def manifest(ui, repo, node=None, rev=None):
2202 def manifest(ui, repo, node=None, rev=None):
2201 """output the current or given revision of the project manifest
2203 """output the current or given revision of the project manifest
2202
2204
2203 Print a list of version controlled files for the given revision.
2205 Print a list of version controlled files for the given revision.
2204 If no revision is given, the first parent of the working directory
2206 If no revision is given, the first parent of the working directory
2205 is used, or the null revision if no revision is checked out.
2207 is used, or the null revision if no revision is checked out.
2206
2208
2207 With -v, print file permissions, symlink and executable bits.
2209 With -v, print file permissions, symlink and executable bits.
2208 With --debug, print file revision hashes.
2210 With --debug, print file revision hashes.
2209 """
2211 """
2210
2212
2211 if rev and node:
2213 if rev and node:
2212 raise util.Abort(_("please specify just one revision"))
2214 raise util.Abort(_("please specify just one revision"))
2213
2215
2214 if not node:
2216 if not node:
2215 node = rev
2217 node = rev
2216
2218
2217 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2219 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2218 ctx = repo[node]
2220 ctx = repo[node]
2219 for f in ctx:
2221 for f in ctx:
2220 if ui.debugflag:
2222 if ui.debugflag:
2221 ui.write("%40s " % hex(ctx.manifest()[f]))
2223 ui.write("%40s " % hex(ctx.manifest()[f]))
2222 if ui.verbose:
2224 if ui.verbose:
2223 ui.write(decor[ctx.flags(f)])
2225 ui.write(decor[ctx.flags(f)])
2224 ui.write("%s\n" % f)
2226 ui.write("%s\n" % f)
2225
2227
2226 def merge(ui, repo, node=None, **opts):
2228 def merge(ui, repo, node=None, **opts):
2227 """merge working directory with another revision
2229 """merge working directory with another revision
2228
2230
2229 The current working directory is updated with all changes made in
2231 The current working directory is updated with all changes made in
2230 the requested revision since the last common predecessor revision.
2232 the requested revision since the last common predecessor revision.
2231
2233
2232 Files that changed between either parent are marked as changed for
2234 Files that changed between either parent are marked as changed for
2233 the next commit and a commit must be performed before any further
2235 the next commit and a commit must be performed before any further
2234 updates to the repository are allowed. The next commit will have
2236 updates to the repository are allowed. The next commit will have
2235 two parents.
2237 two parents.
2236
2238
2237 If no revision is specified, the working directory's parent is a
2239 If no revision is specified, the working directory's parent is a
2238 head revision, and the current branch contains exactly one other
2240 head revision, and the current branch contains exactly one other
2239 head, the other head is merged with by default. Otherwise, an
2241 head, the other head is merged with by default. Otherwise, an
2240 explicit revision with which to merge with must be provided.
2242 explicit revision with which to merge with must be provided.
2241 """
2243 """
2242
2244
2243 if opts.get('rev') and node:
2245 if opts.get('rev') and node:
2244 raise util.Abort(_("please specify just one revision"))
2246 raise util.Abort(_("please specify just one revision"))
2245 if not node:
2247 if not node:
2246 node = opts.get('rev')
2248 node = opts.get('rev')
2247
2249
2248 if not node:
2250 if not node:
2249 branch = repo.changectx(None).branch()
2251 branch = repo.changectx(None).branch()
2250 bheads = repo.branchheads(branch)
2252 bheads = repo.branchheads(branch)
2251 if len(bheads) > 2:
2253 if len(bheads) > 2:
2252 ui.warn(_("abort: branch '%s' has %d heads - "
2254 ui.warn(_("abort: branch '%s' has %d heads - "
2253 "please merge with an explicit rev\n")
2255 "please merge with an explicit rev\n")
2254 % (branch, len(bheads)))
2256 % (branch, len(bheads)))
2255 ui.status(_("(run 'hg heads .' to see heads)\n"))
2257 ui.status(_("(run 'hg heads .' to see heads)\n"))
2256 return False
2258 return False
2257
2259
2258 parent = repo.dirstate.parents()[0]
2260 parent = repo.dirstate.parents()[0]
2259 if len(bheads) == 1:
2261 if len(bheads) == 1:
2260 if len(repo.heads()) > 1:
2262 if len(repo.heads()) > 1:
2261 ui.warn(_("abort: branch '%s' has one head - "
2263 ui.warn(_("abort: branch '%s' has one head - "
2262 "please merge with an explicit rev\n" % branch))
2264 "please merge with an explicit rev\n" % branch))
2263 ui.status(_("(run 'hg heads' to see all heads)\n"))
2265 ui.status(_("(run 'hg heads' to see all heads)\n"))
2264 return False
2266 return False
2265 msg = _('there is nothing to merge')
2267 msg = _('there is nothing to merge')
2266 if parent != repo.lookup(repo[None].branch()):
2268 if parent != repo.lookup(repo[None].branch()):
2267 msg = _('%s - use "hg update" instead') % msg
2269 msg = _('%s - use "hg update" instead') % msg
2268 raise util.Abort(msg)
2270 raise util.Abort(msg)
2269
2271
2270 if parent not in bheads:
2272 if parent not in bheads:
2271 raise util.Abort(_('working dir not at a head rev - '
2273 raise util.Abort(_('working dir not at a head rev - '
2272 'use "hg update" or merge with an explicit rev'))
2274 'use "hg update" or merge with an explicit rev'))
2273 node = parent == bheads[0] and bheads[-1] or bheads[0]
2275 node = parent == bheads[0] and bheads[-1] or bheads[0]
2274
2276
2275 if opts.get('preview'):
2277 if opts.get('preview'):
2276 # find nodes that are ancestors of p2 but not of p1
2278 # find nodes that are ancestors of p2 but not of p1
2277 p1 = repo.lookup('.')
2279 p1 = repo.lookup('.')
2278 p2 = repo.lookup(node)
2280 p2 = repo.lookup(node)
2279 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
2281 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
2280
2282
2281 displayer = cmdutil.show_changeset(ui, repo, opts)
2283 displayer = cmdutil.show_changeset(ui, repo, opts)
2282 for node in nodes:
2284 for node in nodes:
2283 displayer.show(repo[node])
2285 displayer.show(repo[node])
2284 displayer.close()
2286 displayer.close()
2285 return 0
2287 return 0
2286
2288
2287 return hg.merge(repo, node, force=opts.get('force'))
2289 return hg.merge(repo, node, force=opts.get('force'))
2288
2290
2289 def outgoing(ui, repo, dest=None, **opts):
2291 def outgoing(ui, repo, dest=None, **opts):
2290 """show changesets not found in the destination
2292 """show changesets not found in the destination
2291
2293
2292 Show changesets not found in the specified destination repository
2294 Show changesets not found in the specified destination repository
2293 or the default push location. These are the changesets that would
2295 or the default push location. These are the changesets that would
2294 be pushed if a push was requested.
2296 be pushed if a push was requested.
2295
2297
2296 See pull for details of valid destination formats.
2298 See pull for details of valid destination formats.
2297 """
2299 """
2298 limit = cmdutil.loglimit(opts)
2300 limit = cmdutil.loglimit(opts)
2299 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2301 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2300 dest, branches = hg.parseurl(dest, opts.get('branch'))
2302 dest, branches = hg.parseurl(dest, opts.get('branch'))
2301 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2303 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2302 if revs:
2304 if revs:
2303 revs = [repo.lookup(rev) for rev in revs]
2305 revs = [repo.lookup(rev) for rev in revs]
2304
2306
2305 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2307 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2306 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2308 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2307 o = repo.findoutgoing(other, force=opts.get('force'))
2309 o = repo.findoutgoing(other, force=opts.get('force'))
2308 if not o:
2310 if not o:
2309 ui.status(_("no changes found\n"))
2311 ui.status(_("no changes found\n"))
2310 return 1
2312 return 1
2311 o = repo.changelog.nodesbetween(o, revs)[0]
2313 o = repo.changelog.nodesbetween(o, revs)[0]
2312 if opts.get('newest_first'):
2314 if opts.get('newest_first'):
2313 o.reverse()
2315 o.reverse()
2314 displayer = cmdutil.show_changeset(ui, repo, opts)
2316 displayer = cmdutil.show_changeset(ui, repo, opts)
2315 count = 0
2317 count = 0
2316 for n in o:
2318 for n in o:
2317 if limit is not None and count >= limit:
2319 if limit is not None and count >= limit:
2318 break
2320 break
2319 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2321 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2320 if opts.get('no_merges') and len(parents) == 2:
2322 if opts.get('no_merges') and len(parents) == 2:
2321 continue
2323 continue
2322 count += 1
2324 count += 1
2323 displayer.show(repo[n])
2325 displayer.show(repo[n])
2324 displayer.close()
2326 displayer.close()
2325
2327
2326 def parents(ui, repo, file_=None, **opts):
2328 def parents(ui, repo, file_=None, **opts):
2327 """show the parents of the working directory or revision
2329 """show the parents of the working directory or revision
2328
2330
2329 Print the working directory's parent revisions. If a revision is
2331 Print the working directory's parent revisions. If a revision is
2330 given via -r/--rev, the parent of that revision will be printed.
2332 given via -r/--rev, the parent of that revision will be printed.
2331 If a file argument is given, the revision in which the file was
2333 If a file argument is given, the revision in which the file was
2332 last changed (before the working directory revision or the
2334 last changed (before the working directory revision or the
2333 argument to --rev if given) is printed.
2335 argument to --rev if given) is printed.
2334 """
2336 """
2335 rev = opts.get('rev')
2337 rev = opts.get('rev')
2336 if rev:
2338 if rev:
2337 ctx = repo[rev]
2339 ctx = repo[rev]
2338 else:
2340 else:
2339 ctx = repo[None]
2341 ctx = repo[None]
2340
2342
2341 if file_:
2343 if file_:
2342 m = cmdutil.match(repo, (file_,), opts)
2344 m = cmdutil.match(repo, (file_,), opts)
2343 if m.anypats() or len(m.files()) != 1:
2345 if m.anypats() or len(m.files()) != 1:
2344 raise util.Abort(_('can only specify an explicit filename'))
2346 raise util.Abort(_('can only specify an explicit filename'))
2345 file_ = m.files()[0]
2347 file_ = m.files()[0]
2346 filenodes = []
2348 filenodes = []
2347 for cp in ctx.parents():
2349 for cp in ctx.parents():
2348 if not cp:
2350 if not cp:
2349 continue
2351 continue
2350 try:
2352 try:
2351 filenodes.append(cp.filenode(file_))
2353 filenodes.append(cp.filenode(file_))
2352 except error.LookupError:
2354 except error.LookupError:
2353 pass
2355 pass
2354 if not filenodes:
2356 if not filenodes:
2355 raise util.Abort(_("'%s' not found in manifest!") % file_)
2357 raise util.Abort(_("'%s' not found in manifest!") % file_)
2356 fl = repo.file(file_)
2358 fl = repo.file(file_)
2357 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2359 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2358 else:
2360 else:
2359 p = [cp.node() for cp in ctx.parents()]
2361 p = [cp.node() for cp in ctx.parents()]
2360
2362
2361 displayer = cmdutil.show_changeset(ui, repo, opts)
2363 displayer = cmdutil.show_changeset(ui, repo, opts)
2362 for n in p:
2364 for n in p:
2363 if n != nullid:
2365 if n != nullid:
2364 displayer.show(repo[n])
2366 displayer.show(repo[n])
2365 displayer.close()
2367 displayer.close()
2366
2368
2367 def paths(ui, repo, search=None):
2369 def paths(ui, repo, search=None):
2368 """show aliases for remote repositories
2370 """show aliases for remote repositories
2369
2371
2370 Show definition of symbolic path name NAME. If no name is given,
2372 Show definition of symbolic path name NAME. If no name is given,
2371 show definition of all available names.
2373 show definition of all available names.
2372
2374
2373 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2375 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2374 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2376 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2375
2377
2376 See 'hg help urls' for more information.
2378 See 'hg help urls' for more information.
2377 """
2379 """
2378 if search:
2380 if search:
2379 for name, path in ui.configitems("paths"):
2381 for name, path in ui.configitems("paths"):
2380 if name == search:
2382 if name == search:
2381 ui.write("%s\n" % url.hidepassword(path))
2383 ui.write("%s\n" % url.hidepassword(path))
2382 return
2384 return
2383 ui.warn(_("not found!\n"))
2385 ui.warn(_("not found!\n"))
2384 return 1
2386 return 1
2385 else:
2387 else:
2386 for name, path in ui.configitems("paths"):
2388 for name, path in ui.configitems("paths"):
2387 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2389 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2388
2390
2389 def postincoming(ui, repo, modheads, optupdate, checkout):
2391 def postincoming(ui, repo, modheads, optupdate, checkout):
2390 if modheads == 0:
2392 if modheads == 0:
2391 return
2393 return
2392 if optupdate:
2394 if optupdate:
2393 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2395 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2394 return hg.update(repo, checkout)
2396 return hg.update(repo, checkout)
2395 else:
2397 else:
2396 ui.status(_("not updating, since new heads added\n"))
2398 ui.status(_("not updating, since new heads added\n"))
2397 if modheads > 1:
2399 if modheads > 1:
2398 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2400 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2399 else:
2401 else:
2400 ui.status(_("(run 'hg update' to get a working copy)\n"))
2402 ui.status(_("(run 'hg update' to get a working copy)\n"))
2401
2403
2402 def pull(ui, repo, source="default", **opts):
2404 def pull(ui, repo, source="default", **opts):
2403 """pull changes from the specified source
2405 """pull changes from the specified source
2404
2406
2405 Pull changes from a remote repository to a local one.
2407 Pull changes from a remote repository to a local one.
2406
2408
2407 This finds all changes from the repository at the specified path
2409 This finds all changes from the repository at the specified path
2408 or URL and adds them to a local repository (the current one unless
2410 or URL and adds them to a local repository (the current one unless
2409 -R is specified). By default, this does not update the copy of the
2411 -R is specified). By default, this does not update the copy of the
2410 project in the working directory.
2412 project in the working directory.
2411
2413
2412 Use hg incoming if you want to see what would have been added by a
2414 Use hg incoming if you want to see what would have been added by a
2413 pull at the time you issued this command. If you then decide to
2415 pull at the time you issued this command. If you then decide to
2414 added those changes to the repository, you should use pull -r X
2416 added those changes to the repository, you should use pull -r X
2415 where X is the last changeset listed by hg incoming.
2417 where X is the last changeset listed by hg incoming.
2416
2418
2417 If SOURCE is omitted, the 'default' path will be used.
2419 If SOURCE is omitted, the 'default' path will be used.
2418 See 'hg help urls' for more information.
2420 See 'hg help urls' for more information.
2419 """
2421 """
2420 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2422 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2421 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2423 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2422 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2424 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2423 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2425 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2424 if revs:
2426 if revs:
2425 try:
2427 try:
2426 revs = [other.lookup(rev) for rev in revs]
2428 revs = [other.lookup(rev) for rev in revs]
2427 except error.CapabilityError:
2429 except error.CapabilityError:
2428 err = _("Other repository doesn't support revision lookup, "
2430 err = _("Other repository doesn't support revision lookup, "
2429 "so a rev cannot be specified.")
2431 "so a rev cannot be specified.")
2430 raise util.Abort(err)
2432 raise util.Abort(err)
2431
2433
2432 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2434 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2433 if checkout:
2435 if checkout:
2434 checkout = str(repo.changelog.rev(other.lookup(checkout)))
2436 checkout = str(repo.changelog.rev(other.lookup(checkout)))
2435 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2437 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2436
2438
2437 def push(ui, repo, dest=None, **opts):
2439 def push(ui, repo, dest=None, **opts):
2438 """push changes to the specified destination
2440 """push changes to the specified destination
2439
2441
2440 Push changes from the local repository to the specified destination.
2442 Push changes from the local repository to the specified destination.
2441
2443
2442 This is the symmetrical operation for pull. It moves changes from
2444 This is the symmetrical operation for pull. It moves changes from
2443 the current repository to a different one. If the destination is
2445 the current repository to a different one. If the destination is
2444 local this is identical to a pull in that directory from the
2446 local this is identical to a pull in that directory from the
2445 current one.
2447 current one.
2446
2448
2447 By default, push will refuse to run if it detects the result would
2449 By default, push will refuse to run if it detects the result would
2448 increase the number of remote heads. This generally indicates the
2450 increase the number of remote heads. This generally indicates the
2449 user forgot to pull and merge before pushing.
2451 user forgot to pull and merge before pushing.
2450
2452
2451 If -r/--rev is used, the named revision and all its ancestors will
2453 If -r/--rev is used, the named revision and all its ancestors will
2452 be pushed to the remote repository.
2454 be pushed to the remote repository.
2453
2455
2454 Please see 'hg help urls' for important details about ``ssh://``
2456 Please see 'hg help urls' for important details about ``ssh://``
2455 URLs. If DESTINATION is omitted, a default path will be used.
2457 URLs. If DESTINATION is omitted, a default path will be used.
2456 """
2458 """
2457 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2459 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2458 dest, branches = hg.parseurl(dest, opts.get('branch'))
2460 dest, branches = hg.parseurl(dest, opts.get('branch'))
2459 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2461 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2460 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2462 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2461 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2463 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2462 if revs:
2464 if revs:
2463 revs = [repo.lookup(rev) for rev in revs]
2465 revs = [repo.lookup(rev) for rev in revs]
2464
2466
2465 # push subrepos depth-first for coherent ordering
2467 # push subrepos depth-first for coherent ordering
2466 c = repo['']
2468 c = repo['']
2467 subs = c.substate # only repos that are committed
2469 subs = c.substate # only repos that are committed
2468 for s in sorted(subs):
2470 for s in sorted(subs):
2469 c.sub(s).push(opts.get('force'))
2471 c.sub(s).push(opts.get('force'))
2470
2472
2471 r = repo.push(other, opts.get('force'), revs=revs)
2473 r = repo.push(other, opts.get('force'), revs=revs)
2472 return r == 0
2474 return r == 0
2473
2475
2474 def recover(ui, repo):
2476 def recover(ui, repo):
2475 """roll back an interrupted transaction
2477 """roll back an interrupted transaction
2476
2478
2477 Recover from an interrupted commit or pull.
2479 Recover from an interrupted commit or pull.
2478
2480
2479 This command tries to fix the repository status after an
2481 This command tries to fix the repository status after an
2480 interrupted operation. It should only be necessary when Mercurial
2482 interrupted operation. It should only be necessary when Mercurial
2481 suggests it.
2483 suggests it.
2482 """
2484 """
2483 if repo.recover():
2485 if repo.recover():
2484 return hg.verify(repo)
2486 return hg.verify(repo)
2485 return 1
2487 return 1
2486
2488
2487 def remove(ui, repo, *pats, **opts):
2489 def remove(ui, repo, *pats, **opts):
2488 """remove the specified files on the next commit
2490 """remove the specified files on the next commit
2489
2491
2490 Schedule the indicated files for removal from the repository.
2492 Schedule the indicated files for removal from the repository.
2491
2493
2492 This only removes files from the current branch, not from the
2494 This only removes files from the current branch, not from the
2493 entire project history. -A/--after can be used to remove only
2495 entire project history. -A/--after can be used to remove only
2494 files that have already been deleted, -f/--force can be used to
2496 files that have already been deleted, -f/--force can be used to
2495 force deletion, and -Af can be used to remove files from the next
2497 force deletion, and -Af can be used to remove files from the next
2496 revision without deleting them from the working directory.
2498 revision without deleting them from the working directory.
2497
2499
2498 The following table details the behavior of remove for different
2500 The following table details the behavior of remove for different
2499 file states (columns) and option combinations (rows). The file
2501 file states (columns) and option combinations (rows). The file
2500 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
2502 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
2501 reported by hg status). The actions are Warn, Remove (from branch)
2503 reported by hg status). The actions are Warn, Remove (from branch)
2502 and Delete (from disk)::
2504 and Delete (from disk)::
2503
2505
2504 A C M !
2506 A C M !
2505 none W RD W R
2507 none W RD W R
2506 -f R RD RD R
2508 -f R RD RD R
2507 -A W W W R
2509 -A W W W R
2508 -Af R R R R
2510 -Af R R R R
2509
2511
2510 This command schedules the files to be removed at the next commit.
2512 This command schedules the files to be removed at the next commit.
2511 To undo a remove before that, see hg revert.
2513 To undo a remove before that, see hg revert.
2512 """
2514 """
2513
2515
2514 after, force = opts.get('after'), opts.get('force')
2516 after, force = opts.get('after'), opts.get('force')
2515 if not pats and not after:
2517 if not pats and not after:
2516 raise util.Abort(_('no files specified'))
2518 raise util.Abort(_('no files specified'))
2517
2519
2518 m = cmdutil.match(repo, pats, opts)
2520 m = cmdutil.match(repo, pats, opts)
2519 s = repo.status(match=m, clean=True)
2521 s = repo.status(match=m, clean=True)
2520 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2522 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2521
2523
2522 for f in m.files():
2524 for f in m.files():
2523 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2525 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2524 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2526 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2525
2527
2526 def warn(files, reason):
2528 def warn(files, reason):
2527 for f in files:
2529 for f in files:
2528 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2530 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2529 % (m.rel(f), reason))
2531 % (m.rel(f), reason))
2530
2532
2531 if force:
2533 if force:
2532 remove, forget = modified + deleted + clean, added
2534 remove, forget = modified + deleted + clean, added
2533 elif after:
2535 elif after:
2534 remove, forget = deleted, []
2536 remove, forget = deleted, []
2535 warn(modified + added + clean, _('still exists'))
2537 warn(modified + added + clean, _('still exists'))
2536 else:
2538 else:
2537 remove, forget = deleted + clean, []
2539 remove, forget = deleted + clean, []
2538 warn(modified, _('is modified'))
2540 warn(modified, _('is modified'))
2539 warn(added, _('has been marked for add'))
2541 warn(added, _('has been marked for add'))
2540
2542
2541 for f in sorted(remove + forget):
2543 for f in sorted(remove + forget):
2542 if ui.verbose or not m.exact(f):
2544 if ui.verbose or not m.exact(f):
2543 ui.status(_('removing %s\n') % m.rel(f))
2545 ui.status(_('removing %s\n') % m.rel(f))
2544
2546
2545 repo.forget(forget)
2547 repo.forget(forget)
2546 repo.remove(remove, unlink=not after)
2548 repo.remove(remove, unlink=not after)
2547
2549
2548 def rename(ui, repo, *pats, **opts):
2550 def rename(ui, repo, *pats, **opts):
2549 """rename files; equivalent of copy + remove
2551 """rename files; equivalent of copy + remove
2550
2552
2551 Mark dest as copies of sources; mark sources for deletion. If dest
2553 Mark dest as copies of sources; mark sources for deletion. If dest
2552 is a directory, copies are put in that directory. If dest is a
2554 is a directory, copies are put in that directory. If dest is a
2553 file, there can only be one source.
2555 file, there can only be one source.
2554
2556
2555 By default, this command copies the contents of files as they
2557 By default, this command copies the contents of files as they
2556 exist in the working directory. If invoked with -A/--after, the
2558 exist in the working directory. If invoked with -A/--after, the
2557 operation is recorded, but no copying is performed.
2559 operation is recorded, but no copying is performed.
2558
2560
2559 This command takes effect at the next commit. To undo a rename
2561 This command takes effect at the next commit. To undo a rename
2560 before that, see hg revert.
2562 before that, see hg revert.
2561 """
2563 """
2562 wlock = repo.wlock(False)
2564 wlock = repo.wlock(False)
2563 try:
2565 try:
2564 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2566 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2565 finally:
2567 finally:
2566 wlock.release()
2568 wlock.release()
2567
2569
2568 def resolve(ui, repo, *pats, **opts):
2570 def resolve(ui, repo, *pats, **opts):
2569 """various operations to help finish a merge
2571 """various operations to help finish a merge
2570
2572
2571 This command includes several actions that are often useful while
2573 This command includes several actions that are often useful while
2572 performing a merge, after running ``merge`` but before running
2574 performing a merge, after running ``merge`` but before running
2573 ``commit``. (It is only meaningful if your working directory has
2575 ``commit``. (It is only meaningful if your working directory has
2574 two parents.) It is most relevant for merges with unresolved
2576 two parents.) It is most relevant for merges with unresolved
2575 conflicts, which are typically a result of non-interactive merging with
2577 conflicts, which are typically a result of non-interactive merging with
2576 ``internal:merge`` or a command-line merge tool like ``diff3``.
2578 ``internal:merge`` or a command-line merge tool like ``diff3``.
2577
2579
2578 The available actions are:
2580 The available actions are:
2579
2581
2580 1) list files that were merged with conflicts (U, for unresolved)
2582 1) list files that were merged with conflicts (U, for unresolved)
2581 and without conflicts (R, for resolved): ``hg resolve -l``
2583 and without conflicts (R, for resolved): ``hg resolve -l``
2582 (this is like ``status`` for merges)
2584 (this is like ``status`` for merges)
2583 2) record that you have resolved conflicts in certain files:
2585 2) record that you have resolved conflicts in certain files:
2584 ``hg resolve -m [file ...]`` (default: mark all unresolved files)
2586 ``hg resolve -m [file ...]`` (default: mark all unresolved files)
2585 3) forget that you have resolved conflicts in certain files:
2587 3) forget that you have resolved conflicts in certain files:
2586 ``hg resolve -u [file ...]`` (default: unmark all resolved files)
2588 ``hg resolve -u [file ...]`` (default: unmark all resolved files)
2587 4) discard your current attempt(s) at resolving conflicts and
2589 4) discard your current attempt(s) at resolving conflicts and
2588 restart the merge from scratch: ``hg resolve file...``
2590 restart the merge from scratch: ``hg resolve file...``
2589 (or ``-a`` for all unresolved files)
2591 (or ``-a`` for all unresolved files)
2590
2592
2591 Note that Mercurial will not let you commit files with unresolved merge
2593 Note that Mercurial will not let you commit files with unresolved merge
2592 conflicts. You must use ``hg resolve -m ...`` before you can commit
2594 conflicts. You must use ``hg resolve -m ...`` before you can commit
2593 after a conflicting merge.
2595 after a conflicting merge.
2594 """
2596 """
2595
2597
2596 all, mark, unmark, show, nostatus = \
2598 all, mark, unmark, show, nostatus = \
2597 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
2599 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
2598
2600
2599 if (show and (mark or unmark)) or (mark and unmark):
2601 if (show and (mark or unmark)) or (mark and unmark):
2600 raise util.Abort(_("too many options specified"))
2602 raise util.Abort(_("too many options specified"))
2601 if pats and all:
2603 if pats and all:
2602 raise util.Abort(_("can't specify --all and patterns"))
2604 raise util.Abort(_("can't specify --all and patterns"))
2603 if not (all or pats or show or mark or unmark):
2605 if not (all or pats or show or mark or unmark):
2604 raise util.Abort(_('no files or directories specified; '
2606 raise util.Abort(_('no files or directories specified; '
2605 'use --all to remerge all files'))
2607 'use --all to remerge all files'))
2606
2608
2607 ms = mergemod.mergestate(repo)
2609 ms = mergemod.mergestate(repo)
2608 m = cmdutil.match(repo, pats, opts)
2610 m = cmdutil.match(repo, pats, opts)
2609
2611
2610 for f in ms:
2612 for f in ms:
2611 if m(f):
2613 if m(f):
2612 if show:
2614 if show:
2613 if nostatus:
2615 if nostatus:
2614 ui.write("%s\n" % f)
2616 ui.write("%s\n" % f)
2615 else:
2617 else:
2616 ui.write("%s %s\n" % (ms[f].upper(), f),
2618 ui.write("%s %s\n" % (ms[f].upper(), f),
2617 label='resolve.' +
2619 label='resolve.' +
2618 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
2620 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
2619 elif mark:
2621 elif mark:
2620 ms.mark(f, "r")
2622 ms.mark(f, "r")
2621 elif unmark:
2623 elif unmark:
2622 ms.mark(f, "u")
2624 ms.mark(f, "u")
2623 else:
2625 else:
2624 wctx = repo[None]
2626 wctx = repo[None]
2625 mctx = wctx.parents()[-1]
2627 mctx = wctx.parents()[-1]
2626
2628
2627 # backup pre-resolve (merge uses .orig for its own purposes)
2629 # backup pre-resolve (merge uses .orig for its own purposes)
2628 a = repo.wjoin(f)
2630 a = repo.wjoin(f)
2629 util.copyfile(a, a + ".resolve")
2631 util.copyfile(a, a + ".resolve")
2630
2632
2631 # resolve file
2633 # resolve file
2632 ms.resolve(f, wctx, mctx)
2634 ms.resolve(f, wctx, mctx)
2633
2635
2634 # replace filemerge's .orig file with our resolve file
2636 # replace filemerge's .orig file with our resolve file
2635 util.rename(a + ".resolve", a + ".orig")
2637 util.rename(a + ".resolve", a + ".orig")
2636
2638
2637 def revert(ui, repo, *pats, **opts):
2639 def revert(ui, repo, *pats, **opts):
2638 """restore individual files or directories to an earlier state
2640 """restore individual files or directories to an earlier state
2639
2641
2640 (Use update -r to check out earlier revisions, revert does not
2642 (Use update -r to check out earlier revisions, revert does not
2641 change the working directory parents.)
2643 change the working directory parents.)
2642
2644
2643 With no revision specified, revert the named files or directories
2645 With no revision specified, revert the named files or directories
2644 to the contents they had in the parent of the working directory.
2646 to the contents they had in the parent of the working directory.
2645 This restores the contents of the affected files to an unmodified
2647 This restores the contents of the affected files to an unmodified
2646 state and unschedules adds, removes, copies, and renames. If the
2648 state and unschedules adds, removes, copies, and renames. If the
2647 working directory has two parents, you must explicitly specify a
2649 working directory has two parents, you must explicitly specify a
2648 revision.
2650 revision.
2649
2651
2650 Using the -r/--rev option, revert the given files or directories
2652 Using the -r/--rev option, revert the given files or directories
2651 to their contents as of a specific revision. This can be helpful
2653 to their contents as of a specific revision. This can be helpful
2652 to "roll back" some or all of an earlier change. See 'hg help
2654 to "roll back" some or all of an earlier change. See 'hg help
2653 dates' for a list of formats valid for -d/--date.
2655 dates' for a list of formats valid for -d/--date.
2654
2656
2655 Revert modifies the working directory. It does not commit any
2657 Revert modifies the working directory. It does not commit any
2656 changes, or change the parent of the working directory. If you
2658 changes, or change the parent of the working directory. If you
2657 revert to a revision other than the parent of the working
2659 revert to a revision other than the parent of the working
2658 directory, the reverted files will thus appear modified
2660 directory, the reverted files will thus appear modified
2659 afterwards.
2661 afterwards.
2660
2662
2661 If a file has been deleted, it is restored. If the executable mode
2663 If a file has been deleted, it is restored. If the executable mode
2662 of a file was changed, it is reset.
2664 of a file was changed, it is reset.
2663
2665
2664 If names are given, all files matching the names are reverted.
2666 If names are given, all files matching the names are reverted.
2665 If no arguments are given, no files are reverted.
2667 If no arguments are given, no files are reverted.
2666
2668
2667 Modified files are saved with a .orig suffix before reverting.
2669 Modified files are saved with a .orig suffix before reverting.
2668 To disable these backups, use --no-backup.
2670 To disable these backups, use --no-backup.
2669 """
2671 """
2670
2672
2671 if opts["date"]:
2673 if opts["date"]:
2672 if opts["rev"]:
2674 if opts["rev"]:
2673 raise util.Abort(_("you can't specify a revision and a date"))
2675 raise util.Abort(_("you can't specify a revision and a date"))
2674 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2676 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2675
2677
2676 if not pats and not opts.get('all'):
2678 if not pats and not opts.get('all'):
2677 raise util.Abort(_('no files or directories specified; '
2679 raise util.Abort(_('no files or directories specified; '
2678 'use --all to revert the whole repo'))
2680 'use --all to revert the whole repo'))
2679
2681
2680 parent, p2 = repo.dirstate.parents()
2682 parent, p2 = repo.dirstate.parents()
2681 if not opts.get('rev') and p2 != nullid:
2683 if not opts.get('rev') and p2 != nullid:
2682 raise util.Abort(_('uncommitted merge - please provide a '
2684 raise util.Abort(_('uncommitted merge - please provide a '
2683 'specific revision'))
2685 'specific revision'))
2684 ctx = repo[opts.get('rev')]
2686 ctx = repo[opts.get('rev')]
2685 node = ctx.node()
2687 node = ctx.node()
2686 mf = ctx.manifest()
2688 mf = ctx.manifest()
2687 if node == parent:
2689 if node == parent:
2688 pmf = mf
2690 pmf = mf
2689 else:
2691 else:
2690 pmf = None
2692 pmf = None
2691
2693
2692 # need all matching names in dirstate and manifest of target rev,
2694 # need all matching names in dirstate and manifest of target rev,
2693 # so have to walk both. do not print errors if files exist in one
2695 # so have to walk both. do not print errors if files exist in one
2694 # but not other.
2696 # but not other.
2695
2697
2696 names = {}
2698 names = {}
2697
2699
2698 wlock = repo.wlock()
2700 wlock = repo.wlock()
2699 try:
2701 try:
2700 # walk dirstate.
2702 # walk dirstate.
2701
2703
2702 m = cmdutil.match(repo, pats, opts)
2704 m = cmdutil.match(repo, pats, opts)
2703 m.bad = lambda x, y: False
2705 m.bad = lambda x, y: False
2704 for abs in repo.walk(m):
2706 for abs in repo.walk(m):
2705 names[abs] = m.rel(abs), m.exact(abs)
2707 names[abs] = m.rel(abs), m.exact(abs)
2706
2708
2707 # walk target manifest.
2709 # walk target manifest.
2708
2710
2709 def badfn(path, msg):
2711 def badfn(path, msg):
2710 if path in names:
2712 if path in names:
2711 return
2713 return
2712 path_ = path + '/'
2714 path_ = path + '/'
2713 for f in names:
2715 for f in names:
2714 if f.startswith(path_):
2716 if f.startswith(path_):
2715 return
2717 return
2716 ui.warn("%s: %s\n" % (m.rel(path), msg))
2718 ui.warn("%s: %s\n" % (m.rel(path), msg))
2717
2719
2718 m = cmdutil.match(repo, pats, opts)
2720 m = cmdutil.match(repo, pats, opts)
2719 m.bad = badfn
2721 m.bad = badfn
2720 for abs in repo[node].walk(m):
2722 for abs in repo[node].walk(m):
2721 if abs not in names:
2723 if abs not in names:
2722 names[abs] = m.rel(abs), m.exact(abs)
2724 names[abs] = m.rel(abs), m.exact(abs)
2723
2725
2724 m = cmdutil.matchfiles(repo, names)
2726 m = cmdutil.matchfiles(repo, names)
2725 changes = repo.status(match=m)[:4]
2727 changes = repo.status(match=m)[:4]
2726 modified, added, removed, deleted = map(set, changes)
2728 modified, added, removed, deleted = map(set, changes)
2727
2729
2728 # if f is a rename, also revert the source
2730 # if f is a rename, also revert the source
2729 cwd = repo.getcwd()
2731 cwd = repo.getcwd()
2730 for f in added:
2732 for f in added:
2731 src = repo.dirstate.copied(f)
2733 src = repo.dirstate.copied(f)
2732 if src and src not in names and repo.dirstate[src] == 'r':
2734 if src and src not in names and repo.dirstate[src] == 'r':
2733 removed.add(src)
2735 removed.add(src)
2734 names[src] = (repo.pathto(src, cwd), True)
2736 names[src] = (repo.pathto(src, cwd), True)
2735
2737
2736 def removeforget(abs):
2738 def removeforget(abs):
2737 if repo.dirstate[abs] == 'a':
2739 if repo.dirstate[abs] == 'a':
2738 return _('forgetting %s\n')
2740 return _('forgetting %s\n')
2739 return _('removing %s\n')
2741 return _('removing %s\n')
2740
2742
2741 revert = ([], _('reverting %s\n'))
2743 revert = ([], _('reverting %s\n'))
2742 add = ([], _('adding %s\n'))
2744 add = ([], _('adding %s\n'))
2743 remove = ([], removeforget)
2745 remove = ([], removeforget)
2744 undelete = ([], _('undeleting %s\n'))
2746 undelete = ([], _('undeleting %s\n'))
2745
2747
2746 disptable = (
2748 disptable = (
2747 # dispatch table:
2749 # dispatch table:
2748 # file state
2750 # file state
2749 # action if in target manifest
2751 # action if in target manifest
2750 # action if not in target manifest
2752 # action if not in target manifest
2751 # make backup if in target manifest
2753 # make backup if in target manifest
2752 # make backup if not in target manifest
2754 # make backup if not in target manifest
2753 (modified, revert, remove, True, True),
2755 (modified, revert, remove, True, True),
2754 (added, revert, remove, True, False),
2756 (added, revert, remove, True, False),
2755 (removed, undelete, None, False, False),
2757 (removed, undelete, None, False, False),
2756 (deleted, revert, remove, False, False),
2758 (deleted, revert, remove, False, False),
2757 )
2759 )
2758
2760
2759 for abs, (rel, exact) in sorted(names.items()):
2761 for abs, (rel, exact) in sorted(names.items()):
2760 mfentry = mf.get(abs)
2762 mfentry = mf.get(abs)
2761 target = repo.wjoin(abs)
2763 target = repo.wjoin(abs)
2762 def handle(xlist, dobackup):
2764 def handle(xlist, dobackup):
2763 xlist[0].append(abs)
2765 xlist[0].append(abs)
2764 if dobackup and not opts.get('no_backup') and util.lexists(target):
2766 if dobackup and not opts.get('no_backup') and util.lexists(target):
2765 bakname = "%s.orig" % rel
2767 bakname = "%s.orig" % rel
2766 ui.note(_('saving current version of %s as %s\n') %
2768 ui.note(_('saving current version of %s as %s\n') %
2767 (rel, bakname))
2769 (rel, bakname))
2768 if not opts.get('dry_run'):
2770 if not opts.get('dry_run'):
2769 util.copyfile(target, bakname)
2771 util.copyfile(target, bakname)
2770 if ui.verbose or not exact:
2772 if ui.verbose or not exact:
2771 msg = xlist[1]
2773 msg = xlist[1]
2772 if not isinstance(msg, basestring):
2774 if not isinstance(msg, basestring):
2773 msg = msg(abs)
2775 msg = msg(abs)
2774 ui.status(msg % rel)
2776 ui.status(msg % rel)
2775 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2777 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2776 if abs not in table:
2778 if abs not in table:
2777 continue
2779 continue
2778 # file has changed in dirstate
2780 # file has changed in dirstate
2779 if mfentry:
2781 if mfentry:
2780 handle(hitlist, backuphit)
2782 handle(hitlist, backuphit)
2781 elif misslist is not None:
2783 elif misslist is not None:
2782 handle(misslist, backupmiss)
2784 handle(misslist, backupmiss)
2783 break
2785 break
2784 else:
2786 else:
2785 if abs not in repo.dirstate:
2787 if abs not in repo.dirstate:
2786 if mfentry:
2788 if mfentry:
2787 handle(add, True)
2789 handle(add, True)
2788 elif exact:
2790 elif exact:
2789 ui.warn(_('file not managed: %s\n') % rel)
2791 ui.warn(_('file not managed: %s\n') % rel)
2790 continue
2792 continue
2791 # file has not changed in dirstate
2793 # file has not changed in dirstate
2792 if node == parent:
2794 if node == parent:
2793 if exact:
2795 if exact:
2794 ui.warn(_('no changes needed to %s\n') % rel)
2796 ui.warn(_('no changes needed to %s\n') % rel)
2795 continue
2797 continue
2796 if pmf is None:
2798 if pmf is None:
2797 # only need parent manifest in this unlikely case,
2799 # only need parent manifest in this unlikely case,
2798 # so do not read by default
2800 # so do not read by default
2799 pmf = repo[parent].manifest()
2801 pmf = repo[parent].manifest()
2800 if abs in pmf:
2802 if abs in pmf:
2801 if mfentry:
2803 if mfentry:
2802 # if version of file is same in parent and target
2804 # if version of file is same in parent and target
2803 # manifests, do nothing
2805 # manifests, do nothing
2804 if (pmf[abs] != mfentry or
2806 if (pmf[abs] != mfentry or
2805 pmf.flags(abs) != mf.flags(abs)):
2807 pmf.flags(abs) != mf.flags(abs)):
2806 handle(revert, False)
2808 handle(revert, False)
2807 else:
2809 else:
2808 handle(remove, False)
2810 handle(remove, False)
2809
2811
2810 if not opts.get('dry_run'):
2812 if not opts.get('dry_run'):
2811 def checkout(f):
2813 def checkout(f):
2812 fc = ctx[f]
2814 fc = ctx[f]
2813 repo.wwrite(f, fc.data(), fc.flags())
2815 repo.wwrite(f, fc.data(), fc.flags())
2814
2816
2815 audit_path = util.path_auditor(repo.root)
2817 audit_path = util.path_auditor(repo.root)
2816 for f in remove[0]:
2818 for f in remove[0]:
2817 if repo.dirstate[f] == 'a':
2819 if repo.dirstate[f] == 'a':
2818 repo.dirstate.forget(f)
2820 repo.dirstate.forget(f)
2819 continue
2821 continue
2820 audit_path(f)
2822 audit_path(f)
2821 try:
2823 try:
2822 util.unlink(repo.wjoin(f))
2824 util.unlink(repo.wjoin(f))
2823 except OSError:
2825 except OSError:
2824 pass
2826 pass
2825 repo.dirstate.remove(f)
2827 repo.dirstate.remove(f)
2826
2828
2827 normal = None
2829 normal = None
2828 if node == parent:
2830 if node == parent:
2829 # We're reverting to our parent. If possible, we'd like status
2831 # We're reverting to our parent. If possible, we'd like status
2830 # to report the file as clean. We have to use normallookup for
2832 # to report the file as clean. We have to use normallookup for
2831 # merges to avoid losing information about merged/dirty files.
2833 # merges to avoid losing information about merged/dirty files.
2832 if p2 != nullid:
2834 if p2 != nullid:
2833 normal = repo.dirstate.normallookup
2835 normal = repo.dirstate.normallookup
2834 else:
2836 else:
2835 normal = repo.dirstate.normal
2837 normal = repo.dirstate.normal
2836 for f in revert[0]:
2838 for f in revert[0]:
2837 checkout(f)
2839 checkout(f)
2838 if normal:
2840 if normal:
2839 normal(f)
2841 normal(f)
2840
2842
2841 for f in add[0]:
2843 for f in add[0]:
2842 checkout(f)
2844 checkout(f)
2843 repo.dirstate.add(f)
2845 repo.dirstate.add(f)
2844
2846
2845 normal = repo.dirstate.normallookup
2847 normal = repo.dirstate.normallookup
2846 if node == parent and p2 == nullid:
2848 if node == parent and p2 == nullid:
2847 normal = repo.dirstate.normal
2849 normal = repo.dirstate.normal
2848 for f in undelete[0]:
2850 for f in undelete[0]:
2849 checkout(f)
2851 checkout(f)
2850 normal(f)
2852 normal(f)
2851
2853
2852 finally:
2854 finally:
2853 wlock.release()
2855 wlock.release()
2854
2856
2855 def rollback(ui, repo):
2857 def rollback(ui, repo):
2856 """roll back the last transaction
2858 """roll back the last transaction
2857
2859
2858 This command should be used with care. There is only one level of
2860 This command should be used with care. There is only one level of
2859 rollback, and there is no way to undo a rollback. It will also
2861 rollback, and there is no way to undo a rollback. It will also
2860 restore the dirstate at the time of the last transaction, losing
2862 restore the dirstate at the time of the last transaction, losing
2861 any dirstate changes since that time. This command does not alter
2863 any dirstate changes since that time. This command does not alter
2862 the working directory.
2864 the working directory.
2863
2865
2864 Transactions are used to encapsulate the effects of all commands
2866 Transactions are used to encapsulate the effects of all commands
2865 that create new changesets or propagate existing changesets into a
2867 that create new changesets or propagate existing changesets into a
2866 repository. For example, the following commands are transactional,
2868 repository. For example, the following commands are transactional,
2867 and their effects can be rolled back:
2869 and their effects can be rolled back:
2868
2870
2869 - commit
2871 - commit
2870 - import
2872 - import
2871 - pull
2873 - pull
2872 - push (with this repository as the destination)
2874 - push (with this repository as the destination)
2873 - unbundle
2875 - unbundle
2874
2876
2875 This command is not intended for use on public repositories. Once
2877 This command is not intended for use on public repositories. Once
2876 changes are visible for pull by other users, rolling a transaction
2878 changes are visible for pull by other users, rolling a transaction
2877 back locally is ineffective (someone else may already have pulled
2879 back locally is ineffective (someone else may already have pulled
2878 the changes). Furthermore, a race is possible with readers of the
2880 the changes). Furthermore, a race is possible with readers of the
2879 repository; for example an in-progress pull from the repository
2881 repository; for example an in-progress pull from the repository
2880 may fail if a rollback is performed.
2882 may fail if a rollback is performed.
2881 """
2883 """
2882 repo.rollback()
2884 repo.rollback()
2883
2885
2884 def root(ui, repo):
2886 def root(ui, repo):
2885 """print the root (top) of the current working directory
2887 """print the root (top) of the current working directory
2886
2888
2887 Print the root directory of the current repository.
2889 Print the root directory of the current repository.
2888 """
2890 """
2889 ui.write(repo.root + "\n")
2891 ui.write(repo.root + "\n")
2890
2892
2891 def serve(ui, repo, **opts):
2893 def serve(ui, repo, **opts):
2892 """export the repository via HTTP
2894 """export the repository via HTTP
2893
2895
2894 Start a local HTTP repository browser and pull server.
2896 Start a local HTTP repository browser and pull server.
2895
2897
2896 By default, the server logs accesses to stdout and errors to
2898 By default, the server logs accesses to stdout and errors to
2897 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
2899 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
2898 files.
2900 files.
2899
2901
2900 To have the server choose a free port number to listen on, specify
2902 To have the server choose a free port number to listen on, specify
2901 a port number of 0; in this case, the server will print the port
2903 a port number of 0; in this case, the server will print the port
2902 number it uses.
2904 number it uses.
2903 """
2905 """
2904
2906
2905 if opts["stdio"]:
2907 if opts["stdio"]:
2906 if repo is None:
2908 if repo is None:
2907 raise error.RepoError(_("There is no Mercurial repository here"
2909 raise error.RepoError(_("There is no Mercurial repository here"
2908 " (.hg not found)"))
2910 " (.hg not found)"))
2909 s = sshserver.sshserver(ui, repo)
2911 s = sshserver.sshserver(ui, repo)
2910 s.serve_forever()
2912 s.serve_forever()
2911
2913
2912 # this way we can check if something was given in the command-line
2914 # this way we can check if something was given in the command-line
2913 if opts.get('port'):
2915 if opts.get('port'):
2914 opts['port'] = int(opts.get('port'))
2916 opts['port'] = int(opts.get('port'))
2915
2917
2916 baseui = repo and repo.baseui or ui
2918 baseui = repo and repo.baseui or ui
2917 optlist = ("name templates style address port prefix ipv6"
2919 optlist = ("name templates style address port prefix ipv6"
2918 " accesslog errorlog certificate encoding")
2920 " accesslog errorlog certificate encoding")
2919 for o in optlist.split():
2921 for o in optlist.split():
2920 val = opts.get(o, '')
2922 val = opts.get(o, '')
2921 if val in (None, ''): # should check against default options instead
2923 if val in (None, ''): # should check against default options instead
2922 continue
2924 continue
2923 baseui.setconfig("web", o, val)
2925 baseui.setconfig("web", o, val)
2924 if repo and repo.ui != baseui:
2926 if repo and repo.ui != baseui:
2925 repo.ui.setconfig("web", o, val)
2927 repo.ui.setconfig("web", o, val)
2926
2928
2927 if opts.get('webdir_conf'):
2929 if opts.get('webdir_conf'):
2928 app = hgwebdir_mod.hgwebdir(opts['webdir_conf'], ui)
2930 app = hgwebdir_mod.hgwebdir(opts['webdir_conf'], ui)
2929 elif repo is not None:
2931 elif repo is not None:
2930 app = hgweb_mod.hgweb(hg.repository(repo.ui, repo.root))
2932 app = hgweb_mod.hgweb(hg.repository(repo.ui, repo.root))
2931 else:
2933 else:
2932 raise error.RepoError(_("There is no Mercurial repository"
2934 raise error.RepoError(_("There is no Mercurial repository"
2933 " here (.hg not found)"))
2935 " here (.hg not found)"))
2934
2936
2935 class service(object):
2937 class service(object):
2936 def init(self):
2938 def init(self):
2937 util.set_signal_handler()
2939 util.set_signal_handler()
2938 self.httpd = server.create_server(ui, app)
2940 self.httpd = server.create_server(ui, app)
2939
2941
2940 if opts['port'] and not ui.verbose:
2942 if opts['port'] and not ui.verbose:
2941 return
2943 return
2942
2944
2943 if self.httpd.prefix:
2945 if self.httpd.prefix:
2944 prefix = self.httpd.prefix.strip('/') + '/'
2946 prefix = self.httpd.prefix.strip('/') + '/'
2945 else:
2947 else:
2946 prefix = ''
2948 prefix = ''
2947
2949
2948 port = ':%d' % self.httpd.port
2950 port = ':%d' % self.httpd.port
2949 if port == ':80':
2951 if port == ':80':
2950 port = ''
2952 port = ''
2951
2953
2952 bindaddr = self.httpd.addr
2954 bindaddr = self.httpd.addr
2953 if bindaddr == '0.0.0.0':
2955 if bindaddr == '0.0.0.0':
2954 bindaddr = '*'
2956 bindaddr = '*'
2955 elif ':' in bindaddr: # IPv6
2957 elif ':' in bindaddr: # IPv6
2956 bindaddr = '[%s]' % bindaddr
2958 bindaddr = '[%s]' % bindaddr
2957
2959
2958 fqaddr = self.httpd.fqaddr
2960 fqaddr = self.httpd.fqaddr
2959 if ':' in fqaddr:
2961 if ':' in fqaddr:
2960 fqaddr = '[%s]' % fqaddr
2962 fqaddr = '[%s]' % fqaddr
2961 if opts['port']:
2963 if opts['port']:
2962 write = ui.status
2964 write = ui.status
2963 else:
2965 else:
2964 write = ui.write
2966 write = ui.write
2965 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2967 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2966 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2968 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2967
2969
2968 def run(self):
2970 def run(self):
2969 self.httpd.serve_forever()
2971 self.httpd.serve_forever()
2970
2972
2971 service = service()
2973 service = service()
2972
2974
2973 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2975 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2974
2976
2975 def status(ui, repo, *pats, **opts):
2977 def status(ui, repo, *pats, **opts):
2976 """show changed files in the working directory
2978 """show changed files in the working directory
2977
2979
2978 Show status of files in the repository. If names are given, only
2980 Show status of files in the repository. If names are given, only
2979 files that match are shown. Files that are clean or ignored or
2981 files that match are shown. Files that are clean or ignored or
2980 the source of a copy/move operation, are not listed unless
2982 the source of a copy/move operation, are not listed unless
2981 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
2983 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
2982 Unless options described with "show only ..." are given, the
2984 Unless options described with "show only ..." are given, the
2983 options -mardu are used.
2985 options -mardu are used.
2984
2986
2985 Option -q/--quiet hides untracked (unknown and ignored) files
2987 Option -q/--quiet hides untracked (unknown and ignored) files
2986 unless explicitly requested with -u/--unknown or -i/--ignored.
2988 unless explicitly requested with -u/--unknown or -i/--ignored.
2987
2989
2988 NOTE: status may appear to disagree with diff if permissions have
2990 NOTE: status may appear to disagree with diff if permissions have
2989 changed or a merge has occurred. The standard diff format does not
2991 changed or a merge has occurred. The standard diff format does not
2990 report permission changes and diff only reports changes relative
2992 report permission changes and diff only reports changes relative
2991 to one merge parent.
2993 to one merge parent.
2992
2994
2993 If one revision is given, it is used as the base revision.
2995 If one revision is given, it is used as the base revision.
2994 If two revisions are given, the differences between them are
2996 If two revisions are given, the differences between them are
2995 shown. The --change option can also be used as a shortcut to list
2997 shown. The --change option can also be used as a shortcut to list
2996 the changed files of a revision from its first parent.
2998 the changed files of a revision from its first parent.
2997
2999
2998 The codes used to show the status of files are::
3000 The codes used to show the status of files are::
2999
3001
3000 M = modified
3002 M = modified
3001 A = added
3003 A = added
3002 R = removed
3004 R = removed
3003 C = clean
3005 C = clean
3004 ! = missing (deleted by non-hg command, but still tracked)
3006 ! = missing (deleted by non-hg command, but still tracked)
3005 ? = not tracked
3007 ? = not tracked
3006 I = ignored
3008 I = ignored
3007 = origin of the previous file listed as A (added)
3009 = origin of the previous file listed as A (added)
3008 """
3010 """
3009
3011
3010 revs = opts.get('rev')
3012 revs = opts.get('rev')
3011 change = opts.get('change')
3013 change = opts.get('change')
3012
3014
3013 if revs and change:
3015 if revs and change:
3014 msg = _('cannot specify --rev and --change at the same time')
3016 msg = _('cannot specify --rev and --change at the same time')
3015 raise util.Abort(msg)
3017 raise util.Abort(msg)
3016 elif change:
3018 elif change:
3017 node2 = repo.lookup(change)
3019 node2 = repo.lookup(change)
3018 node1 = repo[node2].parents()[0].node()
3020 node1 = repo[node2].parents()[0].node()
3019 else:
3021 else:
3020 node1, node2 = cmdutil.revpair(repo, revs)
3022 node1, node2 = cmdutil.revpair(repo, revs)
3021
3023
3022 cwd = (pats and repo.getcwd()) or ''
3024 cwd = (pats and repo.getcwd()) or ''
3023 end = opts.get('print0') and '\0' or '\n'
3025 end = opts.get('print0') and '\0' or '\n'
3024 copy = {}
3026 copy = {}
3025 states = 'modified added removed deleted unknown ignored clean'.split()
3027 states = 'modified added removed deleted unknown ignored clean'.split()
3026 show = [k for k in states if opts.get(k)]
3028 show = [k for k in states if opts.get(k)]
3027 if opts.get('all'):
3029 if opts.get('all'):
3028 show += ui.quiet and (states[:4] + ['clean']) or states
3030 show += ui.quiet and (states[:4] + ['clean']) or states
3029 if not show:
3031 if not show:
3030 show = ui.quiet and states[:4] or states[:5]
3032 show = ui.quiet and states[:4] or states[:5]
3031
3033
3032 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
3034 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
3033 'ignored' in show, 'clean' in show, 'unknown' in show)
3035 'ignored' in show, 'clean' in show, 'unknown' in show)
3034 changestates = zip(states, 'MAR!?IC', stat)
3036 changestates = zip(states, 'MAR!?IC', stat)
3035
3037
3036 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
3038 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
3037 ctxn = repo[nullid]
3039 ctxn = repo[nullid]
3038 ctx1 = repo[node1]
3040 ctx1 = repo[node1]
3039 ctx2 = repo[node2]
3041 ctx2 = repo[node2]
3040 added = stat[1]
3042 added = stat[1]
3041 if node2 is None:
3043 if node2 is None:
3042 added = stat[0] + stat[1] # merged?
3044 added = stat[0] + stat[1] # merged?
3043
3045
3044 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
3046 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
3045 if k in added:
3047 if k in added:
3046 copy[k] = v
3048 copy[k] = v
3047 elif v in added:
3049 elif v in added:
3048 copy[v] = k
3050 copy[v] = k
3049
3051
3050 for state, char, files in changestates:
3052 for state, char, files in changestates:
3051 if state in show:
3053 if state in show:
3052 format = "%s %%s%s" % (char, end)
3054 format = "%s %%s%s" % (char, end)
3053 if opts.get('no_status'):
3055 if opts.get('no_status'):
3054 format = "%%s%s" % end
3056 format = "%%s%s" % end
3055
3057
3056 for f in files:
3058 for f in files:
3057 ui.write(format % repo.pathto(f, cwd),
3059 ui.write(format % repo.pathto(f, cwd),
3058 label='status.' + state)
3060 label='status.' + state)
3059 if f in copy:
3061 if f in copy:
3060 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
3062 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
3061 label='status.copied')
3063 label='status.copied')
3062
3064
3063 def summary(ui, repo, **opts):
3065 def summary(ui, repo, **opts):
3064 """summarize working directory state
3066 """summarize working directory state
3065
3067
3066 This generates a brief summary of the working directory state,
3068 This generates a brief summary of the working directory state,
3067 including parents, branch, commit status, and available updates.
3069 including parents, branch, commit status, and available updates.
3068
3070
3069 With the --remote option, this will check the default paths for
3071 With the --remote option, this will check the default paths for
3070 incoming and outgoing changes. This can be time-consuming.
3072 incoming and outgoing changes. This can be time-consuming.
3071 """
3073 """
3072
3074
3073 ctx = repo[None]
3075 ctx = repo[None]
3074 parents = ctx.parents()
3076 parents = ctx.parents()
3075 pnode = parents[0].node()
3077 pnode = parents[0].node()
3076 tags = repo.tags()
3078 tags = repo.tags()
3077
3079
3078 for p in parents:
3080 for p in parents:
3079 t = ' '.join([t for t in tags if tags[t] == p.node()])
3081 t = ' '.join([t for t in tags if tags[t] == p.node()])
3080 if p.rev() == -1:
3082 if p.rev() == -1:
3081 if not len(repo):
3083 if not len(repo):
3082 t += _(' (empty repository)')
3084 t += _(' (empty repository)')
3083 else:
3085 else:
3084 t += _(' (no revision checked out)')
3086 t += _(' (no revision checked out)')
3085 ui.write(_('parent: %d:%s %s\n') % (p.rev(), str(p), t))
3087 ui.write(_('parent: %d:%s %s\n') % (p.rev(), str(p), t))
3086 if p.description():
3088 if p.description():
3087 ui.status(' ' + p.description().splitlines()[0].strip() + '\n')
3089 ui.status(' ' + p.description().splitlines()[0].strip() + '\n')
3088
3090
3089 branch = ctx.branch()
3091 branch = ctx.branch()
3090 bheads = repo.branchheads(branch)
3092 bheads = repo.branchheads(branch)
3091 m = _('branch: %s\n') % branch
3093 m = _('branch: %s\n') % branch
3092 if branch != 'default':
3094 if branch != 'default':
3093 ui.write(m)
3095 ui.write(m)
3094 else:
3096 else:
3095 ui.status(m)
3097 ui.status(m)
3096
3098
3097 st = list(repo.status(unknown=True))[:6]
3099 st = list(repo.status(unknown=True))[:6]
3098 ms = mergemod.mergestate(repo)
3100 ms = mergemod.mergestate(repo)
3099 st.append([f for f in ms if ms[f] == 'u'])
3101 st.append([f for f in ms if ms[f] == 'u'])
3100 labels = [_('%d modified'), _('%d added'), _('%d removed'),
3102 labels = [_('%d modified'), _('%d added'), _('%d removed'),
3101 _('%d deleted'), _('%d unknown'), _('%d ignored'),
3103 _('%d deleted'), _('%d unknown'), _('%d ignored'),
3102 _('%d unresolved')]
3104 _('%d unresolved')]
3103 t = []
3105 t = []
3104 for s, l in zip(st, labels):
3106 for s, l in zip(st, labels):
3105 if s:
3107 if s:
3106 t.append(l % len(s))
3108 t.append(l % len(s))
3107
3109
3108 t = ', '.join(t)
3110 t = ', '.join(t)
3109 cleanworkdir = False
3111 cleanworkdir = False
3110
3112
3111 if len(parents) > 1:
3113 if len(parents) > 1:
3112 t += _(' (merge)')
3114 t += _(' (merge)')
3113 elif branch != parents[0].branch():
3115 elif branch != parents[0].branch():
3114 t += _(' (new branch)')
3116 t += _(' (new branch)')
3115 elif (not st[0] and not st[1] and not st[2]):
3117 elif (not st[0] and not st[1] and not st[2]):
3116 t += _(' (clean)')
3118 t += _(' (clean)')
3117 cleanworkdir = True
3119 cleanworkdir = True
3118 elif pnode not in bheads:
3120 elif pnode not in bheads:
3119 t += _(' (new branch head)')
3121 t += _(' (new branch head)')
3120
3122
3121 if cleanworkdir:
3123 if cleanworkdir:
3122 ui.status(_('commit: %s\n') % t.strip())
3124 ui.status(_('commit: %s\n') % t.strip())
3123 else:
3125 else:
3124 ui.write(_('commit: %s\n') % t.strip())
3126 ui.write(_('commit: %s\n') % t.strip())
3125
3127
3126 # all ancestors of branch heads - all ancestors of parent = new csets
3128 # all ancestors of branch heads - all ancestors of parent = new csets
3127 new = [0] * len(repo)
3129 new = [0] * len(repo)
3128 cl = repo.changelog
3130 cl = repo.changelog
3129 for a in [cl.rev(n) for n in bheads]:
3131 for a in [cl.rev(n) for n in bheads]:
3130 new[a] = 1
3132 new[a] = 1
3131 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
3133 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
3132 new[a] = 1
3134 new[a] = 1
3133 for a in [p.rev() for p in parents]:
3135 for a in [p.rev() for p in parents]:
3134 if a >= 0:
3136 if a >= 0:
3135 new[a] = 0
3137 new[a] = 0
3136 for a in cl.ancestors(*[p.rev() for p in parents]):
3138 for a in cl.ancestors(*[p.rev() for p in parents]):
3137 new[a] = 0
3139 new[a] = 0
3138 new = sum(new)
3140 new = sum(new)
3139
3141
3140 if new == 0:
3142 if new == 0:
3141 ui.status(_('update: (current)\n'))
3143 ui.status(_('update: (current)\n'))
3142 elif pnode not in bheads:
3144 elif pnode not in bheads:
3143 ui.write(_('update: %d new changesets (update)\n') % new)
3145 ui.write(_('update: %d new changesets (update)\n') % new)
3144 else:
3146 else:
3145 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
3147 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
3146 (new, len(bheads)))
3148 (new, len(bheads)))
3147
3149
3148 if opts.get('remote'):
3150 if opts.get('remote'):
3149 t = []
3151 t = []
3150 source, branches = hg.parseurl(ui.expandpath('default'))
3152 source, branches = hg.parseurl(ui.expandpath('default'))
3151 other = hg.repository(cmdutil.remoteui(repo, {}), source)
3153 other = hg.repository(cmdutil.remoteui(repo, {}), source)
3152 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3154 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3153 ui.debug('comparing with %s\n' % url.hidepassword(source))
3155 ui.debug('comparing with %s\n' % url.hidepassword(source))
3154 repo.ui.pushbuffer()
3156 repo.ui.pushbuffer()
3155 common, incoming, rheads = repo.findcommonincoming(other)
3157 common, incoming, rheads = repo.findcommonincoming(other)
3156 repo.ui.popbuffer()
3158 repo.ui.popbuffer()
3157 if incoming:
3159 if incoming:
3158 t.append(_('1 or more incoming'))
3160 t.append(_('1 or more incoming'))
3159
3161
3160 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
3162 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
3161 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3163 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3162 other = hg.repository(cmdutil.remoteui(repo, {}), dest)
3164 other = hg.repository(cmdutil.remoteui(repo, {}), dest)
3163 ui.debug('comparing with %s\n' % url.hidepassword(dest))
3165 ui.debug('comparing with %s\n' % url.hidepassword(dest))
3164 repo.ui.pushbuffer()
3166 repo.ui.pushbuffer()
3165 o = repo.findoutgoing(other)
3167 o = repo.findoutgoing(other)
3166 repo.ui.popbuffer()
3168 repo.ui.popbuffer()
3167 o = repo.changelog.nodesbetween(o, None)[0]
3169 o = repo.changelog.nodesbetween(o, None)[0]
3168 if o:
3170 if o:
3169 t.append(_('%d outgoing') % len(o))
3171 t.append(_('%d outgoing') % len(o))
3170
3172
3171 if t:
3173 if t:
3172 ui.write(_('remote: %s\n') % (', '.join(t)))
3174 ui.write(_('remote: %s\n') % (', '.join(t)))
3173 else:
3175 else:
3174 ui.status(_('remote: (synced)\n'))
3176 ui.status(_('remote: (synced)\n'))
3175
3177
3176 def tag(ui, repo, name1, *names, **opts):
3178 def tag(ui, repo, name1, *names, **opts):
3177 """add one or more tags for the current or given revision
3179 """add one or more tags for the current or given revision
3178
3180
3179 Name a particular revision using <name>.
3181 Name a particular revision using <name>.
3180
3182
3181 Tags are used to name particular revisions of the repository and are
3183 Tags are used to name particular revisions of the repository and are
3182 very useful to compare different revisions, to go back to significant
3184 very useful to compare different revisions, to go back to significant
3183 earlier versions or to mark branch points as releases, etc.
3185 earlier versions or to mark branch points as releases, etc.
3184
3186
3185 If no revision is given, the parent of the working directory is
3187 If no revision is given, the parent of the working directory is
3186 used, or tip if no revision is checked out.
3188 used, or tip if no revision is checked out.
3187
3189
3188 To facilitate version control, distribution, and merging of tags,
3190 To facilitate version control, distribution, and merging of tags,
3189 they are stored as a file named ".hgtags" which is managed
3191 they are stored as a file named ".hgtags" which is managed
3190 similarly to other project files and can be hand-edited if
3192 similarly to other project files and can be hand-edited if
3191 necessary. The file '.hg/localtags' is used for local tags (not
3193 necessary. The file '.hg/localtags' is used for local tags (not
3192 shared among repositories).
3194 shared among repositories).
3193
3195
3194 See 'hg help dates' for a list of formats valid for -d/--date.
3196 See 'hg help dates' for a list of formats valid for -d/--date.
3195 """
3197 """
3196
3198
3197 rev_ = "."
3199 rev_ = "."
3198 names = (name1,) + names
3200 names = (name1,) + names
3199 if len(names) != len(set(names)):
3201 if len(names) != len(set(names)):
3200 raise util.Abort(_('tag names must be unique'))
3202 raise util.Abort(_('tag names must be unique'))
3201 for n in names:
3203 for n in names:
3202 if n in ['tip', '.', 'null']:
3204 if n in ['tip', '.', 'null']:
3203 raise util.Abort(_('the name \'%s\' is reserved') % n)
3205 raise util.Abort(_('the name \'%s\' is reserved') % n)
3204 if opts.get('rev') and opts.get('remove'):
3206 if opts.get('rev') and opts.get('remove'):
3205 raise util.Abort(_("--rev and --remove are incompatible"))
3207 raise util.Abort(_("--rev and --remove are incompatible"))
3206 if opts.get('rev'):
3208 if opts.get('rev'):
3207 rev_ = opts['rev']
3209 rev_ = opts['rev']
3208 message = opts.get('message')
3210 message = opts.get('message')
3209 if opts.get('remove'):
3211 if opts.get('remove'):
3210 expectedtype = opts.get('local') and 'local' or 'global'
3212 expectedtype = opts.get('local') and 'local' or 'global'
3211 for n in names:
3213 for n in names:
3212 if not repo.tagtype(n):
3214 if not repo.tagtype(n):
3213 raise util.Abort(_('tag \'%s\' does not exist') % n)
3215 raise util.Abort(_('tag \'%s\' does not exist') % n)
3214 if repo.tagtype(n) != expectedtype:
3216 if repo.tagtype(n) != expectedtype:
3215 if expectedtype == 'global':
3217 if expectedtype == 'global':
3216 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
3218 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
3217 else:
3219 else:
3218 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
3220 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
3219 rev_ = nullid
3221 rev_ = nullid
3220 if not message:
3222 if not message:
3221 # we don't translate commit messages
3223 # we don't translate commit messages
3222 message = 'Removed tag %s' % ', '.join(names)
3224 message = 'Removed tag %s' % ', '.join(names)
3223 elif not opts.get('force'):
3225 elif not opts.get('force'):
3224 for n in names:
3226 for n in names:
3225 if n in repo.tags():
3227 if n in repo.tags():
3226 raise util.Abort(_('tag \'%s\' already exists '
3228 raise util.Abort(_('tag \'%s\' already exists '
3227 '(use -f to force)') % n)
3229 '(use -f to force)') % n)
3228 if not rev_ and repo.dirstate.parents()[1] != nullid:
3230 if not rev_ and repo.dirstate.parents()[1] != nullid:
3229 raise util.Abort(_('uncommitted merge - please provide a '
3231 raise util.Abort(_('uncommitted merge - please provide a '
3230 'specific revision'))
3232 'specific revision'))
3231 r = repo[rev_].node()
3233 r = repo[rev_].node()
3232
3234
3233 if not message:
3235 if not message:
3234 # we don't translate commit messages
3236 # we don't translate commit messages
3235 message = ('Added tag %s for changeset %s' %
3237 message = ('Added tag %s for changeset %s' %
3236 (', '.join(names), short(r)))
3238 (', '.join(names), short(r)))
3237
3239
3238 date = opts.get('date')
3240 date = opts.get('date')
3239 if date:
3241 if date:
3240 date = util.parsedate(date)
3242 date = util.parsedate(date)
3241
3243
3242 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
3244 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
3243
3245
3244 def tags(ui, repo):
3246 def tags(ui, repo):
3245 """list repository tags
3247 """list repository tags
3246
3248
3247 This lists both regular and local tags. When the -v/--verbose
3249 This lists both regular and local tags. When the -v/--verbose
3248 switch is used, a third column "local" is printed for local tags.
3250 switch is used, a third column "local" is printed for local tags.
3249 """
3251 """
3250
3252
3251 hexfunc = ui.debugflag and hex or short
3253 hexfunc = ui.debugflag and hex or short
3252 tagtype = ""
3254 tagtype = ""
3253
3255
3254 for t, n in reversed(repo.tagslist()):
3256 for t, n in reversed(repo.tagslist()):
3255 if ui.quiet:
3257 if ui.quiet:
3256 ui.write("%s\n" % t)
3258 ui.write("%s\n" % t)
3257 continue
3259 continue
3258
3260
3259 try:
3261 try:
3260 hn = hexfunc(n)
3262 hn = hexfunc(n)
3261 r = "%5d:%s" % (repo.changelog.rev(n), hn)
3263 r = "%5d:%s" % (repo.changelog.rev(n), hn)
3262 except error.LookupError:
3264 except error.LookupError:
3263 r = " ?:%s" % hn
3265 r = " ?:%s" % hn
3264 else:
3266 else:
3265 spaces = " " * (30 - encoding.colwidth(t))
3267 spaces = " " * (30 - encoding.colwidth(t))
3266 if ui.verbose:
3268 if ui.verbose:
3267 if repo.tagtype(t) == 'local':
3269 if repo.tagtype(t) == 'local':
3268 tagtype = " local"
3270 tagtype = " local"
3269 else:
3271 else:
3270 tagtype = ""
3272 tagtype = ""
3271 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
3273 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
3272
3274
3273 def tip(ui, repo, **opts):
3275 def tip(ui, repo, **opts):
3274 """show the tip revision
3276 """show the tip revision
3275
3277
3276 The tip revision (usually just called the tip) is the changeset
3278 The tip revision (usually just called the tip) is the changeset
3277 most recently added to the repository (and therefore the most
3279 most recently added to the repository (and therefore the most
3278 recently changed head).
3280 recently changed head).
3279
3281
3280 If you have just made a commit, that commit will be the tip. If
3282 If you have just made a commit, that commit will be the tip. If
3281 you have just pulled changes from another repository, the tip of
3283 you have just pulled changes from another repository, the tip of
3282 that repository becomes the current tip. The "tip" tag is special
3284 that repository becomes the current tip. The "tip" tag is special
3283 and cannot be renamed or assigned to a different changeset.
3285 and cannot be renamed or assigned to a different changeset.
3284 """
3286 """
3285 displayer = cmdutil.show_changeset(ui, repo, opts)
3287 displayer = cmdutil.show_changeset(ui, repo, opts)
3286 displayer.show(repo[len(repo) - 1])
3288 displayer.show(repo[len(repo) - 1])
3287 displayer.close()
3289 displayer.close()
3288
3290
3289 def unbundle(ui, repo, fname1, *fnames, **opts):
3291 def unbundle(ui, repo, fname1, *fnames, **opts):
3290 """apply one or more changegroup files
3292 """apply one or more changegroup files
3291
3293
3292 Apply one or more compressed changegroup files generated by the
3294 Apply one or more compressed changegroup files generated by the
3293 bundle command.
3295 bundle command.
3294 """
3296 """
3295 fnames = (fname1,) + fnames
3297 fnames = (fname1,) + fnames
3296
3298
3297 lock = repo.lock()
3299 lock = repo.lock()
3298 try:
3300 try:
3299 for fname in fnames:
3301 for fname in fnames:
3300 f = url.open(ui, fname)
3302 f = url.open(ui, fname)
3301 gen = changegroup.readbundle(f, fname)
3303 gen = changegroup.readbundle(f, fname)
3302 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
3304 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
3303 finally:
3305 finally:
3304 lock.release()
3306 lock.release()
3305
3307
3306 return postincoming(ui, repo, modheads, opts.get('update'), None)
3308 return postincoming(ui, repo, modheads, opts.get('update'), None)
3307
3309
3308 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
3310 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
3309 """update working directory
3311 """update working directory
3310
3312
3311 Update the repository's working directory to the specified
3313 Update the repository's working directory to the specified
3312 changeset.
3314 changeset.
3313
3315
3314 If no changeset is specified, attempt to update to the head of the
3316 If no changeset is specified, attempt to update to the head of the
3315 current branch. If this head is a descendant of the working
3317 current branch. If this head is a descendant of the working
3316 directory's parent, update to it, otherwise abort.
3318 directory's parent, update to it, otherwise abort.
3317
3319
3318 The following rules apply when the working directory contains
3320 The following rules apply when the working directory contains
3319 uncommitted changes:
3321 uncommitted changes:
3320
3322
3321 1. If neither -c/--check nor -C/--clean is specified, and if
3323 1. If neither -c/--check nor -C/--clean is specified, and if
3322 the requested changeset is an ancestor or descendant of
3324 the requested changeset is an ancestor or descendant of
3323 the working directory's parent, the uncommitted changes
3325 the working directory's parent, the uncommitted changes
3324 are merged into the requested changeset and the merged
3326 are merged into the requested changeset and the merged
3325 result is left uncommitted. If the requested changeset is
3327 result is left uncommitted. If the requested changeset is
3326 not an ancestor or descendant (that is, it is on another
3328 not an ancestor or descendant (that is, it is on another
3327 branch), the update is aborted and the uncommitted changes
3329 branch), the update is aborted and the uncommitted changes
3328 are preserved.
3330 are preserved.
3329
3331
3330 2. With the -c/--check option, the update is aborted and the
3332 2. With the -c/--check option, the update is aborted and the
3331 uncommitted changes are preserved.
3333 uncommitted changes are preserved.
3332
3334
3333 3. With the -C/--clean option, uncommitted changes are discarded and
3335 3. With the -C/--clean option, uncommitted changes are discarded and
3334 the working directory is updated to the requested changeset.
3336 the working directory is updated to the requested changeset.
3335
3337
3336 Use null as the changeset to remove the working directory (like 'hg
3338 Use null as the changeset to remove the working directory (like 'hg
3337 clone -U').
3339 clone -U').
3338
3340
3339 If you want to update just one file to an older changeset, use 'hg revert'.
3341 If you want to update just one file to an older changeset, use 'hg revert'.
3340
3342
3341 See 'hg help dates' for a list of formats valid for -d/--date.
3343 See 'hg help dates' for a list of formats valid for -d/--date.
3342 """
3344 """
3343 if rev and node:
3345 if rev and node:
3344 raise util.Abort(_("please specify just one revision"))
3346 raise util.Abort(_("please specify just one revision"))
3345
3347
3346 if not rev:
3348 if not rev:
3347 rev = node
3349 rev = node
3348
3350
3349 if check and clean:
3351 if check and clean:
3350 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
3352 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
3351
3353
3352 if check:
3354 if check:
3353 # we could use dirty() but we can ignore merge and branch trivia
3355 # we could use dirty() but we can ignore merge and branch trivia
3354 c = repo[None]
3356 c = repo[None]
3355 if c.modified() or c.added() or c.removed():
3357 if c.modified() or c.added() or c.removed():
3356 raise util.Abort(_("uncommitted local changes"))
3358 raise util.Abort(_("uncommitted local changes"))
3357
3359
3358 if date:
3360 if date:
3359 if rev:
3361 if rev:
3360 raise util.Abort(_("you can't specify a revision and a date"))
3362 raise util.Abort(_("you can't specify a revision and a date"))
3361 rev = cmdutil.finddate(ui, repo, date)
3363 rev = cmdutil.finddate(ui, repo, date)
3362
3364
3363 if clean or check:
3365 if clean or check:
3364 return hg.clean(repo, rev)
3366 return hg.clean(repo, rev)
3365 else:
3367 else:
3366 return hg.update(repo, rev)
3368 return hg.update(repo, rev)
3367
3369
3368 def verify(ui, repo):
3370 def verify(ui, repo):
3369 """verify the integrity of the repository
3371 """verify the integrity of the repository
3370
3372
3371 Verify the integrity of the current repository.
3373 Verify the integrity of the current repository.
3372
3374
3373 This will perform an extensive check of the repository's
3375 This will perform an extensive check of the repository's
3374 integrity, validating the hashes and checksums of each entry in
3376 integrity, validating the hashes and checksums of each entry in
3375 the changelog, manifest, and tracked files, as well as the
3377 the changelog, manifest, and tracked files, as well as the
3376 integrity of their crosslinks and indices.
3378 integrity of their crosslinks and indices.
3377 """
3379 """
3378 return hg.verify(repo)
3380 return hg.verify(repo)
3379
3381
3380 def version_(ui):
3382 def version_(ui):
3381 """output version and copyright information"""
3383 """output version and copyright information"""
3382 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3384 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3383 % util.version())
3385 % util.version())
3384 ui.status(_(
3386 ui.status(_(
3385 "\nCopyright (C) 2005-2010 Matt Mackall <mpm@selenic.com> and others\n"
3387 "\nCopyright (C) 2005-2010 Matt Mackall <mpm@selenic.com> and others\n"
3386 "This is free software; see the source for copying conditions. "
3388 "This is free software; see the source for copying conditions. "
3387 "There is NO\nwarranty; "
3389 "There is NO\nwarranty; "
3388 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3390 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3389 ))
3391 ))
3390
3392
3391 # Command options and aliases are listed here, alphabetically
3393 # Command options and aliases are listed here, alphabetically
3392
3394
3393 globalopts = [
3395 globalopts = [
3394 ('R', 'repository', '',
3396 ('R', 'repository', '',
3395 _('repository root directory or name of overlay bundle file')),
3397 _('repository root directory or name of overlay bundle file')),
3396 ('', 'cwd', '', _('change working directory')),
3398 ('', 'cwd', '', _('change working directory')),
3397 ('y', 'noninteractive', None,
3399 ('y', 'noninteractive', None,
3398 _('do not prompt, assume \'yes\' for any required answers')),
3400 _('do not prompt, assume \'yes\' for any required answers')),
3399 ('q', 'quiet', None, _('suppress output')),
3401 ('q', 'quiet', None, _('suppress output')),
3400 ('v', 'verbose', None, _('enable additional output')),
3402 ('v', 'verbose', None, _('enable additional output')),
3401 ('', 'config', [],
3403 ('', 'config', [],
3402 _('set/override config option (use \'section.name=value\')')),
3404 _('set/override config option (use \'section.name=value\')')),
3403 ('', 'debug', None, _('enable debugging output')),
3405 ('', 'debug', None, _('enable debugging output')),
3404 ('', 'debugger', None, _('start debugger')),
3406 ('', 'debugger', None, _('start debugger')),
3405 ('', 'encoding', encoding.encoding, _('set the charset encoding')),
3407 ('', 'encoding', encoding.encoding, _('set the charset encoding')),
3406 ('', 'encodingmode', encoding.encodingmode,
3408 ('', 'encodingmode', encoding.encodingmode,
3407 _('set the charset encoding mode')),
3409 _('set the charset encoding mode')),
3408 ('', 'traceback', None, _('always print a traceback on exception')),
3410 ('', 'traceback', None, _('always print a traceback on exception')),
3409 ('', 'time', None, _('time how long the command takes')),
3411 ('', 'time', None, _('time how long the command takes')),
3410 ('', 'profile', None, _('print command execution profile')),
3412 ('', 'profile', None, _('print command execution profile')),
3411 ('', 'version', None, _('output version information and exit')),
3413 ('', 'version', None, _('output version information and exit')),
3412 ('h', 'help', None, _('display help and exit')),
3414 ('h', 'help', None, _('display help and exit')),
3413 ]
3415 ]
3414
3416
3415 dryrunopts = [('n', 'dry-run', None,
3417 dryrunopts = [('n', 'dry-run', None,
3416 _('do not perform actions, just print output'))]
3418 _('do not perform actions, just print output'))]
3417
3419
3418 remoteopts = [
3420 remoteopts = [
3419 ('e', 'ssh', '', _('specify ssh command to use')),
3421 ('e', 'ssh', '', _('specify ssh command to use')),
3420 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
3422 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
3421 ]
3423 ]
3422
3424
3423 walkopts = [
3425 walkopts = [
3424 ('I', 'include', [], _('include names matching the given patterns')),
3426 ('I', 'include', [], _('include names matching the given patterns')),
3425 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3427 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3426 ]
3428 ]
3427
3429
3428 commitopts = [
3430 commitopts = [
3429 ('m', 'message', '', _('use <text> as commit message')),
3431 ('m', 'message', '', _('use <text> as commit message')),
3430 ('l', 'logfile', '', _('read commit message from <file>')),
3432 ('l', 'logfile', '', _('read commit message from <file>')),
3431 ]
3433 ]
3432
3434
3433 commitopts2 = [
3435 commitopts2 = [
3434 ('d', 'date', '', _('record datecode as commit date')),
3436 ('d', 'date', '', _('record datecode as commit date')),
3435 ('u', 'user', '', _('record the specified user as committer')),
3437 ('u', 'user', '', _('record the specified user as committer')),
3436 ]
3438 ]
3437
3439
3438 templateopts = [
3440 templateopts = [
3439 ('', 'style', '', _('display using template map file')),
3441 ('', 'style', '', _('display using template map file')),
3440 ('', 'template', '', _('display with template')),
3442 ('', 'template', '', _('display with template')),
3441 ]
3443 ]
3442
3444
3443 logopts = [
3445 logopts = [
3444 ('p', 'patch', None, _('show patch')),
3446 ('p', 'patch', None, _('show patch')),
3445 ('g', 'git', None, _('use git extended diff format')),
3447 ('g', 'git', None, _('use git extended diff format')),
3446 ('l', 'limit', '', _('limit number of changes displayed')),
3448 ('l', 'limit', '', _('limit number of changes displayed')),
3447 ('M', 'no-merges', None, _('do not show merges')),
3449 ('M', 'no-merges', None, _('do not show merges')),
3448 ] + templateopts
3450 ] + templateopts
3449
3451
3450 diffopts = [
3452 diffopts = [
3451 ('a', 'text', None, _('treat all files as text')),
3453 ('a', 'text', None, _('treat all files as text')),
3452 ('g', 'git', None, _('use git extended diff format')),
3454 ('g', 'git', None, _('use git extended diff format')),
3453 ('', 'nodates', None, _('omit dates from diff headers'))
3455 ('', 'nodates', None, _('omit dates from diff headers'))
3454 ]
3456 ]
3455
3457
3456 diffopts2 = [
3458 diffopts2 = [
3457 ('p', 'show-function', None, _('show which function each change is in')),
3459 ('p', 'show-function', None, _('show which function each change is in')),
3458 ('', 'reverse', None, _('produce a diff that undoes the changes')),
3460 ('', 'reverse', None, _('produce a diff that undoes the changes')),
3459 ('w', 'ignore-all-space', None,
3461 ('w', 'ignore-all-space', None,
3460 _('ignore white space when comparing lines')),
3462 _('ignore white space when comparing lines')),
3461 ('b', 'ignore-space-change', None,
3463 ('b', 'ignore-space-change', None,
3462 _('ignore changes in the amount of white space')),
3464 _('ignore changes in the amount of white space')),
3463 ('B', 'ignore-blank-lines', None,
3465 ('B', 'ignore-blank-lines', None,
3464 _('ignore changes whose lines are all blank')),
3466 _('ignore changes whose lines are all blank')),
3465 ('U', 'unified', '', _('number of lines of context to show')),
3467 ('U', 'unified', '', _('number of lines of context to show')),
3466 ('', 'stat', None, _('output diffstat-style summary of changes')),
3468 ('', 'stat', None, _('output diffstat-style summary of changes')),
3467 ]
3469 ]
3468
3470
3469 similarityopts = [
3471 similarityopts = [
3470 ('s', 'similarity', '',
3472 ('s', 'similarity', '',
3471 _('guess renamed files by similarity (0<=s<=100)'))
3473 _('guess renamed files by similarity (0<=s<=100)'))
3472 ]
3474 ]
3473
3475
3474 table = {
3476 table = {
3475 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3477 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3476 "addremove":
3478 "addremove":
3477 (addremove, similarityopts + walkopts + dryrunopts,
3479 (addremove, similarityopts + walkopts + dryrunopts,
3478 _('[OPTION]... [FILE]...')),
3480 _('[OPTION]... [FILE]...')),
3479 "^annotate|blame":
3481 "^annotate|blame":
3480 (annotate,
3482 (annotate,
3481 [('r', 'rev', '', _('annotate the specified revision')),
3483 [('r', 'rev', '', _('annotate the specified revision')),
3482 ('', 'follow', None,
3484 ('', 'follow', None,
3483 _('follow copies/renames and list the filename (DEPRECATED)')),
3485 _('follow copies/renames and list the filename (DEPRECATED)')),
3484 ('', 'no-follow', None, _("don't follow copies and renames")),
3486 ('', 'no-follow', None, _("don't follow copies and renames")),
3485 ('a', 'text', None, _('treat all files as text')),
3487 ('a', 'text', None, _('treat all files as text')),
3486 ('u', 'user', None, _('list the author (long with -v)')),
3488 ('u', 'user', None, _('list the author (long with -v)')),
3487 ('f', 'file', None, _('list the filename')),
3489 ('f', 'file', None, _('list the filename')),
3488 ('d', 'date', None, _('list the date (short with -q)')),
3490 ('d', 'date', None, _('list the date (short with -q)')),
3489 ('n', 'number', None, _('list the revision number (default)')),
3491 ('n', 'number', None, _('list the revision number (default)')),
3490 ('c', 'changeset', None, _('list the changeset')),
3492 ('c', 'changeset', None, _('list the changeset')),
3491 ('l', 'line-number', None,
3493 ('l', 'line-number', None,
3492 _('show line number at the first appearance'))
3494 _('show line number at the first appearance'))
3493 ] + walkopts,
3495 ] + walkopts,
3494 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3496 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3495 "archive":
3497 "archive":
3496 (archive,
3498 (archive,
3497 [('', 'no-decode', None, _('do not pass files through decoders')),
3499 [('', 'no-decode', None, _('do not pass files through decoders')),
3498 ('p', 'prefix', '', _('directory prefix for files in archive')),
3500 ('p', 'prefix', '', _('directory prefix for files in archive')),
3499 ('r', 'rev', '', _('revision to distribute')),
3501 ('r', 'rev', '', _('revision to distribute')),
3500 ('t', 'type', '', _('type of distribution to create')),
3502 ('t', 'type', '', _('type of distribution to create')),
3501 ] + walkopts,
3503 ] + walkopts,
3502 _('[OPTION]... DEST')),
3504 _('[OPTION]... DEST')),
3503 "backout":
3505 "backout":
3504 (backout,
3506 (backout,
3505 [('', 'merge', None,
3507 [('', 'merge', None,
3506 _('merge with old dirstate parent after backout')),
3508 _('merge with old dirstate parent after backout')),
3507 ('', 'parent', '', _('parent to choose when backing out merge')),
3509 ('', 'parent', '', _('parent to choose when backing out merge')),
3508 ('r', 'rev', '', _('revision to backout')),
3510 ('r', 'rev', '', _('revision to backout')),
3509 ] + walkopts + commitopts + commitopts2,
3511 ] + walkopts + commitopts + commitopts2,
3510 _('[OPTION]... [-r] REV')),
3512 _('[OPTION]... [-r] REV')),
3511 "bisect":
3513 "bisect":
3512 (bisect,
3514 (bisect,
3513 [('r', 'reset', False, _('reset bisect state')),
3515 [('r', 'reset', False, _('reset bisect state')),
3514 ('g', 'good', False, _('mark changeset good')),
3516 ('g', 'good', False, _('mark changeset good')),
3515 ('b', 'bad', False, _('mark changeset bad')),
3517 ('b', 'bad', False, _('mark changeset bad')),
3516 ('s', 'skip', False, _('skip testing changeset')),
3518 ('s', 'skip', False, _('skip testing changeset')),
3517 ('c', 'command', '', _('use command to check changeset state')),
3519 ('c', 'command', '', _('use command to check changeset state')),
3518 ('U', 'noupdate', False, _('do not update to target'))],
3520 ('U', 'noupdate', False, _('do not update to target'))],
3519 _("[-gbsr] [-U] [-c CMD] [REV]")),
3521 _("[-gbsr] [-U] [-c CMD] [REV]")),
3520 "branch":
3522 "branch":
3521 (branch,
3523 (branch,
3522 [('f', 'force', None,
3524 [('f', 'force', None,
3523 _('set branch name even if it shadows an existing branch')),
3525 _('set branch name even if it shadows an existing branch')),
3524 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3526 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3525 _('[-fC] [NAME]')),
3527 _('[-fC] [NAME]')),
3526 "branches":
3528 "branches":
3527 (branches,
3529 (branches,
3528 [('a', 'active', False,
3530 [('a', 'active', False,
3529 _('show only branches that have unmerged heads')),
3531 _('show only branches that have unmerged heads')),
3530 ('c', 'closed', False,
3532 ('c', 'closed', False,
3531 _('show normal and closed branches'))],
3533 _('show normal and closed branches'))],
3532 _('[-ac]')),
3534 _('[-ac]')),
3533 "bundle":
3535 "bundle":
3534 (bundle,
3536 (bundle,
3535 [('f', 'force', None,
3537 [('f', 'force', None,
3536 _('run even when the destination is unrelated')),
3538 _('run even when the destination is unrelated')),
3537 ('r', 'rev', [],
3539 ('r', 'rev', [],
3538 _('a changeset intended to be added to the destination')),
3540 _('a changeset intended to be added to the destination')),
3539 ('b', 'branch', [],
3541 ('b', 'branch', [],
3540 _('a specific branch you would like to bundle')),
3542 _('a specific branch you would like to bundle')),
3541 ('', 'base', [],
3543 ('', 'base', [],
3542 _('a base changeset assumed to be available at the destination')),
3544 _('a base changeset assumed to be available at the destination')),
3543 ('a', 'all', None, _('bundle all changesets in the repository')),
3545 ('a', 'all', None, _('bundle all changesets in the repository')),
3544 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3546 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3545 ] + remoteopts,
3547 ] + remoteopts,
3546 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3548 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3547 "cat":
3549 "cat":
3548 (cat,
3550 (cat,
3549 [('o', 'output', '', _('print output to file with formatted name')),
3551 [('o', 'output', '', _('print output to file with formatted name')),
3550 ('r', 'rev', '', _('print the given revision')),
3552 ('r', 'rev', '', _('print the given revision')),
3551 ('', 'decode', None, _('apply any matching decode filter')),
3553 ('', 'decode', None, _('apply any matching decode filter')),
3552 ] + walkopts,
3554 ] + walkopts,
3553 _('[OPTION]... FILE...')),
3555 _('[OPTION]... FILE...')),
3554 "^clone":
3556 "^clone":
3555 (clone,
3557 (clone,
3556 [('U', 'noupdate', None,
3558 [('U', 'noupdate', None,
3557 _('the clone will include an empty working copy (only a repository)')),
3559 _('the clone will include an empty working copy (only a repository)')),
3558 ('u', 'updaterev', '',
3560 ('u', 'updaterev', '',
3559 _('revision, tag or branch to check out')),
3561 _('revision, tag or branch to check out')),
3560 ('r', 'rev', [],
3562 ('r', 'rev', [],
3561 _('include the specified changeset')),
3563 _('include the specified changeset')),
3562 ('b', 'branch', [],
3564 ('b', 'branch', [],
3563 _('clone only the specified branch')),
3565 _('clone only the specified branch')),
3564 ('', 'pull', None, _('use pull protocol to copy metadata')),
3566 ('', 'pull', None, _('use pull protocol to copy metadata')),
3565 ('', 'uncompressed', None,
3567 ('', 'uncompressed', None,
3566 _('use uncompressed transfer (fast over LAN)')),
3568 _('use uncompressed transfer (fast over LAN)')),
3567 ] + remoteopts,
3569 ] + remoteopts,
3568 _('[OPTION]... SOURCE [DEST]')),
3570 _('[OPTION]... SOURCE [DEST]')),
3569 "^commit|ci":
3571 "^commit|ci":
3570 (commit,
3572 (commit,
3571 [('A', 'addremove', None,
3573 [('A', 'addremove', None,
3572 _('mark new/missing files as added/removed before committing')),
3574 _('mark new/missing files as added/removed before committing')),
3573 ('', 'close-branch', None,
3575 ('', 'close-branch', None,
3574 _('mark a branch as closed, hiding it from the branch list')),
3576 _('mark a branch as closed, hiding it from the branch list')),
3575 ] + walkopts + commitopts + commitopts2,
3577 ] + walkopts + commitopts + commitopts2,
3576 _('[OPTION]... [FILE]...')),
3578 _('[OPTION]... [FILE]...')),
3577 "copy|cp":
3579 "copy|cp":
3578 (copy,
3580 (copy,
3579 [('A', 'after', None, _('record a copy that has already occurred')),
3581 [('A', 'after', None, _('record a copy that has already occurred')),
3580 ('f', 'force', None,
3582 ('f', 'force', None,
3581 _('forcibly copy over an existing managed file')),
3583 _('forcibly copy over an existing managed file')),
3582 ] + walkopts + dryrunopts,
3584 ] + walkopts + dryrunopts,
3583 _('[OPTION]... [SOURCE]... DEST')),
3585 _('[OPTION]... [SOURCE]... DEST')),
3584 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3586 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3585 "debugcheckstate": (debugcheckstate, [], ''),
3587 "debugcheckstate": (debugcheckstate, [], ''),
3586 "debugcommands": (debugcommands, [], _('[COMMAND]')),
3588 "debugcommands": (debugcommands, [], _('[COMMAND]')),
3587 "debugcomplete":
3589 "debugcomplete":
3588 (debugcomplete,
3590 (debugcomplete,
3589 [('o', 'options', None, _('show the command options'))],
3591 [('o', 'options', None, _('show the command options'))],
3590 _('[-o] CMD')),
3592 _('[-o] CMD')),
3591 "debugdate":
3593 "debugdate":
3592 (debugdate,
3594 (debugdate,
3593 [('e', 'extended', None, _('try extended date formats'))],
3595 [('e', 'extended', None, _('try extended date formats'))],
3594 _('[-e] DATE [RANGE]')),
3596 _('[-e] DATE [RANGE]')),
3595 "debugdata": (debugdata, [], _('FILE REV')),
3597 "debugdata": (debugdata, [], _('FILE REV')),
3596 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3598 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3597 "debugindex": (debugindex, [], _('FILE')),
3599 "debugindex": (debugindex, [], _('FILE')),
3598 "debugindexdot": (debugindexdot, [], _('FILE')),
3600 "debugindexdot": (debugindexdot, [], _('FILE')),
3599 "debuginstall": (debuginstall, [], ''),
3601 "debuginstall": (debuginstall, [], ''),
3600 "debugrebuildstate":
3602 "debugrebuildstate":
3601 (debugrebuildstate,
3603 (debugrebuildstate,
3602 [('r', 'rev', '', _('revision to rebuild to'))],
3604 [('r', 'rev', '', _('revision to rebuild to'))],
3603 _('[-r REV] [REV]')),
3605 _('[-r REV] [REV]')),
3604 "debugrename":
3606 "debugrename":
3605 (debugrename,
3607 (debugrename,
3606 [('r', 'rev', '', _('revision to debug'))],
3608 [('r', 'rev', '', _('revision to debug'))],
3607 _('[-r REV] FILE')),
3609 _('[-r REV] FILE')),
3608 "debugsetparents":
3610 "debugsetparents":
3609 (debugsetparents, [], _('REV1 [REV2]')),
3611 (debugsetparents, [], _('REV1 [REV2]')),
3610 "debugstate":
3612 "debugstate":
3611 (debugstate,
3613 (debugstate,
3612 [('', 'nodates', None, _('do not display the saved mtime'))],
3614 [('', 'nodates', None, _('do not display the saved mtime'))],
3613 _('[OPTION]...')),
3615 _('[OPTION]...')),
3614 "debugsub":
3616 "debugsub":
3615 (debugsub,
3617 (debugsub,
3616 [('r', 'rev', '', _('revision to check'))],
3618 [('r', 'rev', '', _('revision to check'))],
3617 _('[-r REV] [REV]')),
3619 _('[-r REV] [REV]')),
3618 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3620 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3619 "^diff":
3621 "^diff":
3620 (diff,
3622 (diff,
3621 [('r', 'rev', [], _('revision')),
3623 [('r', 'rev', [], _('revision')),
3622 ('c', 'change', '', _('change made by revision'))
3624 ('c', 'change', '', _('change made by revision'))
3623 ] + diffopts + diffopts2 + walkopts,
3625 ] + diffopts + diffopts2 + walkopts,
3624 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...')),
3626 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...')),
3625 "^export":
3627 "^export":
3626 (export,
3628 (export,
3627 [('o', 'output', '', _('print output to file with formatted name')),
3629 [('o', 'output', '', _('print output to file with formatted name')),
3628 ('', 'switch-parent', None, _('diff against the second parent')),
3630 ('', 'switch-parent', None, _('diff against the second parent')),
3629 ('r', 'rev', [], _('revisions to export')),
3631 ('r', 'rev', [], _('revisions to export')),
3630 ] + diffopts,
3632 ] + diffopts,
3631 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3633 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3632 "^forget":
3634 "^forget":
3633 (forget,
3635 (forget,
3634 [] + walkopts,
3636 [] + walkopts,
3635 _('[OPTION]... FILE...')),
3637 _('[OPTION]... FILE...')),
3636 "grep":
3638 "grep":
3637 (grep,
3639 (grep,
3638 [('0', 'print0', None, _('end fields with NUL')),
3640 [('0', 'print0', None, _('end fields with NUL')),
3639 ('', 'all', None, _('print all revisions that match')),
3641 ('', 'all', None, _('print all revisions that match')),
3640 ('f', 'follow', None,
3642 ('f', 'follow', None,
3641 _('follow changeset history,'
3643 _('follow changeset history,'
3642 ' or file history across copies and renames')),
3644 ' or file history across copies and renames')),
3643 ('i', 'ignore-case', None, _('ignore case when matching')),
3645 ('i', 'ignore-case', None, _('ignore case when matching')),
3644 ('l', 'files-with-matches', None,
3646 ('l', 'files-with-matches', None,
3645 _('print only filenames and revisions that match')),
3647 _('print only filenames and revisions that match')),
3646 ('n', 'line-number', None, _('print matching line numbers')),
3648 ('n', 'line-number', None, _('print matching line numbers')),
3647 ('r', 'rev', [], _('search in given revision range')),
3649 ('r', 'rev', [], _('search in given revision range')),
3648 ('u', 'user', None, _('list the author (long with -v)')),
3650 ('u', 'user', None, _('list the author (long with -v)')),
3649 ('d', 'date', None, _('list the date (short with -q)')),
3651 ('d', 'date', None, _('list the date (short with -q)')),
3650 ] + walkopts,
3652 ] + walkopts,
3651 _('[OPTION]... PATTERN [FILE]...')),
3653 _('[OPTION]... PATTERN [FILE]...')),
3652 "heads":
3654 "heads":
3653 (heads,
3655 (heads,
3654 [('r', 'rev', '', _('show only heads which are descendants of REV')),
3656 [('r', 'rev', '', _('show only heads which are descendants of REV')),
3655 ('t', 'topo', False, _('show topological heads only')),
3657 ('t', 'topo', False, _('show topological heads only')),
3656 ('a', 'active', False,
3658 ('a', 'active', False,
3657 _('show active branchheads only [DEPRECATED]')),
3659 _('show active branchheads only [DEPRECATED]')),
3658 ('c', 'closed', False,
3660 ('c', 'closed', False,
3659 _('show normal and closed branch heads')),
3661 _('show normal and closed branch heads')),
3660 ] + templateopts,
3662 ] + templateopts,
3661 _('[-ac] [-r STARTREV] [REV]...')),
3663 _('[-ac] [-r STARTREV] [REV]...')),
3662 "help": (help_, [], _('[TOPIC]')),
3664 "help": (help_, [], _('[TOPIC]')),
3663 "identify|id":
3665 "identify|id":
3664 (identify,
3666 (identify,
3665 [('r', 'rev', '', _('identify the specified revision')),
3667 [('r', 'rev', '', _('identify the specified revision')),
3666 ('n', 'num', None, _('show local revision number')),
3668 ('n', 'num', None, _('show local revision number')),
3667 ('i', 'id', None, _('show global revision id')),
3669 ('i', 'id', None, _('show global revision id')),
3668 ('b', 'branch', None, _('show branch')),
3670 ('b', 'branch', None, _('show branch')),
3669 ('t', 'tags', None, _('show tags'))],
3671 ('t', 'tags', None, _('show tags'))],
3670 _('[-nibt] [-r REV] [SOURCE]')),
3672 _('[-nibt] [-r REV] [SOURCE]')),
3671 "import|patch":
3673 "import|patch":
3672 (import_,
3674 (import_,
3673 [('p', 'strip', 1,
3675 [('p', 'strip', 1,
3674 _('directory strip option for patch. This has the same '
3676 _('directory strip option for patch. This has the same '
3675 'meaning as the corresponding patch option')),
3677 'meaning as the corresponding patch option')),
3676 ('b', 'base', '', _('base path')),
3678 ('b', 'base', '', _('base path')),
3677 ('f', 'force', None,
3679 ('f', 'force', None,
3678 _('skip check for outstanding uncommitted changes')),
3680 _('skip check for outstanding uncommitted changes')),
3679 ('', 'no-commit', None,
3681 ('', 'no-commit', None,
3680 _("don't commit, just update the working directory")),
3682 _("don't commit, just update the working directory")),
3681 ('', 'exact', None,
3683 ('', 'exact', None,
3682 _('apply patch to the nodes from which it was generated')),
3684 _('apply patch to the nodes from which it was generated')),
3683 ('', 'import-branch', None,
3685 ('', 'import-branch', None,
3684 _('use any branch information in patch (implied by --exact)'))] +
3686 _('use any branch information in patch (implied by --exact)'))] +
3685 commitopts + commitopts2 + similarityopts,
3687 commitopts + commitopts2 + similarityopts,
3686 _('[OPTION]... PATCH...')),
3688 _('[OPTION]... PATCH...')),
3687 "incoming|in":
3689 "incoming|in":
3688 (incoming,
3690 (incoming,
3689 [('f', 'force', None,
3691 [('f', 'force', None,
3690 _('run even if remote repository is unrelated')),
3692 _('run even if remote repository is unrelated')),
3691 ('n', 'newest-first', None, _('show newest record first')),
3693 ('n', 'newest-first', None, _('show newest record first')),
3692 ('', 'bundle', '', _('file to store the bundles into')),
3694 ('', 'bundle', '', _('file to store the bundles into')),
3693 ('r', 'rev', [],
3695 ('r', 'rev', [],
3694 _('a remote changeset intended to be added')),
3696 _('a remote changeset intended to be added')),
3695 ('b', 'branch', [],
3697 ('b', 'branch', [],
3696 _('a specific branch you would like to pull')),
3698 _('a specific branch you would like to pull')),
3697 ] + logopts + remoteopts,
3699 ] + logopts + remoteopts,
3698 _('[-p] [-n] [-M] [-f] [-r REV]...'
3700 _('[-p] [-n] [-M] [-f] [-r REV]...'
3699 ' [--bundle FILENAME] [SOURCE]')),
3701 ' [--bundle FILENAME] [SOURCE]')),
3700 "^init":
3702 "^init":
3701 (init,
3703 (init,
3702 remoteopts,
3704 remoteopts,
3703 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3705 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3704 "locate":
3706 "locate":
3705 (locate,
3707 (locate,
3706 [('r', 'rev', '', _('search the repository as it is in REV')),
3708 [('r', 'rev', '', _('search the repository as it is in REV')),
3707 ('0', 'print0', None,
3709 ('0', 'print0', None,
3708 _('end filenames with NUL, for use with xargs')),
3710 _('end filenames with NUL, for use with xargs')),
3709 ('f', 'fullpath', None,
3711 ('f', 'fullpath', None,
3710 _('print complete paths from the filesystem root')),
3712 _('print complete paths from the filesystem root')),
3711 ] + walkopts,
3713 ] + walkopts,
3712 _('[OPTION]... [PATTERN]...')),
3714 _('[OPTION]... [PATTERN]...')),
3713 "^log|history":
3715 "^log|history":
3714 (log,
3716 (log,
3715 [('f', 'follow', None,
3717 [('f', 'follow', None,
3716 _('follow changeset history,'
3718 _('follow changeset history,'
3717 ' or file history across copies and renames')),
3719 ' or file history across copies and renames')),
3718 ('', 'follow-first', None,
3720 ('', 'follow-first', None,
3719 _('only follow the first parent of merge changesets')),
3721 _('only follow the first parent of merge changesets')),
3720 ('d', 'date', '', _('show revisions matching date spec')),
3722 ('d', 'date', '', _('show revisions matching date spec')),
3721 ('C', 'copies', None, _('show copied files')),
3723 ('C', 'copies', None, _('show copied files')),
3722 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3724 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3723 ('r', 'rev', [], _('show the specified revision or range')),
3725 ('r', 'rev', [], _('show the specified revision or range')),
3724 ('', 'removed', None, _('include revisions where files were removed')),
3726 ('', 'removed', None, _('include revisions where files were removed')),
3725 ('m', 'only-merges', None, _('show only merges')),
3727 ('m', 'only-merges', None, _('show only merges')),
3726 ('u', 'user', [], _('revisions committed by user')),
3728 ('u', 'user', [], _('revisions committed by user')),
3727 ('b', 'only-branch', [],
3729 ('b', 'only-branch', [],
3728 _('show only changesets within the given named branch')),
3730 _('show only changesets within the given named branch')),
3729 ('P', 'prune', [],
3731 ('P', 'prune', [],
3730 _('do not display revision or any of its ancestors')),
3732 _('do not display revision or any of its ancestors')),
3731 ] + logopts + walkopts,
3733 ] + logopts + walkopts,
3732 _('[OPTION]... [FILE]')),
3734 _('[OPTION]... [FILE]')),
3733 "manifest":
3735 "manifest":
3734 (manifest,
3736 (manifest,
3735 [('r', 'rev', '', _('revision to display'))],
3737 [('r', 'rev', '', _('revision to display'))],
3736 _('[-r REV]')),
3738 _('[-r REV]')),
3737 "^merge":
3739 "^merge":
3738 (merge,
3740 (merge,
3739 [('f', 'force', None, _('force a merge with outstanding changes')),
3741 [('f', 'force', None, _('force a merge with outstanding changes')),
3740 ('r', 'rev', '', _('revision to merge')),
3742 ('r', 'rev', '', _('revision to merge')),
3741 ('P', 'preview', None,
3743 ('P', 'preview', None,
3742 _('review revisions to merge (no merge is performed)'))],
3744 _('review revisions to merge (no merge is performed)'))],
3743 _('[-P] [-f] [[-r] REV]')),
3745 _('[-P] [-f] [[-r] REV]')),
3744 "outgoing|out":
3746 "outgoing|out":
3745 (outgoing,
3747 (outgoing,
3746 [('f', 'force', None,
3748 [('f', 'force', None,
3747 _('run even when the destination is unrelated')),
3749 _('run even when the destination is unrelated')),
3748 ('r', 'rev', [],
3750 ('r', 'rev', [],
3749 _('a changeset intended to be included in the destination')),
3751 _('a changeset intended to be included in the destination')),
3750 ('n', 'newest-first', None, _('show newest record first')),
3752 ('n', 'newest-first', None, _('show newest record first')),
3751 ('b', 'branch', [],
3753 ('b', 'branch', [],
3752 _('a specific branch you would like to push')),
3754 _('a specific branch you would like to push')),
3753 ] + logopts + remoteopts,
3755 ] + logopts + remoteopts,
3754 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3756 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3755 "parents":
3757 "parents":
3756 (parents,
3758 (parents,
3757 [('r', 'rev', '', _('show parents of the specified revision')),
3759 [('r', 'rev', '', _('show parents of the specified revision')),
3758 ] + templateopts,
3760 ] + templateopts,
3759 _('[-r REV] [FILE]')),
3761 _('[-r REV] [FILE]')),
3760 "paths": (paths, [], _('[NAME]')),
3762 "paths": (paths, [], _('[NAME]')),
3761 "^pull":
3763 "^pull":
3762 (pull,
3764 (pull,
3763 [('u', 'update', None,
3765 [('u', 'update', None,
3764 _('update to new branch head if changesets were pulled')),
3766 _('update to new branch head if changesets were pulled')),
3765 ('f', 'force', None,
3767 ('f', 'force', None,
3766 _('run even when remote repository is unrelated')),
3768 _('run even when remote repository is unrelated')),
3767 ('r', 'rev', [],
3769 ('r', 'rev', [],
3768 _('a remote changeset intended to be added')),
3770 _('a remote changeset intended to be added')),
3769 ('b', 'branch', [],
3771 ('b', 'branch', [],
3770 _('a specific branch you would like to pull')),
3772 _('a specific branch you would like to pull')),
3771 ] + remoteopts,
3773 ] + remoteopts,
3772 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3774 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3773 "^push":
3775 "^push":
3774 (push,
3776 (push,
3775 [('f', 'force', None, _('force push')),
3777 [('f', 'force', None, _('force push')),
3776 ('r', 'rev', [],
3778 ('r', 'rev', [],
3777 _('a changeset intended to be included in the destination')),
3779 _('a changeset intended to be included in the destination')),
3778 ('b', 'branch', [],
3780 ('b', 'branch', [],
3779 _('a specific branch you would like to push')),
3781 _('a specific branch you would like to push')),
3780 ] + remoteopts,
3782 ] + remoteopts,
3781 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3783 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3782 "recover": (recover, []),
3784 "recover": (recover, []),
3783 "^remove|rm":
3785 "^remove|rm":
3784 (remove,
3786 (remove,
3785 [('A', 'after', None, _('record delete for missing files')),
3787 [('A', 'after', None, _('record delete for missing files')),
3786 ('f', 'force', None,
3788 ('f', 'force', None,
3787 _('remove (and delete) file even if added or modified')),
3789 _('remove (and delete) file even if added or modified')),
3788 ] + walkopts,
3790 ] + walkopts,
3789 _('[OPTION]... FILE...')),
3791 _('[OPTION]... FILE...')),
3790 "rename|mv":
3792 "rename|mv":
3791 (rename,
3793 (rename,
3792 [('A', 'after', None, _('record a rename that has already occurred')),
3794 [('A', 'after', None, _('record a rename that has already occurred')),
3793 ('f', 'force', None,
3795 ('f', 'force', None,
3794 _('forcibly copy over an existing managed file')),
3796 _('forcibly copy over an existing managed file')),
3795 ] + walkopts + dryrunopts,
3797 ] + walkopts + dryrunopts,
3796 _('[OPTION]... SOURCE... DEST')),
3798 _('[OPTION]... SOURCE... DEST')),
3797 "resolve":
3799 "resolve":
3798 (resolve,
3800 (resolve,
3799 [('a', 'all', None, _('select all unresolved files')),
3801 [('a', 'all', None, _('select all unresolved files')),
3800 ('l', 'list', None, _('list state of files needing merge')),
3802 ('l', 'list', None, _('list state of files needing merge')),
3801 ('m', 'mark', None, _('mark files as resolved')),
3803 ('m', 'mark', None, _('mark files as resolved')),
3802 ('u', 'unmark', None, _('unmark files as resolved')),
3804 ('u', 'unmark', None, _('unmark files as resolved')),
3803 ('n', 'no-status', None, _('hide status prefix'))]
3805 ('n', 'no-status', None, _('hide status prefix'))]
3804 + walkopts,
3806 + walkopts,
3805 _('[OPTION]... [FILE]...')),
3807 _('[OPTION]... [FILE]...')),
3806 "revert":
3808 "revert":
3807 (revert,
3809 (revert,
3808 [('a', 'all', None, _('revert all changes when no arguments given')),
3810 [('a', 'all', None, _('revert all changes when no arguments given')),
3809 ('d', 'date', '', _('tipmost revision matching date')),
3811 ('d', 'date', '', _('tipmost revision matching date')),
3810 ('r', 'rev', '', _('revert to the specified revision')),
3812 ('r', 'rev', '', _('revert to the specified revision')),
3811 ('', 'no-backup', None, _('do not save backup copies of files')),
3813 ('', 'no-backup', None, _('do not save backup copies of files')),
3812 ] + walkopts + dryrunopts,
3814 ] + walkopts + dryrunopts,
3813 _('[OPTION]... [-r REV] [NAME]...')),
3815 _('[OPTION]... [-r REV] [NAME]...')),
3814 "rollback": (rollback, []),
3816 "rollback": (rollback, []),
3815 "root": (root, []),
3817 "root": (root, []),
3816 "^serve":
3818 "^serve":
3817 (serve,
3819 (serve,
3818 [('A', 'accesslog', '', _('name of access log file to write to')),
3820 [('A', 'accesslog', '', _('name of access log file to write to')),
3819 ('d', 'daemon', None, _('run server in background')),
3821 ('d', 'daemon', None, _('run server in background')),
3820 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3822 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3821 ('E', 'errorlog', '', _('name of error log file to write to')),
3823 ('E', 'errorlog', '', _('name of error log file to write to')),
3822 # use string type, then we can check if something was passed
3824 # use string type, then we can check if something was passed
3823 ('p', 'port', '', _('port to listen on (default: 8000')),
3825 ('p', 'port', '', _('port to listen on (default: 8000')),
3824 ('a', 'address', '',
3826 ('a', 'address', '',
3825 _('address to listen on (default: all interfaces)')),
3827 _('address to listen on (default: all interfaces)')),
3826 ('', 'prefix', '',
3828 ('', 'prefix', '',
3827 _('prefix path to serve from (default: server root)')),
3829 _('prefix path to serve from (default: server root)')),
3828 ('n', 'name', '',
3830 ('n', 'name', '',
3829 _('name to show in web pages (default: working directory)')),
3831 _('name to show in web pages (default: working directory)')),
3830 ('', 'webdir-conf', '', _('name of the webdir config file'
3832 ('', 'webdir-conf', '', _('name of the webdir config file'
3831 ' (serve more than one repository)')),
3833 ' (serve more than one repository)')),
3832 ('', 'pid-file', '', _('name of file to write process ID to')),
3834 ('', 'pid-file', '', _('name of file to write process ID to')),
3833 ('', 'stdio', None, _('for remote clients')),
3835 ('', 'stdio', None, _('for remote clients')),
3834 ('t', 'templates', '', _('web templates to use')),
3836 ('t', 'templates', '', _('web templates to use')),
3835 ('', 'style', '', _('template style to use')),
3837 ('', 'style', '', _('template style to use')),
3836 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3838 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3837 ('', 'certificate', '', _('SSL certificate file'))],
3839 ('', 'certificate', '', _('SSL certificate file'))],
3838 _('[OPTION]...')),
3840 _('[OPTION]...')),
3839 "showconfig|debugconfig":
3841 "showconfig|debugconfig":
3840 (showconfig,
3842 (showconfig,
3841 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3843 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3842 _('[-u] [NAME]...')),
3844 _('[-u] [NAME]...')),
3843 "^summary|sum":
3845 "^summary|sum":
3844 (summary,
3846 (summary,
3845 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
3847 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
3846 "^status|st":
3848 "^status|st":
3847 (status,
3849 (status,
3848 [('A', 'all', None, _('show status of all files')),
3850 [('A', 'all', None, _('show status of all files')),
3849 ('m', 'modified', None, _('show only modified files')),
3851 ('m', 'modified', None, _('show only modified files')),
3850 ('a', 'added', None, _('show only added files')),
3852 ('a', 'added', None, _('show only added files')),
3851 ('r', 'removed', None, _('show only removed files')),
3853 ('r', 'removed', None, _('show only removed files')),
3852 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3854 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3853 ('c', 'clean', None, _('show only files without changes')),
3855 ('c', 'clean', None, _('show only files without changes')),
3854 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3856 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3855 ('i', 'ignored', None, _('show only ignored files')),
3857 ('i', 'ignored', None, _('show only ignored files')),
3856 ('n', 'no-status', None, _('hide status prefix')),
3858 ('n', 'no-status', None, _('hide status prefix')),
3857 ('C', 'copies', None, _('show source of copied files')),
3859 ('C', 'copies', None, _('show source of copied files')),
3858 ('0', 'print0', None,
3860 ('0', 'print0', None,
3859 _('end filenames with NUL, for use with xargs')),
3861 _('end filenames with NUL, for use with xargs')),
3860 ('', 'rev', [], _('show difference from revision')),
3862 ('', 'rev', [], _('show difference from revision')),
3861 ('', 'change', '', _('list the changed files of a revision')),
3863 ('', 'change', '', _('list the changed files of a revision')),
3862 ] + walkopts,
3864 ] + walkopts,
3863 _('[OPTION]... [FILE]...')),
3865 _('[OPTION]... [FILE]...')),
3864 "tag":
3866 "tag":
3865 (tag,
3867 (tag,
3866 [('f', 'force', None, _('replace existing tag')),
3868 [('f', 'force', None, _('replace existing tag')),
3867 ('l', 'local', None, _('make the tag local')),
3869 ('l', 'local', None, _('make the tag local')),
3868 ('r', 'rev', '', _('revision to tag')),
3870 ('r', 'rev', '', _('revision to tag')),
3869 ('', 'remove', None, _('remove a tag')),
3871 ('', 'remove', None, _('remove a tag')),
3870 # -l/--local is already there, commitopts cannot be used
3872 # -l/--local is already there, commitopts cannot be used
3871 ('m', 'message', '', _('use <text> as commit message')),
3873 ('m', 'message', '', _('use <text> as commit message')),
3872 ] + commitopts2,
3874 ] + commitopts2,
3873 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3875 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3874 "tags": (tags, [], ''),
3876 "tags": (tags, [], ''),
3875 "tip":
3877 "tip":
3876 (tip,
3878 (tip,
3877 [('p', 'patch', None, _('show patch')),
3879 [('p', 'patch', None, _('show patch')),
3878 ('g', 'git', None, _('use git extended diff format')),
3880 ('g', 'git', None, _('use git extended diff format')),
3879 ] + templateopts,
3881 ] + templateopts,
3880 _('[-p] [-g]')),
3882 _('[-p] [-g]')),
3881 "unbundle":
3883 "unbundle":
3882 (unbundle,
3884 (unbundle,
3883 [('u', 'update', None,
3885 [('u', 'update', None,
3884 _('update to new branch head if changesets were unbundled'))],
3886 _('update to new branch head if changesets were unbundled'))],
3885 _('[-u] FILE...')),
3887 _('[-u] FILE...')),
3886 "^update|up|checkout|co":
3888 "^update|up|checkout|co":
3887 (update,
3889 (update,
3888 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
3890 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
3889 ('c', 'check', None, _('check for uncommitted changes')),
3891 ('c', 'check', None, _('check for uncommitted changes')),
3890 ('d', 'date', '', _('tipmost revision matching date')),
3892 ('d', 'date', '', _('tipmost revision matching date')),
3891 ('r', 'rev', '', _('revision'))],
3893 ('r', 'rev', '', _('revision'))],
3892 _('[-c] [-C] [-d DATE] [[-r] REV]')),
3894 _('[-c] [-C] [-d DATE] [[-r] REV]')),
3893 "verify": (verify, []),
3895 "verify": (verify, []),
3894 "version": (version_, []),
3896 "version": (version_, []),
3895 }
3897 }
3896
3898
3897 norepo = ("clone init version help debugcommands debugcomplete debugdata"
3899 norepo = ("clone init version help debugcommands debugcomplete debugdata"
3898 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3900 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3899 optionalrepo = ("identify paths serve showconfig debugancestor")
3901 optionalrepo = ("identify paths serve showconfig debugancestor")
@@ -1,1638 +1,1694 b''
1 # patch.py - patch file parsing routines
1 # patch.py - patch file parsing routines
2 #
2 #
3 # Copyright 2006 Brendan Cully <brendan@kublai.com>
3 # Copyright 2006 Brendan Cully <brendan@kublai.com>
4 # Copyright 2007 Chris Mason <chris.mason@oracle.com>
4 # Copyright 2007 Chris Mason <chris.mason@oracle.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 from i18n import _
9 from i18n import _
10 from node import hex, nullid, short
10 from node import hex, nullid, short
11 import base85, cmdutil, mdiff, util, diffhelpers, copies
11 import base85, cmdutil, mdiff, util, diffhelpers, copies
12 import cStringIO, email.Parser, os, re
12 import cStringIO, email.Parser, os, re
13 import sys, tempfile, zlib
13 import sys, tempfile, zlib
14
14
15 gitre = re.compile('diff --git a/(.*) b/(.*)')
15 gitre = re.compile('diff --git a/(.*) b/(.*)')
16
16
17 class PatchError(Exception):
17 class PatchError(Exception):
18 pass
18 pass
19
19
20 class NoHunks(PatchError):
20 class NoHunks(PatchError):
21 pass
21 pass
22
22
23 # helper functions
23 # helper functions
24
24
25 def copyfile(src, dst, basedir):
25 def copyfile(src, dst, basedir):
26 abssrc, absdst = [util.canonpath(basedir, basedir, x) for x in [src, dst]]
26 abssrc, absdst = [util.canonpath(basedir, basedir, x) for x in [src, dst]]
27 if os.path.exists(absdst):
27 if os.path.exists(absdst):
28 raise util.Abort(_("cannot create %s: destination already exists") %
28 raise util.Abort(_("cannot create %s: destination already exists") %
29 dst)
29 dst)
30
30
31 dstdir = os.path.dirname(absdst)
31 dstdir = os.path.dirname(absdst)
32 if dstdir and not os.path.isdir(dstdir):
32 if dstdir and not os.path.isdir(dstdir):
33 try:
33 try:
34 os.makedirs(dstdir)
34 os.makedirs(dstdir)
35 except IOError:
35 except IOError:
36 raise util.Abort(
36 raise util.Abort(
37 _("cannot create %s: unable to create destination directory")
37 _("cannot create %s: unable to create destination directory")
38 % dst)
38 % dst)
39
39
40 util.copyfile(abssrc, absdst)
40 util.copyfile(abssrc, absdst)
41
41
42 # public functions
42 # public functions
43
43
44 def split(stream):
44 def split(stream):
45 '''return an iterator of individual patches from a stream'''
45 '''return an iterator of individual patches from a stream'''
46 def isheader(line, inheader):
46 def isheader(line, inheader):
47 if inheader and line[0] in (' ', '\t'):
47 if inheader and line[0] in (' ', '\t'):
48 # continuation
48 # continuation
49 return True
49 return True
50 l = line.split(': ', 1)
50 l = line.split(': ', 1)
51 return len(l) == 2 and ' ' not in l[0]
51 return len(l) == 2 and ' ' not in l[0]
52
52
53 def chunk(lines):
53 def chunk(lines):
54 return cStringIO.StringIO(''.join(lines))
54 return cStringIO.StringIO(''.join(lines))
55
55
56 def hgsplit(stream, cur):
56 def hgsplit(stream, cur):
57 inheader = True
57 inheader = True
58
58
59 for line in stream:
59 for line in stream:
60 if not line.strip():
60 if not line.strip():
61 inheader = False
61 inheader = False
62 if not inheader and line.startswith('# HG changeset patch'):
62 if not inheader and line.startswith('# HG changeset patch'):
63 yield chunk(cur)
63 yield chunk(cur)
64 cur = []
64 cur = []
65 inheader = True
65 inheader = True
66
66
67 cur.append(line)
67 cur.append(line)
68
68
69 if cur:
69 if cur:
70 yield chunk(cur)
70 yield chunk(cur)
71
71
72 def mboxsplit(stream, cur):
72 def mboxsplit(stream, cur):
73 for line in stream:
73 for line in stream:
74 if line.startswith('From '):
74 if line.startswith('From '):
75 for c in split(chunk(cur[1:])):
75 for c in split(chunk(cur[1:])):
76 yield c
76 yield c
77 cur = []
77 cur = []
78
78
79 cur.append(line)
79 cur.append(line)
80
80
81 if cur:
81 if cur:
82 for c in split(chunk(cur[1:])):
82 for c in split(chunk(cur[1:])):
83 yield c
83 yield c
84
84
85 def mimesplit(stream, cur):
85 def mimesplit(stream, cur):
86 def msgfp(m):
86 def msgfp(m):
87 fp = cStringIO.StringIO()
87 fp = cStringIO.StringIO()
88 g = email.Generator.Generator(fp, mangle_from_=False)
88 g = email.Generator.Generator(fp, mangle_from_=False)
89 g.flatten(m)
89 g.flatten(m)
90 fp.seek(0)
90 fp.seek(0)
91 return fp
91 return fp
92
92
93 for line in stream:
93 for line in stream:
94 cur.append(line)
94 cur.append(line)
95 c = chunk(cur)
95 c = chunk(cur)
96
96
97 m = email.Parser.Parser().parse(c)
97 m = email.Parser.Parser().parse(c)
98 if not m.is_multipart():
98 if not m.is_multipart():
99 yield msgfp(m)
99 yield msgfp(m)
100 else:
100 else:
101 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
101 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
102 for part in m.walk():
102 for part in m.walk():
103 ct = part.get_content_type()
103 ct = part.get_content_type()
104 if ct not in ok_types:
104 if ct not in ok_types:
105 continue
105 continue
106 yield msgfp(part)
106 yield msgfp(part)
107
107
108 def headersplit(stream, cur):
108 def headersplit(stream, cur):
109 inheader = False
109 inheader = False
110
110
111 for line in stream:
111 for line in stream:
112 if not inheader and isheader(line, inheader):
112 if not inheader and isheader(line, inheader):
113 yield chunk(cur)
113 yield chunk(cur)
114 cur = []
114 cur = []
115 inheader = True
115 inheader = True
116 if inheader and not isheader(line, inheader):
116 if inheader and not isheader(line, inheader):
117 inheader = False
117 inheader = False
118
118
119 cur.append(line)
119 cur.append(line)
120
120
121 if cur:
121 if cur:
122 yield chunk(cur)
122 yield chunk(cur)
123
123
124 def remainder(cur):
124 def remainder(cur):
125 yield chunk(cur)
125 yield chunk(cur)
126
126
127 class fiter(object):
127 class fiter(object):
128 def __init__(self, fp):
128 def __init__(self, fp):
129 self.fp = fp
129 self.fp = fp
130
130
131 def __iter__(self):
131 def __iter__(self):
132 return self
132 return self
133
133
134 def next(self):
134 def next(self):
135 l = self.fp.readline()
135 l = self.fp.readline()
136 if not l:
136 if not l:
137 raise StopIteration
137 raise StopIteration
138 return l
138 return l
139
139
140 inheader = False
140 inheader = False
141 cur = []
141 cur = []
142
142
143 mimeheaders = ['content-type']
143 mimeheaders = ['content-type']
144
144
145 if not hasattr(stream, 'next'):
145 if not hasattr(stream, 'next'):
146 # http responses, for example, have readline but not next
146 # http responses, for example, have readline but not next
147 stream = fiter(stream)
147 stream = fiter(stream)
148
148
149 for line in stream:
149 for line in stream:
150 cur.append(line)
150 cur.append(line)
151 if line.startswith('# HG changeset patch'):
151 if line.startswith('# HG changeset patch'):
152 return hgsplit(stream, cur)
152 return hgsplit(stream, cur)
153 elif line.startswith('From '):
153 elif line.startswith('From '):
154 return mboxsplit(stream, cur)
154 return mboxsplit(stream, cur)
155 elif isheader(line, inheader):
155 elif isheader(line, inheader):
156 inheader = True
156 inheader = True
157 if line.split(':', 1)[0].lower() in mimeheaders:
157 if line.split(':', 1)[0].lower() in mimeheaders:
158 # let email parser handle this
158 # let email parser handle this
159 return mimesplit(stream, cur)
159 return mimesplit(stream, cur)
160 elif line.startswith('--- ') and inheader:
160 elif line.startswith('--- ') and inheader:
161 # No evil headers seen by diff start, split by hand
161 # No evil headers seen by diff start, split by hand
162 return headersplit(stream, cur)
162 return headersplit(stream, cur)
163 # Not enough info, keep reading
163 # Not enough info, keep reading
164
164
165 # if we are here, we have a very plain patch
165 # if we are here, we have a very plain patch
166 return remainder(cur)
166 return remainder(cur)
167
167
168 def extract(ui, fileobj):
168 def extract(ui, fileobj):
169 '''extract patch from data read from fileobj.
169 '''extract patch from data read from fileobj.
170
170
171 patch can be a normal patch or contained in an email message.
171 patch can be a normal patch or contained in an email message.
172
172
173 return tuple (filename, message, user, date, node, p1, p2).
173 return tuple (filename, message, user, date, node, p1, p2).
174 Any item in the returned tuple can be None. If filename is None,
174 Any item in the returned tuple can be None. If filename is None,
175 fileobj did not contain a patch. Caller must unlink filename when done.'''
175 fileobj did not contain a patch. Caller must unlink filename when done.'''
176
176
177 # attempt to detect the start of a patch
177 # attempt to detect the start of a patch
178 # (this heuristic is borrowed from quilt)
178 # (this heuristic is borrowed from quilt)
179 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |'
179 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |'
180 r'retrieving revision [0-9]+(\.[0-9]+)*$|'
180 r'retrieving revision [0-9]+(\.[0-9]+)*$|'
181 r'---[ \t].*?^\+\+\+[ \t]|'
181 r'---[ \t].*?^\+\+\+[ \t]|'
182 r'\*\*\*[ \t].*?^---[ \t])', re.MULTILINE|re.DOTALL)
182 r'\*\*\*[ \t].*?^---[ \t])', re.MULTILINE|re.DOTALL)
183
183
184 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
184 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
185 tmpfp = os.fdopen(fd, 'w')
185 tmpfp = os.fdopen(fd, 'w')
186 try:
186 try:
187 msg = email.Parser.Parser().parse(fileobj)
187 msg = email.Parser.Parser().parse(fileobj)
188
188
189 subject = msg['Subject']
189 subject = msg['Subject']
190 user = msg['From']
190 user = msg['From']
191 if not subject and not user:
191 if not subject and not user:
192 # Not an email, restore parsed headers if any
192 # Not an email, restore parsed headers if any
193 subject = '\n'.join(': '.join(h) for h in msg.items()) + '\n'
193 subject = '\n'.join(': '.join(h) for h in msg.items()) + '\n'
194
194
195 gitsendmail = 'git-send-email' in msg.get('X-Mailer', '')
195 gitsendmail = 'git-send-email' in msg.get('X-Mailer', '')
196 # should try to parse msg['Date']
196 # should try to parse msg['Date']
197 date = None
197 date = None
198 nodeid = None
198 nodeid = None
199 branch = None
199 branch = None
200 parents = []
200 parents = []
201
201
202 if subject:
202 if subject:
203 if subject.startswith('[PATCH'):
203 if subject.startswith('[PATCH'):
204 pend = subject.find(']')
204 pend = subject.find(']')
205 if pend >= 0:
205 if pend >= 0:
206 subject = subject[pend + 1:].lstrip()
206 subject = subject[pend + 1:].lstrip()
207 subject = subject.replace('\n\t', ' ')
207 subject = subject.replace('\n\t', ' ')
208 ui.debug('Subject: %s\n' % subject)
208 ui.debug('Subject: %s\n' % subject)
209 if user:
209 if user:
210 ui.debug('From: %s\n' % user)
210 ui.debug('From: %s\n' % user)
211 diffs_seen = 0
211 diffs_seen = 0
212 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
212 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
213 message = ''
213 message = ''
214 for part in msg.walk():
214 for part in msg.walk():
215 content_type = part.get_content_type()
215 content_type = part.get_content_type()
216 ui.debug('Content-Type: %s\n' % content_type)
216 ui.debug('Content-Type: %s\n' % content_type)
217 if content_type not in ok_types:
217 if content_type not in ok_types:
218 continue
218 continue
219 payload = part.get_payload(decode=True)
219 payload = part.get_payload(decode=True)
220 m = diffre.search(payload)
220 m = diffre.search(payload)
221 if m:
221 if m:
222 hgpatch = False
222 hgpatch = False
223 ignoretext = False
223 ignoretext = False
224
224
225 ui.debug('found patch at byte %d\n' % m.start(0))
225 ui.debug('found patch at byte %d\n' % m.start(0))
226 diffs_seen += 1
226 diffs_seen += 1
227 cfp = cStringIO.StringIO()
227 cfp = cStringIO.StringIO()
228 for line in payload[:m.start(0)].splitlines():
228 for line in payload[:m.start(0)].splitlines():
229 if line.startswith('# HG changeset patch'):
229 if line.startswith('# HG changeset patch'):
230 ui.debug('patch generated by hg export\n')
230 ui.debug('patch generated by hg export\n')
231 hgpatch = True
231 hgpatch = True
232 # drop earlier commit message content
232 # drop earlier commit message content
233 cfp.seek(0)
233 cfp.seek(0)
234 cfp.truncate()
234 cfp.truncate()
235 subject = None
235 subject = None
236 elif hgpatch:
236 elif hgpatch:
237 if line.startswith('# User '):
237 if line.startswith('# User '):
238 user = line[7:]
238 user = line[7:]
239 ui.debug('From: %s\n' % user)
239 ui.debug('From: %s\n' % user)
240 elif line.startswith("# Date "):
240 elif line.startswith("# Date "):
241 date = line[7:]
241 date = line[7:]
242 elif line.startswith("# Branch "):
242 elif line.startswith("# Branch "):
243 branch = line[9:]
243 branch = line[9:]
244 elif line.startswith("# Node ID "):
244 elif line.startswith("# Node ID "):
245 nodeid = line[10:]
245 nodeid = line[10:]
246 elif line.startswith("# Parent "):
246 elif line.startswith("# Parent "):
247 parents.append(line[10:])
247 parents.append(line[10:])
248 elif line == '---' and gitsendmail:
248 elif line == '---' and gitsendmail:
249 ignoretext = True
249 ignoretext = True
250 if not line.startswith('# ') and not ignoretext:
250 if not line.startswith('# ') and not ignoretext:
251 cfp.write(line)
251 cfp.write(line)
252 cfp.write('\n')
252 cfp.write('\n')
253 message = cfp.getvalue()
253 message = cfp.getvalue()
254 if tmpfp:
254 if tmpfp:
255 tmpfp.write(payload)
255 tmpfp.write(payload)
256 if not payload.endswith('\n'):
256 if not payload.endswith('\n'):
257 tmpfp.write('\n')
257 tmpfp.write('\n')
258 elif not diffs_seen and message and content_type == 'text/plain':
258 elif not diffs_seen and message and content_type == 'text/plain':
259 message += '\n' + payload
259 message += '\n' + payload
260 except:
260 except:
261 tmpfp.close()
261 tmpfp.close()
262 os.unlink(tmpname)
262 os.unlink(tmpname)
263 raise
263 raise
264
264
265 if subject and not message.startswith(subject):
265 if subject and not message.startswith(subject):
266 message = '%s\n%s' % (subject, message)
266 message = '%s\n%s' % (subject, message)
267 tmpfp.close()
267 tmpfp.close()
268 if not diffs_seen:
268 if not diffs_seen:
269 os.unlink(tmpname)
269 os.unlink(tmpname)
270 return None, message, user, date, branch, None, None, None
270 return None, message, user, date, branch, None, None, None
271 p1 = parents and parents.pop(0) or None
271 p1 = parents and parents.pop(0) or None
272 p2 = parents and parents.pop(0) or None
272 p2 = parents and parents.pop(0) or None
273 return tmpname, message, user, date, branch, nodeid, p1, p2
273 return tmpname, message, user, date, branch, nodeid, p1, p2
274
274
275 GP_PATCH = 1 << 0 # we have to run patch
275 GP_PATCH = 1 << 0 # we have to run patch
276 GP_FILTER = 1 << 1 # there's some copy/rename operation
276 GP_FILTER = 1 << 1 # there's some copy/rename operation
277 GP_BINARY = 1 << 2 # there's a binary patch
277 GP_BINARY = 1 << 2 # there's a binary patch
278
278
279 class patchmeta(object):
279 class patchmeta(object):
280 """Patched file metadata
280 """Patched file metadata
281
281
282 'op' is the performed operation within ADD, DELETE, RENAME, MODIFY
282 'op' is the performed operation within ADD, DELETE, RENAME, MODIFY
283 or COPY. 'path' is patched file path. 'oldpath' is set to the
283 or COPY. 'path' is patched file path. 'oldpath' is set to the
284 origin file when 'op' is either COPY or RENAME, None otherwise. If
284 origin file when 'op' is either COPY or RENAME, None otherwise. If
285 file mode is changed, 'mode' is a tuple (islink, isexec) where
285 file mode is changed, 'mode' is a tuple (islink, isexec) where
286 'islink' is True if the file is a symlink and 'isexec' is True if
286 'islink' is True if the file is a symlink and 'isexec' is True if
287 the file is executable. Otherwise, 'mode' is None.
287 the file is executable. Otherwise, 'mode' is None.
288 """
288 """
289 def __init__(self, path):
289 def __init__(self, path):
290 self.path = path
290 self.path = path
291 self.oldpath = None
291 self.oldpath = None
292 self.mode = None
292 self.mode = None
293 self.op = 'MODIFY'
293 self.op = 'MODIFY'
294 self.lineno = 0
294 self.lineno = 0
295 self.binary = False
295 self.binary = False
296
296
297 def setmode(self, mode):
297 def setmode(self, mode):
298 islink = mode & 020000
298 islink = mode & 020000
299 isexec = mode & 0100
299 isexec = mode & 0100
300 self.mode = (islink, isexec)
300 self.mode = (islink, isexec)
301
301
302 def readgitpatch(lr):
302 def readgitpatch(lr):
303 """extract git-style metadata about patches from <patchname>"""
303 """extract git-style metadata about patches from <patchname>"""
304
304
305 # Filter patch for git information
305 # Filter patch for git information
306 gp = None
306 gp = None
307 gitpatches = []
307 gitpatches = []
308 # Can have a git patch with only metadata, causing patch to complain
308 # Can have a git patch with only metadata, causing patch to complain
309 dopatch = 0
309 dopatch = 0
310
310
311 lineno = 0
311 lineno = 0
312 for line in lr:
312 for line in lr:
313 lineno += 1
313 lineno += 1
314 line = line.rstrip(' \r\n')
314 line = line.rstrip(' \r\n')
315 if line.startswith('diff --git'):
315 if line.startswith('diff --git'):
316 m = gitre.match(line)
316 m = gitre.match(line)
317 if m:
317 if m:
318 if gp:
318 if gp:
319 gitpatches.append(gp)
319 gitpatches.append(gp)
320 dst = m.group(2)
320 dst = m.group(2)
321 gp = patchmeta(dst)
321 gp = patchmeta(dst)
322 gp.lineno = lineno
322 gp.lineno = lineno
323 elif gp:
323 elif gp:
324 if line.startswith('--- '):
324 if line.startswith('--- '):
325 if gp.op in ('COPY', 'RENAME'):
325 if gp.op in ('COPY', 'RENAME'):
326 dopatch |= GP_FILTER
326 dopatch |= GP_FILTER
327 gitpatches.append(gp)
327 gitpatches.append(gp)
328 gp = None
328 gp = None
329 dopatch |= GP_PATCH
329 dopatch |= GP_PATCH
330 continue
330 continue
331 if line.startswith('rename from '):
331 if line.startswith('rename from '):
332 gp.op = 'RENAME'
332 gp.op = 'RENAME'
333 gp.oldpath = line[12:]
333 gp.oldpath = line[12:]
334 elif line.startswith('rename to '):
334 elif line.startswith('rename to '):
335 gp.path = line[10:]
335 gp.path = line[10:]
336 elif line.startswith('copy from '):
336 elif line.startswith('copy from '):
337 gp.op = 'COPY'
337 gp.op = 'COPY'
338 gp.oldpath = line[10:]
338 gp.oldpath = line[10:]
339 elif line.startswith('copy to '):
339 elif line.startswith('copy to '):
340 gp.path = line[8:]
340 gp.path = line[8:]
341 elif line.startswith('deleted file'):
341 elif line.startswith('deleted file'):
342 gp.op = 'DELETE'
342 gp.op = 'DELETE'
343 # is the deleted file a symlink?
343 # is the deleted file a symlink?
344 gp.setmode(int(line[-6:], 8))
344 gp.setmode(int(line[-6:], 8))
345 elif line.startswith('new file mode '):
345 elif line.startswith('new file mode '):
346 gp.op = 'ADD'
346 gp.op = 'ADD'
347 gp.setmode(int(line[-6:], 8))
347 gp.setmode(int(line[-6:], 8))
348 elif line.startswith('new mode '):
348 elif line.startswith('new mode '):
349 gp.setmode(int(line[-6:], 8))
349 gp.setmode(int(line[-6:], 8))
350 elif line.startswith('GIT binary patch'):
350 elif line.startswith('GIT binary patch'):
351 dopatch |= GP_BINARY
351 dopatch |= GP_BINARY
352 gp.binary = True
352 gp.binary = True
353 if gp:
353 if gp:
354 gitpatches.append(gp)
354 gitpatches.append(gp)
355
355
356 if not gitpatches:
356 if not gitpatches:
357 dopatch = GP_PATCH
357 dopatch = GP_PATCH
358
358
359 return (dopatch, gitpatches)
359 return (dopatch, gitpatches)
360
360
361 class linereader(object):
361 class linereader(object):
362 # simple class to allow pushing lines back into the input stream
362 # simple class to allow pushing lines back into the input stream
363 def __init__(self, fp, textmode=False):
363 def __init__(self, fp, textmode=False):
364 self.fp = fp
364 self.fp = fp
365 self.buf = []
365 self.buf = []
366 self.textmode = textmode
366 self.textmode = textmode
367 self.eol = None
367 self.eol = None
368
368
369 def push(self, line):
369 def push(self, line):
370 if line is not None:
370 if line is not None:
371 self.buf.append(line)
371 self.buf.append(line)
372
372
373 def readline(self):
373 def readline(self):
374 if self.buf:
374 if self.buf:
375 l = self.buf[0]
375 l = self.buf[0]
376 del self.buf[0]
376 del self.buf[0]
377 return l
377 return l
378 l = self.fp.readline()
378 l = self.fp.readline()
379 if not self.eol:
379 if not self.eol:
380 if l.endswith('\r\n'):
380 if l.endswith('\r\n'):
381 self.eol = '\r\n'
381 self.eol = '\r\n'
382 elif l.endswith('\n'):
382 elif l.endswith('\n'):
383 self.eol = '\n'
383 self.eol = '\n'
384 if self.textmode and l.endswith('\r\n'):
384 if self.textmode and l.endswith('\r\n'):
385 l = l[:-2] + '\n'
385 l = l[:-2] + '\n'
386 return l
386 return l
387
387
388 def __iter__(self):
388 def __iter__(self):
389 while 1:
389 while 1:
390 l = self.readline()
390 l = self.readline()
391 if not l:
391 if not l:
392 break
392 break
393 yield l
393 yield l
394
394
395 # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1
395 # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1
396 unidesc = re.compile('@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@')
396 unidesc = re.compile('@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@')
397 contextdesc = re.compile('(---|\*\*\*) (\d+)(,(\d+))? (---|\*\*\*)')
397 contextdesc = re.compile('(---|\*\*\*) (\d+)(,(\d+))? (---|\*\*\*)')
398 eolmodes = ['strict', 'crlf', 'lf', 'auto']
398 eolmodes = ['strict', 'crlf', 'lf', 'auto']
399
399
400 class patchfile(object):
400 class patchfile(object):
401 def __init__(self, ui, fname, opener, missing=False, eolmode='strict'):
401 def __init__(self, ui, fname, opener, missing=False, eolmode='strict'):
402 self.fname = fname
402 self.fname = fname
403 self.eolmode = eolmode
403 self.eolmode = eolmode
404 self.eol = None
404 self.eol = None
405 self.opener = opener
405 self.opener = opener
406 self.ui = ui
406 self.ui = ui
407 self.lines = []
407 self.lines = []
408 self.exists = False
408 self.exists = False
409 self.missing = missing
409 self.missing = missing
410 if not missing:
410 if not missing:
411 try:
411 try:
412 self.lines = self.readlines(fname)
412 self.lines = self.readlines(fname)
413 self.exists = True
413 self.exists = True
414 except IOError:
414 except IOError:
415 pass
415 pass
416 else:
416 else:
417 self.ui.warn(_("unable to find '%s' for patching\n") % self.fname)
417 self.ui.warn(_("unable to find '%s' for patching\n") % self.fname)
418
418
419 self.hash = {}
419 self.hash = {}
420 self.dirty = 0
420 self.dirty = 0
421 self.offset = 0
421 self.offset = 0
422 self.skew = 0
422 self.skew = 0
423 self.rej = []
423 self.rej = []
424 self.fileprinted = False
424 self.fileprinted = False
425 self.printfile(False)
425 self.printfile(False)
426 self.hunks = 0
426 self.hunks = 0
427
427
428 def readlines(self, fname):
428 def readlines(self, fname):
429 if os.path.islink(fname):
429 if os.path.islink(fname):
430 return [os.readlink(fname)]
430 return [os.readlink(fname)]
431 fp = self.opener(fname, 'r')
431 fp = self.opener(fname, 'r')
432 try:
432 try:
433 lr = linereader(fp, self.eolmode != 'strict')
433 lr = linereader(fp, self.eolmode != 'strict')
434 lines = list(lr)
434 lines = list(lr)
435 self.eol = lr.eol
435 self.eol = lr.eol
436 return lines
436 return lines
437 finally:
437 finally:
438 fp.close()
438 fp.close()
439
439
440 def writelines(self, fname, lines):
440 def writelines(self, fname, lines):
441 # Ensure supplied data ends in fname, being a regular file or
441 # Ensure supplied data ends in fname, being a regular file or
442 # a symlink. updatedir() will -too magically- take care of
442 # a symlink. updatedir() will -too magically- take care of
443 # setting it to the proper type afterwards.
443 # setting it to the proper type afterwards.
444 islink = os.path.islink(fname)
444 islink = os.path.islink(fname)
445 if islink:
445 if islink:
446 fp = cStringIO.StringIO()
446 fp = cStringIO.StringIO()
447 else:
447 else:
448 fp = self.opener(fname, 'w')
448 fp = self.opener(fname, 'w')
449 try:
449 try:
450 if self.eolmode == 'auto':
450 if self.eolmode == 'auto':
451 eol = self.eol
451 eol = self.eol
452 elif self.eolmode == 'crlf':
452 elif self.eolmode == 'crlf':
453 eol = '\r\n'
453 eol = '\r\n'
454 else:
454 else:
455 eol = '\n'
455 eol = '\n'
456
456
457 if self.eolmode != 'strict' and eol and eol != '\n':
457 if self.eolmode != 'strict' and eol and eol != '\n':
458 for l in lines:
458 for l in lines:
459 if l and l[-1] == '\n':
459 if l and l[-1] == '\n':
460 l = l[:-1] + eol
460 l = l[:-1] + eol
461 fp.write(l)
461 fp.write(l)
462 else:
462 else:
463 fp.writelines(lines)
463 fp.writelines(lines)
464 if islink:
464 if islink:
465 self.opener.symlink(fp.getvalue(), fname)
465 self.opener.symlink(fp.getvalue(), fname)
466 finally:
466 finally:
467 fp.close()
467 fp.close()
468
468
469 def unlink(self, fname):
469 def unlink(self, fname):
470 os.unlink(fname)
470 os.unlink(fname)
471
471
472 def printfile(self, warn):
472 def printfile(self, warn):
473 if self.fileprinted:
473 if self.fileprinted:
474 return
474 return
475 if warn or self.ui.verbose:
475 if warn or self.ui.verbose:
476 self.fileprinted = True
476 self.fileprinted = True
477 s = _("patching file %s\n") % self.fname
477 s = _("patching file %s\n") % self.fname
478 if warn:
478 if warn:
479 self.ui.warn(s)
479 self.ui.warn(s)
480 else:
480 else:
481 self.ui.note(s)
481 self.ui.note(s)
482
482
483
483
484 def findlines(self, l, linenum):
484 def findlines(self, l, linenum):
485 # looks through the hash and finds candidate lines. The
485 # looks through the hash and finds candidate lines. The
486 # result is a list of line numbers sorted based on distance
486 # result is a list of line numbers sorted based on distance
487 # from linenum
487 # from linenum
488
488
489 cand = self.hash.get(l, [])
489 cand = self.hash.get(l, [])
490 if len(cand) > 1:
490 if len(cand) > 1:
491 # resort our list of potentials forward then back.
491 # resort our list of potentials forward then back.
492 cand.sort(key=lambda x: abs(x - linenum))
492 cand.sort(key=lambda x: abs(x - linenum))
493 return cand
493 return cand
494
494
495 def hashlines(self):
495 def hashlines(self):
496 self.hash = {}
496 self.hash = {}
497 for x, s in enumerate(self.lines):
497 for x, s in enumerate(self.lines):
498 self.hash.setdefault(s, []).append(x)
498 self.hash.setdefault(s, []).append(x)
499
499
500 def write_rej(self):
500 def write_rej(self):
501 # our rejects are a little different from patch(1). This always
501 # our rejects are a little different from patch(1). This always
502 # creates rejects in the same form as the original patch. A file
502 # creates rejects in the same form as the original patch. A file
503 # header is inserted so that you can run the reject through patch again
503 # header is inserted so that you can run the reject through patch again
504 # without having to type the filename.
504 # without having to type the filename.
505
505
506 if not self.rej:
506 if not self.rej:
507 return
507 return
508
508
509 fname = self.fname + ".rej"
509 fname = self.fname + ".rej"
510 self.ui.warn(
510 self.ui.warn(
511 _("%d out of %d hunks FAILED -- saving rejects to file %s\n") %
511 _("%d out of %d hunks FAILED -- saving rejects to file %s\n") %
512 (len(self.rej), self.hunks, fname))
512 (len(self.rej), self.hunks, fname))
513
513
514 def rejlines():
514 def rejlines():
515 base = os.path.basename(self.fname)
515 base = os.path.basename(self.fname)
516 yield "--- %s\n+++ %s\n" % (base, base)
516 yield "--- %s\n+++ %s\n" % (base, base)
517 for x in self.rej:
517 for x in self.rej:
518 for l in x.hunk:
518 for l in x.hunk:
519 yield l
519 yield l
520 if l[-1] != '\n':
520 if l[-1] != '\n':
521 yield "\n\ No newline at end of file\n"
521 yield "\n\ No newline at end of file\n"
522
522
523 self.writelines(fname, rejlines())
523 self.writelines(fname, rejlines())
524
524
525 def write(self, dest=None):
525 def write(self, dest=None):
526 if not self.dirty:
526 if not self.dirty:
527 return
527 return
528 if not dest:
528 if not dest:
529 dest = self.fname
529 dest = self.fname
530 self.writelines(dest, self.lines)
530 self.writelines(dest, self.lines)
531
531
532 def close(self):
532 def close(self):
533 self.write()
533 self.write()
534 self.write_rej()
534 self.write_rej()
535
535
536 def apply(self, h):
536 def apply(self, h):
537 if not h.complete():
537 if not h.complete():
538 raise PatchError(_("bad hunk #%d %s (%d %d %d %d)") %
538 raise PatchError(_("bad hunk #%d %s (%d %d %d %d)") %
539 (h.number, h.desc, len(h.a), h.lena, len(h.b),
539 (h.number, h.desc, len(h.a), h.lena, len(h.b),
540 h.lenb))
540 h.lenb))
541
541
542 self.hunks += 1
542 self.hunks += 1
543
543
544 if self.missing:
544 if self.missing:
545 self.rej.append(h)
545 self.rej.append(h)
546 return -1
546 return -1
547
547
548 if self.exists and h.createfile():
548 if self.exists and h.createfile():
549 self.ui.warn(_("file %s already exists\n") % self.fname)
549 self.ui.warn(_("file %s already exists\n") % self.fname)
550 self.rej.append(h)
550 self.rej.append(h)
551 return -1
551 return -1
552
552
553 if isinstance(h, binhunk):
553 if isinstance(h, binhunk):
554 if h.rmfile():
554 if h.rmfile():
555 self.unlink(self.fname)
555 self.unlink(self.fname)
556 else:
556 else:
557 self.lines[:] = h.new()
557 self.lines[:] = h.new()
558 self.offset += len(h.new())
558 self.offset += len(h.new())
559 self.dirty = 1
559 self.dirty = 1
560 return 0
560 return 0
561
561
562 horig = h
562 horig = h
563 if (self.eolmode in ('crlf', 'lf')
563 if (self.eolmode in ('crlf', 'lf')
564 or self.eolmode == 'auto' and self.eol):
564 or self.eolmode == 'auto' and self.eol):
565 # If new eols are going to be normalized, then normalize
565 # If new eols are going to be normalized, then normalize
566 # hunk data before patching. Otherwise, preserve input
566 # hunk data before patching. Otherwise, preserve input
567 # line-endings.
567 # line-endings.
568 h = h.getnormalized()
568 h = h.getnormalized()
569
569
570 # fast case first, no offsets, no fuzz
570 # fast case first, no offsets, no fuzz
571 old = h.old()
571 old = h.old()
572 # patch starts counting at 1 unless we are adding the file
572 # patch starts counting at 1 unless we are adding the file
573 if h.starta == 0:
573 if h.starta == 0:
574 start = 0
574 start = 0
575 else:
575 else:
576 start = h.starta + self.offset - 1
576 start = h.starta + self.offset - 1
577 orig_start = start
577 orig_start = start
578 # if there's skew we want to emit the "(offset %d lines)" even
578 # if there's skew we want to emit the "(offset %d lines)" even
579 # when the hunk cleanly applies at start + skew, so skip the
579 # when the hunk cleanly applies at start + skew, so skip the
580 # fast case code
580 # fast case code
581 if self.skew == 0 and diffhelpers.testhunk(old, self.lines, start) == 0:
581 if self.skew == 0 and diffhelpers.testhunk(old, self.lines, start) == 0:
582 if h.rmfile():
582 if h.rmfile():
583 self.unlink(self.fname)
583 self.unlink(self.fname)
584 else:
584 else:
585 self.lines[start : start + h.lena] = h.new()
585 self.lines[start : start + h.lena] = h.new()
586 self.offset += h.lenb - h.lena
586 self.offset += h.lenb - h.lena
587 self.dirty = 1
587 self.dirty = 1
588 return 0
588 return 0
589
589
590 # ok, we couldn't match the hunk. Lets look for offsets and fuzz it
590 # ok, we couldn't match the hunk. Lets look for offsets and fuzz it
591 self.hashlines()
591 self.hashlines()
592 if h.hunk[-1][0] != ' ':
592 if h.hunk[-1][0] != ' ':
593 # if the hunk tried to put something at the bottom of the file
593 # if the hunk tried to put something at the bottom of the file
594 # override the start line and use eof here
594 # override the start line and use eof here
595 search_start = len(self.lines)
595 search_start = len(self.lines)
596 else:
596 else:
597 search_start = orig_start + self.skew
597 search_start = orig_start + self.skew
598
598
599 for fuzzlen in xrange(3):
599 for fuzzlen in xrange(3):
600 for toponly in [True, False]:
600 for toponly in [True, False]:
601 old = h.old(fuzzlen, toponly)
601 old = h.old(fuzzlen, toponly)
602
602
603 cand = self.findlines(old[0][1:], search_start)
603 cand = self.findlines(old[0][1:], search_start)
604 for l in cand:
604 for l in cand:
605 if diffhelpers.testhunk(old, self.lines, l) == 0:
605 if diffhelpers.testhunk(old, self.lines, l) == 0:
606 newlines = h.new(fuzzlen, toponly)
606 newlines = h.new(fuzzlen, toponly)
607 self.lines[l : l + len(old)] = newlines
607 self.lines[l : l + len(old)] = newlines
608 self.offset += len(newlines) - len(old)
608 self.offset += len(newlines) - len(old)
609 self.skew = l - orig_start
609 self.skew = l - orig_start
610 self.dirty = 1
610 self.dirty = 1
611 offset = l - orig_start - fuzzlen
611 offset = l - orig_start - fuzzlen
612 if fuzzlen:
612 if fuzzlen:
613 msg = _("Hunk #%d succeeded at %d "
613 msg = _("Hunk #%d succeeded at %d "
614 "with fuzz %d "
614 "with fuzz %d "
615 "(offset %d lines).\n")
615 "(offset %d lines).\n")
616 self.printfile(True)
616 self.printfile(True)
617 self.ui.warn(msg %
617 self.ui.warn(msg %
618 (h.number, l + 1, fuzzlen, offset))
618 (h.number, l + 1, fuzzlen, offset))
619 else:
619 else:
620 msg = _("Hunk #%d succeeded at %d "
620 msg = _("Hunk #%d succeeded at %d "
621 "(offset %d lines).\n")
621 "(offset %d lines).\n")
622 self.ui.note(msg % (h.number, l + 1, offset))
622 self.ui.note(msg % (h.number, l + 1, offset))
623 return fuzzlen
623 return fuzzlen
624 self.printfile(True)
624 self.printfile(True)
625 self.ui.warn(_("Hunk #%d FAILED at %d\n") % (h.number, orig_start))
625 self.ui.warn(_("Hunk #%d FAILED at %d\n") % (h.number, orig_start))
626 self.rej.append(horig)
626 self.rej.append(horig)
627 return -1
627 return -1
628
628
629 class hunk(object):
629 class hunk(object):
630 def __init__(self, desc, num, lr, context, create=False, remove=False):
630 def __init__(self, desc, num, lr, context, create=False, remove=False):
631 self.number = num
631 self.number = num
632 self.desc = desc
632 self.desc = desc
633 self.hunk = [desc]
633 self.hunk = [desc]
634 self.a = []
634 self.a = []
635 self.b = []
635 self.b = []
636 self.starta = self.lena = None
636 self.starta = self.lena = None
637 self.startb = self.lenb = None
637 self.startb = self.lenb = None
638 if lr is not None:
638 if lr is not None:
639 if context:
639 if context:
640 self.read_context_hunk(lr)
640 self.read_context_hunk(lr)
641 else:
641 else:
642 self.read_unified_hunk(lr)
642 self.read_unified_hunk(lr)
643 self.create = create
643 self.create = create
644 self.remove = remove and not create
644 self.remove = remove and not create
645
645
646 def getnormalized(self):
646 def getnormalized(self):
647 """Return a copy with line endings normalized to LF."""
647 """Return a copy with line endings normalized to LF."""
648
648
649 def normalize(lines):
649 def normalize(lines):
650 nlines = []
650 nlines = []
651 for line in lines:
651 for line in lines:
652 if line.endswith('\r\n'):
652 if line.endswith('\r\n'):
653 line = line[:-2] + '\n'
653 line = line[:-2] + '\n'
654 nlines.append(line)
654 nlines.append(line)
655 return nlines
655 return nlines
656
656
657 # Dummy object, it is rebuilt manually
657 # Dummy object, it is rebuilt manually
658 nh = hunk(self.desc, self.number, None, None, False, False)
658 nh = hunk(self.desc, self.number, None, None, False, False)
659 nh.number = self.number
659 nh.number = self.number
660 nh.desc = self.desc
660 nh.desc = self.desc
661 nh.hunk = self.hunk
661 nh.hunk = self.hunk
662 nh.a = normalize(self.a)
662 nh.a = normalize(self.a)
663 nh.b = normalize(self.b)
663 nh.b = normalize(self.b)
664 nh.starta = self.starta
664 nh.starta = self.starta
665 nh.startb = self.startb
665 nh.startb = self.startb
666 nh.lena = self.lena
666 nh.lena = self.lena
667 nh.lenb = self.lenb
667 nh.lenb = self.lenb
668 nh.create = self.create
668 nh.create = self.create
669 nh.remove = self.remove
669 nh.remove = self.remove
670 return nh
670 return nh
671
671
672 def read_unified_hunk(self, lr):
672 def read_unified_hunk(self, lr):
673 m = unidesc.match(self.desc)
673 m = unidesc.match(self.desc)
674 if not m:
674 if not m:
675 raise PatchError(_("bad hunk #%d") % self.number)
675 raise PatchError(_("bad hunk #%d") % self.number)
676 self.starta, foo, self.lena, self.startb, foo2, self.lenb = m.groups()
676 self.starta, foo, self.lena, self.startb, foo2, self.lenb = m.groups()
677 if self.lena is None:
677 if self.lena is None:
678 self.lena = 1
678 self.lena = 1
679 else:
679 else:
680 self.lena = int(self.lena)
680 self.lena = int(self.lena)
681 if self.lenb is None:
681 if self.lenb is None:
682 self.lenb = 1
682 self.lenb = 1
683 else:
683 else:
684 self.lenb = int(self.lenb)
684 self.lenb = int(self.lenb)
685 self.starta = int(self.starta)
685 self.starta = int(self.starta)
686 self.startb = int(self.startb)
686 self.startb = int(self.startb)
687 diffhelpers.addlines(lr, self.hunk, self.lena, self.lenb, self.a, self.b)
687 diffhelpers.addlines(lr, self.hunk, self.lena, self.lenb, self.a, self.b)
688 # if we hit eof before finishing out the hunk, the last line will
688 # if we hit eof before finishing out the hunk, the last line will
689 # be zero length. Lets try to fix it up.
689 # be zero length. Lets try to fix it up.
690 while len(self.hunk[-1]) == 0:
690 while len(self.hunk[-1]) == 0:
691 del self.hunk[-1]
691 del self.hunk[-1]
692 del self.a[-1]
692 del self.a[-1]
693 del self.b[-1]
693 del self.b[-1]
694 self.lena -= 1
694 self.lena -= 1
695 self.lenb -= 1
695 self.lenb -= 1
696
696
697 def read_context_hunk(self, lr):
697 def read_context_hunk(self, lr):
698 self.desc = lr.readline()
698 self.desc = lr.readline()
699 m = contextdesc.match(self.desc)
699 m = contextdesc.match(self.desc)
700 if not m:
700 if not m:
701 raise PatchError(_("bad hunk #%d") % self.number)
701 raise PatchError(_("bad hunk #%d") % self.number)
702 foo, self.starta, foo2, aend, foo3 = m.groups()
702 foo, self.starta, foo2, aend, foo3 = m.groups()
703 self.starta = int(self.starta)
703 self.starta = int(self.starta)
704 if aend is None:
704 if aend is None:
705 aend = self.starta
705 aend = self.starta
706 self.lena = int(aend) - self.starta
706 self.lena = int(aend) - self.starta
707 if self.starta:
707 if self.starta:
708 self.lena += 1
708 self.lena += 1
709 for x in xrange(self.lena):
709 for x in xrange(self.lena):
710 l = lr.readline()
710 l = lr.readline()
711 if l.startswith('---'):
711 if l.startswith('---'):
712 lr.push(l)
712 lr.push(l)
713 break
713 break
714 s = l[2:]
714 s = l[2:]
715 if l.startswith('- ') or l.startswith('! '):
715 if l.startswith('- ') or l.startswith('! '):
716 u = '-' + s
716 u = '-' + s
717 elif l.startswith(' '):
717 elif l.startswith(' '):
718 u = ' ' + s
718 u = ' ' + s
719 else:
719 else:
720 raise PatchError(_("bad hunk #%d old text line %d") %
720 raise PatchError(_("bad hunk #%d old text line %d") %
721 (self.number, x))
721 (self.number, x))
722 self.a.append(u)
722 self.a.append(u)
723 self.hunk.append(u)
723 self.hunk.append(u)
724
724
725 l = lr.readline()
725 l = lr.readline()
726 if l.startswith('\ '):
726 if l.startswith('\ '):
727 s = self.a[-1][:-1]
727 s = self.a[-1][:-1]
728 self.a[-1] = s
728 self.a[-1] = s
729 self.hunk[-1] = s
729 self.hunk[-1] = s
730 l = lr.readline()
730 l = lr.readline()
731 m = contextdesc.match(l)
731 m = contextdesc.match(l)
732 if not m:
732 if not m:
733 raise PatchError(_("bad hunk #%d") % self.number)
733 raise PatchError(_("bad hunk #%d") % self.number)
734 foo, self.startb, foo2, bend, foo3 = m.groups()
734 foo, self.startb, foo2, bend, foo3 = m.groups()
735 self.startb = int(self.startb)
735 self.startb = int(self.startb)
736 if bend is None:
736 if bend is None:
737 bend = self.startb
737 bend = self.startb
738 self.lenb = int(bend) - self.startb
738 self.lenb = int(bend) - self.startb
739 if self.startb:
739 if self.startb:
740 self.lenb += 1
740 self.lenb += 1
741 hunki = 1
741 hunki = 1
742 for x in xrange(self.lenb):
742 for x in xrange(self.lenb):
743 l = lr.readline()
743 l = lr.readline()
744 if l.startswith('\ '):
744 if l.startswith('\ '):
745 s = self.b[-1][:-1]
745 s = self.b[-1][:-1]
746 self.b[-1] = s
746 self.b[-1] = s
747 self.hunk[hunki - 1] = s
747 self.hunk[hunki - 1] = s
748 continue
748 continue
749 if not l:
749 if not l:
750 lr.push(l)
750 lr.push(l)
751 break
751 break
752 s = l[2:]
752 s = l[2:]
753 if l.startswith('+ ') or l.startswith('! '):
753 if l.startswith('+ ') or l.startswith('! '):
754 u = '+' + s
754 u = '+' + s
755 elif l.startswith(' '):
755 elif l.startswith(' '):
756 u = ' ' + s
756 u = ' ' + s
757 elif len(self.b) == 0:
757 elif len(self.b) == 0:
758 # this can happen when the hunk does not add any lines
758 # this can happen when the hunk does not add any lines
759 lr.push(l)
759 lr.push(l)
760 break
760 break
761 else:
761 else:
762 raise PatchError(_("bad hunk #%d old text line %d") %
762 raise PatchError(_("bad hunk #%d old text line %d") %
763 (self.number, x))
763 (self.number, x))
764 self.b.append(s)
764 self.b.append(s)
765 while True:
765 while True:
766 if hunki >= len(self.hunk):
766 if hunki >= len(self.hunk):
767 h = ""
767 h = ""
768 else:
768 else:
769 h = self.hunk[hunki]
769 h = self.hunk[hunki]
770 hunki += 1
770 hunki += 1
771 if h == u:
771 if h == u:
772 break
772 break
773 elif h.startswith('-'):
773 elif h.startswith('-'):
774 continue
774 continue
775 else:
775 else:
776 self.hunk.insert(hunki - 1, u)
776 self.hunk.insert(hunki - 1, u)
777 break
777 break
778
778
779 if not self.a:
779 if not self.a:
780 # this happens when lines were only added to the hunk
780 # this happens when lines were only added to the hunk
781 for x in self.hunk:
781 for x in self.hunk:
782 if x.startswith('-') or x.startswith(' '):
782 if x.startswith('-') or x.startswith(' '):
783 self.a.append(x)
783 self.a.append(x)
784 if not self.b:
784 if not self.b:
785 # this happens when lines were only deleted from the hunk
785 # this happens when lines were only deleted from the hunk
786 for x in self.hunk:
786 for x in self.hunk:
787 if x.startswith('+') or x.startswith(' '):
787 if x.startswith('+') or x.startswith(' '):
788 self.b.append(x[1:])
788 self.b.append(x[1:])
789 # @@ -start,len +start,len @@
789 # @@ -start,len +start,len @@
790 self.desc = "@@ -%d,%d +%d,%d @@\n" % (self.starta, self.lena,
790 self.desc = "@@ -%d,%d +%d,%d @@\n" % (self.starta, self.lena,
791 self.startb, self.lenb)
791 self.startb, self.lenb)
792 self.hunk[0] = self.desc
792 self.hunk[0] = self.desc
793
793
794 def fix_newline(self):
794 def fix_newline(self):
795 diffhelpers.fix_newline(self.hunk, self.a, self.b)
795 diffhelpers.fix_newline(self.hunk, self.a, self.b)
796
796
797 def complete(self):
797 def complete(self):
798 return len(self.a) == self.lena and len(self.b) == self.lenb
798 return len(self.a) == self.lena and len(self.b) == self.lenb
799
799
800 def createfile(self):
800 def createfile(self):
801 return self.starta == 0 and self.lena == 0 and self.create
801 return self.starta == 0 and self.lena == 0 and self.create
802
802
803 def rmfile(self):
803 def rmfile(self):
804 return self.startb == 0 and self.lenb == 0 and self.remove
804 return self.startb == 0 and self.lenb == 0 and self.remove
805
805
806 def fuzzit(self, l, fuzz, toponly):
806 def fuzzit(self, l, fuzz, toponly):
807 # this removes context lines from the top and bottom of list 'l'. It
807 # this removes context lines from the top and bottom of list 'l'. It
808 # checks the hunk to make sure only context lines are removed, and then
808 # checks the hunk to make sure only context lines are removed, and then
809 # returns a new shortened list of lines.
809 # returns a new shortened list of lines.
810 fuzz = min(fuzz, len(l)-1)
810 fuzz = min(fuzz, len(l)-1)
811 if fuzz:
811 if fuzz:
812 top = 0
812 top = 0
813 bot = 0
813 bot = 0
814 hlen = len(self.hunk)
814 hlen = len(self.hunk)
815 for x in xrange(hlen - 1):
815 for x in xrange(hlen - 1):
816 # the hunk starts with the @@ line, so use x+1
816 # the hunk starts with the @@ line, so use x+1
817 if self.hunk[x + 1][0] == ' ':
817 if self.hunk[x + 1][0] == ' ':
818 top += 1
818 top += 1
819 else:
819 else:
820 break
820 break
821 if not toponly:
821 if not toponly:
822 for x in xrange(hlen - 1):
822 for x in xrange(hlen - 1):
823 if self.hunk[hlen - bot - 1][0] == ' ':
823 if self.hunk[hlen - bot - 1][0] == ' ':
824 bot += 1
824 bot += 1
825 else:
825 else:
826 break
826 break
827
827
828 # top and bot now count context in the hunk
828 # top and bot now count context in the hunk
829 # adjust them if either one is short
829 # adjust them if either one is short
830 context = max(top, bot, 3)
830 context = max(top, bot, 3)
831 if bot < context:
831 if bot < context:
832 bot = max(0, fuzz - (context - bot))
832 bot = max(0, fuzz - (context - bot))
833 else:
833 else:
834 bot = min(fuzz, bot)
834 bot = min(fuzz, bot)
835 if top < context:
835 if top < context:
836 top = max(0, fuzz - (context - top))
836 top = max(0, fuzz - (context - top))
837 else:
837 else:
838 top = min(fuzz, top)
838 top = min(fuzz, top)
839
839
840 return l[top:len(l)-bot]
840 return l[top:len(l)-bot]
841 return l
841 return l
842
842
843 def old(self, fuzz=0, toponly=False):
843 def old(self, fuzz=0, toponly=False):
844 return self.fuzzit(self.a, fuzz, toponly)
844 return self.fuzzit(self.a, fuzz, toponly)
845
845
846 def new(self, fuzz=0, toponly=False):
846 def new(self, fuzz=0, toponly=False):
847 return self.fuzzit(self.b, fuzz, toponly)
847 return self.fuzzit(self.b, fuzz, toponly)
848
848
849 class binhunk:
849 class binhunk:
850 'A binary patch file. Only understands literals so far.'
850 'A binary patch file. Only understands literals so far.'
851 def __init__(self, gitpatch):
851 def __init__(self, gitpatch):
852 self.gitpatch = gitpatch
852 self.gitpatch = gitpatch
853 self.text = None
853 self.text = None
854 self.hunk = ['GIT binary patch\n']
854 self.hunk = ['GIT binary patch\n']
855
855
856 def createfile(self):
856 def createfile(self):
857 return self.gitpatch.op in ('ADD', 'RENAME', 'COPY')
857 return self.gitpatch.op in ('ADD', 'RENAME', 'COPY')
858
858
859 def rmfile(self):
859 def rmfile(self):
860 return self.gitpatch.op == 'DELETE'
860 return self.gitpatch.op == 'DELETE'
861
861
862 def complete(self):
862 def complete(self):
863 return self.text is not None
863 return self.text is not None
864
864
865 def new(self):
865 def new(self):
866 return [self.text]
866 return [self.text]
867
867
868 def extract(self, lr):
868 def extract(self, lr):
869 line = lr.readline()
869 line = lr.readline()
870 self.hunk.append(line)
870 self.hunk.append(line)
871 while line and not line.startswith('literal '):
871 while line and not line.startswith('literal '):
872 line = lr.readline()
872 line = lr.readline()
873 self.hunk.append(line)
873 self.hunk.append(line)
874 if not line:
874 if not line:
875 raise PatchError(_('could not extract binary patch'))
875 raise PatchError(_('could not extract binary patch'))
876 size = int(line[8:].rstrip())
876 size = int(line[8:].rstrip())
877 dec = []
877 dec = []
878 line = lr.readline()
878 line = lr.readline()
879 self.hunk.append(line)
879 self.hunk.append(line)
880 while len(line) > 1:
880 while len(line) > 1:
881 l = line[0]
881 l = line[0]
882 if l <= 'Z' and l >= 'A':
882 if l <= 'Z' and l >= 'A':
883 l = ord(l) - ord('A') + 1
883 l = ord(l) - ord('A') + 1
884 else:
884 else:
885 l = ord(l) - ord('a') + 27
885 l = ord(l) - ord('a') + 27
886 dec.append(base85.b85decode(line[1:-1])[:l])
886 dec.append(base85.b85decode(line[1:-1])[:l])
887 line = lr.readline()
887 line = lr.readline()
888 self.hunk.append(line)
888 self.hunk.append(line)
889 text = zlib.decompress(''.join(dec))
889 text = zlib.decompress(''.join(dec))
890 if len(text) != size:
890 if len(text) != size:
891 raise PatchError(_('binary patch is %d bytes, not %d') %
891 raise PatchError(_('binary patch is %d bytes, not %d') %
892 len(text), size)
892 len(text), size)
893 self.text = text
893 self.text = text
894
894
895 def parsefilename(str):
895 def parsefilename(str):
896 # --- filename \t|space stuff
896 # --- filename \t|space stuff
897 s = str[4:].rstrip('\r\n')
897 s = str[4:].rstrip('\r\n')
898 i = s.find('\t')
898 i = s.find('\t')
899 if i < 0:
899 if i < 0:
900 i = s.find(' ')
900 i = s.find(' ')
901 if i < 0:
901 if i < 0:
902 return s
902 return s
903 return s[:i]
903 return s[:i]
904
904
905 def selectfile(afile_orig, bfile_orig, hunk, strip):
905 def selectfile(afile_orig, bfile_orig, hunk, strip):
906 def pathstrip(path, count=1):
906 def pathstrip(path, count=1):
907 pathlen = len(path)
907 pathlen = len(path)
908 i = 0
908 i = 0
909 if count == 0:
909 if count == 0:
910 return '', path.rstrip()
910 return '', path.rstrip()
911 while count > 0:
911 while count > 0:
912 i = path.find('/', i)
912 i = path.find('/', i)
913 if i == -1:
913 if i == -1:
914 raise PatchError(_("unable to strip away %d dirs from %s") %
914 raise PatchError(_("unable to strip away %d dirs from %s") %
915 (count, path))
915 (count, path))
916 i += 1
916 i += 1
917 # consume '//' in the path
917 # consume '//' in the path
918 while i < pathlen - 1 and path[i] == '/':
918 while i < pathlen - 1 and path[i] == '/':
919 i += 1
919 i += 1
920 count -= 1
920 count -= 1
921 return path[:i].lstrip(), path[i:].rstrip()
921 return path[:i].lstrip(), path[i:].rstrip()
922
922
923 nulla = afile_orig == "/dev/null"
923 nulla = afile_orig == "/dev/null"
924 nullb = bfile_orig == "/dev/null"
924 nullb = bfile_orig == "/dev/null"
925 abase, afile = pathstrip(afile_orig, strip)
925 abase, afile = pathstrip(afile_orig, strip)
926 gooda = not nulla and util.lexists(afile)
926 gooda = not nulla and util.lexists(afile)
927 bbase, bfile = pathstrip(bfile_orig, strip)
927 bbase, bfile = pathstrip(bfile_orig, strip)
928 if afile == bfile:
928 if afile == bfile:
929 goodb = gooda
929 goodb = gooda
930 else:
930 else:
931 goodb = not nullb and os.path.exists(bfile)
931 goodb = not nullb and os.path.exists(bfile)
932 createfunc = hunk.createfile
932 createfunc = hunk.createfile
933 missing = not goodb and not gooda and not createfunc()
933 missing = not goodb and not gooda and not createfunc()
934
934
935 # some diff programs apparently produce create patches where the
935 # some diff programs apparently produce create patches where the
936 # afile is not /dev/null, but afile starts with bfile
936 # afile is not /dev/null, but afile starts with bfile
937 abasedir = afile[:afile.rfind('/') + 1]
937 abasedir = afile[:afile.rfind('/') + 1]
938 bbasedir = bfile[:bfile.rfind('/') + 1]
938 bbasedir = bfile[:bfile.rfind('/') + 1]
939 if missing and abasedir == bbasedir and afile.startswith(bfile):
939 if missing and abasedir == bbasedir and afile.startswith(bfile):
940 # this isn't very pretty
940 # this isn't very pretty
941 hunk.create = True
941 hunk.create = True
942 if createfunc():
942 if createfunc():
943 missing = False
943 missing = False
944 else:
944 else:
945 hunk.create = False
945 hunk.create = False
946
946
947 # If afile is "a/b/foo" and bfile is "a/b/foo.orig" we assume the
947 # If afile is "a/b/foo" and bfile is "a/b/foo.orig" we assume the
948 # diff is between a file and its backup. In this case, the original
948 # diff is between a file and its backup. In this case, the original
949 # file should be patched (see original mpatch code).
949 # file should be patched (see original mpatch code).
950 isbackup = (abase == bbase and bfile.startswith(afile))
950 isbackup = (abase == bbase and bfile.startswith(afile))
951 fname = None
951 fname = None
952 if not missing:
952 if not missing:
953 if gooda and goodb:
953 if gooda and goodb:
954 fname = isbackup and afile or bfile
954 fname = isbackup and afile or bfile
955 elif gooda:
955 elif gooda:
956 fname = afile
956 fname = afile
957
957
958 if not fname:
958 if not fname:
959 if not nullb:
959 if not nullb:
960 fname = isbackup and afile or bfile
960 fname = isbackup and afile or bfile
961 elif not nulla:
961 elif not nulla:
962 fname = afile
962 fname = afile
963 else:
963 else:
964 raise PatchError(_("undefined source and destination files"))
964 raise PatchError(_("undefined source and destination files"))
965
965
966 return fname, missing
966 return fname, missing
967
967
968 def scangitpatch(lr, firstline):
968 def scangitpatch(lr, firstline):
969 """
969 """
970 Git patches can emit:
970 Git patches can emit:
971 - rename a to b
971 - rename a to b
972 - change b
972 - change b
973 - copy a to c
973 - copy a to c
974 - change c
974 - change c
975
975
976 We cannot apply this sequence as-is, the renamed 'a' could not be
976 We cannot apply this sequence as-is, the renamed 'a' could not be
977 found for it would have been renamed already. And we cannot copy
977 found for it would have been renamed already. And we cannot copy
978 from 'b' instead because 'b' would have been changed already. So
978 from 'b' instead because 'b' would have been changed already. So
979 we scan the git patch for copy and rename commands so we can
979 we scan the git patch for copy and rename commands so we can
980 perform the copies ahead of time.
980 perform the copies ahead of time.
981 """
981 """
982 pos = 0
982 pos = 0
983 try:
983 try:
984 pos = lr.fp.tell()
984 pos = lr.fp.tell()
985 fp = lr.fp
985 fp = lr.fp
986 except IOError:
986 except IOError:
987 fp = cStringIO.StringIO(lr.fp.read())
987 fp = cStringIO.StringIO(lr.fp.read())
988 gitlr = linereader(fp, lr.textmode)
988 gitlr = linereader(fp, lr.textmode)
989 gitlr.push(firstline)
989 gitlr.push(firstline)
990 (dopatch, gitpatches) = readgitpatch(gitlr)
990 (dopatch, gitpatches) = readgitpatch(gitlr)
991 fp.seek(pos)
991 fp.seek(pos)
992 return dopatch, gitpatches
992 return dopatch, gitpatches
993
993
994 def iterhunks(ui, fp, sourcefile=None):
994 def iterhunks(ui, fp, sourcefile=None):
995 """Read a patch and yield the following events:
995 """Read a patch and yield the following events:
996 - ("file", afile, bfile, firsthunk): select a new target file.
996 - ("file", afile, bfile, firsthunk): select a new target file.
997 - ("hunk", hunk): a new hunk is ready to be applied, follows a
997 - ("hunk", hunk): a new hunk is ready to be applied, follows a
998 "file" event.
998 "file" event.
999 - ("git", gitchanges): current diff is in git format, gitchanges
999 - ("git", gitchanges): current diff is in git format, gitchanges
1000 maps filenames to gitpatch records. Unique event.
1000 maps filenames to gitpatch records. Unique event.
1001 """
1001 """
1002 changed = {}
1002 changed = {}
1003 current_hunk = None
1003 current_hunk = None
1004 afile = ""
1004 afile = ""
1005 bfile = ""
1005 bfile = ""
1006 state = None
1006 state = None
1007 hunknum = 0
1007 hunknum = 0
1008 emitfile = False
1008 emitfile = False
1009 git = False
1009 git = False
1010
1010
1011 # our states
1011 # our states
1012 BFILE = 1
1012 BFILE = 1
1013 context = None
1013 context = None
1014 lr = linereader(fp)
1014 lr = linereader(fp)
1015 # gitworkdone is True if a git operation (copy, rename, ...) was
1015 # gitworkdone is True if a git operation (copy, rename, ...) was
1016 # performed already for the current file. Useful when the file
1016 # performed already for the current file. Useful when the file
1017 # section may have no hunk.
1017 # section may have no hunk.
1018 gitworkdone = False
1018 gitworkdone = False
1019 empty = None
1019 empty = None
1020
1020
1021 while True:
1021 while True:
1022 newfile = newgitfile = False
1022 newfile = newgitfile = False
1023 x = lr.readline()
1023 x = lr.readline()
1024 if not x:
1024 if not x:
1025 break
1025 break
1026 if current_hunk:
1026 if current_hunk:
1027 if x.startswith('\ '):
1027 if x.startswith('\ '):
1028 current_hunk.fix_newline()
1028 current_hunk.fix_newline()
1029 yield 'hunk', current_hunk
1029 yield 'hunk', current_hunk
1030 current_hunk = None
1030 current_hunk = None
1031 empty = False
1031 empty = False
1032 if ((sourcefile or state == BFILE) and ((not context and x[0] == '@') or
1032 if ((sourcefile or state == BFILE) and ((not context and x[0] == '@') or
1033 ((context is not False) and x.startswith('***************')))):
1033 ((context is not False) and x.startswith('***************')))):
1034 try:
1034 try:
1035 if context is None and x.startswith('***************'):
1035 if context is None and x.startswith('***************'):
1036 context = True
1036 context = True
1037 gpatch = changed.get(bfile)
1037 gpatch = changed.get(bfile)
1038 create = afile == '/dev/null' or gpatch and gpatch.op == 'ADD'
1038 create = afile == '/dev/null' or gpatch and gpatch.op == 'ADD'
1039 remove = bfile == '/dev/null' or gpatch and gpatch.op == 'DELETE'
1039 remove = bfile == '/dev/null' or gpatch and gpatch.op == 'DELETE'
1040 current_hunk = hunk(x, hunknum + 1, lr, context, create, remove)
1040 current_hunk = hunk(x, hunknum + 1, lr, context, create, remove)
1041 except PatchError, err:
1041 except PatchError, err:
1042 ui.debug(err)
1042 ui.debug(err)
1043 current_hunk = None
1043 current_hunk = None
1044 continue
1044 continue
1045 hunknum += 1
1045 hunknum += 1
1046 if emitfile:
1046 if emitfile:
1047 emitfile = False
1047 emitfile = False
1048 yield 'file', (afile, bfile, current_hunk)
1048 yield 'file', (afile, bfile, current_hunk)
1049 empty = False
1049 empty = False
1050 elif state == BFILE and x.startswith('GIT binary patch'):
1050 elif state == BFILE and x.startswith('GIT binary patch'):
1051 current_hunk = binhunk(changed[bfile])
1051 current_hunk = binhunk(changed[bfile])
1052 hunknum += 1
1052 hunknum += 1
1053 if emitfile:
1053 if emitfile:
1054 emitfile = False
1054 emitfile = False
1055 yield 'file', ('a/' + afile, 'b/' + bfile, current_hunk)
1055 yield 'file', ('a/' + afile, 'b/' + bfile, current_hunk)
1056 empty = False
1056 empty = False
1057 current_hunk.extract(lr)
1057 current_hunk.extract(lr)
1058 elif x.startswith('diff --git'):
1058 elif x.startswith('diff --git'):
1059 # check for git diff, scanning the whole patch file if needed
1059 # check for git diff, scanning the whole patch file if needed
1060 m = gitre.match(x)
1060 m = gitre.match(x)
1061 gitworkdone = False
1061 gitworkdone = False
1062 if m:
1062 if m:
1063 afile, bfile = m.group(1, 2)
1063 afile, bfile = m.group(1, 2)
1064 if not git:
1064 if not git:
1065 git = True
1065 git = True
1066 gitpatches = scangitpatch(lr, x)[1]
1066 gitpatches = scangitpatch(lr, x)[1]
1067 yield 'git', gitpatches
1067 yield 'git', gitpatches
1068 for gp in gitpatches:
1068 for gp in gitpatches:
1069 changed[gp.path] = gp
1069 changed[gp.path] = gp
1070 # else error?
1070 # else error?
1071 # copy/rename + modify should modify target, not source
1071 # copy/rename + modify should modify target, not source
1072 gp = changed.get(bfile)
1072 gp = changed.get(bfile)
1073 if gp and (gp.op in ('COPY', 'DELETE', 'RENAME', 'ADD')
1073 if gp and (gp.op in ('COPY', 'DELETE', 'RENAME', 'ADD')
1074 or gp.mode):
1074 or gp.mode):
1075 afile = bfile
1075 afile = bfile
1076 gitworkdone = True
1076 gitworkdone = True
1077 newgitfile = True
1077 newgitfile = True
1078 elif x.startswith('---'):
1078 elif x.startswith('---'):
1079 # check for a unified diff
1079 # check for a unified diff
1080 l2 = lr.readline()
1080 l2 = lr.readline()
1081 if not l2.startswith('+++'):
1081 if not l2.startswith('+++'):
1082 lr.push(l2)
1082 lr.push(l2)
1083 continue
1083 continue
1084 newfile = True
1084 newfile = True
1085 context = False
1085 context = False
1086 afile = parsefilename(x)
1086 afile = parsefilename(x)
1087 bfile = parsefilename(l2)
1087 bfile = parsefilename(l2)
1088 elif x.startswith('***'):
1088 elif x.startswith('***'):
1089 # check for a context diff
1089 # check for a context diff
1090 l2 = lr.readline()
1090 l2 = lr.readline()
1091 if not l2.startswith('---'):
1091 if not l2.startswith('---'):
1092 lr.push(l2)
1092 lr.push(l2)
1093 continue
1093 continue
1094 l3 = lr.readline()
1094 l3 = lr.readline()
1095 lr.push(l3)
1095 lr.push(l3)
1096 if not l3.startswith("***************"):
1096 if not l3.startswith("***************"):
1097 lr.push(l2)
1097 lr.push(l2)
1098 continue
1098 continue
1099 newfile = True
1099 newfile = True
1100 context = True
1100 context = True
1101 afile = parsefilename(x)
1101 afile = parsefilename(x)
1102 bfile = parsefilename(l2)
1102 bfile = parsefilename(l2)
1103
1103
1104 if newfile:
1104 if newfile:
1105 if empty:
1105 if empty:
1106 raise NoHunks
1106 raise NoHunks
1107 empty = not gitworkdone
1107 empty = not gitworkdone
1108 gitworkdone = False
1108 gitworkdone = False
1109
1109
1110 if newgitfile or newfile:
1110 if newgitfile or newfile:
1111 emitfile = True
1111 emitfile = True
1112 state = BFILE
1112 state = BFILE
1113 hunknum = 0
1113 hunknum = 0
1114 if current_hunk:
1114 if current_hunk:
1115 if current_hunk.complete():
1115 if current_hunk.complete():
1116 yield 'hunk', current_hunk
1116 yield 'hunk', current_hunk
1117 empty = False
1117 empty = False
1118 else:
1118 else:
1119 raise PatchError(_("malformed patch %s %s") % (afile,
1119 raise PatchError(_("malformed patch %s %s") % (afile,
1120 current_hunk.desc))
1120 current_hunk.desc))
1121
1121
1122 if (empty is None and not gitworkdone) or empty:
1122 if (empty is None and not gitworkdone) or empty:
1123 raise NoHunks
1123 raise NoHunks
1124
1124
1125 def applydiff(ui, fp, changed, strip=1, sourcefile=None, eolmode='strict'):
1125 def applydiff(ui, fp, changed, strip=1, sourcefile=None, eolmode='strict'):
1126 """
1126 """
1127 Reads a patch from fp and tries to apply it.
1127 Reads a patch from fp and tries to apply it.
1128
1128
1129 The dict 'changed' is filled in with all of the filenames changed
1129 The dict 'changed' is filled in with all of the filenames changed
1130 by the patch. Returns 0 for a clean patch, -1 if any rejects were
1130 by the patch. Returns 0 for a clean patch, -1 if any rejects were
1131 found and 1 if there was any fuzz.
1131 found and 1 if there was any fuzz.
1132
1132
1133 If 'eolmode' is 'strict', the patch content and patched file are
1133 If 'eolmode' is 'strict', the patch content and patched file are
1134 read in binary mode. Otherwise, line endings are ignored when
1134 read in binary mode. Otherwise, line endings are ignored when
1135 patching then normalized according to 'eolmode'.
1135 patching then normalized according to 'eolmode'.
1136 """
1136 """
1137 rejects = 0
1137 rejects = 0
1138 err = 0
1138 err = 0
1139 current_file = None
1139 current_file = None
1140 gitpatches = None
1140 gitpatches = None
1141 opener = util.opener(os.getcwd())
1141 opener = util.opener(os.getcwd())
1142
1142
1143 def closefile():
1143 def closefile():
1144 if not current_file:
1144 if not current_file:
1145 return 0
1145 return 0
1146 current_file.close()
1146 current_file.close()
1147 return len(current_file.rej)
1147 return len(current_file.rej)
1148
1148
1149 for state, values in iterhunks(ui, fp, sourcefile):
1149 for state, values in iterhunks(ui, fp, sourcefile):
1150 if state == 'hunk':
1150 if state == 'hunk':
1151 if not current_file:
1151 if not current_file:
1152 continue
1152 continue
1153 current_hunk = values
1153 current_hunk = values
1154 ret = current_file.apply(current_hunk)
1154 ret = current_file.apply(current_hunk)
1155 if ret >= 0:
1155 if ret >= 0:
1156 changed.setdefault(current_file.fname, None)
1156 changed.setdefault(current_file.fname, None)
1157 if ret > 0:
1157 if ret > 0:
1158 err = 1
1158 err = 1
1159 elif state == 'file':
1159 elif state == 'file':
1160 rejects += closefile()
1160 rejects += closefile()
1161 afile, bfile, first_hunk = values
1161 afile, bfile, first_hunk = values
1162 try:
1162 try:
1163 if sourcefile:
1163 if sourcefile:
1164 current_file = patchfile(ui, sourcefile, opener,
1164 current_file = patchfile(ui, sourcefile, opener,
1165 eolmode=eolmode)
1165 eolmode=eolmode)
1166 else:
1166 else:
1167 current_file, missing = selectfile(afile, bfile,
1167 current_file, missing = selectfile(afile, bfile,
1168 first_hunk, strip)
1168 first_hunk, strip)
1169 current_file = patchfile(ui, current_file, opener,
1169 current_file = patchfile(ui, current_file, opener,
1170 missing, eolmode)
1170 missing, eolmode)
1171 except PatchError, err:
1171 except PatchError, err:
1172 ui.warn(str(err) + '\n')
1172 ui.warn(str(err) + '\n')
1173 current_file, current_hunk = None, None
1173 current_file, current_hunk = None, None
1174 rejects += 1
1174 rejects += 1
1175 continue
1175 continue
1176 elif state == 'git':
1176 elif state == 'git':
1177 gitpatches = values
1177 gitpatches = values
1178 cwd = os.getcwd()
1178 cwd = os.getcwd()
1179 for gp in gitpatches:
1179 for gp in gitpatches:
1180 if gp.op in ('COPY', 'RENAME'):
1180 if gp.op in ('COPY', 'RENAME'):
1181 copyfile(gp.oldpath, gp.path, cwd)
1181 copyfile(gp.oldpath, gp.path, cwd)
1182 changed[gp.path] = gp
1182 changed[gp.path] = gp
1183 else:
1183 else:
1184 raise util.Abort(_('unsupported parser state: %s') % state)
1184 raise util.Abort(_('unsupported parser state: %s') % state)
1185
1185
1186 rejects += closefile()
1186 rejects += closefile()
1187
1187
1188 if rejects:
1188 if rejects:
1189 return -1
1189 return -1
1190 return err
1190 return err
1191
1191
1192 def updatedir(ui, repo, patches, similarity=0):
1192 def updatedir(ui, repo, patches, similarity=0):
1193 '''Update dirstate after patch application according to metadata'''
1193 '''Update dirstate after patch application according to metadata'''
1194 if not patches:
1194 if not patches:
1195 return
1195 return
1196 copies = []
1196 copies = []
1197 removes = set()
1197 removes = set()
1198 cfiles = patches.keys()
1198 cfiles = patches.keys()
1199 cwd = repo.getcwd()
1199 cwd = repo.getcwd()
1200 if cwd:
1200 if cwd:
1201 cfiles = [util.pathto(repo.root, cwd, f) for f in patches.keys()]
1201 cfiles = [util.pathto(repo.root, cwd, f) for f in patches.keys()]
1202 for f in patches:
1202 for f in patches:
1203 gp = patches[f]
1203 gp = patches[f]
1204 if not gp:
1204 if not gp:
1205 continue
1205 continue
1206 if gp.op == 'RENAME':
1206 if gp.op == 'RENAME':
1207 copies.append((gp.oldpath, gp.path))
1207 copies.append((gp.oldpath, gp.path))
1208 removes.add(gp.oldpath)
1208 removes.add(gp.oldpath)
1209 elif gp.op == 'COPY':
1209 elif gp.op == 'COPY':
1210 copies.append((gp.oldpath, gp.path))
1210 copies.append((gp.oldpath, gp.path))
1211 elif gp.op == 'DELETE':
1211 elif gp.op == 'DELETE':
1212 removes.add(gp.path)
1212 removes.add(gp.path)
1213 for src, dst in copies:
1213 for src, dst in copies:
1214 repo.copy(src, dst)
1214 repo.copy(src, dst)
1215 if (not similarity) and removes:
1215 if (not similarity) and removes:
1216 repo.remove(sorted(removes), True)
1216 repo.remove(sorted(removes), True)
1217 for f in patches:
1217 for f in patches:
1218 gp = patches[f]
1218 gp = patches[f]
1219 if gp and gp.mode:
1219 if gp and gp.mode:
1220 islink, isexec = gp.mode
1220 islink, isexec = gp.mode
1221 dst = repo.wjoin(gp.path)
1221 dst = repo.wjoin(gp.path)
1222 # patch won't create empty files
1222 # patch won't create empty files
1223 if gp.op == 'ADD' and not os.path.exists(dst):
1223 if gp.op == 'ADD' and not os.path.exists(dst):
1224 flags = (isexec and 'x' or '') + (islink and 'l' or '')
1224 flags = (isexec and 'x' or '') + (islink and 'l' or '')
1225 repo.wwrite(gp.path, '', flags)
1225 repo.wwrite(gp.path, '', flags)
1226 elif gp.op != 'DELETE':
1226 elif gp.op != 'DELETE':
1227 util.set_flags(dst, islink, isexec)
1227 util.set_flags(dst, islink, isexec)
1228 cmdutil.addremove(repo, cfiles, similarity=similarity)
1228 cmdutil.addremove(repo, cfiles, similarity=similarity)
1229 files = patches.keys()
1229 files = patches.keys()
1230 files.extend([r for r in removes if r not in files])
1230 files.extend([r for r in removes if r not in files])
1231 return sorted(files)
1231 return sorted(files)
1232
1232
1233 def externalpatch(patcher, args, patchname, ui, strip, cwd, files):
1233 def externalpatch(patcher, args, patchname, ui, strip, cwd, files):
1234 """use <patcher> to apply <patchname> to the working directory.
1234 """use <patcher> to apply <patchname> to the working directory.
1235 returns whether patch was applied with fuzz factor."""
1235 returns whether patch was applied with fuzz factor."""
1236
1236
1237 fuzz = False
1237 fuzz = False
1238 if cwd:
1238 if cwd:
1239 args.append('-d %s' % util.shellquote(cwd))
1239 args.append('-d %s' % util.shellquote(cwd))
1240 fp = util.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
1240 fp = util.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
1241 util.shellquote(patchname)))
1241 util.shellquote(patchname)))
1242
1242
1243 for line in fp:
1243 for line in fp:
1244 line = line.rstrip()
1244 line = line.rstrip()
1245 ui.note(line + '\n')
1245 ui.note(line + '\n')
1246 if line.startswith('patching file '):
1246 if line.startswith('patching file '):
1247 pf = util.parse_patch_output(line)
1247 pf = util.parse_patch_output(line)
1248 printed_file = False
1248 printed_file = False
1249 files.setdefault(pf, None)
1249 files.setdefault(pf, None)
1250 elif line.find('with fuzz') >= 0:
1250 elif line.find('with fuzz') >= 0:
1251 fuzz = True
1251 fuzz = True
1252 if not printed_file:
1252 if not printed_file:
1253 ui.warn(pf + '\n')
1253 ui.warn(pf + '\n')
1254 printed_file = True
1254 printed_file = True
1255 ui.warn(line + '\n')
1255 ui.warn(line + '\n')
1256 elif line.find('saving rejects to file') >= 0:
1256 elif line.find('saving rejects to file') >= 0:
1257 ui.warn(line + '\n')
1257 ui.warn(line + '\n')
1258 elif line.find('FAILED') >= 0:
1258 elif line.find('FAILED') >= 0:
1259 if not printed_file:
1259 if not printed_file:
1260 ui.warn(pf + '\n')
1260 ui.warn(pf + '\n')
1261 printed_file = True
1261 printed_file = True
1262 ui.warn(line + '\n')
1262 ui.warn(line + '\n')
1263 code = fp.close()
1263 code = fp.close()
1264 if code:
1264 if code:
1265 raise PatchError(_("patch command failed: %s") %
1265 raise PatchError(_("patch command failed: %s") %
1266 util.explain_exit(code)[0])
1266 util.explain_exit(code)[0])
1267 return fuzz
1267 return fuzz
1268
1268
1269 def internalpatch(patchobj, ui, strip, cwd, files=None, eolmode='strict'):
1269 def internalpatch(patchobj, ui, strip, cwd, files=None, eolmode='strict'):
1270 """use builtin patch to apply <patchobj> to the working directory.
1270 """use builtin patch to apply <patchobj> to the working directory.
1271 returns whether patch was applied with fuzz factor."""
1271 returns whether patch was applied with fuzz factor."""
1272
1272
1273 if files is None:
1273 if files is None:
1274 files = {}
1274 files = {}
1275 if eolmode is None:
1275 if eolmode is None:
1276 eolmode = ui.config('patch', 'eol', 'strict')
1276 eolmode = ui.config('patch', 'eol', 'strict')
1277 if eolmode.lower() not in eolmodes:
1277 if eolmode.lower() not in eolmodes:
1278 raise util.Abort(_('Unsupported line endings type: %s') % eolmode)
1278 raise util.Abort(_('Unsupported line endings type: %s') % eolmode)
1279 eolmode = eolmode.lower()
1279 eolmode = eolmode.lower()
1280
1280
1281 try:
1281 try:
1282 fp = open(patchobj, 'rb')
1282 fp = open(patchobj, 'rb')
1283 except TypeError:
1283 except TypeError:
1284 fp = patchobj
1284 fp = patchobj
1285 if cwd:
1285 if cwd:
1286 curdir = os.getcwd()
1286 curdir = os.getcwd()
1287 os.chdir(cwd)
1287 os.chdir(cwd)
1288 try:
1288 try:
1289 ret = applydiff(ui, fp, files, strip=strip, eolmode=eolmode)
1289 ret = applydiff(ui, fp, files, strip=strip, eolmode=eolmode)
1290 finally:
1290 finally:
1291 if cwd:
1291 if cwd:
1292 os.chdir(curdir)
1292 os.chdir(curdir)
1293 if fp != patchobj:
1293 if fp != patchobj:
1294 fp.close()
1294 fp.close()
1295 if ret < 0:
1295 if ret < 0:
1296 raise PatchError
1296 raise PatchError
1297 return ret > 0
1297 return ret > 0
1298
1298
1299 def patch(patchname, ui, strip=1, cwd=None, files=None, eolmode='strict'):
1299 def patch(patchname, ui, strip=1, cwd=None, files=None, eolmode='strict'):
1300 """Apply <patchname> to the working directory.
1300 """Apply <patchname> to the working directory.
1301
1301
1302 'eolmode' specifies how end of lines should be handled. It can be:
1302 'eolmode' specifies how end of lines should be handled. It can be:
1303 - 'strict': inputs are read in binary mode, EOLs are preserved
1303 - 'strict': inputs are read in binary mode, EOLs are preserved
1304 - 'crlf': EOLs are ignored when patching and reset to CRLF
1304 - 'crlf': EOLs are ignored when patching and reset to CRLF
1305 - 'lf': EOLs are ignored when patching and reset to LF
1305 - 'lf': EOLs are ignored when patching and reset to LF
1306 - None: get it from user settings, default to 'strict'
1306 - None: get it from user settings, default to 'strict'
1307 'eolmode' is ignored when using an external patcher program.
1307 'eolmode' is ignored when using an external patcher program.
1308
1308
1309 Returns whether patch was applied with fuzz factor.
1309 Returns whether patch was applied with fuzz factor.
1310 """
1310 """
1311 patcher = ui.config('ui', 'patch')
1311 patcher = ui.config('ui', 'patch')
1312 args = []
1312 args = []
1313 if files is None:
1313 if files is None:
1314 files = {}
1314 files = {}
1315 try:
1315 try:
1316 if patcher:
1316 if patcher:
1317 return externalpatch(patcher, args, patchname, ui, strip, cwd,
1317 return externalpatch(patcher, args, patchname, ui, strip, cwd,
1318 files)
1318 files)
1319 else:
1319 else:
1320 try:
1320 try:
1321 return internalpatch(patchname, ui, strip, cwd, files, eolmode)
1321 return internalpatch(patchname, ui, strip, cwd, files, eolmode)
1322 except NoHunks:
1322 except NoHunks:
1323 ui.warn(_('internal patcher failed\n'
1323 ui.warn(_('internal patcher failed\n'
1324 'please report details to '
1324 'please report details to '
1325 'http://mercurial.selenic.com/bts/\n'
1325 'http://mercurial.selenic.com/bts/\n'
1326 'or mercurial@selenic.com\n'))
1326 'or mercurial@selenic.com\n'))
1327 patcher = (util.find_exe('gpatch') or util.find_exe('patch')
1327 patcher = (util.find_exe('gpatch') or util.find_exe('patch')
1328 or 'patch')
1328 or 'patch')
1329 ui.debug('no valid hunks found; trying with %r instead\n' %
1329 ui.debug('no valid hunks found; trying with %r instead\n' %
1330 patcher)
1330 patcher)
1331 if util.needbinarypatch():
1331 if util.needbinarypatch():
1332 args.append('--binary')
1332 args.append('--binary')
1333 return externalpatch(patcher, args, patchname, ui, strip, cwd,
1333 return externalpatch(patcher, args, patchname, ui, strip, cwd,
1334 files)
1334 files)
1335 except PatchError, err:
1335 except PatchError, err:
1336 s = str(err)
1336 s = str(err)
1337 if s:
1337 if s:
1338 raise util.Abort(s)
1338 raise util.Abort(s)
1339 else:
1339 else:
1340 raise util.Abort(_('patch failed to apply'))
1340 raise util.Abort(_('patch failed to apply'))
1341
1341
1342 def b85diff(to, tn):
1342 def b85diff(to, tn):
1343 '''print base85-encoded binary diff'''
1343 '''print base85-encoded binary diff'''
1344 def gitindex(text):
1344 def gitindex(text):
1345 if not text:
1345 if not text:
1346 return '0' * 40
1346 return '0' * 40
1347 l = len(text)
1347 l = len(text)
1348 s = util.sha1('blob %d\0' % l)
1348 s = util.sha1('blob %d\0' % l)
1349 s.update(text)
1349 s.update(text)
1350 return s.hexdigest()
1350 return s.hexdigest()
1351
1351
1352 def fmtline(line):
1352 def fmtline(line):
1353 l = len(line)
1353 l = len(line)
1354 if l <= 26:
1354 if l <= 26:
1355 l = chr(ord('A') + l - 1)
1355 l = chr(ord('A') + l - 1)
1356 else:
1356 else:
1357 l = chr(l - 26 + ord('a') - 1)
1357 l = chr(l - 26 + ord('a') - 1)
1358 return '%c%s\n' % (l, base85.b85encode(line, True))
1358 return '%c%s\n' % (l, base85.b85encode(line, True))
1359
1359
1360 def chunk(text, csize=52):
1360 def chunk(text, csize=52):
1361 l = len(text)
1361 l = len(text)
1362 i = 0
1362 i = 0
1363 while i < l:
1363 while i < l:
1364 yield text[i:i + csize]
1364 yield text[i:i + csize]
1365 i += csize
1365 i += csize
1366
1366
1367 tohash = gitindex(to)
1367 tohash = gitindex(to)
1368 tnhash = gitindex(tn)
1368 tnhash = gitindex(tn)
1369 if tohash == tnhash:
1369 if tohash == tnhash:
1370 return ""
1370 return ""
1371
1371
1372 # TODO: deltas
1372 # TODO: deltas
1373 ret = ['index %s..%s\nGIT binary patch\nliteral %s\n' %
1373 ret = ['index %s..%s\nGIT binary patch\nliteral %s\n' %
1374 (tohash, tnhash, len(tn))]
1374 (tohash, tnhash, len(tn))]
1375 for l in chunk(zlib.compress(tn)):
1375 for l in chunk(zlib.compress(tn)):
1376 ret.append(fmtline(l))
1376 ret.append(fmtline(l))
1377 ret.append('\n')
1377 ret.append('\n')
1378 return ''.join(ret)
1378 return ''.join(ret)
1379
1379
1380 class GitDiffRequired(Exception):
1380 class GitDiffRequired(Exception):
1381 pass
1381 pass
1382
1382
1383 def diffopts(ui, opts=None, untrusted=False):
1383 def diffopts(ui, opts=None, untrusted=False):
1384 def get(key, name=None, getter=ui.configbool):
1384 def get(key, name=None, getter=ui.configbool):
1385 return ((opts and opts.get(key)) or
1385 return ((opts and opts.get(key)) or
1386 getter('diff', name or key, None, untrusted=untrusted))
1386 getter('diff', name or key, None, untrusted=untrusted))
1387 return mdiff.diffopts(
1387 return mdiff.diffopts(
1388 text=opts and opts.get('text'),
1388 text=opts and opts.get('text'),
1389 git=get('git'),
1389 git=get('git'),
1390 nodates=get('nodates'),
1390 nodates=get('nodates'),
1391 showfunc=get('show_function', 'showfunc'),
1391 showfunc=get('show_function', 'showfunc'),
1392 ignorews=get('ignore_all_space', 'ignorews'),
1392 ignorews=get('ignore_all_space', 'ignorews'),
1393 ignorewsamount=get('ignore_space_change', 'ignorewsamount'),
1393 ignorewsamount=get('ignore_space_change', 'ignorewsamount'),
1394 ignoreblanklines=get('ignore_blank_lines', 'ignoreblanklines'),
1394 ignoreblanklines=get('ignore_blank_lines', 'ignoreblanklines'),
1395 context=get('unified', getter=ui.config))
1395 context=get('unified', getter=ui.config))
1396
1396
1397 def diff(repo, node1=None, node2=None, match=None, changes=None, opts=None,
1397 def diff(repo, node1=None, node2=None, match=None, changes=None, opts=None,
1398 losedatafn=None):
1398 losedatafn=None):
1399 '''yields diff of changes to files between two nodes, or node and
1399 '''yields diff of changes to files between two nodes, or node and
1400 working directory.
1400 working directory.
1401
1401
1402 if node1 is None, use first dirstate parent instead.
1402 if node1 is None, use first dirstate parent instead.
1403 if node2 is None, compare node1 with working directory.
1403 if node2 is None, compare node1 with working directory.
1404
1404
1405 losedatafn(**kwarg) is a callable run when opts.upgrade=True and
1405 losedatafn(**kwarg) is a callable run when opts.upgrade=True and
1406 every time some change cannot be represented with the current
1406 every time some change cannot be represented with the current
1407 patch format. Return False to upgrade to git patch format, True to
1407 patch format. Return False to upgrade to git patch format, True to
1408 accept the loss or raise an exception to abort the diff. It is
1408 accept the loss or raise an exception to abort the diff. It is
1409 called with the name of current file being diffed as 'fn'. If set
1409 called with the name of current file being diffed as 'fn'. If set
1410 to None, patches will always be upgraded to git format when
1410 to None, patches will always be upgraded to git format when
1411 necessary.
1411 necessary.
1412 '''
1412 '''
1413
1413
1414 if opts is None:
1414 if opts is None:
1415 opts = mdiff.defaultopts
1415 opts = mdiff.defaultopts
1416
1416
1417 if not node1 and not node2:
1417 if not node1 and not node2:
1418 node1 = repo.dirstate.parents()[0]
1418 node1 = repo.dirstate.parents()[0]
1419
1419
1420 def lrugetfilectx():
1420 def lrugetfilectx():
1421 cache = {}
1421 cache = {}
1422 order = []
1422 order = []
1423 def getfilectx(f, ctx):
1423 def getfilectx(f, ctx):
1424 fctx = ctx.filectx(f, filelog=cache.get(f))
1424 fctx = ctx.filectx(f, filelog=cache.get(f))
1425 if f not in cache:
1425 if f not in cache:
1426 if len(cache) > 20:
1426 if len(cache) > 20:
1427 del cache[order.pop(0)]
1427 del cache[order.pop(0)]
1428 cache[f] = fctx.filelog()
1428 cache[f] = fctx.filelog()
1429 else:
1429 else:
1430 order.remove(f)
1430 order.remove(f)
1431 order.append(f)
1431 order.append(f)
1432 return fctx
1432 return fctx
1433 return getfilectx
1433 return getfilectx
1434 getfilectx = lrugetfilectx()
1434 getfilectx = lrugetfilectx()
1435
1435
1436 ctx1 = repo[node1]
1436 ctx1 = repo[node1]
1437 ctx2 = repo[node2]
1437 ctx2 = repo[node2]
1438
1438
1439 if not changes:
1439 if not changes:
1440 changes = repo.status(ctx1, ctx2, match=match)
1440 changes = repo.status(ctx1, ctx2, match=match)
1441 modified, added, removed = changes[:3]
1441 modified, added, removed = changes[:3]
1442
1442
1443 if not modified and not added and not removed:
1443 if not modified and not added and not removed:
1444 return []
1444 return []
1445
1445
1446 revs = None
1446 revs = None
1447 if not repo.ui.quiet:
1447 if not repo.ui.quiet:
1448 hexfunc = repo.ui.debugflag and hex or short
1448 hexfunc = repo.ui.debugflag and hex or short
1449 revs = [hexfunc(node) for node in [node1, node2] if node]
1449 revs = [hexfunc(node) for node in [node1, node2] if node]
1450
1450
1451 copy = {}
1451 copy = {}
1452 if opts.git or opts.upgrade:
1452 if opts.git or opts.upgrade:
1453 copy = copies.copies(repo, ctx1, ctx2, repo[nullid])[0]
1453 copy = copies.copies(repo, ctx1, ctx2, repo[nullid])[0]
1454
1454
1455 difffn = lambda opts, losedata: trydiff(repo, revs, ctx1, ctx2,
1455 difffn = lambda opts, losedata: trydiff(repo, revs, ctx1, ctx2,
1456 modified, added, removed, copy, getfilectx, opts, losedata)
1456 modified, added, removed, copy, getfilectx, opts, losedata)
1457 if opts.upgrade and not opts.git:
1457 if opts.upgrade and not opts.git:
1458 try:
1458 try:
1459 def losedata(fn):
1459 def losedata(fn):
1460 if not losedatafn or not losedatafn(fn=fn):
1460 if not losedatafn or not losedatafn(fn=fn):
1461 raise GitDiffRequired()
1461 raise GitDiffRequired()
1462 # Buffer the whole output until we are sure it can be generated
1462 # Buffer the whole output until we are sure it can be generated
1463 return list(difffn(opts.copy(git=False), losedata))
1463 return list(difffn(opts.copy(git=False), losedata))
1464 except GitDiffRequired:
1464 except GitDiffRequired:
1465 return difffn(opts.copy(git=True), None)
1465 return difffn(opts.copy(git=True), None)
1466 else:
1466 else:
1467 return difffn(opts, None)
1467 return difffn(opts, None)
1468
1468
1469 def difflabel(func, *args, **kw):
1470 '''yields 2-tuples of (output, label) based on the output of func()'''
1471 prefixes = [('diff', 'diff.diffline'),
1472 ('copy', 'diff.extended'),
1473 ('rename', 'diff.extended'),
1474 ('old', 'diff.extended'),
1475 ('new', 'diff.extended'),
1476 ('deleted', 'diff.extended'),
1477 ('---', 'diff.file_a'),
1478 ('+++', 'diff.file_b'),
1479 ('@@', 'diff.hunk'),
1480 ('-', 'diff.deleted'),
1481 ('+', 'diff.inserted')]
1482
1483 for chunk in func(*args, **kw):
1484 lines = chunk.split('\n')
1485 for i, line in enumerate(lines):
1486 if i != 0:
1487 yield ('\n', '')
1488 stripline = line
1489 if line and line[0] in '+-':
1490 # highlight trailing whitespace, but only in changed lines
1491 stripline = line.rstrip()
1492 for prefix, label in prefixes:
1493 if stripline.startswith(prefix):
1494 yield (stripline, label)
1495 break
1496 else:
1497 yield (line, '')
1498 if line != stripline:
1499 yield (line[len(stripline):], 'diff.trailingwhitespace')
1500
1501 def diffui(*args, **kw):
1502 '''like diff(), but yields 2-tuples of (output, label) for ui.write()'''
1503 return difflabel(diff, *args, **kw)
1504
1505
1469 def _addmodehdr(header, omode, nmode):
1506 def _addmodehdr(header, omode, nmode):
1470 if omode != nmode:
1507 if omode != nmode:
1471 header.append('old mode %s\n' % omode)
1508 header.append('old mode %s\n' % omode)
1472 header.append('new mode %s\n' % nmode)
1509 header.append('new mode %s\n' % nmode)
1473
1510
1474 def trydiff(repo, revs, ctx1, ctx2, modified, added, removed,
1511 def trydiff(repo, revs, ctx1, ctx2, modified, added, removed,
1475 copy, getfilectx, opts, losedatafn):
1512 copy, getfilectx, opts, losedatafn):
1476
1513
1477 date1 = util.datestr(ctx1.date())
1514 date1 = util.datestr(ctx1.date())
1478 man1 = ctx1.manifest()
1515 man1 = ctx1.manifest()
1479
1516
1480 gone = set()
1517 gone = set()
1481 gitmode = {'l': '120000', 'x': '100755', '': '100644'}
1518 gitmode = {'l': '120000', 'x': '100755', '': '100644'}
1482
1519
1483 copyto = dict([(v, k) for k, v in copy.items()])
1520 copyto = dict([(v, k) for k, v in copy.items()])
1484
1521
1485 if opts.git:
1522 if opts.git:
1486 revs = None
1523 revs = None
1487
1524
1488 for f in sorted(modified + added + removed):
1525 for f in sorted(modified + added + removed):
1489 to = None
1526 to = None
1490 tn = None
1527 tn = None
1491 dodiff = True
1528 dodiff = True
1492 header = []
1529 header = []
1493 if f in man1:
1530 if f in man1:
1494 to = getfilectx(f, ctx1).data()
1531 to = getfilectx(f, ctx1).data()
1495 if f not in removed:
1532 if f not in removed:
1496 tn = getfilectx(f, ctx2).data()
1533 tn = getfilectx(f, ctx2).data()
1497 a, b = f, f
1534 a, b = f, f
1498 if opts.git or losedatafn:
1535 if opts.git or losedatafn:
1499 if f in added:
1536 if f in added:
1500 mode = gitmode[ctx2.flags(f)]
1537 mode = gitmode[ctx2.flags(f)]
1501 if f in copy or f in copyto:
1538 if f in copy or f in copyto:
1502 if opts.git:
1539 if opts.git:
1503 if f in copy:
1540 if f in copy:
1504 a = copy[f]
1541 a = copy[f]
1505 else:
1542 else:
1506 a = copyto[f]
1543 a = copyto[f]
1507 omode = gitmode[man1.flags(a)]
1544 omode = gitmode[man1.flags(a)]
1508 _addmodehdr(header, omode, mode)
1545 _addmodehdr(header, omode, mode)
1509 if a in removed and a not in gone:
1546 if a in removed and a not in gone:
1510 op = 'rename'
1547 op = 'rename'
1511 gone.add(a)
1548 gone.add(a)
1512 else:
1549 else:
1513 op = 'copy'
1550 op = 'copy'
1514 header.append('%s from %s\n' % (op, a))
1551 header.append('%s from %s\n' % (op, a))
1515 header.append('%s to %s\n' % (op, f))
1552 header.append('%s to %s\n' % (op, f))
1516 to = getfilectx(a, ctx1).data()
1553 to = getfilectx(a, ctx1).data()
1517 else:
1554 else:
1518 losedatafn(f)
1555 losedatafn(f)
1519 else:
1556 else:
1520 if opts.git:
1557 if opts.git:
1521 header.append('new file mode %s\n' % mode)
1558 header.append('new file mode %s\n' % mode)
1522 elif ctx2.flags(f):
1559 elif ctx2.flags(f):
1523 losedatafn(f)
1560 losedatafn(f)
1524 if util.binary(tn):
1561 if util.binary(tn):
1525 if opts.git:
1562 if opts.git:
1526 dodiff = 'binary'
1563 dodiff = 'binary'
1527 else:
1564 else:
1528 losedatafn(f)
1565 losedatafn(f)
1529 if not opts.git and not tn:
1566 if not opts.git and not tn:
1530 # regular diffs cannot represent new empty file
1567 # regular diffs cannot represent new empty file
1531 losedatafn(f)
1568 losedatafn(f)
1532 elif f in removed:
1569 elif f in removed:
1533 if opts.git:
1570 if opts.git:
1534 # have we already reported a copy above?
1571 # have we already reported a copy above?
1535 if ((f in copy and copy[f] in added
1572 if ((f in copy and copy[f] in added
1536 and copyto[copy[f]] == f) or
1573 and copyto[copy[f]] == f) or
1537 (f in copyto and copyto[f] in added
1574 (f in copyto and copyto[f] in added
1538 and copy[copyto[f]] == f)):
1575 and copy[copyto[f]] == f)):
1539 dodiff = False
1576 dodiff = False
1540 else:
1577 else:
1541 header.append('deleted file mode %s\n' %
1578 header.append('deleted file mode %s\n' %
1542 gitmode[man1.flags(f)])
1579 gitmode[man1.flags(f)])
1543 elif not to:
1580 elif not to:
1544 # regular diffs cannot represent empty file deletion
1581 # regular diffs cannot represent empty file deletion
1545 losedatafn(f)
1582 losedatafn(f)
1546 else:
1583 else:
1547 oflag = man1.flags(f)
1584 oflag = man1.flags(f)
1548 nflag = ctx2.flags(f)
1585 nflag = ctx2.flags(f)
1549 binary = util.binary(to) or util.binary(tn)
1586 binary = util.binary(to) or util.binary(tn)
1550 if opts.git:
1587 if opts.git:
1551 _addmodehdr(header, gitmode[oflag], gitmode[nflag])
1588 _addmodehdr(header, gitmode[oflag], gitmode[nflag])
1552 if binary:
1589 if binary:
1553 dodiff = 'binary'
1590 dodiff = 'binary'
1554 elif binary or nflag != oflag:
1591 elif binary or nflag != oflag:
1555 losedatafn(f)
1592 losedatafn(f)
1556 if opts.git:
1593 if opts.git:
1557 header.insert(0, mdiff.diffline(revs, a, b, opts))
1594 header.insert(0, mdiff.diffline(revs, a, b, opts))
1558
1595
1559 if dodiff:
1596 if dodiff:
1560 if dodiff == 'binary':
1597 if dodiff == 'binary':
1561 text = b85diff(to, tn)
1598 text = b85diff(to, tn)
1562 else:
1599 else:
1563 text = mdiff.unidiff(to, date1,
1600 text = mdiff.unidiff(to, date1,
1564 # ctx2 date may be dynamic
1601 # ctx2 date may be dynamic
1565 tn, util.datestr(ctx2.date()),
1602 tn, util.datestr(ctx2.date()),
1566 a, b, revs, opts=opts)
1603 a, b, revs, opts=opts)
1567 if header and (text or len(header) > 1):
1604 if header and (text or len(header) > 1):
1568 yield ''.join(header)
1605 yield ''.join(header)
1569 if text:
1606 if text:
1570 yield text
1607 yield text
1571
1608
1572 def diffstatdata(lines):
1609 def diffstatdata(lines):
1573 filename, adds, removes = None, 0, 0
1610 filename, adds, removes = None, 0, 0
1574 for line in lines:
1611 for line in lines:
1575 if line.startswith('diff'):
1612 if line.startswith('diff'):
1576 if filename:
1613 if filename:
1577 isbinary = adds == 0 and removes == 0
1614 isbinary = adds == 0 and removes == 0
1578 yield (filename, adds, removes, isbinary)
1615 yield (filename, adds, removes, isbinary)
1579 # set numbers to 0 anyway when starting new file
1616 # set numbers to 0 anyway when starting new file
1580 adds, removes = 0, 0
1617 adds, removes = 0, 0
1581 if line.startswith('diff --git'):
1618 if line.startswith('diff --git'):
1582 filename = gitre.search(line).group(1)
1619 filename = gitre.search(line).group(1)
1583 else:
1620 else:
1584 # format: "diff -r ... -r ... filename"
1621 # format: "diff -r ... -r ... filename"
1585 filename = line.split(None, 5)[-1]
1622 filename = line.split(None, 5)[-1]
1586 elif line.startswith('+') and not line.startswith('+++'):
1623 elif line.startswith('+') and not line.startswith('+++'):
1587 adds += 1
1624 adds += 1
1588 elif line.startswith('-') and not line.startswith('---'):
1625 elif line.startswith('-') and not line.startswith('---'):
1589 removes += 1
1626 removes += 1
1590 if filename:
1627 if filename:
1591 isbinary = adds == 0 and removes == 0
1628 isbinary = adds == 0 and removes == 0
1592 yield (filename, adds, removes, isbinary)
1629 yield (filename, adds, removes, isbinary)
1593
1630
1594 def diffstat(lines, width=80, git=False):
1631 def diffstat(lines, width=80, git=False):
1595 output = []
1632 output = []
1596 stats = list(diffstatdata(lines))
1633 stats = list(diffstatdata(lines))
1597
1634
1598 maxtotal, maxname = 0, 0
1635 maxtotal, maxname = 0, 0
1599 totaladds, totalremoves = 0, 0
1636 totaladds, totalremoves = 0, 0
1600 hasbinary = False
1637 hasbinary = False
1601 for filename, adds, removes, isbinary in stats:
1638 for filename, adds, removes, isbinary in stats:
1602 totaladds += adds
1639 totaladds += adds
1603 totalremoves += removes
1640 totalremoves += removes
1604 maxname = max(maxname, len(filename))
1641 maxname = max(maxname, len(filename))
1605 maxtotal = max(maxtotal, adds + removes)
1642 maxtotal = max(maxtotal, adds + removes)
1606 if isbinary:
1643 if isbinary:
1607 hasbinary = True
1644 hasbinary = True
1608
1645
1609 countwidth = len(str(maxtotal))
1646 countwidth = len(str(maxtotal))
1610 if hasbinary and countwidth < 3:
1647 if hasbinary and countwidth < 3:
1611 countwidth = 3
1648 countwidth = 3
1612 graphwidth = width - countwidth - maxname - 6
1649 graphwidth = width - countwidth - maxname - 6
1613 if graphwidth < 10:
1650 if graphwidth < 10:
1614 graphwidth = 10
1651 graphwidth = 10
1615
1652
1616 def scale(i):
1653 def scale(i):
1617 if maxtotal <= graphwidth:
1654 if maxtotal <= graphwidth:
1618 return i
1655 return i
1619 # If diffstat runs out of room it doesn't print anything,
1656 # If diffstat runs out of room it doesn't print anything,
1620 # which isn't very useful, so always print at least one + or -
1657 # which isn't very useful, so always print at least one + or -
1621 # if there were at least some changes.
1658 # if there were at least some changes.
1622 return max(i * graphwidth // maxtotal, int(bool(i)))
1659 return max(i * graphwidth // maxtotal, int(bool(i)))
1623
1660
1624 for filename, adds, removes, isbinary in stats:
1661 for filename, adds, removes, isbinary in stats:
1625 if git and isbinary:
1662 if git and isbinary:
1626 count = 'Bin'
1663 count = 'Bin'
1627 else:
1664 else:
1628 count = adds + removes
1665 count = adds + removes
1629 pluses = '+' * scale(adds)
1666 pluses = '+' * scale(adds)
1630 minuses = '-' * scale(removes)
1667 minuses = '-' * scale(removes)
1631 output.append(' %-*s | %*s %s%s\n' % (maxname, filename, countwidth,
1668 output.append(' %-*s | %*s %s%s\n' % (maxname, filename, countwidth,
1632 count, pluses, minuses))
1669 count, pluses, minuses))
1633
1670
1634 if stats:
1671 if stats:
1635 output.append(_(' %d files changed, %d insertions(+), %d deletions(-)\n')
1672 output.append(_(' %d files changed, %d insertions(+), %d deletions(-)\n')
1636 % (len(stats), totaladds, totalremoves))
1673 % (len(stats), totaladds, totalremoves))
1637
1674
1638 return ''.join(output)
1675 return ''.join(output)
1676
1677 def diffstatui(*args, **kw):
1678 '''like diffstat(), but yields 2-tuples of (output, label) for
1679 ui.write()
1680 '''
1681
1682 for line in diffstat(*args, **kw).splitlines():
1683 if line and line[-1] in '+-':
1684 name, graph = line.rsplit(' ', 1)
1685 yield (name + ' ', '')
1686 m = re.search(r'\++', graph)
1687 if m:
1688 yield (m.group(0), 'diffstat.inserted')
1689 m = re.search(r'-+', graph)
1690 if m:
1691 yield (m.group(0), 'diffstat.deleted')
1692 else:
1693 yield (line, '')
1694 yield ('\n', '')
General Comments 0
You need to be logged in to leave comments. Login now