##// END OF EJS Templates
import: import each patch in a file or stream as a separate change...
Brendan Cully -
r10384:832f3538 default
parent child Browse files
Show More
@@ -1,3779 +1,3803 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
15 from hgweb import server
16 import merge as merge_
16 import merge as merge_
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
32
33 bad = []
33 bad = []
34 names = []
34 names = []
35 m = cmdutil.match(repo, pats, opts)
35 m = cmdutil.match(repo, pats, opts)
36 oldbad = m.bad
36 oldbad = m.bad
37 m.bad = lambda x, y: bad.append(x) or oldbad(x, y)
37 m.bad = lambda x, y: bad.append(x) or oldbad(x, y)
38
38
39 for f in repo.walk(m):
39 for f in repo.walk(m):
40 exact = m.exact(f)
40 exact = m.exact(f)
41 if exact or f not in repo.dirstate:
41 if exact or f not in repo.dirstate:
42 names.append(f)
42 names.append(f)
43 if ui.verbose or not exact:
43 if ui.verbose or not exact:
44 ui.status(_('adding %s\n') % m.rel(f))
44 ui.status(_('adding %s\n') % m.rel(f))
45 if not opts.get('dry_run'):
45 if not opts.get('dry_run'):
46 bad += [f for f in repo.add(names) if f in m.files()]
46 bad += [f for f in repo.add(names) if f in m.files()]
47 return bad and 1 or 0
47 return bad and 1 or 0
48
48
49 def addremove(ui, repo, *pats, **opts):
49 def addremove(ui, repo, *pats, **opts):
50 """add all new files, delete all missing files
50 """add all new files, delete all missing files
51
51
52 Add all new files and remove all missing files from the
52 Add all new files and remove all missing files from the
53 repository.
53 repository.
54
54
55 New files are ignored if they match any of the patterns in
55 New files are ignored if they match any of the patterns in
56 .hgignore. As with add, these changes take effect at the next
56 .hgignore. As with add, these changes take effect at the next
57 commit.
57 commit.
58
58
59 Use the -s/--similarity option to detect renamed files. With a
59 Use the -s/--similarity option to detect renamed files. With a
60 parameter greater than 0, this compares every removed file with
60 parameter greater than 0, this compares every removed file with
61 every added file and records those similar enough as renames. This
61 every added file and records those similar enough as renames. This
62 option takes a percentage between 0 (disabled) and 100 (files must
62 option takes a percentage between 0 (disabled) and 100 (files must
63 be identical) as its parameter. Detecting renamed files this way
63 be identical) as its parameter. Detecting renamed files this way
64 can be expensive.
64 can be expensive.
65 """
65 """
66 try:
66 try:
67 sim = float(opts.get('similarity') or 0)
67 sim = float(opts.get('similarity') or 0)
68 except ValueError:
68 except ValueError:
69 raise util.Abort(_('similarity must be a number'))
69 raise util.Abort(_('similarity must be a number'))
70 if sim < 0 or sim > 100:
70 if sim < 0 or sim > 100:
71 raise util.Abort(_('similarity must be between 0 and 100'))
71 raise util.Abort(_('similarity must be between 0 and 100'))
72 return cmdutil.addremove(repo, pats, opts, similarity=sim / 100.0)
72 return cmdutil.addremove(repo, pats, opts, similarity=sim / 100.0)
73
73
74 def annotate(ui, repo, *pats, **opts):
74 def annotate(ui, repo, *pats, **opts):
75 """show changeset information by line for each file
75 """show changeset information by line for each file
76
76
77 List changes in files, showing the revision id responsible for
77 List changes in files, showing the revision id responsible for
78 each line
78 each line
79
79
80 This command is useful for discovering when a change was made and
80 This command is useful for discovering when a change was made and
81 by whom.
81 by whom.
82
82
83 Without the -a/--text option, annotate will avoid processing files
83 Without the -a/--text option, annotate will avoid processing files
84 it detects as binary. With -a, annotate will annotate the file
84 it detects as binary. With -a, annotate will annotate the file
85 anyway, although the results will probably be neither useful
85 anyway, although the results will probably be neither useful
86 nor desirable.
86 nor desirable.
87 """
87 """
88 datefunc = ui.quiet and util.shortdate or util.datestr
88 datefunc = ui.quiet and util.shortdate or util.datestr
89 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
89 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
90
90
91 if not pats:
91 if not pats:
92 raise util.Abort(_('at least one filename or pattern is required'))
92 raise util.Abort(_('at least one filename or pattern is required'))
93
93
94 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
94 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
95 ('number', lambda x: str(x[0].rev())),
95 ('number', lambda x: str(x[0].rev())),
96 ('changeset', lambda x: short(x[0].node())),
96 ('changeset', lambda x: short(x[0].node())),
97 ('date', getdate),
97 ('date', getdate),
98 ('file', lambda x: x[0].path()),
98 ('file', lambda x: x[0].path()),
99 ]
99 ]
100
100
101 if (not opts.get('user') and not opts.get('changeset')
101 if (not opts.get('user') and not opts.get('changeset')
102 and not opts.get('date') and not opts.get('file')):
102 and not opts.get('date') and not opts.get('file')):
103 opts['number'] = 1
103 opts['number'] = 1
104
104
105 linenumber = opts.get('line_number') is not None
105 linenumber = opts.get('line_number') is not None
106 if (linenumber and (not opts.get('changeset')) and (not opts.get('number'))):
106 if (linenumber and (not opts.get('changeset')) and (not opts.get('number'))):
107 raise util.Abort(_('at least one of -n/-c is required for -l'))
107 raise util.Abort(_('at least one of -n/-c is required for -l'))
108
108
109 funcmap = [func for op, func in opmap if opts.get(op)]
109 funcmap = [func for op, func in opmap if opts.get(op)]
110 if linenumber:
110 if linenumber:
111 lastfunc = funcmap[-1]
111 lastfunc = funcmap[-1]
112 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
112 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
113
113
114 ctx = repo[opts.get('rev')]
114 ctx = repo[opts.get('rev')]
115 m = cmdutil.match(repo, pats, opts)
115 m = cmdutil.match(repo, pats, opts)
116 follow = not opts.get('no_follow')
116 follow = not opts.get('no_follow')
117 for abs in ctx.walk(m):
117 for abs in ctx.walk(m):
118 fctx = ctx[abs]
118 fctx = ctx[abs]
119 if not opts.get('text') and util.binary(fctx.data()):
119 if not opts.get('text') and util.binary(fctx.data()):
120 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
120 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
121 continue
121 continue
122
122
123 lines = fctx.annotate(follow=follow, linenumber=linenumber)
123 lines = fctx.annotate(follow=follow, linenumber=linenumber)
124 pieces = []
124 pieces = []
125
125
126 for f in funcmap:
126 for f in funcmap:
127 l = [f(n) for n, dummy in lines]
127 l = [f(n) for n, dummy in lines]
128 if l:
128 if l:
129 ml = max(map(len, l))
129 ml = max(map(len, l))
130 pieces.append(["%*s" % (ml, x) for x in l])
130 pieces.append(["%*s" % (ml, x) for x in l])
131
131
132 if pieces:
132 if pieces:
133 for p, l in zip(zip(*pieces), lines):
133 for p, l in zip(zip(*pieces), lines):
134 ui.write("%s: %s" % (" ".join(p), l[1]))
134 ui.write("%s: %s" % (" ".join(p), l[1]))
135
135
136 def archive(ui, repo, dest, **opts):
136 def archive(ui, repo, dest, **opts):
137 '''create an unversioned archive of a repository revision
137 '''create an unversioned archive of a repository revision
138
138
139 By default, the revision used is the parent of the working
139 By default, the revision used is the parent of the working
140 directory; use -r/--rev to specify a different revision.
140 directory; use -r/--rev to specify a different revision.
141
141
142 To specify the type of archive to create, use -t/--type. Valid
142 To specify the type of archive to create, use -t/--type. Valid
143 types are:
143 types are:
144
144
145 :``files``: a directory full of files (default)
145 :``files``: a directory full of files (default)
146 :``tar``: tar archive, uncompressed
146 :``tar``: tar archive, uncompressed
147 :``tbz2``: tar archive, compressed using bzip2
147 :``tbz2``: tar archive, compressed using bzip2
148 :``tgz``: tar archive, compressed using gzip
148 :``tgz``: tar archive, compressed using gzip
149 :``uzip``: zip archive, uncompressed
149 :``uzip``: zip archive, uncompressed
150 :``zip``: zip archive, compressed using deflate
150 :``zip``: zip archive, compressed using deflate
151
151
152 The exact name of the destination archive or directory is given
152 The exact name of the destination archive or directory is given
153 using a format string; see 'hg help export' for details.
153 using a format string; see 'hg help export' for details.
154
154
155 Each member added to an archive file has a directory prefix
155 Each member added to an archive file has a directory prefix
156 prepended. Use -p/--prefix to specify a format string for the
156 prepended. Use -p/--prefix to specify a format string for the
157 prefix. The default is the basename of the archive, with suffixes
157 prefix. The default is the basename of the archive, with suffixes
158 removed.
158 removed.
159 '''
159 '''
160
160
161 ctx = repo[opts.get('rev')]
161 ctx = repo[opts.get('rev')]
162 if not ctx:
162 if not ctx:
163 raise util.Abort(_('no working directory: please specify a revision'))
163 raise util.Abort(_('no working directory: please specify a revision'))
164 node = ctx.node()
164 node = ctx.node()
165 dest = cmdutil.make_filename(repo, dest, node)
165 dest = cmdutil.make_filename(repo, dest, node)
166 if os.path.realpath(dest) == repo.root:
166 if os.path.realpath(dest) == repo.root:
167 raise util.Abort(_('repository root cannot be destination'))
167 raise util.Abort(_('repository root cannot be destination'))
168 matchfn = cmdutil.match(repo, [], opts)
168 matchfn = cmdutil.match(repo, [], opts)
169 kind = opts.get('type') or 'files'
169 kind = opts.get('type') or 'files'
170 prefix = opts.get('prefix')
170 prefix = opts.get('prefix')
171 if dest == '-':
171 if dest == '-':
172 if kind == 'files':
172 if kind == 'files':
173 raise util.Abort(_('cannot archive plain files to stdout'))
173 raise util.Abort(_('cannot archive plain files to stdout'))
174 dest = sys.stdout
174 dest = sys.stdout
175 if not prefix:
175 if not prefix:
176 prefix = os.path.basename(repo.root) + '-%h'
176 prefix = os.path.basename(repo.root) + '-%h'
177 prefix = cmdutil.make_filename(repo, prefix, node)
177 prefix = cmdutil.make_filename(repo, prefix, node)
178 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
178 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
179 matchfn, prefix)
179 matchfn, prefix)
180
180
181 def backout(ui, repo, node=None, rev=None, **opts):
181 def backout(ui, repo, node=None, rev=None, **opts):
182 '''reverse effect of earlier changeset
182 '''reverse effect of earlier changeset
183
183
184 Commit the backed out changes as a new changeset. The new
184 Commit the backed out changes as a new changeset. The new
185 changeset is a child of the backed out changeset.
185 changeset is a child of the backed out changeset.
186
186
187 If you backout a changeset other than the tip, a new head is
187 If you backout a changeset other than the tip, a new head is
188 created. This head will be the new tip and you should merge this
188 created. This head will be the new tip and you should merge this
189 backout changeset with another head.
189 backout changeset with another head.
190
190
191 The --merge option remembers the parent of the working directory
191 The --merge option remembers the parent of the working directory
192 before starting the backout, then merges the new head with that
192 before starting the backout, then merges the new head with that
193 changeset afterwards. This saves you from doing the merge by hand.
193 changeset afterwards. This saves you from doing the merge by hand.
194 The result of this merge is not committed, as with a normal merge.
194 The result of this merge is not committed, as with a normal merge.
195
195
196 See 'hg help dates' for a list of formats valid for -d/--date.
196 See 'hg help dates' for a list of formats valid for -d/--date.
197 '''
197 '''
198 if rev and node:
198 if rev and node:
199 raise util.Abort(_("please specify just one revision"))
199 raise util.Abort(_("please specify just one revision"))
200
200
201 if not rev:
201 if not rev:
202 rev = node
202 rev = node
203
203
204 if not rev:
204 if not rev:
205 raise util.Abort(_("please specify a revision to backout"))
205 raise util.Abort(_("please specify a revision to backout"))
206
206
207 date = opts.get('date')
207 date = opts.get('date')
208 if date:
208 if date:
209 opts['date'] = util.parsedate(date)
209 opts['date'] = util.parsedate(date)
210
210
211 cmdutil.bail_if_changed(repo)
211 cmdutil.bail_if_changed(repo)
212 node = repo.lookup(rev)
212 node = repo.lookup(rev)
213
213
214 op1, op2 = repo.dirstate.parents()
214 op1, op2 = repo.dirstate.parents()
215 a = repo.changelog.ancestor(op1, node)
215 a = repo.changelog.ancestor(op1, node)
216 if a != node:
216 if a != node:
217 raise util.Abort(_('cannot backout change on a different branch'))
217 raise util.Abort(_('cannot backout change on a different branch'))
218
218
219 p1, p2 = repo.changelog.parents(node)
219 p1, p2 = repo.changelog.parents(node)
220 if p1 == nullid:
220 if p1 == nullid:
221 raise util.Abort(_('cannot backout a change with no parents'))
221 raise util.Abort(_('cannot backout a change with no parents'))
222 if p2 != nullid:
222 if p2 != nullid:
223 if not opts.get('parent'):
223 if not opts.get('parent'):
224 raise util.Abort(_('cannot backout a merge changeset without '
224 raise util.Abort(_('cannot backout a merge changeset without '
225 '--parent'))
225 '--parent'))
226 p = repo.lookup(opts['parent'])
226 p = repo.lookup(opts['parent'])
227 if p not in (p1, p2):
227 if p not in (p1, p2):
228 raise util.Abort(_('%s is not a parent of %s') %
228 raise util.Abort(_('%s is not a parent of %s') %
229 (short(p), short(node)))
229 (short(p), short(node)))
230 parent = p
230 parent = p
231 else:
231 else:
232 if opts.get('parent'):
232 if opts.get('parent'):
233 raise util.Abort(_('cannot use --parent on non-merge changeset'))
233 raise util.Abort(_('cannot use --parent on non-merge changeset'))
234 parent = p1
234 parent = p1
235
235
236 # the backout should appear on the same branch
236 # the backout should appear on the same branch
237 branch = repo.dirstate.branch()
237 branch = repo.dirstate.branch()
238 hg.clean(repo, node, show_stats=False)
238 hg.clean(repo, node, show_stats=False)
239 repo.dirstate.setbranch(branch)
239 repo.dirstate.setbranch(branch)
240 revert_opts = opts.copy()
240 revert_opts = opts.copy()
241 revert_opts['date'] = None
241 revert_opts['date'] = None
242 revert_opts['all'] = True
242 revert_opts['all'] = True
243 revert_opts['rev'] = hex(parent)
243 revert_opts['rev'] = hex(parent)
244 revert_opts['no_backup'] = None
244 revert_opts['no_backup'] = None
245 revert(ui, repo, **revert_opts)
245 revert(ui, repo, **revert_opts)
246 commit_opts = opts.copy()
246 commit_opts = opts.copy()
247 commit_opts['addremove'] = False
247 commit_opts['addremove'] = False
248 if not commit_opts['message'] and not commit_opts['logfile']:
248 if not commit_opts['message'] and not commit_opts['logfile']:
249 # we don't translate commit messages
249 # we don't translate commit messages
250 commit_opts['message'] = "Backed out changeset %s" % short(node)
250 commit_opts['message'] = "Backed out changeset %s" % short(node)
251 commit_opts['force_editor'] = True
251 commit_opts['force_editor'] = True
252 commit(ui, repo, **commit_opts)
252 commit(ui, repo, **commit_opts)
253 def nice(node):
253 def nice(node):
254 return '%d:%s' % (repo.changelog.rev(node), short(node))
254 return '%d:%s' % (repo.changelog.rev(node), short(node))
255 ui.status(_('changeset %s backs out changeset %s\n') %
255 ui.status(_('changeset %s backs out changeset %s\n') %
256 (nice(repo.changelog.tip()), nice(node)))
256 (nice(repo.changelog.tip()), nice(node)))
257 if op1 != node:
257 if op1 != node:
258 hg.clean(repo, op1, show_stats=False)
258 hg.clean(repo, op1, show_stats=False)
259 if opts.get('merge'):
259 if opts.get('merge'):
260 ui.status(_('merging with changeset %s\n')
260 ui.status(_('merging with changeset %s\n')
261 % nice(repo.changelog.tip()))
261 % nice(repo.changelog.tip()))
262 hg.merge(repo, hex(repo.changelog.tip()))
262 hg.merge(repo, hex(repo.changelog.tip()))
263 else:
263 else:
264 ui.status(_('the backout changeset is a new head - '
264 ui.status(_('the backout changeset is a new head - '
265 'do not forget to merge\n'))
265 'do not forget to merge\n'))
266 ui.status(_('(use "backout --merge" '
266 ui.status(_('(use "backout --merge" '
267 'if you want to auto-merge)\n'))
267 'if you want to auto-merge)\n'))
268
268
269 def bisect(ui, repo, rev=None, extra=None, command=None,
269 def bisect(ui, repo, rev=None, extra=None, command=None,
270 reset=None, good=None, bad=None, skip=None, noupdate=None):
270 reset=None, good=None, bad=None, skip=None, noupdate=None):
271 """subdivision search of changesets
271 """subdivision search of changesets
272
272
273 This command helps to find changesets which introduce problems. To
273 This command helps to find changesets which introduce problems. To
274 use, mark the earliest changeset you know exhibits the problem as
274 use, mark the earliest changeset you know exhibits the problem as
275 bad, then mark the latest changeset which is free from the problem
275 bad, then mark the latest changeset which is free from the problem
276 as good. Bisect will update your working directory to a revision
276 as good. Bisect will update your working directory to a revision
277 for testing (unless the -U/--noupdate option is specified). Once
277 for testing (unless the -U/--noupdate option is specified). Once
278 you have performed tests, mark the working directory as good or
278 you have performed tests, mark the working directory as good or
279 bad, and bisect will either update to another candidate changeset
279 bad, and bisect will either update to another candidate changeset
280 or announce that it has found the bad revision.
280 or announce that it has found the bad revision.
281
281
282 As a shortcut, you can also use the revision argument to mark a
282 As a shortcut, you can also use the revision argument to mark a
283 revision as good or bad without checking it out first.
283 revision as good or bad without checking it out first.
284
284
285 If you supply a command, it will be used for automatic bisection.
285 If you supply a command, it will be used for automatic bisection.
286 Its exit status will be used to mark revisions as good or bad:
286 Its exit status will be used to mark revisions as good or bad:
287 status 0 means good, 125 means to skip the revision, 127
287 status 0 means good, 125 means to skip the revision, 127
288 (command not found) will abort the bisection, and any other
288 (command not found) will abort the bisection, and any other
289 non-zero exit status means the revision is bad.
289 non-zero exit status means the revision is bad.
290 """
290 """
291 def print_result(nodes, good):
291 def print_result(nodes, good):
292 displayer = cmdutil.show_changeset(ui, repo, {})
292 displayer = cmdutil.show_changeset(ui, repo, {})
293 if len(nodes) == 1:
293 if len(nodes) == 1:
294 # narrowed it down to a single revision
294 # narrowed it down to a single revision
295 if good:
295 if good:
296 ui.write(_("The first good revision is:\n"))
296 ui.write(_("The first good revision is:\n"))
297 else:
297 else:
298 ui.write(_("The first bad revision is:\n"))
298 ui.write(_("The first bad revision is:\n"))
299 displayer.show(repo[nodes[0]])
299 displayer.show(repo[nodes[0]])
300 else:
300 else:
301 # multiple possible revisions
301 # multiple possible revisions
302 if good:
302 if good:
303 ui.write(_("Due to skipped revisions, the first "
303 ui.write(_("Due to skipped revisions, the first "
304 "good revision could be any of:\n"))
304 "good revision could be any of:\n"))
305 else:
305 else:
306 ui.write(_("Due to skipped revisions, the first "
306 ui.write(_("Due to skipped revisions, the first "
307 "bad revision could be any of:\n"))
307 "bad revision could be any of:\n"))
308 for n in nodes:
308 for n in nodes:
309 displayer.show(repo[n])
309 displayer.show(repo[n])
310 displayer.close()
310 displayer.close()
311
311
312 def check_state(state, interactive=True):
312 def check_state(state, interactive=True):
313 if not state['good'] or not state['bad']:
313 if not state['good'] or not state['bad']:
314 if (good or bad or skip or reset) and interactive:
314 if (good or bad or skip or reset) and interactive:
315 return
315 return
316 if not state['good']:
316 if not state['good']:
317 raise util.Abort(_('cannot bisect (no known good revisions)'))
317 raise util.Abort(_('cannot bisect (no known good revisions)'))
318 else:
318 else:
319 raise util.Abort(_('cannot bisect (no known bad revisions)'))
319 raise util.Abort(_('cannot bisect (no known bad revisions)'))
320 return True
320 return True
321
321
322 # backward compatibility
322 # backward compatibility
323 if rev in "good bad reset init".split():
323 if rev in "good bad reset init".split():
324 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
324 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
325 cmd, rev, extra = rev, extra, None
325 cmd, rev, extra = rev, extra, None
326 if cmd == "good":
326 if cmd == "good":
327 good = True
327 good = True
328 elif cmd == "bad":
328 elif cmd == "bad":
329 bad = True
329 bad = True
330 else:
330 else:
331 reset = True
331 reset = True
332 elif extra or good + bad + skip + reset + bool(command) > 1:
332 elif extra or good + bad + skip + reset + bool(command) > 1:
333 raise util.Abort(_('incompatible arguments'))
333 raise util.Abort(_('incompatible arguments'))
334
334
335 if reset:
335 if reset:
336 p = repo.join("bisect.state")
336 p = repo.join("bisect.state")
337 if os.path.exists(p):
337 if os.path.exists(p):
338 os.unlink(p)
338 os.unlink(p)
339 return
339 return
340
340
341 state = hbisect.load_state(repo)
341 state = hbisect.load_state(repo)
342
342
343 if command:
343 if command:
344 changesets = 1
344 changesets = 1
345 try:
345 try:
346 while changesets:
346 while changesets:
347 # update state
347 # update state
348 status = util.system(command)
348 status = util.system(command)
349 if status == 125:
349 if status == 125:
350 transition = "skip"
350 transition = "skip"
351 elif status == 0:
351 elif status == 0:
352 transition = "good"
352 transition = "good"
353 # status < 0 means process was killed
353 # status < 0 means process was killed
354 elif status == 127:
354 elif status == 127:
355 raise util.Abort(_("failed to execute %s") % command)
355 raise util.Abort(_("failed to execute %s") % command)
356 elif status < 0:
356 elif status < 0:
357 raise util.Abort(_("%s killed") % command)
357 raise util.Abort(_("%s killed") % command)
358 else:
358 else:
359 transition = "bad"
359 transition = "bad"
360 ctx = repo[rev or '.']
360 ctx = repo[rev or '.']
361 state[transition].append(ctx.node())
361 state[transition].append(ctx.node())
362 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
362 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
363 check_state(state, interactive=False)
363 check_state(state, interactive=False)
364 # bisect
364 # bisect
365 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
365 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
366 # update to next check
366 # update to next check
367 cmdutil.bail_if_changed(repo)
367 cmdutil.bail_if_changed(repo)
368 hg.clean(repo, nodes[0], show_stats=False)
368 hg.clean(repo, nodes[0], show_stats=False)
369 finally:
369 finally:
370 hbisect.save_state(repo, state)
370 hbisect.save_state(repo, state)
371 return print_result(nodes, good)
371 return print_result(nodes, good)
372
372
373 # update state
373 # update state
374 node = repo.lookup(rev or '.')
374 node = repo.lookup(rev or '.')
375 if good or bad or skip:
375 if good or bad or skip:
376 if good:
376 if good:
377 state['good'].append(node)
377 state['good'].append(node)
378 elif bad:
378 elif bad:
379 state['bad'].append(node)
379 state['bad'].append(node)
380 elif skip:
380 elif skip:
381 state['skip'].append(node)
381 state['skip'].append(node)
382 hbisect.save_state(repo, state)
382 hbisect.save_state(repo, state)
383
383
384 if not check_state(state):
384 if not check_state(state):
385 return
385 return
386
386
387 # actually bisect
387 # actually bisect
388 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
388 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
389 if changesets == 0:
389 if changesets == 0:
390 print_result(nodes, good)
390 print_result(nodes, good)
391 else:
391 else:
392 assert len(nodes) == 1 # only a single node can be tested next
392 assert len(nodes) == 1 # only a single node can be tested next
393 node = nodes[0]
393 node = nodes[0]
394 # compute the approximate number of remaining tests
394 # compute the approximate number of remaining tests
395 tests, size = 0, 2
395 tests, size = 0, 2
396 while size <= changesets:
396 while size <= changesets:
397 tests, size = tests + 1, size * 2
397 tests, size = tests + 1, size * 2
398 rev = repo.changelog.rev(node)
398 rev = repo.changelog.rev(node)
399 ui.write(_("Testing changeset %d:%s "
399 ui.write(_("Testing changeset %d:%s "
400 "(%d changesets remaining, ~%d tests)\n")
400 "(%d changesets remaining, ~%d tests)\n")
401 % (rev, short(node), changesets, tests))
401 % (rev, short(node), changesets, tests))
402 if not noupdate:
402 if not noupdate:
403 cmdutil.bail_if_changed(repo)
403 cmdutil.bail_if_changed(repo)
404 return hg.clean(repo, node)
404 return hg.clean(repo, node)
405
405
406 def branch(ui, repo, label=None, **opts):
406 def branch(ui, repo, label=None, **opts):
407 """set or show the current branch name
407 """set or show the current branch name
408
408
409 With no argument, show the current branch name. With one argument,
409 With no argument, show the current branch name. With one argument,
410 set the working directory branch name (the branch will not exist
410 set the working directory branch name (the branch will not exist
411 in the repository until the next commit). Standard practice
411 in the repository until the next commit). Standard practice
412 recommends that primary development take place on the 'default'
412 recommends that primary development take place on the 'default'
413 branch.
413 branch.
414
414
415 Unless -f/--force is specified, branch will not let you set a
415 Unless -f/--force is specified, branch will not let you set a
416 branch name that already exists, even if it's inactive.
416 branch name that already exists, even if it's inactive.
417
417
418 Use -C/--clean to reset the working directory branch to that of
418 Use -C/--clean to reset the working directory branch to that of
419 the parent of the working directory, negating a previous branch
419 the parent of the working directory, negating a previous branch
420 change.
420 change.
421
421
422 Use the command 'hg update' to switch to an existing branch. Use
422 Use the command 'hg update' to switch to an existing branch. Use
423 'hg commit --close-branch' to mark this branch as closed.
423 'hg commit --close-branch' to mark this branch as closed.
424 """
424 """
425
425
426 if opts.get('clean'):
426 if opts.get('clean'):
427 label = repo[None].parents()[0].branch()
427 label = repo[None].parents()[0].branch()
428 repo.dirstate.setbranch(label)
428 repo.dirstate.setbranch(label)
429 ui.status(_('reset working directory to branch %s\n') % label)
429 ui.status(_('reset working directory to branch %s\n') % label)
430 elif label:
430 elif label:
431 utflabel = encoding.fromlocal(label)
431 utflabel = encoding.fromlocal(label)
432 if not opts.get('force') and utflabel in repo.branchtags():
432 if not opts.get('force') and utflabel in repo.branchtags():
433 if label not in [p.branch() for p in repo.parents()]:
433 if label not in [p.branch() for p in repo.parents()]:
434 raise util.Abort(_('a branch of the same name already exists'
434 raise util.Abort(_('a branch of the same name already exists'
435 ' (use --force to override)'))
435 ' (use --force to override)'))
436 repo.dirstate.setbranch(utflabel)
436 repo.dirstate.setbranch(utflabel)
437 ui.status(_('marked working directory as branch %s\n') % label)
437 ui.status(_('marked working directory as branch %s\n') % label)
438 else:
438 else:
439 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
439 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
440
440
441 def branches(ui, repo, active=False, closed=False):
441 def branches(ui, repo, active=False, closed=False):
442 """list repository named branches
442 """list repository named branches
443
443
444 List the repository's named branches, indicating which ones are
444 List the repository's named branches, indicating which ones are
445 inactive. If -c/--closed is specified, also list branches which have
445 inactive. If -c/--closed is specified, also list branches which have
446 been marked closed (see hg commit --close-branch).
446 been marked closed (see hg commit --close-branch).
447
447
448 If -a/--active is specified, only show active branches. A branch
448 If -a/--active is specified, only show active branches. A branch
449 is considered active if it contains repository heads.
449 is considered active if it contains repository heads.
450
450
451 Use the command 'hg update' to switch to an existing branch.
451 Use the command 'hg update' to switch to an existing branch.
452 """
452 """
453
453
454 hexfunc = ui.debugflag and hex or short
454 hexfunc = ui.debugflag and hex or short
455 activebranches = [repo[n].branch() for n in repo.heads()]
455 activebranches = [repo[n].branch() for n in repo.heads()]
456 def testactive(tag, node):
456 def testactive(tag, node):
457 realhead = tag in activebranches
457 realhead = tag in activebranches
458 open = node in repo.branchheads(tag, closed=False)
458 open = node in repo.branchheads(tag, closed=False)
459 return realhead and open
459 return realhead and open
460 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
460 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
461 for tag, node in repo.branchtags().items()],
461 for tag, node in repo.branchtags().items()],
462 reverse=True)
462 reverse=True)
463
463
464 for isactive, node, tag in branches:
464 for isactive, node, tag in branches:
465 if (not active) or isactive:
465 if (not active) or isactive:
466 encodedtag = encoding.tolocal(tag)
466 encodedtag = encoding.tolocal(tag)
467 if ui.quiet:
467 if ui.quiet:
468 ui.write("%s\n" % encodedtag)
468 ui.write("%s\n" % encodedtag)
469 else:
469 else:
470 hn = repo.lookup(node)
470 hn = repo.lookup(node)
471 if isactive:
471 if isactive:
472 notice = ''
472 notice = ''
473 elif hn not in repo.branchheads(tag, closed=False):
473 elif hn not in repo.branchheads(tag, closed=False):
474 if not closed:
474 if not closed:
475 continue
475 continue
476 notice = _(' (closed)')
476 notice = _(' (closed)')
477 else:
477 else:
478 notice = _(' (inactive)')
478 notice = _(' (inactive)')
479 rev = str(node).rjust(31 - encoding.colwidth(encodedtag))
479 rev = str(node).rjust(31 - encoding.colwidth(encodedtag))
480 data = encodedtag, rev, hexfunc(hn), notice
480 data = encodedtag, rev, hexfunc(hn), notice
481 ui.write("%s %s:%s%s\n" % data)
481 ui.write("%s %s:%s%s\n" % data)
482
482
483 def bundle(ui, repo, fname, dest=None, **opts):
483 def bundle(ui, repo, fname, dest=None, **opts):
484 """create a changegroup file
484 """create a changegroup file
485
485
486 Generate a compressed changegroup file collecting changesets not
486 Generate a compressed changegroup file collecting changesets not
487 known to be in another repository.
487 known to be in another repository.
488
488
489 If you omit the destination repository, then hg assumes the
489 If you omit the destination repository, then hg assumes the
490 destination will have all the nodes you specify with --base
490 destination will have all the nodes you specify with --base
491 parameters. To create a bundle containing all changesets, use
491 parameters. To create a bundle containing all changesets, use
492 -a/--all (or --base null).
492 -a/--all (or --base null).
493
493
494 You can change compression method with the -t/--type option.
494 You can change compression method with the -t/--type option.
495 The available compression methods are: none, bzip2, and
495 The available compression methods are: none, bzip2, and
496 gzip (by default, bundles are compressed using bzip2).
496 gzip (by default, bundles are compressed using bzip2).
497
497
498 The bundle file can then be transferred using conventional means
498 The bundle file can then be transferred using conventional means
499 and applied to another repository with the unbundle or pull
499 and applied to another repository with the unbundle or pull
500 command. This is useful when direct push and pull are not
500 command. This is useful when direct push and pull are not
501 available or when exporting an entire repository is undesirable.
501 available or when exporting an entire repository is undesirable.
502
502
503 Applying bundles preserves all changeset contents including
503 Applying bundles preserves all changeset contents including
504 permissions, copy/rename information, and revision history.
504 permissions, copy/rename information, and revision history.
505 """
505 """
506 revs = opts.get('rev') or None
506 revs = opts.get('rev') or None
507 if revs:
507 if revs:
508 revs = [repo.lookup(rev) for rev in revs]
508 revs = [repo.lookup(rev) for rev in revs]
509 if opts.get('all'):
509 if opts.get('all'):
510 base = ['null']
510 base = ['null']
511 else:
511 else:
512 base = opts.get('base')
512 base = opts.get('base')
513 if base:
513 if base:
514 if dest:
514 if dest:
515 raise util.Abort(_("--base is incompatible with specifying "
515 raise util.Abort(_("--base is incompatible with specifying "
516 "a destination"))
516 "a destination"))
517 base = [repo.lookup(rev) for rev in base]
517 base = [repo.lookup(rev) for rev in base]
518 # create the right base
518 # create the right base
519 # XXX: nodesbetween / changegroup* should be "fixed" instead
519 # XXX: nodesbetween / changegroup* should be "fixed" instead
520 o = []
520 o = []
521 has = set((nullid,))
521 has = set((nullid,))
522 for n in base:
522 for n in base:
523 has.update(repo.changelog.reachable(n))
523 has.update(repo.changelog.reachable(n))
524 if revs:
524 if revs:
525 visit = list(revs)
525 visit = list(revs)
526 else:
526 else:
527 visit = repo.changelog.heads()
527 visit = repo.changelog.heads()
528 seen = {}
528 seen = {}
529 while visit:
529 while visit:
530 n = visit.pop(0)
530 n = visit.pop(0)
531 parents = [p for p in repo.changelog.parents(n) if p not in has]
531 parents = [p for p in repo.changelog.parents(n) if p not in has]
532 if len(parents) == 0:
532 if len(parents) == 0:
533 o.insert(0, n)
533 o.insert(0, n)
534 else:
534 else:
535 for p in parents:
535 for p in parents:
536 if p not in seen:
536 if p not in seen:
537 seen[p] = 1
537 seen[p] = 1
538 visit.append(p)
538 visit.append(p)
539 else:
539 else:
540 dest = ui.expandpath(dest or 'default-push', dest or 'default')
540 dest = ui.expandpath(dest or 'default-push', dest or 'default')
541 dest, branches = hg.parseurl(dest, opts.get('branch'))
541 dest, branches = hg.parseurl(dest, opts.get('branch'))
542 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
542 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
543 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
543 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
544 o = repo.findoutgoing(other, force=opts.get('force'))
544 o = repo.findoutgoing(other, force=opts.get('force'))
545
545
546 if revs:
546 if revs:
547 cg = repo.changegroupsubset(o, revs, 'bundle')
547 cg = repo.changegroupsubset(o, revs, 'bundle')
548 else:
548 else:
549 cg = repo.changegroup(o, 'bundle')
549 cg = repo.changegroup(o, 'bundle')
550
550
551 bundletype = opts.get('type', 'bzip2').lower()
551 bundletype = opts.get('type', 'bzip2').lower()
552 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
552 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
553 bundletype = btypes.get(bundletype)
553 bundletype = btypes.get(bundletype)
554 if bundletype not in changegroup.bundletypes:
554 if bundletype not in changegroup.bundletypes:
555 raise util.Abort(_('unknown bundle type specified with --type'))
555 raise util.Abort(_('unknown bundle type specified with --type'))
556
556
557 changegroup.writebundle(cg, fname, bundletype)
557 changegroup.writebundle(cg, fname, bundletype)
558
558
559 def cat(ui, repo, file1, *pats, **opts):
559 def cat(ui, repo, file1, *pats, **opts):
560 """output the current or given revision of files
560 """output the current or given revision of files
561
561
562 Print the specified files as they were at the given revision. If
562 Print the specified files as they were at the given revision. If
563 no revision is given, the parent of the working directory is used,
563 no revision is given, the parent of the working directory is used,
564 or tip if no revision is checked out.
564 or tip if no revision is checked out.
565
565
566 Output may be to a file, in which case the name of the file is
566 Output may be to a file, in which case the name of the file is
567 given using a format string. The formatting rules are the same as
567 given using a format string. The formatting rules are the same as
568 for the export command, with the following additions:
568 for the export command, with the following additions:
569
569
570 :``%s``: basename of file being printed
570 :``%s``: basename of file being printed
571 :``%d``: dirname of file being printed, or '.' if in repository root
571 :``%d``: dirname of file being printed, or '.' if in repository root
572 :``%p``: root-relative path name of file being printed
572 :``%p``: root-relative path name of file being printed
573 """
573 """
574 ctx = repo[opts.get('rev')]
574 ctx = repo[opts.get('rev')]
575 err = 1
575 err = 1
576 m = cmdutil.match(repo, (file1,) + pats, opts)
576 m = cmdutil.match(repo, (file1,) + pats, opts)
577 for abs in ctx.walk(m):
577 for abs in ctx.walk(m):
578 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
578 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
579 data = ctx[abs].data()
579 data = ctx[abs].data()
580 if opts.get('decode'):
580 if opts.get('decode'):
581 data = repo.wwritedata(abs, data)
581 data = repo.wwritedata(abs, data)
582 fp.write(data)
582 fp.write(data)
583 err = 0
583 err = 0
584 return err
584 return err
585
585
586 def clone(ui, source, dest=None, **opts):
586 def clone(ui, source, dest=None, **opts):
587 """make a copy of an existing repository
587 """make a copy of an existing repository
588
588
589 Create a copy of an existing repository in a new directory.
589 Create a copy of an existing repository in a new directory.
590
590
591 If no destination directory name is specified, it defaults to the
591 If no destination directory name is specified, it defaults to the
592 basename of the source.
592 basename of the source.
593
593
594 The location of the source is added to the new repository's
594 The location of the source is added to the new repository's
595 .hg/hgrc file, as the default to be used for future pulls.
595 .hg/hgrc file, as the default to be used for future pulls.
596
596
597 See 'hg help urls' for valid source format details.
597 See 'hg help urls' for valid source format details.
598
598
599 It is possible to specify an ``ssh://`` URL as the destination, but no
599 It is possible to specify an ``ssh://`` URL as the destination, but no
600 .hg/hgrc and working directory will be created on the remote side.
600 .hg/hgrc and working directory will be created on the remote side.
601 Please see 'hg help urls' for important details about ``ssh://`` URLs.
601 Please see 'hg help urls' for important details about ``ssh://`` URLs.
602
602
603 If the -U/--noupdate option is specified, the new clone will contain
603 If the -U/--noupdate option is specified, the new clone will contain
604 only a repository (.hg) and no working copy (the working copy parent
604 only a repository (.hg) and no working copy (the working copy parent
605 will be the null changeset). Otherwise, clone will initially check
605 will be the null changeset). Otherwise, clone will initially check
606 out (in order of precedence):
606 out (in order of precedence):
607
607
608 a) the changeset, tag or branch specified with -u/--updaterev
608 a) the changeset, tag or branch specified with -u/--updaterev
609 b) the changeset, tag or branch given with the first -r/--rev
609 b) the changeset, tag or branch given with the first -r/--rev
610 c) the branch given with the first -b/--branch
610 c) the branch given with the first -b/--branch
611 d) the branch given with the url#branch source syntax
611 d) the branch given with the url#branch source syntax
612 e) the head of the default branch
612 e) the head of the default branch
613
613
614 Use 'hg clone -u . src dst' to checkout the source repository's
614 Use 'hg clone -u . src dst' to checkout the source repository's
615 parent changeset (applicable for local source repositories only).
615 parent changeset (applicable for local source repositories only).
616
616
617 A set of changesets (tags, or branch names) to pull may be specified
617 A set of changesets (tags, or branch names) to pull may be specified
618 by listing each changeset (tag, or branch name) with -r/--rev.
618 by listing each changeset (tag, or branch name) with -r/--rev.
619 If -r/--rev is used, the cloned repository will contain only a subset
619 If -r/--rev is used, the cloned repository will contain only a subset
620 of the changesets of the source repository. Only the set of changesets
620 of the changesets of the source repository. Only the set of changesets
621 defined by all -r/--rev options (including all their ancestors)
621 defined by all -r/--rev options (including all their ancestors)
622 will be pulled into the destination repository.
622 will be pulled into the destination repository.
623 No subsequent changesets (including subsequent tags) will be present
623 No subsequent changesets (including subsequent tags) will be present
624 in the destination.
624 in the destination.
625
625
626 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
626 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
627 local source repositories.
627 local source repositories.
628
628
629 For efficiency, hardlinks are used for cloning whenever the source
629 For efficiency, hardlinks are used for cloning whenever the source
630 and destination are on the same filesystem (note this applies only
630 and destination are on the same filesystem (note this applies only
631 to the repository data, not to the checked out files). Some
631 to the repository data, not to the checked out files). Some
632 filesystems, such as AFS, implement hardlinking incorrectly, but
632 filesystems, such as AFS, implement hardlinking incorrectly, but
633 do not report errors. In these cases, use the --pull option to
633 do not report errors. In these cases, use the --pull option to
634 avoid hardlinking.
634 avoid hardlinking.
635
635
636 In some cases, you can clone repositories and checked out files
636 In some cases, you can clone repositories and checked out files
637 using full hardlinks with ::
637 using full hardlinks with ::
638
638
639 $ cp -al REPO REPOCLONE
639 $ cp -al REPO REPOCLONE
640
640
641 This is the fastest way to clone, but it is not always safe. The
641 This is the fastest way to clone, but it is not always safe. The
642 operation is not atomic (making sure REPO is not modified during
642 operation is not atomic (making sure REPO is not modified during
643 the operation is up to you) and you have to make sure your editor
643 the operation is up to you) and you have to make sure your editor
644 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
644 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
645 this is not compatible with certain extensions that place their
645 this is not compatible with certain extensions that place their
646 metadata under the .hg directory, such as mq.
646 metadata under the .hg directory, such as mq.
647 """
647 """
648 if opts.get('noupdate') and opts.get('updaterev'):
648 if opts.get('noupdate') and opts.get('updaterev'):
649 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
649 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
650
650
651 hg.clone(cmdutil.remoteui(ui, opts), source, dest,
651 hg.clone(cmdutil.remoteui(ui, opts), source, dest,
652 pull=opts.get('pull'),
652 pull=opts.get('pull'),
653 stream=opts.get('uncompressed'),
653 stream=opts.get('uncompressed'),
654 rev=opts.get('rev'),
654 rev=opts.get('rev'),
655 update=opts.get('updaterev') or not opts.get('noupdate'),
655 update=opts.get('updaterev') or not opts.get('noupdate'),
656 branch=opts.get('branch'))
656 branch=opts.get('branch'))
657
657
658 def commit(ui, repo, *pats, **opts):
658 def commit(ui, repo, *pats, **opts):
659 """commit the specified files or all outstanding changes
659 """commit the specified files or all outstanding changes
660
660
661 Commit changes to the given files into the repository. Unlike a
661 Commit changes to the given files into the repository. Unlike a
662 centralized RCS, this operation is a local operation. See hg push
662 centralized RCS, this operation is a local operation. See hg push
663 for a way to actively distribute your changes.
663 for a way to actively distribute your changes.
664
664
665 If a list of files is omitted, all changes reported by "hg status"
665 If a list of files is omitted, all changes reported by "hg status"
666 will be committed.
666 will be committed.
667
667
668 If you are committing the result of a merge, do not provide any
668 If you are committing the result of a merge, do not provide any
669 filenames or -I/-X filters.
669 filenames or -I/-X filters.
670
670
671 If no commit message is specified, the configured editor is
671 If no commit message is specified, the configured editor is
672 started to prompt you for a message.
672 started to prompt you for a message.
673
673
674 See 'hg help dates' for a list of formats valid for -d/--date.
674 See 'hg help dates' for a list of formats valid for -d/--date.
675 """
675 """
676 extra = {}
676 extra = {}
677 if opts.get('close_branch'):
677 if opts.get('close_branch'):
678 extra['close'] = 1
678 extra['close'] = 1
679 e = cmdutil.commiteditor
679 e = cmdutil.commiteditor
680 if opts.get('force_editor'):
680 if opts.get('force_editor'):
681 e = cmdutil.commitforceeditor
681 e = cmdutil.commitforceeditor
682
682
683 def commitfunc(ui, repo, message, match, opts):
683 def commitfunc(ui, repo, message, match, opts):
684 return repo.commit(message, opts.get('user'), opts.get('date'), match,
684 return repo.commit(message, opts.get('user'), opts.get('date'), match,
685 editor=e, extra=extra)
685 editor=e, extra=extra)
686
686
687 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
687 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
688 if not node:
688 if not node:
689 ui.status(_("nothing changed\n"))
689 ui.status(_("nothing changed\n"))
690 return
690 return
691 cl = repo.changelog
691 cl = repo.changelog
692 rev = cl.rev(node)
692 rev = cl.rev(node)
693 parents = cl.parentrevs(rev)
693 parents = cl.parentrevs(rev)
694 if rev - 1 in parents:
694 if rev - 1 in parents:
695 # one of the parents was the old tip
695 # one of the parents was the old tip
696 pass
696 pass
697 elif (parents == (nullrev, nullrev) or
697 elif (parents == (nullrev, nullrev) or
698 len(cl.heads(cl.node(parents[0]))) > 1 and
698 len(cl.heads(cl.node(parents[0]))) > 1 and
699 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
699 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
700 ui.status(_('created new head\n'))
700 ui.status(_('created new head\n'))
701
701
702 if ui.debugflag:
702 if ui.debugflag:
703 ui.write(_('committed changeset %d:%s\n') % (rev, hex(node)))
703 ui.write(_('committed changeset %d:%s\n') % (rev, hex(node)))
704 elif ui.verbose:
704 elif ui.verbose:
705 ui.write(_('committed changeset %d:%s\n') % (rev, short(node)))
705 ui.write(_('committed changeset %d:%s\n') % (rev, short(node)))
706
706
707 def copy(ui, repo, *pats, **opts):
707 def copy(ui, repo, *pats, **opts):
708 """mark files as copied for the next commit
708 """mark files as copied for the next commit
709
709
710 Mark dest as having copies of source files. If dest is a
710 Mark dest as having copies of source files. If dest is a
711 directory, copies are put in that directory. If dest is a file,
711 directory, copies are put in that directory. If dest is a file,
712 the source must be a single file.
712 the source must be a single file.
713
713
714 By default, this command copies the contents of files as they
714 By default, this command copies the contents of files as they
715 exist in the working directory. If invoked with -A/--after, the
715 exist in the working directory. If invoked with -A/--after, the
716 operation is recorded, but no copying is performed.
716 operation is recorded, but no copying is performed.
717
717
718 This command takes effect with the next commit. To undo a copy
718 This command takes effect with the next commit. To undo a copy
719 before that, see hg revert.
719 before that, see hg revert.
720 """
720 """
721 wlock = repo.wlock(False)
721 wlock = repo.wlock(False)
722 try:
722 try:
723 return cmdutil.copy(ui, repo, pats, opts)
723 return cmdutil.copy(ui, repo, pats, opts)
724 finally:
724 finally:
725 wlock.release()
725 wlock.release()
726
726
727 def debugancestor(ui, repo, *args):
727 def debugancestor(ui, repo, *args):
728 """find the ancestor revision of two revisions in a given index"""
728 """find the ancestor revision of two revisions in a given index"""
729 if len(args) == 3:
729 if len(args) == 3:
730 index, rev1, rev2 = args
730 index, rev1, rev2 = args
731 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
731 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
732 lookup = r.lookup
732 lookup = r.lookup
733 elif len(args) == 2:
733 elif len(args) == 2:
734 if not repo:
734 if not repo:
735 raise util.Abort(_("There is no Mercurial repository here "
735 raise util.Abort(_("There is no Mercurial repository here "
736 "(.hg not found)"))
736 "(.hg not found)"))
737 rev1, rev2 = args
737 rev1, rev2 = args
738 r = repo.changelog
738 r = repo.changelog
739 lookup = repo.lookup
739 lookup = repo.lookup
740 else:
740 else:
741 raise util.Abort(_('either two or three arguments required'))
741 raise util.Abort(_('either two or three arguments required'))
742 a = r.ancestor(lookup(rev1), lookup(rev2))
742 a = r.ancestor(lookup(rev1), lookup(rev2))
743 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
743 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
744
744
745 def debugcommands(ui, cmd='', *args):
745 def debugcommands(ui, cmd='', *args):
746 for cmd, vals in sorted(table.iteritems()):
746 for cmd, vals in sorted(table.iteritems()):
747 cmd = cmd.split('|')[0].strip('^')
747 cmd = cmd.split('|')[0].strip('^')
748 opts = ', '.join([i[1] for i in vals[1]])
748 opts = ', '.join([i[1] for i in vals[1]])
749 ui.write('%s: %s\n' % (cmd, opts))
749 ui.write('%s: %s\n' % (cmd, opts))
750
750
751 def debugcomplete(ui, cmd='', **opts):
751 def debugcomplete(ui, cmd='', **opts):
752 """returns the completion list associated with the given command"""
752 """returns the completion list associated with the given command"""
753
753
754 if opts.get('options'):
754 if opts.get('options'):
755 options = []
755 options = []
756 otables = [globalopts]
756 otables = [globalopts]
757 if cmd:
757 if cmd:
758 aliases, entry = cmdutil.findcmd(cmd, table, False)
758 aliases, entry = cmdutil.findcmd(cmd, table, False)
759 otables.append(entry[1])
759 otables.append(entry[1])
760 for t in otables:
760 for t in otables:
761 for o in t:
761 for o in t:
762 if o[0]:
762 if o[0]:
763 options.append('-%s' % o[0])
763 options.append('-%s' % o[0])
764 options.append('--%s' % o[1])
764 options.append('--%s' % o[1])
765 ui.write("%s\n" % "\n".join(options))
765 ui.write("%s\n" % "\n".join(options))
766 return
766 return
767
767
768 cmdlist = cmdutil.findpossible(cmd, table)
768 cmdlist = cmdutil.findpossible(cmd, table)
769 if ui.verbose:
769 if ui.verbose:
770 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
770 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
771 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
771 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
772
772
773 def debugfsinfo(ui, path = "."):
773 def debugfsinfo(ui, path = "."):
774 open('.debugfsinfo', 'w').write('')
774 open('.debugfsinfo', 'w').write('')
775 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
775 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
776 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
776 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
777 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
777 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
778 and 'yes' or 'no'))
778 and 'yes' or 'no'))
779 os.unlink('.debugfsinfo')
779 os.unlink('.debugfsinfo')
780
780
781 def debugrebuildstate(ui, repo, rev="tip"):
781 def debugrebuildstate(ui, repo, rev="tip"):
782 """rebuild the dirstate as it would look like for the given revision"""
782 """rebuild the dirstate as it would look like for the given revision"""
783 ctx = repo[rev]
783 ctx = repo[rev]
784 wlock = repo.wlock()
784 wlock = repo.wlock()
785 try:
785 try:
786 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
786 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
787 finally:
787 finally:
788 wlock.release()
788 wlock.release()
789
789
790 def debugcheckstate(ui, repo):
790 def debugcheckstate(ui, repo):
791 """validate the correctness of the current dirstate"""
791 """validate the correctness of the current dirstate"""
792 parent1, parent2 = repo.dirstate.parents()
792 parent1, parent2 = repo.dirstate.parents()
793 m1 = repo[parent1].manifest()
793 m1 = repo[parent1].manifest()
794 m2 = repo[parent2].manifest()
794 m2 = repo[parent2].manifest()
795 errors = 0
795 errors = 0
796 for f in repo.dirstate:
796 for f in repo.dirstate:
797 state = repo.dirstate[f]
797 state = repo.dirstate[f]
798 if state in "nr" and f not in m1:
798 if state in "nr" and f not in m1:
799 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
799 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
800 errors += 1
800 errors += 1
801 if state in "a" and f in m1:
801 if state in "a" and f in m1:
802 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
802 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
803 errors += 1
803 errors += 1
804 if state in "m" and f not in m1 and f not in m2:
804 if state in "m" and f not in m1 and f not in m2:
805 ui.warn(_("%s in state %s, but not in either manifest\n") %
805 ui.warn(_("%s in state %s, but not in either manifest\n") %
806 (f, state))
806 (f, state))
807 errors += 1
807 errors += 1
808 for f in m1:
808 for f in m1:
809 state = repo.dirstate[f]
809 state = repo.dirstate[f]
810 if state not in "nrm":
810 if state not in "nrm":
811 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
811 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
812 errors += 1
812 errors += 1
813 if errors:
813 if errors:
814 error = _(".hg/dirstate inconsistent with current parent's manifest")
814 error = _(".hg/dirstate inconsistent with current parent's manifest")
815 raise util.Abort(error)
815 raise util.Abort(error)
816
816
817 def showconfig(ui, repo, *values, **opts):
817 def showconfig(ui, repo, *values, **opts):
818 """show combined config settings from all hgrc files
818 """show combined config settings from all hgrc files
819
819
820 With no arguments, print names and values of all config items.
820 With no arguments, print names and values of all config items.
821
821
822 With one argument of the form section.name, print just the value
822 With one argument of the form section.name, print just the value
823 of that config item.
823 of that config item.
824
824
825 With multiple arguments, print names and values of all config
825 With multiple arguments, print names and values of all config
826 items with matching section names.
826 items with matching section names.
827
827
828 With --debug, the source (filename and line number) is printed
828 With --debug, the source (filename and line number) is printed
829 for each config item.
829 for each config item.
830 """
830 """
831
831
832 untrusted = bool(opts.get('untrusted'))
832 untrusted = bool(opts.get('untrusted'))
833 if values:
833 if values:
834 if len([v for v in values if '.' in v]) > 1:
834 if len([v for v in values if '.' in v]) > 1:
835 raise util.Abort(_('only one config item permitted'))
835 raise util.Abort(_('only one config item permitted'))
836 for section, name, value in ui.walkconfig(untrusted=untrusted):
836 for section, name, value in ui.walkconfig(untrusted=untrusted):
837 sectname = section + '.' + name
837 sectname = section + '.' + name
838 if values:
838 if values:
839 for v in values:
839 for v in values:
840 if v == section:
840 if v == section:
841 ui.debug('%s: ' %
841 ui.debug('%s: ' %
842 ui.configsource(section, name, untrusted))
842 ui.configsource(section, name, untrusted))
843 ui.write('%s=%s\n' % (sectname, value))
843 ui.write('%s=%s\n' % (sectname, value))
844 elif v == sectname:
844 elif v == sectname:
845 ui.debug('%s: ' %
845 ui.debug('%s: ' %
846 ui.configsource(section, name, untrusted))
846 ui.configsource(section, name, untrusted))
847 ui.write(value, '\n')
847 ui.write(value, '\n')
848 else:
848 else:
849 ui.debug('%s: ' %
849 ui.debug('%s: ' %
850 ui.configsource(section, name, untrusted))
850 ui.configsource(section, name, untrusted))
851 ui.write('%s=%s\n' % (sectname, value))
851 ui.write('%s=%s\n' % (sectname, value))
852
852
853 def debugsetparents(ui, repo, rev1, rev2=None):
853 def debugsetparents(ui, repo, rev1, rev2=None):
854 """manually set the parents of the current working directory
854 """manually set the parents of the current working directory
855
855
856 This is useful for writing repository conversion tools, but should
856 This is useful for writing repository conversion tools, but should
857 be used with care.
857 be used with care.
858 """
858 """
859
859
860 if not rev2:
860 if not rev2:
861 rev2 = hex(nullid)
861 rev2 = hex(nullid)
862
862
863 wlock = repo.wlock()
863 wlock = repo.wlock()
864 try:
864 try:
865 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
865 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
866 finally:
866 finally:
867 wlock.release()
867 wlock.release()
868
868
869 def debugstate(ui, repo, nodates=None):
869 def debugstate(ui, repo, nodates=None):
870 """show the contents of the current dirstate"""
870 """show the contents of the current dirstate"""
871 timestr = ""
871 timestr = ""
872 showdate = not nodates
872 showdate = not nodates
873 for file_, ent in sorted(repo.dirstate._map.iteritems()):
873 for file_, ent in sorted(repo.dirstate._map.iteritems()):
874 if showdate:
874 if showdate:
875 if ent[3] == -1:
875 if ent[3] == -1:
876 # Pad or slice to locale representation
876 # Pad or slice to locale representation
877 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
877 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
878 time.localtime(0)))
878 time.localtime(0)))
879 timestr = 'unset'
879 timestr = 'unset'
880 timestr = (timestr[:locale_len] +
880 timestr = (timestr[:locale_len] +
881 ' ' * (locale_len - len(timestr)))
881 ' ' * (locale_len - len(timestr)))
882 else:
882 else:
883 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
883 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
884 time.localtime(ent[3]))
884 time.localtime(ent[3]))
885 if ent[1] & 020000:
885 if ent[1] & 020000:
886 mode = 'lnk'
886 mode = 'lnk'
887 else:
887 else:
888 mode = '%3o' % (ent[1] & 0777)
888 mode = '%3o' % (ent[1] & 0777)
889 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
889 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
890 for f in repo.dirstate.copies():
890 for f in repo.dirstate.copies():
891 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
891 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
892
892
893 def debugsub(ui, repo, rev=None):
893 def debugsub(ui, repo, rev=None):
894 if rev == '':
894 if rev == '':
895 rev = None
895 rev = None
896 for k, v in sorted(repo[rev].substate.items()):
896 for k, v in sorted(repo[rev].substate.items()):
897 ui.write('path %s\n' % k)
897 ui.write('path %s\n' % k)
898 ui.write(' source %s\n' % v[0])
898 ui.write(' source %s\n' % v[0])
899 ui.write(' revision %s\n' % v[1])
899 ui.write(' revision %s\n' % v[1])
900
900
901 def debugdata(ui, file_, rev):
901 def debugdata(ui, file_, rev):
902 """dump the contents of a data file revision"""
902 """dump the contents of a data file revision"""
903 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
903 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
904 try:
904 try:
905 ui.write(r.revision(r.lookup(rev)))
905 ui.write(r.revision(r.lookup(rev)))
906 except KeyError:
906 except KeyError:
907 raise util.Abort(_('invalid revision identifier %s') % rev)
907 raise util.Abort(_('invalid revision identifier %s') % rev)
908
908
909 def debugdate(ui, date, range=None, **opts):
909 def debugdate(ui, date, range=None, **opts):
910 """parse and display a date"""
910 """parse and display a date"""
911 if opts["extended"]:
911 if opts["extended"]:
912 d = util.parsedate(date, util.extendeddateformats)
912 d = util.parsedate(date, util.extendeddateformats)
913 else:
913 else:
914 d = util.parsedate(date)
914 d = util.parsedate(date)
915 ui.write("internal: %s %s\n" % d)
915 ui.write("internal: %s %s\n" % d)
916 ui.write("standard: %s\n" % util.datestr(d))
916 ui.write("standard: %s\n" % util.datestr(d))
917 if range:
917 if range:
918 m = util.matchdate(range)
918 m = util.matchdate(range)
919 ui.write("match: %s\n" % m(d[0]))
919 ui.write("match: %s\n" % m(d[0]))
920
920
921 def debugindex(ui, file_):
921 def debugindex(ui, file_):
922 """dump the contents of an index file"""
922 """dump the contents of an index file"""
923 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
923 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
924 ui.write(" rev offset length base linkrev"
924 ui.write(" rev offset length base linkrev"
925 " nodeid p1 p2\n")
925 " nodeid p1 p2\n")
926 for i in r:
926 for i in r:
927 node = r.node(i)
927 node = r.node(i)
928 try:
928 try:
929 pp = r.parents(node)
929 pp = r.parents(node)
930 except:
930 except:
931 pp = [nullid, nullid]
931 pp = [nullid, nullid]
932 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
932 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
933 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
933 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
934 short(node), short(pp[0]), short(pp[1])))
934 short(node), short(pp[0]), short(pp[1])))
935
935
936 def debugindexdot(ui, file_):
936 def debugindexdot(ui, file_):
937 """dump an index DAG as a graphviz dot file"""
937 """dump an index DAG as a graphviz dot file"""
938 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
938 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
939 ui.write("digraph G {\n")
939 ui.write("digraph G {\n")
940 for i in r:
940 for i in r:
941 node = r.node(i)
941 node = r.node(i)
942 pp = r.parents(node)
942 pp = r.parents(node)
943 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
943 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
944 if pp[1] != nullid:
944 if pp[1] != nullid:
945 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
945 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
946 ui.write("}\n")
946 ui.write("}\n")
947
947
948 def debuginstall(ui):
948 def debuginstall(ui):
949 '''test Mercurial installation'''
949 '''test Mercurial installation'''
950
950
951 def writetemp(contents):
951 def writetemp(contents):
952 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
952 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
953 f = os.fdopen(fd, "wb")
953 f = os.fdopen(fd, "wb")
954 f.write(contents)
954 f.write(contents)
955 f.close()
955 f.close()
956 return name
956 return name
957
957
958 problems = 0
958 problems = 0
959
959
960 # encoding
960 # encoding
961 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
961 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
962 try:
962 try:
963 encoding.fromlocal("test")
963 encoding.fromlocal("test")
964 except util.Abort, inst:
964 except util.Abort, inst:
965 ui.write(" %s\n" % inst)
965 ui.write(" %s\n" % inst)
966 ui.write(_(" (check that your locale is properly set)\n"))
966 ui.write(_(" (check that your locale is properly set)\n"))
967 problems += 1
967 problems += 1
968
968
969 # compiled modules
969 # compiled modules
970 ui.status(_("Checking extensions...\n"))
970 ui.status(_("Checking extensions...\n"))
971 try:
971 try:
972 import bdiff, mpatch, base85
972 import bdiff, mpatch, base85
973 except Exception, inst:
973 except Exception, inst:
974 ui.write(" %s\n" % inst)
974 ui.write(" %s\n" % inst)
975 ui.write(_(" One or more extensions could not be found"))
975 ui.write(_(" One or more extensions could not be found"))
976 ui.write(_(" (check that you compiled the extensions)\n"))
976 ui.write(_(" (check that you compiled the extensions)\n"))
977 problems += 1
977 problems += 1
978
978
979 # templates
979 # templates
980 ui.status(_("Checking templates...\n"))
980 ui.status(_("Checking templates...\n"))
981 try:
981 try:
982 import templater
982 import templater
983 templater.templater(templater.templatepath("map-cmdline.default"))
983 templater.templater(templater.templatepath("map-cmdline.default"))
984 except Exception, inst:
984 except Exception, inst:
985 ui.write(" %s\n" % inst)
985 ui.write(" %s\n" % inst)
986 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
986 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
987 problems += 1
987 problems += 1
988
988
989 # patch
989 # patch
990 ui.status(_("Checking patch...\n"))
990 ui.status(_("Checking patch...\n"))
991 patchproblems = 0
991 patchproblems = 0
992 a = "1\n2\n3\n4\n"
992 a = "1\n2\n3\n4\n"
993 b = "1\n2\n3\ninsert\n4\n"
993 b = "1\n2\n3\ninsert\n4\n"
994 fa = writetemp(a)
994 fa = writetemp(a)
995 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
995 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
996 os.path.basename(fa))
996 os.path.basename(fa))
997 fd = writetemp(d)
997 fd = writetemp(d)
998
998
999 files = {}
999 files = {}
1000 try:
1000 try:
1001 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
1001 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
1002 except util.Abort, e:
1002 except util.Abort, e:
1003 ui.write(_(" patch call failed:\n"))
1003 ui.write(_(" patch call failed:\n"))
1004 ui.write(" " + str(e) + "\n")
1004 ui.write(" " + str(e) + "\n")
1005 patchproblems += 1
1005 patchproblems += 1
1006 else:
1006 else:
1007 if list(files) != [os.path.basename(fa)]:
1007 if list(files) != [os.path.basename(fa)]:
1008 ui.write(_(" unexpected patch output!\n"))
1008 ui.write(_(" unexpected patch output!\n"))
1009 patchproblems += 1
1009 patchproblems += 1
1010 a = open(fa).read()
1010 a = open(fa).read()
1011 if a != b:
1011 if a != b:
1012 ui.write(_(" patch test failed!\n"))
1012 ui.write(_(" patch test failed!\n"))
1013 patchproblems += 1
1013 patchproblems += 1
1014
1014
1015 if patchproblems:
1015 if patchproblems:
1016 if ui.config('ui', 'patch'):
1016 if ui.config('ui', 'patch'):
1017 ui.write(_(" (Current patch tool may be incompatible with patch,"
1017 ui.write(_(" (Current patch tool may be incompatible with patch,"
1018 " or misconfigured. Please check your .hgrc file)\n"))
1018 " or misconfigured. Please check your .hgrc file)\n"))
1019 else:
1019 else:
1020 ui.write(_(" Internal patcher failure, please report this error"
1020 ui.write(_(" Internal patcher failure, please report this error"
1021 " to http://mercurial.selenic.com/bts/\n"))
1021 " to http://mercurial.selenic.com/bts/\n"))
1022 problems += patchproblems
1022 problems += patchproblems
1023
1023
1024 os.unlink(fa)
1024 os.unlink(fa)
1025 os.unlink(fd)
1025 os.unlink(fd)
1026
1026
1027 # editor
1027 # editor
1028 ui.status(_("Checking commit editor...\n"))
1028 ui.status(_("Checking commit editor...\n"))
1029 editor = ui.geteditor()
1029 editor = ui.geteditor()
1030 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1030 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1031 if not cmdpath:
1031 if not cmdpath:
1032 if editor == 'vi':
1032 if editor == 'vi':
1033 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1033 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1034 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1034 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1035 else:
1035 else:
1036 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1036 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1037 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1037 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1038 problems += 1
1038 problems += 1
1039
1039
1040 # check username
1040 # check username
1041 ui.status(_("Checking username...\n"))
1041 ui.status(_("Checking username...\n"))
1042 try:
1042 try:
1043 user = ui.username()
1043 user = ui.username()
1044 except util.Abort, e:
1044 except util.Abort, e:
1045 ui.write(" %s\n" % e)
1045 ui.write(" %s\n" % e)
1046 ui.write(_(" (specify a username in your .hgrc file)\n"))
1046 ui.write(_(" (specify a username in your .hgrc file)\n"))
1047 problems += 1
1047 problems += 1
1048
1048
1049 if not problems:
1049 if not problems:
1050 ui.status(_("No problems detected\n"))
1050 ui.status(_("No problems detected\n"))
1051 else:
1051 else:
1052 ui.write(_("%s problems detected,"
1052 ui.write(_("%s problems detected,"
1053 " please check your install!\n") % problems)
1053 " please check your install!\n") % problems)
1054
1054
1055 return problems
1055 return problems
1056
1056
1057 def debugrename(ui, repo, file1, *pats, **opts):
1057 def debugrename(ui, repo, file1, *pats, **opts):
1058 """dump rename information"""
1058 """dump rename information"""
1059
1059
1060 ctx = repo[opts.get('rev')]
1060 ctx = repo[opts.get('rev')]
1061 m = cmdutil.match(repo, (file1,) + pats, opts)
1061 m = cmdutil.match(repo, (file1,) + pats, opts)
1062 for abs in ctx.walk(m):
1062 for abs in ctx.walk(m):
1063 fctx = ctx[abs]
1063 fctx = ctx[abs]
1064 o = fctx.filelog().renamed(fctx.filenode())
1064 o = fctx.filelog().renamed(fctx.filenode())
1065 rel = m.rel(abs)
1065 rel = m.rel(abs)
1066 if o:
1066 if o:
1067 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1067 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1068 else:
1068 else:
1069 ui.write(_("%s not renamed\n") % rel)
1069 ui.write(_("%s not renamed\n") % rel)
1070
1070
1071 def debugwalk(ui, repo, *pats, **opts):
1071 def debugwalk(ui, repo, *pats, **opts):
1072 """show how files match on given patterns"""
1072 """show how files match on given patterns"""
1073 m = cmdutil.match(repo, pats, opts)
1073 m = cmdutil.match(repo, pats, opts)
1074 items = list(repo.walk(m))
1074 items = list(repo.walk(m))
1075 if not items:
1075 if not items:
1076 return
1076 return
1077 fmt = 'f %%-%ds %%-%ds %%s' % (
1077 fmt = 'f %%-%ds %%-%ds %%s' % (
1078 max([len(abs) for abs in items]),
1078 max([len(abs) for abs in items]),
1079 max([len(m.rel(abs)) for abs in items]))
1079 max([len(m.rel(abs)) for abs in items]))
1080 for abs in items:
1080 for abs in items:
1081 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1081 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1082 ui.write("%s\n" % line.rstrip())
1082 ui.write("%s\n" % line.rstrip())
1083
1083
1084 def diff(ui, repo, *pats, **opts):
1084 def diff(ui, repo, *pats, **opts):
1085 """diff repository (or selected files)
1085 """diff repository (or selected files)
1086
1086
1087 Show differences between revisions for the specified files.
1087 Show differences between revisions for the specified files.
1088
1088
1089 Differences between files are shown using the unified diff format.
1089 Differences between files are shown using the unified diff format.
1090
1090
1091 NOTE: diff may generate unexpected results for merges, as it will
1091 NOTE: diff may generate unexpected results for merges, as it will
1092 default to comparing against the working directory's first parent
1092 default to comparing against the working directory's first parent
1093 changeset if no revisions are specified.
1093 changeset if no revisions are specified.
1094
1094
1095 When two revision arguments are given, then changes are shown
1095 When two revision arguments are given, then changes are shown
1096 between those revisions. If only one revision is specified then
1096 between those revisions. If only one revision is specified then
1097 that revision is compared to the working directory, and, when no
1097 that revision is compared to the working directory, and, when no
1098 revisions are specified, the working directory files are compared
1098 revisions are specified, the working directory files are compared
1099 to its parent.
1099 to its parent.
1100
1100
1101 Without the -a/--text option, diff will avoid generating diffs of
1101 Without the -a/--text option, diff will avoid generating diffs of
1102 files it detects as binary. With -a, diff will generate a diff
1102 files it detects as binary. With -a, diff will generate a diff
1103 anyway, probably with undesirable results.
1103 anyway, probably with undesirable results.
1104
1104
1105 Use the -g/--git option to generate diffs in the git extended diff
1105 Use the -g/--git option to generate diffs in the git extended diff
1106 format. For more information, read 'hg help diffs'.
1106 format. For more information, read 'hg help diffs'.
1107 """
1107 """
1108
1108
1109 revs = opts.get('rev')
1109 revs = opts.get('rev')
1110 change = opts.get('change')
1110 change = opts.get('change')
1111 stat = opts.get('stat')
1111 stat = opts.get('stat')
1112 reverse = opts.get('reverse')
1112 reverse = opts.get('reverse')
1113
1113
1114 if revs and change:
1114 if revs and change:
1115 msg = _('cannot specify --rev and --change at the same time')
1115 msg = _('cannot specify --rev and --change at the same time')
1116 raise util.Abort(msg)
1116 raise util.Abort(msg)
1117 elif change:
1117 elif change:
1118 node2 = repo.lookup(change)
1118 node2 = repo.lookup(change)
1119 node1 = repo[node2].parents()[0].node()
1119 node1 = repo[node2].parents()[0].node()
1120 else:
1120 else:
1121 node1, node2 = cmdutil.revpair(repo, revs)
1121 node1, node2 = cmdutil.revpair(repo, revs)
1122
1122
1123 if reverse:
1123 if reverse:
1124 node1, node2 = node2, node1
1124 node1, node2 = node2, node1
1125
1125
1126 if stat:
1126 if stat:
1127 opts['unified'] = '0'
1127 opts['unified'] = '0'
1128 diffopts = patch.diffopts(ui, opts)
1128 diffopts = patch.diffopts(ui, opts)
1129
1129
1130 m = cmdutil.match(repo, pats, opts)
1130 m = cmdutil.match(repo, pats, opts)
1131 it = patch.diff(repo, node1, node2, match=m, opts=diffopts)
1131 it = patch.diff(repo, node1, node2, match=m, opts=diffopts)
1132 if stat:
1132 if stat:
1133 width = ui.interactive() and util.termwidth() or 80
1133 width = ui.interactive() and util.termwidth() or 80
1134 ui.write(patch.diffstat(util.iterlines(it), width=width,
1134 ui.write(patch.diffstat(util.iterlines(it), width=width,
1135 git=diffopts.git))
1135 git=diffopts.git))
1136 else:
1136 else:
1137 for chunk in it:
1137 for chunk in it:
1138 ui.write(chunk)
1138 ui.write(chunk)
1139
1139
1140 def export(ui, repo, *changesets, **opts):
1140 def export(ui, repo, *changesets, **opts):
1141 """dump the header and diffs for one or more changesets
1141 """dump the header and diffs for one or more changesets
1142
1142
1143 Print the changeset header and diffs for one or more revisions.
1143 Print the changeset header and diffs for one or more revisions.
1144
1144
1145 The information shown in the changeset header is: author, date,
1145 The information shown in the changeset header is: author, date,
1146 branch name (if non-default), changeset hash, parent(s) and commit
1146 branch name (if non-default), changeset hash, parent(s) and commit
1147 comment.
1147 comment.
1148
1148
1149 NOTE: export may generate unexpected diff output for merge
1149 NOTE: export may generate unexpected diff output for merge
1150 changesets, as it will compare the merge changeset against its
1150 changesets, as it will compare the merge changeset against its
1151 first parent only.
1151 first parent only.
1152
1152
1153 Output may be to a file, in which case the name of the file is
1153 Output may be to a file, in which case the name of the file is
1154 given using a format string. The formatting rules are as follows:
1154 given using a format string. The formatting rules are as follows:
1155
1155
1156 :``%%``: literal "%" character
1156 :``%%``: literal "%" character
1157 :``%H``: changeset hash (40 bytes of hexadecimal)
1157 :``%H``: changeset hash (40 bytes of hexadecimal)
1158 :``%N``: number of patches being generated
1158 :``%N``: number of patches being generated
1159 :``%R``: changeset revision number
1159 :``%R``: changeset revision number
1160 :``%b``: basename of the exporting repository
1160 :``%b``: basename of the exporting repository
1161 :``%h``: short-form changeset hash (12 bytes of hexadecimal)
1161 :``%h``: short-form changeset hash (12 bytes of hexadecimal)
1162 :``%n``: zero-padded sequence number, starting at 1
1162 :``%n``: zero-padded sequence number, starting at 1
1163 :``%r``: zero-padded changeset revision number
1163 :``%r``: zero-padded changeset revision number
1164
1164
1165 Without the -a/--text option, export will avoid generating diffs
1165 Without the -a/--text option, export will avoid generating diffs
1166 of files it detects as binary. With -a, export will generate a
1166 of files it detects as binary. With -a, export will generate a
1167 diff anyway, probably with undesirable results.
1167 diff anyway, probably with undesirable results.
1168
1168
1169 Use the -g/--git option to generate diffs in the git extended diff
1169 Use the -g/--git option to generate diffs in the git extended diff
1170 format. See 'hg help diffs' for more information.
1170 format. See 'hg help diffs' for more information.
1171
1171
1172 With the --switch-parent option, the diff will be against the
1172 With the --switch-parent option, the diff will be against the
1173 second parent. It can be useful to review a merge.
1173 second parent. It can be useful to review a merge.
1174 """
1174 """
1175 changesets += tuple(opts.get('rev', []))
1175 changesets += tuple(opts.get('rev', []))
1176 if not changesets:
1176 if not changesets:
1177 raise util.Abort(_("export requires at least one changeset"))
1177 raise util.Abort(_("export requires at least one changeset"))
1178 revs = cmdutil.revrange(repo, changesets)
1178 revs = cmdutil.revrange(repo, changesets)
1179 if len(revs) > 1:
1179 if len(revs) > 1:
1180 ui.note(_('exporting patches:\n'))
1180 ui.note(_('exporting patches:\n'))
1181 else:
1181 else:
1182 ui.note(_('exporting patch:\n'))
1182 ui.note(_('exporting patch:\n'))
1183 patch.export(repo, revs, template=opts.get('output'),
1183 patch.export(repo, revs, template=opts.get('output'),
1184 switch_parent=opts.get('switch_parent'),
1184 switch_parent=opts.get('switch_parent'),
1185 opts=patch.diffopts(ui, opts))
1185 opts=patch.diffopts(ui, opts))
1186
1186
1187 def forget(ui, repo, *pats, **opts):
1187 def forget(ui, repo, *pats, **opts):
1188 """forget the specified files on the next commit
1188 """forget the specified files on the next commit
1189
1189
1190 Mark the specified files so they will no longer be tracked
1190 Mark the specified files so they will no longer be tracked
1191 after the next commit.
1191 after the next commit.
1192
1192
1193 This only removes files from the current branch, not from the
1193 This only removes files from the current branch, not from the
1194 entire project history, and it does not delete them from the
1194 entire project history, and it does not delete them from the
1195 working directory.
1195 working directory.
1196
1196
1197 To undo a forget before the next commit, see hg add.
1197 To undo a forget before the next commit, see hg add.
1198 """
1198 """
1199
1199
1200 if not pats:
1200 if not pats:
1201 raise util.Abort(_('no files specified'))
1201 raise util.Abort(_('no files specified'))
1202
1202
1203 m = cmdutil.match(repo, pats, opts)
1203 m = cmdutil.match(repo, pats, opts)
1204 s = repo.status(match=m, clean=True)
1204 s = repo.status(match=m, clean=True)
1205 forget = sorted(s[0] + s[1] + s[3] + s[6])
1205 forget = sorted(s[0] + s[1] + s[3] + s[6])
1206
1206
1207 for f in m.files():
1207 for f in m.files():
1208 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1208 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1209 ui.warn(_('not removing %s: file is already untracked\n')
1209 ui.warn(_('not removing %s: file is already untracked\n')
1210 % m.rel(f))
1210 % m.rel(f))
1211
1211
1212 for f in forget:
1212 for f in forget:
1213 if ui.verbose or not m.exact(f):
1213 if ui.verbose or not m.exact(f):
1214 ui.status(_('removing %s\n') % m.rel(f))
1214 ui.status(_('removing %s\n') % m.rel(f))
1215
1215
1216 repo.remove(forget, unlink=False)
1216 repo.remove(forget, unlink=False)
1217
1217
1218 def grep(ui, repo, pattern, *pats, **opts):
1218 def grep(ui, repo, pattern, *pats, **opts):
1219 """search for a pattern in specified files and revisions
1219 """search for a pattern in specified files and revisions
1220
1220
1221 Search revisions of files for a regular expression.
1221 Search revisions of files for a regular expression.
1222
1222
1223 This command behaves differently than Unix grep. It only accepts
1223 This command behaves differently than Unix grep. It only accepts
1224 Python/Perl regexps. It searches repository history, not the
1224 Python/Perl regexps. It searches repository history, not the
1225 working directory. It always prints the revision number in which a
1225 working directory. It always prints the revision number in which a
1226 match appears.
1226 match appears.
1227
1227
1228 By default, grep only prints output for the first revision of a
1228 By default, grep only prints output for the first revision of a
1229 file in which it finds a match. To get it to print every revision
1229 file in which it finds a match. To get it to print every revision
1230 that contains a change in match status ("-" for a match that
1230 that contains a change in match status ("-" for a match that
1231 becomes a non-match, or "+" for a non-match that becomes a match),
1231 becomes a non-match, or "+" for a non-match that becomes a match),
1232 use the --all flag.
1232 use the --all flag.
1233 """
1233 """
1234 reflags = 0
1234 reflags = 0
1235 if opts.get('ignore_case'):
1235 if opts.get('ignore_case'):
1236 reflags |= re.I
1236 reflags |= re.I
1237 try:
1237 try:
1238 regexp = re.compile(pattern, reflags)
1238 regexp = re.compile(pattern, reflags)
1239 except Exception, inst:
1239 except Exception, inst:
1240 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1240 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1241 return None
1241 return None
1242 sep, eol = ':', '\n'
1242 sep, eol = ':', '\n'
1243 if opts.get('print0'):
1243 if opts.get('print0'):
1244 sep = eol = '\0'
1244 sep = eol = '\0'
1245
1245
1246 getfile = util.lrucachefunc(repo.file)
1246 getfile = util.lrucachefunc(repo.file)
1247
1247
1248 def matchlines(body):
1248 def matchlines(body):
1249 begin = 0
1249 begin = 0
1250 linenum = 0
1250 linenum = 0
1251 while True:
1251 while True:
1252 match = regexp.search(body, begin)
1252 match = regexp.search(body, begin)
1253 if not match:
1253 if not match:
1254 break
1254 break
1255 mstart, mend = match.span()
1255 mstart, mend = match.span()
1256 linenum += body.count('\n', begin, mstart) + 1
1256 linenum += body.count('\n', begin, mstart) + 1
1257 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1257 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1258 begin = body.find('\n', mend) + 1 or len(body)
1258 begin = body.find('\n', mend) + 1 or len(body)
1259 lend = begin - 1
1259 lend = begin - 1
1260 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1260 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1261
1261
1262 class linestate(object):
1262 class linestate(object):
1263 def __init__(self, line, linenum, colstart, colend):
1263 def __init__(self, line, linenum, colstart, colend):
1264 self.line = line
1264 self.line = line
1265 self.linenum = linenum
1265 self.linenum = linenum
1266 self.colstart = colstart
1266 self.colstart = colstart
1267 self.colend = colend
1267 self.colend = colend
1268
1268
1269 def __hash__(self):
1269 def __hash__(self):
1270 return hash((self.linenum, self.line))
1270 return hash((self.linenum, self.line))
1271
1271
1272 def __eq__(self, other):
1272 def __eq__(self, other):
1273 return self.line == other.line
1273 return self.line == other.line
1274
1274
1275 matches = {}
1275 matches = {}
1276 copies = {}
1276 copies = {}
1277 def grepbody(fn, rev, body):
1277 def grepbody(fn, rev, body):
1278 matches[rev].setdefault(fn, [])
1278 matches[rev].setdefault(fn, [])
1279 m = matches[rev][fn]
1279 m = matches[rev][fn]
1280 for lnum, cstart, cend, line in matchlines(body):
1280 for lnum, cstart, cend, line in matchlines(body):
1281 s = linestate(line, lnum, cstart, cend)
1281 s = linestate(line, lnum, cstart, cend)
1282 m.append(s)
1282 m.append(s)
1283
1283
1284 def difflinestates(a, b):
1284 def difflinestates(a, b):
1285 sm = difflib.SequenceMatcher(None, a, b)
1285 sm = difflib.SequenceMatcher(None, a, b)
1286 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1286 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1287 if tag == 'insert':
1287 if tag == 'insert':
1288 for i in xrange(blo, bhi):
1288 for i in xrange(blo, bhi):
1289 yield ('+', b[i])
1289 yield ('+', b[i])
1290 elif tag == 'delete':
1290 elif tag == 'delete':
1291 for i in xrange(alo, ahi):
1291 for i in xrange(alo, ahi):
1292 yield ('-', a[i])
1292 yield ('-', a[i])
1293 elif tag == 'replace':
1293 elif tag == 'replace':
1294 for i in xrange(alo, ahi):
1294 for i in xrange(alo, ahi):
1295 yield ('-', a[i])
1295 yield ('-', a[i])
1296 for i in xrange(blo, bhi):
1296 for i in xrange(blo, bhi):
1297 yield ('+', b[i])
1297 yield ('+', b[i])
1298
1298
1299 def display(fn, ctx, pstates, states):
1299 def display(fn, ctx, pstates, states):
1300 rev = ctx.rev()
1300 rev = ctx.rev()
1301 datefunc = ui.quiet and util.shortdate or util.datestr
1301 datefunc = ui.quiet and util.shortdate or util.datestr
1302 found = False
1302 found = False
1303 filerevmatches = {}
1303 filerevmatches = {}
1304 if opts.get('all'):
1304 if opts.get('all'):
1305 iter = difflinestates(pstates, states)
1305 iter = difflinestates(pstates, states)
1306 else:
1306 else:
1307 iter = [('', l) for l in states]
1307 iter = [('', l) for l in states]
1308 for change, l in iter:
1308 for change, l in iter:
1309 cols = [fn, str(rev)]
1309 cols = [fn, str(rev)]
1310 if opts.get('line_number'):
1310 if opts.get('line_number'):
1311 cols.append(str(l.linenum))
1311 cols.append(str(l.linenum))
1312 if opts.get('all'):
1312 if opts.get('all'):
1313 cols.append(change)
1313 cols.append(change)
1314 if opts.get('user'):
1314 if opts.get('user'):
1315 cols.append(ui.shortuser(ctx.user()))
1315 cols.append(ui.shortuser(ctx.user()))
1316 if opts.get('date'):
1316 if opts.get('date'):
1317 cols.append(datefunc(ctx.date()))
1317 cols.append(datefunc(ctx.date()))
1318 if opts.get('files_with_matches'):
1318 if opts.get('files_with_matches'):
1319 c = (fn, rev)
1319 c = (fn, rev)
1320 if c in filerevmatches:
1320 if c in filerevmatches:
1321 continue
1321 continue
1322 filerevmatches[c] = 1
1322 filerevmatches[c] = 1
1323 else:
1323 else:
1324 cols.append(l.line)
1324 cols.append(l.line)
1325 ui.write(sep.join(cols), eol)
1325 ui.write(sep.join(cols), eol)
1326 found = True
1326 found = True
1327 return found
1327 return found
1328
1328
1329 skip = {}
1329 skip = {}
1330 revfiles = {}
1330 revfiles = {}
1331 matchfn = cmdutil.match(repo, pats, opts)
1331 matchfn = cmdutil.match(repo, pats, opts)
1332 found = False
1332 found = False
1333 follow = opts.get('follow')
1333 follow = opts.get('follow')
1334
1334
1335 def prep(ctx, fns):
1335 def prep(ctx, fns):
1336 rev = ctx.rev()
1336 rev = ctx.rev()
1337 pctx = ctx.parents()[0]
1337 pctx = ctx.parents()[0]
1338 parent = pctx.rev()
1338 parent = pctx.rev()
1339 matches.setdefault(rev, {})
1339 matches.setdefault(rev, {})
1340 matches.setdefault(parent, {})
1340 matches.setdefault(parent, {})
1341 files = revfiles.setdefault(rev, [])
1341 files = revfiles.setdefault(rev, [])
1342 for fn in fns:
1342 for fn in fns:
1343 flog = getfile(fn)
1343 flog = getfile(fn)
1344 try:
1344 try:
1345 fnode = ctx.filenode(fn)
1345 fnode = ctx.filenode(fn)
1346 except error.LookupError:
1346 except error.LookupError:
1347 continue
1347 continue
1348
1348
1349 copied = flog.renamed(fnode)
1349 copied = flog.renamed(fnode)
1350 copy = follow and copied and copied[0]
1350 copy = follow and copied and copied[0]
1351 if copy:
1351 if copy:
1352 copies.setdefault(rev, {})[fn] = copy
1352 copies.setdefault(rev, {})[fn] = copy
1353 if fn in skip:
1353 if fn in skip:
1354 if copy:
1354 if copy:
1355 skip[copy] = True
1355 skip[copy] = True
1356 continue
1356 continue
1357 files.append(fn)
1357 files.append(fn)
1358
1358
1359 if fn not in matches[rev]:
1359 if fn not in matches[rev]:
1360 grepbody(fn, rev, flog.read(fnode))
1360 grepbody(fn, rev, flog.read(fnode))
1361
1361
1362 pfn = copy or fn
1362 pfn = copy or fn
1363 if pfn not in matches[parent]:
1363 if pfn not in matches[parent]:
1364 try:
1364 try:
1365 fnode = pctx.filenode(pfn)
1365 fnode = pctx.filenode(pfn)
1366 grepbody(pfn, parent, flog.read(fnode))
1366 grepbody(pfn, parent, flog.read(fnode))
1367 except error.LookupError:
1367 except error.LookupError:
1368 pass
1368 pass
1369
1369
1370 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
1370 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
1371 rev = ctx.rev()
1371 rev = ctx.rev()
1372 parent = ctx.parents()[0].rev()
1372 parent = ctx.parents()[0].rev()
1373 for fn in sorted(revfiles.get(rev, [])):
1373 for fn in sorted(revfiles.get(rev, [])):
1374 states = matches[rev][fn]
1374 states = matches[rev][fn]
1375 copy = copies.get(rev, {}).get(fn)
1375 copy = copies.get(rev, {}).get(fn)
1376 if fn in skip:
1376 if fn in skip:
1377 if copy:
1377 if copy:
1378 skip[copy] = True
1378 skip[copy] = True
1379 continue
1379 continue
1380 pstates = matches.get(parent, {}).get(copy or fn, [])
1380 pstates = matches.get(parent, {}).get(copy or fn, [])
1381 if pstates or states:
1381 if pstates or states:
1382 r = display(fn, ctx, pstates, states)
1382 r = display(fn, ctx, pstates, states)
1383 found = found or r
1383 found = found or r
1384 if r and not opts.get('all'):
1384 if r and not opts.get('all'):
1385 skip[fn] = True
1385 skip[fn] = True
1386 if copy:
1386 if copy:
1387 skip[copy] = True
1387 skip[copy] = True
1388 del matches[rev]
1388 del matches[rev]
1389 del revfiles[rev]
1389 del revfiles[rev]
1390
1390
1391 def heads(ui, repo, *branchrevs, **opts):
1391 def heads(ui, repo, *branchrevs, **opts):
1392 """show current repository heads or show branch heads
1392 """show current repository heads or show branch heads
1393
1393
1394 With no arguments, show all repository branch heads.
1394 With no arguments, show all repository branch heads.
1395
1395
1396 Repository "heads" are changesets with no child changesets. They are
1396 Repository "heads" are changesets with no child changesets. They are
1397 where development generally takes place and are the usual targets
1397 where development generally takes place and are the usual targets
1398 for update and merge operations. Branch heads are changesets that have
1398 for update and merge operations. Branch heads are changesets that have
1399 no child changeset on the same branch.
1399 no child changeset on the same branch.
1400
1400
1401 If one or more REVs are given, only branch heads on the branches
1401 If one or more REVs are given, only branch heads on the branches
1402 associated with the specified changesets are shown.
1402 associated with the specified changesets are shown.
1403
1403
1404 If -c/--closed is specified, also show branch heads marked closed
1404 If -c/--closed is specified, also show branch heads marked closed
1405 (see hg commit --close-branch).
1405 (see hg commit --close-branch).
1406
1406
1407 If STARTREV is specified, only those heads that are descendants of
1407 If STARTREV is specified, only those heads that are descendants of
1408 STARTREV will be displayed.
1408 STARTREV will be displayed.
1409
1409
1410 If -t/--topo is specified, named branch mechanics will be ignored and only
1410 If -t/--topo is specified, named branch mechanics will be ignored and only
1411 changesets without children will be shown.
1411 changesets without children will be shown.
1412 """
1412 """
1413
1413
1414 if opts.get('rev'):
1414 if opts.get('rev'):
1415 start = repo.lookup(opts['rev'])
1415 start = repo.lookup(opts['rev'])
1416 else:
1416 else:
1417 start = None
1417 start = None
1418
1418
1419 if opts.get('topo'):
1419 if opts.get('topo'):
1420 heads = [repo[h] for h in repo.heads(start)]
1420 heads = [repo[h] for h in repo.heads(start)]
1421 else:
1421 else:
1422 heads = []
1422 heads = []
1423 for b, ls in repo.branchmap().iteritems():
1423 for b, ls in repo.branchmap().iteritems():
1424 if start is None:
1424 if start is None:
1425 heads += [repo[h] for h in ls]
1425 heads += [repo[h] for h in ls]
1426 continue
1426 continue
1427 startrev = repo.changelog.rev(start)
1427 startrev = repo.changelog.rev(start)
1428 descendants = set(repo.changelog.descendants(startrev))
1428 descendants = set(repo.changelog.descendants(startrev))
1429 descendants.add(startrev)
1429 descendants.add(startrev)
1430 rev = repo.changelog.rev
1430 rev = repo.changelog.rev
1431 heads += [repo[h] for h in ls if rev(h) in descendants]
1431 heads += [repo[h] for h in ls if rev(h) in descendants]
1432
1432
1433 if branchrevs:
1433 if branchrevs:
1434 decode, encode = encoding.fromlocal, encoding.tolocal
1434 decode, encode = encoding.fromlocal, encoding.tolocal
1435 branches = set(repo[decode(br)].branch() for br in branchrevs)
1435 branches = set(repo[decode(br)].branch() for br in branchrevs)
1436 heads = [h for h in heads if h.branch() in branches]
1436 heads = [h for h in heads if h.branch() in branches]
1437
1437
1438 if not opts.get('closed'):
1438 if not opts.get('closed'):
1439 heads = [h for h in heads if not h.extra().get('close')]
1439 heads = [h for h in heads if not h.extra().get('close')]
1440
1440
1441 if opts.get('active') and branchrevs:
1441 if opts.get('active') and branchrevs:
1442 dagheads = repo.heads(start)
1442 dagheads = repo.heads(start)
1443 heads = [h for h in heads if h.node() in dagheads]
1443 heads = [h for h in heads if h.node() in dagheads]
1444
1444
1445 if branchrevs:
1445 if branchrevs:
1446 haveheads = set(h.branch() for h in heads)
1446 haveheads = set(h.branch() for h in heads)
1447 if branches - haveheads:
1447 if branches - haveheads:
1448 headless = ', '.join(encode(b) for b in branches - haveheads)
1448 headless = ', '.join(encode(b) for b in branches - haveheads)
1449 msg = _('no open branch heads found on branches %s')
1449 msg = _('no open branch heads found on branches %s')
1450 if opts.get('rev'):
1450 if opts.get('rev'):
1451 msg += _(' (started at %s)' % opts['rev'])
1451 msg += _(' (started at %s)' % opts['rev'])
1452 ui.warn((msg + '\n') % headless)
1452 ui.warn((msg + '\n') % headless)
1453
1453
1454 if not heads:
1454 if not heads:
1455 return 1
1455 return 1
1456
1456
1457 heads = sorted(heads, key=lambda x: -x.rev())
1457 heads = sorted(heads, key=lambda x: -x.rev())
1458 displayer = cmdutil.show_changeset(ui, repo, opts)
1458 displayer = cmdutil.show_changeset(ui, repo, opts)
1459 for ctx in heads:
1459 for ctx in heads:
1460 displayer.show(ctx)
1460 displayer.show(ctx)
1461 displayer.close()
1461 displayer.close()
1462
1462
1463 def help_(ui, name=None, with_version=False, unknowncmd=False):
1463 def help_(ui, name=None, with_version=False, unknowncmd=False):
1464 """show help for a given topic or a help overview
1464 """show help for a given topic or a help overview
1465
1465
1466 With no arguments, print a list of commands with short help messages.
1466 With no arguments, print a list of commands with short help messages.
1467
1467
1468 Given a topic, extension, or command name, print help for that
1468 Given a topic, extension, or command name, print help for that
1469 topic."""
1469 topic."""
1470 option_lists = []
1470 option_lists = []
1471 textwidth = util.termwidth() - 2
1471 textwidth = util.termwidth() - 2
1472
1472
1473 def addglobalopts(aliases):
1473 def addglobalopts(aliases):
1474 if ui.verbose:
1474 if ui.verbose:
1475 option_lists.append((_("global options:"), globalopts))
1475 option_lists.append((_("global options:"), globalopts))
1476 if name == 'shortlist':
1476 if name == 'shortlist':
1477 option_lists.append((_('use "hg help" for the full list '
1477 option_lists.append((_('use "hg help" for the full list '
1478 'of commands'), ()))
1478 'of commands'), ()))
1479 else:
1479 else:
1480 if name == 'shortlist':
1480 if name == 'shortlist':
1481 msg = _('use "hg help" for the full list of commands '
1481 msg = _('use "hg help" for the full list of commands '
1482 'or "hg -v" for details')
1482 'or "hg -v" for details')
1483 elif aliases:
1483 elif aliases:
1484 msg = _('use "hg -v help%s" to show aliases and '
1484 msg = _('use "hg -v help%s" to show aliases and '
1485 'global options') % (name and " " + name or "")
1485 'global options') % (name and " " + name or "")
1486 else:
1486 else:
1487 msg = _('use "hg -v help %s" to show global options') % name
1487 msg = _('use "hg -v help %s" to show global options') % name
1488 option_lists.append((msg, ()))
1488 option_lists.append((msg, ()))
1489
1489
1490 def helpcmd(name):
1490 def helpcmd(name):
1491 if with_version:
1491 if with_version:
1492 version_(ui)
1492 version_(ui)
1493 ui.write('\n')
1493 ui.write('\n')
1494
1494
1495 try:
1495 try:
1496 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
1496 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
1497 except error.AmbiguousCommand, inst:
1497 except error.AmbiguousCommand, inst:
1498 # py3k fix: except vars can't be used outside the scope of the
1498 # py3k fix: except vars can't be used outside the scope of the
1499 # except block, nor can be used inside a lambda. python issue4617
1499 # except block, nor can be used inside a lambda. python issue4617
1500 prefix = inst.args[0]
1500 prefix = inst.args[0]
1501 select = lambda c: c.lstrip('^').startswith(prefix)
1501 select = lambda c: c.lstrip('^').startswith(prefix)
1502 helplist(_('list of commands:\n\n'), select)
1502 helplist(_('list of commands:\n\n'), select)
1503 return
1503 return
1504
1504
1505 # check if it's an invalid alias and display its error if it is
1505 # check if it's an invalid alias and display its error if it is
1506 if getattr(entry[0], 'badalias', False):
1506 if getattr(entry[0], 'badalias', False):
1507 if not unknowncmd:
1507 if not unknowncmd:
1508 entry[0](ui)
1508 entry[0](ui)
1509 return
1509 return
1510
1510
1511 # synopsis
1511 # synopsis
1512 if len(entry) > 2:
1512 if len(entry) > 2:
1513 if entry[2].startswith('hg'):
1513 if entry[2].startswith('hg'):
1514 ui.write("%s\n" % entry[2])
1514 ui.write("%s\n" % entry[2])
1515 else:
1515 else:
1516 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
1516 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
1517 else:
1517 else:
1518 ui.write('hg %s\n' % aliases[0])
1518 ui.write('hg %s\n' % aliases[0])
1519
1519
1520 # aliases
1520 # aliases
1521 if not ui.quiet and len(aliases) > 1:
1521 if not ui.quiet and len(aliases) > 1:
1522 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1522 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1523
1523
1524 # description
1524 # description
1525 doc = gettext(entry[0].__doc__)
1525 doc = gettext(entry[0].__doc__)
1526 if not doc:
1526 if not doc:
1527 doc = _("(no help text available)")
1527 doc = _("(no help text available)")
1528 if ui.quiet:
1528 if ui.quiet:
1529 doc = doc.splitlines()[0]
1529 doc = doc.splitlines()[0]
1530 ui.write("\n%s\n" % minirst.format(doc, textwidth))
1530 ui.write("\n%s\n" % minirst.format(doc, textwidth))
1531
1531
1532 if not ui.quiet:
1532 if not ui.quiet:
1533 # options
1533 # options
1534 if entry[1]:
1534 if entry[1]:
1535 option_lists.append((_("options:\n"), entry[1]))
1535 option_lists.append((_("options:\n"), entry[1]))
1536
1536
1537 addglobalopts(False)
1537 addglobalopts(False)
1538
1538
1539 def helplist(header, select=None):
1539 def helplist(header, select=None):
1540 h = {}
1540 h = {}
1541 cmds = {}
1541 cmds = {}
1542 for c, e in table.iteritems():
1542 for c, e in table.iteritems():
1543 f = c.split("|", 1)[0]
1543 f = c.split("|", 1)[0]
1544 if select and not select(f):
1544 if select and not select(f):
1545 continue
1545 continue
1546 if (not select and name != 'shortlist' and
1546 if (not select and name != 'shortlist' and
1547 e[0].__module__ != __name__):
1547 e[0].__module__ != __name__):
1548 continue
1548 continue
1549 if name == "shortlist" and not f.startswith("^"):
1549 if name == "shortlist" and not f.startswith("^"):
1550 continue
1550 continue
1551 f = f.lstrip("^")
1551 f = f.lstrip("^")
1552 if not ui.debugflag and f.startswith("debug"):
1552 if not ui.debugflag and f.startswith("debug"):
1553 continue
1553 continue
1554 doc = e[0].__doc__
1554 doc = e[0].__doc__
1555 if doc and 'DEPRECATED' in doc and not ui.verbose:
1555 if doc and 'DEPRECATED' in doc and not ui.verbose:
1556 continue
1556 continue
1557 doc = gettext(doc)
1557 doc = gettext(doc)
1558 if not doc:
1558 if not doc:
1559 doc = _("(no help text available)")
1559 doc = _("(no help text available)")
1560 h[f] = doc.splitlines()[0].rstrip()
1560 h[f] = doc.splitlines()[0].rstrip()
1561 cmds[f] = c.lstrip("^")
1561 cmds[f] = c.lstrip("^")
1562
1562
1563 if not h:
1563 if not h:
1564 ui.status(_('no commands defined\n'))
1564 ui.status(_('no commands defined\n'))
1565 return
1565 return
1566
1566
1567 ui.status(header)
1567 ui.status(header)
1568 fns = sorted(h)
1568 fns = sorted(h)
1569 m = max(map(len, fns))
1569 m = max(map(len, fns))
1570 for f in fns:
1570 for f in fns:
1571 if ui.verbose:
1571 if ui.verbose:
1572 commands = cmds[f].replace("|",", ")
1572 commands = cmds[f].replace("|",", ")
1573 ui.write(" %s:\n %s\n"%(commands, h[f]))
1573 ui.write(" %s:\n %s\n"%(commands, h[f]))
1574 else:
1574 else:
1575 ui.write(' %-*s %s\n' % (m, f, util.wrap(h[f], m + 4)))
1575 ui.write(' %-*s %s\n' % (m, f, util.wrap(h[f], m + 4)))
1576
1576
1577 if not ui.quiet:
1577 if not ui.quiet:
1578 addglobalopts(True)
1578 addglobalopts(True)
1579
1579
1580 def helptopic(name):
1580 def helptopic(name):
1581 for names, header, doc in help.helptable:
1581 for names, header, doc in help.helptable:
1582 if name in names:
1582 if name in names:
1583 break
1583 break
1584 else:
1584 else:
1585 raise error.UnknownCommand(name)
1585 raise error.UnknownCommand(name)
1586
1586
1587 # description
1587 # description
1588 if not doc:
1588 if not doc:
1589 doc = _("(no help text available)")
1589 doc = _("(no help text available)")
1590 if hasattr(doc, '__call__'):
1590 if hasattr(doc, '__call__'):
1591 doc = doc()
1591 doc = doc()
1592
1592
1593 ui.write("%s\n\n" % header)
1593 ui.write("%s\n\n" % header)
1594 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
1594 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
1595
1595
1596 def helpext(name):
1596 def helpext(name):
1597 try:
1597 try:
1598 mod = extensions.find(name)
1598 mod = extensions.find(name)
1599 doc = gettext(mod.__doc__) or _('no help text available')
1599 doc = gettext(mod.__doc__) or _('no help text available')
1600 except KeyError:
1600 except KeyError:
1601 mod = None
1601 mod = None
1602 doc = extensions.disabledext(name)
1602 doc = extensions.disabledext(name)
1603 if not doc:
1603 if not doc:
1604 raise error.UnknownCommand(name)
1604 raise error.UnknownCommand(name)
1605
1605
1606 if '\n' not in doc:
1606 if '\n' not in doc:
1607 head, tail = doc, ""
1607 head, tail = doc, ""
1608 else:
1608 else:
1609 head, tail = doc.split('\n', 1)
1609 head, tail = doc.split('\n', 1)
1610 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
1610 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
1611 if tail:
1611 if tail:
1612 ui.write(minirst.format(tail, textwidth))
1612 ui.write(minirst.format(tail, textwidth))
1613 ui.status('\n\n')
1613 ui.status('\n\n')
1614
1614
1615 if mod:
1615 if mod:
1616 try:
1616 try:
1617 ct = mod.cmdtable
1617 ct = mod.cmdtable
1618 except AttributeError:
1618 except AttributeError:
1619 ct = {}
1619 ct = {}
1620 modcmds = set([c.split('|', 1)[0] for c in ct])
1620 modcmds = set([c.split('|', 1)[0] for c in ct])
1621 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1621 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1622 else:
1622 else:
1623 ui.write(_('use "hg help extensions" for information on enabling '
1623 ui.write(_('use "hg help extensions" for information on enabling '
1624 'extensions\n'))
1624 'extensions\n'))
1625
1625
1626 def helpextcmd(name):
1626 def helpextcmd(name):
1627 cmd, ext, mod = extensions.disabledcmd(name, ui.config('ui', 'strict'))
1627 cmd, ext, mod = extensions.disabledcmd(name, ui.config('ui', 'strict'))
1628 doc = gettext(mod.__doc__).splitlines()[0]
1628 doc = gettext(mod.__doc__).splitlines()[0]
1629
1629
1630 msg = help.listexts(_("'%s' is provided by the following "
1630 msg = help.listexts(_("'%s' is provided by the following "
1631 "extension:") % cmd, {ext: doc}, len(ext),
1631 "extension:") % cmd, {ext: doc}, len(ext),
1632 indent=4)
1632 indent=4)
1633 ui.write(minirst.format(msg, textwidth))
1633 ui.write(minirst.format(msg, textwidth))
1634 ui.write('\n\n')
1634 ui.write('\n\n')
1635 ui.write(_('use "hg help extensions" for information on enabling '
1635 ui.write(_('use "hg help extensions" for information on enabling '
1636 'extensions\n'))
1636 'extensions\n'))
1637
1637
1638 if name and name != 'shortlist':
1638 if name and name != 'shortlist':
1639 i = None
1639 i = None
1640 if unknowncmd:
1640 if unknowncmd:
1641 queries = (helpextcmd,)
1641 queries = (helpextcmd,)
1642 else:
1642 else:
1643 queries = (helptopic, helpcmd, helpext, helpextcmd)
1643 queries = (helptopic, helpcmd, helpext, helpextcmd)
1644 for f in queries:
1644 for f in queries:
1645 try:
1645 try:
1646 f(name)
1646 f(name)
1647 i = None
1647 i = None
1648 break
1648 break
1649 except error.UnknownCommand, inst:
1649 except error.UnknownCommand, inst:
1650 i = inst
1650 i = inst
1651 if i:
1651 if i:
1652 raise i
1652 raise i
1653
1653
1654 else:
1654 else:
1655 # program name
1655 # program name
1656 if ui.verbose or with_version:
1656 if ui.verbose or with_version:
1657 version_(ui)
1657 version_(ui)
1658 else:
1658 else:
1659 ui.status(_("Mercurial Distributed SCM\n"))
1659 ui.status(_("Mercurial Distributed SCM\n"))
1660 ui.status('\n')
1660 ui.status('\n')
1661
1661
1662 # list of commands
1662 # list of commands
1663 if name == "shortlist":
1663 if name == "shortlist":
1664 header = _('basic commands:\n\n')
1664 header = _('basic commands:\n\n')
1665 else:
1665 else:
1666 header = _('list of commands:\n\n')
1666 header = _('list of commands:\n\n')
1667
1667
1668 helplist(header)
1668 helplist(header)
1669 if name != 'shortlist':
1669 if name != 'shortlist':
1670 exts, maxlength = extensions.enabled()
1670 exts, maxlength = extensions.enabled()
1671 text = help.listexts(_('enabled extensions:'), exts, maxlength)
1671 text = help.listexts(_('enabled extensions:'), exts, maxlength)
1672 if text:
1672 if text:
1673 ui.write("\n%s\n" % minirst.format(text, textwidth))
1673 ui.write("\n%s\n" % minirst.format(text, textwidth))
1674
1674
1675 # list all option lists
1675 # list all option lists
1676 opt_output = []
1676 opt_output = []
1677 for title, options in option_lists:
1677 for title, options in option_lists:
1678 opt_output.append(("\n%s" % title, None))
1678 opt_output.append(("\n%s" % title, None))
1679 for shortopt, longopt, default, desc in options:
1679 for shortopt, longopt, default, desc in options:
1680 if _("DEPRECATED") in desc and not ui.verbose:
1680 if _("DEPRECATED") in desc and not ui.verbose:
1681 continue
1681 continue
1682 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1682 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1683 longopt and " --%s" % longopt),
1683 longopt and " --%s" % longopt),
1684 "%s%s" % (desc,
1684 "%s%s" % (desc,
1685 default
1685 default
1686 and _(" (default: %s)") % default
1686 and _(" (default: %s)") % default
1687 or "")))
1687 or "")))
1688
1688
1689 if not name:
1689 if not name:
1690 ui.write(_("\nadditional help topics:\n\n"))
1690 ui.write(_("\nadditional help topics:\n\n"))
1691 topics = []
1691 topics = []
1692 for names, header, doc in help.helptable:
1692 for names, header, doc in help.helptable:
1693 topics.append((sorted(names, key=len, reverse=True)[0], header))
1693 topics.append((sorted(names, key=len, reverse=True)[0], header))
1694 topics_len = max([len(s[0]) for s in topics])
1694 topics_len = max([len(s[0]) for s in topics])
1695 for t, desc in topics:
1695 for t, desc in topics:
1696 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1696 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1697
1697
1698 if opt_output:
1698 if opt_output:
1699 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1699 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1700 for first, second in opt_output:
1700 for first, second in opt_output:
1701 if second:
1701 if second:
1702 second = util.wrap(second, opts_len + 3)
1702 second = util.wrap(second, opts_len + 3)
1703 ui.write(" %-*s %s\n" % (opts_len, first, second))
1703 ui.write(" %-*s %s\n" % (opts_len, first, second))
1704 else:
1704 else:
1705 ui.write("%s\n" % first)
1705 ui.write("%s\n" % first)
1706
1706
1707 def identify(ui, repo, source=None,
1707 def identify(ui, repo, source=None,
1708 rev=None, num=None, id=None, branch=None, tags=None):
1708 rev=None, num=None, id=None, branch=None, tags=None):
1709 """identify the working copy or specified revision
1709 """identify the working copy or specified revision
1710
1710
1711 With no revision, print a summary of the current state of the
1711 With no revision, print a summary of the current state of the
1712 repository.
1712 repository.
1713
1713
1714 Specifying a path to a repository root or Mercurial bundle will
1714 Specifying a path to a repository root or Mercurial bundle will
1715 cause lookup to operate on that repository/bundle.
1715 cause lookup to operate on that repository/bundle.
1716
1716
1717 This summary identifies the repository state using one or two
1717 This summary identifies the repository state using one or two
1718 parent hash identifiers, followed by a "+" if there are
1718 parent hash identifiers, followed by a "+" if there are
1719 uncommitted changes in the working directory, a list of tags for
1719 uncommitted changes in the working directory, a list of tags for
1720 this revision and a branch name for non-default branches.
1720 this revision and a branch name for non-default branches.
1721 """
1721 """
1722
1722
1723 if not repo and not source:
1723 if not repo and not source:
1724 raise util.Abort(_("There is no Mercurial repository here "
1724 raise util.Abort(_("There is no Mercurial repository here "
1725 "(.hg not found)"))
1725 "(.hg not found)"))
1726
1726
1727 hexfunc = ui.debugflag and hex or short
1727 hexfunc = ui.debugflag and hex or short
1728 default = not (num or id or branch or tags)
1728 default = not (num or id or branch or tags)
1729 output = []
1729 output = []
1730
1730
1731 revs = []
1731 revs = []
1732 if source:
1732 if source:
1733 source, branches = hg.parseurl(ui.expandpath(source))
1733 source, branches = hg.parseurl(ui.expandpath(source))
1734 repo = hg.repository(ui, source)
1734 repo = hg.repository(ui, source)
1735 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
1735 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
1736
1736
1737 if not repo.local():
1737 if not repo.local():
1738 if not rev and revs:
1738 if not rev and revs:
1739 rev = revs[0]
1739 rev = revs[0]
1740 if not rev:
1740 if not rev:
1741 rev = "tip"
1741 rev = "tip"
1742 if num or branch or tags:
1742 if num or branch or tags:
1743 raise util.Abort(
1743 raise util.Abort(
1744 "can't query remote revision number, branch, or tags")
1744 "can't query remote revision number, branch, or tags")
1745 output = [hexfunc(repo.lookup(rev))]
1745 output = [hexfunc(repo.lookup(rev))]
1746 elif not rev:
1746 elif not rev:
1747 ctx = repo[None]
1747 ctx = repo[None]
1748 parents = ctx.parents()
1748 parents = ctx.parents()
1749 changed = False
1749 changed = False
1750 if default or id or num:
1750 if default or id or num:
1751 changed = ctx.files() + ctx.deleted()
1751 changed = ctx.files() + ctx.deleted()
1752 if default or id:
1752 if default or id:
1753 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1753 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1754 (changed) and "+" or "")]
1754 (changed) and "+" or "")]
1755 if num:
1755 if num:
1756 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1756 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1757 (changed) and "+" or ""))
1757 (changed) and "+" or ""))
1758 else:
1758 else:
1759 ctx = repo[rev]
1759 ctx = repo[rev]
1760 if default or id:
1760 if default or id:
1761 output = [hexfunc(ctx.node())]
1761 output = [hexfunc(ctx.node())]
1762 if num:
1762 if num:
1763 output.append(str(ctx.rev()))
1763 output.append(str(ctx.rev()))
1764
1764
1765 if repo.local() and default and not ui.quiet:
1765 if repo.local() and default and not ui.quiet:
1766 b = encoding.tolocal(ctx.branch())
1766 b = encoding.tolocal(ctx.branch())
1767 if b != 'default':
1767 if b != 'default':
1768 output.append("(%s)" % b)
1768 output.append("(%s)" % b)
1769
1769
1770 # multiple tags for a single parent separated by '/'
1770 # multiple tags for a single parent separated by '/'
1771 t = "/".join(ctx.tags())
1771 t = "/".join(ctx.tags())
1772 if t:
1772 if t:
1773 output.append(t)
1773 output.append(t)
1774
1774
1775 if branch:
1775 if branch:
1776 output.append(encoding.tolocal(ctx.branch()))
1776 output.append(encoding.tolocal(ctx.branch()))
1777
1777
1778 if tags:
1778 if tags:
1779 output.extend(ctx.tags())
1779 output.extend(ctx.tags())
1780
1780
1781 ui.write("%s\n" % ' '.join(output))
1781 ui.write("%s\n" % ' '.join(output))
1782
1782
1783 def import_(ui, repo, patch1, *patches, **opts):
1783 def import_(ui, repo, patch1, *patches, **opts):
1784 """import an ordered set of patches
1784 """import an ordered set of patches
1785
1785
1786 Import a list of patches and commit them individually (unless
1786 Import a list of patches and commit them individually (unless
1787 --no-commit is specified).
1787 --no-commit is specified).
1788
1788
1789 If there are outstanding changes in the working directory, import
1789 If there are outstanding changes in the working directory, import
1790 will abort unless given the -f/--force flag.
1790 will abort unless given the -f/--force flag.
1791
1791
1792 You can import a patch straight from a mail message. Even patches
1792 You can import a patch straight from a mail message. Even patches
1793 as attachments work (to use the body part, it must have type
1793 as attachments work (to use the body part, it must have type
1794 text/plain or text/x-patch). From and Subject headers of email
1794 text/plain or text/x-patch). From and Subject headers of email
1795 message are used as default committer and commit message. All
1795 message are used as default committer and commit message. All
1796 text/plain body parts before first diff are added to commit
1796 text/plain body parts before first diff are added to commit
1797 message.
1797 message.
1798
1798
1799 If the imported patch was generated by hg export, user and
1799 If the imported patch was generated by hg export, user and
1800 description from patch override values from message headers and
1800 description from patch override values from message headers and
1801 body. Values given on command line with -m/--message and -u/--user
1801 body. Values given on command line with -m/--message and -u/--user
1802 override these.
1802 override these.
1803
1803
1804 If --exact is specified, import will set the working directory to
1804 If --exact is specified, import will set the working directory to
1805 the parent of each patch before applying it, and will abort if the
1805 the parent of each patch before applying it, and will abort if the
1806 resulting changeset has a different ID than the one recorded in
1806 resulting changeset has a different ID than the one recorded in
1807 the patch. This may happen due to character set problems or other
1807 the patch. This may happen due to character set problems or other
1808 deficiencies in the text patch format.
1808 deficiencies in the text patch format.
1809
1809
1810 With -s/--similarity, hg will attempt to discover renames and
1810 With -s/--similarity, hg will attempt to discover renames and
1811 copies in the patch in the same way as 'addremove'.
1811 copies in the patch in the same way as 'addremove'.
1812
1812
1813 To read a patch from standard input, use "-" as the patch name. If
1813 To read a patch from standard input, use "-" as the patch name. If
1814 a URL is specified, the patch will be downloaded from it.
1814 a URL is specified, the patch will be downloaded from it.
1815 See 'hg help dates' for a list of formats valid for -d/--date.
1815 See 'hg help dates' for a list of formats valid for -d/--date.
1816 """
1816 """
1817 patches = (patch1,) + patches
1817 patches = (patch1,) + patches
1818
1818
1819 date = opts.get('date')
1819 date = opts.get('date')
1820 if date:
1820 if date:
1821 opts['date'] = util.parsedate(date)
1821 opts['date'] = util.parsedate(date)
1822
1822
1823 try:
1823 try:
1824 sim = float(opts.get('similarity') or 0)
1824 sim = float(opts.get('similarity') or 0)
1825 except ValueError:
1825 except ValueError:
1826 raise util.Abort(_('similarity must be a number'))
1826 raise util.Abort(_('similarity must be a number'))
1827 if sim < 0 or sim > 100:
1827 if sim < 0 or sim > 100:
1828 raise util.Abort(_('similarity must be between 0 and 100'))
1828 raise util.Abort(_('similarity must be between 0 and 100'))
1829
1829
1830 if opts.get('exact') or not opts.get('force'):
1830 if opts.get('exact') or not opts.get('force'):
1831 cmdutil.bail_if_changed(repo)
1831 cmdutil.bail_if_changed(repo)
1832
1832
1833 d = opts["base"]
1833 d = opts["base"]
1834 strip = opts["strip"]
1834 strip = opts["strip"]
1835 wlock = lock = None
1835 wlock = lock = None
1836
1837 def tryone(ui, hunk):
1838 tmpname, message, user, date, branch, nodeid, p1, p2 = patch.extract(ui, hunk)
1839
1840 if not tmpname:
1841 return None
1842 commitid = _('to working directory')
1843
1844 try:
1845 cmdline_message = cmdutil.logmessage(opts)
1846 if cmdline_message:
1847 # pickup the cmdline msg
1848 message = cmdline_message
1849 elif message:
1850 # pickup the patch msg
1851 message = message.strip()
1852 else:
1853 # launch the editor
1854 message = None
1855 ui.debug('message:\n%s\n' % message)
1856
1857 wp = repo.parents()
1858 if opts.get('exact'):
1859 if not nodeid or not p1:
1860 raise util.Abort(_('not a Mercurial patch'))
1861 p1 = repo.lookup(p1)
1862 p2 = repo.lookup(p2 or hex(nullid))
1863
1864 if p1 != wp[0].node():
1865 hg.clean(repo, p1)
1866 repo.dirstate.setparents(p1, p2)
1867 elif p2:
1868 try:
1869 p1 = repo.lookup(p1)
1870 p2 = repo.lookup(p2)
1871 if p1 == wp[0].node():
1872 repo.dirstate.setparents(p1, p2)
1873 except error.RepoError:
1874 pass
1875 if opts.get('exact') or opts.get('import_branch'):
1876 repo.dirstate.setbranch(branch or 'default')
1877
1878 files = {}
1879 try:
1880 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1881 files=files, eolmode=None)
1882 finally:
1883 files = patch.updatedir(ui, repo, files,
1884 similarity=sim / 100.0)
1885 if not opts.get('no_commit'):
1886 if opts.get('exact'):
1887 m = None
1888 else:
1889 m = cmdutil.matchfiles(repo, files or [])
1890 n = repo.commit(message, opts.get('user') or user,
1891 opts.get('date') or date, match=m,
1892 editor=cmdutil.commiteditor)
1893 if opts.get('exact'):
1894 if hex(n) != nodeid:
1895 repo.rollback()
1896 raise util.Abort(_('patch is damaged'
1897 ' or loses information'))
1898 # Force a dirstate write so that the next transaction
1899 # backups an up-do-date file.
1900 repo.dirstate.write()
1901 if n:
1902 commitid = short(n)
1903
1904 return commitid
1905 finally:
1906 os.unlink(tmpname)
1907
1836 try:
1908 try:
1837 wlock = repo.wlock()
1909 wlock = repo.wlock()
1838 lock = repo.lock()
1910 lock = repo.lock()
1911 lastcommit = None
1839 for p in patches:
1912 for p in patches:
1840 pf = os.path.join(d, p)
1913 pf = os.path.join(d, p)
1841
1914
1842 if pf == '-':
1915 if pf == '-':
1843 ui.status(_("applying patch from stdin\n"))
1916 ui.status(_("applying patch from stdin\n"))
1844 pf = sys.stdin
1917 pf = sys.stdin
1845 else:
1918 else:
1846 ui.status(_("applying %s\n") % p)
1919 ui.status(_("applying %s\n") % p)
1847 pf = url.open(ui, pf)
1920 pf = url.open(ui, pf)
1848 data = patch.extract(ui, pf)
1921
1849 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1922 haspatch = False
1850
1923 for hunk in patch.split(pf):
1851 if tmpname is None:
1924 commitid = tryone(ui, hunk)
1925 if commitid:
1926 haspatch = True
1927 if lastcommit:
1928 ui.status(_('applied %s\n') % lastcommit)
1929 lastcommit = commitid
1930
1931 if not haspatch:
1852 raise util.Abort(_('no diffs found'))
1932 raise util.Abort(_('no diffs found'))
1853
1933
1854 try:
1855 cmdline_message = cmdutil.logmessage(opts)
1856 if cmdline_message:
1857 # pickup the cmdline msg
1858 message = cmdline_message
1859 elif message:
1860 # pickup the patch msg
1861 message = message.strip()
1862 else:
1863 # launch the editor
1864 message = None
1865 ui.debug('message:\n%s\n' % message)
1866
1867 wp = repo.parents()
1868 if opts.get('exact'):
1869 if not nodeid or not p1:
1870 raise util.Abort(_('not a Mercurial patch'))
1871 p1 = repo.lookup(p1)
1872 p2 = repo.lookup(p2 or hex(nullid))
1873
1874 if p1 != wp[0].node():
1875 hg.clean(repo, p1)
1876 repo.dirstate.setparents(p1, p2)
1877 elif p2:
1878 try:
1879 p1 = repo.lookup(p1)
1880 p2 = repo.lookup(p2)
1881 if p1 == wp[0].node():
1882 repo.dirstate.setparents(p1, p2)
1883 except error.RepoError:
1884 pass
1885 if opts.get('exact') or opts.get('import_branch'):
1886 repo.dirstate.setbranch(branch or 'default')
1887
1888 files = {}
1889 try:
1890 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1891 files=files, eolmode=None)
1892 finally:
1893 files = patch.updatedir(ui, repo, files,
1894 similarity=sim / 100.0)
1895 if not opts.get('no_commit'):
1896 m = cmdutil.matchfiles(repo, files or [])
1897 n = repo.commit(message, opts.get('user') or user,
1898 opts.get('date') or date, match=m,
1899 editor=cmdutil.commiteditor)
1900 if opts.get('exact'):
1901 if hex(n) != nodeid:
1902 repo.rollback()
1903 raise util.Abort(_('patch is damaged'
1904 ' or loses information'))
1905 # Force a dirstate write so that the next transaction
1906 # backups an up-do-date file.
1907 repo.dirstate.write()
1908 finally:
1909 os.unlink(tmpname)
1910 finally:
1934 finally:
1911 release(lock, wlock)
1935 release(lock, wlock)
1912
1936
1913 def incoming(ui, repo, source="default", **opts):
1937 def incoming(ui, repo, source="default", **opts):
1914 """show new changesets found in source
1938 """show new changesets found in source
1915
1939
1916 Show new changesets found in the specified path/URL or the default
1940 Show new changesets found in the specified path/URL or the default
1917 pull location. These are the changesets that would have been pulled
1941 pull location. These are the changesets that would have been pulled
1918 if a pull at the time you issued this command.
1942 if a pull at the time you issued this command.
1919
1943
1920 For remote repository, using --bundle avoids downloading the
1944 For remote repository, using --bundle avoids downloading the
1921 changesets twice if the incoming is followed by a pull.
1945 changesets twice if the incoming is followed by a pull.
1922
1946
1923 See pull for valid source format details.
1947 See pull for valid source format details.
1924 """
1948 """
1925 limit = cmdutil.loglimit(opts)
1949 limit = cmdutil.loglimit(opts)
1926 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
1950 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
1927 other = hg.repository(cmdutil.remoteui(repo, opts), source)
1951 other = hg.repository(cmdutil.remoteui(repo, opts), source)
1928 ui.status(_('comparing with %s\n') % url.hidepassword(source))
1952 ui.status(_('comparing with %s\n') % url.hidepassword(source))
1929 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
1953 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
1930 if revs:
1954 if revs:
1931 revs = [other.lookup(rev) for rev in revs]
1955 revs = [other.lookup(rev) for rev in revs]
1932 common, incoming, rheads = repo.findcommonincoming(other, heads=revs,
1956 common, incoming, rheads = repo.findcommonincoming(other, heads=revs,
1933 force=opts["force"])
1957 force=opts["force"])
1934 if not incoming:
1958 if not incoming:
1935 try:
1959 try:
1936 os.unlink(opts["bundle"])
1960 os.unlink(opts["bundle"])
1937 except:
1961 except:
1938 pass
1962 pass
1939 ui.status(_("no changes found\n"))
1963 ui.status(_("no changes found\n"))
1940 return 1
1964 return 1
1941
1965
1942 cleanup = None
1966 cleanup = None
1943 try:
1967 try:
1944 fname = opts["bundle"]
1968 fname = opts["bundle"]
1945 if fname or not other.local():
1969 if fname or not other.local():
1946 # create a bundle (uncompressed if other repo is not local)
1970 # create a bundle (uncompressed if other repo is not local)
1947
1971
1948 if revs is None and other.capable('changegroupsubset'):
1972 if revs is None and other.capable('changegroupsubset'):
1949 revs = rheads
1973 revs = rheads
1950
1974
1951 if revs is None:
1975 if revs is None:
1952 cg = other.changegroup(incoming, "incoming")
1976 cg = other.changegroup(incoming, "incoming")
1953 else:
1977 else:
1954 cg = other.changegroupsubset(incoming, revs, 'incoming')
1978 cg = other.changegroupsubset(incoming, revs, 'incoming')
1955 bundletype = other.local() and "HG10BZ" or "HG10UN"
1979 bundletype = other.local() and "HG10BZ" or "HG10UN"
1956 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1980 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1957 # keep written bundle?
1981 # keep written bundle?
1958 if opts["bundle"]:
1982 if opts["bundle"]:
1959 cleanup = None
1983 cleanup = None
1960 if not other.local():
1984 if not other.local():
1961 # use the created uncompressed bundlerepo
1985 # use the created uncompressed bundlerepo
1962 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1986 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1963
1987
1964 o = other.changelog.nodesbetween(incoming, revs)[0]
1988 o = other.changelog.nodesbetween(incoming, revs)[0]
1965 if opts.get('newest_first'):
1989 if opts.get('newest_first'):
1966 o.reverse()
1990 o.reverse()
1967 displayer = cmdutil.show_changeset(ui, other, opts)
1991 displayer = cmdutil.show_changeset(ui, other, opts)
1968 count = 0
1992 count = 0
1969 for n in o:
1993 for n in o:
1970 if limit is not None and count >= limit:
1994 if limit is not None and count >= limit:
1971 break
1995 break
1972 parents = [p for p in other.changelog.parents(n) if p != nullid]
1996 parents = [p for p in other.changelog.parents(n) if p != nullid]
1973 if opts.get('no_merges') and len(parents) == 2:
1997 if opts.get('no_merges') and len(parents) == 2:
1974 continue
1998 continue
1975 count += 1
1999 count += 1
1976 displayer.show(other[n])
2000 displayer.show(other[n])
1977 displayer.close()
2001 displayer.close()
1978 finally:
2002 finally:
1979 if hasattr(other, 'close'):
2003 if hasattr(other, 'close'):
1980 other.close()
2004 other.close()
1981 if cleanup:
2005 if cleanup:
1982 os.unlink(cleanup)
2006 os.unlink(cleanup)
1983
2007
1984 def init(ui, dest=".", **opts):
2008 def init(ui, dest=".", **opts):
1985 """create a new repository in the given directory
2009 """create a new repository in the given directory
1986
2010
1987 Initialize a new repository in the given directory. If the given
2011 Initialize a new repository in the given directory. If the given
1988 directory does not exist, it will be created.
2012 directory does not exist, it will be created.
1989
2013
1990 If no directory is given, the current directory is used.
2014 If no directory is given, the current directory is used.
1991
2015
1992 It is possible to specify an ``ssh://`` URL as the destination.
2016 It is possible to specify an ``ssh://`` URL as the destination.
1993 See 'hg help urls' for more information.
2017 See 'hg help urls' for more information.
1994 """
2018 """
1995 hg.repository(cmdutil.remoteui(ui, opts), dest, create=1)
2019 hg.repository(cmdutil.remoteui(ui, opts), dest, create=1)
1996
2020
1997 def locate(ui, repo, *pats, **opts):
2021 def locate(ui, repo, *pats, **opts):
1998 """locate files matching specific patterns
2022 """locate files matching specific patterns
1999
2023
2000 Print files under Mercurial control in the working directory whose
2024 Print files under Mercurial control in the working directory whose
2001 names match the given patterns.
2025 names match the given patterns.
2002
2026
2003 By default, this command searches all directories in the working
2027 By default, this command searches all directories in the working
2004 directory. To search just the current directory and its
2028 directory. To search just the current directory and its
2005 subdirectories, use "--include .".
2029 subdirectories, use "--include .".
2006
2030
2007 If no patterns are given to match, this command prints the names
2031 If no patterns are given to match, this command prints the names
2008 of all files under Mercurial control in the working directory.
2032 of all files under Mercurial control in the working directory.
2009
2033
2010 If you want to feed the output of this command into the "xargs"
2034 If you want to feed the output of this command into the "xargs"
2011 command, use the -0 option to both this command and "xargs". This
2035 command, use the -0 option to both this command and "xargs". This
2012 will avoid the problem of "xargs" treating single filenames that
2036 will avoid the problem of "xargs" treating single filenames that
2013 contain whitespace as multiple filenames.
2037 contain whitespace as multiple filenames.
2014 """
2038 """
2015 end = opts.get('print0') and '\0' or '\n'
2039 end = opts.get('print0') and '\0' or '\n'
2016 rev = opts.get('rev') or None
2040 rev = opts.get('rev') or None
2017
2041
2018 ret = 1
2042 ret = 1
2019 m = cmdutil.match(repo, pats, opts, default='relglob')
2043 m = cmdutil.match(repo, pats, opts, default='relglob')
2020 m.bad = lambda x, y: False
2044 m.bad = lambda x, y: False
2021 for abs in repo[rev].walk(m):
2045 for abs in repo[rev].walk(m):
2022 if not rev and abs not in repo.dirstate:
2046 if not rev and abs not in repo.dirstate:
2023 continue
2047 continue
2024 if opts.get('fullpath'):
2048 if opts.get('fullpath'):
2025 ui.write(repo.wjoin(abs), end)
2049 ui.write(repo.wjoin(abs), end)
2026 else:
2050 else:
2027 ui.write(((pats and m.rel(abs)) or abs), end)
2051 ui.write(((pats and m.rel(abs)) or abs), end)
2028 ret = 0
2052 ret = 0
2029
2053
2030 return ret
2054 return ret
2031
2055
2032 def log(ui, repo, *pats, **opts):
2056 def log(ui, repo, *pats, **opts):
2033 """show revision history of entire repository or files
2057 """show revision history of entire repository or files
2034
2058
2035 Print the revision history of the specified files or the entire
2059 Print the revision history of the specified files or the entire
2036 project.
2060 project.
2037
2061
2038 File history is shown without following rename or copy history of
2062 File history is shown without following rename or copy history of
2039 files. Use -f/--follow with a filename to follow history across
2063 files. Use -f/--follow with a filename to follow history across
2040 renames and copies. --follow without a filename will only show
2064 renames and copies. --follow without a filename will only show
2041 ancestors or descendants of the starting revision. --follow-first
2065 ancestors or descendants of the starting revision. --follow-first
2042 only follows the first parent of merge revisions.
2066 only follows the first parent of merge revisions.
2043
2067
2044 If no revision range is specified, the default is tip:0 unless
2068 If no revision range is specified, the default is tip:0 unless
2045 --follow is set, in which case the working directory parent is
2069 --follow is set, in which case the working directory parent is
2046 used as the starting revision.
2070 used as the starting revision.
2047
2071
2048 See 'hg help dates' for a list of formats valid for -d/--date.
2072 See 'hg help dates' for a list of formats valid for -d/--date.
2049
2073
2050 By default this command prints revision number and changeset id,
2074 By default this command prints revision number and changeset id,
2051 tags, non-trivial parents, user, date and time, and a summary for
2075 tags, non-trivial parents, user, date and time, and a summary for
2052 each commit. When the -v/--verbose switch is used, the list of
2076 each commit. When the -v/--verbose switch is used, the list of
2053 changed files and full commit message are shown.
2077 changed files and full commit message are shown.
2054
2078
2055 NOTE: log -p/--patch may generate unexpected diff output for merge
2079 NOTE: log -p/--patch may generate unexpected diff output for merge
2056 changesets, as it will only compare the merge changeset against
2080 changesets, as it will only compare the merge changeset against
2057 its first parent. Also, only files different from BOTH parents
2081 its first parent. Also, only files different from BOTH parents
2058 will appear in files:.
2082 will appear in files:.
2059 """
2083 """
2060
2084
2061 matchfn = cmdutil.match(repo, pats, opts)
2085 matchfn = cmdutil.match(repo, pats, opts)
2062 limit = cmdutil.loglimit(opts)
2086 limit = cmdutil.loglimit(opts)
2063 count = 0
2087 count = 0
2064
2088
2065 endrev = None
2089 endrev = None
2066 if opts.get('copies') and opts.get('rev'):
2090 if opts.get('copies') and opts.get('rev'):
2067 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
2091 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
2068
2092
2069 df = False
2093 df = False
2070 if opts["date"]:
2094 if opts["date"]:
2071 df = util.matchdate(opts["date"])
2095 df = util.matchdate(opts["date"])
2072
2096
2073 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
2097 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
2074 def prep(ctx, fns):
2098 def prep(ctx, fns):
2075 rev = ctx.rev()
2099 rev = ctx.rev()
2076 parents = [p for p in repo.changelog.parentrevs(rev)
2100 parents = [p for p in repo.changelog.parentrevs(rev)
2077 if p != nullrev]
2101 if p != nullrev]
2078 if opts.get('no_merges') and len(parents) == 2:
2102 if opts.get('no_merges') and len(parents) == 2:
2079 return
2103 return
2080 if opts.get('only_merges') and len(parents) != 2:
2104 if opts.get('only_merges') and len(parents) != 2:
2081 return
2105 return
2082 if opts.get('only_branch') and ctx.branch() not in opts['only_branch']:
2106 if opts.get('only_branch') and ctx.branch() not in opts['only_branch']:
2083 return
2107 return
2084 if df and not df(ctx.date()[0]):
2108 if df and not df(ctx.date()[0]):
2085 return
2109 return
2086 if opts['user'] and not [k for k in opts['user'] if k in ctx.user()]:
2110 if opts['user'] and not [k for k in opts['user'] if k in ctx.user()]:
2087 return
2111 return
2088 if opts.get('keyword'):
2112 if opts.get('keyword'):
2089 for k in [kw.lower() for kw in opts['keyword']]:
2113 for k in [kw.lower() for kw in opts['keyword']]:
2090 if (k in ctx.user().lower() or
2114 if (k in ctx.user().lower() or
2091 k in ctx.description().lower() or
2115 k in ctx.description().lower() or
2092 k in " ".join(ctx.files()).lower()):
2116 k in " ".join(ctx.files()).lower()):
2093 break
2117 break
2094 else:
2118 else:
2095 return
2119 return
2096
2120
2097 copies = None
2121 copies = None
2098 if opts.get('copies') and rev:
2122 if opts.get('copies') and rev:
2099 copies = []
2123 copies = []
2100 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2124 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2101 for fn in ctx.files():
2125 for fn in ctx.files():
2102 rename = getrenamed(fn, rev)
2126 rename = getrenamed(fn, rev)
2103 if rename:
2127 if rename:
2104 copies.append((fn, rename[0]))
2128 copies.append((fn, rename[0]))
2105
2129
2106 displayer.show(ctx, copies=copies)
2130 displayer.show(ctx, copies=copies)
2107
2131
2108 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2132 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2109 if count == limit:
2133 if count == limit:
2110 break
2134 break
2111 if displayer.flush(ctx.rev()):
2135 if displayer.flush(ctx.rev()):
2112 count += 1
2136 count += 1
2113 displayer.close()
2137 displayer.close()
2114
2138
2115 def manifest(ui, repo, node=None, rev=None):
2139 def manifest(ui, repo, node=None, rev=None):
2116 """output the current or given revision of the project manifest
2140 """output the current or given revision of the project manifest
2117
2141
2118 Print a list of version controlled files for the given revision.
2142 Print a list of version controlled files for the given revision.
2119 If no revision is given, the first parent of the working directory
2143 If no revision is given, the first parent of the working directory
2120 is used, or the null revision if no revision is checked out.
2144 is used, or the null revision if no revision is checked out.
2121
2145
2122 With -v, print file permissions, symlink and executable bits.
2146 With -v, print file permissions, symlink and executable bits.
2123 With --debug, print file revision hashes.
2147 With --debug, print file revision hashes.
2124 """
2148 """
2125
2149
2126 if rev and node:
2150 if rev and node:
2127 raise util.Abort(_("please specify just one revision"))
2151 raise util.Abort(_("please specify just one revision"))
2128
2152
2129 if not node:
2153 if not node:
2130 node = rev
2154 node = rev
2131
2155
2132 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2156 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2133 ctx = repo[node]
2157 ctx = repo[node]
2134 for f in ctx:
2158 for f in ctx:
2135 if ui.debugflag:
2159 if ui.debugflag:
2136 ui.write("%40s " % hex(ctx.manifest()[f]))
2160 ui.write("%40s " % hex(ctx.manifest()[f]))
2137 if ui.verbose:
2161 if ui.verbose:
2138 ui.write(decor[ctx.flags(f)])
2162 ui.write(decor[ctx.flags(f)])
2139 ui.write("%s\n" % f)
2163 ui.write("%s\n" % f)
2140
2164
2141 def merge(ui, repo, node=None, **opts):
2165 def merge(ui, repo, node=None, **opts):
2142 """merge working directory with another revision
2166 """merge working directory with another revision
2143
2167
2144 The current working directory is updated with all changes made in
2168 The current working directory is updated with all changes made in
2145 the requested revision since the last common predecessor revision.
2169 the requested revision since the last common predecessor revision.
2146
2170
2147 Files that changed between either parent are marked as changed for
2171 Files that changed between either parent are marked as changed for
2148 the next commit and a commit must be performed before any further
2172 the next commit and a commit must be performed before any further
2149 updates to the repository are allowed. The next commit will have
2173 updates to the repository are allowed. The next commit will have
2150 two parents.
2174 two parents.
2151
2175
2152 If no revision is specified, the working directory's parent is a
2176 If no revision is specified, the working directory's parent is a
2153 head revision, and the current branch contains exactly one other
2177 head revision, and the current branch contains exactly one other
2154 head, the other head is merged with by default. Otherwise, an
2178 head, the other head is merged with by default. Otherwise, an
2155 explicit revision with which to merge with must be provided.
2179 explicit revision with which to merge with must be provided.
2156 """
2180 """
2157
2181
2158 if opts.get('rev') and node:
2182 if opts.get('rev') and node:
2159 raise util.Abort(_("please specify just one revision"))
2183 raise util.Abort(_("please specify just one revision"))
2160 if not node:
2184 if not node:
2161 node = opts.get('rev')
2185 node = opts.get('rev')
2162
2186
2163 if not node:
2187 if not node:
2164 branch = repo.changectx(None).branch()
2188 branch = repo.changectx(None).branch()
2165 bheads = repo.branchheads(branch)
2189 bheads = repo.branchheads(branch)
2166 if len(bheads) > 2:
2190 if len(bheads) > 2:
2167 ui.warn(_("abort: branch '%s' has %d heads - "
2191 ui.warn(_("abort: branch '%s' has %d heads - "
2168 "please merge with an explicit rev\n")
2192 "please merge with an explicit rev\n")
2169 % (branch, len(bheads)))
2193 % (branch, len(bheads)))
2170 ui.status(_("(run 'hg heads .' to see heads)\n"))
2194 ui.status(_("(run 'hg heads .' to see heads)\n"))
2171 return False
2195 return False
2172
2196
2173 parent = repo.dirstate.parents()[0]
2197 parent = repo.dirstate.parents()[0]
2174 if len(bheads) == 1:
2198 if len(bheads) == 1:
2175 if len(repo.heads()) > 1:
2199 if len(repo.heads()) > 1:
2176 ui.warn(_("abort: branch '%s' has one head - "
2200 ui.warn(_("abort: branch '%s' has one head - "
2177 "please merge with an explicit rev\n" % branch))
2201 "please merge with an explicit rev\n" % branch))
2178 ui.status(_("(run 'hg heads' to see all heads)\n"))
2202 ui.status(_("(run 'hg heads' to see all heads)\n"))
2179 return False
2203 return False
2180 msg = _('there is nothing to merge')
2204 msg = _('there is nothing to merge')
2181 if parent != repo.lookup(repo[None].branch()):
2205 if parent != repo.lookup(repo[None].branch()):
2182 msg = _('%s - use "hg update" instead') % msg
2206 msg = _('%s - use "hg update" instead') % msg
2183 raise util.Abort(msg)
2207 raise util.Abort(msg)
2184
2208
2185 if parent not in bheads:
2209 if parent not in bheads:
2186 raise util.Abort(_('working dir not at a head rev - '
2210 raise util.Abort(_('working dir not at a head rev - '
2187 'use "hg update" or merge with an explicit rev'))
2211 'use "hg update" or merge with an explicit rev'))
2188 node = parent == bheads[0] and bheads[-1] or bheads[0]
2212 node = parent == bheads[0] and bheads[-1] or bheads[0]
2189
2213
2190 if opts.get('preview'):
2214 if opts.get('preview'):
2191 p1 = repo['.']
2215 p1 = repo['.']
2192 p2 = repo[node]
2216 p2 = repo[node]
2193 common = p1.ancestor(p2)
2217 common = p1.ancestor(p2)
2194 roots, heads = [common.node()], [p2.node()]
2218 roots, heads = [common.node()], [p2.node()]
2195 displayer = cmdutil.show_changeset(ui, repo, opts)
2219 displayer = cmdutil.show_changeset(ui, repo, opts)
2196 for node in repo.changelog.nodesbetween(roots=roots, heads=heads)[0]:
2220 for node in repo.changelog.nodesbetween(roots=roots, heads=heads)[0]:
2197 if node not in roots:
2221 if node not in roots:
2198 displayer.show(repo[node])
2222 displayer.show(repo[node])
2199 displayer.close()
2223 displayer.close()
2200 return 0
2224 return 0
2201
2225
2202 return hg.merge(repo, node, force=opts.get('force'))
2226 return hg.merge(repo, node, force=opts.get('force'))
2203
2227
2204 def outgoing(ui, repo, dest=None, **opts):
2228 def outgoing(ui, repo, dest=None, **opts):
2205 """show changesets not found in the destination
2229 """show changesets not found in the destination
2206
2230
2207 Show changesets not found in the specified destination repository
2231 Show changesets not found in the specified destination repository
2208 or the default push location. These are the changesets that would
2232 or the default push location. These are the changesets that would
2209 be pushed if a push was requested.
2233 be pushed if a push was requested.
2210
2234
2211 See pull for details of valid destination formats.
2235 See pull for details of valid destination formats.
2212 """
2236 """
2213 limit = cmdutil.loglimit(opts)
2237 limit = cmdutil.loglimit(opts)
2214 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2238 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2215 dest, branches = hg.parseurl(dest, opts.get('branch'))
2239 dest, branches = hg.parseurl(dest, opts.get('branch'))
2216 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2240 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2217 if revs:
2241 if revs:
2218 revs = [repo.lookup(rev) for rev in revs]
2242 revs = [repo.lookup(rev) for rev in revs]
2219
2243
2220 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2244 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2221 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2245 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2222 o = repo.findoutgoing(other, force=opts.get('force'))
2246 o = repo.findoutgoing(other, force=opts.get('force'))
2223 if not o:
2247 if not o:
2224 ui.status(_("no changes found\n"))
2248 ui.status(_("no changes found\n"))
2225 return 1
2249 return 1
2226 o = repo.changelog.nodesbetween(o, revs)[0]
2250 o = repo.changelog.nodesbetween(o, revs)[0]
2227 if opts.get('newest_first'):
2251 if opts.get('newest_first'):
2228 o.reverse()
2252 o.reverse()
2229 displayer = cmdutil.show_changeset(ui, repo, opts)
2253 displayer = cmdutil.show_changeset(ui, repo, opts)
2230 count = 0
2254 count = 0
2231 for n in o:
2255 for n in o:
2232 if limit is not None and count >= limit:
2256 if limit is not None and count >= limit:
2233 break
2257 break
2234 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2258 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2235 if opts.get('no_merges') and len(parents) == 2:
2259 if opts.get('no_merges') and len(parents) == 2:
2236 continue
2260 continue
2237 count += 1
2261 count += 1
2238 displayer.show(repo[n])
2262 displayer.show(repo[n])
2239 displayer.close()
2263 displayer.close()
2240
2264
2241 def parents(ui, repo, file_=None, **opts):
2265 def parents(ui, repo, file_=None, **opts):
2242 """show the parents of the working directory or revision
2266 """show the parents of the working directory or revision
2243
2267
2244 Print the working directory's parent revisions. If a revision is
2268 Print the working directory's parent revisions. If a revision is
2245 given via -r/--rev, the parent of that revision will be printed.
2269 given via -r/--rev, the parent of that revision will be printed.
2246 If a file argument is given, the revision in which the file was
2270 If a file argument is given, the revision in which the file was
2247 last changed (before the working directory revision or the
2271 last changed (before the working directory revision or the
2248 argument to --rev if given) is printed.
2272 argument to --rev if given) is printed.
2249 """
2273 """
2250 rev = opts.get('rev')
2274 rev = opts.get('rev')
2251 if rev:
2275 if rev:
2252 ctx = repo[rev]
2276 ctx = repo[rev]
2253 else:
2277 else:
2254 ctx = repo[None]
2278 ctx = repo[None]
2255
2279
2256 if file_:
2280 if file_:
2257 m = cmdutil.match(repo, (file_,), opts)
2281 m = cmdutil.match(repo, (file_,), opts)
2258 if m.anypats() or len(m.files()) != 1:
2282 if m.anypats() or len(m.files()) != 1:
2259 raise util.Abort(_('can only specify an explicit filename'))
2283 raise util.Abort(_('can only specify an explicit filename'))
2260 file_ = m.files()[0]
2284 file_ = m.files()[0]
2261 filenodes = []
2285 filenodes = []
2262 for cp in ctx.parents():
2286 for cp in ctx.parents():
2263 if not cp:
2287 if not cp:
2264 continue
2288 continue
2265 try:
2289 try:
2266 filenodes.append(cp.filenode(file_))
2290 filenodes.append(cp.filenode(file_))
2267 except error.LookupError:
2291 except error.LookupError:
2268 pass
2292 pass
2269 if not filenodes:
2293 if not filenodes:
2270 raise util.Abort(_("'%s' not found in manifest!") % file_)
2294 raise util.Abort(_("'%s' not found in manifest!") % file_)
2271 fl = repo.file(file_)
2295 fl = repo.file(file_)
2272 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2296 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2273 else:
2297 else:
2274 p = [cp.node() for cp in ctx.parents()]
2298 p = [cp.node() for cp in ctx.parents()]
2275
2299
2276 displayer = cmdutil.show_changeset(ui, repo, opts)
2300 displayer = cmdutil.show_changeset(ui, repo, opts)
2277 for n in p:
2301 for n in p:
2278 if n != nullid:
2302 if n != nullid:
2279 displayer.show(repo[n])
2303 displayer.show(repo[n])
2280 displayer.close()
2304 displayer.close()
2281
2305
2282 def paths(ui, repo, search=None):
2306 def paths(ui, repo, search=None):
2283 """show aliases for remote repositories
2307 """show aliases for remote repositories
2284
2308
2285 Show definition of symbolic path name NAME. If no name is given,
2309 Show definition of symbolic path name NAME. If no name is given,
2286 show definition of all available names.
2310 show definition of all available names.
2287
2311
2288 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2312 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2289 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2313 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2290
2314
2291 See 'hg help urls' for more information.
2315 See 'hg help urls' for more information.
2292 """
2316 """
2293 if search:
2317 if search:
2294 for name, path in ui.configitems("paths"):
2318 for name, path in ui.configitems("paths"):
2295 if name == search:
2319 if name == search:
2296 ui.write("%s\n" % url.hidepassword(path))
2320 ui.write("%s\n" % url.hidepassword(path))
2297 return
2321 return
2298 ui.warn(_("not found!\n"))
2322 ui.warn(_("not found!\n"))
2299 return 1
2323 return 1
2300 else:
2324 else:
2301 for name, path in ui.configitems("paths"):
2325 for name, path in ui.configitems("paths"):
2302 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2326 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2303
2327
2304 def postincoming(ui, repo, modheads, optupdate, checkout):
2328 def postincoming(ui, repo, modheads, optupdate, checkout):
2305 if modheads == 0:
2329 if modheads == 0:
2306 return
2330 return
2307 if optupdate:
2331 if optupdate:
2308 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2332 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2309 return hg.update(repo, checkout)
2333 return hg.update(repo, checkout)
2310 else:
2334 else:
2311 ui.status(_("not updating, since new heads added\n"))
2335 ui.status(_("not updating, since new heads added\n"))
2312 if modheads > 1:
2336 if modheads > 1:
2313 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2337 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2314 else:
2338 else:
2315 ui.status(_("(run 'hg update' to get a working copy)\n"))
2339 ui.status(_("(run 'hg update' to get a working copy)\n"))
2316
2340
2317 def pull(ui, repo, source="default", **opts):
2341 def pull(ui, repo, source="default", **opts):
2318 """pull changes from the specified source
2342 """pull changes from the specified source
2319
2343
2320 Pull changes from a remote repository to a local one.
2344 Pull changes from a remote repository to a local one.
2321
2345
2322 This finds all changes from the repository at the specified path
2346 This finds all changes from the repository at the specified path
2323 or URL and adds them to a local repository (the current one unless
2347 or URL and adds them to a local repository (the current one unless
2324 -R is specified). By default, this does not update the copy of the
2348 -R is specified). By default, this does not update the copy of the
2325 project in the working directory.
2349 project in the working directory.
2326
2350
2327 Use hg incoming if you want to see what would have been added by a
2351 Use hg incoming if you want to see what would have been added by a
2328 pull at the time you issued this command. If you then decide to
2352 pull at the time you issued this command. If you then decide to
2329 added those changes to the repository, you should use pull -r X
2353 added those changes to the repository, you should use pull -r X
2330 where X is the last changeset listed by hg incoming.
2354 where X is the last changeset listed by hg incoming.
2331
2355
2332 If SOURCE is omitted, the 'default' path will be used.
2356 If SOURCE is omitted, the 'default' path will be used.
2333 See 'hg help urls' for more information.
2357 See 'hg help urls' for more information.
2334 """
2358 """
2335 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2359 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2336 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2360 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2337 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2361 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2338 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2362 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2339 if revs:
2363 if revs:
2340 try:
2364 try:
2341 revs = [other.lookup(rev) for rev in revs]
2365 revs = [other.lookup(rev) for rev in revs]
2342 except error.CapabilityError:
2366 except error.CapabilityError:
2343 err = _("Other repository doesn't support revision lookup, "
2367 err = _("Other repository doesn't support revision lookup, "
2344 "so a rev cannot be specified.")
2368 "so a rev cannot be specified.")
2345 raise util.Abort(err)
2369 raise util.Abort(err)
2346
2370
2347 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2371 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2348 if checkout:
2372 if checkout:
2349 checkout = str(repo.changelog.rev(other.lookup(checkout)))
2373 checkout = str(repo.changelog.rev(other.lookup(checkout)))
2350 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2374 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2351
2375
2352 def push(ui, repo, dest=None, **opts):
2376 def push(ui, repo, dest=None, **opts):
2353 """push changes to the specified destination
2377 """push changes to the specified destination
2354
2378
2355 Push changes from the local repository to the specified destination.
2379 Push changes from the local repository to the specified destination.
2356
2380
2357 This is the symmetrical operation for pull. It moves changes from
2381 This is the symmetrical operation for pull. It moves changes from
2358 the current repository to a different one. If the destination is
2382 the current repository to a different one. If the destination is
2359 local this is identical to a pull in that directory from the
2383 local this is identical to a pull in that directory from the
2360 current one.
2384 current one.
2361
2385
2362 By default, push will refuse to run if it detects the result would
2386 By default, push will refuse to run if it detects the result would
2363 increase the number of remote heads. This generally indicates the
2387 increase the number of remote heads. This generally indicates the
2364 user forgot to pull and merge before pushing.
2388 user forgot to pull and merge before pushing.
2365
2389
2366 If -r/--rev is used, the named revision and all its ancestors will
2390 If -r/--rev is used, the named revision and all its ancestors will
2367 be pushed to the remote repository.
2391 be pushed to the remote repository.
2368
2392
2369 Please see 'hg help urls' for important details about ``ssh://``
2393 Please see 'hg help urls' for important details about ``ssh://``
2370 URLs. If DESTINATION is omitted, a default path will be used.
2394 URLs. If DESTINATION is omitted, a default path will be used.
2371 """
2395 """
2372 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2396 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2373 dest, branches = hg.parseurl(dest, opts.get('branch'))
2397 dest, branches = hg.parseurl(dest, opts.get('branch'))
2374 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2398 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2375 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2399 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2376 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2400 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2377 if revs:
2401 if revs:
2378 revs = [repo.lookup(rev) for rev in revs]
2402 revs = [repo.lookup(rev) for rev in revs]
2379
2403
2380 # push subrepos depth-first for coherent ordering
2404 # push subrepos depth-first for coherent ordering
2381 c = repo['']
2405 c = repo['']
2382 subs = c.substate # only repos that are committed
2406 subs = c.substate # only repos that are committed
2383 for s in sorted(subs):
2407 for s in sorted(subs):
2384 c.sub(s).push(opts.get('force'))
2408 c.sub(s).push(opts.get('force'))
2385
2409
2386 r = repo.push(other, opts.get('force'), revs=revs)
2410 r = repo.push(other, opts.get('force'), revs=revs)
2387 return r == 0
2411 return r == 0
2388
2412
2389 def recover(ui, repo):
2413 def recover(ui, repo):
2390 """roll back an interrupted transaction
2414 """roll back an interrupted transaction
2391
2415
2392 Recover from an interrupted commit or pull.
2416 Recover from an interrupted commit or pull.
2393
2417
2394 This command tries to fix the repository status after an
2418 This command tries to fix the repository status after an
2395 interrupted operation. It should only be necessary when Mercurial
2419 interrupted operation. It should only be necessary when Mercurial
2396 suggests it.
2420 suggests it.
2397 """
2421 """
2398 if repo.recover():
2422 if repo.recover():
2399 return hg.verify(repo)
2423 return hg.verify(repo)
2400 return 1
2424 return 1
2401
2425
2402 def remove(ui, repo, *pats, **opts):
2426 def remove(ui, repo, *pats, **opts):
2403 """remove the specified files on the next commit
2427 """remove the specified files on the next commit
2404
2428
2405 Schedule the indicated files for removal from the repository.
2429 Schedule the indicated files for removal from the repository.
2406
2430
2407 This only removes files from the current branch, not from the
2431 This only removes files from the current branch, not from the
2408 entire project history. -A/--after can be used to remove only
2432 entire project history. -A/--after can be used to remove only
2409 files that have already been deleted, -f/--force can be used to
2433 files that have already been deleted, -f/--force can be used to
2410 force deletion, and -Af can be used to remove files from the next
2434 force deletion, and -Af can be used to remove files from the next
2411 revision without deleting them from the working directory.
2435 revision without deleting them from the working directory.
2412
2436
2413 The following table details the behavior of remove for different
2437 The following table details the behavior of remove for different
2414 file states (columns) and option combinations (rows). The file
2438 file states (columns) and option combinations (rows). The file
2415 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
2439 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
2416 reported by hg status). The actions are Warn, Remove (from branch)
2440 reported by hg status). The actions are Warn, Remove (from branch)
2417 and Delete (from disk)::
2441 and Delete (from disk)::
2418
2442
2419 A C M !
2443 A C M !
2420 none W RD W R
2444 none W RD W R
2421 -f R RD RD R
2445 -f R RD RD R
2422 -A W W W R
2446 -A W W W R
2423 -Af R R R R
2447 -Af R R R R
2424
2448
2425 This command schedules the files to be removed at the next commit.
2449 This command schedules the files to be removed at the next commit.
2426 To undo a remove before that, see hg revert.
2450 To undo a remove before that, see hg revert.
2427 """
2451 """
2428
2452
2429 after, force = opts.get('after'), opts.get('force')
2453 after, force = opts.get('after'), opts.get('force')
2430 if not pats and not after:
2454 if not pats and not after:
2431 raise util.Abort(_('no files specified'))
2455 raise util.Abort(_('no files specified'))
2432
2456
2433 m = cmdutil.match(repo, pats, opts)
2457 m = cmdutil.match(repo, pats, opts)
2434 s = repo.status(match=m, clean=True)
2458 s = repo.status(match=m, clean=True)
2435 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2459 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2436
2460
2437 for f in m.files():
2461 for f in m.files():
2438 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2462 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2439 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2463 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2440
2464
2441 def warn(files, reason):
2465 def warn(files, reason):
2442 for f in files:
2466 for f in files:
2443 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2467 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2444 % (m.rel(f), reason))
2468 % (m.rel(f), reason))
2445
2469
2446 if force:
2470 if force:
2447 remove, forget = modified + deleted + clean, added
2471 remove, forget = modified + deleted + clean, added
2448 elif after:
2472 elif after:
2449 remove, forget = deleted, []
2473 remove, forget = deleted, []
2450 warn(modified + added + clean, _('still exists'))
2474 warn(modified + added + clean, _('still exists'))
2451 else:
2475 else:
2452 remove, forget = deleted + clean, []
2476 remove, forget = deleted + clean, []
2453 warn(modified, _('is modified'))
2477 warn(modified, _('is modified'))
2454 warn(added, _('has been marked for add'))
2478 warn(added, _('has been marked for add'))
2455
2479
2456 for f in sorted(remove + forget):
2480 for f in sorted(remove + forget):
2457 if ui.verbose or not m.exact(f):
2481 if ui.verbose or not m.exact(f):
2458 ui.status(_('removing %s\n') % m.rel(f))
2482 ui.status(_('removing %s\n') % m.rel(f))
2459
2483
2460 repo.forget(forget)
2484 repo.forget(forget)
2461 repo.remove(remove, unlink=not after)
2485 repo.remove(remove, unlink=not after)
2462
2486
2463 def rename(ui, repo, *pats, **opts):
2487 def rename(ui, repo, *pats, **opts):
2464 """rename files; equivalent of copy + remove
2488 """rename files; equivalent of copy + remove
2465
2489
2466 Mark dest as copies of sources; mark sources for deletion. If dest
2490 Mark dest as copies of sources; mark sources for deletion. If dest
2467 is a directory, copies are put in that directory. If dest is a
2491 is a directory, copies are put in that directory. If dest is a
2468 file, there can only be one source.
2492 file, there can only be one source.
2469
2493
2470 By default, this command copies the contents of files as they
2494 By default, this command copies the contents of files as they
2471 exist in the working directory. If invoked with -A/--after, the
2495 exist in the working directory. If invoked with -A/--after, the
2472 operation is recorded, but no copying is performed.
2496 operation is recorded, but no copying is performed.
2473
2497
2474 This command takes effect at the next commit. To undo a rename
2498 This command takes effect at the next commit. To undo a rename
2475 before that, see hg revert.
2499 before that, see hg revert.
2476 """
2500 """
2477 wlock = repo.wlock(False)
2501 wlock = repo.wlock(False)
2478 try:
2502 try:
2479 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2503 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2480 finally:
2504 finally:
2481 wlock.release()
2505 wlock.release()
2482
2506
2483 def resolve(ui, repo, *pats, **opts):
2507 def resolve(ui, repo, *pats, **opts):
2484 """retry file merges from a merge or update
2508 """retry file merges from a merge or update
2485
2509
2486 This command can cleanly retry unresolved file merges using file
2510 This command can cleanly retry unresolved file merges using file
2487 revisions preserved from the last update or merge.
2511 revisions preserved from the last update or merge.
2488
2512
2489 If a conflict is resolved manually, please note that the changes
2513 If a conflict is resolved manually, please note that the changes
2490 will be overwritten if the merge is retried with resolve. The
2514 will be overwritten if the merge is retried with resolve. The
2491 -m/--mark switch should be used to mark the file as resolved.
2515 -m/--mark switch should be used to mark the file as resolved.
2492
2516
2493 You can specify a set of files to operate on, or use the -a/--all
2517 You can specify a set of files to operate on, or use the -a/--all
2494 switch to select all unresolved files.
2518 switch to select all unresolved files.
2495
2519
2496 This command also allows listing resolved files and manually
2520 This command also allows listing resolved files and manually
2497 indicating whether or not files are resolved. All files must be
2521 indicating whether or not files are resolved. All files must be
2498 marked as resolved before a commit is permitted.
2522 marked as resolved before a commit is permitted.
2499
2523
2500 The codes used to show the status of files are::
2524 The codes used to show the status of files are::
2501
2525
2502 U = unresolved
2526 U = unresolved
2503 R = resolved
2527 R = resolved
2504 """
2528 """
2505
2529
2506 all, mark, unmark, show, nostatus = \
2530 all, mark, unmark, show, nostatus = \
2507 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
2531 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
2508
2532
2509 if (show and (mark or unmark)) or (mark and unmark):
2533 if (show and (mark or unmark)) or (mark and unmark):
2510 raise util.Abort(_("too many options specified"))
2534 raise util.Abort(_("too many options specified"))
2511 if pats and all:
2535 if pats and all:
2512 raise util.Abort(_("can't specify --all and patterns"))
2536 raise util.Abort(_("can't specify --all and patterns"))
2513 if not (all or pats or show or mark or unmark):
2537 if not (all or pats or show or mark or unmark):
2514 raise util.Abort(_('no files or directories specified; '
2538 raise util.Abort(_('no files or directories specified; '
2515 'use --all to remerge all files'))
2539 'use --all to remerge all files'))
2516
2540
2517 ms = merge_.mergestate(repo)
2541 ms = merge_.mergestate(repo)
2518 m = cmdutil.match(repo, pats, opts)
2542 m = cmdutil.match(repo, pats, opts)
2519
2543
2520 for f in ms:
2544 for f in ms:
2521 if m(f):
2545 if m(f):
2522 if show:
2546 if show:
2523 if nostatus:
2547 if nostatus:
2524 ui.write("%s\n" % f)
2548 ui.write("%s\n" % f)
2525 else:
2549 else:
2526 ui.write("%s %s\n" % (ms[f].upper(), f))
2550 ui.write("%s %s\n" % (ms[f].upper(), f))
2527 elif mark:
2551 elif mark:
2528 ms.mark(f, "r")
2552 ms.mark(f, "r")
2529 elif unmark:
2553 elif unmark:
2530 ms.mark(f, "u")
2554 ms.mark(f, "u")
2531 else:
2555 else:
2532 wctx = repo[None]
2556 wctx = repo[None]
2533 mctx = wctx.parents()[-1]
2557 mctx = wctx.parents()[-1]
2534
2558
2535 # backup pre-resolve (merge uses .orig for its own purposes)
2559 # backup pre-resolve (merge uses .orig for its own purposes)
2536 a = repo.wjoin(f)
2560 a = repo.wjoin(f)
2537 util.copyfile(a, a + ".resolve")
2561 util.copyfile(a, a + ".resolve")
2538
2562
2539 # resolve file
2563 # resolve file
2540 ms.resolve(f, wctx, mctx)
2564 ms.resolve(f, wctx, mctx)
2541
2565
2542 # replace filemerge's .orig file with our resolve file
2566 # replace filemerge's .orig file with our resolve file
2543 util.rename(a + ".resolve", a + ".orig")
2567 util.rename(a + ".resolve", a + ".orig")
2544
2568
2545 def revert(ui, repo, *pats, **opts):
2569 def revert(ui, repo, *pats, **opts):
2546 """restore individual files or directories to an earlier state
2570 """restore individual files or directories to an earlier state
2547
2571
2548 (Use update -r to check out earlier revisions, revert does not
2572 (Use update -r to check out earlier revisions, revert does not
2549 change the working directory parents.)
2573 change the working directory parents.)
2550
2574
2551 With no revision specified, revert the named files or directories
2575 With no revision specified, revert the named files or directories
2552 to the contents they had in the parent of the working directory.
2576 to the contents they had in the parent of the working directory.
2553 This restores the contents of the affected files to an unmodified
2577 This restores the contents of the affected files to an unmodified
2554 state and unschedules adds, removes, copies, and renames. If the
2578 state and unschedules adds, removes, copies, and renames. If the
2555 working directory has two parents, you must explicitly specify a
2579 working directory has two parents, you must explicitly specify a
2556 revision.
2580 revision.
2557
2581
2558 Using the -r/--rev option, revert the given files or directories
2582 Using the -r/--rev option, revert the given files or directories
2559 to their contents as of a specific revision. This can be helpful
2583 to their contents as of a specific revision. This can be helpful
2560 to "roll back" some or all of an earlier change. See 'hg help
2584 to "roll back" some or all of an earlier change. See 'hg help
2561 dates' for a list of formats valid for -d/--date.
2585 dates' for a list of formats valid for -d/--date.
2562
2586
2563 Revert modifies the working directory. It does not commit any
2587 Revert modifies the working directory. It does not commit any
2564 changes, or change the parent of the working directory. If you
2588 changes, or change the parent of the working directory. If you
2565 revert to a revision other than the parent of the working
2589 revert to a revision other than the parent of the working
2566 directory, the reverted files will thus appear modified
2590 directory, the reverted files will thus appear modified
2567 afterwards.
2591 afterwards.
2568
2592
2569 If a file has been deleted, it is restored. If the executable mode
2593 If a file has been deleted, it is restored. If the executable mode
2570 of a file was changed, it is reset.
2594 of a file was changed, it is reset.
2571
2595
2572 If names are given, all files matching the names are reverted.
2596 If names are given, all files matching the names are reverted.
2573 If no arguments are given, no files are reverted.
2597 If no arguments are given, no files are reverted.
2574
2598
2575 Modified files are saved with a .orig suffix before reverting.
2599 Modified files are saved with a .orig suffix before reverting.
2576 To disable these backups, use --no-backup.
2600 To disable these backups, use --no-backup.
2577 """
2601 """
2578
2602
2579 if opts["date"]:
2603 if opts["date"]:
2580 if opts["rev"]:
2604 if opts["rev"]:
2581 raise util.Abort(_("you can't specify a revision and a date"))
2605 raise util.Abort(_("you can't specify a revision and a date"))
2582 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2606 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2583
2607
2584 if not pats and not opts.get('all'):
2608 if not pats and not opts.get('all'):
2585 raise util.Abort(_('no files or directories specified; '
2609 raise util.Abort(_('no files or directories specified; '
2586 'use --all to revert the whole repo'))
2610 'use --all to revert the whole repo'))
2587
2611
2588 parent, p2 = repo.dirstate.parents()
2612 parent, p2 = repo.dirstate.parents()
2589 if not opts.get('rev') and p2 != nullid:
2613 if not opts.get('rev') and p2 != nullid:
2590 raise util.Abort(_('uncommitted merge - please provide a '
2614 raise util.Abort(_('uncommitted merge - please provide a '
2591 'specific revision'))
2615 'specific revision'))
2592 ctx = repo[opts.get('rev')]
2616 ctx = repo[opts.get('rev')]
2593 node = ctx.node()
2617 node = ctx.node()
2594 mf = ctx.manifest()
2618 mf = ctx.manifest()
2595 if node == parent:
2619 if node == parent:
2596 pmf = mf
2620 pmf = mf
2597 else:
2621 else:
2598 pmf = None
2622 pmf = None
2599
2623
2600 # need all matching names in dirstate and manifest of target rev,
2624 # need all matching names in dirstate and manifest of target rev,
2601 # so have to walk both. do not print errors if files exist in one
2625 # so have to walk both. do not print errors if files exist in one
2602 # but not other.
2626 # but not other.
2603
2627
2604 names = {}
2628 names = {}
2605
2629
2606 wlock = repo.wlock()
2630 wlock = repo.wlock()
2607 try:
2631 try:
2608 # walk dirstate.
2632 # walk dirstate.
2609
2633
2610 m = cmdutil.match(repo, pats, opts)
2634 m = cmdutil.match(repo, pats, opts)
2611 m.bad = lambda x, y: False
2635 m.bad = lambda x, y: False
2612 for abs in repo.walk(m):
2636 for abs in repo.walk(m):
2613 names[abs] = m.rel(abs), m.exact(abs)
2637 names[abs] = m.rel(abs), m.exact(abs)
2614
2638
2615 # walk target manifest.
2639 # walk target manifest.
2616
2640
2617 def badfn(path, msg):
2641 def badfn(path, msg):
2618 if path in names:
2642 if path in names:
2619 return
2643 return
2620 path_ = path + '/'
2644 path_ = path + '/'
2621 for f in names:
2645 for f in names:
2622 if f.startswith(path_):
2646 if f.startswith(path_):
2623 return
2647 return
2624 ui.warn("%s: %s\n" % (m.rel(path), msg))
2648 ui.warn("%s: %s\n" % (m.rel(path), msg))
2625
2649
2626 m = cmdutil.match(repo, pats, opts)
2650 m = cmdutil.match(repo, pats, opts)
2627 m.bad = badfn
2651 m.bad = badfn
2628 for abs in repo[node].walk(m):
2652 for abs in repo[node].walk(m):
2629 if abs not in names:
2653 if abs not in names:
2630 names[abs] = m.rel(abs), m.exact(abs)
2654 names[abs] = m.rel(abs), m.exact(abs)
2631
2655
2632 m = cmdutil.matchfiles(repo, names)
2656 m = cmdutil.matchfiles(repo, names)
2633 changes = repo.status(match=m)[:4]
2657 changes = repo.status(match=m)[:4]
2634 modified, added, removed, deleted = map(set, changes)
2658 modified, added, removed, deleted = map(set, changes)
2635
2659
2636 # if f is a rename, also revert the source
2660 # if f is a rename, also revert the source
2637 cwd = repo.getcwd()
2661 cwd = repo.getcwd()
2638 for f in added:
2662 for f in added:
2639 src = repo.dirstate.copied(f)
2663 src = repo.dirstate.copied(f)
2640 if src and src not in names and repo.dirstate[src] == 'r':
2664 if src and src not in names and repo.dirstate[src] == 'r':
2641 removed.add(src)
2665 removed.add(src)
2642 names[src] = (repo.pathto(src, cwd), True)
2666 names[src] = (repo.pathto(src, cwd), True)
2643
2667
2644 def removeforget(abs):
2668 def removeforget(abs):
2645 if repo.dirstate[abs] == 'a':
2669 if repo.dirstate[abs] == 'a':
2646 return _('forgetting %s\n')
2670 return _('forgetting %s\n')
2647 return _('removing %s\n')
2671 return _('removing %s\n')
2648
2672
2649 revert = ([], _('reverting %s\n'))
2673 revert = ([], _('reverting %s\n'))
2650 add = ([], _('adding %s\n'))
2674 add = ([], _('adding %s\n'))
2651 remove = ([], removeforget)
2675 remove = ([], removeforget)
2652 undelete = ([], _('undeleting %s\n'))
2676 undelete = ([], _('undeleting %s\n'))
2653
2677
2654 disptable = (
2678 disptable = (
2655 # dispatch table:
2679 # dispatch table:
2656 # file state
2680 # file state
2657 # action if in target manifest
2681 # action if in target manifest
2658 # action if not in target manifest
2682 # action if not in target manifest
2659 # make backup if in target manifest
2683 # make backup if in target manifest
2660 # make backup if not in target manifest
2684 # make backup if not in target manifest
2661 (modified, revert, remove, True, True),
2685 (modified, revert, remove, True, True),
2662 (added, revert, remove, True, False),
2686 (added, revert, remove, True, False),
2663 (removed, undelete, None, False, False),
2687 (removed, undelete, None, False, False),
2664 (deleted, revert, remove, False, False),
2688 (deleted, revert, remove, False, False),
2665 )
2689 )
2666
2690
2667 for abs, (rel, exact) in sorted(names.items()):
2691 for abs, (rel, exact) in sorted(names.items()):
2668 mfentry = mf.get(abs)
2692 mfentry = mf.get(abs)
2669 target = repo.wjoin(abs)
2693 target = repo.wjoin(abs)
2670 def handle(xlist, dobackup):
2694 def handle(xlist, dobackup):
2671 xlist[0].append(abs)
2695 xlist[0].append(abs)
2672 if dobackup and not opts.get('no_backup') and util.lexists(target):
2696 if dobackup and not opts.get('no_backup') and util.lexists(target):
2673 bakname = "%s.orig" % rel
2697 bakname = "%s.orig" % rel
2674 ui.note(_('saving current version of %s as %s\n') %
2698 ui.note(_('saving current version of %s as %s\n') %
2675 (rel, bakname))
2699 (rel, bakname))
2676 if not opts.get('dry_run'):
2700 if not opts.get('dry_run'):
2677 util.copyfile(target, bakname)
2701 util.copyfile(target, bakname)
2678 if ui.verbose or not exact:
2702 if ui.verbose or not exact:
2679 msg = xlist[1]
2703 msg = xlist[1]
2680 if not isinstance(msg, basestring):
2704 if not isinstance(msg, basestring):
2681 msg = msg(abs)
2705 msg = msg(abs)
2682 ui.status(msg % rel)
2706 ui.status(msg % rel)
2683 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2707 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2684 if abs not in table:
2708 if abs not in table:
2685 continue
2709 continue
2686 # file has changed in dirstate
2710 # file has changed in dirstate
2687 if mfentry:
2711 if mfentry:
2688 handle(hitlist, backuphit)
2712 handle(hitlist, backuphit)
2689 elif misslist is not None:
2713 elif misslist is not None:
2690 handle(misslist, backupmiss)
2714 handle(misslist, backupmiss)
2691 break
2715 break
2692 else:
2716 else:
2693 if abs not in repo.dirstate:
2717 if abs not in repo.dirstate:
2694 if mfentry:
2718 if mfentry:
2695 handle(add, True)
2719 handle(add, True)
2696 elif exact:
2720 elif exact:
2697 ui.warn(_('file not managed: %s\n') % rel)
2721 ui.warn(_('file not managed: %s\n') % rel)
2698 continue
2722 continue
2699 # file has not changed in dirstate
2723 # file has not changed in dirstate
2700 if node == parent:
2724 if node == parent:
2701 if exact:
2725 if exact:
2702 ui.warn(_('no changes needed to %s\n') % rel)
2726 ui.warn(_('no changes needed to %s\n') % rel)
2703 continue
2727 continue
2704 if pmf is None:
2728 if pmf is None:
2705 # only need parent manifest in this unlikely case,
2729 # only need parent manifest in this unlikely case,
2706 # so do not read by default
2730 # so do not read by default
2707 pmf = repo[parent].manifest()
2731 pmf = repo[parent].manifest()
2708 if abs in pmf:
2732 if abs in pmf:
2709 if mfentry:
2733 if mfentry:
2710 # if version of file is same in parent and target
2734 # if version of file is same in parent and target
2711 # manifests, do nothing
2735 # manifests, do nothing
2712 if (pmf[abs] != mfentry or
2736 if (pmf[abs] != mfentry or
2713 pmf.flags(abs) != mf.flags(abs)):
2737 pmf.flags(abs) != mf.flags(abs)):
2714 handle(revert, False)
2738 handle(revert, False)
2715 else:
2739 else:
2716 handle(remove, False)
2740 handle(remove, False)
2717
2741
2718 if not opts.get('dry_run'):
2742 if not opts.get('dry_run'):
2719 def checkout(f):
2743 def checkout(f):
2720 fc = ctx[f]
2744 fc = ctx[f]
2721 repo.wwrite(f, fc.data(), fc.flags())
2745 repo.wwrite(f, fc.data(), fc.flags())
2722
2746
2723 audit_path = util.path_auditor(repo.root)
2747 audit_path = util.path_auditor(repo.root)
2724 for f in remove[0]:
2748 for f in remove[0]:
2725 if repo.dirstate[f] == 'a':
2749 if repo.dirstate[f] == 'a':
2726 repo.dirstate.forget(f)
2750 repo.dirstate.forget(f)
2727 continue
2751 continue
2728 audit_path(f)
2752 audit_path(f)
2729 try:
2753 try:
2730 util.unlink(repo.wjoin(f))
2754 util.unlink(repo.wjoin(f))
2731 except OSError:
2755 except OSError:
2732 pass
2756 pass
2733 repo.dirstate.remove(f)
2757 repo.dirstate.remove(f)
2734
2758
2735 normal = None
2759 normal = None
2736 if node == parent:
2760 if node == parent:
2737 # We're reverting to our parent. If possible, we'd like status
2761 # We're reverting to our parent. If possible, we'd like status
2738 # to report the file as clean. We have to use normallookup for
2762 # to report the file as clean. We have to use normallookup for
2739 # merges to avoid losing information about merged/dirty files.
2763 # merges to avoid losing information about merged/dirty files.
2740 if p2 != nullid:
2764 if p2 != nullid:
2741 normal = repo.dirstate.normallookup
2765 normal = repo.dirstate.normallookup
2742 else:
2766 else:
2743 normal = repo.dirstate.normal
2767 normal = repo.dirstate.normal
2744 for f in revert[0]:
2768 for f in revert[0]:
2745 checkout(f)
2769 checkout(f)
2746 if normal:
2770 if normal:
2747 normal(f)
2771 normal(f)
2748
2772
2749 for f in add[0]:
2773 for f in add[0]:
2750 checkout(f)
2774 checkout(f)
2751 repo.dirstate.add(f)
2775 repo.dirstate.add(f)
2752
2776
2753 normal = repo.dirstate.normallookup
2777 normal = repo.dirstate.normallookup
2754 if node == parent and p2 == nullid:
2778 if node == parent and p2 == nullid:
2755 normal = repo.dirstate.normal
2779 normal = repo.dirstate.normal
2756 for f in undelete[0]:
2780 for f in undelete[0]:
2757 checkout(f)
2781 checkout(f)
2758 normal(f)
2782 normal(f)
2759
2783
2760 finally:
2784 finally:
2761 wlock.release()
2785 wlock.release()
2762
2786
2763 def rollback(ui, repo):
2787 def rollback(ui, repo):
2764 """roll back the last transaction
2788 """roll back the last transaction
2765
2789
2766 This command should be used with care. There is only one level of
2790 This command should be used with care. There is only one level of
2767 rollback, and there is no way to undo a rollback. It will also
2791 rollback, and there is no way to undo a rollback. It will also
2768 restore the dirstate at the time of the last transaction, losing
2792 restore the dirstate at the time of the last transaction, losing
2769 any dirstate changes since that time. This command does not alter
2793 any dirstate changes since that time. This command does not alter
2770 the working directory.
2794 the working directory.
2771
2795
2772 Transactions are used to encapsulate the effects of all commands
2796 Transactions are used to encapsulate the effects of all commands
2773 that create new changesets or propagate existing changesets into a
2797 that create new changesets or propagate existing changesets into a
2774 repository. For example, the following commands are transactional,
2798 repository. For example, the following commands are transactional,
2775 and their effects can be rolled back:
2799 and their effects can be rolled back:
2776
2800
2777 - commit
2801 - commit
2778 - import
2802 - import
2779 - pull
2803 - pull
2780 - push (with this repository as the destination)
2804 - push (with this repository as the destination)
2781 - unbundle
2805 - unbundle
2782
2806
2783 This command is not intended for use on public repositories. Once
2807 This command is not intended for use on public repositories. Once
2784 changes are visible for pull by other users, rolling a transaction
2808 changes are visible for pull by other users, rolling a transaction
2785 back locally is ineffective (someone else may already have pulled
2809 back locally is ineffective (someone else may already have pulled
2786 the changes). Furthermore, a race is possible with readers of the
2810 the changes). Furthermore, a race is possible with readers of the
2787 repository; for example an in-progress pull from the repository
2811 repository; for example an in-progress pull from the repository
2788 may fail if a rollback is performed.
2812 may fail if a rollback is performed.
2789 """
2813 """
2790 repo.rollback()
2814 repo.rollback()
2791
2815
2792 def root(ui, repo):
2816 def root(ui, repo):
2793 """print the root (top) of the current working directory
2817 """print the root (top) of the current working directory
2794
2818
2795 Print the root directory of the current repository.
2819 Print the root directory of the current repository.
2796 """
2820 """
2797 ui.write(repo.root + "\n")
2821 ui.write(repo.root + "\n")
2798
2822
2799 def serve(ui, repo, **opts):
2823 def serve(ui, repo, **opts):
2800 """export the repository via HTTP
2824 """export the repository via HTTP
2801
2825
2802 Start a local HTTP repository browser and pull server.
2826 Start a local HTTP repository browser and pull server.
2803
2827
2804 By default, the server logs accesses to stdout and errors to
2828 By default, the server logs accesses to stdout and errors to
2805 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
2829 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
2806 files.
2830 files.
2807 """
2831 """
2808
2832
2809 if opts["stdio"]:
2833 if opts["stdio"]:
2810 if repo is None:
2834 if repo is None:
2811 raise error.RepoError(_("There is no Mercurial repository here"
2835 raise error.RepoError(_("There is no Mercurial repository here"
2812 " (.hg not found)"))
2836 " (.hg not found)"))
2813 s = sshserver.sshserver(ui, repo)
2837 s = sshserver.sshserver(ui, repo)
2814 s.serve_forever()
2838 s.serve_forever()
2815
2839
2816 baseui = repo and repo.baseui or ui
2840 baseui = repo and repo.baseui or ui
2817 optlist = ("name templates style address port prefix ipv6"
2841 optlist = ("name templates style address port prefix ipv6"
2818 " accesslog errorlog webdir_conf certificate encoding")
2842 " accesslog errorlog webdir_conf certificate encoding")
2819 for o in optlist.split():
2843 for o in optlist.split():
2820 if opts.get(o, None):
2844 if opts.get(o, None):
2821 baseui.setconfig("web", o, str(opts[o]))
2845 baseui.setconfig("web", o, str(opts[o]))
2822 if (repo is not None) and (repo.ui != baseui):
2846 if (repo is not None) and (repo.ui != baseui):
2823 repo.ui.setconfig("web", o, str(opts[o]))
2847 repo.ui.setconfig("web", o, str(opts[o]))
2824
2848
2825 if repo is None and not ui.config("web", "webdir_conf"):
2849 if repo is None and not ui.config("web", "webdir_conf"):
2826 raise error.RepoError(_("There is no Mercurial repository here"
2850 raise error.RepoError(_("There is no Mercurial repository here"
2827 " (.hg not found)"))
2851 " (.hg not found)"))
2828
2852
2829 class service(object):
2853 class service(object):
2830 def init(self):
2854 def init(self):
2831 util.set_signal_handler()
2855 util.set_signal_handler()
2832 self.httpd = server.create_server(baseui, repo)
2856 self.httpd = server.create_server(baseui, repo)
2833
2857
2834 if not ui.verbose:
2858 if not ui.verbose:
2835 return
2859 return
2836
2860
2837 if self.httpd.prefix:
2861 if self.httpd.prefix:
2838 prefix = self.httpd.prefix.strip('/') + '/'
2862 prefix = self.httpd.prefix.strip('/') + '/'
2839 else:
2863 else:
2840 prefix = ''
2864 prefix = ''
2841
2865
2842 port = ':%d' % self.httpd.port
2866 port = ':%d' % self.httpd.port
2843 if port == ':80':
2867 if port == ':80':
2844 port = ''
2868 port = ''
2845
2869
2846 bindaddr = self.httpd.addr
2870 bindaddr = self.httpd.addr
2847 if bindaddr == '0.0.0.0':
2871 if bindaddr == '0.0.0.0':
2848 bindaddr = '*'
2872 bindaddr = '*'
2849 elif ':' in bindaddr: # IPv6
2873 elif ':' in bindaddr: # IPv6
2850 bindaddr = '[%s]' % bindaddr
2874 bindaddr = '[%s]' % bindaddr
2851
2875
2852 fqaddr = self.httpd.fqaddr
2876 fqaddr = self.httpd.fqaddr
2853 if ':' in fqaddr:
2877 if ':' in fqaddr:
2854 fqaddr = '[%s]' % fqaddr
2878 fqaddr = '[%s]' % fqaddr
2855 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2879 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2856 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2880 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2857
2881
2858 def run(self):
2882 def run(self):
2859 self.httpd.serve_forever()
2883 self.httpd.serve_forever()
2860
2884
2861 service = service()
2885 service = service()
2862
2886
2863 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2887 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2864
2888
2865 def status(ui, repo, *pats, **opts):
2889 def status(ui, repo, *pats, **opts):
2866 """show changed files in the working directory
2890 """show changed files in the working directory
2867
2891
2868 Show status of files in the repository. If names are given, only
2892 Show status of files in the repository. If names are given, only
2869 files that match are shown. Files that are clean or ignored or
2893 files that match are shown. Files that are clean or ignored or
2870 the source of a copy/move operation, are not listed unless
2894 the source of a copy/move operation, are not listed unless
2871 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
2895 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
2872 Unless options described with "show only ..." are given, the
2896 Unless options described with "show only ..." are given, the
2873 options -mardu are used.
2897 options -mardu are used.
2874
2898
2875 Option -q/--quiet hides untracked (unknown and ignored) files
2899 Option -q/--quiet hides untracked (unknown and ignored) files
2876 unless explicitly requested with -u/--unknown or -i/--ignored.
2900 unless explicitly requested with -u/--unknown or -i/--ignored.
2877
2901
2878 NOTE: status may appear to disagree with diff if permissions have
2902 NOTE: status may appear to disagree with diff if permissions have
2879 changed or a merge has occurred. The standard diff format does not
2903 changed or a merge has occurred. The standard diff format does not
2880 report permission changes and diff only reports changes relative
2904 report permission changes and diff only reports changes relative
2881 to one merge parent.
2905 to one merge parent.
2882
2906
2883 If one revision is given, it is used as the base revision.
2907 If one revision is given, it is used as the base revision.
2884 If two revisions are given, the differences between them are
2908 If two revisions are given, the differences between them are
2885 shown. The --change option can also be used as a shortcut to list
2909 shown. The --change option can also be used as a shortcut to list
2886 the changed files of a revision from its first parent.
2910 the changed files of a revision from its first parent.
2887
2911
2888 The codes used to show the status of files are::
2912 The codes used to show the status of files are::
2889
2913
2890 M = modified
2914 M = modified
2891 A = added
2915 A = added
2892 R = removed
2916 R = removed
2893 C = clean
2917 C = clean
2894 ! = missing (deleted by non-hg command, but still tracked)
2918 ! = missing (deleted by non-hg command, but still tracked)
2895 ? = not tracked
2919 ? = not tracked
2896 I = ignored
2920 I = ignored
2897 = origin of the previous file listed as A (added)
2921 = origin of the previous file listed as A (added)
2898 """
2922 """
2899
2923
2900 revs = opts.get('rev')
2924 revs = opts.get('rev')
2901 change = opts.get('change')
2925 change = opts.get('change')
2902
2926
2903 if revs and change:
2927 if revs and change:
2904 msg = _('cannot specify --rev and --change at the same time')
2928 msg = _('cannot specify --rev and --change at the same time')
2905 raise util.Abort(msg)
2929 raise util.Abort(msg)
2906 elif change:
2930 elif change:
2907 node2 = repo.lookup(change)
2931 node2 = repo.lookup(change)
2908 node1 = repo[node2].parents()[0].node()
2932 node1 = repo[node2].parents()[0].node()
2909 else:
2933 else:
2910 node1, node2 = cmdutil.revpair(repo, revs)
2934 node1, node2 = cmdutil.revpair(repo, revs)
2911
2935
2912 cwd = (pats and repo.getcwd()) or ''
2936 cwd = (pats and repo.getcwd()) or ''
2913 end = opts.get('print0') and '\0' or '\n'
2937 end = opts.get('print0') and '\0' or '\n'
2914 copy = {}
2938 copy = {}
2915 states = 'modified added removed deleted unknown ignored clean'.split()
2939 states = 'modified added removed deleted unknown ignored clean'.split()
2916 show = [k for k in states if opts.get(k)]
2940 show = [k for k in states if opts.get(k)]
2917 if opts.get('all'):
2941 if opts.get('all'):
2918 show += ui.quiet and (states[:4] + ['clean']) or states
2942 show += ui.quiet and (states[:4] + ['clean']) or states
2919 if not show:
2943 if not show:
2920 show = ui.quiet and states[:4] or states[:5]
2944 show = ui.quiet and states[:4] or states[:5]
2921
2945
2922 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
2946 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
2923 'ignored' in show, 'clean' in show, 'unknown' in show)
2947 'ignored' in show, 'clean' in show, 'unknown' in show)
2924 changestates = zip(states, 'MAR!?IC', stat)
2948 changestates = zip(states, 'MAR!?IC', stat)
2925
2949
2926 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
2950 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
2927 ctxn = repo[nullid]
2951 ctxn = repo[nullid]
2928 ctx1 = repo[node1]
2952 ctx1 = repo[node1]
2929 ctx2 = repo[node2]
2953 ctx2 = repo[node2]
2930 added = stat[1]
2954 added = stat[1]
2931 if node2 is None:
2955 if node2 is None:
2932 added = stat[0] + stat[1] # merged?
2956 added = stat[0] + stat[1] # merged?
2933
2957
2934 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
2958 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
2935 if k in added:
2959 if k in added:
2936 copy[k] = v
2960 copy[k] = v
2937 elif v in added:
2961 elif v in added:
2938 copy[v] = k
2962 copy[v] = k
2939
2963
2940 for state, char, files in changestates:
2964 for state, char, files in changestates:
2941 if state in show:
2965 if state in show:
2942 format = "%s %%s%s" % (char, end)
2966 format = "%s %%s%s" % (char, end)
2943 if opts.get('no_status'):
2967 if opts.get('no_status'):
2944 format = "%%s%s" % end
2968 format = "%%s%s" % end
2945
2969
2946 for f in files:
2970 for f in files:
2947 ui.write(format % repo.pathto(f, cwd))
2971 ui.write(format % repo.pathto(f, cwd))
2948 if f in copy:
2972 if f in copy:
2949 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2973 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2950
2974
2951 def summary(ui, repo, **opts):
2975 def summary(ui, repo, **opts):
2952 """summarize working directory state
2976 """summarize working directory state
2953
2977
2954 This generates a brief summary of the working directory state,
2978 This generates a brief summary of the working directory state,
2955 including parents, branch, commit status, and available updates.
2979 including parents, branch, commit status, and available updates.
2956
2980
2957 With the --remote option, this will check the default paths for
2981 With the --remote option, this will check the default paths for
2958 incoming and outgoing changes. This can be time-consuming.
2982 incoming and outgoing changes. This can be time-consuming.
2959 """
2983 """
2960
2984
2961 ctx = repo[None]
2985 ctx = repo[None]
2962 parents = ctx.parents()
2986 parents = ctx.parents()
2963 pnode = parents[0].node()
2987 pnode = parents[0].node()
2964 tags = repo.tags()
2988 tags = repo.tags()
2965
2989
2966 for p in parents:
2990 for p in parents:
2967 t = ' '.join([t for t in tags if tags[t] == p.node()])
2991 t = ' '.join([t for t in tags if tags[t] == p.node()])
2968 if p.rev() == -1:
2992 if p.rev() == -1:
2969 if not len(repo):
2993 if not len(repo):
2970 t += _(' (empty repository)')
2994 t += _(' (empty repository)')
2971 else:
2995 else:
2972 t += _(' (no revision checked out)')
2996 t += _(' (no revision checked out)')
2973 ui.write(_('parent: %d:%s %s\n') % (p.rev(), str(p), t))
2997 ui.write(_('parent: %d:%s %s\n') % (p.rev(), str(p), t))
2974 if p.description():
2998 if p.description():
2975 ui.status(' ' + p.description().splitlines()[0].strip() + '\n')
2999 ui.status(' ' + p.description().splitlines()[0].strip() + '\n')
2976
3000
2977 branch = ctx.branch()
3001 branch = ctx.branch()
2978 bheads = repo.branchheads(branch)
3002 bheads = repo.branchheads(branch)
2979 m = _('branch: %s\n') % branch
3003 m = _('branch: %s\n') % branch
2980 if branch != 'default':
3004 if branch != 'default':
2981 ui.write(m)
3005 ui.write(m)
2982 else:
3006 else:
2983 ui.status(m)
3007 ui.status(m)
2984
3008
2985 st = list(repo.status(unknown=True))[:7]
3009 st = list(repo.status(unknown=True))[:7]
2986 ms = merge_.mergestate(repo)
3010 ms = merge_.mergestate(repo)
2987 st.append([f for f in ms if f == 'u'])
3011 st.append([f for f in ms if f == 'u'])
2988 labels = [_('%d modified'), _('%d added'), _('%d removed'),
3012 labels = [_('%d modified'), _('%d added'), _('%d removed'),
2989 _('%d deleted'), _('%d unknown'), _('%d ignored'),
3013 _('%d deleted'), _('%d unknown'), _('%d ignored'),
2990 _('%d unresolved')]
3014 _('%d unresolved')]
2991 t = []
3015 t = []
2992 for s, l in zip(st, labels):
3016 for s, l in zip(st, labels):
2993 if s:
3017 if s:
2994 t.append(l % len(s))
3018 t.append(l % len(s))
2995
3019
2996 t = ', '.join(t)
3020 t = ', '.join(t)
2997 cleanworkdir = False
3021 cleanworkdir = False
2998
3022
2999 if len(parents) > 1:
3023 if len(parents) > 1:
3000 t += _(' (merge)')
3024 t += _(' (merge)')
3001 elif branch != parents[0].branch():
3025 elif branch != parents[0].branch():
3002 t += _(' (new branch)')
3026 t += _(' (new branch)')
3003 elif (not st[0] and not st[1] and not st[2]):
3027 elif (not st[0] and not st[1] and not st[2]):
3004 t += _(' (clean)')
3028 t += _(' (clean)')
3005 cleanworkdir = True
3029 cleanworkdir = True
3006 elif pnode not in bheads:
3030 elif pnode not in bheads:
3007 t += _(' (new branch head)')
3031 t += _(' (new branch head)')
3008
3032
3009 if cleanworkdir:
3033 if cleanworkdir:
3010 ui.status(_('commit: %s\n') % t.strip())
3034 ui.status(_('commit: %s\n') % t.strip())
3011 else:
3035 else:
3012 ui.write(_('commit: %s\n') % t.strip())
3036 ui.write(_('commit: %s\n') % t.strip())
3013
3037
3014 # all ancestors of branch heads - all ancestors of parent = new csets
3038 # all ancestors of branch heads - all ancestors of parent = new csets
3015 new = [0] * len(repo)
3039 new = [0] * len(repo)
3016 cl = repo.changelog
3040 cl = repo.changelog
3017 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
3041 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
3018 new[a] = 1
3042 new[a] = 1
3019 for a in cl.ancestors(*[p.rev() for p in parents]):
3043 for a in cl.ancestors(*[p.rev() for p in parents]):
3020 new[a] = 0
3044 new[a] = 0
3021 new = sum(new)
3045 new = sum(new)
3022
3046
3023 if new == 0:
3047 if new == 0:
3024 ui.status(_('update: (current)\n'))
3048 ui.status(_('update: (current)\n'))
3025 elif pnode not in bheads:
3049 elif pnode not in bheads:
3026 ui.write(_('update: %d new changesets (update)\n') % new)
3050 ui.write(_('update: %d new changesets (update)\n') % new)
3027 else:
3051 else:
3028 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
3052 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
3029 (new, len(bheads)))
3053 (new, len(bheads)))
3030
3054
3031 if opts.get('remote'):
3055 if opts.get('remote'):
3032 t = []
3056 t = []
3033 source, revs, checkout = hg.parseurl(ui.expandpath('default'),
3057 source, revs, checkout = hg.parseurl(ui.expandpath('default'),
3034 opts.get('rev'))
3058 opts.get('rev'))
3035 other = hg.repository(cmdutil.remoteui(repo, {}), source)
3059 other = hg.repository(cmdutil.remoteui(repo, {}), source)
3036 ui.debug('comparing with %s\n' % url.hidepassword(source))
3060 ui.debug('comparing with %s\n' % url.hidepassword(source))
3037 repo.ui.pushbuffer()
3061 repo.ui.pushbuffer()
3038 common, incoming, rheads = repo.findcommonincoming(other)
3062 common, incoming, rheads = repo.findcommonincoming(other)
3039 repo.ui.popbuffer()
3063 repo.ui.popbuffer()
3040 if incoming:
3064 if incoming:
3041 t.append(_('1 or more incoming'))
3065 t.append(_('1 or more incoming'))
3042
3066
3043 dest, revs, checkout = hg.parseurl(
3067 dest, revs, checkout = hg.parseurl(
3044 ui.expandpath('default-push', 'default'))
3068 ui.expandpath('default-push', 'default'))
3045 other = hg.repository(cmdutil.remoteui(repo, {}), dest)
3069 other = hg.repository(cmdutil.remoteui(repo, {}), dest)
3046 ui.debug('comparing with %s\n' % url.hidepassword(dest))
3070 ui.debug('comparing with %s\n' % url.hidepassword(dest))
3047 repo.ui.pushbuffer()
3071 repo.ui.pushbuffer()
3048 o = repo.findoutgoing(other)
3072 o = repo.findoutgoing(other)
3049 repo.ui.popbuffer()
3073 repo.ui.popbuffer()
3050 o = repo.changelog.nodesbetween(o, revs)[0]
3074 o = repo.changelog.nodesbetween(o, revs)[0]
3051 if o:
3075 if o:
3052 t.append(_('%d outgoing') % len(o))
3076 t.append(_('%d outgoing') % len(o))
3053
3077
3054 if t:
3078 if t:
3055 ui.write(_('remote: %s\n') % (', '.join(t)))
3079 ui.write(_('remote: %s\n') % (', '.join(t)))
3056 else:
3080 else:
3057 ui.status(_('remote: (synced)\n'))
3081 ui.status(_('remote: (synced)\n'))
3058
3082
3059 def tag(ui, repo, name1, *names, **opts):
3083 def tag(ui, repo, name1, *names, **opts):
3060 """add one or more tags for the current or given revision
3084 """add one or more tags for the current or given revision
3061
3085
3062 Name a particular revision using <name>.
3086 Name a particular revision using <name>.
3063
3087
3064 Tags are used to name particular revisions of the repository and are
3088 Tags are used to name particular revisions of the repository and are
3065 very useful to compare different revisions, to go back to significant
3089 very useful to compare different revisions, to go back to significant
3066 earlier versions or to mark branch points as releases, etc.
3090 earlier versions or to mark branch points as releases, etc.
3067
3091
3068 If no revision is given, the parent of the working directory is
3092 If no revision is given, the parent of the working directory is
3069 used, or tip if no revision is checked out.
3093 used, or tip if no revision is checked out.
3070
3094
3071 To facilitate version control, distribution, and merging of tags,
3095 To facilitate version control, distribution, and merging of tags,
3072 they are stored as a file named ".hgtags" which is managed
3096 they are stored as a file named ".hgtags" which is managed
3073 similarly to other project files and can be hand-edited if
3097 similarly to other project files and can be hand-edited if
3074 necessary. The file '.hg/localtags' is used for local tags (not
3098 necessary. The file '.hg/localtags' is used for local tags (not
3075 shared among repositories).
3099 shared among repositories).
3076
3100
3077 See 'hg help dates' for a list of formats valid for -d/--date.
3101 See 'hg help dates' for a list of formats valid for -d/--date.
3078 """
3102 """
3079
3103
3080 rev_ = "."
3104 rev_ = "."
3081 names = (name1,) + names
3105 names = (name1,) + names
3082 if len(names) != len(set(names)):
3106 if len(names) != len(set(names)):
3083 raise util.Abort(_('tag names must be unique'))
3107 raise util.Abort(_('tag names must be unique'))
3084 for n in names:
3108 for n in names:
3085 if n in ['tip', '.', 'null']:
3109 if n in ['tip', '.', 'null']:
3086 raise util.Abort(_('the name \'%s\' is reserved') % n)
3110 raise util.Abort(_('the name \'%s\' is reserved') % n)
3087 if opts.get('rev') and opts.get('remove'):
3111 if opts.get('rev') and opts.get('remove'):
3088 raise util.Abort(_("--rev and --remove are incompatible"))
3112 raise util.Abort(_("--rev and --remove are incompatible"))
3089 if opts.get('rev'):
3113 if opts.get('rev'):
3090 rev_ = opts['rev']
3114 rev_ = opts['rev']
3091 message = opts.get('message')
3115 message = opts.get('message')
3092 if opts.get('remove'):
3116 if opts.get('remove'):
3093 expectedtype = opts.get('local') and 'local' or 'global'
3117 expectedtype = opts.get('local') and 'local' or 'global'
3094 for n in names:
3118 for n in names:
3095 if not repo.tagtype(n):
3119 if not repo.tagtype(n):
3096 raise util.Abort(_('tag \'%s\' does not exist') % n)
3120 raise util.Abort(_('tag \'%s\' does not exist') % n)
3097 if repo.tagtype(n) != expectedtype:
3121 if repo.tagtype(n) != expectedtype:
3098 if expectedtype == 'global':
3122 if expectedtype == 'global':
3099 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
3123 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
3100 else:
3124 else:
3101 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
3125 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
3102 rev_ = nullid
3126 rev_ = nullid
3103 if not message:
3127 if not message:
3104 # we don't translate commit messages
3128 # we don't translate commit messages
3105 message = 'Removed tag %s' % ', '.join(names)
3129 message = 'Removed tag %s' % ', '.join(names)
3106 elif not opts.get('force'):
3130 elif not opts.get('force'):
3107 for n in names:
3131 for n in names:
3108 if n in repo.tags():
3132 if n in repo.tags():
3109 raise util.Abort(_('tag \'%s\' already exists '
3133 raise util.Abort(_('tag \'%s\' already exists '
3110 '(use -f to force)') % n)
3134 '(use -f to force)') % n)
3111 if not rev_ and repo.dirstate.parents()[1] != nullid:
3135 if not rev_ and repo.dirstate.parents()[1] != nullid:
3112 raise util.Abort(_('uncommitted merge - please provide a '
3136 raise util.Abort(_('uncommitted merge - please provide a '
3113 'specific revision'))
3137 'specific revision'))
3114 r = repo[rev_].node()
3138 r = repo[rev_].node()
3115
3139
3116 if not message:
3140 if not message:
3117 # we don't translate commit messages
3141 # we don't translate commit messages
3118 message = ('Added tag %s for changeset %s' %
3142 message = ('Added tag %s for changeset %s' %
3119 (', '.join(names), short(r)))
3143 (', '.join(names), short(r)))
3120
3144
3121 date = opts.get('date')
3145 date = opts.get('date')
3122 if date:
3146 if date:
3123 date = util.parsedate(date)
3147 date = util.parsedate(date)
3124
3148
3125 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
3149 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
3126
3150
3127 def tags(ui, repo):
3151 def tags(ui, repo):
3128 """list repository tags
3152 """list repository tags
3129
3153
3130 This lists both regular and local tags. When the -v/--verbose
3154 This lists both regular and local tags. When the -v/--verbose
3131 switch is used, a third column "local" is printed for local tags.
3155 switch is used, a third column "local" is printed for local tags.
3132 """
3156 """
3133
3157
3134 hexfunc = ui.debugflag and hex or short
3158 hexfunc = ui.debugflag and hex or short
3135 tagtype = ""
3159 tagtype = ""
3136
3160
3137 for t, n in reversed(repo.tagslist()):
3161 for t, n in reversed(repo.tagslist()):
3138 if ui.quiet:
3162 if ui.quiet:
3139 ui.write("%s\n" % t)
3163 ui.write("%s\n" % t)
3140 continue
3164 continue
3141
3165
3142 try:
3166 try:
3143 hn = hexfunc(n)
3167 hn = hexfunc(n)
3144 r = "%5d:%s" % (repo.changelog.rev(n), hn)
3168 r = "%5d:%s" % (repo.changelog.rev(n), hn)
3145 except error.LookupError:
3169 except error.LookupError:
3146 r = " ?:%s" % hn
3170 r = " ?:%s" % hn
3147 else:
3171 else:
3148 spaces = " " * (30 - encoding.colwidth(t))
3172 spaces = " " * (30 - encoding.colwidth(t))
3149 if ui.verbose:
3173 if ui.verbose:
3150 if repo.tagtype(t) == 'local':
3174 if repo.tagtype(t) == 'local':
3151 tagtype = " local"
3175 tagtype = " local"
3152 else:
3176 else:
3153 tagtype = ""
3177 tagtype = ""
3154 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
3178 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
3155
3179
3156 def tip(ui, repo, **opts):
3180 def tip(ui, repo, **opts):
3157 """show the tip revision
3181 """show the tip revision
3158
3182
3159 The tip revision (usually just called the tip) is the changeset
3183 The tip revision (usually just called the tip) is the changeset
3160 most recently added to the repository (and therefore the most
3184 most recently added to the repository (and therefore the most
3161 recently changed head).
3185 recently changed head).
3162
3186
3163 If you have just made a commit, that commit will be the tip. If
3187 If you have just made a commit, that commit will be the tip. If
3164 you have just pulled changes from another repository, the tip of
3188 you have just pulled changes from another repository, the tip of
3165 that repository becomes the current tip. The "tip" tag is special
3189 that repository becomes the current tip. The "tip" tag is special
3166 and cannot be renamed or assigned to a different changeset.
3190 and cannot be renamed or assigned to a different changeset.
3167 """
3191 """
3168 displayer = cmdutil.show_changeset(ui, repo, opts)
3192 displayer = cmdutil.show_changeset(ui, repo, opts)
3169 displayer.show(repo[len(repo) - 1])
3193 displayer.show(repo[len(repo) - 1])
3170 displayer.close()
3194 displayer.close()
3171
3195
3172 def unbundle(ui, repo, fname1, *fnames, **opts):
3196 def unbundle(ui, repo, fname1, *fnames, **opts):
3173 """apply one or more changegroup files
3197 """apply one or more changegroup files
3174
3198
3175 Apply one or more compressed changegroup files generated by the
3199 Apply one or more compressed changegroup files generated by the
3176 bundle command.
3200 bundle command.
3177 """
3201 """
3178 fnames = (fname1,) + fnames
3202 fnames = (fname1,) + fnames
3179
3203
3180 lock = repo.lock()
3204 lock = repo.lock()
3181 try:
3205 try:
3182 for fname in fnames:
3206 for fname in fnames:
3183 f = url.open(ui, fname)
3207 f = url.open(ui, fname)
3184 gen = changegroup.readbundle(f, fname)
3208 gen = changegroup.readbundle(f, fname)
3185 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
3209 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
3186 finally:
3210 finally:
3187 lock.release()
3211 lock.release()
3188
3212
3189 return postincoming(ui, repo, modheads, opts.get('update'), None)
3213 return postincoming(ui, repo, modheads, opts.get('update'), None)
3190
3214
3191 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
3215 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
3192 """update working directory
3216 """update working directory
3193
3217
3194 Update the repository's working directory to the specified
3218 Update the repository's working directory to the specified
3195 changeset.
3219 changeset.
3196
3220
3197 If no changeset is specified, attempt to update to the head of the
3221 If no changeset is specified, attempt to update to the head of the
3198 current branch. If this head is a descendant of the working
3222 current branch. If this head is a descendant of the working
3199 directory's parent, update to it, otherwise abort.
3223 directory's parent, update to it, otherwise abort.
3200
3224
3201 The following rules apply when the working directory contains
3225 The following rules apply when the working directory contains
3202 uncommitted changes:
3226 uncommitted changes:
3203
3227
3204 1. If neither -c/--check nor -C/--clean is specified, and if
3228 1. If neither -c/--check nor -C/--clean is specified, and if
3205 the requested changeset is an ancestor or descendant of
3229 the requested changeset is an ancestor or descendant of
3206 the working directory's parent, the uncommitted changes
3230 the working directory's parent, the uncommitted changes
3207 are merged into the requested changeset and the merged
3231 are merged into the requested changeset and the merged
3208 result is left uncommitted. If the requested changeset is
3232 result is left uncommitted. If the requested changeset is
3209 not an ancestor or descendant (that is, it is on another
3233 not an ancestor or descendant (that is, it is on another
3210 branch), the update is aborted and the uncommitted changes
3234 branch), the update is aborted and the uncommitted changes
3211 are preserved.
3235 are preserved.
3212
3236
3213 2. With the -c/--check option, the update is aborted and the
3237 2. With the -c/--check option, the update is aborted and the
3214 uncommitted changes are preserved.
3238 uncommitted changes are preserved.
3215
3239
3216 3. With the -C/--clean option, uncommitted changes are discarded and
3240 3. With the -C/--clean option, uncommitted changes are discarded and
3217 the working directory is updated to the requested changeset.
3241 the working directory is updated to the requested changeset.
3218
3242
3219 Use null as the changeset to remove the working directory (like 'hg
3243 Use null as the changeset to remove the working directory (like 'hg
3220 clone -U').
3244 clone -U').
3221
3245
3222 If you want to update just one file to an older changeset, use 'hg revert'.
3246 If you want to update just one file to an older changeset, use 'hg revert'.
3223
3247
3224 See 'hg help dates' for a list of formats valid for -d/--date.
3248 See 'hg help dates' for a list of formats valid for -d/--date.
3225 """
3249 """
3226 if rev and node:
3250 if rev and node:
3227 raise util.Abort(_("please specify just one revision"))
3251 raise util.Abort(_("please specify just one revision"))
3228
3252
3229 if not rev:
3253 if not rev:
3230 rev = node
3254 rev = node
3231
3255
3232 if check and clean:
3256 if check and clean:
3233 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
3257 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
3234
3258
3235 if check:
3259 if check:
3236 # we could use dirty() but we can ignore merge and branch trivia
3260 # we could use dirty() but we can ignore merge and branch trivia
3237 c = repo[None]
3261 c = repo[None]
3238 if c.modified() or c.added() or c.removed():
3262 if c.modified() or c.added() or c.removed():
3239 raise util.Abort(_("uncommitted local changes"))
3263 raise util.Abort(_("uncommitted local changes"))
3240
3264
3241 if date:
3265 if date:
3242 if rev:
3266 if rev:
3243 raise util.Abort(_("you can't specify a revision and a date"))
3267 raise util.Abort(_("you can't specify a revision and a date"))
3244 rev = cmdutil.finddate(ui, repo, date)
3268 rev = cmdutil.finddate(ui, repo, date)
3245
3269
3246 if clean or check:
3270 if clean or check:
3247 return hg.clean(repo, rev)
3271 return hg.clean(repo, rev)
3248 else:
3272 else:
3249 return hg.update(repo, rev)
3273 return hg.update(repo, rev)
3250
3274
3251 def verify(ui, repo):
3275 def verify(ui, repo):
3252 """verify the integrity of the repository
3276 """verify the integrity of the repository
3253
3277
3254 Verify the integrity of the current repository.
3278 Verify the integrity of the current repository.
3255
3279
3256 This will perform an extensive check of the repository's
3280 This will perform an extensive check of the repository's
3257 integrity, validating the hashes and checksums of each entry in
3281 integrity, validating the hashes and checksums of each entry in
3258 the changelog, manifest, and tracked files, as well as the
3282 the changelog, manifest, and tracked files, as well as the
3259 integrity of their crosslinks and indices.
3283 integrity of their crosslinks and indices.
3260 """
3284 """
3261 return hg.verify(repo)
3285 return hg.verify(repo)
3262
3286
3263 def version_(ui):
3287 def version_(ui):
3264 """output version and copyright information"""
3288 """output version and copyright information"""
3265 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3289 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3266 % util.version())
3290 % util.version())
3267 ui.status(_(
3291 ui.status(_(
3268 "\nCopyright (C) 2005-2010 Matt Mackall <mpm@selenic.com> and others\n"
3292 "\nCopyright (C) 2005-2010 Matt Mackall <mpm@selenic.com> and others\n"
3269 "This is free software; see the source for copying conditions. "
3293 "This is free software; see the source for copying conditions. "
3270 "There is NO\nwarranty; "
3294 "There is NO\nwarranty; "
3271 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3295 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3272 ))
3296 ))
3273
3297
3274 # Command options and aliases are listed here, alphabetically
3298 # Command options and aliases are listed here, alphabetically
3275
3299
3276 globalopts = [
3300 globalopts = [
3277 ('R', 'repository', '',
3301 ('R', 'repository', '',
3278 _('repository root directory or name of overlay bundle file')),
3302 _('repository root directory or name of overlay bundle file')),
3279 ('', 'cwd', '', _('change working directory')),
3303 ('', 'cwd', '', _('change working directory')),
3280 ('y', 'noninteractive', None,
3304 ('y', 'noninteractive', None,
3281 _('do not prompt, assume \'yes\' for any required answers')),
3305 _('do not prompt, assume \'yes\' for any required answers')),
3282 ('q', 'quiet', None, _('suppress output')),
3306 ('q', 'quiet', None, _('suppress output')),
3283 ('v', 'verbose', None, _('enable additional output')),
3307 ('v', 'verbose', None, _('enable additional output')),
3284 ('', 'config', [], _('set/override config option')),
3308 ('', 'config', [], _('set/override config option')),
3285 ('', 'debug', None, _('enable debugging output')),
3309 ('', 'debug', None, _('enable debugging output')),
3286 ('', 'debugger', None, _('start debugger')),
3310 ('', 'debugger', None, _('start debugger')),
3287 ('', 'encoding', encoding.encoding, _('set the charset encoding')),
3311 ('', 'encoding', encoding.encoding, _('set the charset encoding')),
3288 ('', 'encodingmode', encoding.encodingmode,
3312 ('', 'encodingmode', encoding.encodingmode,
3289 _('set the charset encoding mode')),
3313 _('set the charset encoding mode')),
3290 ('', 'traceback', None, _('always print a traceback on exception')),
3314 ('', 'traceback', None, _('always print a traceback on exception')),
3291 ('', 'time', None, _('time how long the command takes')),
3315 ('', 'time', None, _('time how long the command takes')),
3292 ('', 'profile', None, _('print command execution profile')),
3316 ('', 'profile', None, _('print command execution profile')),
3293 ('', 'version', None, _('output version information and exit')),
3317 ('', 'version', None, _('output version information and exit')),
3294 ('h', 'help', None, _('display help and exit')),
3318 ('h', 'help', None, _('display help and exit')),
3295 ]
3319 ]
3296
3320
3297 dryrunopts = [('n', 'dry-run', None,
3321 dryrunopts = [('n', 'dry-run', None,
3298 _('do not perform actions, just print output'))]
3322 _('do not perform actions, just print output'))]
3299
3323
3300 remoteopts = [
3324 remoteopts = [
3301 ('e', 'ssh', '', _('specify ssh command to use')),
3325 ('e', 'ssh', '', _('specify ssh command to use')),
3302 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
3326 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
3303 ]
3327 ]
3304
3328
3305 walkopts = [
3329 walkopts = [
3306 ('I', 'include', [], _('include names matching the given patterns')),
3330 ('I', 'include', [], _('include names matching the given patterns')),
3307 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3331 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3308 ]
3332 ]
3309
3333
3310 commitopts = [
3334 commitopts = [
3311 ('m', 'message', '', _('use <text> as commit message')),
3335 ('m', 'message', '', _('use <text> as commit message')),
3312 ('l', 'logfile', '', _('read commit message from <file>')),
3336 ('l', 'logfile', '', _('read commit message from <file>')),
3313 ]
3337 ]
3314
3338
3315 commitopts2 = [
3339 commitopts2 = [
3316 ('d', 'date', '', _('record datecode as commit date')),
3340 ('d', 'date', '', _('record datecode as commit date')),
3317 ('u', 'user', '', _('record the specified user as committer')),
3341 ('u', 'user', '', _('record the specified user as committer')),
3318 ]
3342 ]
3319
3343
3320 templateopts = [
3344 templateopts = [
3321 ('', 'style', '', _('display using template map file')),
3345 ('', 'style', '', _('display using template map file')),
3322 ('', 'template', '', _('display with template')),
3346 ('', 'template', '', _('display with template')),
3323 ]
3347 ]
3324
3348
3325 logopts = [
3349 logopts = [
3326 ('p', 'patch', None, _('show patch')),
3350 ('p', 'patch', None, _('show patch')),
3327 ('g', 'git', None, _('use git extended diff format')),
3351 ('g', 'git', None, _('use git extended diff format')),
3328 ('l', 'limit', '', _('limit number of changes displayed')),
3352 ('l', 'limit', '', _('limit number of changes displayed')),
3329 ('M', 'no-merges', None, _('do not show merges')),
3353 ('M', 'no-merges', None, _('do not show merges')),
3330 ] + templateopts
3354 ] + templateopts
3331
3355
3332 diffopts = [
3356 diffopts = [
3333 ('a', 'text', None, _('treat all files as text')),
3357 ('a', 'text', None, _('treat all files as text')),
3334 ('g', 'git', None, _('use git extended diff format')),
3358 ('g', 'git', None, _('use git extended diff format')),
3335 ('', 'nodates', None, _('omit dates from diff headers'))
3359 ('', 'nodates', None, _('omit dates from diff headers'))
3336 ]
3360 ]
3337
3361
3338 diffopts2 = [
3362 diffopts2 = [
3339 ('p', 'show-function', None, _('show which function each change is in')),
3363 ('p', 'show-function', None, _('show which function each change is in')),
3340 ('', 'reverse', None, _('produce a diff that undoes the changes')),
3364 ('', 'reverse', None, _('produce a diff that undoes the changes')),
3341 ('w', 'ignore-all-space', None,
3365 ('w', 'ignore-all-space', None,
3342 _('ignore white space when comparing lines')),
3366 _('ignore white space when comparing lines')),
3343 ('b', 'ignore-space-change', None,
3367 ('b', 'ignore-space-change', None,
3344 _('ignore changes in the amount of white space')),
3368 _('ignore changes in the amount of white space')),
3345 ('B', 'ignore-blank-lines', None,
3369 ('B', 'ignore-blank-lines', None,
3346 _('ignore changes whose lines are all blank')),
3370 _('ignore changes whose lines are all blank')),
3347 ('U', 'unified', '', _('number of lines of context to show')),
3371 ('U', 'unified', '', _('number of lines of context to show')),
3348 ('', 'stat', None, _('output diffstat-style summary of changes')),
3372 ('', 'stat', None, _('output diffstat-style summary of changes')),
3349 ]
3373 ]
3350
3374
3351 similarityopts = [
3375 similarityopts = [
3352 ('s', 'similarity', '',
3376 ('s', 'similarity', '',
3353 _('guess renamed files by similarity (0<=s<=100)'))
3377 _('guess renamed files by similarity (0<=s<=100)'))
3354 ]
3378 ]
3355
3379
3356 table = {
3380 table = {
3357 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3381 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3358 "addremove":
3382 "addremove":
3359 (addremove, similarityopts + walkopts + dryrunopts,
3383 (addremove, similarityopts + walkopts + dryrunopts,
3360 _('[OPTION]... [FILE]...')),
3384 _('[OPTION]... [FILE]...')),
3361 "^annotate|blame":
3385 "^annotate|blame":
3362 (annotate,
3386 (annotate,
3363 [('r', 'rev', '', _('annotate the specified revision')),
3387 [('r', 'rev', '', _('annotate the specified revision')),
3364 ('', 'follow', None, _('follow copies and renames (DEPRECATED)')),
3388 ('', 'follow', None, _('follow copies and renames (DEPRECATED)')),
3365 ('', 'no-follow', None, _("don't follow copies and renames")),
3389 ('', 'no-follow', None, _("don't follow copies and renames")),
3366 ('a', 'text', None, _('treat all files as text')),
3390 ('a', 'text', None, _('treat all files as text')),
3367 ('u', 'user', None, _('list the author (long with -v)')),
3391 ('u', 'user', None, _('list the author (long with -v)')),
3368 ('f', 'file', None, _('list the filename')),
3392 ('f', 'file', None, _('list the filename')),
3369 ('d', 'date', None, _('list the date (short with -q)')),
3393 ('d', 'date', None, _('list the date (short with -q)')),
3370 ('n', 'number', None, _('list the revision number (default)')),
3394 ('n', 'number', None, _('list the revision number (default)')),
3371 ('c', 'changeset', None, _('list the changeset')),
3395 ('c', 'changeset', None, _('list the changeset')),
3372 ('l', 'line-number', None,
3396 ('l', 'line-number', None,
3373 _('show line number at the first appearance'))
3397 _('show line number at the first appearance'))
3374 ] + walkopts,
3398 ] + walkopts,
3375 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3399 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3376 "archive":
3400 "archive":
3377 (archive,
3401 (archive,
3378 [('', 'no-decode', None, _('do not pass files through decoders')),
3402 [('', 'no-decode', None, _('do not pass files through decoders')),
3379 ('p', 'prefix', '', _('directory prefix for files in archive')),
3403 ('p', 'prefix', '', _('directory prefix for files in archive')),
3380 ('r', 'rev', '', _('revision to distribute')),
3404 ('r', 'rev', '', _('revision to distribute')),
3381 ('t', 'type', '', _('type of distribution to create')),
3405 ('t', 'type', '', _('type of distribution to create')),
3382 ] + walkopts,
3406 ] + walkopts,
3383 _('[OPTION]... DEST')),
3407 _('[OPTION]... DEST')),
3384 "backout":
3408 "backout":
3385 (backout,
3409 (backout,
3386 [('', 'merge', None,
3410 [('', 'merge', None,
3387 _('merge with old dirstate parent after backout')),
3411 _('merge with old dirstate parent after backout')),
3388 ('', 'parent', '', _('parent to choose when backing out merge')),
3412 ('', 'parent', '', _('parent to choose when backing out merge')),
3389 ('r', 'rev', '', _('revision to backout')),
3413 ('r', 'rev', '', _('revision to backout')),
3390 ] + walkopts + commitopts + commitopts2,
3414 ] + walkopts + commitopts + commitopts2,
3391 _('[OPTION]... [-r] REV')),
3415 _('[OPTION]... [-r] REV')),
3392 "bisect":
3416 "bisect":
3393 (bisect,
3417 (bisect,
3394 [('r', 'reset', False, _('reset bisect state')),
3418 [('r', 'reset', False, _('reset bisect state')),
3395 ('g', 'good', False, _('mark changeset good')),
3419 ('g', 'good', False, _('mark changeset good')),
3396 ('b', 'bad', False, _('mark changeset bad')),
3420 ('b', 'bad', False, _('mark changeset bad')),
3397 ('s', 'skip', False, _('skip testing changeset')),
3421 ('s', 'skip', False, _('skip testing changeset')),
3398 ('c', 'command', '', _('use command to check changeset state')),
3422 ('c', 'command', '', _('use command to check changeset state')),
3399 ('U', 'noupdate', False, _('do not update to target'))],
3423 ('U', 'noupdate', False, _('do not update to target'))],
3400 _("[-gbsr] [-U] [-c CMD] [REV]")),
3424 _("[-gbsr] [-U] [-c CMD] [REV]")),
3401 "branch":
3425 "branch":
3402 (branch,
3426 (branch,
3403 [('f', 'force', None,
3427 [('f', 'force', None,
3404 _('set branch name even if it shadows an existing branch')),
3428 _('set branch name even if it shadows an existing branch')),
3405 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3429 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3406 _('[-fC] [NAME]')),
3430 _('[-fC] [NAME]')),
3407 "branches":
3431 "branches":
3408 (branches,
3432 (branches,
3409 [('a', 'active', False,
3433 [('a', 'active', False,
3410 _('show only branches that have unmerged heads')),
3434 _('show only branches that have unmerged heads')),
3411 ('c', 'closed', False,
3435 ('c', 'closed', False,
3412 _('show normal and closed branches'))],
3436 _('show normal and closed branches'))],
3413 _('[-ac]')),
3437 _('[-ac]')),
3414 "bundle":
3438 "bundle":
3415 (bundle,
3439 (bundle,
3416 [('f', 'force', None,
3440 [('f', 'force', None,
3417 _('run even when the destination is unrelated')),
3441 _('run even when the destination is unrelated')),
3418 ('r', 'rev', [],
3442 ('r', 'rev', [],
3419 _('a changeset intended to be added to the destination')),
3443 _('a changeset intended to be added to the destination')),
3420 ('b', 'branch', [],
3444 ('b', 'branch', [],
3421 _('a specific branch you would like to bundle')),
3445 _('a specific branch you would like to bundle')),
3422 ('', 'base', [],
3446 ('', 'base', [],
3423 _('a base changeset assumed to be available at the destination')),
3447 _('a base changeset assumed to be available at the destination')),
3424 ('a', 'all', None, _('bundle all changesets in the repository')),
3448 ('a', 'all', None, _('bundle all changesets in the repository')),
3425 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3449 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3426 ] + remoteopts,
3450 ] + remoteopts,
3427 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3451 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3428 "cat":
3452 "cat":
3429 (cat,
3453 (cat,
3430 [('o', 'output', '', _('print output to file with formatted name')),
3454 [('o', 'output', '', _('print output to file with formatted name')),
3431 ('r', 'rev', '', _('print the given revision')),
3455 ('r', 'rev', '', _('print the given revision')),
3432 ('', 'decode', None, _('apply any matching decode filter')),
3456 ('', 'decode', None, _('apply any matching decode filter')),
3433 ] + walkopts,
3457 ] + walkopts,
3434 _('[OPTION]... FILE...')),
3458 _('[OPTION]... FILE...')),
3435 "^clone":
3459 "^clone":
3436 (clone,
3460 (clone,
3437 [('U', 'noupdate', None,
3461 [('U', 'noupdate', None,
3438 _('the clone will include an empty working copy (only a repository)')),
3462 _('the clone will include an empty working copy (only a repository)')),
3439 ('u', 'updaterev', '',
3463 ('u', 'updaterev', '',
3440 _('revision, tag or branch to check out')),
3464 _('revision, tag or branch to check out')),
3441 ('r', 'rev', [],
3465 ('r', 'rev', [],
3442 _('include the specified changeset')),
3466 _('include the specified changeset')),
3443 ('b', 'branch', [],
3467 ('b', 'branch', [],
3444 _('clone only the specified branch')),
3468 _('clone only the specified branch')),
3445 ('', 'pull', None, _('use pull protocol to copy metadata')),
3469 ('', 'pull', None, _('use pull protocol to copy metadata')),
3446 ('', 'uncompressed', None,
3470 ('', 'uncompressed', None,
3447 _('use uncompressed transfer (fast over LAN)')),
3471 _('use uncompressed transfer (fast over LAN)')),
3448 ] + remoteopts,
3472 ] + remoteopts,
3449 _('[OPTION]... SOURCE [DEST]')),
3473 _('[OPTION]... SOURCE [DEST]')),
3450 "^commit|ci":
3474 "^commit|ci":
3451 (commit,
3475 (commit,
3452 [('A', 'addremove', None,
3476 [('A', 'addremove', None,
3453 _('mark new/missing files as added/removed before committing')),
3477 _('mark new/missing files as added/removed before committing')),
3454 ('', 'close-branch', None,
3478 ('', 'close-branch', None,
3455 _('mark a branch as closed, hiding it from the branch list')),
3479 _('mark a branch as closed, hiding it from the branch list')),
3456 ] + walkopts + commitopts + commitopts2,
3480 ] + walkopts + commitopts + commitopts2,
3457 _('[OPTION]... [FILE]...')),
3481 _('[OPTION]... [FILE]...')),
3458 "copy|cp":
3482 "copy|cp":
3459 (copy,
3483 (copy,
3460 [('A', 'after', None, _('record a copy that has already occurred')),
3484 [('A', 'after', None, _('record a copy that has already occurred')),
3461 ('f', 'force', None,
3485 ('f', 'force', None,
3462 _('forcibly copy over an existing managed file')),
3486 _('forcibly copy over an existing managed file')),
3463 ] + walkopts + dryrunopts,
3487 ] + walkopts + dryrunopts,
3464 _('[OPTION]... [SOURCE]... DEST')),
3488 _('[OPTION]... [SOURCE]... DEST')),
3465 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3489 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3466 "debugcheckstate": (debugcheckstate, [], ''),
3490 "debugcheckstate": (debugcheckstate, [], ''),
3467 "debugcommands": (debugcommands, [], _('[COMMAND]')),
3491 "debugcommands": (debugcommands, [], _('[COMMAND]')),
3468 "debugcomplete":
3492 "debugcomplete":
3469 (debugcomplete,
3493 (debugcomplete,
3470 [('o', 'options', None, _('show the command options'))],
3494 [('o', 'options', None, _('show the command options'))],
3471 _('[-o] CMD')),
3495 _('[-o] CMD')),
3472 "debugdate":
3496 "debugdate":
3473 (debugdate,
3497 (debugdate,
3474 [('e', 'extended', None, _('try extended date formats'))],
3498 [('e', 'extended', None, _('try extended date formats'))],
3475 _('[-e] DATE [RANGE]')),
3499 _('[-e] DATE [RANGE]')),
3476 "debugdata": (debugdata, [], _('FILE REV')),
3500 "debugdata": (debugdata, [], _('FILE REV')),
3477 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3501 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3478 "debugindex": (debugindex, [], _('FILE')),
3502 "debugindex": (debugindex, [], _('FILE')),
3479 "debugindexdot": (debugindexdot, [], _('FILE')),
3503 "debugindexdot": (debugindexdot, [], _('FILE')),
3480 "debuginstall": (debuginstall, [], ''),
3504 "debuginstall": (debuginstall, [], ''),
3481 "debugrebuildstate":
3505 "debugrebuildstate":
3482 (debugrebuildstate,
3506 (debugrebuildstate,
3483 [('r', 'rev', '', _('revision to rebuild to'))],
3507 [('r', 'rev', '', _('revision to rebuild to'))],
3484 _('[-r REV] [REV]')),
3508 _('[-r REV] [REV]')),
3485 "debugrename":
3509 "debugrename":
3486 (debugrename,
3510 (debugrename,
3487 [('r', 'rev', '', _('revision to debug'))],
3511 [('r', 'rev', '', _('revision to debug'))],
3488 _('[-r REV] FILE')),
3512 _('[-r REV] FILE')),
3489 "debugsetparents":
3513 "debugsetparents":
3490 (debugsetparents, [], _('REV1 [REV2]')),
3514 (debugsetparents, [], _('REV1 [REV2]')),
3491 "debugstate":
3515 "debugstate":
3492 (debugstate,
3516 (debugstate,
3493 [('', 'nodates', None, _('do not display the saved mtime'))],
3517 [('', 'nodates', None, _('do not display the saved mtime'))],
3494 _('[OPTION]...')),
3518 _('[OPTION]...')),
3495 "debugsub":
3519 "debugsub":
3496 (debugsub,
3520 (debugsub,
3497 [('r', 'rev', '', _('revision to check'))],
3521 [('r', 'rev', '', _('revision to check'))],
3498 _('[-r REV] [REV]')),
3522 _('[-r REV] [REV]')),
3499 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3523 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3500 "^diff":
3524 "^diff":
3501 (diff,
3525 (diff,
3502 [('r', 'rev', [], _('revision')),
3526 [('r', 'rev', [], _('revision')),
3503 ('c', 'change', '', _('change made by revision'))
3527 ('c', 'change', '', _('change made by revision'))
3504 ] + diffopts + diffopts2 + walkopts,
3528 ] + diffopts + diffopts2 + walkopts,
3505 _('[OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3529 _('[OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3506 "^export":
3530 "^export":
3507 (export,
3531 (export,
3508 [('o', 'output', '', _('print output to file with formatted name')),
3532 [('o', 'output', '', _('print output to file with formatted name')),
3509 ('', 'switch-parent', None, _('diff against the second parent')),
3533 ('', 'switch-parent', None, _('diff against the second parent')),
3510 ('r', 'rev', [], _('revisions to export')),
3534 ('r', 'rev', [], _('revisions to export')),
3511 ] + diffopts,
3535 ] + diffopts,
3512 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3536 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3513 "^forget":
3537 "^forget":
3514 (forget,
3538 (forget,
3515 [] + walkopts,
3539 [] + walkopts,
3516 _('[OPTION]... FILE...')),
3540 _('[OPTION]... FILE...')),
3517 "grep":
3541 "grep":
3518 (grep,
3542 (grep,
3519 [('0', 'print0', None, _('end fields with NUL')),
3543 [('0', 'print0', None, _('end fields with NUL')),
3520 ('', 'all', None, _('print all revisions that match')),
3544 ('', 'all', None, _('print all revisions that match')),
3521 ('f', 'follow', None,
3545 ('f', 'follow', None,
3522 _('follow changeset history,'
3546 _('follow changeset history,'
3523 ' or file history across copies and renames')),
3547 ' or file history across copies and renames')),
3524 ('i', 'ignore-case', None, _('ignore case when matching')),
3548 ('i', 'ignore-case', None, _('ignore case when matching')),
3525 ('l', 'files-with-matches', None,
3549 ('l', 'files-with-matches', None,
3526 _('print only filenames and revisions that match')),
3550 _('print only filenames and revisions that match')),
3527 ('n', 'line-number', None, _('print matching line numbers')),
3551 ('n', 'line-number', None, _('print matching line numbers')),
3528 ('r', 'rev', [], _('search in given revision range')),
3552 ('r', 'rev', [], _('search in given revision range')),
3529 ('u', 'user', None, _('list the author (long with -v)')),
3553 ('u', 'user', None, _('list the author (long with -v)')),
3530 ('d', 'date', None, _('list the date (short with -q)')),
3554 ('d', 'date', None, _('list the date (short with -q)')),
3531 ] + walkopts,
3555 ] + walkopts,
3532 _('[OPTION]... PATTERN [FILE]...')),
3556 _('[OPTION]... PATTERN [FILE]...')),
3533 "heads":
3557 "heads":
3534 (heads,
3558 (heads,
3535 [('r', 'rev', '', _('show only heads which are descendants of REV')),
3559 [('r', 'rev', '', _('show only heads which are descendants of REV')),
3536 ('t', 'topo', False, _('show topological heads only')),
3560 ('t', 'topo', False, _('show topological heads only')),
3537 ('a', 'active', False,
3561 ('a', 'active', False,
3538 _('show active branchheads only [DEPRECATED]')),
3562 _('show active branchheads only [DEPRECATED]')),
3539 ('c', 'closed', False,
3563 ('c', 'closed', False,
3540 _('show normal and closed branch heads')),
3564 _('show normal and closed branch heads')),
3541 ] + templateopts,
3565 ] + templateopts,
3542 _('[-ac] [-r STARTREV] [REV]...')),
3566 _('[-ac] [-r STARTREV] [REV]...')),
3543 "help": (help_, [], _('[TOPIC]')),
3567 "help": (help_, [], _('[TOPIC]')),
3544 "identify|id":
3568 "identify|id":
3545 (identify,
3569 (identify,
3546 [('r', 'rev', '', _('identify the specified revision')),
3570 [('r', 'rev', '', _('identify the specified revision')),
3547 ('n', 'num', None, _('show local revision number')),
3571 ('n', 'num', None, _('show local revision number')),
3548 ('i', 'id', None, _('show global revision id')),
3572 ('i', 'id', None, _('show global revision id')),
3549 ('b', 'branch', None, _('show branch')),
3573 ('b', 'branch', None, _('show branch')),
3550 ('t', 'tags', None, _('show tags'))],
3574 ('t', 'tags', None, _('show tags'))],
3551 _('[-nibt] [-r REV] [SOURCE]')),
3575 _('[-nibt] [-r REV] [SOURCE]')),
3552 "import|patch":
3576 "import|patch":
3553 (import_,
3577 (import_,
3554 [('p', 'strip', 1,
3578 [('p', 'strip', 1,
3555 _('directory strip option for patch. This has the same '
3579 _('directory strip option for patch. This has the same '
3556 'meaning as the corresponding patch option')),
3580 'meaning as the corresponding patch option')),
3557 ('b', 'base', '', _('base path')),
3581 ('b', 'base', '', _('base path')),
3558 ('f', 'force', None,
3582 ('f', 'force', None,
3559 _('skip check for outstanding uncommitted changes')),
3583 _('skip check for outstanding uncommitted changes')),
3560 ('', 'no-commit', None,
3584 ('', 'no-commit', None,
3561 _("don't commit, just update the working directory")),
3585 _("don't commit, just update the working directory")),
3562 ('', 'exact', None,
3586 ('', 'exact', None,
3563 _('apply patch to the nodes from which it was generated')),
3587 _('apply patch to the nodes from which it was generated')),
3564 ('', 'import-branch', None,
3588 ('', 'import-branch', None,
3565 _('use any branch information in patch (implied by --exact)'))] +
3589 _('use any branch information in patch (implied by --exact)'))] +
3566 commitopts + commitopts2 + similarityopts,
3590 commitopts + commitopts2 + similarityopts,
3567 _('[OPTION]... PATCH...')),
3591 _('[OPTION]... PATCH...')),
3568 "incoming|in":
3592 "incoming|in":
3569 (incoming,
3593 (incoming,
3570 [('f', 'force', None,
3594 [('f', 'force', None,
3571 _('run even if remote repository is unrelated')),
3595 _('run even if remote repository is unrelated')),
3572 ('n', 'newest-first', None, _('show newest record first')),
3596 ('n', 'newest-first', None, _('show newest record first')),
3573 ('', 'bundle', '', _('file to store the bundles into')),
3597 ('', 'bundle', '', _('file to store the bundles into')),
3574 ('r', 'rev', [],
3598 ('r', 'rev', [],
3575 _('a remote changeset intended to be added')),
3599 _('a remote changeset intended to be added')),
3576 ('b', 'branch', [],
3600 ('b', 'branch', [],
3577 _('a specific branch you would like to pull')),
3601 _('a specific branch you would like to pull')),
3578 ] + logopts + remoteopts,
3602 ] + logopts + remoteopts,
3579 _('[-p] [-n] [-M] [-f] [-r REV]...'
3603 _('[-p] [-n] [-M] [-f] [-r REV]...'
3580 ' [--bundle FILENAME] [SOURCE]')),
3604 ' [--bundle FILENAME] [SOURCE]')),
3581 "^init":
3605 "^init":
3582 (init,
3606 (init,
3583 remoteopts,
3607 remoteopts,
3584 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3608 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3585 "locate":
3609 "locate":
3586 (locate,
3610 (locate,
3587 [('r', 'rev', '', _('search the repository as it is in REV')),
3611 [('r', 'rev', '', _('search the repository as it is in REV')),
3588 ('0', 'print0', None,
3612 ('0', 'print0', None,
3589 _('end filenames with NUL, for use with xargs')),
3613 _('end filenames with NUL, for use with xargs')),
3590 ('f', 'fullpath', None,
3614 ('f', 'fullpath', None,
3591 _('print complete paths from the filesystem root')),
3615 _('print complete paths from the filesystem root')),
3592 ] + walkopts,
3616 ] + walkopts,
3593 _('[OPTION]... [PATTERN]...')),
3617 _('[OPTION]... [PATTERN]...')),
3594 "^log|history":
3618 "^log|history":
3595 (log,
3619 (log,
3596 [('f', 'follow', None,
3620 [('f', 'follow', None,
3597 _('follow changeset history,'
3621 _('follow changeset history,'
3598 ' or file history across copies and renames')),
3622 ' or file history across copies and renames')),
3599 ('', 'follow-first', None,
3623 ('', 'follow-first', None,
3600 _('only follow the first parent of merge changesets')),
3624 _('only follow the first parent of merge changesets')),
3601 ('d', 'date', '', _('show revisions matching date spec')),
3625 ('d', 'date', '', _('show revisions matching date spec')),
3602 ('C', 'copies', None, _('show copied files')),
3626 ('C', 'copies', None, _('show copied files')),
3603 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3627 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3604 ('r', 'rev', [], _('show the specified revision or range')),
3628 ('r', 'rev', [], _('show the specified revision or range')),
3605 ('', 'removed', None, _('include revisions where files were removed')),
3629 ('', 'removed', None, _('include revisions where files were removed')),
3606 ('m', 'only-merges', None, _('show only merges')),
3630 ('m', 'only-merges', None, _('show only merges')),
3607 ('u', 'user', [], _('revisions committed by user')),
3631 ('u', 'user', [], _('revisions committed by user')),
3608 ('b', 'only-branch', [],
3632 ('b', 'only-branch', [],
3609 _('show only changesets within the given named branch')),
3633 _('show only changesets within the given named branch')),
3610 ('P', 'prune', [],
3634 ('P', 'prune', [],
3611 _('do not display revision or any of its ancestors')),
3635 _('do not display revision or any of its ancestors')),
3612 ] + logopts + walkopts,
3636 ] + logopts + walkopts,
3613 _('[OPTION]... [FILE]')),
3637 _('[OPTION]... [FILE]')),
3614 "manifest":
3638 "manifest":
3615 (manifest,
3639 (manifest,
3616 [('r', 'rev', '', _('revision to display'))],
3640 [('r', 'rev', '', _('revision to display'))],
3617 _('[-r REV]')),
3641 _('[-r REV]')),
3618 "^merge":
3642 "^merge":
3619 (merge,
3643 (merge,
3620 [('f', 'force', None, _('force a merge with outstanding changes')),
3644 [('f', 'force', None, _('force a merge with outstanding changes')),
3621 ('r', 'rev', '', _('revision to merge')),
3645 ('r', 'rev', '', _('revision to merge')),
3622 ('P', 'preview', None,
3646 ('P', 'preview', None,
3623 _('review revisions to merge (no merge is performed)'))],
3647 _('review revisions to merge (no merge is performed)'))],
3624 _('[-P] [-f] [[-r] REV]')),
3648 _('[-P] [-f] [[-r] REV]')),
3625 "outgoing|out":
3649 "outgoing|out":
3626 (outgoing,
3650 (outgoing,
3627 [('f', 'force', None,
3651 [('f', 'force', None,
3628 _('run even when the destination is unrelated')),
3652 _('run even when the destination is unrelated')),
3629 ('r', 'rev', [],
3653 ('r', 'rev', [],
3630 _('a changeset intended to be included in the destination')),
3654 _('a changeset intended to be included in the destination')),
3631 ('n', 'newest-first', None, _('show newest record first')),
3655 ('n', 'newest-first', None, _('show newest record first')),
3632 ('b', 'branch', [],
3656 ('b', 'branch', [],
3633 _('a specific branch you would like to push')),
3657 _('a specific branch you would like to push')),
3634 ] + logopts + remoteopts,
3658 ] + logopts + remoteopts,
3635 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3659 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3636 "parents":
3660 "parents":
3637 (parents,
3661 (parents,
3638 [('r', 'rev', '', _('show parents of the specified revision')),
3662 [('r', 'rev', '', _('show parents of the specified revision')),
3639 ] + templateopts,
3663 ] + templateopts,
3640 _('[-r REV] [FILE]')),
3664 _('[-r REV] [FILE]')),
3641 "paths": (paths, [], _('[NAME]')),
3665 "paths": (paths, [], _('[NAME]')),
3642 "^pull":
3666 "^pull":
3643 (pull,
3667 (pull,
3644 [('u', 'update', None,
3668 [('u', 'update', None,
3645 _('update to new branch head if changesets were pulled')),
3669 _('update to new branch head if changesets were pulled')),
3646 ('f', 'force', None,
3670 ('f', 'force', None,
3647 _('run even when remote repository is unrelated')),
3671 _('run even when remote repository is unrelated')),
3648 ('r', 'rev', [],
3672 ('r', 'rev', [],
3649 _('a remote changeset intended to be added')),
3673 _('a remote changeset intended to be added')),
3650 ('b', 'branch', [],
3674 ('b', 'branch', [],
3651 _('a specific branch you would like to pull')),
3675 _('a specific branch you would like to pull')),
3652 ] + remoteopts,
3676 ] + remoteopts,
3653 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3677 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3654 "^push":
3678 "^push":
3655 (push,
3679 (push,
3656 [('f', 'force', None, _('force push')),
3680 [('f', 'force', None, _('force push')),
3657 ('r', 'rev', [],
3681 ('r', 'rev', [],
3658 _('a changeset intended to be included in the destination')),
3682 _('a changeset intended to be included in the destination')),
3659 ('b', 'branch', [],
3683 ('b', 'branch', [],
3660 _('a specific branch you would like to push')),
3684 _('a specific branch you would like to push')),
3661 ] + remoteopts,
3685 ] + remoteopts,
3662 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3686 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3663 "recover": (recover, []),
3687 "recover": (recover, []),
3664 "^remove|rm":
3688 "^remove|rm":
3665 (remove,
3689 (remove,
3666 [('A', 'after', None, _('record delete for missing files')),
3690 [('A', 'after', None, _('record delete for missing files')),
3667 ('f', 'force', None,
3691 ('f', 'force', None,
3668 _('remove (and delete) file even if added or modified')),
3692 _('remove (and delete) file even if added or modified')),
3669 ] + walkopts,
3693 ] + walkopts,
3670 _('[OPTION]... FILE...')),
3694 _('[OPTION]... FILE...')),
3671 "rename|mv":
3695 "rename|mv":
3672 (rename,
3696 (rename,
3673 [('A', 'after', None, _('record a rename that has already occurred')),
3697 [('A', 'after', None, _('record a rename that has already occurred')),
3674 ('f', 'force', None,
3698 ('f', 'force', None,
3675 _('forcibly copy over an existing managed file')),
3699 _('forcibly copy over an existing managed file')),
3676 ] + walkopts + dryrunopts,
3700 ] + walkopts + dryrunopts,
3677 _('[OPTION]... SOURCE... DEST')),
3701 _('[OPTION]... SOURCE... DEST')),
3678 "resolve":
3702 "resolve":
3679 (resolve,
3703 (resolve,
3680 [('a', 'all', None, _('select all unresolved files')),
3704 [('a', 'all', None, _('select all unresolved files')),
3681 ('l', 'list', None, _('list state of files needing merge')),
3705 ('l', 'list', None, _('list state of files needing merge')),
3682 ('m', 'mark', None, _('mark files as resolved')),
3706 ('m', 'mark', None, _('mark files as resolved')),
3683 ('u', 'unmark', None, _('unmark files as resolved')),
3707 ('u', 'unmark', None, _('unmark files as resolved')),
3684 ('n', 'no-status', None, _('hide status prefix'))]
3708 ('n', 'no-status', None, _('hide status prefix'))]
3685 + walkopts,
3709 + walkopts,
3686 _('[OPTION]... [FILE]...')),
3710 _('[OPTION]... [FILE]...')),
3687 "revert":
3711 "revert":
3688 (revert,
3712 (revert,
3689 [('a', 'all', None, _('revert all changes when no arguments given')),
3713 [('a', 'all', None, _('revert all changes when no arguments given')),
3690 ('d', 'date', '', _('tipmost revision matching date')),
3714 ('d', 'date', '', _('tipmost revision matching date')),
3691 ('r', 'rev', '', _('revert to the specified revision')),
3715 ('r', 'rev', '', _('revert to the specified revision')),
3692 ('', 'no-backup', None, _('do not save backup copies of files')),
3716 ('', 'no-backup', None, _('do not save backup copies of files')),
3693 ] + walkopts + dryrunopts,
3717 ] + walkopts + dryrunopts,
3694 _('[OPTION]... [-r REV] [NAME]...')),
3718 _('[OPTION]... [-r REV] [NAME]...')),
3695 "rollback": (rollback, []),
3719 "rollback": (rollback, []),
3696 "root": (root, []),
3720 "root": (root, []),
3697 "^serve":
3721 "^serve":
3698 (serve,
3722 (serve,
3699 [('A', 'accesslog', '', _('name of access log file to write to')),
3723 [('A', 'accesslog', '', _('name of access log file to write to')),
3700 ('d', 'daemon', None, _('run server in background')),
3724 ('d', 'daemon', None, _('run server in background')),
3701 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3725 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3702 ('E', 'errorlog', '', _('name of error log file to write to')),
3726 ('E', 'errorlog', '', _('name of error log file to write to')),
3703 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3727 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3704 ('a', 'address', '',
3728 ('a', 'address', '',
3705 _('address to listen on (default: all interfaces)')),
3729 _('address to listen on (default: all interfaces)')),
3706 ('', 'prefix', '',
3730 ('', 'prefix', '',
3707 _('prefix path to serve from (default: server root)')),
3731 _('prefix path to serve from (default: server root)')),
3708 ('n', 'name', '',
3732 ('n', 'name', '',
3709 _('name to show in web pages (default: working directory)')),
3733 _('name to show in web pages (default: working directory)')),
3710 ('', 'webdir-conf', '', _('name of the webdir config file'
3734 ('', 'webdir-conf', '', _('name of the webdir config file'
3711 ' (serve more than one repository)')),
3735 ' (serve more than one repository)')),
3712 ('', 'pid-file', '', _('name of file to write process ID to')),
3736 ('', 'pid-file', '', _('name of file to write process ID to')),
3713 ('', 'stdio', None, _('for remote clients')),
3737 ('', 'stdio', None, _('for remote clients')),
3714 ('t', 'templates', '', _('web templates to use')),
3738 ('t', 'templates', '', _('web templates to use')),
3715 ('', 'style', '', _('template style to use')),
3739 ('', 'style', '', _('template style to use')),
3716 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3740 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3717 ('', 'certificate', '', _('SSL certificate file'))],
3741 ('', 'certificate', '', _('SSL certificate file'))],
3718 _('[OPTION]...')),
3742 _('[OPTION]...')),
3719 "showconfig|debugconfig":
3743 "showconfig|debugconfig":
3720 (showconfig,
3744 (showconfig,
3721 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3745 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3722 _('[-u] [NAME]...')),
3746 _('[-u] [NAME]...')),
3723 "^summary|sum":
3747 "^summary|sum":
3724 (summary,
3748 (summary,
3725 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
3749 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
3726 "^status|st":
3750 "^status|st":
3727 (status,
3751 (status,
3728 [('A', 'all', None, _('show status of all files')),
3752 [('A', 'all', None, _('show status of all files')),
3729 ('m', 'modified', None, _('show only modified files')),
3753 ('m', 'modified', None, _('show only modified files')),
3730 ('a', 'added', None, _('show only added files')),
3754 ('a', 'added', None, _('show only added files')),
3731 ('r', 'removed', None, _('show only removed files')),
3755 ('r', 'removed', None, _('show only removed files')),
3732 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3756 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3733 ('c', 'clean', None, _('show only files without changes')),
3757 ('c', 'clean', None, _('show only files without changes')),
3734 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3758 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3735 ('i', 'ignored', None, _('show only ignored files')),
3759 ('i', 'ignored', None, _('show only ignored files')),
3736 ('n', 'no-status', None, _('hide status prefix')),
3760 ('n', 'no-status', None, _('hide status prefix')),
3737 ('C', 'copies', None, _('show source of copied files')),
3761 ('C', 'copies', None, _('show source of copied files')),
3738 ('0', 'print0', None,
3762 ('0', 'print0', None,
3739 _('end filenames with NUL, for use with xargs')),
3763 _('end filenames with NUL, for use with xargs')),
3740 ('', 'rev', [], _('show difference from revision')),
3764 ('', 'rev', [], _('show difference from revision')),
3741 ('', 'change', '', _('list the changed files of a revision')),
3765 ('', 'change', '', _('list the changed files of a revision')),
3742 ] + walkopts,
3766 ] + walkopts,
3743 _('[OPTION]... [FILE]...')),
3767 _('[OPTION]... [FILE]...')),
3744 "tag":
3768 "tag":
3745 (tag,
3769 (tag,
3746 [('f', 'force', None, _('replace existing tag')),
3770 [('f', 'force', None, _('replace existing tag')),
3747 ('l', 'local', None, _('make the tag local')),
3771 ('l', 'local', None, _('make the tag local')),
3748 ('r', 'rev', '', _('revision to tag')),
3772 ('r', 'rev', '', _('revision to tag')),
3749 ('', 'remove', None, _('remove a tag')),
3773 ('', 'remove', None, _('remove a tag')),
3750 # -l/--local is already there, commitopts cannot be used
3774 # -l/--local is already there, commitopts cannot be used
3751 ('m', 'message', '', _('use <text> as commit message')),
3775 ('m', 'message', '', _('use <text> as commit message')),
3752 ] + commitopts2,
3776 ] + commitopts2,
3753 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3777 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3754 "tags": (tags, [], ''),
3778 "tags": (tags, [], ''),
3755 "tip":
3779 "tip":
3756 (tip,
3780 (tip,
3757 [('p', 'patch', None, _('show patch')),
3781 [('p', 'patch', None, _('show patch')),
3758 ('g', 'git', None, _('use git extended diff format')),
3782 ('g', 'git', None, _('use git extended diff format')),
3759 ] + templateopts,
3783 ] + templateopts,
3760 _('[-p] [-g]')),
3784 _('[-p] [-g]')),
3761 "unbundle":
3785 "unbundle":
3762 (unbundle,
3786 (unbundle,
3763 [('u', 'update', None,
3787 [('u', 'update', None,
3764 _('update to new branch head if changesets were unbundled'))],
3788 _('update to new branch head if changesets were unbundled'))],
3765 _('[-u] FILE...')),
3789 _('[-u] FILE...')),
3766 "^update|up|checkout|co":
3790 "^update|up|checkout|co":
3767 (update,
3791 (update,
3768 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
3792 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
3769 ('c', 'check', None, _('check for uncommitted changes')),
3793 ('c', 'check', None, _('check for uncommitted changes')),
3770 ('d', 'date', '', _('tipmost revision matching date')),
3794 ('d', 'date', '', _('tipmost revision matching date')),
3771 ('r', 'rev', '', _('revision'))],
3795 ('r', 'rev', '', _('revision'))],
3772 _('[-c] [-C] [-d DATE] [[-r] REV]')),
3796 _('[-c] [-C] [-d DATE] [[-r] REV]')),
3773 "verify": (verify, []),
3797 "verify": (verify, []),
3774 "version": (version_, []),
3798 "version": (version_, []),
3775 }
3799 }
3776
3800
3777 norepo = ("clone init version help debugcommands debugcomplete debugdata"
3801 norepo = ("clone init version help debugcommands debugcomplete debugdata"
3778 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3802 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3779 optionalrepo = ("identify paths serve showconfig debugancestor")
3803 optionalrepo = ("identify paths serve showconfig debugancestor")
@@ -1,1534 +1,1658 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):
45 '''return an iterator of individual patches from a stream'''
46 def isheader(line, inheader):
47 if inheader and line[0] in (' ', '\t'):
48 # continuation
49 return True
50 l = line.split(': ', 1)
51 return len(l) == 2 and ' ' not in l[0]
52
53 def chunk(lines):
54 return cStringIO.StringIO(''.join(lines))
55
56 def hgsplit(stream, cur):
57 inheader = True
58
59 for line in stream:
60 if not line.strip():
61 inheader = False
62 if not inheader and line.startswith('# HG changeset patch'):
63 yield chunk(cur)
64 cur = []
65 inheader = True
66
67 cur.append(line)
68
69 if cur:
70 yield chunk(cur)
71
72 def mboxsplit(stream, cur):
73 for line in stream:
74 if line.startswith('From '):
75 for c in split(chunk(cur[1:])):
76 yield c
77 cur = []
78
79 cur.append(line)
80
81 if cur:
82 for c in split(chunk(cur[1:])):
83 yield c
84
85 def mimesplit(stream, cur):
86 def msgfp(m):
87 fp = cStringIO.StringIO()
88 g = email.Generator.Generator(fp, mangle_from_=False)
89 g.flatten(m)
90 fp.seek(0)
91 return fp
92
93 for line in stream:
94 cur.append(line)
95 c = chunk(cur)
96
97 m = email.Parser.Parser().parse(c)
98 if not m.is_multipart():
99 yield msgfp(m)
100 else:
101 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
102 for part in m.walk():
103 ct = part.get_content_type()
104 if ct not in ok_types:
105 continue
106 yield msgfp(part)
107
108 def headersplit(stream, cur):
109 inheader = False
110
111 for line in stream:
112 if not inheader and isheader(line, inheader):
113 yield chunk(cur)
114 cur = []
115 inheader = True
116 if inheader and not isheader(line, inheader):
117 inheader = False
118
119 cur.append(line)
120
121 if cur:
122 yield chunk(cur)
123
124 def remainder(cur):
125 yield chunk(cur)
126
127 class fiter(object):
128 def __init__(self, fp):
129 self.fp = fp
130
131 def __iter__(self):
132 return self
133
134 def next(self):
135 l = self.fp.readline()
136 if not l:
137 raise StopIteration
138 return l
139
140 inheader = False
141 cur = []
142
143 mimeheaders = ['content-type']
144
145 if not hasattr(stream, 'next'):
146 # http responses, for example, have readline but not next
147 stream = fiter(stream)
148
149 for line in stream:
150 cur.append(line)
151 if line.startswith('# HG changeset patch'):
152 return hgsplit(stream, cur)
153 elif line.startswith('From '):
154 return mboxsplit(stream, cur)
155 elif isheader(line, inheader):
156 inheader = True
157 if line.split(':', 1)[0].lower() in mimeheaders:
158 # let email parser handle this
159 return mimesplit(stream, cur)
160 elif inheader:
161 # No evil headers seen, split by hand
162 return headersplit(stream, cur)
163 # Not enough info, keep reading
164
165 # if we are here, we have a very plain patch
166 return remainder(cur)
167
44 def extract(ui, fileobj):
168 def extract(ui, fileobj):
45 '''extract patch from data read from fileobj.
169 '''extract patch from data read from fileobj.
46
170
47 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.
48
172
49 return tuple (filename, message, user, date, node, p1, p2).
173 return tuple (filename, message, user, date, node, p1, p2).
50 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,
51 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.'''
52
176
53 # attempt to detect the start of a patch
177 # attempt to detect the start of a patch
54 # (this heuristic is borrowed from quilt)
178 # (this heuristic is borrowed from quilt)
55 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |'
179 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |'
56 r'retrieving revision [0-9]+(\.[0-9]+)*$|'
180 r'retrieving revision [0-9]+(\.[0-9]+)*$|'
57 r'(---|\*\*\*)[ \t])', re.MULTILINE)
181 r'(---|\*\*\*)[ \t])', re.MULTILINE)
58
182
59 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
183 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
60 tmpfp = os.fdopen(fd, 'w')
184 tmpfp = os.fdopen(fd, 'w')
61 try:
185 try:
62 msg = email.Parser.Parser().parse(fileobj)
186 msg = email.Parser.Parser().parse(fileobj)
63
187
64 subject = msg['Subject']
188 subject = msg['Subject']
65 user = msg['From']
189 user = msg['From']
66 if not subject and not user:
190 if not subject and not user:
67 # Not an email, restore parsed headers if any
191 # Not an email, restore parsed headers if any
68 subject = '\n'.join(': '.join(h) for h in msg.items()) + '\n'
192 subject = '\n'.join(': '.join(h) for h in msg.items()) + '\n'
69
193
70 gitsendmail = 'git-send-email' in msg.get('X-Mailer', '')
194 gitsendmail = 'git-send-email' in msg.get('X-Mailer', '')
71 # should try to parse msg['Date']
195 # should try to parse msg['Date']
72 date = None
196 date = None
73 nodeid = None
197 nodeid = None
74 branch = None
198 branch = None
75 parents = []
199 parents = []
76
200
77 if subject:
201 if subject:
78 if subject.startswith('[PATCH'):
202 if subject.startswith('[PATCH'):
79 pend = subject.find(']')
203 pend = subject.find(']')
80 if pend >= 0:
204 if pend >= 0:
81 subject = subject[pend + 1:].lstrip()
205 subject = subject[pend + 1:].lstrip()
82 subject = subject.replace('\n\t', ' ')
206 subject = subject.replace('\n\t', ' ')
83 ui.debug('Subject: %s\n' % subject)
207 ui.debug('Subject: %s\n' % subject)
84 if user:
208 if user:
85 ui.debug('From: %s\n' % user)
209 ui.debug('From: %s\n' % user)
86 diffs_seen = 0
210 diffs_seen = 0
87 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
211 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
88 message = ''
212 message = ''
89 for part in msg.walk():
213 for part in msg.walk():
90 content_type = part.get_content_type()
214 content_type = part.get_content_type()
91 ui.debug('Content-Type: %s\n' % content_type)
215 ui.debug('Content-Type: %s\n' % content_type)
92 if content_type not in ok_types:
216 if content_type not in ok_types:
93 continue
217 continue
94 payload = part.get_payload(decode=True)
218 payload = part.get_payload(decode=True)
95 m = diffre.search(payload)
219 m = diffre.search(payload)
96 if m:
220 if m:
97 hgpatch = False
221 hgpatch = False
98 ignoretext = False
222 ignoretext = False
99
223
100 ui.debug('found patch at byte %d\n' % m.start(0))
224 ui.debug('found patch at byte %d\n' % m.start(0))
101 diffs_seen += 1
225 diffs_seen += 1
102 cfp = cStringIO.StringIO()
226 cfp = cStringIO.StringIO()
103 for line in payload[:m.start(0)].splitlines():
227 for line in payload[:m.start(0)].splitlines():
104 if line.startswith('# HG changeset patch'):
228 if line.startswith('# HG changeset patch'):
105 ui.debug('patch generated by hg export\n')
229 ui.debug('patch generated by hg export\n')
106 hgpatch = True
230 hgpatch = True
107 # drop earlier commit message content
231 # drop earlier commit message content
108 cfp.seek(0)
232 cfp.seek(0)
109 cfp.truncate()
233 cfp.truncate()
110 subject = None
234 subject = None
111 elif hgpatch:
235 elif hgpatch:
112 if line.startswith('# User '):
236 if line.startswith('# User '):
113 user = line[7:]
237 user = line[7:]
114 ui.debug('From: %s\n' % user)
238 ui.debug('From: %s\n' % user)
115 elif line.startswith("# Date "):
239 elif line.startswith("# Date "):
116 date = line[7:]
240 date = line[7:]
117 elif line.startswith("# Branch "):
241 elif line.startswith("# Branch "):
118 branch = line[9:]
242 branch = line[9:]
119 elif line.startswith("# Node ID "):
243 elif line.startswith("# Node ID "):
120 nodeid = line[10:]
244 nodeid = line[10:]
121 elif line.startswith("# Parent "):
245 elif line.startswith("# Parent "):
122 parents.append(line[10:])
246 parents.append(line[10:])
123 elif line == '---' and gitsendmail:
247 elif line == '---' and gitsendmail:
124 ignoretext = True
248 ignoretext = True
125 if not line.startswith('# ') and not ignoretext:
249 if not line.startswith('# ') and not ignoretext:
126 cfp.write(line)
250 cfp.write(line)
127 cfp.write('\n')
251 cfp.write('\n')
128 message = cfp.getvalue()
252 message = cfp.getvalue()
129 if tmpfp:
253 if tmpfp:
130 tmpfp.write(payload)
254 tmpfp.write(payload)
131 if not payload.endswith('\n'):
255 if not payload.endswith('\n'):
132 tmpfp.write('\n')
256 tmpfp.write('\n')
133 elif not diffs_seen and message and content_type == 'text/plain':
257 elif not diffs_seen and message and content_type == 'text/plain':
134 message += '\n' + payload
258 message += '\n' + payload
135 except:
259 except:
136 tmpfp.close()
260 tmpfp.close()
137 os.unlink(tmpname)
261 os.unlink(tmpname)
138 raise
262 raise
139
263
140 if subject and not message.startswith(subject):
264 if subject and not message.startswith(subject):
141 message = '%s\n%s' % (subject, message)
265 message = '%s\n%s' % (subject, message)
142 tmpfp.close()
266 tmpfp.close()
143 if not diffs_seen:
267 if not diffs_seen:
144 os.unlink(tmpname)
268 os.unlink(tmpname)
145 return None, message, user, date, branch, None, None, None
269 return None, message, user, date, branch, None, None, None
146 p1 = parents and parents.pop(0) or None
270 p1 = parents and parents.pop(0) or None
147 p2 = parents and parents.pop(0) or None
271 p2 = parents and parents.pop(0) or None
148 return tmpname, message, user, date, branch, nodeid, p1, p2
272 return tmpname, message, user, date, branch, nodeid, p1, p2
149
273
150 GP_PATCH = 1 << 0 # we have to run patch
274 GP_PATCH = 1 << 0 # we have to run patch
151 GP_FILTER = 1 << 1 # there's some copy/rename operation
275 GP_FILTER = 1 << 1 # there's some copy/rename operation
152 GP_BINARY = 1 << 2 # there's a binary patch
276 GP_BINARY = 1 << 2 # there's a binary patch
153
277
154 class patchmeta(object):
278 class patchmeta(object):
155 """Patched file metadata
279 """Patched file metadata
156
280
157 'op' is the performed operation within ADD, DELETE, RENAME, MODIFY
281 'op' is the performed operation within ADD, DELETE, RENAME, MODIFY
158 or COPY. 'path' is patched file path. 'oldpath' is set to the
282 or COPY. 'path' is patched file path. 'oldpath' is set to the
159 origin file when 'op' is either COPY or RENAME, None otherwise. If
283 origin file when 'op' is either COPY or RENAME, None otherwise. If
160 file mode is changed, 'mode' is a tuple (islink, isexec) where
284 file mode is changed, 'mode' is a tuple (islink, isexec) where
161 'islink' is True if the file is a symlink and 'isexec' is True if
285 'islink' is True if the file is a symlink and 'isexec' is True if
162 the file is executable. Otherwise, 'mode' is None.
286 the file is executable. Otherwise, 'mode' is None.
163 """
287 """
164 def __init__(self, path):
288 def __init__(self, path):
165 self.path = path
289 self.path = path
166 self.oldpath = None
290 self.oldpath = None
167 self.mode = None
291 self.mode = None
168 self.op = 'MODIFY'
292 self.op = 'MODIFY'
169 self.lineno = 0
293 self.lineno = 0
170 self.binary = False
294 self.binary = False
171
295
172 def setmode(self, mode):
296 def setmode(self, mode):
173 islink = mode & 020000
297 islink = mode & 020000
174 isexec = mode & 0100
298 isexec = mode & 0100
175 self.mode = (islink, isexec)
299 self.mode = (islink, isexec)
176
300
177 def readgitpatch(lr):
301 def readgitpatch(lr):
178 """extract git-style metadata about patches from <patchname>"""
302 """extract git-style metadata about patches from <patchname>"""
179
303
180 # Filter patch for git information
304 # Filter patch for git information
181 gp = None
305 gp = None
182 gitpatches = []
306 gitpatches = []
183 # Can have a git patch with only metadata, causing patch to complain
307 # Can have a git patch with only metadata, causing patch to complain
184 dopatch = 0
308 dopatch = 0
185
309
186 lineno = 0
310 lineno = 0
187 for line in lr:
311 for line in lr:
188 lineno += 1
312 lineno += 1
189 line = line.rstrip(' \r\n')
313 line = line.rstrip(' \r\n')
190 if line.startswith('diff --git'):
314 if line.startswith('diff --git'):
191 m = gitre.match(line)
315 m = gitre.match(line)
192 if m:
316 if m:
193 if gp:
317 if gp:
194 gitpatches.append(gp)
318 gitpatches.append(gp)
195 dst = m.group(2)
319 dst = m.group(2)
196 gp = patchmeta(dst)
320 gp = patchmeta(dst)
197 gp.lineno = lineno
321 gp.lineno = lineno
198 elif gp:
322 elif gp:
199 if line.startswith('--- '):
323 if line.startswith('--- '):
200 if gp.op in ('COPY', 'RENAME'):
324 if gp.op in ('COPY', 'RENAME'):
201 dopatch |= GP_FILTER
325 dopatch |= GP_FILTER
202 gitpatches.append(gp)
326 gitpatches.append(gp)
203 gp = None
327 gp = None
204 dopatch |= GP_PATCH
328 dopatch |= GP_PATCH
205 continue
329 continue
206 if line.startswith('rename from '):
330 if line.startswith('rename from '):
207 gp.op = 'RENAME'
331 gp.op = 'RENAME'
208 gp.oldpath = line[12:]
332 gp.oldpath = line[12:]
209 elif line.startswith('rename to '):
333 elif line.startswith('rename to '):
210 gp.path = line[10:]
334 gp.path = line[10:]
211 elif line.startswith('copy from '):
335 elif line.startswith('copy from '):
212 gp.op = 'COPY'
336 gp.op = 'COPY'
213 gp.oldpath = line[10:]
337 gp.oldpath = line[10:]
214 elif line.startswith('copy to '):
338 elif line.startswith('copy to '):
215 gp.path = line[8:]
339 gp.path = line[8:]
216 elif line.startswith('deleted file'):
340 elif line.startswith('deleted file'):
217 gp.op = 'DELETE'
341 gp.op = 'DELETE'
218 # is the deleted file a symlink?
342 # is the deleted file a symlink?
219 gp.setmode(int(line[-6:], 8))
343 gp.setmode(int(line[-6:], 8))
220 elif line.startswith('new file mode '):
344 elif line.startswith('new file mode '):
221 gp.op = 'ADD'
345 gp.op = 'ADD'
222 gp.setmode(int(line[-6:], 8))
346 gp.setmode(int(line[-6:], 8))
223 elif line.startswith('new mode '):
347 elif line.startswith('new mode '):
224 gp.setmode(int(line[-6:], 8))
348 gp.setmode(int(line[-6:], 8))
225 elif line.startswith('GIT binary patch'):
349 elif line.startswith('GIT binary patch'):
226 dopatch |= GP_BINARY
350 dopatch |= GP_BINARY
227 gp.binary = True
351 gp.binary = True
228 if gp:
352 if gp:
229 gitpatches.append(gp)
353 gitpatches.append(gp)
230
354
231 if not gitpatches:
355 if not gitpatches:
232 dopatch = GP_PATCH
356 dopatch = GP_PATCH
233
357
234 return (dopatch, gitpatches)
358 return (dopatch, gitpatches)
235
359
236 class linereader(object):
360 class linereader(object):
237 # simple class to allow pushing lines back into the input stream
361 # simple class to allow pushing lines back into the input stream
238 def __init__(self, fp, textmode=False):
362 def __init__(self, fp, textmode=False):
239 self.fp = fp
363 self.fp = fp
240 self.buf = []
364 self.buf = []
241 self.textmode = textmode
365 self.textmode = textmode
242 self.eol = None
366 self.eol = None
243
367
244 def push(self, line):
368 def push(self, line):
245 if line is not None:
369 if line is not None:
246 self.buf.append(line)
370 self.buf.append(line)
247
371
248 def readline(self):
372 def readline(self):
249 if self.buf:
373 if self.buf:
250 l = self.buf[0]
374 l = self.buf[0]
251 del self.buf[0]
375 del self.buf[0]
252 return l
376 return l
253 l = self.fp.readline()
377 l = self.fp.readline()
254 if not self.eol:
378 if not self.eol:
255 if l.endswith('\r\n'):
379 if l.endswith('\r\n'):
256 self.eol = '\r\n'
380 self.eol = '\r\n'
257 elif l.endswith('\n'):
381 elif l.endswith('\n'):
258 self.eol = '\n'
382 self.eol = '\n'
259 if self.textmode and l.endswith('\r\n'):
383 if self.textmode and l.endswith('\r\n'):
260 l = l[:-2] + '\n'
384 l = l[:-2] + '\n'
261 return l
385 return l
262
386
263 def __iter__(self):
387 def __iter__(self):
264 while 1:
388 while 1:
265 l = self.readline()
389 l = self.readline()
266 if not l:
390 if not l:
267 break
391 break
268 yield l
392 yield l
269
393
270 # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1
394 # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1
271 unidesc = re.compile('@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@')
395 unidesc = re.compile('@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@')
272 contextdesc = re.compile('(---|\*\*\*) (\d+)(,(\d+))? (---|\*\*\*)')
396 contextdesc = re.compile('(---|\*\*\*) (\d+)(,(\d+))? (---|\*\*\*)')
273 eolmodes = ['strict', 'crlf', 'lf', 'auto']
397 eolmodes = ['strict', 'crlf', 'lf', 'auto']
274
398
275 class patchfile(object):
399 class patchfile(object):
276 def __init__(self, ui, fname, opener, missing=False, eolmode='strict'):
400 def __init__(self, ui, fname, opener, missing=False, eolmode='strict'):
277 self.fname = fname
401 self.fname = fname
278 self.eolmode = eolmode
402 self.eolmode = eolmode
279 self.eol = None
403 self.eol = None
280 self.opener = opener
404 self.opener = opener
281 self.ui = ui
405 self.ui = ui
282 self.lines = []
406 self.lines = []
283 self.exists = False
407 self.exists = False
284 self.missing = missing
408 self.missing = missing
285 if not missing:
409 if not missing:
286 try:
410 try:
287 self.lines = self.readlines(fname)
411 self.lines = self.readlines(fname)
288 self.exists = True
412 self.exists = True
289 except IOError:
413 except IOError:
290 pass
414 pass
291 else:
415 else:
292 self.ui.warn(_("unable to find '%s' for patching\n") % self.fname)
416 self.ui.warn(_("unable to find '%s' for patching\n") % self.fname)
293
417
294 self.hash = {}
418 self.hash = {}
295 self.dirty = 0
419 self.dirty = 0
296 self.offset = 0
420 self.offset = 0
297 self.skew = 0
421 self.skew = 0
298 self.rej = []
422 self.rej = []
299 self.fileprinted = False
423 self.fileprinted = False
300 self.printfile(False)
424 self.printfile(False)
301 self.hunks = 0
425 self.hunks = 0
302
426
303 def readlines(self, fname):
427 def readlines(self, fname):
304 if os.path.islink(fname):
428 if os.path.islink(fname):
305 return [os.readlink(fname)]
429 return [os.readlink(fname)]
306 fp = self.opener(fname, 'r')
430 fp = self.opener(fname, 'r')
307 try:
431 try:
308 lr = linereader(fp, self.eolmode != 'strict')
432 lr = linereader(fp, self.eolmode != 'strict')
309 lines = list(lr)
433 lines = list(lr)
310 self.eol = lr.eol
434 self.eol = lr.eol
311 return lines
435 return lines
312 finally:
436 finally:
313 fp.close()
437 fp.close()
314
438
315 def writelines(self, fname, lines):
439 def writelines(self, fname, lines):
316 # Ensure supplied data ends in fname, being a regular file or
440 # Ensure supplied data ends in fname, being a regular file or
317 # a symlink. updatedir() will -too magically- take care of
441 # a symlink. updatedir() will -too magically- take care of
318 # setting it to the proper type afterwards.
442 # setting it to the proper type afterwards.
319 islink = os.path.islink(fname)
443 islink = os.path.islink(fname)
320 if islink:
444 if islink:
321 fp = cStringIO.StringIO()
445 fp = cStringIO.StringIO()
322 else:
446 else:
323 fp = self.opener(fname, 'w')
447 fp = self.opener(fname, 'w')
324 try:
448 try:
325 if self.eolmode == 'auto':
449 if self.eolmode == 'auto':
326 eol = self.eol
450 eol = self.eol
327 elif self.eolmode == 'crlf':
451 elif self.eolmode == 'crlf':
328 eol = '\r\n'
452 eol = '\r\n'
329 else:
453 else:
330 eol = '\n'
454 eol = '\n'
331
455
332 if self.eolmode != 'strict' and eol and eol != '\n':
456 if self.eolmode != 'strict' and eol and eol != '\n':
333 for l in lines:
457 for l in lines:
334 if l and l[-1] == '\n':
458 if l and l[-1] == '\n':
335 l = l[:-1] + eol
459 l = l[:-1] + eol
336 fp.write(l)
460 fp.write(l)
337 else:
461 else:
338 fp.writelines(lines)
462 fp.writelines(lines)
339 if islink:
463 if islink:
340 self.opener.symlink(fp.getvalue(), fname)
464 self.opener.symlink(fp.getvalue(), fname)
341 finally:
465 finally:
342 fp.close()
466 fp.close()
343
467
344 def unlink(self, fname):
468 def unlink(self, fname):
345 os.unlink(fname)
469 os.unlink(fname)
346
470
347 def printfile(self, warn):
471 def printfile(self, warn):
348 if self.fileprinted:
472 if self.fileprinted:
349 return
473 return
350 if warn or self.ui.verbose:
474 if warn or self.ui.verbose:
351 self.fileprinted = True
475 self.fileprinted = True
352 s = _("patching file %s\n") % self.fname
476 s = _("patching file %s\n") % self.fname
353 if warn:
477 if warn:
354 self.ui.warn(s)
478 self.ui.warn(s)
355 else:
479 else:
356 self.ui.note(s)
480 self.ui.note(s)
357
481
358
482
359 def findlines(self, l, linenum):
483 def findlines(self, l, linenum):
360 # looks through the hash and finds candidate lines. The
484 # looks through the hash and finds candidate lines. The
361 # result is a list of line numbers sorted based on distance
485 # result is a list of line numbers sorted based on distance
362 # from linenum
486 # from linenum
363
487
364 cand = self.hash.get(l, [])
488 cand = self.hash.get(l, [])
365 if len(cand) > 1:
489 if len(cand) > 1:
366 # resort our list of potentials forward then back.
490 # resort our list of potentials forward then back.
367 cand.sort(key=lambda x: abs(x - linenum))
491 cand.sort(key=lambda x: abs(x - linenum))
368 return cand
492 return cand
369
493
370 def hashlines(self):
494 def hashlines(self):
371 self.hash = {}
495 self.hash = {}
372 for x, s in enumerate(self.lines):
496 for x, s in enumerate(self.lines):
373 self.hash.setdefault(s, []).append(x)
497 self.hash.setdefault(s, []).append(x)
374
498
375 def write_rej(self):
499 def write_rej(self):
376 # our rejects are a little different from patch(1). This always
500 # our rejects are a little different from patch(1). This always
377 # creates rejects in the same form as the original patch. A file
501 # creates rejects in the same form as the original patch. A file
378 # header is inserted so that you can run the reject through patch again
502 # header is inserted so that you can run the reject through patch again
379 # without having to type the filename.
503 # without having to type the filename.
380
504
381 if not self.rej:
505 if not self.rej:
382 return
506 return
383
507
384 fname = self.fname + ".rej"
508 fname = self.fname + ".rej"
385 self.ui.warn(
509 self.ui.warn(
386 _("%d out of %d hunks FAILED -- saving rejects to file %s\n") %
510 _("%d out of %d hunks FAILED -- saving rejects to file %s\n") %
387 (len(self.rej), self.hunks, fname))
511 (len(self.rej), self.hunks, fname))
388
512
389 def rejlines():
513 def rejlines():
390 base = os.path.basename(self.fname)
514 base = os.path.basename(self.fname)
391 yield "--- %s\n+++ %s\n" % (base, base)
515 yield "--- %s\n+++ %s\n" % (base, base)
392 for x in self.rej:
516 for x in self.rej:
393 for l in x.hunk:
517 for l in x.hunk:
394 yield l
518 yield l
395 if l[-1] != '\n':
519 if l[-1] != '\n':
396 yield "\n\ No newline at end of file\n"
520 yield "\n\ No newline at end of file\n"
397
521
398 self.writelines(fname, rejlines())
522 self.writelines(fname, rejlines())
399
523
400 def write(self, dest=None):
524 def write(self, dest=None):
401 if not self.dirty:
525 if not self.dirty:
402 return
526 return
403 if not dest:
527 if not dest:
404 dest = self.fname
528 dest = self.fname
405 self.writelines(dest, self.lines)
529 self.writelines(dest, self.lines)
406
530
407 def close(self):
531 def close(self):
408 self.write()
532 self.write()
409 self.write_rej()
533 self.write_rej()
410
534
411 def apply(self, h):
535 def apply(self, h):
412 if not h.complete():
536 if not h.complete():
413 raise PatchError(_("bad hunk #%d %s (%d %d %d %d)") %
537 raise PatchError(_("bad hunk #%d %s (%d %d %d %d)") %
414 (h.number, h.desc, len(h.a), h.lena, len(h.b),
538 (h.number, h.desc, len(h.a), h.lena, len(h.b),
415 h.lenb))
539 h.lenb))
416
540
417 self.hunks += 1
541 self.hunks += 1
418
542
419 if self.missing:
543 if self.missing:
420 self.rej.append(h)
544 self.rej.append(h)
421 return -1
545 return -1
422
546
423 if self.exists and h.createfile():
547 if self.exists and h.createfile():
424 self.ui.warn(_("file %s already exists\n") % self.fname)
548 self.ui.warn(_("file %s already exists\n") % self.fname)
425 self.rej.append(h)
549 self.rej.append(h)
426 return -1
550 return -1
427
551
428 if isinstance(h, binhunk):
552 if isinstance(h, binhunk):
429 if h.rmfile():
553 if h.rmfile():
430 self.unlink(self.fname)
554 self.unlink(self.fname)
431 else:
555 else:
432 self.lines[:] = h.new()
556 self.lines[:] = h.new()
433 self.offset += len(h.new())
557 self.offset += len(h.new())
434 self.dirty = 1
558 self.dirty = 1
435 return 0
559 return 0
436
560
437 horig = h
561 horig = h
438 if (self.eolmode in ('crlf', 'lf')
562 if (self.eolmode in ('crlf', 'lf')
439 or self.eolmode == 'auto' and self.eol):
563 or self.eolmode == 'auto' and self.eol):
440 # If new eols are going to be normalized, then normalize
564 # If new eols are going to be normalized, then normalize
441 # hunk data before patching. Otherwise, preserve input
565 # hunk data before patching. Otherwise, preserve input
442 # line-endings.
566 # line-endings.
443 h = h.getnormalized()
567 h = h.getnormalized()
444
568
445 # fast case first, no offsets, no fuzz
569 # fast case first, no offsets, no fuzz
446 old = h.old()
570 old = h.old()
447 # patch starts counting at 1 unless we are adding the file
571 # patch starts counting at 1 unless we are adding the file
448 if h.starta == 0:
572 if h.starta == 0:
449 start = 0
573 start = 0
450 else:
574 else:
451 start = h.starta + self.offset - 1
575 start = h.starta + self.offset - 1
452 orig_start = start
576 orig_start = start
453 # if there's skew we want to emit the "(offset %d lines)" even
577 # if there's skew we want to emit the "(offset %d lines)" even
454 # when the hunk cleanly applies at start + skew, so skip the
578 # when the hunk cleanly applies at start + skew, so skip the
455 # fast case code
579 # fast case code
456 if self.skew == 0 and diffhelpers.testhunk(old, self.lines, start) == 0:
580 if self.skew == 0 and diffhelpers.testhunk(old, self.lines, start) == 0:
457 if h.rmfile():
581 if h.rmfile():
458 self.unlink(self.fname)
582 self.unlink(self.fname)
459 else:
583 else:
460 self.lines[start : start + h.lena] = h.new()
584 self.lines[start : start + h.lena] = h.new()
461 self.offset += h.lenb - h.lena
585 self.offset += h.lenb - h.lena
462 self.dirty = 1
586 self.dirty = 1
463 return 0
587 return 0
464
588
465 # ok, we couldn't match the hunk. Lets look for offsets and fuzz it
589 # ok, we couldn't match the hunk. Lets look for offsets and fuzz it
466 self.hashlines()
590 self.hashlines()
467 if h.hunk[-1][0] != ' ':
591 if h.hunk[-1][0] != ' ':
468 # if the hunk tried to put something at the bottom of the file
592 # if the hunk tried to put something at the bottom of the file
469 # override the start line and use eof here
593 # override the start line and use eof here
470 search_start = len(self.lines)
594 search_start = len(self.lines)
471 else:
595 else:
472 search_start = orig_start + self.skew
596 search_start = orig_start + self.skew
473
597
474 for fuzzlen in xrange(3):
598 for fuzzlen in xrange(3):
475 for toponly in [True, False]:
599 for toponly in [True, False]:
476 old = h.old(fuzzlen, toponly)
600 old = h.old(fuzzlen, toponly)
477
601
478 cand = self.findlines(old[0][1:], search_start)
602 cand = self.findlines(old[0][1:], search_start)
479 for l in cand:
603 for l in cand:
480 if diffhelpers.testhunk(old, self.lines, l) == 0:
604 if diffhelpers.testhunk(old, self.lines, l) == 0:
481 newlines = h.new(fuzzlen, toponly)
605 newlines = h.new(fuzzlen, toponly)
482 self.lines[l : l + len(old)] = newlines
606 self.lines[l : l + len(old)] = newlines
483 self.offset += len(newlines) - len(old)
607 self.offset += len(newlines) - len(old)
484 self.skew = l - orig_start
608 self.skew = l - orig_start
485 self.dirty = 1
609 self.dirty = 1
486 if fuzzlen:
610 if fuzzlen:
487 fuzzstr = "with fuzz %d " % fuzzlen
611 fuzzstr = "with fuzz %d " % fuzzlen
488 f = self.ui.warn
612 f = self.ui.warn
489 self.printfile(True)
613 self.printfile(True)
490 else:
614 else:
491 fuzzstr = ""
615 fuzzstr = ""
492 f = self.ui.note
616 f = self.ui.note
493 offset = l - orig_start - fuzzlen
617 offset = l - orig_start - fuzzlen
494 if offset == 1:
618 if offset == 1:
495 msg = _("Hunk #%d succeeded at %d %s"
619 msg = _("Hunk #%d succeeded at %d %s"
496 "(offset %d line).\n")
620 "(offset %d line).\n")
497 else:
621 else:
498 msg = _("Hunk #%d succeeded at %d %s"
622 msg = _("Hunk #%d succeeded at %d %s"
499 "(offset %d lines).\n")
623 "(offset %d lines).\n")
500 f(msg % (h.number, l + 1, fuzzstr, offset))
624 f(msg % (h.number, l + 1, fuzzstr, offset))
501 return fuzzlen
625 return fuzzlen
502 self.printfile(True)
626 self.printfile(True)
503 self.ui.warn(_("Hunk #%d FAILED at %d\n") % (h.number, orig_start))
627 self.ui.warn(_("Hunk #%d FAILED at %d\n") % (h.number, orig_start))
504 self.rej.append(horig)
628 self.rej.append(horig)
505 return -1
629 return -1
506
630
507 class hunk(object):
631 class hunk(object):
508 def __init__(self, desc, num, lr, context, create=False, remove=False):
632 def __init__(self, desc, num, lr, context, create=False, remove=False):
509 self.number = num
633 self.number = num
510 self.desc = desc
634 self.desc = desc
511 self.hunk = [desc]
635 self.hunk = [desc]
512 self.a = []
636 self.a = []
513 self.b = []
637 self.b = []
514 self.starta = self.lena = None
638 self.starta = self.lena = None
515 self.startb = self.lenb = None
639 self.startb = self.lenb = None
516 if lr is not None:
640 if lr is not None:
517 if context:
641 if context:
518 self.read_context_hunk(lr)
642 self.read_context_hunk(lr)
519 else:
643 else:
520 self.read_unified_hunk(lr)
644 self.read_unified_hunk(lr)
521 self.create = create
645 self.create = create
522 self.remove = remove and not create
646 self.remove = remove and not create
523
647
524 def getnormalized(self):
648 def getnormalized(self):
525 """Return a copy with line endings normalized to LF."""
649 """Return a copy with line endings normalized to LF."""
526
650
527 def normalize(lines):
651 def normalize(lines):
528 nlines = []
652 nlines = []
529 for line in lines:
653 for line in lines:
530 if line.endswith('\r\n'):
654 if line.endswith('\r\n'):
531 line = line[:-2] + '\n'
655 line = line[:-2] + '\n'
532 nlines.append(line)
656 nlines.append(line)
533 return nlines
657 return nlines
534
658
535 # Dummy object, it is rebuilt manually
659 # Dummy object, it is rebuilt manually
536 nh = hunk(self.desc, self.number, None, None, False, False)
660 nh = hunk(self.desc, self.number, None, None, False, False)
537 nh.number = self.number
661 nh.number = self.number
538 nh.desc = self.desc
662 nh.desc = self.desc
539 nh.a = normalize(self.a)
663 nh.a = normalize(self.a)
540 nh.b = normalize(self.b)
664 nh.b = normalize(self.b)
541 nh.starta = self.starta
665 nh.starta = self.starta
542 nh.startb = self.startb
666 nh.startb = self.startb
543 nh.lena = self.lena
667 nh.lena = self.lena
544 nh.lenb = self.lenb
668 nh.lenb = self.lenb
545 nh.create = self.create
669 nh.create = self.create
546 nh.remove = self.remove
670 nh.remove = self.remove
547 return nh
671 return nh
548
672
549 def read_unified_hunk(self, lr):
673 def read_unified_hunk(self, lr):
550 m = unidesc.match(self.desc)
674 m = unidesc.match(self.desc)
551 if not m:
675 if not m:
552 raise PatchError(_("bad hunk #%d") % self.number)
676 raise PatchError(_("bad hunk #%d") % self.number)
553 self.starta, foo, self.lena, self.startb, foo2, self.lenb = m.groups()
677 self.starta, foo, self.lena, self.startb, foo2, self.lenb = m.groups()
554 if self.lena is None:
678 if self.lena is None:
555 self.lena = 1
679 self.lena = 1
556 else:
680 else:
557 self.lena = int(self.lena)
681 self.lena = int(self.lena)
558 if self.lenb is None:
682 if self.lenb is None:
559 self.lenb = 1
683 self.lenb = 1
560 else:
684 else:
561 self.lenb = int(self.lenb)
685 self.lenb = int(self.lenb)
562 self.starta = int(self.starta)
686 self.starta = int(self.starta)
563 self.startb = int(self.startb)
687 self.startb = int(self.startb)
564 diffhelpers.addlines(lr, self.hunk, self.lena, self.lenb, self.a, self.b)
688 diffhelpers.addlines(lr, self.hunk, self.lena, self.lenb, self.a, self.b)
565 # if we hit eof before finishing out the hunk, the last line will
689 # if we hit eof before finishing out the hunk, the last line will
566 # be zero length. Lets try to fix it up.
690 # be zero length. Lets try to fix it up.
567 while len(self.hunk[-1]) == 0:
691 while len(self.hunk[-1]) == 0:
568 del self.hunk[-1]
692 del self.hunk[-1]
569 del self.a[-1]
693 del self.a[-1]
570 del self.b[-1]
694 del self.b[-1]
571 self.lena -= 1
695 self.lena -= 1
572 self.lenb -= 1
696 self.lenb -= 1
573
697
574 def read_context_hunk(self, lr):
698 def read_context_hunk(self, lr):
575 self.desc = lr.readline()
699 self.desc = lr.readline()
576 m = contextdesc.match(self.desc)
700 m = contextdesc.match(self.desc)
577 if not m:
701 if not m:
578 raise PatchError(_("bad hunk #%d") % self.number)
702 raise PatchError(_("bad hunk #%d") % self.number)
579 foo, self.starta, foo2, aend, foo3 = m.groups()
703 foo, self.starta, foo2, aend, foo3 = m.groups()
580 self.starta = int(self.starta)
704 self.starta = int(self.starta)
581 if aend is None:
705 if aend is None:
582 aend = self.starta
706 aend = self.starta
583 self.lena = int(aend) - self.starta
707 self.lena = int(aend) - self.starta
584 if self.starta:
708 if self.starta:
585 self.lena += 1
709 self.lena += 1
586 for x in xrange(self.lena):
710 for x in xrange(self.lena):
587 l = lr.readline()
711 l = lr.readline()
588 if l.startswith('---'):
712 if l.startswith('---'):
589 lr.push(l)
713 lr.push(l)
590 break
714 break
591 s = l[2:]
715 s = l[2:]
592 if l.startswith('- ') or l.startswith('! '):
716 if l.startswith('- ') or l.startswith('! '):
593 u = '-' + s
717 u = '-' + s
594 elif l.startswith(' '):
718 elif l.startswith(' '):
595 u = ' ' + s
719 u = ' ' + s
596 else:
720 else:
597 raise PatchError(_("bad hunk #%d old text line %d") %
721 raise PatchError(_("bad hunk #%d old text line %d") %
598 (self.number, x))
722 (self.number, x))
599 self.a.append(u)
723 self.a.append(u)
600 self.hunk.append(u)
724 self.hunk.append(u)
601
725
602 l = lr.readline()
726 l = lr.readline()
603 if l.startswith('\ '):
727 if l.startswith('\ '):
604 s = self.a[-1][:-1]
728 s = self.a[-1][:-1]
605 self.a[-1] = s
729 self.a[-1] = s
606 self.hunk[-1] = s
730 self.hunk[-1] = s
607 l = lr.readline()
731 l = lr.readline()
608 m = contextdesc.match(l)
732 m = contextdesc.match(l)
609 if not m:
733 if not m:
610 raise PatchError(_("bad hunk #%d") % self.number)
734 raise PatchError(_("bad hunk #%d") % self.number)
611 foo, self.startb, foo2, bend, foo3 = m.groups()
735 foo, self.startb, foo2, bend, foo3 = m.groups()
612 self.startb = int(self.startb)
736 self.startb = int(self.startb)
613 if bend is None:
737 if bend is None:
614 bend = self.startb
738 bend = self.startb
615 self.lenb = int(bend) - self.startb
739 self.lenb = int(bend) - self.startb
616 if self.startb:
740 if self.startb:
617 self.lenb += 1
741 self.lenb += 1
618 hunki = 1
742 hunki = 1
619 for x in xrange(self.lenb):
743 for x in xrange(self.lenb):
620 l = lr.readline()
744 l = lr.readline()
621 if l.startswith('\ '):
745 if l.startswith('\ '):
622 s = self.b[-1][:-1]
746 s = self.b[-1][:-1]
623 self.b[-1] = s
747 self.b[-1] = s
624 self.hunk[hunki - 1] = s
748 self.hunk[hunki - 1] = s
625 continue
749 continue
626 if not l:
750 if not l:
627 lr.push(l)
751 lr.push(l)
628 break
752 break
629 s = l[2:]
753 s = l[2:]
630 if l.startswith('+ ') or l.startswith('! '):
754 if l.startswith('+ ') or l.startswith('! '):
631 u = '+' + s
755 u = '+' + s
632 elif l.startswith(' '):
756 elif l.startswith(' '):
633 u = ' ' + s
757 u = ' ' + s
634 elif len(self.b) == 0:
758 elif len(self.b) == 0:
635 # this can happen when the hunk does not add any lines
759 # this can happen when the hunk does not add any lines
636 lr.push(l)
760 lr.push(l)
637 break
761 break
638 else:
762 else:
639 raise PatchError(_("bad hunk #%d old text line %d") %
763 raise PatchError(_("bad hunk #%d old text line %d") %
640 (self.number, x))
764 (self.number, x))
641 self.b.append(s)
765 self.b.append(s)
642 while True:
766 while True:
643 if hunki >= len(self.hunk):
767 if hunki >= len(self.hunk):
644 h = ""
768 h = ""
645 else:
769 else:
646 h = self.hunk[hunki]
770 h = self.hunk[hunki]
647 hunki += 1
771 hunki += 1
648 if h == u:
772 if h == u:
649 break
773 break
650 elif h.startswith('-'):
774 elif h.startswith('-'):
651 continue
775 continue
652 else:
776 else:
653 self.hunk.insert(hunki - 1, u)
777 self.hunk.insert(hunki - 1, u)
654 break
778 break
655
779
656 if not self.a:
780 if not self.a:
657 # this happens when lines were only added to the hunk
781 # this happens when lines were only added to the hunk
658 for x in self.hunk:
782 for x in self.hunk:
659 if x.startswith('-') or x.startswith(' '):
783 if x.startswith('-') or x.startswith(' '):
660 self.a.append(x)
784 self.a.append(x)
661 if not self.b:
785 if not self.b:
662 # this happens when lines were only deleted from the hunk
786 # this happens when lines were only deleted from the hunk
663 for x in self.hunk:
787 for x in self.hunk:
664 if x.startswith('+') or x.startswith(' '):
788 if x.startswith('+') or x.startswith(' '):
665 self.b.append(x[1:])
789 self.b.append(x[1:])
666 # @@ -start,len +start,len @@
790 # @@ -start,len +start,len @@
667 self.desc = "@@ -%d,%d +%d,%d @@\n" % (self.starta, self.lena,
791 self.desc = "@@ -%d,%d +%d,%d @@\n" % (self.starta, self.lena,
668 self.startb, self.lenb)
792 self.startb, self.lenb)
669 self.hunk[0] = self.desc
793 self.hunk[0] = self.desc
670
794
671 def fix_newline(self):
795 def fix_newline(self):
672 diffhelpers.fix_newline(self.hunk, self.a, self.b)
796 diffhelpers.fix_newline(self.hunk, self.a, self.b)
673
797
674 def complete(self):
798 def complete(self):
675 return len(self.a) == self.lena and len(self.b) == self.lenb
799 return len(self.a) == self.lena and len(self.b) == self.lenb
676
800
677 def createfile(self):
801 def createfile(self):
678 return self.starta == 0 and self.lena == 0 and self.create
802 return self.starta == 0 and self.lena == 0 and self.create
679
803
680 def rmfile(self):
804 def rmfile(self):
681 return self.startb == 0 and self.lenb == 0 and self.remove
805 return self.startb == 0 and self.lenb == 0 and self.remove
682
806
683 def fuzzit(self, l, fuzz, toponly):
807 def fuzzit(self, l, fuzz, toponly):
684 # this removes context lines from the top and bottom of list 'l'. It
808 # this removes context lines from the top and bottom of list 'l'. It
685 # checks the hunk to make sure only context lines are removed, and then
809 # checks the hunk to make sure only context lines are removed, and then
686 # returns a new shortened list of lines.
810 # returns a new shortened list of lines.
687 fuzz = min(fuzz, len(l)-1)
811 fuzz = min(fuzz, len(l)-1)
688 if fuzz:
812 if fuzz:
689 top = 0
813 top = 0
690 bot = 0
814 bot = 0
691 hlen = len(self.hunk)
815 hlen = len(self.hunk)
692 for x in xrange(hlen - 1):
816 for x in xrange(hlen - 1):
693 # the hunk starts with the @@ line, so use x+1
817 # the hunk starts with the @@ line, so use x+1
694 if self.hunk[x + 1][0] == ' ':
818 if self.hunk[x + 1][0] == ' ':
695 top += 1
819 top += 1
696 else:
820 else:
697 break
821 break
698 if not toponly:
822 if not toponly:
699 for x in xrange(hlen - 1):
823 for x in xrange(hlen - 1):
700 if self.hunk[hlen - bot - 1][0] == ' ':
824 if self.hunk[hlen - bot - 1][0] == ' ':
701 bot += 1
825 bot += 1
702 else:
826 else:
703 break
827 break
704
828
705 # top and bot now count context in the hunk
829 # top and bot now count context in the hunk
706 # adjust them if either one is short
830 # adjust them if either one is short
707 context = max(top, bot, 3)
831 context = max(top, bot, 3)
708 if bot < context:
832 if bot < context:
709 bot = max(0, fuzz - (context - bot))
833 bot = max(0, fuzz - (context - bot))
710 else:
834 else:
711 bot = min(fuzz, bot)
835 bot = min(fuzz, bot)
712 if top < context:
836 if top < context:
713 top = max(0, fuzz - (context - top))
837 top = max(0, fuzz - (context - top))
714 else:
838 else:
715 top = min(fuzz, top)
839 top = min(fuzz, top)
716
840
717 return l[top:len(l)-bot]
841 return l[top:len(l)-bot]
718 return l
842 return l
719
843
720 def old(self, fuzz=0, toponly=False):
844 def old(self, fuzz=0, toponly=False):
721 return self.fuzzit(self.a, fuzz, toponly)
845 return self.fuzzit(self.a, fuzz, toponly)
722
846
723 def new(self, fuzz=0, toponly=False):
847 def new(self, fuzz=0, toponly=False):
724 return self.fuzzit(self.b, fuzz, toponly)
848 return self.fuzzit(self.b, fuzz, toponly)
725
849
726 class binhunk:
850 class binhunk:
727 'A binary patch file. Only understands literals so far.'
851 'A binary patch file. Only understands literals so far.'
728 def __init__(self, gitpatch):
852 def __init__(self, gitpatch):
729 self.gitpatch = gitpatch
853 self.gitpatch = gitpatch
730 self.text = None
854 self.text = None
731 self.hunk = ['GIT binary patch\n']
855 self.hunk = ['GIT binary patch\n']
732
856
733 def createfile(self):
857 def createfile(self):
734 return self.gitpatch.op in ('ADD', 'RENAME', 'COPY')
858 return self.gitpatch.op in ('ADD', 'RENAME', 'COPY')
735
859
736 def rmfile(self):
860 def rmfile(self):
737 return self.gitpatch.op == 'DELETE'
861 return self.gitpatch.op == 'DELETE'
738
862
739 def complete(self):
863 def complete(self):
740 return self.text is not None
864 return self.text is not None
741
865
742 def new(self):
866 def new(self):
743 return [self.text]
867 return [self.text]
744
868
745 def extract(self, lr):
869 def extract(self, lr):
746 line = lr.readline()
870 line = lr.readline()
747 self.hunk.append(line)
871 self.hunk.append(line)
748 while line and not line.startswith('literal '):
872 while line and not line.startswith('literal '):
749 line = lr.readline()
873 line = lr.readline()
750 self.hunk.append(line)
874 self.hunk.append(line)
751 if not line:
875 if not line:
752 raise PatchError(_('could not extract binary patch'))
876 raise PatchError(_('could not extract binary patch'))
753 size = int(line[8:].rstrip())
877 size = int(line[8:].rstrip())
754 dec = []
878 dec = []
755 line = lr.readline()
879 line = lr.readline()
756 self.hunk.append(line)
880 self.hunk.append(line)
757 while len(line) > 1:
881 while len(line) > 1:
758 l = line[0]
882 l = line[0]
759 if l <= 'Z' and l >= 'A':
883 if l <= 'Z' and l >= 'A':
760 l = ord(l) - ord('A') + 1
884 l = ord(l) - ord('A') + 1
761 else:
885 else:
762 l = ord(l) - ord('a') + 27
886 l = ord(l) - ord('a') + 27
763 dec.append(base85.b85decode(line[1:-1])[:l])
887 dec.append(base85.b85decode(line[1:-1])[:l])
764 line = lr.readline()
888 line = lr.readline()
765 self.hunk.append(line)
889 self.hunk.append(line)
766 text = zlib.decompress(''.join(dec))
890 text = zlib.decompress(''.join(dec))
767 if len(text) != size:
891 if len(text) != size:
768 raise PatchError(_('binary patch is %d bytes, not %d') %
892 raise PatchError(_('binary patch is %d bytes, not %d') %
769 len(text), size)
893 len(text), size)
770 self.text = text
894 self.text = text
771
895
772 def parsefilename(str):
896 def parsefilename(str):
773 # --- filename \t|space stuff
897 # --- filename \t|space stuff
774 s = str[4:].rstrip('\r\n')
898 s = str[4:].rstrip('\r\n')
775 i = s.find('\t')
899 i = s.find('\t')
776 if i < 0:
900 if i < 0:
777 i = s.find(' ')
901 i = s.find(' ')
778 if i < 0:
902 if i < 0:
779 return s
903 return s
780 return s[:i]
904 return s[:i]
781
905
782 def selectfile(afile_orig, bfile_orig, hunk, strip):
906 def selectfile(afile_orig, bfile_orig, hunk, strip):
783 def pathstrip(path, count=1):
907 def pathstrip(path, count=1):
784 pathlen = len(path)
908 pathlen = len(path)
785 i = 0
909 i = 0
786 if count == 0:
910 if count == 0:
787 return '', path.rstrip()
911 return '', path.rstrip()
788 while count > 0:
912 while count > 0:
789 i = path.find('/', i)
913 i = path.find('/', i)
790 if i == -1:
914 if i == -1:
791 raise PatchError(_("unable to strip away %d dirs from %s") %
915 raise PatchError(_("unable to strip away %d dirs from %s") %
792 (count, path))
916 (count, path))
793 i += 1
917 i += 1
794 # consume '//' in the path
918 # consume '//' in the path
795 while i < pathlen - 1 and path[i] == '/':
919 while i < pathlen - 1 and path[i] == '/':
796 i += 1
920 i += 1
797 count -= 1
921 count -= 1
798 return path[:i].lstrip(), path[i:].rstrip()
922 return path[:i].lstrip(), path[i:].rstrip()
799
923
800 nulla = afile_orig == "/dev/null"
924 nulla = afile_orig == "/dev/null"
801 nullb = bfile_orig == "/dev/null"
925 nullb = bfile_orig == "/dev/null"
802 abase, afile = pathstrip(afile_orig, strip)
926 abase, afile = pathstrip(afile_orig, strip)
803 gooda = not nulla and util.lexists(afile)
927 gooda = not nulla and util.lexists(afile)
804 bbase, bfile = pathstrip(bfile_orig, strip)
928 bbase, bfile = pathstrip(bfile_orig, strip)
805 if afile == bfile:
929 if afile == bfile:
806 goodb = gooda
930 goodb = gooda
807 else:
931 else:
808 goodb = not nullb and os.path.exists(bfile)
932 goodb = not nullb and os.path.exists(bfile)
809 createfunc = hunk.createfile
933 createfunc = hunk.createfile
810 missing = not goodb and not gooda and not createfunc()
934 missing = not goodb and not gooda and not createfunc()
811
935
812 # some diff programs apparently produce create patches where the
936 # some diff programs apparently produce create patches where the
813 # afile is not /dev/null, but rather the same name as the bfile
937 # afile is not /dev/null, but rather the same name as the bfile
814 if missing and afile == bfile:
938 if missing and afile == bfile:
815 # this isn't very pretty
939 # this isn't very pretty
816 hunk.create = True
940 hunk.create = True
817 if createfunc():
941 if createfunc():
818 missing = False
942 missing = False
819 else:
943 else:
820 hunk.create = False
944 hunk.create = False
821
945
822 # If afile is "a/b/foo" and bfile is "a/b/foo.orig" we assume the
946 # If afile is "a/b/foo" and bfile is "a/b/foo.orig" we assume the
823 # diff is between a file and its backup. In this case, the original
947 # diff is between a file and its backup. In this case, the original
824 # file should be patched (see original mpatch code).
948 # file should be patched (see original mpatch code).
825 isbackup = (abase == bbase and bfile.startswith(afile))
949 isbackup = (abase == bbase and bfile.startswith(afile))
826 fname = None
950 fname = None
827 if not missing:
951 if not missing:
828 if gooda and goodb:
952 if gooda and goodb:
829 fname = isbackup and afile or bfile
953 fname = isbackup and afile or bfile
830 elif gooda:
954 elif gooda:
831 fname = afile
955 fname = afile
832
956
833 if not fname:
957 if not fname:
834 if not nullb:
958 if not nullb:
835 fname = isbackup and afile or bfile
959 fname = isbackup and afile or bfile
836 elif not nulla:
960 elif not nulla:
837 fname = afile
961 fname = afile
838 else:
962 else:
839 raise PatchError(_("undefined source and destination files"))
963 raise PatchError(_("undefined source and destination files"))
840
964
841 return fname, missing
965 return fname, missing
842
966
843 def scangitpatch(lr, firstline):
967 def scangitpatch(lr, firstline):
844 """
968 """
845 Git patches can emit:
969 Git patches can emit:
846 - rename a to b
970 - rename a to b
847 - change b
971 - change b
848 - copy a to c
972 - copy a to c
849 - change c
973 - change c
850
974
851 We cannot apply this sequence as-is, the renamed 'a' could not be
975 We cannot apply this sequence as-is, the renamed 'a' could not be
852 found for it would have been renamed already. And we cannot copy
976 found for it would have been renamed already. And we cannot copy
853 from 'b' instead because 'b' would have been changed already. So
977 from 'b' instead because 'b' would have been changed already. So
854 we scan the git patch for copy and rename commands so we can
978 we scan the git patch for copy and rename commands so we can
855 perform the copies ahead of time.
979 perform the copies ahead of time.
856 """
980 """
857 pos = 0
981 pos = 0
858 try:
982 try:
859 pos = lr.fp.tell()
983 pos = lr.fp.tell()
860 fp = lr.fp
984 fp = lr.fp
861 except IOError:
985 except IOError:
862 fp = cStringIO.StringIO(lr.fp.read())
986 fp = cStringIO.StringIO(lr.fp.read())
863 gitlr = linereader(fp, lr.textmode)
987 gitlr = linereader(fp, lr.textmode)
864 gitlr.push(firstline)
988 gitlr.push(firstline)
865 (dopatch, gitpatches) = readgitpatch(gitlr)
989 (dopatch, gitpatches) = readgitpatch(gitlr)
866 fp.seek(pos)
990 fp.seek(pos)
867 return dopatch, gitpatches
991 return dopatch, gitpatches
868
992
869 def iterhunks(ui, fp, sourcefile=None):
993 def iterhunks(ui, fp, sourcefile=None):
870 """Read a patch and yield the following events:
994 """Read a patch and yield the following events:
871 - ("file", afile, bfile, firsthunk): select a new target file.
995 - ("file", afile, bfile, firsthunk): select a new target file.
872 - ("hunk", hunk): a new hunk is ready to be applied, follows a
996 - ("hunk", hunk): a new hunk is ready to be applied, follows a
873 "file" event.
997 "file" event.
874 - ("git", gitchanges): current diff is in git format, gitchanges
998 - ("git", gitchanges): current diff is in git format, gitchanges
875 maps filenames to gitpatch records. Unique event.
999 maps filenames to gitpatch records. Unique event.
876 """
1000 """
877 changed = {}
1001 changed = {}
878 current_hunk = None
1002 current_hunk = None
879 afile = ""
1003 afile = ""
880 bfile = ""
1004 bfile = ""
881 state = None
1005 state = None
882 hunknum = 0
1006 hunknum = 0
883 emitfile = False
1007 emitfile = False
884 git = False
1008 git = False
885
1009
886 # our states
1010 # our states
887 BFILE = 1
1011 BFILE = 1
888 context = None
1012 context = None
889 lr = linereader(fp)
1013 lr = linereader(fp)
890 dopatch = True
1014 dopatch = True
891 # gitworkdone is True if a git operation (copy, rename, ...) was
1015 # gitworkdone is True if a git operation (copy, rename, ...) was
892 # performed already for the current file. Useful when the file
1016 # performed already for the current file. Useful when the file
893 # section may have no hunk.
1017 # section may have no hunk.
894 gitworkdone = False
1018 gitworkdone = False
895
1019
896 while True:
1020 while True:
897 newfile = False
1021 newfile = False
898 x = lr.readline()
1022 x = lr.readline()
899 if not x:
1023 if not x:
900 break
1024 break
901 if current_hunk:
1025 if current_hunk:
902 if x.startswith('\ '):
1026 if x.startswith('\ '):
903 current_hunk.fix_newline()
1027 current_hunk.fix_newline()
904 yield 'hunk', current_hunk
1028 yield 'hunk', current_hunk
905 current_hunk = None
1029 current_hunk = None
906 gitworkdone = False
1030 gitworkdone = False
907 if ((sourcefile or state == BFILE) and ((not context and x[0] == '@') or
1031 if ((sourcefile or state == BFILE) and ((not context and x[0] == '@') or
908 ((context is not False) and x.startswith('***************')))):
1032 ((context is not False) and x.startswith('***************')))):
909 try:
1033 try:
910 if context is None and x.startswith('***************'):
1034 if context is None and x.startswith('***************'):
911 context = True
1035 context = True
912 gpatch = changed.get(bfile)
1036 gpatch = changed.get(bfile)
913 create = afile == '/dev/null' or gpatch and gpatch.op == 'ADD'
1037 create = afile == '/dev/null' or gpatch and gpatch.op == 'ADD'
914 remove = bfile == '/dev/null' or gpatch and gpatch.op == 'DELETE'
1038 remove = bfile == '/dev/null' or gpatch and gpatch.op == 'DELETE'
915 current_hunk = hunk(x, hunknum + 1, lr, context, create, remove)
1039 current_hunk = hunk(x, hunknum + 1, lr, context, create, remove)
916 except PatchError, err:
1040 except PatchError, err:
917 ui.debug(err)
1041 ui.debug(err)
918 current_hunk = None
1042 current_hunk = None
919 continue
1043 continue
920 hunknum += 1
1044 hunknum += 1
921 if emitfile:
1045 if emitfile:
922 emitfile = False
1046 emitfile = False
923 yield 'file', (afile, bfile, current_hunk)
1047 yield 'file', (afile, bfile, current_hunk)
924 elif state == BFILE and x.startswith('GIT binary patch'):
1048 elif state == BFILE and x.startswith('GIT binary patch'):
925 current_hunk = binhunk(changed[bfile])
1049 current_hunk = binhunk(changed[bfile])
926 hunknum += 1
1050 hunknum += 1
927 if emitfile:
1051 if emitfile:
928 emitfile = False
1052 emitfile = False
929 yield 'file', ('a/' + afile, 'b/' + bfile, current_hunk)
1053 yield 'file', ('a/' + afile, 'b/' + bfile, current_hunk)
930 current_hunk.extract(lr)
1054 current_hunk.extract(lr)
931 elif x.startswith('diff --git'):
1055 elif x.startswith('diff --git'):
932 # check for git diff, scanning the whole patch file if needed
1056 # check for git diff, scanning the whole patch file if needed
933 m = gitre.match(x)
1057 m = gitre.match(x)
934 if m:
1058 if m:
935 afile, bfile = m.group(1, 2)
1059 afile, bfile = m.group(1, 2)
936 if not git:
1060 if not git:
937 git = True
1061 git = True
938 dopatch, gitpatches = scangitpatch(lr, x)
1062 dopatch, gitpatches = scangitpatch(lr, x)
939 yield 'git', gitpatches
1063 yield 'git', gitpatches
940 for gp in gitpatches:
1064 for gp in gitpatches:
941 changed[gp.path] = gp
1065 changed[gp.path] = gp
942 # else error?
1066 # else error?
943 # copy/rename + modify should modify target, not source
1067 # copy/rename + modify should modify target, not source
944 gp = changed.get(bfile)
1068 gp = changed.get(bfile)
945 if gp and gp.op in ('COPY', 'DELETE', 'RENAME', 'ADD'):
1069 if gp and gp.op in ('COPY', 'DELETE', 'RENAME', 'ADD'):
946 afile = bfile
1070 afile = bfile
947 gitworkdone = True
1071 gitworkdone = True
948 newfile = True
1072 newfile = True
949 elif x.startswith('---'):
1073 elif x.startswith('---'):
950 # check for a unified diff
1074 # check for a unified diff
951 l2 = lr.readline()
1075 l2 = lr.readline()
952 if not l2.startswith('+++'):
1076 if not l2.startswith('+++'):
953 lr.push(l2)
1077 lr.push(l2)
954 continue
1078 continue
955 newfile = True
1079 newfile = True
956 context = False
1080 context = False
957 afile = parsefilename(x)
1081 afile = parsefilename(x)
958 bfile = parsefilename(l2)
1082 bfile = parsefilename(l2)
959 elif x.startswith('***'):
1083 elif x.startswith('***'):
960 # check for a context diff
1084 # check for a context diff
961 l2 = lr.readline()
1085 l2 = lr.readline()
962 if not l2.startswith('---'):
1086 if not l2.startswith('---'):
963 lr.push(l2)
1087 lr.push(l2)
964 continue
1088 continue
965 l3 = lr.readline()
1089 l3 = lr.readline()
966 lr.push(l3)
1090 lr.push(l3)
967 if not l3.startswith("***************"):
1091 if not l3.startswith("***************"):
968 lr.push(l2)
1092 lr.push(l2)
969 continue
1093 continue
970 newfile = True
1094 newfile = True
971 context = True
1095 context = True
972 afile = parsefilename(x)
1096 afile = parsefilename(x)
973 bfile = parsefilename(l2)
1097 bfile = parsefilename(l2)
974
1098
975 if newfile:
1099 if newfile:
976 emitfile = True
1100 emitfile = True
977 state = BFILE
1101 state = BFILE
978 hunknum = 0
1102 hunknum = 0
979 if current_hunk:
1103 if current_hunk:
980 if current_hunk.complete():
1104 if current_hunk.complete():
981 yield 'hunk', current_hunk
1105 yield 'hunk', current_hunk
982 else:
1106 else:
983 raise PatchError(_("malformed patch %s %s") % (afile,
1107 raise PatchError(_("malformed patch %s %s") % (afile,
984 current_hunk.desc))
1108 current_hunk.desc))
985
1109
986 if hunknum == 0 and dopatch and not gitworkdone:
1110 if hunknum == 0 and dopatch and not gitworkdone:
987 raise NoHunks
1111 raise NoHunks
988
1112
989 def applydiff(ui, fp, changed, strip=1, sourcefile=None, eolmode='strict'):
1113 def applydiff(ui, fp, changed, strip=1, sourcefile=None, eolmode='strict'):
990 """
1114 """
991 Reads a patch from fp and tries to apply it.
1115 Reads a patch from fp and tries to apply it.
992
1116
993 The dict 'changed' is filled in with all of the filenames changed
1117 The dict 'changed' is filled in with all of the filenames changed
994 by the patch. Returns 0 for a clean patch, -1 if any rejects were
1118 by the patch. Returns 0 for a clean patch, -1 if any rejects were
995 found and 1 if there was any fuzz.
1119 found and 1 if there was any fuzz.
996
1120
997 If 'eolmode' is 'strict', the patch content and patched file are
1121 If 'eolmode' is 'strict', the patch content and patched file are
998 read in binary mode. Otherwise, line endings are ignored when
1122 read in binary mode. Otherwise, line endings are ignored when
999 patching then normalized according to 'eolmode'.
1123 patching then normalized according to 'eolmode'.
1000 """
1124 """
1001 rejects = 0
1125 rejects = 0
1002 err = 0
1126 err = 0
1003 current_file = None
1127 current_file = None
1004 gitpatches = None
1128 gitpatches = None
1005 opener = util.opener(os.getcwd())
1129 opener = util.opener(os.getcwd())
1006
1130
1007 def closefile():
1131 def closefile():
1008 if not current_file:
1132 if not current_file:
1009 return 0
1133 return 0
1010 current_file.close()
1134 current_file.close()
1011 return len(current_file.rej)
1135 return len(current_file.rej)
1012
1136
1013 for state, values in iterhunks(ui, fp, sourcefile):
1137 for state, values in iterhunks(ui, fp, sourcefile):
1014 if state == 'hunk':
1138 if state == 'hunk':
1015 if not current_file:
1139 if not current_file:
1016 continue
1140 continue
1017 current_hunk = values
1141 current_hunk = values
1018 ret = current_file.apply(current_hunk)
1142 ret = current_file.apply(current_hunk)
1019 if ret >= 0:
1143 if ret >= 0:
1020 changed.setdefault(current_file.fname, None)
1144 changed.setdefault(current_file.fname, None)
1021 if ret > 0:
1145 if ret > 0:
1022 err = 1
1146 err = 1
1023 elif state == 'file':
1147 elif state == 'file':
1024 rejects += closefile()
1148 rejects += closefile()
1025 afile, bfile, first_hunk = values
1149 afile, bfile, first_hunk = values
1026 try:
1150 try:
1027 if sourcefile:
1151 if sourcefile:
1028 current_file = patchfile(ui, sourcefile, opener,
1152 current_file = patchfile(ui, sourcefile, opener,
1029 eolmode=eolmode)
1153 eolmode=eolmode)
1030 else:
1154 else:
1031 current_file, missing = selectfile(afile, bfile,
1155 current_file, missing = selectfile(afile, bfile,
1032 first_hunk, strip)
1156 first_hunk, strip)
1033 current_file = patchfile(ui, current_file, opener,
1157 current_file = patchfile(ui, current_file, opener,
1034 missing, eolmode)
1158 missing, eolmode)
1035 except PatchError, err:
1159 except PatchError, err:
1036 ui.warn(str(err) + '\n')
1160 ui.warn(str(err) + '\n')
1037 current_file, current_hunk = None, None
1161 current_file, current_hunk = None, None
1038 rejects += 1
1162 rejects += 1
1039 continue
1163 continue
1040 elif state == 'git':
1164 elif state == 'git':
1041 gitpatches = values
1165 gitpatches = values
1042 cwd = os.getcwd()
1166 cwd = os.getcwd()
1043 for gp in gitpatches:
1167 for gp in gitpatches:
1044 if gp.op in ('COPY', 'RENAME'):
1168 if gp.op in ('COPY', 'RENAME'):
1045 copyfile(gp.oldpath, gp.path, cwd)
1169 copyfile(gp.oldpath, gp.path, cwd)
1046 changed[gp.path] = gp
1170 changed[gp.path] = gp
1047 else:
1171 else:
1048 raise util.Abort(_('unsupported parser state: %s') % state)
1172 raise util.Abort(_('unsupported parser state: %s') % state)
1049
1173
1050 rejects += closefile()
1174 rejects += closefile()
1051
1175
1052 if rejects:
1176 if rejects:
1053 return -1
1177 return -1
1054 return err
1178 return err
1055
1179
1056 def diffopts(ui, opts=None, untrusted=False):
1180 def diffopts(ui, opts=None, untrusted=False):
1057 def get(key, name=None, getter=ui.configbool):
1181 def get(key, name=None, getter=ui.configbool):
1058 return ((opts and opts.get(key)) or
1182 return ((opts and opts.get(key)) or
1059 getter('diff', name or key, None, untrusted=untrusted))
1183 getter('diff', name or key, None, untrusted=untrusted))
1060 return mdiff.diffopts(
1184 return mdiff.diffopts(
1061 text=opts and opts.get('text'),
1185 text=opts and opts.get('text'),
1062 git=get('git'),
1186 git=get('git'),
1063 nodates=get('nodates'),
1187 nodates=get('nodates'),
1064 showfunc=get('show_function', 'showfunc'),
1188 showfunc=get('show_function', 'showfunc'),
1065 ignorews=get('ignore_all_space', 'ignorews'),
1189 ignorews=get('ignore_all_space', 'ignorews'),
1066 ignorewsamount=get('ignore_space_change', 'ignorewsamount'),
1190 ignorewsamount=get('ignore_space_change', 'ignorewsamount'),
1067 ignoreblanklines=get('ignore_blank_lines', 'ignoreblanklines'),
1191 ignoreblanklines=get('ignore_blank_lines', 'ignoreblanklines'),
1068 context=get('unified', getter=ui.config))
1192 context=get('unified', getter=ui.config))
1069
1193
1070 def updatedir(ui, repo, patches, similarity=0):
1194 def updatedir(ui, repo, patches, similarity=0):
1071 '''Update dirstate after patch application according to metadata'''
1195 '''Update dirstate after patch application according to metadata'''
1072 if not patches:
1196 if not patches:
1073 return
1197 return
1074 copies = []
1198 copies = []
1075 removes = set()
1199 removes = set()
1076 cfiles = patches.keys()
1200 cfiles = patches.keys()
1077 cwd = repo.getcwd()
1201 cwd = repo.getcwd()
1078 if cwd:
1202 if cwd:
1079 cfiles = [util.pathto(repo.root, cwd, f) for f in patches.keys()]
1203 cfiles = [util.pathto(repo.root, cwd, f) for f in patches.keys()]
1080 for f in patches:
1204 for f in patches:
1081 gp = patches[f]
1205 gp = patches[f]
1082 if not gp:
1206 if not gp:
1083 continue
1207 continue
1084 if gp.op == 'RENAME':
1208 if gp.op == 'RENAME':
1085 copies.append((gp.oldpath, gp.path))
1209 copies.append((gp.oldpath, gp.path))
1086 removes.add(gp.oldpath)
1210 removes.add(gp.oldpath)
1087 elif gp.op == 'COPY':
1211 elif gp.op == 'COPY':
1088 copies.append((gp.oldpath, gp.path))
1212 copies.append((gp.oldpath, gp.path))
1089 elif gp.op == 'DELETE':
1213 elif gp.op == 'DELETE':
1090 removes.add(gp.path)
1214 removes.add(gp.path)
1091 for src, dst in copies:
1215 for src, dst in copies:
1092 repo.copy(src, dst)
1216 repo.copy(src, dst)
1093 if (not similarity) and removes:
1217 if (not similarity) and removes:
1094 repo.remove(sorted(removes), True)
1218 repo.remove(sorted(removes), True)
1095 for f in patches:
1219 for f in patches:
1096 gp = patches[f]
1220 gp = patches[f]
1097 if gp and gp.mode:
1221 if gp and gp.mode:
1098 islink, isexec = gp.mode
1222 islink, isexec = gp.mode
1099 dst = repo.wjoin(gp.path)
1223 dst = repo.wjoin(gp.path)
1100 # patch won't create empty files
1224 # patch won't create empty files
1101 if gp.op == 'ADD' and not os.path.exists(dst):
1225 if gp.op == 'ADD' and not os.path.exists(dst):
1102 flags = (isexec and 'x' or '') + (islink and 'l' or '')
1226 flags = (isexec and 'x' or '') + (islink and 'l' or '')
1103 repo.wwrite(gp.path, '', flags)
1227 repo.wwrite(gp.path, '', flags)
1104 elif gp.op != 'DELETE':
1228 elif gp.op != 'DELETE':
1105 util.set_flags(dst, islink, isexec)
1229 util.set_flags(dst, islink, isexec)
1106 cmdutil.addremove(repo, cfiles, similarity=similarity)
1230 cmdutil.addremove(repo, cfiles, similarity=similarity)
1107 files = patches.keys()
1231 files = patches.keys()
1108 files.extend([r for r in removes if r not in files])
1232 files.extend([r for r in removes if r not in files])
1109 return sorted(files)
1233 return sorted(files)
1110
1234
1111 def externalpatch(patcher, args, patchname, ui, strip, cwd, files):
1235 def externalpatch(patcher, args, patchname, ui, strip, cwd, files):
1112 """use <patcher> to apply <patchname> to the working directory.
1236 """use <patcher> to apply <patchname> to the working directory.
1113 returns whether patch was applied with fuzz factor."""
1237 returns whether patch was applied with fuzz factor."""
1114
1238
1115 fuzz = False
1239 fuzz = False
1116 if cwd:
1240 if cwd:
1117 args.append('-d %s' % util.shellquote(cwd))
1241 args.append('-d %s' % util.shellquote(cwd))
1118 fp = util.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
1242 fp = util.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
1119 util.shellquote(patchname)))
1243 util.shellquote(patchname)))
1120
1244
1121 for line in fp:
1245 for line in fp:
1122 line = line.rstrip()
1246 line = line.rstrip()
1123 ui.note(line + '\n')
1247 ui.note(line + '\n')
1124 if line.startswith('patching file '):
1248 if line.startswith('patching file '):
1125 pf = util.parse_patch_output(line)
1249 pf = util.parse_patch_output(line)
1126 printed_file = False
1250 printed_file = False
1127 files.setdefault(pf, None)
1251 files.setdefault(pf, None)
1128 elif line.find('with fuzz') >= 0:
1252 elif line.find('with fuzz') >= 0:
1129 fuzz = True
1253 fuzz = True
1130 if not printed_file:
1254 if not printed_file:
1131 ui.warn(pf + '\n')
1255 ui.warn(pf + '\n')
1132 printed_file = True
1256 printed_file = True
1133 ui.warn(line + '\n')
1257 ui.warn(line + '\n')
1134 elif line.find('saving rejects to file') >= 0:
1258 elif line.find('saving rejects to file') >= 0:
1135 ui.warn(line + '\n')
1259 ui.warn(line + '\n')
1136 elif line.find('FAILED') >= 0:
1260 elif line.find('FAILED') >= 0:
1137 if not printed_file:
1261 if not printed_file:
1138 ui.warn(pf + '\n')
1262 ui.warn(pf + '\n')
1139 printed_file = True
1263 printed_file = True
1140 ui.warn(line + '\n')
1264 ui.warn(line + '\n')
1141 code = fp.close()
1265 code = fp.close()
1142 if code:
1266 if code:
1143 raise PatchError(_("patch command failed: %s") %
1267 raise PatchError(_("patch command failed: %s") %
1144 util.explain_exit(code)[0])
1268 util.explain_exit(code)[0])
1145 return fuzz
1269 return fuzz
1146
1270
1147 def internalpatch(patchobj, ui, strip, cwd, files=None, eolmode='strict'):
1271 def internalpatch(patchobj, ui, strip, cwd, files=None, eolmode='strict'):
1148 """use builtin patch to apply <patchobj> to the working directory.
1272 """use builtin patch to apply <patchobj> to the working directory.
1149 returns whether patch was applied with fuzz factor."""
1273 returns whether patch was applied with fuzz factor."""
1150
1274
1151 if files is None:
1275 if files is None:
1152 files = {}
1276 files = {}
1153 if eolmode is None:
1277 if eolmode is None:
1154 eolmode = ui.config('patch', 'eol', 'strict')
1278 eolmode = ui.config('patch', 'eol', 'strict')
1155 if eolmode.lower() not in eolmodes:
1279 if eolmode.lower() not in eolmodes:
1156 raise util.Abort(_('Unsupported line endings type: %s') % eolmode)
1280 raise util.Abort(_('Unsupported line endings type: %s') % eolmode)
1157 eolmode = eolmode.lower()
1281 eolmode = eolmode.lower()
1158
1282
1159 try:
1283 try:
1160 fp = open(patchobj, 'rb')
1284 fp = open(patchobj, 'rb')
1161 except TypeError:
1285 except TypeError:
1162 fp = patchobj
1286 fp = patchobj
1163 if cwd:
1287 if cwd:
1164 curdir = os.getcwd()
1288 curdir = os.getcwd()
1165 os.chdir(cwd)
1289 os.chdir(cwd)
1166 try:
1290 try:
1167 ret = applydiff(ui, fp, files, strip=strip, eolmode=eolmode)
1291 ret = applydiff(ui, fp, files, strip=strip, eolmode=eolmode)
1168 finally:
1292 finally:
1169 if cwd:
1293 if cwd:
1170 os.chdir(curdir)
1294 os.chdir(curdir)
1171 if fp != patchobj:
1295 if fp != patchobj:
1172 fp.close()
1296 fp.close()
1173 if ret < 0:
1297 if ret < 0:
1174 raise PatchError
1298 raise PatchError
1175 return ret > 0
1299 return ret > 0
1176
1300
1177 def patch(patchname, ui, strip=1, cwd=None, files=None, eolmode='strict'):
1301 def patch(patchname, ui, strip=1, cwd=None, files=None, eolmode='strict'):
1178 """Apply <patchname> to the working directory.
1302 """Apply <patchname> to the working directory.
1179
1303
1180 'eolmode' specifies how end of lines should be handled. It can be:
1304 'eolmode' specifies how end of lines should be handled. It can be:
1181 - 'strict': inputs are read in binary mode, EOLs are preserved
1305 - 'strict': inputs are read in binary mode, EOLs are preserved
1182 - 'crlf': EOLs are ignored when patching and reset to CRLF
1306 - 'crlf': EOLs are ignored when patching and reset to CRLF
1183 - 'lf': EOLs are ignored when patching and reset to LF
1307 - 'lf': EOLs are ignored when patching and reset to LF
1184 - None: get it from user settings, default to 'strict'
1308 - None: get it from user settings, default to 'strict'
1185 'eolmode' is ignored when using an external patcher program.
1309 'eolmode' is ignored when using an external patcher program.
1186
1310
1187 Returns whether patch was applied with fuzz factor.
1311 Returns whether patch was applied with fuzz factor.
1188 """
1312 """
1189 patcher = ui.config('ui', 'patch')
1313 patcher = ui.config('ui', 'patch')
1190 args = []
1314 args = []
1191 if files is None:
1315 if files is None:
1192 files = {}
1316 files = {}
1193 try:
1317 try:
1194 if patcher:
1318 if patcher:
1195 return externalpatch(patcher, args, patchname, ui, strip, cwd,
1319 return externalpatch(patcher, args, patchname, ui, strip, cwd,
1196 files)
1320 files)
1197 else:
1321 else:
1198 try:
1322 try:
1199 return internalpatch(patchname, ui, strip, cwd, files, eolmode)
1323 return internalpatch(patchname, ui, strip, cwd, files, eolmode)
1200 except NoHunks:
1324 except NoHunks:
1201 patcher = (util.find_exe('gpatch') or util.find_exe('patch')
1325 patcher = (util.find_exe('gpatch') or util.find_exe('patch')
1202 or 'patch')
1326 or 'patch')
1203 ui.debug('no valid hunks found; trying with %r instead\n' %
1327 ui.debug('no valid hunks found; trying with %r instead\n' %
1204 patcher)
1328 patcher)
1205 if util.needbinarypatch():
1329 if util.needbinarypatch():
1206 args.append('--binary')
1330 args.append('--binary')
1207 return externalpatch(patcher, args, patchname, ui, strip, cwd,
1331 return externalpatch(patcher, args, patchname, ui, strip, cwd,
1208 files)
1332 files)
1209 except PatchError, err:
1333 except PatchError, err:
1210 s = str(err)
1334 s = str(err)
1211 if s:
1335 if s:
1212 raise util.Abort(s)
1336 raise util.Abort(s)
1213 else:
1337 else:
1214 raise util.Abort(_('patch failed to apply'))
1338 raise util.Abort(_('patch failed to apply'))
1215
1339
1216 def b85diff(to, tn):
1340 def b85diff(to, tn):
1217 '''print base85-encoded binary diff'''
1341 '''print base85-encoded binary diff'''
1218 def gitindex(text):
1342 def gitindex(text):
1219 if not text:
1343 if not text:
1220 return '0' * 40
1344 return '0' * 40
1221 l = len(text)
1345 l = len(text)
1222 s = util.sha1('blob %d\0' % l)
1346 s = util.sha1('blob %d\0' % l)
1223 s.update(text)
1347 s.update(text)
1224 return s.hexdigest()
1348 return s.hexdigest()
1225
1349
1226 def fmtline(line):
1350 def fmtline(line):
1227 l = len(line)
1351 l = len(line)
1228 if l <= 26:
1352 if l <= 26:
1229 l = chr(ord('A') + l - 1)
1353 l = chr(ord('A') + l - 1)
1230 else:
1354 else:
1231 l = chr(l - 26 + ord('a') - 1)
1355 l = chr(l - 26 + ord('a') - 1)
1232 return '%c%s\n' % (l, base85.b85encode(line, True))
1356 return '%c%s\n' % (l, base85.b85encode(line, True))
1233
1357
1234 def chunk(text, csize=52):
1358 def chunk(text, csize=52):
1235 l = len(text)
1359 l = len(text)
1236 i = 0
1360 i = 0
1237 while i < l:
1361 while i < l:
1238 yield text[i:i + csize]
1362 yield text[i:i + csize]
1239 i += csize
1363 i += csize
1240
1364
1241 tohash = gitindex(to)
1365 tohash = gitindex(to)
1242 tnhash = gitindex(tn)
1366 tnhash = gitindex(tn)
1243 if tohash == tnhash:
1367 if tohash == tnhash:
1244 return ""
1368 return ""
1245
1369
1246 # TODO: deltas
1370 # TODO: deltas
1247 ret = ['index %s..%s\nGIT binary patch\nliteral %s\n' %
1371 ret = ['index %s..%s\nGIT binary patch\nliteral %s\n' %
1248 (tohash, tnhash, len(tn))]
1372 (tohash, tnhash, len(tn))]
1249 for l in chunk(zlib.compress(tn)):
1373 for l in chunk(zlib.compress(tn)):
1250 ret.append(fmtline(l))
1374 ret.append(fmtline(l))
1251 ret.append('\n')
1375 ret.append('\n')
1252 return ''.join(ret)
1376 return ''.join(ret)
1253
1377
1254 class GitDiffRequired(Exception):
1378 class GitDiffRequired(Exception):
1255 pass
1379 pass
1256
1380
1257 def diff(repo, node1=None, node2=None, match=None, changes=None, opts=None,
1381 def diff(repo, node1=None, node2=None, match=None, changes=None, opts=None,
1258 losedatafn=None):
1382 losedatafn=None):
1259 '''yields diff of changes to files between two nodes, or node and
1383 '''yields diff of changes to files between two nodes, or node and
1260 working directory.
1384 working directory.
1261
1385
1262 if node1 is None, use first dirstate parent instead.
1386 if node1 is None, use first dirstate parent instead.
1263 if node2 is None, compare node1 with working directory.
1387 if node2 is None, compare node1 with working directory.
1264
1388
1265 losedatafn(**kwarg) is a callable run when opts.upgrade=True and
1389 losedatafn(**kwarg) is a callable run when opts.upgrade=True and
1266 every time some change cannot be represented with the current
1390 every time some change cannot be represented with the current
1267 patch format. Return False to upgrade to git patch format, True to
1391 patch format. Return False to upgrade to git patch format, True to
1268 accept the loss or raise an exception to abort the diff. It is
1392 accept the loss or raise an exception to abort the diff. It is
1269 called with the name of current file being diffed as 'fn'. If set
1393 called with the name of current file being diffed as 'fn'. If set
1270 to None, patches will always be upgraded to git format when
1394 to None, patches will always be upgraded to git format when
1271 necessary.
1395 necessary.
1272 '''
1396 '''
1273
1397
1274 if opts is None:
1398 if opts is None:
1275 opts = mdiff.defaultopts
1399 opts = mdiff.defaultopts
1276
1400
1277 if not node1 and not node2:
1401 if not node1 and not node2:
1278 node1 = repo.dirstate.parents()[0]
1402 node1 = repo.dirstate.parents()[0]
1279
1403
1280 def lrugetfilectx():
1404 def lrugetfilectx():
1281 cache = {}
1405 cache = {}
1282 order = []
1406 order = []
1283 def getfilectx(f, ctx):
1407 def getfilectx(f, ctx):
1284 fctx = ctx.filectx(f, filelog=cache.get(f))
1408 fctx = ctx.filectx(f, filelog=cache.get(f))
1285 if f not in cache:
1409 if f not in cache:
1286 if len(cache) > 20:
1410 if len(cache) > 20:
1287 del cache[order.pop(0)]
1411 del cache[order.pop(0)]
1288 cache[f] = fctx.filelog()
1412 cache[f] = fctx.filelog()
1289 else:
1413 else:
1290 order.remove(f)
1414 order.remove(f)
1291 order.append(f)
1415 order.append(f)
1292 return fctx
1416 return fctx
1293 return getfilectx
1417 return getfilectx
1294 getfilectx = lrugetfilectx()
1418 getfilectx = lrugetfilectx()
1295
1419
1296 ctx1 = repo[node1]
1420 ctx1 = repo[node1]
1297 ctx2 = repo[node2]
1421 ctx2 = repo[node2]
1298
1422
1299 if not changes:
1423 if not changes:
1300 changes = repo.status(ctx1, ctx2, match=match)
1424 changes = repo.status(ctx1, ctx2, match=match)
1301 modified, added, removed = changes[:3]
1425 modified, added, removed = changes[:3]
1302
1426
1303 if not modified and not added and not removed:
1427 if not modified and not added and not removed:
1304 return []
1428 return []
1305
1429
1306 revs = None
1430 revs = None
1307 if not repo.ui.quiet:
1431 if not repo.ui.quiet:
1308 hexfunc = repo.ui.debugflag and hex or short
1432 hexfunc = repo.ui.debugflag and hex or short
1309 revs = [hexfunc(node) for node in [node1, node2] if node]
1433 revs = [hexfunc(node) for node in [node1, node2] if node]
1310
1434
1311 copy = {}
1435 copy = {}
1312 if opts.git or opts.upgrade:
1436 if opts.git or opts.upgrade:
1313 copy = copies.copies(repo, ctx1, ctx2, repo[nullid])[0]
1437 copy = copies.copies(repo, ctx1, ctx2, repo[nullid])[0]
1314 copy = copy.copy()
1438 copy = copy.copy()
1315 for k, v in copy.items():
1439 for k, v in copy.items():
1316 copy[v] = k
1440 copy[v] = k
1317
1441
1318 difffn = lambda opts, losedata: trydiff(repo, revs, ctx1, ctx2,
1442 difffn = lambda opts, losedata: trydiff(repo, revs, ctx1, ctx2,
1319 modified, added, removed, copy, getfilectx, opts, losedata)
1443 modified, added, removed, copy, getfilectx, opts, losedata)
1320 if opts.upgrade and not opts.git:
1444 if opts.upgrade and not opts.git:
1321 try:
1445 try:
1322 def losedata(fn):
1446 def losedata(fn):
1323 if not losedatafn or not losedatafn(fn=fn):
1447 if not losedatafn or not losedatafn(fn=fn):
1324 raise GitDiffRequired()
1448 raise GitDiffRequired()
1325 # Buffer the whole output until we are sure it can be generated
1449 # Buffer the whole output until we are sure it can be generated
1326 return list(difffn(opts.copy(git=False), losedata))
1450 return list(difffn(opts.copy(git=False), losedata))
1327 except GitDiffRequired:
1451 except GitDiffRequired:
1328 return difffn(opts.copy(git=True), None)
1452 return difffn(opts.copy(git=True), None)
1329 else:
1453 else:
1330 return difffn(opts, None)
1454 return difffn(opts, None)
1331
1455
1332 def _addmodehdr(header, omode, nmode):
1456 def _addmodehdr(header, omode, nmode):
1333 if omode != nmode:
1457 if omode != nmode:
1334 header.append('old mode %s\n' % omode)
1458 header.append('old mode %s\n' % omode)
1335 header.append('new mode %s\n' % nmode)
1459 header.append('new mode %s\n' % nmode)
1336
1460
1337 def trydiff(repo, revs, ctx1, ctx2, modified, added, removed,
1461 def trydiff(repo, revs, ctx1, ctx2, modified, added, removed,
1338 copy, getfilectx, opts, losedatafn):
1462 copy, getfilectx, opts, losedatafn):
1339
1463
1340 date1 = util.datestr(ctx1.date())
1464 date1 = util.datestr(ctx1.date())
1341 man1 = ctx1.manifest()
1465 man1 = ctx1.manifest()
1342
1466
1343 gone = set()
1467 gone = set()
1344 gitmode = {'l': '120000', 'x': '100755', '': '100644'}
1468 gitmode = {'l': '120000', 'x': '100755', '': '100644'}
1345
1469
1346 if opts.git:
1470 if opts.git:
1347 revs = None
1471 revs = None
1348
1472
1349 for f in sorted(modified + added + removed):
1473 for f in sorted(modified + added + removed):
1350 to = None
1474 to = None
1351 tn = None
1475 tn = None
1352 dodiff = True
1476 dodiff = True
1353 header = []
1477 header = []
1354 if f in man1:
1478 if f in man1:
1355 to = getfilectx(f, ctx1).data()
1479 to = getfilectx(f, ctx1).data()
1356 if f not in removed:
1480 if f not in removed:
1357 tn = getfilectx(f, ctx2).data()
1481 tn = getfilectx(f, ctx2).data()
1358 a, b = f, f
1482 a, b = f, f
1359 if opts.git or losedatafn:
1483 if opts.git or losedatafn:
1360 if f in added:
1484 if f in added:
1361 mode = gitmode[ctx2.flags(f)]
1485 mode = gitmode[ctx2.flags(f)]
1362 if f in copy:
1486 if f in copy:
1363 if opts.git:
1487 if opts.git:
1364 a = copy[f]
1488 a = copy[f]
1365 omode = gitmode[man1.flags(a)]
1489 omode = gitmode[man1.flags(a)]
1366 _addmodehdr(header, omode, mode)
1490 _addmodehdr(header, omode, mode)
1367 if a in removed and a not in gone:
1491 if a in removed and a not in gone:
1368 op = 'rename'
1492 op = 'rename'
1369 gone.add(a)
1493 gone.add(a)
1370 else:
1494 else:
1371 op = 'copy'
1495 op = 'copy'
1372 header.append('%s from %s\n' % (op, a))
1496 header.append('%s from %s\n' % (op, a))
1373 header.append('%s to %s\n' % (op, f))
1497 header.append('%s to %s\n' % (op, f))
1374 to = getfilectx(a, ctx1).data()
1498 to = getfilectx(a, ctx1).data()
1375 else:
1499 else:
1376 losedatafn(f)
1500 losedatafn(f)
1377 else:
1501 else:
1378 if opts.git:
1502 if opts.git:
1379 header.append('new file mode %s\n' % mode)
1503 header.append('new file mode %s\n' % mode)
1380 elif ctx2.flags(f):
1504 elif ctx2.flags(f):
1381 losedatafn(f)
1505 losedatafn(f)
1382 if util.binary(tn):
1506 if util.binary(tn):
1383 if opts.git:
1507 if opts.git:
1384 dodiff = 'binary'
1508 dodiff = 'binary'
1385 else:
1509 else:
1386 losedatafn(f)
1510 losedatafn(f)
1387 if not opts.git and not tn:
1511 if not opts.git and not tn:
1388 # regular diffs cannot represent new empty file
1512 # regular diffs cannot represent new empty file
1389 losedatafn(f)
1513 losedatafn(f)
1390 elif f in removed:
1514 elif f in removed:
1391 if opts.git:
1515 if opts.git:
1392 # have we already reported a copy above?
1516 # have we already reported a copy above?
1393 if f in copy and copy[f] in added and copy[copy[f]] == f:
1517 if f in copy and copy[f] in added and copy[copy[f]] == f:
1394 dodiff = False
1518 dodiff = False
1395 else:
1519 else:
1396 header.append('deleted file mode %s\n' %
1520 header.append('deleted file mode %s\n' %
1397 gitmode[man1.flags(f)])
1521 gitmode[man1.flags(f)])
1398 elif not to:
1522 elif not to:
1399 # regular diffs cannot represent empty file deletion
1523 # regular diffs cannot represent empty file deletion
1400 losedatafn(f)
1524 losedatafn(f)
1401 else:
1525 else:
1402 oflag = man1.flags(f)
1526 oflag = man1.flags(f)
1403 nflag = ctx2.flags(f)
1527 nflag = ctx2.flags(f)
1404 binary = util.binary(to) or util.binary(tn)
1528 binary = util.binary(to) or util.binary(tn)
1405 if opts.git:
1529 if opts.git:
1406 _addmodehdr(header, gitmode[oflag], gitmode[nflag])
1530 _addmodehdr(header, gitmode[oflag], gitmode[nflag])
1407 if binary:
1531 if binary:
1408 dodiff = 'binary'
1532 dodiff = 'binary'
1409 elif binary or nflag != oflag:
1533 elif binary or nflag != oflag:
1410 losedatafn(f)
1534 losedatafn(f)
1411 if opts.git:
1535 if opts.git:
1412 header.insert(0, mdiff.diffline(revs, a, b, opts))
1536 header.insert(0, mdiff.diffline(revs, a, b, opts))
1413
1537
1414 if dodiff:
1538 if dodiff:
1415 if dodiff == 'binary':
1539 if dodiff == 'binary':
1416 text = b85diff(to, tn)
1540 text = b85diff(to, tn)
1417 else:
1541 else:
1418 text = mdiff.unidiff(to, date1,
1542 text = mdiff.unidiff(to, date1,
1419 # ctx2 date may be dynamic
1543 # ctx2 date may be dynamic
1420 tn, util.datestr(ctx2.date()),
1544 tn, util.datestr(ctx2.date()),
1421 a, b, revs, opts=opts)
1545 a, b, revs, opts=opts)
1422 if header and (text or len(header) > 1):
1546 if header and (text or len(header) > 1):
1423 yield ''.join(header)
1547 yield ''.join(header)
1424 if text:
1548 if text:
1425 yield text
1549 yield text
1426
1550
1427 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
1551 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
1428 opts=None):
1552 opts=None):
1429 '''export changesets as hg patches.'''
1553 '''export changesets as hg patches.'''
1430
1554
1431 total = len(revs)
1555 total = len(revs)
1432 revwidth = max([len(str(rev)) for rev in revs])
1556 revwidth = max([len(str(rev)) for rev in revs])
1433
1557
1434 def single(rev, seqno, fp):
1558 def single(rev, seqno, fp):
1435 ctx = repo[rev]
1559 ctx = repo[rev]
1436 node = ctx.node()
1560 node = ctx.node()
1437 parents = [p.node() for p in ctx.parents() if p]
1561 parents = [p.node() for p in ctx.parents() if p]
1438 branch = ctx.branch()
1562 branch = ctx.branch()
1439 if switch_parent:
1563 if switch_parent:
1440 parents.reverse()
1564 parents.reverse()
1441 prev = (parents and parents[0]) or nullid
1565 prev = (parents and parents[0]) or nullid
1442
1566
1443 if not fp:
1567 if not fp:
1444 fp = cmdutil.make_file(repo, template, node, total=total,
1568 fp = cmdutil.make_file(repo, template, node, total=total,
1445 seqno=seqno, revwidth=revwidth,
1569 seqno=seqno, revwidth=revwidth,
1446 mode='ab')
1570 mode='ab')
1447 if fp != sys.stdout and hasattr(fp, 'name'):
1571 if fp != sys.stdout and hasattr(fp, 'name'):
1448 repo.ui.note("%s\n" % fp.name)
1572 repo.ui.note("%s\n" % fp.name)
1449
1573
1450 fp.write("# HG changeset patch\n")
1574 fp.write("# HG changeset patch\n")
1451 fp.write("# User %s\n" % ctx.user())
1575 fp.write("# User %s\n" % ctx.user())
1452 fp.write("# Date %d %d\n" % ctx.date())
1576 fp.write("# Date %d %d\n" % ctx.date())
1453 if branch and (branch != 'default'):
1577 if branch and (branch != 'default'):
1454 fp.write("# Branch %s\n" % branch)
1578 fp.write("# Branch %s\n" % branch)
1455 fp.write("# Node ID %s\n" % hex(node))
1579 fp.write("# Node ID %s\n" % hex(node))
1456 fp.write("# Parent %s\n" % hex(prev))
1580 fp.write("# Parent %s\n" % hex(prev))
1457 if len(parents) > 1:
1581 if len(parents) > 1:
1458 fp.write("# Parent %s\n" % hex(parents[1]))
1582 fp.write("# Parent %s\n" % hex(parents[1]))
1459 fp.write(ctx.description().rstrip())
1583 fp.write(ctx.description().rstrip())
1460 fp.write("\n\n")
1584 fp.write("\n\n")
1461
1585
1462 for chunk in diff(repo, prev, node, opts=opts):
1586 for chunk in diff(repo, prev, node, opts=opts):
1463 fp.write(chunk)
1587 fp.write(chunk)
1464
1588
1465 for seqno, rev in enumerate(revs):
1589 for seqno, rev in enumerate(revs):
1466 single(rev, seqno + 1, fp)
1590 single(rev, seqno + 1, fp)
1467
1591
1468 def diffstatdata(lines):
1592 def diffstatdata(lines):
1469 filename, adds, removes = None, 0, 0
1593 filename, adds, removes = None, 0, 0
1470 for line in lines:
1594 for line in lines:
1471 if line.startswith('diff'):
1595 if line.startswith('diff'):
1472 if filename:
1596 if filename:
1473 isbinary = adds == 0 and removes == 0
1597 isbinary = adds == 0 and removes == 0
1474 yield (filename, adds, removes, isbinary)
1598 yield (filename, adds, removes, isbinary)
1475 # set numbers to 0 anyway when starting new file
1599 # set numbers to 0 anyway when starting new file
1476 adds, removes = 0, 0
1600 adds, removes = 0, 0
1477 if line.startswith('diff --git'):
1601 if line.startswith('diff --git'):
1478 filename = gitre.search(line).group(1)
1602 filename = gitre.search(line).group(1)
1479 else:
1603 else:
1480 # format: "diff -r ... -r ... filename"
1604 # format: "diff -r ... -r ... filename"
1481 filename = line.split(None, 5)[-1]
1605 filename = line.split(None, 5)[-1]
1482 elif line.startswith('+') and not line.startswith('+++'):
1606 elif line.startswith('+') and not line.startswith('+++'):
1483 adds += 1
1607 adds += 1
1484 elif line.startswith('-') and not line.startswith('---'):
1608 elif line.startswith('-') and not line.startswith('---'):
1485 removes += 1
1609 removes += 1
1486 if filename:
1610 if filename:
1487 isbinary = adds == 0 and removes == 0
1611 isbinary = adds == 0 and removes == 0
1488 yield (filename, adds, removes, isbinary)
1612 yield (filename, adds, removes, isbinary)
1489
1613
1490 def diffstat(lines, width=80, git=False):
1614 def diffstat(lines, width=80, git=False):
1491 output = []
1615 output = []
1492 stats = list(diffstatdata(lines))
1616 stats = list(diffstatdata(lines))
1493
1617
1494 maxtotal, maxname = 0, 0
1618 maxtotal, maxname = 0, 0
1495 totaladds, totalremoves = 0, 0
1619 totaladds, totalremoves = 0, 0
1496 hasbinary = False
1620 hasbinary = False
1497 for filename, adds, removes, isbinary in stats:
1621 for filename, adds, removes, isbinary in stats:
1498 totaladds += adds
1622 totaladds += adds
1499 totalremoves += removes
1623 totalremoves += removes
1500 maxname = max(maxname, len(filename))
1624 maxname = max(maxname, len(filename))
1501 maxtotal = max(maxtotal, adds + removes)
1625 maxtotal = max(maxtotal, adds + removes)
1502 if isbinary:
1626 if isbinary:
1503 hasbinary = True
1627 hasbinary = True
1504
1628
1505 countwidth = len(str(maxtotal))
1629 countwidth = len(str(maxtotal))
1506 if hasbinary and countwidth < 3:
1630 if hasbinary and countwidth < 3:
1507 countwidth = 3
1631 countwidth = 3
1508 graphwidth = width - countwidth - maxname - 6
1632 graphwidth = width - countwidth - maxname - 6
1509 if graphwidth < 10:
1633 if graphwidth < 10:
1510 graphwidth = 10
1634 graphwidth = 10
1511
1635
1512 def scale(i):
1636 def scale(i):
1513 if maxtotal <= graphwidth:
1637 if maxtotal <= graphwidth:
1514 return i
1638 return i
1515 # If diffstat runs out of room it doesn't print anything,
1639 # If diffstat runs out of room it doesn't print anything,
1516 # which isn't very useful, so always print at least one + or -
1640 # which isn't very useful, so always print at least one + or -
1517 # if there were at least some changes.
1641 # if there were at least some changes.
1518 return max(i * graphwidth // maxtotal, int(bool(i)))
1642 return max(i * graphwidth // maxtotal, int(bool(i)))
1519
1643
1520 for filename, adds, removes, isbinary in stats:
1644 for filename, adds, removes, isbinary in stats:
1521 if git and isbinary:
1645 if git and isbinary:
1522 count = 'Bin'
1646 count = 'Bin'
1523 else:
1647 else:
1524 count = adds + removes
1648 count = adds + removes
1525 pluses = '+' * scale(adds)
1649 pluses = '+' * scale(adds)
1526 minuses = '-' * scale(removes)
1650 minuses = '-' * scale(removes)
1527 output.append(' %-*s | %*s %s%s\n' % (maxname, filename, countwidth,
1651 output.append(' %-*s | %*s %s%s\n' % (maxname, filename, countwidth,
1528 count, pluses, minuses))
1652 count, pluses, minuses))
1529
1653
1530 if stats:
1654 if stats:
1531 output.append(_(' %d files changed, %d insertions(+), %d deletions(-)\n')
1655 output.append(_(' %d files changed, %d insertions(+), %d deletions(-)\n')
1532 % (len(stats), totaladds, totalremoves))
1656 % (len(stats), totaladds, totalremoves))
1533
1657
1534 return ''.join(output)
1658 return ''.join(output)
@@ -1,367 +1,374 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 hg init a
3 hg init a
4 mkdir a/d1
4 mkdir a/d1
5 mkdir a/d1/d2
5 mkdir a/d1/d2
6 echo line 1 > a/a
6 echo line 1 > a/a
7 echo line 1 > a/d1/d2/a
7 echo line 1 > a/d1/d2/a
8 hg --cwd a ci -Ama
8 hg --cwd a ci -Ama
9
9
10 echo line 2 >> a/a
10 echo line 2 >> a/a
11 hg --cwd a ci -u someone -d '1 0' -m'second change'
11 hg --cwd a ci -u someone -d '1 0' -m'second change'
12
12
13 echo % import exported patch
13 echo % import exported patch
14 hg clone -r0 a b
14 hg clone -r0 a b
15 hg --cwd a export tip > tip.patch
15 hg --cwd a export tip > tip.patch
16 hg --cwd b import ../tip.patch
16 hg --cwd b import ../tip.patch
17 echo % message should be same
17 echo % message should be same
18 hg --cwd b tip | grep 'second change'
18 hg --cwd b tip | grep 'second change'
19 echo % committer should be same
19 echo % committer should be same
20 hg --cwd b tip | grep someone
20 hg --cwd b tip | grep someone
21 rm -r b
21 rm -r b
22
22
23 echo % import exported patch with external patcher
23 echo % import exported patch with external patcher
24 cat > dummypatch.py <<EOF
24 cat > dummypatch.py <<EOF
25 print 'patching file a'
25 print 'patching file a'
26 file('a', 'wb').write('line2\n')
26 file('a', 'wb').write('line2\n')
27 EOF
27 EOF
28 chmod +x dummypatch.py
28 chmod +x dummypatch.py
29 hg clone -r0 a b
29 hg clone -r0 a b
30 hg --cwd a export tip > tip.patch
30 hg --cwd a export tip > tip.patch
31 hg --config ui.patch='python ../dummypatch.py' --cwd b import ../tip.patch
31 hg --config ui.patch='python ../dummypatch.py' --cwd b import ../tip.patch
32 cat b/a
32 cat b/a
33 rm -r b
33 rm -r b
34
34
35 echo % import of plain diff should fail without message
35 echo % import of plain diff should fail without message
36 hg clone -r0 a b
36 hg clone -r0 a b
37 hg --cwd a diff -r0:1 > tip.patch
37 hg --cwd a diff -r0:1 > tip.patch
38 hg --cwd b import ../tip.patch
38 hg --cwd b import ../tip.patch
39 rm -r b
39 rm -r b
40
40
41 echo % import of plain diff should be ok with message
41 echo % import of plain diff should be ok with message
42 hg clone -r0 a b
42 hg clone -r0 a b
43 hg --cwd a diff -r0:1 > tip.patch
43 hg --cwd a diff -r0:1 > tip.patch
44 hg --cwd b import -mpatch ../tip.patch
44 hg --cwd b import -mpatch ../tip.patch
45 rm -r b
45 rm -r b
46
46
47 echo % import of plain diff with specific date and user
47 echo % import of plain diff with specific date and user
48 hg clone -r0 a b
48 hg clone -r0 a b
49 hg --cwd a diff -r0:1 > tip.patch
49 hg --cwd a diff -r0:1 > tip.patch
50 hg --cwd b import -mpatch -d '1 0' -u 'user@nowhere.net' ../tip.patch
50 hg --cwd b import -mpatch -d '1 0' -u 'user@nowhere.net' ../tip.patch
51 hg -R b tip -pv
51 hg -R b tip -pv
52 rm -r b
52 rm -r b
53
53
54 echo % import of plain diff should be ok with --no-commit
54 echo % import of plain diff should be ok with --no-commit
55 hg clone -r0 a b
55 hg clone -r0 a b
56 hg --cwd a diff -r0:1 > tip.patch
56 hg --cwd a diff -r0:1 > tip.patch
57 hg --cwd b import --no-commit ../tip.patch
57 hg --cwd b import --no-commit ../tip.patch
58 hg --cwd b diff --nodates
58 hg --cwd b diff --nodates
59 rm -r b
59 rm -r b
60
60
61 echo % hg -R repo import
61 echo % hg -R repo import
62 # put the clone in a subdir - having a directory named "a"
62 # put the clone in a subdir - having a directory named "a"
63 # used to hide a bug.
63 # used to hide a bug.
64 mkdir dir
64 mkdir dir
65 hg clone -r0 a dir/b
65 hg clone -r0 a dir/b
66 hg --cwd a export tip > dir/tip.patch
66 hg --cwd a export tip > dir/tip.patch
67 cd dir
67 cd dir
68 hg -R b import tip.patch
68 hg -R b import tip.patch
69 cd ..
69 cd ..
70 rm -r dir
70 rm -r dir
71
71
72 echo % import from stdin
72 echo % import from stdin
73 hg clone -r0 a b
73 hg clone -r0 a b
74 hg --cwd a export tip | hg --cwd b import -
74 hg --cwd a export tip | hg --cwd b import -
75 rm -r b
75 rm -r b
76
76
77 echo % import two patches in one stream
78 hg init b
79 hg --cwd a export 0:tip | hg --cwd b import -
80 hg --cwd a id
81 hg --cwd b id
82 rm -r b
83
77 echo % override commit message
84 echo % override commit message
78 hg clone -r0 a b
85 hg clone -r0 a b
79 hg --cwd a export tip | hg --cwd b import -m 'override' -
86 hg --cwd a export tip | hg --cwd b import -m 'override' -
80 hg --cwd b tip | grep override
87 hg --cwd b tip | grep override
81 rm -r b
88 rm -r b
82
89
83 cat > mkmsg.py <<EOF
90 cat > mkmsg.py <<EOF
84 import email.Message, sys
91 import email.Message, sys
85 msg = email.Message.Message()
92 msg = email.Message.Message()
86 msg.set_payload('email commit message\n' + open('tip.patch', 'rb').read())
93 msg.set_payload('email commit message\n' + open('tip.patch', 'rb').read())
87 msg['Subject'] = 'email patch'
94 msg['Subject'] = 'email patch'
88 msg['From'] = 'email patcher'
95 msg['From'] = 'email patcher'
89 sys.stdout.write(msg.as_string())
96 sys.stdout.write(msg.as_string())
90 EOF
97 EOF
91
98
92 echo % plain diff in email, subject, message body
99 echo % plain diff in email, subject, message body
93 hg clone -r0 a b
100 hg clone -r0 a b
94 hg --cwd a diff -r0:1 > tip.patch
101 hg --cwd a diff -r0:1 > tip.patch
95 python mkmsg.py > msg.patch
102 python mkmsg.py > msg.patch
96 hg --cwd b import ../msg.patch
103 hg --cwd b import ../msg.patch
97 hg --cwd b tip | grep email
104 hg --cwd b tip | grep email
98 rm -r b
105 rm -r b
99
106
100 echo % plain diff in email, no subject, message body
107 echo % plain diff in email, no subject, message body
101 hg clone -r0 a b
108 hg clone -r0 a b
102 grep -v '^Subject:' msg.patch | hg --cwd b import -
109 grep -v '^Subject:' msg.patch | hg --cwd b import -
103 rm -r b
110 rm -r b
104
111
105 echo % plain diff in email, subject, no message body
112 echo % plain diff in email, subject, no message body
106 hg clone -r0 a b
113 hg clone -r0 a b
107 grep -v '^email ' msg.patch | hg --cwd b import -
114 grep -v '^email ' msg.patch | hg --cwd b import -
108 rm -r b
115 rm -r b
109
116
110 echo % plain diff in email, no subject, no message body, should fail
117 echo % plain diff in email, no subject, no message body, should fail
111 hg clone -r0 a b
118 hg clone -r0 a b
112 egrep -v '^(Subject|email)' msg.patch | hg --cwd b import -
119 egrep -v '^(Subject|email)' msg.patch | hg --cwd b import -
113 rm -r b
120 rm -r b
114
121
115 echo % hg export in email, should use patch header
122 echo % hg export in email, should use patch header
116 hg clone -r0 a b
123 hg clone -r0 a b
117 hg --cwd a export tip > tip.patch
124 hg --cwd a export tip > tip.patch
118 python mkmsg.py | hg --cwd b import -
125 python mkmsg.py | hg --cwd b import -
119 hg --cwd b tip | grep second
126 hg --cwd b tip | grep second
120 rm -r b
127 rm -r b
121
128
122 # subject: duplicate detection, removal of [PATCH]
129 # subject: duplicate detection, removal of [PATCH]
123 # The '---' tests the gitsendmail handling without proper mail headers
130 # The '---' tests the gitsendmail handling without proper mail headers
124 cat > mkmsg2.py <<EOF
131 cat > mkmsg2.py <<EOF
125 import email.Message, sys
132 import email.Message, sys
126 msg = email.Message.Message()
133 msg = email.Message.Message()
127 msg.set_payload('email patch\n\nnext line\n---\n' + open('tip.patch').read())
134 msg.set_payload('email patch\n\nnext line\n---\n' + open('tip.patch').read())
128 msg['Subject'] = '[PATCH] email patch'
135 msg['Subject'] = '[PATCH] email patch'
129 msg['From'] = 'email patcher'
136 msg['From'] = 'email patcher'
130 sys.stdout.write(msg.as_string())
137 sys.stdout.write(msg.as_string())
131 EOF
138 EOF
132
139
133 echo '% plain diff in email, [PATCH] subject, message body with subject'
140 echo '% plain diff in email, [PATCH] subject, message body with subject'
134 hg clone -r0 a b
141 hg clone -r0 a b
135 hg --cwd a diff -r0:1 > tip.patch
142 hg --cwd a diff -r0:1 > tip.patch
136 python mkmsg2.py | hg --cwd b import -
143 python mkmsg2.py | hg --cwd b import -
137 hg --cwd b tip --template '{desc}\n'
144 hg --cwd b tip --template '{desc}\n'
138 rm -r b
145 rm -r b
139
146
140 # We weren't backing up the correct dirstate file when importing many patches
147 # We weren't backing up the correct dirstate file when importing many patches
141 # (issue963)
148 # (issue963)
142 echo '% import patch1 patch2; rollback'
149 echo '% import patch1 patch2; rollback'
143 echo line 3 >> a/a
150 echo line 3 >> a/a
144 hg --cwd a ci -m'third change'
151 hg --cwd a ci -m'third change'
145 hg --cwd a export -o '../patch%R' 1 2
152 hg --cwd a export -o '../patch%R' 1 2
146 hg clone -qr0 a b
153 hg clone -qr0 a b
147 hg --cwd b parents --template 'parent: {rev}\n'
154 hg --cwd b parents --template 'parent: {rev}\n'
148 hg --cwd b import ../patch1 ../patch2
155 hg --cwd b import ../patch1 ../patch2
149 hg --cwd b rollback
156 hg --cwd b rollback
150 hg --cwd b parents --template 'parent: {rev}\n'
157 hg --cwd b parents --template 'parent: {rev}\n'
151 rm -r b
158 rm -r b
152
159
153 # bug non regression test
160 # bug non regression test
154 # importing a patch in a subdirectory failed at the commit stage
161 # importing a patch in a subdirectory failed at the commit stage
155 echo line 2 >> a/d1/d2/a
162 echo line 2 >> a/d1/d2/a
156 hg --cwd a ci -u someoneelse -d '1 0' -m'subdir change'
163 hg --cwd a ci -u someoneelse -d '1 0' -m'subdir change'
157 echo % hg import in a subdirectory
164 echo % hg import in a subdirectory
158 hg clone -r0 a b
165 hg clone -r0 a b
159 hg --cwd a export tip | sed -e 's/d1\/d2\///' > tip.patch
166 hg --cwd a export tip | sed -e 's/d1\/d2\///' > tip.patch
160 dir=`pwd`
167 dir=`pwd`
161 cd b/d1/d2 2>&1 > /dev/null
168 cd b/d1/d2 2>&1 > /dev/null
162 hg import ../../../tip.patch
169 hg import ../../../tip.patch
163 cd $dir
170 cd $dir
164 echo "% message should be 'subdir change'"
171 echo "% message should be 'subdir change'"
165 hg --cwd b tip | grep 'subdir change'
172 hg --cwd b tip | grep 'subdir change'
166 echo "% committer should be 'someoneelse'"
173 echo "% committer should be 'someoneelse'"
167 hg --cwd b tip | grep someoneelse
174 hg --cwd b tip | grep someoneelse
168 echo "% should be empty"
175 echo "% should be empty"
169 hg --cwd b status
176 hg --cwd b status
170
177
171
178
172 # Test fuzziness (ambiguous patch location, fuzz=2)
179 # Test fuzziness (ambiguous patch location, fuzz=2)
173 echo % test fuzziness
180 echo % test fuzziness
174 hg init fuzzy
181 hg init fuzzy
175 cd fuzzy
182 cd fuzzy
176 echo line1 > a
183 echo line1 > a
177 echo line0 >> a
184 echo line0 >> a
178 echo line3 >> a
185 echo line3 >> a
179 hg ci -Am adda
186 hg ci -Am adda
180 echo line1 > a
187 echo line1 > a
181 echo line2 >> a
188 echo line2 >> a
182 echo line0 >> a
189 echo line0 >> a
183 echo line3 >> a
190 echo line3 >> a
184 hg ci -m change a
191 hg ci -m change a
185 hg export tip > tip.patch
192 hg export tip > tip.patch
186 hg up -C 0
193 hg up -C 0
187 echo line1 > a
194 echo line1 > a
188 echo line0 >> a
195 echo line0 >> a
189 echo line1 >> a
196 echo line1 >> a
190 echo line0 >> a
197 echo line0 >> a
191 hg ci -m brancha
198 hg ci -m brancha
192 hg import -v tip.patch
199 hg import -v tip.patch
193 cd ..
200 cd ..
194
201
195 # Test hunk touching empty files (issue906)
202 # Test hunk touching empty files (issue906)
196 hg init empty
203 hg init empty
197 cd empty
204 cd empty
198 touch a
205 touch a
199 touch b1
206 touch b1
200 touch c1
207 touch c1
201 echo d > d
208 echo d > d
202 hg ci -Am init
209 hg ci -Am init
203 echo a > a
210 echo a > a
204 echo b > b1
211 echo b > b1
205 hg mv b1 b2
212 hg mv b1 b2
206 echo c > c1
213 echo c > c1
207 hg copy c1 c2
214 hg copy c1 c2
208 rm d
215 rm d
209 touch d
216 touch d
210 hg diff --git
217 hg diff --git
211 hg ci -m empty
218 hg ci -m empty
212 hg export --git tip > empty.diff
219 hg export --git tip > empty.diff
213 hg up -C 0
220 hg up -C 0
214 hg import empty.diff
221 hg import empty.diff
215 for name in a b1 b2 c1 c2 d;
222 for name in a b1 b2 c1 c2 d;
216 do
223 do
217 echo % $name file
224 echo % $name file
218 test -f $name && cat $name
225 test -f $name && cat $name
219 done
226 done
220 cd ..
227 cd ..
221
228
222 # Test importing a patch ending with a binary file removal
229 # Test importing a patch ending with a binary file removal
223 echo % test trailing binary removal
230 echo % test trailing binary removal
224 hg init binaryremoval
231 hg init binaryremoval
225 cd binaryremoval
232 cd binaryremoval
226 echo a > a
233 echo a > a
227 python -c "file('b', 'wb').write('a\x00b')"
234 python -c "file('b', 'wb').write('a\x00b')"
228 hg ci -Am addall
235 hg ci -Am addall
229 hg rm a
236 hg rm a
230 hg rm b
237 hg rm b
231 hg st
238 hg st
232 hg ci -m remove
239 hg ci -m remove
233 hg export --git . > remove.diff
240 hg export --git . > remove.diff
234 cat remove.diff | grep git
241 cat remove.diff | grep git
235 hg up -C 0
242 hg up -C 0
236 hg import remove.diff
243 hg import remove.diff
237 hg manifest
244 hg manifest
238 cd ..
245 cd ..
239
246
240 echo % 'test update+rename with common name (issue 927)'
247 echo % 'test update+rename with common name (issue 927)'
241 hg init t
248 hg init t
242 cd t
249 cd t
243 touch a
250 touch a
244 hg ci -Am t
251 hg ci -Am t
245 echo a > a
252 echo a > a
246 # Here, bfile.startswith(afile)
253 # Here, bfile.startswith(afile)
247 hg copy a a2
254 hg copy a a2
248 hg ci -m copya
255 hg ci -m copya
249 hg export --git tip > copy.diff
256 hg export --git tip > copy.diff
250 hg up -C 0
257 hg up -C 0
251 hg import copy.diff
258 hg import copy.diff
252 echo % view a
259 echo % view a
253 # a should contain an 'a'
260 # a should contain an 'a'
254 cat a
261 cat a
255 echo % view a2
262 echo % view a2
256 # and a2 should have duplicated it
263 # and a2 should have duplicated it
257 cat a2
264 cat a2
258 cd ..
265 cd ..
259
266
260 echo % 'test -p0'
267 echo % 'test -p0'
261 hg init p0
268 hg init p0
262 cd p0
269 cd p0
263 echo a > a
270 echo a > a
264 hg ci -Am t
271 hg ci -Am t
265 hg import -p0 - << EOF
272 hg import -p0 - << EOF
266 foobar
273 foobar
267 --- a Sat Apr 12 22:43:58 2008 -0400
274 --- a Sat Apr 12 22:43:58 2008 -0400
268 +++ a Sat Apr 12 22:44:05 2008 -0400
275 +++ a Sat Apr 12 22:44:05 2008 -0400
269 @@ -1,1 +1,1 @@
276 @@ -1,1 +1,1 @@
270 -a
277 -a
271 +bb
278 +bb
272 EOF
279 EOF
273 hg status
280 hg status
274 cat a
281 cat a
275 cd ..
282 cd ..
276
283
277 echo % 'test paths outside repo root'
284 echo % 'test paths outside repo root'
278 mkdir outside
285 mkdir outside
279 touch outside/foo
286 touch outside/foo
280 hg init inside
287 hg init inside
281 cd inside
288 cd inside
282 hg import - <<EOF
289 hg import - <<EOF
283 diff --git a/a b/b
290 diff --git a/a b/b
284 rename from ../outside/foo
291 rename from ../outside/foo
285 rename to bar
292 rename to bar
286 EOF
293 EOF
287 cd ..
294 cd ..
288
295
289 echo '% test import with similarity (issue295)'
296 echo '% test import with similarity (issue295)'
290 hg init sim
297 hg init sim
291 cd sim
298 cd sim
292 echo 'this is a test' > a
299 echo 'this is a test' > a
293 hg ci -Ama
300 hg ci -Ama
294 cat > ../rename.diff <<EOF
301 cat > ../rename.diff <<EOF
295 diff --git a/a b/a
302 diff --git a/a b/a
296 deleted file mode 100644
303 deleted file mode 100644
297 --- a/a
304 --- a/a
298 +++ /dev/null
305 +++ /dev/null
299 @@ -1,1 +0,0 @@
306 @@ -1,1 +0,0 @@
300 -this is a test
307 -this is a test
301 diff --git a/b b/b
308 diff --git a/b b/b
302 new file mode 100644
309 new file mode 100644
303 --- /dev/null
310 --- /dev/null
304 +++ b/b
311 +++ b/b
305 @@ -0,0 +1,2 @@
312 @@ -0,0 +1,2 @@
306 +this is a test
313 +this is a test
307 +foo
314 +foo
308 EOF
315 EOF
309 hg import --no-commit -v -s 1 ../rename.diff
316 hg import --no-commit -v -s 1 ../rename.diff
310 hg st -C
317 hg st -C
311 hg revert -a
318 hg revert -a
312 rm b
319 rm b
313 hg import --no-commit -v -s 100 ../rename.diff
320 hg import --no-commit -v -s 100 ../rename.diff
314 hg st -C
321 hg st -C
315 cd ..
322 cd ..
316
323
317
324
318 echo '% add empty file from the end of patch (issue 1495)'
325 echo '% add empty file from the end of patch (issue 1495)'
319 hg init addemptyend
326 hg init addemptyend
320 cd addemptyend
327 cd addemptyend
321 touch a
328 touch a
322 hg addremove
329 hg addremove
323 hg ci -m "commit"
330 hg ci -m "commit"
324 cat > a.patch <<EOF
331 cat > a.patch <<EOF
325 diff --git a/a b/a
332 diff --git a/a b/a
326 --- a/a
333 --- a/a
327 +++ b/a
334 +++ b/a
328 @@ -0,0 +1,1 @@
335 @@ -0,0 +1,1 @@
329 +a
336 +a
330 diff --git a/b b/b
337 diff --git a/b b/b
331 new file mode 100644
338 new file mode 100644
332 EOF
339 EOF
333 hg import --no-commit a.patch
340 hg import --no-commit a.patch
334 cd ..
341 cd ..
335
342
336 echo '% create file when source is not /dev/null'
343 echo '% create file when source is not /dev/null'
337 cat > create.patch <<EOF
344 cat > create.patch <<EOF
338 diff -Naur proj-orig/foo proj-new/foo
345 diff -Naur proj-orig/foo proj-new/foo
339 --- proj-orig/foo 1969-12-31 16:00:00.000000000 -0800
346 --- proj-orig/foo 1969-12-31 16:00:00.000000000 -0800
340 +++ proj-new/foo 2009-07-17 16:50:45.801368000 -0700
347 +++ proj-new/foo 2009-07-17 16:50:45.801368000 -0700
341 @@ -0,0 +1,1 @@
348 @@ -0,0 +1,1 @@
342 +a
349 +a
343 EOF
350 EOF
344 hg init oddcreate
351 hg init oddcreate
345 cd oddcreate
352 cd oddcreate
346 hg import --no-commit ../create.patch
353 hg import --no-commit ../create.patch
347 cat foo
354 cat foo
348
355
349 echo % 'first line mistaken for email headers (issue 1859)'
356 echo % 'first line mistaken for email headers (issue 1859)'
350 hg init emailconfusion
357 hg init emailconfusion
351 cd emailconfusion
358 cd emailconfusion
352 cat > a.patch <<EOF
359 cat > a.patch <<EOF
353 module: summary
360 module: summary
354
361
355 description
362 description
356
363
357
364
358 diff -r 000000000000 -r 9b4c1e343b55 test.txt
365 diff -r 000000000000 -r 9b4c1e343b55 test.txt
359 --- /dev/null
366 --- /dev/null
360 +++ b/a
367 +++ b/a
361 @@ -0,0 +1,1 @@
368 @@ -0,0 +1,1 @@
362 +a
369 +a
363 EOF
370 EOF
364 hg import -d '0 0' a.patch
371 hg import -d '0 0' a.patch
365 hg parents -v
372 hg parents -v
366 cd ..
373 cd ..
367
374
@@ -1,310 +1,316 b''
1 adding a
1 adding a
2 adding d1/d2/a
2 adding d1/d2/a
3 % import exported patch
3 % import exported patch
4 requesting all changes
4 requesting all changes
5 adding changesets
5 adding changesets
6 adding manifests
6 adding manifests
7 adding file changes
7 adding file changes
8 added 1 changesets with 2 changes to 2 files
8 added 1 changesets with 2 changes to 2 files
9 updating to branch default
9 updating to branch default
10 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
10 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
11 applying ../tip.patch
11 applying ../tip.patch
12 % message should be same
12 % message should be same
13 summary: second change
13 summary: second change
14 % committer should be same
14 % committer should be same
15 user: someone
15 user: someone
16 % import exported patch with external patcher
16 % import exported patch with external patcher
17 requesting all changes
17 requesting all changes
18 adding changesets
18 adding changesets
19 adding manifests
19 adding manifests
20 adding file changes
20 adding file changes
21 added 1 changesets with 2 changes to 2 files
21 added 1 changesets with 2 changes to 2 files
22 updating to branch default
22 updating to branch default
23 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
23 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
24 applying ../tip.patch
24 applying ../tip.patch
25 line2
25 line2
26 % import of plain diff should fail without message
26 % import of plain diff should fail without message
27 requesting all changes
27 requesting all changes
28 adding changesets
28 adding changesets
29 adding manifests
29 adding manifests
30 adding file changes
30 adding file changes
31 added 1 changesets with 2 changes to 2 files
31 added 1 changesets with 2 changes to 2 files
32 updating to branch default
32 updating to branch default
33 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
33 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
34 applying ../tip.patch
34 applying ../tip.patch
35 abort: empty commit message
35 abort: empty commit message
36 % import of plain diff should be ok with message
36 % import of plain diff should be ok with message
37 requesting all changes
37 requesting all changes
38 adding changesets
38 adding changesets
39 adding manifests
39 adding manifests
40 adding file changes
40 adding file changes
41 added 1 changesets with 2 changes to 2 files
41 added 1 changesets with 2 changes to 2 files
42 updating to branch default
42 updating to branch default
43 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
43 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
44 applying ../tip.patch
44 applying ../tip.patch
45 % import of plain diff with specific date and user
45 % import of plain diff with specific date and user
46 requesting all changes
46 requesting all changes
47 adding changesets
47 adding changesets
48 adding manifests
48 adding manifests
49 adding file changes
49 adding file changes
50 added 1 changesets with 2 changes to 2 files
50 added 1 changesets with 2 changes to 2 files
51 updating to branch default
51 updating to branch default
52 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
52 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
53 applying ../tip.patch
53 applying ../tip.patch
54 changeset: 1:ca68f19f3a40
54 changeset: 1:ca68f19f3a40
55 tag: tip
55 tag: tip
56 user: user@nowhere.net
56 user: user@nowhere.net
57 date: Thu Jan 01 00:00:01 1970 +0000
57 date: Thu Jan 01 00:00:01 1970 +0000
58 files: a
58 files: a
59 description:
59 description:
60 patch
60 patch
61
61
62
62
63 diff -r 80971e65b431 -r ca68f19f3a40 a
63 diff -r 80971e65b431 -r ca68f19f3a40 a
64 --- a/a Thu Jan 01 00:00:00 1970 +0000
64 --- a/a Thu Jan 01 00:00:00 1970 +0000
65 +++ b/a Thu Jan 01 00:00:01 1970 +0000
65 +++ b/a Thu Jan 01 00:00:01 1970 +0000
66 @@ -1,1 +1,2 @@
66 @@ -1,1 +1,2 @@
67 line 1
67 line 1
68 +line 2
68 +line 2
69
69
70 % import of plain diff should be ok with --no-commit
70 % import of plain diff should be ok with --no-commit
71 requesting all changes
71 requesting all changes
72 adding changesets
72 adding changesets
73 adding manifests
73 adding manifests
74 adding file changes
74 adding file changes
75 added 1 changesets with 2 changes to 2 files
75 added 1 changesets with 2 changes to 2 files
76 updating to branch default
76 updating to branch default
77 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
77 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
78 applying ../tip.patch
78 applying ../tip.patch
79 diff -r 80971e65b431 a
79 diff -r 80971e65b431 a
80 --- a/a
80 --- a/a
81 +++ b/a
81 +++ b/a
82 @@ -1,1 +1,2 @@
82 @@ -1,1 +1,2 @@
83 line 1
83 line 1
84 +line 2
84 +line 2
85 % hg -R repo import
85 % hg -R repo import
86 requesting all changes
86 requesting all changes
87 adding changesets
87 adding changesets
88 adding manifests
88 adding manifests
89 adding file changes
89 adding file changes
90 added 1 changesets with 2 changes to 2 files
90 added 1 changesets with 2 changes to 2 files
91 updating to branch default
91 updating to branch default
92 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
92 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
93 applying tip.patch
93 applying tip.patch
94 % import from stdin
94 % import from stdin
95 requesting all changes
95 requesting all changes
96 adding changesets
96 adding changesets
97 adding manifests
97 adding manifests
98 adding file changes
98 adding file changes
99 added 1 changesets with 2 changes to 2 files
99 added 1 changesets with 2 changes to 2 files
100 updating to branch default
100 updating to branch default
101 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
101 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
102 applying patch from stdin
102 applying patch from stdin
103 % import two patches in one stream
104 applying patch from stdin
105 applied 80971e65b431
106 1d4bd90af0e4 tip
107 1d4bd90af0e4 tip
103 % override commit message
108 % override commit message
104 requesting all changes
109 requesting all changes
105 adding changesets
110 adding changesets
106 adding manifests
111 adding manifests
107 adding file changes
112 adding file changes
108 added 1 changesets with 2 changes to 2 files
113 added 1 changesets with 2 changes to 2 files
109 updating to branch default
114 updating to branch default
110 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
115 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
111 applying patch from stdin
116 applying patch from stdin
112 summary: override
117 summary: override
113 % plain diff in email, subject, message body
118 % plain diff in email, subject, message body
114 requesting all changes
119 requesting all changes
115 adding changesets
120 adding changesets
116 adding manifests
121 adding manifests
117 adding file changes
122 adding file changes
118 added 1 changesets with 2 changes to 2 files
123 added 1 changesets with 2 changes to 2 files
119 updating to branch default
124 updating to branch default
120 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
125 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
121 applying ../msg.patch
126 applying ../msg.patch
122 user: email patcher
127 user: email patcher
123 summary: email patch
128 summary: email patch
124 % plain diff in email, no subject, message body
129 % plain diff in email, no subject, message body
125 requesting all changes
130 requesting all changes
126 adding changesets
131 adding changesets
127 adding manifests
132 adding manifests
128 adding file changes
133 adding file changes
129 added 1 changesets with 2 changes to 2 files
134 added 1 changesets with 2 changes to 2 files
130 updating to branch default
135 updating to branch default
131 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
136 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
132 applying patch from stdin
137 applying patch from stdin
133 % plain diff in email, subject, no message body
138 % plain diff in email, subject, no message body
134 requesting all changes
139 requesting all changes
135 adding changesets
140 adding changesets
136 adding manifests
141 adding manifests
137 adding file changes
142 adding file changes
138 added 1 changesets with 2 changes to 2 files
143 added 1 changesets with 2 changes to 2 files
139 updating to branch default
144 updating to branch default
140 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
145 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
141 applying patch from stdin
146 applying patch from stdin
142 % plain diff in email, no subject, no message body, should fail
147 % plain diff in email, no subject, no message body, should fail
143 requesting all changes
148 requesting all changes
144 adding changesets
149 adding changesets
145 adding manifests
150 adding manifests
146 adding file changes
151 adding file changes
147 added 1 changesets with 2 changes to 2 files
152 added 1 changesets with 2 changes to 2 files
148 updating to branch default
153 updating to branch default
149 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
154 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
150 applying patch from stdin
155 applying patch from stdin
151 abort: empty commit message
156 abort: empty commit message
152 % hg export in email, should use patch header
157 % hg export in email, should use patch header
153 requesting all changes
158 requesting all changes
154 adding changesets
159 adding changesets
155 adding manifests
160 adding manifests
156 adding file changes
161 adding file changes
157 added 1 changesets with 2 changes to 2 files
162 added 1 changesets with 2 changes to 2 files
158 updating to branch default
163 updating to branch default
159 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
164 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
160 applying patch from stdin
165 applying patch from stdin
161 summary: second change
166 summary: second change
162 % plain diff in email, [PATCH] subject, message body with subject
167 % plain diff in email, [PATCH] subject, message body with subject
163 requesting all changes
168 requesting all changes
164 adding changesets
169 adding changesets
165 adding manifests
170 adding manifests
166 adding file changes
171 adding file changes
167 added 1 changesets with 2 changes to 2 files
172 added 1 changesets with 2 changes to 2 files
168 updating to branch default
173 updating to branch default
169 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
174 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
170 applying patch from stdin
175 applying patch from stdin
171 email patch
176 email patch
172
177
173 next line
178 next line
174 ---
179 ---
175 % import patch1 patch2; rollback
180 % import patch1 patch2; rollback
176 parent: 0
181 parent: 0
177 applying ../patch1
182 applying ../patch1
178 applying ../patch2
183 applying ../patch2
184 applied 1d4bd90af0e4
179 rolling back last transaction
185 rolling back last transaction
180 parent: 1
186 parent: 1
181 % hg import in a subdirectory
187 % hg import in a subdirectory
182 requesting all changes
188 requesting all changes
183 adding changesets
189 adding changesets
184 adding manifests
190 adding manifests
185 adding file changes
191 adding file changes
186 added 1 changesets with 2 changes to 2 files
192 added 1 changesets with 2 changes to 2 files
187 updating to branch default
193 updating to branch default
188 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
194 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
189 applying ../../../tip.patch
195 applying ../../../tip.patch
190 % message should be 'subdir change'
196 % message should be 'subdir change'
191 summary: subdir change
197 summary: subdir change
192 % committer should be 'someoneelse'
198 % committer should be 'someoneelse'
193 user: someoneelse
199 user: someoneelse
194 % should be empty
200 % should be empty
195 % test fuzziness
201 % test fuzziness
196 adding a
202 adding a
197 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
203 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
198 created new head
204 created new head
199 applying tip.patch
205 applying tip.patch
200 patching file a
206 patching file a
201 Hunk #1 succeeded at 1 with fuzz 2 (offset -2 lines).
207 Hunk #1 succeeded at 1 with fuzz 2 (offset -2 lines).
202 a
208 a
203 adding a
209 adding a
204 adding b1
210 adding b1
205 adding c1
211 adding c1
206 adding d
212 adding d
207 diff --git a/a b/a
213 diff --git a/a b/a
208 --- a/a
214 --- a/a
209 +++ b/a
215 +++ b/a
210 @@ -0,0 +1,1 @@
216 @@ -0,0 +1,1 @@
211 +a
217 +a
212 diff --git a/b1 b/b2
218 diff --git a/b1 b/b2
213 rename from b1
219 rename from b1
214 rename to b2
220 rename to b2
215 --- a/b1
221 --- a/b1
216 +++ b/b2
222 +++ b/b2
217 @@ -0,0 +1,1 @@
223 @@ -0,0 +1,1 @@
218 +b
224 +b
219 diff --git a/c1 b/c1
225 diff --git a/c1 b/c1
220 --- a/c1
226 --- a/c1
221 +++ b/c1
227 +++ b/c1
222 @@ -0,0 +1,1 @@
228 @@ -0,0 +1,1 @@
223 +c
229 +c
224 diff --git a/c1 b/c2
230 diff --git a/c1 b/c2
225 copy from c1
231 copy from c1
226 copy to c2
232 copy to c2
227 --- a/c1
233 --- a/c1
228 +++ b/c2
234 +++ b/c2
229 @@ -0,0 +1,1 @@
235 @@ -0,0 +1,1 @@
230 +c
236 +c
231 diff --git a/d b/d
237 diff --git a/d b/d
232 --- a/d
238 --- a/d
233 +++ b/d
239 +++ b/d
234 @@ -1,1 +0,0 @@
240 @@ -1,1 +0,0 @@
235 -d
241 -d
236 4 files updated, 0 files merged, 2 files removed, 0 files unresolved
242 4 files updated, 0 files merged, 2 files removed, 0 files unresolved
237 applying empty.diff
243 applying empty.diff
238 % a file
244 % a file
239 a
245 a
240 % b1 file
246 % b1 file
241 % b2 file
247 % b2 file
242 b
248 b
243 % c1 file
249 % c1 file
244 c
250 c
245 % c2 file
251 % c2 file
246 c
252 c
247 % d file
253 % d file
248 % test trailing binary removal
254 % test trailing binary removal
249 adding a
255 adding a
250 adding b
256 adding b
251 R a
257 R a
252 R b
258 R b
253 diff --git a/a b/a
259 diff --git a/a b/a
254 diff --git a/b b/b
260 diff --git a/b b/b
255 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
261 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
256 applying remove.diff
262 applying remove.diff
257 % test update+rename with common name (issue 927)
263 % test update+rename with common name (issue 927)
258 adding a
264 adding a
259 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
265 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
260 applying copy.diff
266 applying copy.diff
261 % view a
267 % view a
262 a
268 a
263 % view a2
269 % view a2
264 a
270 a
265 % test -p0
271 % test -p0
266 adding a
272 adding a
267 applying patch from stdin
273 applying patch from stdin
268 bb
274 bb
269 % test paths outside repo root
275 % test paths outside repo root
270 applying patch from stdin
276 applying patch from stdin
271 abort: ../outside/foo not under root
277 abort: ../outside/foo not under root
272 % test import with similarity (issue295)
278 % test import with similarity (issue295)
273 adding a
279 adding a
274 applying ../rename.diff
280 applying ../rename.diff
275 patching file a
281 patching file a
276 patching file b
282 patching file b
277 removing a
283 removing a
278 adding b
284 adding b
279 recording removal of a as rename to b (88% similar)
285 recording removal of a as rename to b (88% similar)
280 A b
286 A b
281 a
287 a
282 R a
288 R a
283 undeleting a
289 undeleting a
284 forgetting b
290 forgetting b
285 applying ../rename.diff
291 applying ../rename.diff
286 patching file a
292 patching file a
287 patching file b
293 patching file b
288 removing a
294 removing a
289 adding b
295 adding b
290 A b
296 A b
291 R a
297 R a
292 % add empty file from the end of patch (issue 1495)
298 % add empty file from the end of patch (issue 1495)
293 adding a
299 adding a
294 applying a.patch
300 applying a.patch
295 % create file when source is not /dev/null
301 % create file when source is not /dev/null
296 applying ../create.patch
302 applying ../create.patch
297 a
303 a
298 % first line mistaken for email headers (issue 1859)
304 % first line mistaken for email headers (issue 1859)
299 applying a.patch
305 applying a.patch
300 changeset: 0:5a681217c0ad
306 changeset: 0:5a681217c0ad
301 tag: tip
307 tag: tip
302 user: test
308 user: test
303 date: Thu Jan 01 00:00:00 1970 +0000
309 date: Thu Jan 01 00:00:00 1970 +0000
304 files: a
310 files: a
305 description:
311 description:
306 module: summary
312 module: summary
307
313
308 description
314 description
309
315
310
316
General Comments 0
You need to be logged in to leave comments. Login now