##// END OF EJS Templates
help: miscellaneous language fixes
timeless -
r7807:bd8f4463 default
parent child Browse files
Show More
@@ -1,480 +1,480 b''
1 '''sending Mercurial changesets as a series of patch emails
1 '''sending Mercurial changesets as a series of patch emails
2
2
3 The series is started off with a "[PATCH 0 of N]" introduction,
3 The series is started off with a "[PATCH 0 of N]" introduction,
4 which describes the series as a whole.
4 which describes the series as a whole.
5
5
6 Each patch email has a Subject line of "[PATCH M of N] ...", using
6 Each patch email has a Subject line of "[PATCH M of N] ...", using
7 the first line of the changeset description as the subject text.
7 the first line of the changeset description as the subject text.
8 The message contains two or three body parts:
8 The message contains two or three body parts:
9
9
10 The remainder of the changeset description.
10 The remainder of the changeset description.
11
11
12 [Optional] The result of running diffstat on the patch.
12 [Optional] The result of running diffstat on the patch.
13
13
14 The patch itself, as generated by "hg export".
14 The patch itself, as generated by "hg export".
15
15
16 Each message refers to all of its predecessors using the In-Reply-To
16 Each message refers to all of its predecessors using the In-Reply-To
17 and References headers, so they will show up as a sequence in
17 and References headers, so they will show up as a sequence in
18 threaded mail and news readers, and in mail archives.
18 threaded mail and news readers, and in mail archives.
19
19
20 For each changeset, you will be prompted with a diffstat summary and
20 For each changeset, you will be prompted with a diffstat summary and
21 the changeset summary, so you can be sure you are sending the right changes.
21 the changeset summary, so you can be sure you are sending the right changes.
22
22
23 To enable this extension:
23 To enable this extension:
24
24
25 [extensions]
25 [extensions]
26 hgext.patchbomb =
26 hgext.patchbomb =
27
27
28 To configure other defaults, add a section like this to your hgrc file:
28 To configure other defaults, add a section like this to your hgrc file:
29
29
30 [email]
30 [email]
31 from = My Name <my@email>
31 from = My Name <my@email>
32 to = recipient1, recipient2, ...
32 to = recipient1, recipient2, ...
33 cc = cc1, cc2, ...
33 cc = cc1, cc2, ...
34 bcc = bcc1, bcc2, ...
34 bcc = bcc1, bcc2, ...
35
35
36 Then you can use the "hg email" command to mail a series of changesets
36 Then you can use the "hg email" command to mail a series of changesets
37 as a patchbomb.
37 as a patchbomb.
38
38
39 To avoid sending patches prematurely, it is a good idea to first run
39 To avoid sending patches prematurely, it is a good idea to first run
40 the "email" command with the "-n" option (test only). You will be
40 the "email" command with the "-n" option (test only). You will be
41 prompted for an email recipient address, a subject an an introductory
41 prompted for an email recipient address, a subject an an introductory
42 message describing the patches of your patchbomb. Then when all is
42 message describing the patches of your patchbomb. Then when all is
43 done, patchbomb messages are displayed. If PAGER environment variable
43 done, patchbomb messages are displayed. If PAGER environment variable
44 is set, your pager will be fired up once for each patchbomb message, so
44 is set, your pager will be fired up once for each patchbomb message, so
45 you can verify everything is alright.
45 you can verify everything is alright.
46
46
47 The "-m" (mbox) option is also very useful. Instead of previewing
47 The "-m" (mbox) option is also very useful. Instead of previewing
48 each patchbomb message in a pager or sending the messages directly,
48 each patchbomb message in a pager or sending the messages directly,
49 it will create a UNIX mailbox file with the patch emails. This
49 it will create a UNIX mailbox file with the patch emails. This
50 mailbox file can be previewed with any mail user agent which supports
50 mailbox file can be previewed with any mail user agent which supports
51 UNIX mbox files, i.e. with mutt:
51 UNIX mbox files, e.g. with mutt:
52
52
53 % mutt -R -f mbox
53 % mutt -R -f mbox
54
54
55 When you are previewing the patchbomb messages, you can use `formail'
55 When you are previewing the patchbomb messages, you can use `formail'
56 (a utility that is commonly installed as part of the procmail package),
56 (a utility that is commonly installed as part of the procmail package),
57 to send each message out:
57 to send each message out:
58
58
59 % formail -s sendmail -bm -t < mbox
59 % formail -s sendmail -bm -t < mbox
60
60
61 That should be all. Now your patchbomb is on its way out.
61 That should be all. Now your patchbomb is on its way out.
62
62
63 You can also either configure the method option in the email section
63 You can also either configure the method option in the email section
64 to be a sendmail compatable mailer or fill out the [smtp] section so
64 to be a sendmail compatable mailer or fill out the [smtp] section so
65 that the patchbomb extension can automatically send patchbombs directly
65 that the patchbomb extension can automatically send patchbombs directly
66 from the commandline. See the [email] and [smtp] sections in hgrc(5)
66 from the commandline. See the [email] and [smtp] sections in hgrc(5)
67 for details.'''
67 for details.'''
68
68
69 import os, errno, socket, tempfile, cStringIO
69 import os, errno, socket, tempfile, cStringIO
70 import email.MIMEMultipart, email.MIMEBase
70 import email.MIMEMultipart, email.MIMEBase
71 import email.Utils, email.Encoders, email.Generator
71 import email.Utils, email.Encoders, email.Generator
72 from mercurial import cmdutil, commands, hg, mail, patch, util
72 from mercurial import cmdutil, commands, hg, mail, patch, util
73 from mercurial.i18n import _
73 from mercurial.i18n import _
74 from mercurial.node import bin
74 from mercurial.node import bin
75
75
76 def prompt(ui, prompt, default=None, rest=': ', empty_ok=False):
76 def prompt(ui, prompt, default=None, rest=': ', empty_ok=False):
77 if not ui.interactive:
77 if not ui.interactive:
78 return default
78 return default
79 if default:
79 if default:
80 prompt += ' [%s]' % default
80 prompt += ' [%s]' % default
81 prompt += rest
81 prompt += rest
82 while True:
82 while True:
83 r = ui.prompt(prompt, default=default)
83 r = ui.prompt(prompt, default=default)
84 if r:
84 if r:
85 return r
85 return r
86 if default is not None:
86 if default is not None:
87 return default
87 return default
88 if empty_ok:
88 if empty_ok:
89 return r
89 return r
90 ui.warn(_('Please enter a valid value.\n'))
90 ui.warn(_('Please enter a valid value.\n'))
91
91
92 def cdiffstat(ui, summary, patchlines):
92 def cdiffstat(ui, summary, patchlines):
93 s = patch.diffstat(patchlines)
93 s = patch.diffstat(patchlines)
94 if summary:
94 if summary:
95 ui.write(summary, '\n')
95 ui.write(summary, '\n')
96 ui.write(s, '\n')
96 ui.write(s, '\n')
97 ans = prompt(ui, _('does the diffstat above look okay? '), 'y')
97 ans = prompt(ui, _('does the diffstat above look okay? '), 'y')
98 if not ans.lower().startswith('y'):
98 if not ans.lower().startswith('y'):
99 raise util.Abort(_('diffstat rejected'))
99 raise util.Abort(_('diffstat rejected'))
100 return s
100 return s
101
101
102 def makepatch(ui, repo, patch, opts, _charsets, idx, total, patchname=None):
102 def makepatch(ui, repo, patch, opts, _charsets, idx, total, patchname=None):
103
103
104 desc = []
104 desc = []
105 node = None
105 node = None
106 body = ''
106 body = ''
107
107
108 for line in patch:
108 for line in patch:
109 if line.startswith('#'):
109 if line.startswith('#'):
110 if line.startswith('# Node ID'):
110 if line.startswith('# Node ID'):
111 node = line.split()[-1]
111 node = line.split()[-1]
112 continue
112 continue
113 if line.startswith('diff -r') or line.startswith('diff --git'):
113 if line.startswith('diff -r') or line.startswith('diff --git'):
114 break
114 break
115 desc.append(line)
115 desc.append(line)
116
116
117 if not patchname and not node:
117 if not patchname and not node:
118 raise ValueError
118 raise ValueError
119
119
120 if opts.get('attach'):
120 if opts.get('attach'):
121 body = ('\n'.join(desc[1:]).strip() or
121 body = ('\n'.join(desc[1:]).strip() or
122 'Patch subject is complete summary.')
122 'Patch subject is complete summary.')
123 body += '\n\n\n'
123 body += '\n\n\n'
124
124
125 if opts.get('plain'):
125 if opts.get('plain'):
126 while patch and patch[0].startswith('# '):
126 while patch and patch[0].startswith('# '):
127 patch.pop(0)
127 patch.pop(0)
128 if patch:
128 if patch:
129 patch.pop(0)
129 patch.pop(0)
130 while patch and not patch[0].strip():
130 while patch and not patch[0].strip():
131 patch.pop(0)
131 patch.pop(0)
132
132
133 if opts.get('diffstat'):
133 if opts.get('diffstat'):
134 body += cdiffstat(ui, '\n'.join(desc), patch) + '\n\n'
134 body += cdiffstat(ui, '\n'.join(desc), patch) + '\n\n'
135
135
136 if opts.get('attach') or opts.get('inline'):
136 if opts.get('attach') or opts.get('inline'):
137 msg = email.MIMEMultipart.MIMEMultipart()
137 msg = email.MIMEMultipart.MIMEMultipart()
138 if body:
138 if body:
139 msg.attach(mail.mimeencode(ui, body, _charsets, opts.get('test')))
139 msg.attach(mail.mimeencode(ui, body, _charsets, opts.get('test')))
140 p = mail.mimetextpatch('\n'.join(patch), 'x-patch', opts.get('test'))
140 p = mail.mimetextpatch('\n'.join(patch), 'x-patch', opts.get('test'))
141 binnode = bin(node)
141 binnode = bin(node)
142 # if node is mq patch, it will have patch file name as tag
142 # if node is mq patch, it will have patch file name as tag
143 if not patchname:
143 if not patchname:
144 patchtags = [t for t in repo.nodetags(binnode)
144 patchtags = [t for t in repo.nodetags(binnode)
145 if t.endswith('.patch') or t.endswith('.diff')]
145 if t.endswith('.patch') or t.endswith('.diff')]
146 if patchtags:
146 if patchtags:
147 patchname = patchtags[0]
147 patchname = patchtags[0]
148 elif total > 1:
148 elif total > 1:
149 patchname = cmdutil.make_filename(repo, '%b-%n.patch',
149 patchname = cmdutil.make_filename(repo, '%b-%n.patch',
150 binnode, seqno=idx, total=total)
150 binnode, seqno=idx, total=total)
151 else:
151 else:
152 patchname = cmdutil.make_filename(repo, '%b.patch', binnode)
152 patchname = cmdutil.make_filename(repo, '%b.patch', binnode)
153 disposition = 'inline'
153 disposition = 'inline'
154 if opts.get('attach'):
154 if opts.get('attach'):
155 disposition = 'attachment'
155 disposition = 'attachment'
156 p['Content-Disposition'] = disposition + '; filename=' + patchname
156 p['Content-Disposition'] = disposition + '; filename=' + patchname
157 msg.attach(p)
157 msg.attach(p)
158 else:
158 else:
159 body += '\n'.join(patch)
159 body += '\n'.join(patch)
160 msg = mail.mimetextpatch(body, display=opts.get('test'))
160 msg = mail.mimetextpatch(body, display=opts.get('test'))
161
161
162 subj = desc[0].strip().rstrip('. ')
162 subj = desc[0].strip().rstrip('. ')
163 if total == 1 and not opts.get('intro'):
163 if total == 1 and not opts.get('intro'):
164 subj = '[PATCH] ' + (opts.get('subject') or subj)
164 subj = '[PATCH] ' + (opts.get('subject') or subj)
165 else:
165 else:
166 tlen = len(str(total))
166 tlen = len(str(total))
167 subj = '[PATCH %0*d of %d] %s' % (tlen, idx, total, subj)
167 subj = '[PATCH %0*d of %d] %s' % (tlen, idx, total, subj)
168 msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test'))
168 msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test'))
169 msg['X-Mercurial-Node'] = node
169 msg['X-Mercurial-Node'] = node
170 return msg, subj
170 return msg, subj
171
171
172 def patchbomb(ui, repo, *revs, **opts):
172 def patchbomb(ui, repo, *revs, **opts):
173 '''send changesets by email
173 '''send changesets by email
174
174
175 By default, diffs are sent in the format generated by hg export,
175 By default, diffs are sent in the format generated by hg export,
176 one per message. The series starts with a "[PATCH 0 of N]"
176 one per message. The series starts with a "[PATCH 0 of N]"
177 introduction, which describes the series as a whole.
177 introduction, which describes the series as a whole.
178
178
179 Each patch email has a Subject line of "[PATCH M of N] ...", using
179 Each patch email has a Subject line of "[PATCH M of N] ...", using
180 the first line of the changeset description as the subject text.
180 the first line of the changeset description as the subject text.
181 The message contains two or three body parts. First, the rest of
181 The message contains two or three body parts. First, the rest of
182 the changeset description. Next, (optionally) if the diffstat
182 the changeset description. Next, (optionally) if the diffstat
183 program is installed, the result of running diffstat on the patch.
183 program is installed, the result of running diffstat on the patch.
184 Finally, the patch itself, as generated by "hg export".
184 Finally, the patch itself, as generated by "hg export".
185
185
186 With --outgoing, emails will be generated for patches not
186 With --outgoing, emails will be generated for patches not
187 found in the destination repository (or only those which are
187 found in the destination repository (or only those which are
188 ancestors of the specified revisions if any are provided)
188 ancestors of the specified revisions if any are provided)
189
189
190 With --bundle, changesets are selected as for --outgoing,
190 With --bundle, changesets are selected as for --outgoing,
191 but a single email containing a binary Mercurial bundle as an
191 but a single email containing a binary Mercurial bundle as an
192 attachment will be sent.
192 attachment will be sent.
193
193
194 Examples:
194 Examples:
195
195
196 hg email -r 3000 # send patch 3000 only
196 hg email -r 3000 # send patch 3000 only
197 hg email -r 3000 -r 3001 # send patches 3000 and 3001
197 hg email -r 3000 -r 3001 # send patches 3000 and 3001
198 hg email -r 3000:3005 # send patches 3000 through 3005
198 hg email -r 3000:3005 # send patches 3000 through 3005
199 hg email 3000 # send patch 3000 (deprecated)
199 hg email 3000 # send patch 3000 (deprecated)
200
200
201 hg email -o # send all patches not in default
201 hg email -o # send all patches not in default
202 hg email -o DEST # send all patches not in DEST
202 hg email -o DEST # send all patches not in DEST
203 hg email -o -r 3000 # send all ancestors of 3000 not in default
203 hg email -o -r 3000 # send all ancestors of 3000 not in default
204 hg email -o -r 3000 DEST # send all ancestors of 3000 not in DEST
204 hg email -o -r 3000 DEST # send all ancestors of 3000 not in DEST
205
205
206 hg email -b # send bundle of all patches not in default
206 hg email -b # send bundle of all patches not in default
207 hg email -b DEST # send bundle of all patches not in DEST
207 hg email -b DEST # send bundle of all patches not in DEST
208 hg email -b -r 3000 # bundle of all ancestors of 3000 not in default
208 hg email -b -r 3000 # bundle of all ancestors of 3000 not in default
209 hg email -b -r 3000 DEST # bundle of all ancestors of 3000 not in DEST
209 hg email -b -r 3000 DEST # bundle of all ancestors of 3000 not in DEST
210
210
211 Before using this command, you will need to enable email in your hgrc.
211 Before using this command, you will need to enable email in your hgrc.
212 See the [email] section in hgrc(5) for details.
212 See the [email] section in hgrc(5) for details.
213 '''
213 '''
214
214
215 _charsets = mail._charsets(ui)
215 _charsets = mail._charsets(ui)
216
216
217 def outgoing(dest, revs):
217 def outgoing(dest, revs):
218 '''Return the revisions present locally but not in dest'''
218 '''Return the revisions present locally but not in dest'''
219 dest = ui.expandpath(dest or 'default-push', dest or 'default')
219 dest = ui.expandpath(dest or 'default-push', dest or 'default')
220 revs = [repo.lookup(rev) for rev in revs]
220 revs = [repo.lookup(rev) for rev in revs]
221 other = hg.repository(ui, dest)
221 other = hg.repository(ui, dest)
222 ui.status(_('comparing with %s\n') % dest)
222 ui.status(_('comparing with %s\n') % dest)
223 o = repo.findoutgoing(other)
223 o = repo.findoutgoing(other)
224 if not o:
224 if not o:
225 ui.status(_("no changes found\n"))
225 ui.status(_("no changes found\n"))
226 return []
226 return []
227 o = repo.changelog.nodesbetween(o, revs or None)[0]
227 o = repo.changelog.nodesbetween(o, revs or None)[0]
228 return [str(repo.changelog.rev(r)) for r in o]
228 return [str(repo.changelog.rev(r)) for r in o]
229
229
230 def getpatches(revs):
230 def getpatches(revs):
231 for r in cmdutil.revrange(repo, revs):
231 for r in cmdutil.revrange(repo, revs):
232 output = cStringIO.StringIO()
232 output = cStringIO.StringIO()
233 p = patch.export(repo, [r], fp=output,
233 p = patch.export(repo, [r], fp=output,
234 opts=patch.diffopts(ui, opts))
234 opts=patch.diffopts(ui, opts))
235 yield output.getvalue().split('\n')
235 yield output.getvalue().split('\n')
236
236
237 def getbundle(dest):
237 def getbundle(dest):
238 tmpdir = tempfile.mkdtemp(prefix='hg-email-bundle-')
238 tmpdir = tempfile.mkdtemp(prefix='hg-email-bundle-')
239 tmpfn = os.path.join(tmpdir, 'bundle')
239 tmpfn = os.path.join(tmpdir, 'bundle')
240 try:
240 try:
241 commands.bundle(ui, repo, tmpfn, dest, **opts)
241 commands.bundle(ui, repo, tmpfn, dest, **opts)
242 return open(tmpfn, 'rb').read()
242 return open(tmpfn, 'rb').read()
243 finally:
243 finally:
244 try:
244 try:
245 os.unlink(tmpfn)
245 os.unlink(tmpfn)
246 except:
246 except:
247 pass
247 pass
248 os.rmdir(tmpdir)
248 os.rmdir(tmpdir)
249
249
250 if not (opts.get('test') or opts.get('mbox')):
250 if not (opts.get('test') or opts.get('mbox')):
251 # really sending
251 # really sending
252 mail.validateconfig(ui)
252 mail.validateconfig(ui)
253
253
254 if not (revs or opts.get('rev')
254 if not (revs or opts.get('rev')
255 or opts.get('outgoing') or opts.get('bundle')
255 or opts.get('outgoing') or opts.get('bundle')
256 or opts.get('patches')):
256 or opts.get('patches')):
257 raise util.Abort(_('specify at least one changeset with -r or -o'))
257 raise util.Abort(_('specify at least one changeset with -r or -o'))
258
258
259 cmdutil.setremoteconfig(ui, opts)
259 cmdutil.setremoteconfig(ui, opts)
260 if opts.get('outgoing') and opts.get('bundle'):
260 if opts.get('outgoing') and opts.get('bundle'):
261 raise util.Abort(_("--outgoing mode always on with --bundle;"
261 raise util.Abort(_("--outgoing mode always on with --bundle;"
262 " do not re-specify --outgoing"))
262 " do not re-specify --outgoing"))
263
263
264 if opts.get('outgoing') or opts.get('bundle'):
264 if opts.get('outgoing') or opts.get('bundle'):
265 if len(revs) > 1:
265 if len(revs) > 1:
266 raise util.Abort(_("too many destinations"))
266 raise util.Abort(_("too many destinations"))
267 dest = revs and revs[0] or None
267 dest = revs and revs[0] or None
268 revs = []
268 revs = []
269
269
270 if opts.get('rev'):
270 if opts.get('rev'):
271 if revs:
271 if revs:
272 raise util.Abort(_('use only one form to specify the revision'))
272 raise util.Abort(_('use only one form to specify the revision'))
273 revs = opts.get('rev')
273 revs = opts.get('rev')
274
274
275 if opts.get('outgoing'):
275 if opts.get('outgoing'):
276 revs = outgoing(dest, opts.get('rev'))
276 revs = outgoing(dest, opts.get('rev'))
277 if opts.get('bundle'):
277 if opts.get('bundle'):
278 opts['revs'] = revs
278 opts['revs'] = revs
279
279
280 # start
280 # start
281 if opts.get('date'):
281 if opts.get('date'):
282 start_time = util.parsedate(opts.get('date'))
282 start_time = util.parsedate(opts.get('date'))
283 else:
283 else:
284 start_time = util.makedate()
284 start_time = util.makedate()
285
285
286 def genmsgid(id):
286 def genmsgid(id):
287 return '<%s.%s@%s>' % (id[:20], int(start_time[0]), socket.getfqdn())
287 return '<%s.%s@%s>' % (id[:20], int(start_time[0]), socket.getfqdn())
288
288
289 def getdescription(body, sender):
289 def getdescription(body, sender):
290 if opts.get('desc'):
290 if opts.get('desc'):
291 body = open(opts.get('desc')).read()
291 body = open(opts.get('desc')).read()
292 else:
292 else:
293 ui.write(_('\nWrite the introductory message for the '
293 ui.write(_('\nWrite the introductory message for the '
294 'patch series.\n\n'))
294 'patch series.\n\n'))
295 body = ui.edit(body, sender)
295 body = ui.edit(body, sender)
296 return body
296 return body
297
297
298 def getpatchmsgs(patches, patchnames=None):
298 def getpatchmsgs(patches, patchnames=None):
299 jumbo = []
299 jumbo = []
300 msgs = []
300 msgs = []
301
301
302 ui.write(_('This patch series consists of %d patches.\n\n')
302 ui.write(_('This patch series consists of %d patches.\n\n')
303 % len(patches))
303 % len(patches))
304
304
305 name = None
305 name = None
306 for i, p in enumerate(patches):
306 for i, p in enumerate(patches):
307 jumbo.extend(p)
307 jumbo.extend(p)
308 if patchnames:
308 if patchnames:
309 name = patchnames[i]
309 name = patchnames[i]
310 msg = makepatch(ui, repo, p, opts, _charsets, i + 1,
310 msg = makepatch(ui, repo, p, opts, _charsets, i + 1,
311 len(patches), name)
311 len(patches), name)
312 msgs.append(msg)
312 msgs.append(msg)
313
313
314 if len(patches) > 1 or opts.get('intro'):
314 if len(patches) > 1 or opts.get('intro'):
315 tlen = len(str(len(patches)))
315 tlen = len(str(len(patches)))
316
316
317 subj = '[PATCH %0*d of %d] %s' % (
317 subj = '[PATCH %0*d of %d] %s' % (
318 tlen, 0, len(patches),
318 tlen, 0, len(patches),
319 opts.get('subject') or
319 opts.get('subject') or
320 prompt(ui, 'Subject:',
320 prompt(ui, 'Subject:',
321 rest=' [PATCH %0*d of %d] ' % (tlen, 0, len(patches))))
321 rest=' [PATCH %0*d of %d] ' % (tlen, 0, len(patches))))
322
322
323 body = ''
323 body = ''
324 if opts.get('diffstat'):
324 if opts.get('diffstat'):
325 d = cdiffstat(ui, _('Final summary:\n'), jumbo)
325 d = cdiffstat(ui, _('Final summary:\n'), jumbo)
326 if d:
326 if d:
327 body = '\n' + d
327 body = '\n' + d
328
328
329 body = getdescription(body, sender)
329 body = getdescription(body, sender)
330 msg = mail.mimeencode(ui, body, _charsets, opts.get('test'))
330 msg = mail.mimeencode(ui, body, _charsets, opts.get('test'))
331 msg['Subject'] = mail.headencode(ui, subj, _charsets,
331 msg['Subject'] = mail.headencode(ui, subj, _charsets,
332 opts.get('test'))
332 opts.get('test'))
333
333
334 msgs.insert(0, (msg, subj))
334 msgs.insert(0, (msg, subj))
335 return msgs
335 return msgs
336
336
337 def getbundlemsgs(bundle):
337 def getbundlemsgs(bundle):
338 subj = (opts.get('subject')
338 subj = (opts.get('subject')
339 or prompt(ui, 'Subject:', 'A bundle for your repository'))
339 or prompt(ui, 'Subject:', 'A bundle for your repository'))
340
340
341 body = getdescription('', sender)
341 body = getdescription('', sender)
342 msg = email.MIMEMultipart.MIMEMultipart()
342 msg = email.MIMEMultipart.MIMEMultipart()
343 if body:
343 if body:
344 msg.attach(mail.mimeencode(ui, body, _charsets, opts.get('test')))
344 msg.attach(mail.mimeencode(ui, body, _charsets, opts.get('test')))
345 datapart = email.MIMEBase.MIMEBase('application', 'x-mercurial-bundle')
345 datapart = email.MIMEBase.MIMEBase('application', 'x-mercurial-bundle')
346 datapart.set_payload(bundle)
346 datapart.set_payload(bundle)
347 datapart.add_header('Content-Disposition', 'attachment',
347 datapart.add_header('Content-Disposition', 'attachment',
348 filename='bundle.hg')
348 filename='bundle.hg')
349 email.Encoders.encode_base64(datapart)
349 email.Encoders.encode_base64(datapart)
350 msg.attach(datapart)
350 msg.attach(datapart)
351 msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test'))
351 msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test'))
352 return [(msg, subj)]
352 return [(msg, subj)]
353
353
354 sender = (opts.get('from') or ui.config('email', 'from') or
354 sender = (opts.get('from') or ui.config('email', 'from') or
355 ui.config('patchbomb', 'from') or
355 ui.config('patchbomb', 'from') or
356 prompt(ui, 'From', ui.username()))
356 prompt(ui, 'From', ui.username()))
357
357
358 # internal option used by pbranches
358 # internal option used by pbranches
359 patches = opts.get('patches')
359 patches = opts.get('patches')
360 if patches:
360 if patches:
361 msgs = getpatchmsgs(patches, opts.get('patchnames'))
361 msgs = getpatchmsgs(patches, opts.get('patchnames'))
362 elif opts.get('bundle'):
362 elif opts.get('bundle'):
363 msgs = getbundlemsgs(getbundle(dest))
363 msgs = getbundlemsgs(getbundle(dest))
364 else:
364 else:
365 msgs = getpatchmsgs(list(getpatches(revs)))
365 msgs = getpatchmsgs(list(getpatches(revs)))
366
366
367 def getaddrs(opt, prpt, default = None):
367 def getaddrs(opt, prpt, default = None):
368 addrs = opts.get(opt) or (ui.config('email', opt) or
368 addrs = opts.get(opt) or (ui.config('email', opt) or
369 ui.config('patchbomb', opt) or
369 ui.config('patchbomb', opt) or
370 prompt(ui, prpt, default)).split(',')
370 prompt(ui, prpt, default)).split(',')
371 return [mail.addressencode(ui, a.strip(), _charsets, opts.get('test'))
371 return [mail.addressencode(ui, a.strip(), _charsets, opts.get('test'))
372 for a in addrs if a.strip()]
372 for a in addrs if a.strip()]
373
373
374 to = getaddrs('to', 'To')
374 to = getaddrs('to', 'To')
375 cc = getaddrs('cc', 'Cc', '')
375 cc = getaddrs('cc', 'Cc', '')
376
376
377 bcc = opts.get('bcc') or (ui.config('email', 'bcc') or
377 bcc = opts.get('bcc') or (ui.config('email', 'bcc') or
378 ui.config('patchbomb', 'bcc') or '').split(',')
378 ui.config('patchbomb', 'bcc') or '').split(',')
379 bcc = [mail.addressencode(ui, a.strip(), _charsets, opts.get('test'))
379 bcc = [mail.addressencode(ui, a.strip(), _charsets, opts.get('test'))
380 for a in bcc if a.strip()]
380 for a in bcc if a.strip()]
381
381
382 ui.write('\n')
382 ui.write('\n')
383
383
384 parent = None
384 parent = None
385
385
386 sender_addr = email.Utils.parseaddr(sender)[1]
386 sender_addr = email.Utils.parseaddr(sender)[1]
387 sender = mail.addressencode(ui, sender, _charsets, opts.get('test'))
387 sender = mail.addressencode(ui, sender, _charsets, opts.get('test'))
388 sendmail = None
388 sendmail = None
389 for m, subj in msgs:
389 for m, subj in msgs:
390 try:
390 try:
391 m['Message-Id'] = genmsgid(m['X-Mercurial-Node'])
391 m['Message-Id'] = genmsgid(m['X-Mercurial-Node'])
392 except TypeError:
392 except TypeError:
393 m['Message-Id'] = genmsgid('patchbomb')
393 m['Message-Id'] = genmsgid('patchbomb')
394 if parent:
394 if parent:
395 m['In-Reply-To'] = parent
395 m['In-Reply-To'] = parent
396 m['References'] = parent
396 m['References'] = parent
397 else:
397 else:
398 parent = m['Message-Id']
398 parent = m['Message-Id']
399 m['Date'] = util.datestr(start_time, "%a, %d %b %Y %H:%M:%S %1%2")
399 m['Date'] = util.datestr(start_time, "%a, %d %b %Y %H:%M:%S %1%2")
400
400
401 start_time = (start_time[0] + 1, start_time[1])
401 start_time = (start_time[0] + 1, start_time[1])
402 m['From'] = sender
402 m['From'] = sender
403 m['To'] = ', '.join(to)
403 m['To'] = ', '.join(to)
404 if cc:
404 if cc:
405 m['Cc'] = ', '.join(cc)
405 m['Cc'] = ', '.join(cc)
406 if bcc:
406 if bcc:
407 m['Bcc'] = ', '.join(bcc)
407 m['Bcc'] = ', '.join(bcc)
408 if opts.get('test'):
408 if opts.get('test'):
409 ui.status(_('Displaying '), subj, ' ...\n')
409 ui.status(_('Displaying '), subj, ' ...\n')
410 ui.flush()
410 ui.flush()
411 if 'PAGER' in os.environ:
411 if 'PAGER' in os.environ:
412 fp = util.popen(os.environ['PAGER'], 'w')
412 fp = util.popen(os.environ['PAGER'], 'w')
413 else:
413 else:
414 fp = ui
414 fp = ui
415 generator = email.Generator.Generator(fp, mangle_from_=False)
415 generator = email.Generator.Generator(fp, mangle_from_=False)
416 try:
416 try:
417 generator.flatten(m, 0)
417 generator.flatten(m, 0)
418 fp.write('\n')
418 fp.write('\n')
419 except IOError, inst:
419 except IOError, inst:
420 if inst.errno != errno.EPIPE:
420 if inst.errno != errno.EPIPE:
421 raise
421 raise
422 if fp is not ui:
422 if fp is not ui:
423 fp.close()
423 fp.close()
424 elif opts.get('mbox'):
424 elif opts.get('mbox'):
425 ui.status(_('Writing '), subj, ' ...\n')
425 ui.status(_('Writing '), subj, ' ...\n')
426 fp = open(opts.get('mbox'), 'In-Reply-To' in m and 'ab+' or 'wb+')
426 fp = open(opts.get('mbox'), 'In-Reply-To' in m and 'ab+' or 'wb+')
427 generator = email.Generator.Generator(fp, mangle_from_=True)
427 generator = email.Generator.Generator(fp, mangle_from_=True)
428 date = util.datestr(start_time, '%a %b %d %H:%M:%S %Y')
428 date = util.datestr(start_time, '%a %b %d %H:%M:%S %Y')
429 fp.write('From %s %s\n' % (sender_addr, date))
429 fp.write('From %s %s\n' % (sender_addr, date))
430 generator.flatten(m, 0)
430 generator.flatten(m, 0)
431 fp.write('\n\n')
431 fp.write('\n\n')
432 fp.close()
432 fp.close()
433 else:
433 else:
434 if not sendmail:
434 if not sendmail:
435 sendmail = mail.connect(ui)
435 sendmail = mail.connect(ui)
436 ui.status(_('Sending '), subj, ' ...\n')
436 ui.status(_('Sending '), subj, ' ...\n')
437 # Exim does not remove the Bcc field
437 # Exim does not remove the Bcc field
438 del m['Bcc']
438 del m['Bcc']
439 fp = cStringIO.StringIO()
439 fp = cStringIO.StringIO()
440 generator = email.Generator.Generator(fp, mangle_from_=False)
440 generator = email.Generator.Generator(fp, mangle_from_=False)
441 generator.flatten(m, 0)
441 generator.flatten(m, 0)
442 sendmail(sender, to + bcc + cc, fp.getvalue())
442 sendmail(sender, to + bcc + cc, fp.getvalue())
443
443
444 emailopts = [
444 emailopts = [
445 ('a', 'attach', None, _('send patches as attachments')),
445 ('a', 'attach', None, _('send patches as attachments')),
446 ('i', 'inline', None, _('send patches as inline attachments')),
446 ('i', 'inline', None, _('send patches as inline attachments')),
447 ('', 'bcc', [], _('email addresses of blind copy recipients')),
447 ('', 'bcc', [], _('email addresses of blind carbon copy recipients')),
448 ('c', 'cc', [], _('email addresses of copy recipients')),
448 ('c', 'cc', [], _('email addresses of copy recipients')),
449 ('d', 'diffstat', None, _('add diffstat output to messages')),
449 ('d', 'diffstat', None, _('add diffstat output to messages')),
450 ('', 'date', '', _('use the given date as the sending date')),
450 ('', 'date', '', _('use the given date as the sending date')),
451 ('', 'desc', '', _('use the given file as the series description')),
451 ('', 'desc', '', _('use the given file as the series description')),
452 ('f', 'from', '', _('email address of sender')),
452 ('f', 'from', '', _('email address of sender')),
453 ('n', 'test', None, _('print messages that would be sent')),
453 ('n', 'test', None, _('print messages that would be sent')),
454 ('m', 'mbox', '',
454 ('m', 'mbox', '',
455 _('write messages to mbox file instead of sending them')),
455 _('write messages to mbox file instead of sending them')),
456 ('s', 'subject', '',
456 ('s', 'subject', '',
457 _('subject of first message (intro or single patch)')),
457 _('subject of first message (intro or single patch)')),
458 ('t', 'to', [], _('email addresses of recipients')),
458 ('t', 'to', [], _('email addresses of recipients')),
459 ]
459 ]
460
460
461
461
462 cmdtable = {
462 cmdtable = {
463 "email":
463 "email":
464 (patchbomb,
464 (patchbomb,
465 [('g', 'git', None, _('use git extended diff format')),
465 [('g', 'git', None, _('use git extended diff format')),
466 ('', 'plain', None, _('omit hg patch header')),
466 ('', 'plain', None, _('omit hg patch header')),
467 ('o', 'outgoing', None,
467 ('o', 'outgoing', None,
468 _('send changes not found in the target repository')),
468 _('send changes not found in the target repository')),
469 ('b', 'bundle', None,
469 ('b', 'bundle', None,
470 _('send changes not in target as a binary bundle')),
470 _('send changes not in target as a binary bundle')),
471 ('r', 'rev', [], _('a revision to send')),
471 ('r', 'rev', [], _('a revision to send')),
472 ('', 'force', None,
472 ('', 'force', None,
473 _('run even when remote repository is unrelated (with -b)')),
473 _('run even when remote repository is unrelated (with -b)')),
474 ('', 'base', [],
474 ('', 'base', [],
475 _('a base changeset to specify instead of a destination (with -b)')),
475 _('a base changeset to specify instead of a destination (with -b)')),
476 ('', 'intro', None,
476 ('', 'intro', None,
477 _('send an introduction email for a single patch')),
477 _('send an introduction email for a single patch')),
478 ] + emailopts + commands.remoteopts,
478 ] + emailopts + commands.remoteopts,
479 _('hg email [OPTION]... [DEST]...'))
479 _('hg email [OPTION]... [DEST]...'))
480 }
480 }
@@ -1,221 +1,221 b''
1 # changelog.py - changelog class for mercurial
1 # changelog.py - changelog class 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
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from node import bin, hex, nullid
8 from node import bin, hex, nullid
9 from i18n import _
9 from i18n import _
10 import util, error, revlog
10 import util, error, revlog
11
11
12 def _string_escape(text):
12 def _string_escape(text):
13 """
13 """
14 >>> d = {'nl': chr(10), 'bs': chr(92), 'cr': chr(13), 'nul': chr(0)}
14 >>> d = {'nl': chr(10), 'bs': chr(92), 'cr': chr(13), 'nul': chr(0)}
15 >>> s = "ab%(nl)scd%(bs)s%(bs)sn%(nul)sab%(cr)scd%(bs)s%(nl)s" % d
15 >>> s = "ab%(nl)scd%(bs)s%(bs)sn%(nul)sab%(cr)scd%(bs)s%(nl)s" % d
16 >>> s
16 >>> s
17 'ab\\ncd\\\\\\\\n\\x00ab\\rcd\\\\\\n'
17 'ab\\ncd\\\\\\\\n\\x00ab\\rcd\\\\\\n'
18 >>> res = _string_escape(s)
18 >>> res = _string_escape(s)
19 >>> s == res.decode('string_escape')
19 >>> s == res.decode('string_escape')
20 True
20 True
21 """
21 """
22 # subset of the string_escape codec
22 # subset of the string_escape codec
23 text = text.replace('\\', '\\\\').replace('\n', '\\n').replace('\r', '\\r')
23 text = text.replace('\\', '\\\\').replace('\n', '\\n').replace('\r', '\\r')
24 return text.replace('\0', '\\0')
24 return text.replace('\0', '\\0')
25
25
26 class appender:
26 class appender:
27 '''the changelog index must be update last on disk, so we use this class
27 '''the changelog index must be updated last on disk, so we use this class
28 to delay writes to it'''
28 to delay writes to it'''
29 def __init__(self, fp, buf):
29 def __init__(self, fp, buf):
30 self.data = buf
30 self.data = buf
31 self.fp = fp
31 self.fp = fp
32 self.offset = fp.tell()
32 self.offset = fp.tell()
33 self.size = util.fstat(fp).st_size
33 self.size = util.fstat(fp).st_size
34
34
35 def end(self):
35 def end(self):
36 return self.size + len("".join(self.data))
36 return self.size + len("".join(self.data))
37 def tell(self):
37 def tell(self):
38 return self.offset
38 return self.offset
39 def flush(self):
39 def flush(self):
40 pass
40 pass
41 def close(self):
41 def close(self):
42 self.fp.close()
42 self.fp.close()
43
43
44 def seek(self, offset, whence=0):
44 def seek(self, offset, whence=0):
45 '''virtual file offset spans real file and data'''
45 '''virtual file offset spans real file and data'''
46 if whence == 0:
46 if whence == 0:
47 self.offset = offset
47 self.offset = offset
48 elif whence == 1:
48 elif whence == 1:
49 self.offset += offset
49 self.offset += offset
50 elif whence == 2:
50 elif whence == 2:
51 self.offset = self.end() + offset
51 self.offset = self.end() + offset
52 if self.offset < self.size:
52 if self.offset < self.size:
53 self.fp.seek(self.offset)
53 self.fp.seek(self.offset)
54
54
55 def read(self, count=-1):
55 def read(self, count=-1):
56 '''only trick here is reads that span real file and data'''
56 '''only trick here is reads that span real file and data'''
57 ret = ""
57 ret = ""
58 if self.offset < self.size:
58 if self.offset < self.size:
59 s = self.fp.read(count)
59 s = self.fp.read(count)
60 ret = s
60 ret = s
61 self.offset += len(s)
61 self.offset += len(s)
62 if count > 0:
62 if count > 0:
63 count -= len(s)
63 count -= len(s)
64 if count != 0:
64 if count != 0:
65 doff = self.offset - self.size
65 doff = self.offset - self.size
66 self.data.insert(0, "".join(self.data))
66 self.data.insert(0, "".join(self.data))
67 del self.data[1:]
67 del self.data[1:]
68 s = self.data[0][doff:doff+count]
68 s = self.data[0][doff:doff+count]
69 self.offset += len(s)
69 self.offset += len(s)
70 ret += s
70 ret += s
71 return ret
71 return ret
72
72
73 def write(self, s):
73 def write(self, s):
74 self.data.append(str(s))
74 self.data.append(str(s))
75 self.offset += len(s)
75 self.offset += len(s)
76
76
77 class changelog(revlog.revlog):
77 class changelog(revlog.revlog):
78 def __init__(self, opener):
78 def __init__(self, opener):
79 revlog.revlog.__init__(self, opener, "00changelog.i")
79 revlog.revlog.__init__(self, opener, "00changelog.i")
80
80
81 def delayupdate(self):
81 def delayupdate(self):
82 "delay visibility of index updates to other readers"
82 "delay visibility of index updates to other readers"
83 self._realopener = self.opener
83 self._realopener = self.opener
84 self.opener = self._delayopener
84 self.opener = self._delayopener
85 self._delaycount = len(self)
85 self._delaycount = len(self)
86 self._delaybuf = []
86 self._delaybuf = []
87 self._delayname = None
87 self._delayname = None
88
88
89 def finalize(self, tr):
89 def finalize(self, tr):
90 "finalize index updates"
90 "finalize index updates"
91 self.opener = self._realopener
91 self.opener = self._realopener
92 # move redirected index data back into place
92 # move redirected index data back into place
93 if self._delayname:
93 if self._delayname:
94 util.rename(self._delayname + ".a", self._delayname)
94 util.rename(self._delayname + ".a", self._delayname)
95 elif self._delaybuf:
95 elif self._delaybuf:
96 fp = self.opener(self.indexfile, 'a')
96 fp = self.opener(self.indexfile, 'a')
97 fp.write("".join(self._delaybuf))
97 fp.write("".join(self._delaybuf))
98 fp.close()
98 fp.close()
99 self._delaybuf = []
99 self._delaybuf = []
100 # split when we're done
100 # split when we're done
101 self.checkinlinesize(tr)
101 self.checkinlinesize(tr)
102
102
103 def _delayopener(self, name, mode='r'):
103 def _delayopener(self, name, mode='r'):
104 fp = self._realopener(name, mode)
104 fp = self._realopener(name, mode)
105 # only divert the index
105 # only divert the index
106 if not name == self.indexfile:
106 if not name == self.indexfile:
107 return fp
107 return fp
108 # if we're doing an initial clone, divert to another file
108 # if we're doing an initial clone, divert to another file
109 if self._delaycount == 0:
109 if self._delaycount == 0:
110 self._delayname = fp.name
110 self._delayname = fp.name
111 if not len(self):
111 if not len(self):
112 # make sure to truncate the file
112 # make sure to truncate the file
113 mode = mode.replace('a', 'w')
113 mode = mode.replace('a', 'w')
114 return self._realopener(name + ".a", mode)
114 return self._realopener(name + ".a", mode)
115 # otherwise, divert to memory
115 # otherwise, divert to memory
116 return appender(fp, self._delaybuf)
116 return appender(fp, self._delaybuf)
117
117
118 def readpending(self, file):
118 def readpending(self, file):
119 r = revlog.revlog(self.opener, file)
119 r = revlog.revlog(self.opener, file)
120 self.index = r.index
120 self.index = r.index
121 self.nodemap = r.nodemap
121 self.nodemap = r.nodemap
122 self._chunkcache = r._chunkcache
122 self._chunkcache = r._chunkcache
123
123
124 def writepending(self):
124 def writepending(self):
125 "create a file containing the unfinalized state for pretxnchangegroup"
125 "create a file containing the unfinalized state for pretxnchangegroup"
126 if self._delaybuf:
126 if self._delaybuf:
127 # make a temporary copy of the index
127 # make a temporary copy of the index
128 fp1 = self._realopener(self.indexfile)
128 fp1 = self._realopener(self.indexfile)
129 fp2 = self._realopener(self.indexfile + ".a", "w")
129 fp2 = self._realopener(self.indexfile + ".a", "w")
130 fp2.write(fp1.read())
130 fp2.write(fp1.read())
131 # add pending data
131 # add pending data
132 fp2.write("".join(self._delaybuf))
132 fp2.write("".join(self._delaybuf))
133 fp2.close()
133 fp2.close()
134 # switch modes so finalize can simply rename
134 # switch modes so finalize can simply rename
135 self._delaybuf = []
135 self._delaybuf = []
136 self._delayname = fp1.name
136 self._delayname = fp1.name
137
137
138 if self._delayname:
138 if self._delayname:
139 return True
139 return True
140
140
141 return False
141 return False
142
142
143 def checkinlinesize(self, tr, fp=None):
143 def checkinlinesize(self, tr, fp=None):
144 if self.opener == self._delayopener:
144 if self.opener == self._delayopener:
145 return
145 return
146 return revlog.revlog.checkinlinesize(self, tr, fp)
146 return revlog.revlog.checkinlinesize(self, tr, fp)
147
147
148 def decode_extra(self, text):
148 def decode_extra(self, text):
149 extra = {}
149 extra = {}
150 for l in text.split('\0'):
150 for l in text.split('\0'):
151 if l:
151 if l:
152 k, v = l.decode('string_escape').split(':', 1)
152 k, v = l.decode('string_escape').split(':', 1)
153 extra[k] = v
153 extra[k] = v
154 return extra
154 return extra
155
155
156 def encode_extra(self, d):
156 def encode_extra(self, d):
157 # keys must be sorted to produce a deterministic changelog entry
157 # keys must be sorted to produce a deterministic changelog entry
158 items = [_string_escape('%s:%s' % (k, d[k])) for k in util.sort(d)]
158 items = [_string_escape('%s:%s' % (k, d[k])) for k in util.sort(d)]
159 return "\0".join(items)
159 return "\0".join(items)
160
160
161 def read(self, node):
161 def read(self, node):
162 """
162 """
163 format used:
163 format used:
164 nodeid\n : manifest node in ascii
164 nodeid\n : manifest node in ascii
165 user\n : user, no \n or \r allowed
165 user\n : user, no \n or \r allowed
166 time tz extra\n : date (time is int or float, timezone is int)
166 time tz extra\n : date (time is int or float, timezone is int)
167 : extra is metadatas, encoded and separated by '\0'
167 : extra is metadatas, encoded and separated by '\0'
168 : older versions ignore it
168 : older versions ignore it
169 files\n\n : files modified by the cset, no \n or \r allowed
169 files\n\n : files modified by the cset, no \n or \r allowed
170 (.*) : comment (free text, ideally utf-8)
170 (.*) : comment (free text, ideally utf-8)
171
171
172 changelog v0 doesn't use extra
172 changelog v0 doesn't use extra
173 """
173 """
174 text = self.revision(node)
174 text = self.revision(node)
175 if not text:
175 if not text:
176 return (nullid, "", (0, 0), [], "", {'branch': 'default'})
176 return (nullid, "", (0, 0), [], "", {'branch': 'default'})
177 last = text.index("\n\n")
177 last = text.index("\n\n")
178 desc = util.tolocal(text[last + 2:])
178 desc = util.tolocal(text[last + 2:])
179 l = text[:last].split('\n')
179 l = text[:last].split('\n')
180 manifest = bin(l[0])
180 manifest = bin(l[0])
181 user = util.tolocal(l[1])
181 user = util.tolocal(l[1])
182
182
183 extra_data = l[2].split(' ', 2)
183 extra_data = l[2].split(' ', 2)
184 if len(extra_data) != 3:
184 if len(extra_data) != 3:
185 time = float(extra_data.pop(0))
185 time = float(extra_data.pop(0))
186 try:
186 try:
187 # various tools did silly things with the time zone field.
187 # various tools did silly things with the time zone field.
188 timezone = int(extra_data[0])
188 timezone = int(extra_data[0])
189 except:
189 except:
190 timezone = 0
190 timezone = 0
191 extra = {}
191 extra = {}
192 else:
192 else:
193 time, timezone, extra = extra_data
193 time, timezone, extra = extra_data
194 time, timezone = float(time), int(timezone)
194 time, timezone = float(time), int(timezone)
195 extra = self.decode_extra(extra)
195 extra = self.decode_extra(extra)
196 if not extra.get('branch'):
196 if not extra.get('branch'):
197 extra['branch'] = 'default'
197 extra['branch'] = 'default'
198 files = l[3:]
198 files = l[3:]
199 return (manifest, user, (time, timezone), files, desc, extra)
199 return (manifest, user, (time, timezone), files, desc, extra)
200
200
201 def add(self, manifest, files, desc, transaction, p1=None, p2=None,
201 def add(self, manifest, files, desc, transaction, p1=None, p2=None,
202 user=None, date=None, extra={}):
202 user=None, date=None, extra={}):
203
203
204 user = user.strip()
204 user = user.strip()
205 if "\n" in user:
205 if "\n" in user:
206 raise error.RevlogError(_("username %s contains a newline")
206 raise error.RevlogError(_("username %s contains a newline")
207 % repr(user))
207 % repr(user))
208 user, desc = util.fromlocal(user), util.fromlocal(desc)
208 user, desc = util.fromlocal(user), util.fromlocal(desc)
209
209
210 if date:
210 if date:
211 parseddate = "%d %d" % util.parsedate(date)
211 parseddate = "%d %d" % util.parsedate(date)
212 else:
212 else:
213 parseddate = "%d %d" % util.makedate()
213 parseddate = "%d %d" % util.makedate()
214 if extra and extra.get("branch") in ("default", ""):
214 if extra and extra.get("branch") in ("default", ""):
215 del extra["branch"]
215 del extra["branch"]
216 if extra:
216 if extra:
217 extra = self.encode_extra(extra)
217 extra = self.encode_extra(extra)
218 parseddate = "%s %s" % (parseddate, extra)
218 parseddate = "%s %s" % (parseddate, extra)
219 l = [hex(manifest), user, parseddate] + util.sort(files) + ["", desc]
219 l = [hex(manifest), user, parseddate] + util.sort(files) + ["", desc]
220 text = "\n".join(l)
220 text = "\n".join(l)
221 return self.addrevision(text, transaction, len(self), p1, p2)
221 return self.addrevision(text, transaction, len(self), p1, p2)
@@ -1,1187 +1,1187 b''
1 # cmdutil.py - help for command processing in mercurial
1 # cmdutil.py - help for command processing in 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
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from node import hex, nullid, nullrev, short
8 from node import hex, nullid, nullrev, short
9 from i18n import _
9 from i18n import _
10 import os, sys, bisect, stat
10 import os, sys, bisect, stat
11 import mdiff, bdiff, util, templater, templatefilters, patch, errno, error
11 import mdiff, bdiff, util, templater, templatefilters, patch, errno, error
12 import match as _match
12 import match as _match
13
13
14 revrangesep = ':'
14 revrangesep = ':'
15
15
16 def findpossible(cmd, table, strict=False):
16 def findpossible(cmd, table, strict=False):
17 """
17 """
18 Return cmd -> (aliases, command table entry)
18 Return cmd -> (aliases, command table entry)
19 for each matching command.
19 for each matching command.
20 Return debug commands (or their aliases) only if no normal command matches.
20 Return debug commands (or their aliases) only if no normal command matches.
21 """
21 """
22 choice = {}
22 choice = {}
23 debugchoice = {}
23 debugchoice = {}
24 for e in table.keys():
24 for e in table.keys():
25 aliases = e.lstrip("^").split("|")
25 aliases = e.lstrip("^").split("|")
26 found = None
26 found = None
27 if cmd in aliases:
27 if cmd in aliases:
28 found = cmd
28 found = cmd
29 elif not strict:
29 elif not strict:
30 for a in aliases:
30 for a in aliases:
31 if a.startswith(cmd):
31 if a.startswith(cmd):
32 found = a
32 found = a
33 break
33 break
34 if found is not None:
34 if found is not None:
35 if aliases[0].startswith("debug") or found.startswith("debug"):
35 if aliases[0].startswith("debug") or found.startswith("debug"):
36 debugchoice[found] = (aliases, table[e])
36 debugchoice[found] = (aliases, table[e])
37 else:
37 else:
38 choice[found] = (aliases, table[e])
38 choice[found] = (aliases, table[e])
39
39
40 if not choice and debugchoice:
40 if not choice and debugchoice:
41 choice = debugchoice
41 choice = debugchoice
42
42
43 return choice
43 return choice
44
44
45 def findcmd(cmd, table, strict=True):
45 def findcmd(cmd, table, strict=True):
46 """Return (aliases, command table entry) for command string."""
46 """Return (aliases, command table entry) for command string."""
47 choice = findpossible(cmd, table, strict)
47 choice = findpossible(cmd, table, strict)
48
48
49 if cmd in choice:
49 if cmd in choice:
50 return choice[cmd]
50 return choice[cmd]
51
51
52 if len(choice) > 1:
52 if len(choice) > 1:
53 clist = choice.keys()
53 clist = choice.keys()
54 clist.sort()
54 clist.sort()
55 raise error.AmbiguousCommand(cmd, clist)
55 raise error.AmbiguousCommand(cmd, clist)
56
56
57 if choice:
57 if choice:
58 return choice.values()[0]
58 return choice.values()[0]
59
59
60 raise error.UnknownCommand(cmd)
60 raise error.UnknownCommand(cmd)
61
61
62 def bail_if_changed(repo):
62 def bail_if_changed(repo):
63 if repo.dirstate.parents()[1] != nullid:
63 if repo.dirstate.parents()[1] != nullid:
64 raise util.Abort(_('outstanding uncommitted merge'))
64 raise util.Abort(_('outstanding uncommitted merge'))
65 modified, added, removed, deleted = repo.status()[:4]
65 modified, added, removed, deleted = repo.status()[:4]
66 if modified or added or removed or deleted:
66 if modified or added or removed or deleted:
67 raise util.Abort(_("outstanding uncommitted changes"))
67 raise util.Abort(_("outstanding uncommitted changes"))
68
68
69 def logmessage(opts):
69 def logmessage(opts):
70 """ get the log message according to -m and -l option """
70 """ get the log message according to -m and -l option """
71 message = opts.get('message')
71 message = opts.get('message')
72 logfile = opts.get('logfile')
72 logfile = opts.get('logfile')
73
73
74 if message and logfile:
74 if message and logfile:
75 raise util.Abort(_('options --message and --logfile are mutually '
75 raise util.Abort(_('options --message and --logfile are mutually '
76 'exclusive'))
76 'exclusive'))
77 if not message and logfile:
77 if not message and logfile:
78 try:
78 try:
79 if logfile == '-':
79 if logfile == '-':
80 message = sys.stdin.read()
80 message = sys.stdin.read()
81 else:
81 else:
82 message = open(logfile).read()
82 message = open(logfile).read()
83 except IOError, inst:
83 except IOError, inst:
84 raise util.Abort(_("can't read commit message '%s': %s") %
84 raise util.Abort(_("can't read commit message '%s': %s") %
85 (logfile, inst.strerror))
85 (logfile, inst.strerror))
86 return message
86 return message
87
87
88 def loglimit(opts):
88 def loglimit(opts):
89 """get the log limit according to option -l/--limit"""
89 """get the log limit according to option -l/--limit"""
90 limit = opts.get('limit')
90 limit = opts.get('limit')
91 if limit:
91 if limit:
92 try:
92 try:
93 limit = int(limit)
93 limit = int(limit)
94 except ValueError:
94 except ValueError:
95 raise util.Abort(_('limit must be a positive integer'))
95 raise util.Abort(_('limit must be a positive integer'))
96 if limit <= 0: raise util.Abort(_('limit must be positive'))
96 if limit <= 0: raise util.Abort(_('limit must be positive'))
97 else:
97 else:
98 limit = sys.maxint
98 limit = sys.maxint
99 return limit
99 return limit
100
100
101 def setremoteconfig(ui, opts):
101 def setremoteconfig(ui, opts):
102 "copy remote options to ui tree"
102 "copy remote options to ui tree"
103 if opts.get('ssh'):
103 if opts.get('ssh'):
104 ui.setconfig("ui", "ssh", opts['ssh'])
104 ui.setconfig("ui", "ssh", opts['ssh'])
105 if opts.get('remotecmd'):
105 if opts.get('remotecmd'):
106 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
106 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
107
107
108 def revpair(repo, revs):
108 def revpair(repo, revs):
109 '''return pair of nodes, given list of revisions. second item can
109 '''return pair of nodes, given list of revisions. second item can
110 be None, meaning use working dir.'''
110 be None, meaning use working dir.'''
111
111
112 def revfix(repo, val, defval):
112 def revfix(repo, val, defval):
113 if not val and val != 0 and defval is not None:
113 if not val and val != 0 and defval is not None:
114 val = defval
114 val = defval
115 return repo.lookup(val)
115 return repo.lookup(val)
116
116
117 if not revs:
117 if not revs:
118 return repo.dirstate.parents()[0], None
118 return repo.dirstate.parents()[0], None
119 end = None
119 end = None
120 if len(revs) == 1:
120 if len(revs) == 1:
121 if revrangesep in revs[0]:
121 if revrangesep in revs[0]:
122 start, end = revs[0].split(revrangesep, 1)
122 start, end = revs[0].split(revrangesep, 1)
123 start = revfix(repo, start, 0)
123 start = revfix(repo, start, 0)
124 end = revfix(repo, end, len(repo) - 1)
124 end = revfix(repo, end, len(repo) - 1)
125 else:
125 else:
126 start = revfix(repo, revs[0], None)
126 start = revfix(repo, revs[0], None)
127 elif len(revs) == 2:
127 elif len(revs) == 2:
128 if revrangesep in revs[0] or revrangesep in revs[1]:
128 if revrangesep in revs[0] or revrangesep in revs[1]:
129 raise util.Abort(_('too many revisions specified'))
129 raise util.Abort(_('too many revisions specified'))
130 start = revfix(repo, revs[0], None)
130 start = revfix(repo, revs[0], None)
131 end = revfix(repo, revs[1], None)
131 end = revfix(repo, revs[1], None)
132 else:
132 else:
133 raise util.Abort(_('too many revisions specified'))
133 raise util.Abort(_('too many revisions specified'))
134 return start, end
134 return start, end
135
135
136 def revrange(repo, revs):
136 def revrange(repo, revs):
137 """Yield revision as strings from a list of revision specifications."""
137 """Yield revision as strings from a list of revision specifications."""
138
138
139 def revfix(repo, val, defval):
139 def revfix(repo, val, defval):
140 if not val and val != 0 and defval is not None:
140 if not val and val != 0 and defval is not None:
141 return defval
141 return defval
142 return repo.changelog.rev(repo.lookup(val))
142 return repo.changelog.rev(repo.lookup(val))
143
143
144 seen, l = {}, []
144 seen, l = {}, []
145 for spec in revs:
145 for spec in revs:
146 if revrangesep in spec:
146 if revrangesep in spec:
147 start, end = spec.split(revrangesep, 1)
147 start, end = spec.split(revrangesep, 1)
148 start = revfix(repo, start, 0)
148 start = revfix(repo, start, 0)
149 end = revfix(repo, end, len(repo) - 1)
149 end = revfix(repo, end, len(repo) - 1)
150 step = start > end and -1 or 1
150 step = start > end and -1 or 1
151 for rev in xrange(start, end+step, step):
151 for rev in xrange(start, end+step, step):
152 if rev in seen:
152 if rev in seen:
153 continue
153 continue
154 seen[rev] = 1
154 seen[rev] = 1
155 l.append(rev)
155 l.append(rev)
156 else:
156 else:
157 rev = revfix(repo, spec, None)
157 rev = revfix(repo, spec, None)
158 if rev in seen:
158 if rev in seen:
159 continue
159 continue
160 seen[rev] = 1
160 seen[rev] = 1
161 l.append(rev)
161 l.append(rev)
162
162
163 return l
163 return l
164
164
165 def make_filename(repo, pat, node,
165 def make_filename(repo, pat, node,
166 total=None, seqno=None, revwidth=None, pathname=None):
166 total=None, seqno=None, revwidth=None, pathname=None):
167 node_expander = {
167 node_expander = {
168 'H': lambda: hex(node),
168 'H': lambda: hex(node),
169 'R': lambda: str(repo.changelog.rev(node)),
169 'R': lambda: str(repo.changelog.rev(node)),
170 'h': lambda: short(node),
170 'h': lambda: short(node),
171 }
171 }
172 expander = {
172 expander = {
173 '%': lambda: '%',
173 '%': lambda: '%',
174 'b': lambda: os.path.basename(repo.root),
174 'b': lambda: os.path.basename(repo.root),
175 }
175 }
176
176
177 try:
177 try:
178 if node:
178 if node:
179 expander.update(node_expander)
179 expander.update(node_expander)
180 if node:
180 if node:
181 expander['r'] = (lambda:
181 expander['r'] = (lambda:
182 str(repo.changelog.rev(node)).zfill(revwidth or 0))
182 str(repo.changelog.rev(node)).zfill(revwidth or 0))
183 if total is not None:
183 if total is not None:
184 expander['N'] = lambda: str(total)
184 expander['N'] = lambda: str(total)
185 if seqno is not None:
185 if seqno is not None:
186 expander['n'] = lambda: str(seqno)
186 expander['n'] = lambda: str(seqno)
187 if total is not None and seqno is not None:
187 if total is not None and seqno is not None:
188 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
188 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
189 if pathname is not None:
189 if pathname is not None:
190 expander['s'] = lambda: os.path.basename(pathname)
190 expander['s'] = lambda: os.path.basename(pathname)
191 expander['d'] = lambda: os.path.dirname(pathname) or '.'
191 expander['d'] = lambda: os.path.dirname(pathname) or '.'
192 expander['p'] = lambda: pathname
192 expander['p'] = lambda: pathname
193
193
194 newname = []
194 newname = []
195 patlen = len(pat)
195 patlen = len(pat)
196 i = 0
196 i = 0
197 while i < patlen:
197 while i < patlen:
198 c = pat[i]
198 c = pat[i]
199 if c == '%':
199 if c == '%':
200 i += 1
200 i += 1
201 c = pat[i]
201 c = pat[i]
202 c = expander[c]()
202 c = expander[c]()
203 newname.append(c)
203 newname.append(c)
204 i += 1
204 i += 1
205 return ''.join(newname)
205 return ''.join(newname)
206 except KeyError, inst:
206 except KeyError, inst:
207 raise util.Abort(_("invalid format spec '%%%s' in output file name") %
207 raise util.Abort(_("invalid format spec '%%%s' in output file name") %
208 inst.args[0])
208 inst.args[0])
209
209
210 def make_file(repo, pat, node=None,
210 def make_file(repo, pat, node=None,
211 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
211 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
212
212
213 writable = 'w' in mode or 'a' in mode
213 writable = 'w' in mode or 'a' in mode
214
214
215 if not pat or pat == '-':
215 if not pat or pat == '-':
216 return writable and sys.stdout or sys.stdin
216 return writable and sys.stdout or sys.stdin
217 if hasattr(pat, 'write') and writable:
217 if hasattr(pat, 'write') and writable:
218 return pat
218 return pat
219 if hasattr(pat, 'read') and 'r' in mode:
219 if hasattr(pat, 'read') and 'r' in mode:
220 return pat
220 return pat
221 return open(make_filename(repo, pat, node, total, seqno, revwidth,
221 return open(make_filename(repo, pat, node, total, seqno, revwidth,
222 pathname),
222 pathname),
223 mode)
223 mode)
224
224
225 def match(repo, pats=[], opts={}, globbed=False, default='relpath'):
225 def match(repo, pats=[], opts={}, globbed=False, default='relpath'):
226 if not globbed and default == 'relpath':
226 if not globbed and default == 'relpath':
227 pats = util.expand_glob(pats or [])
227 pats = util.expand_glob(pats or [])
228 m = _match.match(repo.root, repo.getcwd(), pats,
228 m = _match.match(repo.root, repo.getcwd(), pats,
229 opts.get('include'), opts.get('exclude'), default)
229 opts.get('include'), opts.get('exclude'), default)
230 def badfn(f, msg):
230 def badfn(f, msg):
231 repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
231 repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
232 return False
232 return False
233 m.bad = badfn
233 m.bad = badfn
234 return m
234 return m
235
235
236 def matchall(repo):
236 def matchall(repo):
237 return _match.always(repo.root, repo.getcwd())
237 return _match.always(repo.root, repo.getcwd())
238
238
239 def matchfiles(repo, files):
239 def matchfiles(repo, files):
240 return _match.exact(repo.root, repo.getcwd(), files)
240 return _match.exact(repo.root, repo.getcwd(), files)
241
241
242 def findrenames(repo, added=None, removed=None, threshold=0.5):
242 def findrenames(repo, added=None, removed=None, threshold=0.5):
243 '''find renamed files -- yields (before, after, score) tuples'''
243 '''find renamed files -- yields (before, after, score) tuples'''
244 if added is None or removed is None:
244 if added is None or removed is None:
245 added, removed = repo.status()[1:3]
245 added, removed = repo.status()[1:3]
246 ctx = repo['.']
246 ctx = repo['.']
247 for a in added:
247 for a in added:
248 aa = repo.wread(a)
248 aa = repo.wread(a)
249 bestname, bestscore = None, threshold
249 bestname, bestscore = None, threshold
250 for r in removed:
250 for r in removed:
251 rr = ctx.filectx(r).data()
251 rr = ctx.filectx(r).data()
252
252
253 # bdiff.blocks() returns blocks of matching lines
253 # bdiff.blocks() returns blocks of matching lines
254 # count the number of bytes in each
254 # count the number of bytes in each
255 equal = 0
255 equal = 0
256 alines = mdiff.splitnewlines(aa)
256 alines = mdiff.splitnewlines(aa)
257 matches = bdiff.blocks(aa, rr)
257 matches = bdiff.blocks(aa, rr)
258 for x1,x2,y1,y2 in matches:
258 for x1,x2,y1,y2 in matches:
259 for line in alines[x1:x2]:
259 for line in alines[x1:x2]:
260 equal += len(line)
260 equal += len(line)
261
261
262 lengths = len(aa) + len(rr)
262 lengths = len(aa) + len(rr)
263 if lengths:
263 if lengths:
264 myscore = equal*2.0 / lengths
264 myscore = equal*2.0 / lengths
265 if myscore >= bestscore:
265 if myscore >= bestscore:
266 bestname, bestscore = r, myscore
266 bestname, bestscore = r, myscore
267 if bestname:
267 if bestname:
268 yield bestname, a, bestscore
268 yield bestname, a, bestscore
269
269
270 def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
270 def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
271 if dry_run is None:
271 if dry_run is None:
272 dry_run = opts.get('dry_run')
272 dry_run = opts.get('dry_run')
273 if similarity is None:
273 if similarity is None:
274 similarity = float(opts.get('similarity') or 0)
274 similarity = float(opts.get('similarity') or 0)
275 add, remove = [], []
275 add, remove = [], []
276 mapping = {}
276 mapping = {}
277 audit_path = util.path_auditor(repo.root)
277 audit_path = util.path_auditor(repo.root)
278 m = match(repo, pats, opts)
278 m = match(repo, pats, opts)
279 for abs in repo.walk(m):
279 for abs in repo.walk(m):
280 target = repo.wjoin(abs)
280 target = repo.wjoin(abs)
281 good = True
281 good = True
282 try:
282 try:
283 audit_path(abs)
283 audit_path(abs)
284 except:
284 except:
285 good = False
285 good = False
286 rel = m.rel(abs)
286 rel = m.rel(abs)
287 exact = m.exact(abs)
287 exact = m.exact(abs)
288 if good and abs not in repo.dirstate:
288 if good and abs not in repo.dirstate:
289 add.append(abs)
289 add.append(abs)
290 mapping[abs] = rel, m.exact(abs)
290 mapping[abs] = rel, m.exact(abs)
291 if repo.ui.verbose or not exact:
291 if repo.ui.verbose or not exact:
292 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
292 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
293 if repo.dirstate[abs] != 'r' and (not good or not util.lexists(target)
293 if repo.dirstate[abs] != 'r' and (not good or not util.lexists(target)
294 or (os.path.isdir(target) and not os.path.islink(target))):
294 or (os.path.isdir(target) and not os.path.islink(target))):
295 remove.append(abs)
295 remove.append(abs)
296 mapping[abs] = rel, exact
296 mapping[abs] = rel, exact
297 if repo.ui.verbose or not exact:
297 if repo.ui.verbose or not exact:
298 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
298 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
299 if not dry_run:
299 if not dry_run:
300 repo.remove(remove)
300 repo.remove(remove)
301 repo.add(add)
301 repo.add(add)
302 if similarity > 0:
302 if similarity > 0:
303 for old, new, score in findrenames(repo, add, remove, similarity):
303 for old, new, score in findrenames(repo, add, remove, similarity):
304 oldrel, oldexact = mapping[old]
304 oldrel, oldexact = mapping[old]
305 newrel, newexact = mapping[new]
305 newrel, newexact = mapping[new]
306 if repo.ui.verbose or not oldexact or not newexact:
306 if repo.ui.verbose or not oldexact or not newexact:
307 repo.ui.status(_('recording removal of %s as rename to %s '
307 repo.ui.status(_('recording removal of %s as rename to %s '
308 '(%d%% similar)\n') %
308 '(%d%% similar)\n') %
309 (oldrel, newrel, score * 100))
309 (oldrel, newrel, score * 100))
310 if not dry_run:
310 if not dry_run:
311 repo.copy(old, new)
311 repo.copy(old, new)
312
312
313 def copy(ui, repo, pats, opts, rename=False):
313 def copy(ui, repo, pats, opts, rename=False):
314 # called with the repo lock held
314 # called with the repo lock held
315 #
315 #
316 # hgsep => pathname that uses "/" to separate directories
316 # hgsep => pathname that uses "/" to separate directories
317 # ossep => pathname that uses os.sep to separate directories
317 # ossep => pathname that uses os.sep to separate directories
318 cwd = repo.getcwd()
318 cwd = repo.getcwd()
319 targets = {}
319 targets = {}
320 after = opts.get("after")
320 after = opts.get("after")
321 dryrun = opts.get("dry_run")
321 dryrun = opts.get("dry_run")
322
322
323 def walkpat(pat):
323 def walkpat(pat):
324 srcs = []
324 srcs = []
325 m = match(repo, [pat], opts, globbed=True)
325 m = match(repo, [pat], opts, globbed=True)
326 for abs in repo.walk(m):
326 for abs in repo.walk(m):
327 state = repo.dirstate[abs]
327 state = repo.dirstate[abs]
328 rel = m.rel(abs)
328 rel = m.rel(abs)
329 exact = m.exact(abs)
329 exact = m.exact(abs)
330 if state in '?r':
330 if state in '?r':
331 if exact and state == '?':
331 if exact and state == '?':
332 ui.warn(_('%s: not copying - file is not managed\n') % rel)
332 ui.warn(_('%s: not copying - file is not managed\n') % rel)
333 if exact and state == 'r':
333 if exact and state == 'r':
334 ui.warn(_('%s: not copying - file has been marked for'
334 ui.warn(_('%s: not copying - file has been marked for'
335 ' remove\n') % rel)
335 ' remove\n') % rel)
336 continue
336 continue
337 # abs: hgsep
337 # abs: hgsep
338 # rel: ossep
338 # rel: ossep
339 srcs.append((abs, rel, exact))
339 srcs.append((abs, rel, exact))
340 return srcs
340 return srcs
341
341
342 # abssrc: hgsep
342 # abssrc: hgsep
343 # relsrc: ossep
343 # relsrc: ossep
344 # otarget: ossep
344 # otarget: ossep
345 def copyfile(abssrc, relsrc, otarget, exact):
345 def copyfile(abssrc, relsrc, otarget, exact):
346 abstarget = util.canonpath(repo.root, cwd, otarget)
346 abstarget = util.canonpath(repo.root, cwd, otarget)
347 reltarget = repo.pathto(abstarget, cwd)
347 reltarget = repo.pathto(abstarget, cwd)
348 target = repo.wjoin(abstarget)
348 target = repo.wjoin(abstarget)
349 src = repo.wjoin(abssrc)
349 src = repo.wjoin(abssrc)
350 state = repo.dirstate[abstarget]
350 state = repo.dirstate[abstarget]
351
351
352 # check for collisions
352 # check for collisions
353 prevsrc = targets.get(abstarget)
353 prevsrc = targets.get(abstarget)
354 if prevsrc is not None:
354 if prevsrc is not None:
355 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
355 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
356 (reltarget, repo.pathto(abssrc, cwd),
356 (reltarget, repo.pathto(abssrc, cwd),
357 repo.pathto(prevsrc, cwd)))
357 repo.pathto(prevsrc, cwd)))
358 return
358 return
359
359
360 # check for overwrites
360 # check for overwrites
361 exists = os.path.exists(target)
361 exists = os.path.exists(target)
362 if (not after and exists or after and state in 'mn'):
362 if (not after and exists or after and state in 'mn'):
363 if not opts['force']:
363 if not opts['force']:
364 ui.warn(_('%s: not overwriting - file exists\n') %
364 ui.warn(_('%s: not overwriting - file exists\n') %
365 reltarget)
365 reltarget)
366 return
366 return
367
367
368 if after:
368 if after:
369 if not exists:
369 if not exists:
370 return
370 return
371 elif not dryrun:
371 elif not dryrun:
372 try:
372 try:
373 if exists:
373 if exists:
374 os.unlink(target)
374 os.unlink(target)
375 targetdir = os.path.dirname(target) or '.'
375 targetdir = os.path.dirname(target) or '.'
376 if not os.path.isdir(targetdir):
376 if not os.path.isdir(targetdir):
377 os.makedirs(targetdir)
377 os.makedirs(targetdir)
378 util.copyfile(src, target)
378 util.copyfile(src, target)
379 except IOError, inst:
379 except IOError, inst:
380 if inst.errno == errno.ENOENT:
380 if inst.errno == errno.ENOENT:
381 ui.warn(_('%s: deleted in working copy\n') % relsrc)
381 ui.warn(_('%s: deleted in working copy\n') % relsrc)
382 else:
382 else:
383 ui.warn(_('%s: cannot copy - %s\n') %
383 ui.warn(_('%s: cannot copy - %s\n') %
384 (relsrc, inst.strerror))
384 (relsrc, inst.strerror))
385 return True # report a failure
385 return True # report a failure
386
386
387 if ui.verbose or not exact:
387 if ui.verbose or not exact:
388 action = rename and "moving" or "copying"
388 action = rename and "moving" or "copying"
389 ui.status(_('%s %s to %s\n') % (action, relsrc, reltarget))
389 ui.status(_('%s %s to %s\n') % (action, relsrc, reltarget))
390
390
391 targets[abstarget] = abssrc
391 targets[abstarget] = abssrc
392
392
393 # fix up dirstate
393 # fix up dirstate
394 origsrc = repo.dirstate.copied(abssrc) or abssrc
394 origsrc = repo.dirstate.copied(abssrc) or abssrc
395 if abstarget == origsrc: # copying back a copy?
395 if abstarget == origsrc: # copying back a copy?
396 if state not in 'mn' and not dryrun:
396 if state not in 'mn' and not dryrun:
397 repo.dirstate.normallookup(abstarget)
397 repo.dirstate.normallookup(abstarget)
398 else:
398 else:
399 if repo.dirstate[origsrc] == 'a' and origsrc == abssrc:
399 if repo.dirstate[origsrc] == 'a' and origsrc == abssrc:
400 if not ui.quiet:
400 if not ui.quiet:
401 ui.warn(_("%s has not been committed yet, so no copy "
401 ui.warn(_("%s has not been committed yet, so no copy "
402 "data will be stored for %s.\n")
402 "data will be stored for %s.\n")
403 % (repo.pathto(origsrc, cwd), reltarget))
403 % (repo.pathto(origsrc, cwd), reltarget))
404 if repo.dirstate[abstarget] in '?r' and not dryrun:
404 if repo.dirstate[abstarget] in '?r' and not dryrun:
405 repo.add([abstarget])
405 repo.add([abstarget])
406 elif not dryrun:
406 elif not dryrun:
407 repo.copy(origsrc, abstarget)
407 repo.copy(origsrc, abstarget)
408
408
409 if rename and not dryrun:
409 if rename and not dryrun:
410 repo.remove([abssrc], not after)
410 repo.remove([abssrc], not after)
411
411
412 # pat: ossep
412 # pat: ossep
413 # dest ossep
413 # dest ossep
414 # srcs: list of (hgsep, hgsep, ossep, bool)
414 # srcs: list of (hgsep, hgsep, ossep, bool)
415 # return: function that takes hgsep and returns ossep
415 # return: function that takes hgsep and returns ossep
416 def targetpathfn(pat, dest, srcs):
416 def targetpathfn(pat, dest, srcs):
417 if os.path.isdir(pat):
417 if os.path.isdir(pat):
418 abspfx = util.canonpath(repo.root, cwd, pat)
418 abspfx = util.canonpath(repo.root, cwd, pat)
419 abspfx = util.localpath(abspfx)
419 abspfx = util.localpath(abspfx)
420 if destdirexists:
420 if destdirexists:
421 striplen = len(os.path.split(abspfx)[0])
421 striplen = len(os.path.split(abspfx)[0])
422 else:
422 else:
423 striplen = len(abspfx)
423 striplen = len(abspfx)
424 if striplen:
424 if striplen:
425 striplen += len(os.sep)
425 striplen += len(os.sep)
426 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
426 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
427 elif destdirexists:
427 elif destdirexists:
428 res = lambda p: os.path.join(dest,
428 res = lambda p: os.path.join(dest,
429 os.path.basename(util.localpath(p)))
429 os.path.basename(util.localpath(p)))
430 else:
430 else:
431 res = lambda p: dest
431 res = lambda p: dest
432 return res
432 return res
433
433
434 # pat: ossep
434 # pat: ossep
435 # dest ossep
435 # dest ossep
436 # srcs: list of (hgsep, hgsep, ossep, bool)
436 # srcs: list of (hgsep, hgsep, ossep, bool)
437 # return: function that takes hgsep and returns ossep
437 # return: function that takes hgsep and returns ossep
438 def targetpathafterfn(pat, dest, srcs):
438 def targetpathafterfn(pat, dest, srcs):
439 if util.patkind(pat, None)[0]:
439 if util.patkind(pat, None)[0]:
440 # a mercurial pattern
440 # a mercurial pattern
441 res = lambda p: os.path.join(dest,
441 res = lambda p: os.path.join(dest,
442 os.path.basename(util.localpath(p)))
442 os.path.basename(util.localpath(p)))
443 else:
443 else:
444 abspfx = util.canonpath(repo.root, cwd, pat)
444 abspfx = util.canonpath(repo.root, cwd, pat)
445 if len(abspfx) < len(srcs[0][0]):
445 if len(abspfx) < len(srcs[0][0]):
446 # A directory. Either the target path contains the last
446 # A directory. Either the target path contains the last
447 # component of the source path or it does not.
447 # component of the source path or it does not.
448 def evalpath(striplen):
448 def evalpath(striplen):
449 score = 0
449 score = 0
450 for s in srcs:
450 for s in srcs:
451 t = os.path.join(dest, util.localpath(s[0])[striplen:])
451 t = os.path.join(dest, util.localpath(s[0])[striplen:])
452 if os.path.exists(t):
452 if os.path.exists(t):
453 score += 1
453 score += 1
454 return score
454 return score
455
455
456 abspfx = util.localpath(abspfx)
456 abspfx = util.localpath(abspfx)
457 striplen = len(abspfx)
457 striplen = len(abspfx)
458 if striplen:
458 if striplen:
459 striplen += len(os.sep)
459 striplen += len(os.sep)
460 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
460 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
461 score = evalpath(striplen)
461 score = evalpath(striplen)
462 striplen1 = len(os.path.split(abspfx)[0])
462 striplen1 = len(os.path.split(abspfx)[0])
463 if striplen1:
463 if striplen1:
464 striplen1 += len(os.sep)
464 striplen1 += len(os.sep)
465 if evalpath(striplen1) > score:
465 if evalpath(striplen1) > score:
466 striplen = striplen1
466 striplen = striplen1
467 res = lambda p: os.path.join(dest,
467 res = lambda p: os.path.join(dest,
468 util.localpath(p)[striplen:])
468 util.localpath(p)[striplen:])
469 else:
469 else:
470 # a file
470 # a file
471 if destdirexists:
471 if destdirexists:
472 res = lambda p: os.path.join(dest,
472 res = lambda p: os.path.join(dest,
473 os.path.basename(util.localpath(p)))
473 os.path.basename(util.localpath(p)))
474 else:
474 else:
475 res = lambda p: dest
475 res = lambda p: dest
476 return res
476 return res
477
477
478
478
479 pats = util.expand_glob(pats)
479 pats = util.expand_glob(pats)
480 if not pats:
480 if not pats:
481 raise util.Abort(_('no source or destination specified'))
481 raise util.Abort(_('no source or destination specified'))
482 if len(pats) == 1:
482 if len(pats) == 1:
483 raise util.Abort(_('no destination specified'))
483 raise util.Abort(_('no destination specified'))
484 dest = pats.pop()
484 dest = pats.pop()
485 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
485 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
486 if not destdirexists:
486 if not destdirexists:
487 if len(pats) > 1 or util.patkind(pats[0], None)[0]:
487 if len(pats) > 1 or util.patkind(pats[0], None)[0]:
488 raise util.Abort(_('with multiple sources, destination must be an '
488 raise util.Abort(_('with multiple sources, destination must be an '
489 'existing directory'))
489 'existing directory'))
490 if util.endswithsep(dest):
490 if util.endswithsep(dest):
491 raise util.Abort(_('destination %s is not a directory') % dest)
491 raise util.Abort(_('destination %s is not a directory') % dest)
492
492
493 tfn = targetpathfn
493 tfn = targetpathfn
494 if after:
494 if after:
495 tfn = targetpathafterfn
495 tfn = targetpathafterfn
496 copylist = []
496 copylist = []
497 for pat in pats:
497 for pat in pats:
498 srcs = walkpat(pat)
498 srcs = walkpat(pat)
499 if not srcs:
499 if not srcs:
500 continue
500 continue
501 copylist.append((tfn(pat, dest, srcs), srcs))
501 copylist.append((tfn(pat, dest, srcs), srcs))
502 if not copylist:
502 if not copylist:
503 raise util.Abort(_('no files to copy'))
503 raise util.Abort(_('no files to copy'))
504
504
505 errors = 0
505 errors = 0
506 for targetpath, srcs in copylist:
506 for targetpath, srcs in copylist:
507 for abssrc, relsrc, exact in srcs:
507 for abssrc, relsrc, exact in srcs:
508 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
508 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
509 errors += 1
509 errors += 1
510
510
511 if errors:
511 if errors:
512 ui.warn(_('(consider using --after)\n'))
512 ui.warn(_('(consider using --after)\n'))
513
513
514 return errors
514 return errors
515
515
516 def service(opts, parentfn=None, initfn=None, runfn=None):
516 def service(opts, parentfn=None, initfn=None, runfn=None):
517 '''Run a command as a service.'''
517 '''Run a command as a service.'''
518
518
519 if opts['daemon'] and not opts['daemon_pipefds']:
519 if opts['daemon'] and not opts['daemon_pipefds']:
520 rfd, wfd = os.pipe()
520 rfd, wfd = os.pipe()
521 args = sys.argv[:]
521 args = sys.argv[:]
522 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
522 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
523 # Don't pass --cwd to the child process, because we've already
523 # Don't pass --cwd to the child process, because we've already
524 # changed directory.
524 # changed directory.
525 for i in xrange(1,len(args)):
525 for i in xrange(1,len(args)):
526 if args[i].startswith('--cwd='):
526 if args[i].startswith('--cwd='):
527 del args[i]
527 del args[i]
528 break
528 break
529 elif args[i].startswith('--cwd'):
529 elif args[i].startswith('--cwd'):
530 del args[i:i+2]
530 del args[i:i+2]
531 break
531 break
532 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
532 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
533 args[0], args)
533 args[0], args)
534 os.close(wfd)
534 os.close(wfd)
535 os.read(rfd, 1)
535 os.read(rfd, 1)
536 if parentfn:
536 if parentfn:
537 return parentfn(pid)
537 return parentfn(pid)
538 else:
538 else:
539 os._exit(0)
539 os._exit(0)
540
540
541 if initfn:
541 if initfn:
542 initfn()
542 initfn()
543
543
544 if opts['pid_file']:
544 if opts['pid_file']:
545 fp = open(opts['pid_file'], 'w')
545 fp = open(opts['pid_file'], 'w')
546 fp.write(str(os.getpid()) + '\n')
546 fp.write(str(os.getpid()) + '\n')
547 fp.close()
547 fp.close()
548
548
549 if opts['daemon_pipefds']:
549 if opts['daemon_pipefds']:
550 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
550 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
551 os.close(rfd)
551 os.close(rfd)
552 try:
552 try:
553 os.setsid()
553 os.setsid()
554 except AttributeError:
554 except AttributeError:
555 pass
555 pass
556 os.write(wfd, 'y')
556 os.write(wfd, 'y')
557 os.close(wfd)
557 os.close(wfd)
558 sys.stdout.flush()
558 sys.stdout.flush()
559 sys.stderr.flush()
559 sys.stderr.flush()
560 fd = os.open(util.nulldev, os.O_RDWR)
560 fd = os.open(util.nulldev, os.O_RDWR)
561 if fd != 0: os.dup2(fd, 0)
561 if fd != 0: os.dup2(fd, 0)
562 if fd != 1: os.dup2(fd, 1)
562 if fd != 1: os.dup2(fd, 1)
563 if fd != 2: os.dup2(fd, 2)
563 if fd != 2: os.dup2(fd, 2)
564 if fd not in (0, 1, 2): os.close(fd)
564 if fd not in (0, 1, 2): os.close(fd)
565
565
566 if runfn:
566 if runfn:
567 return runfn()
567 return runfn()
568
568
569 class changeset_printer(object):
569 class changeset_printer(object):
570 '''show changeset information when templating not requested.'''
570 '''show changeset information when templating not requested.'''
571
571
572 def __init__(self, ui, repo, patch, diffopts, buffered):
572 def __init__(self, ui, repo, patch, diffopts, buffered):
573 self.ui = ui
573 self.ui = ui
574 self.repo = repo
574 self.repo = repo
575 self.buffered = buffered
575 self.buffered = buffered
576 self.patch = patch
576 self.patch = patch
577 self.diffopts = diffopts
577 self.diffopts = diffopts
578 self.header = {}
578 self.header = {}
579 self.hunk = {}
579 self.hunk = {}
580 self.lastheader = None
580 self.lastheader = None
581
581
582 def flush(self, rev):
582 def flush(self, rev):
583 if rev in self.header:
583 if rev in self.header:
584 h = self.header[rev]
584 h = self.header[rev]
585 if h != self.lastheader:
585 if h != self.lastheader:
586 self.lastheader = h
586 self.lastheader = h
587 self.ui.write(h)
587 self.ui.write(h)
588 del self.header[rev]
588 del self.header[rev]
589 if rev in self.hunk:
589 if rev in self.hunk:
590 self.ui.write(self.hunk[rev])
590 self.ui.write(self.hunk[rev])
591 del self.hunk[rev]
591 del self.hunk[rev]
592 return 1
592 return 1
593 return 0
593 return 0
594
594
595 def show(self, ctx, copies=(), **props):
595 def show(self, ctx, copies=(), **props):
596 if self.buffered:
596 if self.buffered:
597 self.ui.pushbuffer()
597 self.ui.pushbuffer()
598 self._show(ctx, copies, props)
598 self._show(ctx, copies, props)
599 self.hunk[ctx.rev()] = self.ui.popbuffer()
599 self.hunk[ctx.rev()] = self.ui.popbuffer()
600 else:
600 else:
601 self._show(ctx, copies, props)
601 self._show(ctx, copies, props)
602
602
603 def _show(self, ctx, copies, props):
603 def _show(self, ctx, copies, props):
604 '''show a single changeset or file revision'''
604 '''show a single changeset or file revision'''
605 changenode = ctx.node()
605 changenode = ctx.node()
606 rev = ctx.rev()
606 rev = ctx.rev()
607
607
608 if self.ui.quiet:
608 if self.ui.quiet:
609 self.ui.write("%d:%s\n" % (rev, short(changenode)))
609 self.ui.write("%d:%s\n" % (rev, short(changenode)))
610 return
610 return
611
611
612 log = self.repo.changelog
612 log = self.repo.changelog
613 changes = log.read(changenode)
613 changes = log.read(changenode)
614 date = util.datestr(changes[2])
614 date = util.datestr(changes[2])
615 extra = changes[5]
615 extra = changes[5]
616 branch = extra.get("branch")
616 branch = extra.get("branch")
617
617
618 hexfunc = self.ui.debugflag and hex or short
618 hexfunc = self.ui.debugflag and hex or short
619
619
620 parents = [(p, hexfunc(log.node(p)))
620 parents = [(p, hexfunc(log.node(p)))
621 for p in self._meaningful_parentrevs(log, rev)]
621 for p in self._meaningful_parentrevs(log, rev)]
622
622
623 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
623 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
624
624
625 # don't show the default branch name
625 # don't show the default branch name
626 if branch != 'default':
626 if branch != 'default':
627 branch = util.tolocal(branch)
627 branch = util.tolocal(branch)
628 self.ui.write(_("branch: %s\n") % branch)
628 self.ui.write(_("branch: %s\n") % branch)
629 for tag in self.repo.nodetags(changenode):
629 for tag in self.repo.nodetags(changenode):
630 self.ui.write(_("tag: %s\n") % tag)
630 self.ui.write(_("tag: %s\n") % tag)
631 for parent in parents:
631 for parent in parents:
632 self.ui.write(_("parent: %d:%s\n") % parent)
632 self.ui.write(_("parent: %d:%s\n") % parent)
633
633
634 if self.ui.debugflag:
634 if self.ui.debugflag:
635 self.ui.write(_("manifest: %d:%s\n") %
635 self.ui.write(_("manifest: %d:%s\n") %
636 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
636 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
637 self.ui.write(_("user: %s\n") % changes[1])
637 self.ui.write(_("user: %s\n") % changes[1])
638 self.ui.write(_("date: %s\n") % date)
638 self.ui.write(_("date: %s\n") % date)
639
639
640 if self.ui.debugflag:
640 if self.ui.debugflag:
641 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
641 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
642 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
642 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
643 files):
643 files):
644 if value:
644 if value:
645 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
645 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
646 elif changes[3] and self.ui.verbose:
646 elif changes[3] and self.ui.verbose:
647 self.ui.write(_("files: %s\n") % " ".join(changes[3]))
647 self.ui.write(_("files: %s\n") % " ".join(changes[3]))
648 if copies and self.ui.verbose:
648 if copies and self.ui.verbose:
649 copies = ['%s (%s)' % c for c in copies]
649 copies = ['%s (%s)' % c for c in copies]
650 self.ui.write(_("copies: %s\n") % ' '.join(copies))
650 self.ui.write(_("copies: %s\n") % ' '.join(copies))
651
651
652 if extra and self.ui.debugflag:
652 if extra and self.ui.debugflag:
653 for key, value in util.sort(extra.items()):
653 for key, value in util.sort(extra.items()):
654 self.ui.write(_("extra: %s=%s\n")
654 self.ui.write(_("extra: %s=%s\n")
655 % (key, value.encode('string_escape')))
655 % (key, value.encode('string_escape')))
656
656
657 description = changes[4].strip()
657 description = changes[4].strip()
658 if description:
658 if description:
659 if self.ui.verbose:
659 if self.ui.verbose:
660 self.ui.write(_("description:\n"))
660 self.ui.write(_("description:\n"))
661 self.ui.write(description)
661 self.ui.write(description)
662 self.ui.write("\n\n")
662 self.ui.write("\n\n")
663 else:
663 else:
664 self.ui.write(_("summary: %s\n") %
664 self.ui.write(_("summary: %s\n") %
665 description.splitlines()[0])
665 description.splitlines()[0])
666 self.ui.write("\n")
666 self.ui.write("\n")
667
667
668 self.showpatch(changenode)
668 self.showpatch(changenode)
669
669
670 def showpatch(self, node):
670 def showpatch(self, node):
671 if self.patch:
671 if self.patch:
672 prev = self.repo.changelog.parents(node)[0]
672 prev = self.repo.changelog.parents(node)[0]
673 chunks = patch.diff(self.repo, prev, node, match=self.patch,
673 chunks = patch.diff(self.repo, prev, node, match=self.patch,
674 opts=patch.diffopts(self.ui, self.diffopts))
674 opts=patch.diffopts(self.ui, self.diffopts))
675 for chunk in chunks:
675 for chunk in chunks:
676 self.ui.write(chunk)
676 self.ui.write(chunk)
677 self.ui.write("\n")
677 self.ui.write("\n")
678
678
679 def _meaningful_parentrevs(self, log, rev):
679 def _meaningful_parentrevs(self, log, rev):
680 """Return list of meaningful (or all if debug) parentrevs for rev.
680 """Return list of meaningful (or all if debug) parentrevs for rev.
681
681
682 For merges (two non-nullrev revisions) both parents are meaningful.
682 For merges (two non-nullrev revisions) both parents are meaningful.
683 Otherwise the first parent revision is considered meaningful if it
683 Otherwise the first parent revision is considered meaningful if it
684 is not the preceding revision.
684 is not the preceding revision.
685 """
685 """
686 parents = log.parentrevs(rev)
686 parents = log.parentrevs(rev)
687 if not self.ui.debugflag and parents[1] == nullrev:
687 if not self.ui.debugflag and parents[1] == nullrev:
688 if parents[0] >= rev - 1:
688 if parents[0] >= rev - 1:
689 parents = []
689 parents = []
690 else:
690 else:
691 parents = [parents[0]]
691 parents = [parents[0]]
692 return parents
692 return parents
693
693
694
694
695 class changeset_templater(changeset_printer):
695 class changeset_templater(changeset_printer):
696 '''format changeset information.'''
696 '''format changeset information.'''
697
697
698 def __init__(self, ui, repo, patch, diffopts, mapfile, buffered):
698 def __init__(self, ui, repo, patch, diffopts, mapfile, buffered):
699 changeset_printer.__init__(self, ui, repo, patch, diffopts, buffered)
699 changeset_printer.__init__(self, ui, repo, patch, diffopts, buffered)
700 filters = templatefilters.filters.copy()
700 filters = templatefilters.filters.copy()
701 filters['formatnode'] = (ui.debugflag and (lambda x: x)
701 filters['formatnode'] = (ui.debugflag and (lambda x: x)
702 or (lambda x: x[:12]))
702 or (lambda x: x[:12]))
703 self.t = templater.templater(mapfile, filters,
703 self.t = templater.templater(mapfile, filters,
704 cache={
704 cache={
705 'parent': '{rev}:{node|formatnode} ',
705 'parent': '{rev}:{node|formatnode} ',
706 'manifest': '{rev}:{node|formatnode}',
706 'manifest': '{rev}:{node|formatnode}',
707 'filecopy': '{name} ({source})'})
707 'filecopy': '{name} ({source})'})
708
708
709 def use_template(self, t):
709 def use_template(self, t):
710 '''set template string to use'''
710 '''set template string to use'''
711 self.t.cache['changeset'] = t
711 self.t.cache['changeset'] = t
712
712
713 def _show(self, ctx, copies, props):
713 def _show(self, ctx, copies, props):
714 '''show a single changeset or file revision'''
714 '''show a single changeset or file revision'''
715 changenode = ctx.node()
715 changenode = ctx.node()
716 rev = ctx.rev()
716 rev = ctx.rev()
717
717
718 log = self.repo.changelog
718 log = self.repo.changelog
719 changes = log.read(changenode)
719 changes = log.read(changenode)
720
720
721 def showlist(name, values, plural=None, **args):
721 def showlist(name, values, plural=None, **args):
722 '''expand set of values.
722 '''expand set of values.
723 name is name of key in template map.
723 name is name of key in template map.
724 values is list of strings or dicts.
724 values is list of strings or dicts.
725 plural is plural of name, if not simply name + 's'.
725 plural is plural of name, if not simply name + 's'.
726
726
727 expansion works like this, given name 'foo'.
727 expansion works like this, given name 'foo'.
728
728
729 if values is empty, expand 'no_foos'.
729 if values is empty, expand 'no_foos'.
730
730
731 if 'foo' not in template map, return values as a string,
731 if 'foo' not in template map, return values as a string,
732 joined by space.
732 joined by space.
733
733
734 expand 'start_foos'.
734 expand 'start_foos'.
735
735
736 for each value, expand 'foo'. if 'last_foo' in template
736 for each value, expand 'foo'. if 'last_foo' in template
737 map, expand it instead of 'foo' for last key.
737 map, expand it instead of 'foo' for last key.
738
738
739 expand 'end_foos'.
739 expand 'end_foos'.
740 '''
740 '''
741 if plural: names = plural
741 if plural: names = plural
742 else: names = name + 's'
742 else: names = name + 's'
743 if not values:
743 if not values:
744 noname = 'no_' + names
744 noname = 'no_' + names
745 if noname in self.t:
745 if noname in self.t:
746 yield self.t(noname, **args)
746 yield self.t(noname, **args)
747 return
747 return
748 if name not in self.t:
748 if name not in self.t:
749 if isinstance(values[0], str):
749 if isinstance(values[0], str):
750 yield ' '.join(values)
750 yield ' '.join(values)
751 else:
751 else:
752 for v in values:
752 for v in values:
753 yield dict(v, **args)
753 yield dict(v, **args)
754 return
754 return
755 startname = 'start_' + names
755 startname = 'start_' + names
756 if startname in self.t:
756 if startname in self.t:
757 yield self.t(startname, **args)
757 yield self.t(startname, **args)
758 vargs = args.copy()
758 vargs = args.copy()
759 def one(v, tag=name):
759 def one(v, tag=name):
760 try:
760 try:
761 vargs.update(v)
761 vargs.update(v)
762 except (AttributeError, ValueError):
762 except (AttributeError, ValueError):
763 try:
763 try:
764 for a, b in v:
764 for a, b in v:
765 vargs[a] = b
765 vargs[a] = b
766 except ValueError:
766 except ValueError:
767 vargs[name] = v
767 vargs[name] = v
768 return self.t(tag, **vargs)
768 return self.t(tag, **vargs)
769 lastname = 'last_' + name
769 lastname = 'last_' + name
770 if lastname in self.t:
770 if lastname in self.t:
771 last = values.pop()
771 last = values.pop()
772 else:
772 else:
773 last = None
773 last = None
774 for v in values:
774 for v in values:
775 yield one(v)
775 yield one(v)
776 if last is not None:
776 if last is not None:
777 yield one(last, tag=lastname)
777 yield one(last, tag=lastname)
778 endname = 'end_' + names
778 endname = 'end_' + names
779 if endname in self.t:
779 if endname in self.t:
780 yield self.t(endname, **args)
780 yield self.t(endname, **args)
781
781
782 def showbranches(**args):
782 def showbranches(**args):
783 branch = changes[5].get("branch")
783 branch = changes[5].get("branch")
784 if branch != 'default':
784 if branch != 'default':
785 branch = util.tolocal(branch)
785 branch = util.tolocal(branch)
786 return showlist('branch', [branch], plural='branches', **args)
786 return showlist('branch', [branch], plural='branches', **args)
787
787
788 def showparents(**args):
788 def showparents(**args):
789 parents = [[('rev', p), ('node', hex(log.node(p)))]
789 parents = [[('rev', p), ('node', hex(log.node(p)))]
790 for p in self._meaningful_parentrevs(log, rev)]
790 for p in self._meaningful_parentrevs(log, rev)]
791 return showlist('parent', parents, **args)
791 return showlist('parent', parents, **args)
792
792
793 def showtags(**args):
793 def showtags(**args):
794 return showlist('tag', self.repo.nodetags(changenode), **args)
794 return showlist('tag', self.repo.nodetags(changenode), **args)
795
795
796 def showextras(**args):
796 def showextras(**args):
797 for key, value in util.sort(changes[5].items()):
797 for key, value in util.sort(changes[5].items()):
798 args = args.copy()
798 args = args.copy()
799 args.update(dict(key=key, value=value))
799 args.update(dict(key=key, value=value))
800 yield self.t('extra', **args)
800 yield self.t('extra', **args)
801
801
802 def showcopies(**args):
802 def showcopies(**args):
803 c = [{'name': x[0], 'source': x[1]} for x in copies]
803 c = [{'name': x[0], 'source': x[1]} for x in copies]
804 return showlist('file_copy', c, plural='file_copies', **args)
804 return showlist('file_copy', c, plural='file_copies', **args)
805
805
806 files = []
806 files = []
807 def getfiles():
807 def getfiles():
808 if not files:
808 if not files:
809 files[:] = self.repo.status(
809 files[:] = self.repo.status(
810 log.parents(changenode)[0], changenode)[:3]
810 log.parents(changenode)[0], changenode)[:3]
811 return files
811 return files
812 def showfiles(**args):
812 def showfiles(**args):
813 return showlist('file', changes[3], **args)
813 return showlist('file', changes[3], **args)
814 def showmods(**args):
814 def showmods(**args):
815 return showlist('file_mod', getfiles()[0], **args)
815 return showlist('file_mod', getfiles()[0], **args)
816 def showadds(**args):
816 def showadds(**args):
817 return showlist('file_add', getfiles()[1], **args)
817 return showlist('file_add', getfiles()[1], **args)
818 def showdels(**args):
818 def showdels(**args):
819 return showlist('file_del', getfiles()[2], **args)
819 return showlist('file_del', getfiles()[2], **args)
820 def showmanifest(**args):
820 def showmanifest(**args):
821 args = args.copy()
821 args = args.copy()
822 args.update(dict(rev=self.repo.manifest.rev(changes[0]),
822 args.update(dict(rev=self.repo.manifest.rev(changes[0]),
823 node=hex(changes[0])))
823 node=hex(changes[0])))
824 return self.t('manifest', **args)
824 return self.t('manifest', **args)
825
825
826 defprops = {
826 defprops = {
827 'author': changes[1],
827 'author': changes[1],
828 'branches': showbranches,
828 'branches': showbranches,
829 'date': changes[2],
829 'date': changes[2],
830 'desc': changes[4].strip(),
830 'desc': changes[4].strip(),
831 'file_adds': showadds,
831 'file_adds': showadds,
832 'file_dels': showdels,
832 'file_dels': showdels,
833 'file_mods': showmods,
833 'file_mods': showmods,
834 'files': showfiles,
834 'files': showfiles,
835 'file_copies': showcopies,
835 'file_copies': showcopies,
836 'manifest': showmanifest,
836 'manifest': showmanifest,
837 'node': hex(changenode),
837 'node': hex(changenode),
838 'parents': showparents,
838 'parents': showparents,
839 'rev': rev,
839 'rev': rev,
840 'tags': showtags,
840 'tags': showtags,
841 'extras': showextras,
841 'extras': showextras,
842 }
842 }
843 props = props.copy()
843 props = props.copy()
844 props.update(defprops)
844 props.update(defprops)
845
845
846 try:
846 try:
847 if self.ui.debugflag and 'header_debug' in self.t:
847 if self.ui.debugflag and 'header_debug' in self.t:
848 key = 'header_debug'
848 key = 'header_debug'
849 elif self.ui.quiet and 'header_quiet' in self.t:
849 elif self.ui.quiet and 'header_quiet' in self.t:
850 key = 'header_quiet'
850 key = 'header_quiet'
851 elif self.ui.verbose and 'header_verbose' in self.t:
851 elif self.ui.verbose and 'header_verbose' in self.t:
852 key = 'header_verbose'
852 key = 'header_verbose'
853 elif 'header' in self.t:
853 elif 'header' in self.t:
854 key = 'header'
854 key = 'header'
855 else:
855 else:
856 key = ''
856 key = ''
857 if key:
857 if key:
858 h = templater.stringify(self.t(key, **props))
858 h = templater.stringify(self.t(key, **props))
859 if self.buffered:
859 if self.buffered:
860 self.header[rev] = h
860 self.header[rev] = h
861 else:
861 else:
862 self.ui.write(h)
862 self.ui.write(h)
863 if self.ui.debugflag and 'changeset_debug' in self.t:
863 if self.ui.debugflag and 'changeset_debug' in self.t:
864 key = 'changeset_debug'
864 key = 'changeset_debug'
865 elif self.ui.quiet and 'changeset_quiet' in self.t:
865 elif self.ui.quiet and 'changeset_quiet' in self.t:
866 key = 'changeset_quiet'
866 key = 'changeset_quiet'
867 elif self.ui.verbose and 'changeset_verbose' in self.t:
867 elif self.ui.verbose and 'changeset_verbose' in self.t:
868 key = 'changeset_verbose'
868 key = 'changeset_verbose'
869 else:
869 else:
870 key = 'changeset'
870 key = 'changeset'
871 self.ui.write(templater.stringify(self.t(key, **props)))
871 self.ui.write(templater.stringify(self.t(key, **props)))
872 self.showpatch(changenode)
872 self.showpatch(changenode)
873 except KeyError, inst:
873 except KeyError, inst:
874 raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
874 raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
875 inst.args[0]))
875 inst.args[0]))
876 except SyntaxError, inst:
876 except SyntaxError, inst:
877 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
877 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
878
878
879 def show_changeset(ui, repo, opts, buffered=False, matchfn=False):
879 def show_changeset(ui, repo, opts, buffered=False, matchfn=False):
880 """show one changeset using template or regular display.
880 """show one changeset using template or regular display.
881
881
882 Display format will be the first non-empty hit of:
882 Display format will be the first non-empty hit of:
883 1. option 'template'
883 1. option 'template'
884 2. option 'style'
884 2. option 'style'
885 3. [ui] setting 'logtemplate'
885 3. [ui] setting 'logtemplate'
886 4. [ui] setting 'style'
886 4. [ui] setting 'style'
887 If all of these values are either the unset or the empty string,
887 If all of these values are either the unset or the empty string,
888 regular display via changeset_printer() is done.
888 regular display via changeset_printer() is done.
889 """
889 """
890 # options
890 # options
891 patch = False
891 patch = False
892 if opts.get('patch'):
892 if opts.get('patch'):
893 patch = matchfn or matchall(repo)
893 patch = matchfn or matchall(repo)
894
894
895 tmpl = opts.get('template')
895 tmpl = opts.get('template')
896 mapfile = None
896 mapfile = None
897 if tmpl:
897 if tmpl:
898 tmpl = templater.parsestring(tmpl, quoted=False)
898 tmpl = templater.parsestring(tmpl, quoted=False)
899 else:
899 else:
900 mapfile = opts.get('style')
900 mapfile = opts.get('style')
901 # ui settings
901 # ui settings
902 if not mapfile:
902 if not mapfile:
903 tmpl = ui.config('ui', 'logtemplate')
903 tmpl = ui.config('ui', 'logtemplate')
904 if tmpl:
904 if tmpl:
905 tmpl = templater.parsestring(tmpl)
905 tmpl = templater.parsestring(tmpl)
906 else:
906 else:
907 mapfile = ui.config('ui', 'style')
907 mapfile = ui.config('ui', 'style')
908
908
909 if tmpl or mapfile:
909 if tmpl or mapfile:
910 if mapfile:
910 if mapfile:
911 if not os.path.split(mapfile)[0]:
911 if not os.path.split(mapfile)[0]:
912 mapname = (templater.templatepath('map-cmdline.' + mapfile)
912 mapname = (templater.templatepath('map-cmdline.' + mapfile)
913 or templater.templatepath(mapfile))
913 or templater.templatepath(mapfile))
914 if mapname: mapfile = mapname
914 if mapname: mapfile = mapname
915 try:
915 try:
916 t = changeset_templater(ui, repo, patch, opts, mapfile, buffered)
916 t = changeset_templater(ui, repo, patch, opts, mapfile, buffered)
917 except SyntaxError, inst:
917 except SyntaxError, inst:
918 raise util.Abort(inst.args[0])
918 raise util.Abort(inst.args[0])
919 if tmpl: t.use_template(tmpl)
919 if tmpl: t.use_template(tmpl)
920 return t
920 return t
921 return changeset_printer(ui, repo, patch, opts, buffered)
921 return changeset_printer(ui, repo, patch, opts, buffered)
922
922
923 def finddate(ui, repo, date):
923 def finddate(ui, repo, date):
924 """Find the tipmost changeset that matches the given date spec"""
924 """Find the tipmost changeset that matches the given date spec"""
925 df = util.matchdate(date)
925 df = util.matchdate(date)
926 get = util.cachefunc(lambda r: repo[r].changeset())
926 get = util.cachefunc(lambda r: repo[r].changeset())
927 changeiter, matchfn = walkchangerevs(ui, repo, [], get, {'rev':None})
927 changeiter, matchfn = walkchangerevs(ui, repo, [], get, {'rev':None})
928 results = {}
928 results = {}
929 for st, rev, fns in changeiter:
929 for st, rev, fns in changeiter:
930 if st == 'add':
930 if st == 'add':
931 d = get(rev)[2]
931 d = get(rev)[2]
932 if df(d[0]):
932 if df(d[0]):
933 results[rev] = d
933 results[rev] = d
934 elif st == 'iter':
934 elif st == 'iter':
935 if rev in results:
935 if rev in results:
936 ui.status(_("Found revision %s from %s\n") %
936 ui.status(_("Found revision %s from %s\n") %
937 (rev, util.datestr(results[rev])))
937 (rev, util.datestr(results[rev])))
938 return str(rev)
938 return str(rev)
939
939
940 raise util.Abort(_("revision matching date not found"))
940 raise util.Abort(_("revision matching date not found"))
941
941
942 def walkchangerevs(ui, repo, pats, change, opts):
942 def walkchangerevs(ui, repo, pats, change, opts):
943 '''Iterate over files and the revs they changed in.
943 '''Iterate over files and the revs in which they changed.
944
944
945 Callers most commonly need to iterate backwards over the history
945 Callers most commonly need to iterate backwards over the history
946 it is interested in. Doing so has awful (quadratic-looking)
946 in which they are interested. Doing so has awful (quadratic-looking)
947 performance, so we use iterators in a "windowed" way.
947 performance, so we use iterators in a "windowed" way.
948
948
949 We walk a window of revisions in the desired order. Within the
949 We walk a window of revisions in the desired order. Within the
950 window, we first walk forwards to gather data, then in the desired
950 window, we first walk forwards to gather data, then in the desired
951 order (usually backwards) to display it.
951 order (usually backwards) to display it.
952
952
953 This function returns an (iterator, matchfn) tuple. The iterator
953 This function returns an (iterator, matchfn) tuple. The iterator
954 yields 3-tuples. They will be of one of the following forms:
954 yields 3-tuples. They will be of one of the following forms:
955
955
956 "window", incrementing, lastrev: stepping through a window,
956 "window", incrementing, lastrev: stepping through a window,
957 positive if walking forwards through revs, last rev in the
957 positive if walking forwards through revs, last rev in the
958 sequence iterated over - use to reset state for the current window
958 sequence iterated over - use to reset state for the current window
959
959
960 "add", rev, fns: out-of-order traversal of the given file names
960 "add", rev, fns: out-of-order traversal of the given file names
961 fns, which changed during revision rev - use to gather data for
961 fns, which changed during revision rev - use to gather data for
962 possible display
962 possible display
963
963
964 "iter", rev, None: in-order traversal of the revs earlier iterated
964 "iter", rev, None: in-order traversal of the revs earlier iterated
965 over with "add" - use to display data'''
965 over with "add" - use to display data'''
966
966
967 def increasing_windows(start, end, windowsize=8, sizelimit=512):
967 def increasing_windows(start, end, windowsize=8, sizelimit=512):
968 if start < end:
968 if start < end:
969 while start < end:
969 while start < end:
970 yield start, min(windowsize, end-start)
970 yield start, min(windowsize, end-start)
971 start += windowsize
971 start += windowsize
972 if windowsize < sizelimit:
972 if windowsize < sizelimit:
973 windowsize *= 2
973 windowsize *= 2
974 else:
974 else:
975 while start > end:
975 while start > end:
976 yield start, min(windowsize, start-end-1)
976 yield start, min(windowsize, start-end-1)
977 start -= windowsize
977 start -= windowsize
978 if windowsize < sizelimit:
978 if windowsize < sizelimit:
979 windowsize *= 2
979 windowsize *= 2
980
980
981 m = match(repo, pats, opts)
981 m = match(repo, pats, opts)
982 follow = opts.get('follow') or opts.get('follow_first')
982 follow = opts.get('follow') or opts.get('follow_first')
983
983
984 if not len(repo):
984 if not len(repo):
985 return [], m
985 return [], m
986
986
987 if follow:
987 if follow:
988 defrange = '%s:0' % repo['.'].rev()
988 defrange = '%s:0' % repo['.'].rev()
989 else:
989 else:
990 defrange = '-1:0'
990 defrange = '-1:0'
991 revs = revrange(repo, opts['rev'] or [defrange])
991 revs = revrange(repo, opts['rev'] or [defrange])
992 wanted = {}
992 wanted = {}
993 slowpath = m.anypats() or (m.files() and opts.get('removed'))
993 slowpath = m.anypats() or (m.files() and opts.get('removed'))
994 fncache = {}
994 fncache = {}
995
995
996 if not slowpath and not m.files():
996 if not slowpath and not m.files():
997 # No files, no patterns. Display all revs.
997 # No files, no patterns. Display all revs.
998 wanted = dict.fromkeys(revs)
998 wanted = dict.fromkeys(revs)
999 copies = []
999 copies = []
1000 if not slowpath:
1000 if not slowpath:
1001 # Only files, no patterns. Check the history of each file.
1001 # Only files, no patterns. Check the history of each file.
1002 def filerevgen(filelog, node):
1002 def filerevgen(filelog, node):
1003 cl_count = len(repo)
1003 cl_count = len(repo)
1004 if node is None:
1004 if node is None:
1005 last = len(filelog) - 1
1005 last = len(filelog) - 1
1006 else:
1006 else:
1007 last = filelog.rev(node)
1007 last = filelog.rev(node)
1008 for i, window in increasing_windows(last, nullrev):
1008 for i, window in increasing_windows(last, nullrev):
1009 revs = []
1009 revs = []
1010 for j in xrange(i - window, i + 1):
1010 for j in xrange(i - window, i + 1):
1011 n = filelog.node(j)
1011 n = filelog.node(j)
1012 revs.append((filelog.linkrev(j),
1012 revs.append((filelog.linkrev(j),
1013 follow and filelog.renamed(n)))
1013 follow and filelog.renamed(n)))
1014 revs.reverse()
1014 revs.reverse()
1015 for rev in revs:
1015 for rev in revs:
1016 # only yield rev for which we have the changelog, it can
1016 # only yield rev for which we have the changelog, it can
1017 # happen while doing "hg log" during a pull or commit
1017 # happen while doing "hg log" during a pull or commit
1018 if rev[0] < cl_count:
1018 if rev[0] < cl_count:
1019 yield rev
1019 yield rev
1020 def iterfiles():
1020 def iterfiles():
1021 for filename in m.files():
1021 for filename in m.files():
1022 yield filename, None
1022 yield filename, None
1023 for filename_node in copies:
1023 for filename_node in copies:
1024 yield filename_node
1024 yield filename_node
1025 minrev, maxrev = min(revs), max(revs)
1025 minrev, maxrev = min(revs), max(revs)
1026 for file_, node in iterfiles():
1026 for file_, node in iterfiles():
1027 filelog = repo.file(file_)
1027 filelog = repo.file(file_)
1028 if not len(filelog):
1028 if not len(filelog):
1029 if node is None:
1029 if node is None:
1030 # A zero count may be a directory or deleted file, so
1030 # A zero count may be a directory or deleted file, so
1031 # try to find matching entries on the slow path.
1031 # try to find matching entries on the slow path.
1032 if follow:
1032 if follow:
1033 raise util.Abort(_('cannot follow nonexistent file: "%s"') % file_)
1033 raise util.Abort(_('cannot follow nonexistent file: "%s"') % file_)
1034 slowpath = True
1034 slowpath = True
1035 break
1035 break
1036 else:
1036 else:
1037 ui.warn(_('%s:%s copy source revision cannot be found!\n')
1037 ui.warn(_('%s:%s copy source revision cannot be found!\n')
1038 % (file_, short(node)))
1038 % (file_, short(node)))
1039 continue
1039 continue
1040 for rev, copied in filerevgen(filelog, node):
1040 for rev, copied in filerevgen(filelog, node):
1041 if rev <= maxrev:
1041 if rev <= maxrev:
1042 if rev < minrev:
1042 if rev < minrev:
1043 break
1043 break
1044 fncache.setdefault(rev, [])
1044 fncache.setdefault(rev, [])
1045 fncache[rev].append(file_)
1045 fncache[rev].append(file_)
1046 wanted[rev] = 1
1046 wanted[rev] = 1
1047 if follow and copied:
1047 if follow and copied:
1048 copies.append(copied)
1048 copies.append(copied)
1049 if slowpath:
1049 if slowpath:
1050 if follow:
1050 if follow:
1051 raise util.Abort(_('can only follow copies/renames for explicit '
1051 raise util.Abort(_('can only follow copies/renames for explicit '
1052 'file names'))
1052 'file names'))
1053
1053
1054 # The slow path checks files modified in every changeset.
1054 # The slow path checks files modified in every changeset.
1055 def changerevgen():
1055 def changerevgen():
1056 for i, window in increasing_windows(len(repo) - 1, nullrev):
1056 for i, window in increasing_windows(len(repo) - 1, nullrev):
1057 for j in xrange(i - window, i + 1):
1057 for j in xrange(i - window, i + 1):
1058 yield j, change(j)[3]
1058 yield j, change(j)[3]
1059
1059
1060 for rev, changefiles in changerevgen():
1060 for rev, changefiles in changerevgen():
1061 matches = filter(m, changefiles)
1061 matches = filter(m, changefiles)
1062 if matches:
1062 if matches:
1063 fncache[rev] = matches
1063 fncache[rev] = matches
1064 wanted[rev] = 1
1064 wanted[rev] = 1
1065
1065
1066 class followfilter:
1066 class followfilter:
1067 def __init__(self, onlyfirst=False):
1067 def __init__(self, onlyfirst=False):
1068 self.startrev = nullrev
1068 self.startrev = nullrev
1069 self.roots = []
1069 self.roots = []
1070 self.onlyfirst = onlyfirst
1070 self.onlyfirst = onlyfirst
1071
1071
1072 def match(self, rev):
1072 def match(self, rev):
1073 def realparents(rev):
1073 def realparents(rev):
1074 if self.onlyfirst:
1074 if self.onlyfirst:
1075 return repo.changelog.parentrevs(rev)[0:1]
1075 return repo.changelog.parentrevs(rev)[0:1]
1076 else:
1076 else:
1077 return filter(lambda x: x != nullrev,
1077 return filter(lambda x: x != nullrev,
1078 repo.changelog.parentrevs(rev))
1078 repo.changelog.parentrevs(rev))
1079
1079
1080 if self.startrev == nullrev:
1080 if self.startrev == nullrev:
1081 self.startrev = rev
1081 self.startrev = rev
1082 return True
1082 return True
1083
1083
1084 if rev > self.startrev:
1084 if rev > self.startrev:
1085 # forward: all descendants
1085 # forward: all descendants
1086 if not self.roots:
1086 if not self.roots:
1087 self.roots.append(self.startrev)
1087 self.roots.append(self.startrev)
1088 for parent in realparents(rev):
1088 for parent in realparents(rev):
1089 if parent in self.roots:
1089 if parent in self.roots:
1090 self.roots.append(rev)
1090 self.roots.append(rev)
1091 return True
1091 return True
1092 else:
1092 else:
1093 # backwards: all parents
1093 # backwards: all parents
1094 if not self.roots:
1094 if not self.roots:
1095 self.roots.extend(realparents(self.startrev))
1095 self.roots.extend(realparents(self.startrev))
1096 if rev in self.roots:
1096 if rev in self.roots:
1097 self.roots.remove(rev)
1097 self.roots.remove(rev)
1098 self.roots.extend(realparents(rev))
1098 self.roots.extend(realparents(rev))
1099 return True
1099 return True
1100
1100
1101 return False
1101 return False
1102
1102
1103 # it might be worthwhile to do this in the iterator if the rev range
1103 # it might be worthwhile to do this in the iterator if the rev range
1104 # is descending and the prune args are all within that range
1104 # is descending and the prune args are all within that range
1105 for rev in opts.get('prune', ()):
1105 for rev in opts.get('prune', ()):
1106 rev = repo.changelog.rev(repo.lookup(rev))
1106 rev = repo.changelog.rev(repo.lookup(rev))
1107 ff = followfilter()
1107 ff = followfilter()
1108 stop = min(revs[0], revs[-1])
1108 stop = min(revs[0], revs[-1])
1109 for x in xrange(rev, stop-1, -1):
1109 for x in xrange(rev, stop-1, -1):
1110 if ff.match(x) and x in wanted:
1110 if ff.match(x) and x in wanted:
1111 del wanted[x]
1111 del wanted[x]
1112
1112
1113 def iterate():
1113 def iterate():
1114 if follow and not m.files():
1114 if follow and not m.files():
1115 ff = followfilter(onlyfirst=opts.get('follow_first'))
1115 ff = followfilter(onlyfirst=opts.get('follow_first'))
1116 def want(rev):
1116 def want(rev):
1117 if ff.match(rev) and rev in wanted:
1117 if ff.match(rev) and rev in wanted:
1118 return True
1118 return True
1119 return False
1119 return False
1120 else:
1120 else:
1121 def want(rev):
1121 def want(rev):
1122 return rev in wanted
1122 return rev in wanted
1123
1123
1124 for i, window in increasing_windows(0, len(revs)):
1124 for i, window in increasing_windows(0, len(revs)):
1125 yield 'window', revs[0] < revs[-1], revs[-1]
1125 yield 'window', revs[0] < revs[-1], revs[-1]
1126 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
1126 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
1127 for rev in util.sort(list(nrevs)):
1127 for rev in util.sort(list(nrevs)):
1128 fns = fncache.get(rev)
1128 fns = fncache.get(rev)
1129 if not fns:
1129 if not fns:
1130 def fns_generator():
1130 def fns_generator():
1131 for f in change(rev)[3]:
1131 for f in change(rev)[3]:
1132 if m(f):
1132 if m(f):
1133 yield f
1133 yield f
1134 fns = fns_generator()
1134 fns = fns_generator()
1135 yield 'add', rev, fns
1135 yield 'add', rev, fns
1136 for rev in nrevs:
1136 for rev in nrevs:
1137 yield 'iter', rev, None
1137 yield 'iter', rev, None
1138 return iterate(), m
1138 return iterate(), m
1139
1139
1140 def commit(ui, repo, commitfunc, pats, opts):
1140 def commit(ui, repo, commitfunc, pats, opts):
1141 '''commit the specified files or all outstanding changes'''
1141 '''commit the specified files or all outstanding changes'''
1142 date = opts.get('date')
1142 date = opts.get('date')
1143 if date:
1143 if date:
1144 opts['date'] = util.parsedate(date)
1144 opts['date'] = util.parsedate(date)
1145 message = logmessage(opts)
1145 message = logmessage(opts)
1146
1146
1147 # extract addremove carefully -- this function can be called from a command
1147 # extract addremove carefully -- this function can be called from a command
1148 # that doesn't support addremove
1148 # that doesn't support addremove
1149 if opts.get('addremove'):
1149 if opts.get('addremove'):
1150 addremove(repo, pats, opts)
1150 addremove(repo, pats, opts)
1151
1151
1152 m = match(repo, pats, opts)
1152 m = match(repo, pats, opts)
1153 if pats:
1153 if pats:
1154 modified, added, removed = repo.status(match=m)[:3]
1154 modified, added, removed = repo.status(match=m)[:3]
1155 files = util.sort(modified + added + removed)
1155 files = util.sort(modified + added + removed)
1156
1156
1157 def is_dir(f):
1157 def is_dir(f):
1158 name = f + '/'
1158 name = f + '/'
1159 i = bisect.bisect(files, name)
1159 i = bisect.bisect(files, name)
1160 return i < len(files) and files[i].startswith(name)
1160 return i < len(files) and files[i].startswith(name)
1161
1161
1162 for f in m.files():
1162 for f in m.files():
1163 if f == '.':
1163 if f == '.':
1164 continue
1164 continue
1165 if f not in files:
1165 if f not in files:
1166 rf = repo.wjoin(f)
1166 rf = repo.wjoin(f)
1167 rel = repo.pathto(f)
1167 rel = repo.pathto(f)
1168 try:
1168 try:
1169 mode = os.lstat(rf)[stat.ST_MODE]
1169 mode = os.lstat(rf)[stat.ST_MODE]
1170 except OSError:
1170 except OSError:
1171 if is_dir(f): # deleted directory ?
1171 if is_dir(f): # deleted directory ?
1172 continue
1172 continue
1173 raise util.Abort(_("file %s not found!") % rel)
1173 raise util.Abort(_("file %s not found!") % rel)
1174 if stat.S_ISDIR(mode):
1174 if stat.S_ISDIR(mode):
1175 if not is_dir(f):
1175 if not is_dir(f):
1176 raise util.Abort(_("no match under directory %s!")
1176 raise util.Abort(_("no match under directory %s!")
1177 % rel)
1177 % rel)
1178 elif not (stat.S_ISREG(mode) or stat.S_ISLNK(mode)):
1178 elif not (stat.S_ISREG(mode) or stat.S_ISLNK(mode)):
1179 raise util.Abort(_("can't commit %s: "
1179 raise util.Abort(_("can't commit %s: "
1180 "unsupported file type!") % rel)
1180 "unsupported file type!") % rel)
1181 elif f not in repo.dirstate:
1181 elif f not in repo.dirstate:
1182 raise util.Abort(_("file %s not tracked!") % rel)
1182 raise util.Abort(_("file %s not tracked!") % rel)
1183 m = matchfiles(repo, files)
1183 m = matchfiles(repo, files)
1184 try:
1184 try:
1185 return commitfunc(ui, repo, message, m, opts)
1185 return commitfunc(ui, repo, message, m, opts)
1186 except ValueError, inst:
1186 except ValueError, inst:
1187 raise util.Abort(str(inst))
1187 raise util.Abort(str(inst))
@@ -1,3416 +1,3416 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
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from node import hex, nullid, nullrev, short
8 from node import hex, nullid, nullrev, short
9 from i18n import _, gettext
9 from i18n import _, gettext
10 import os, re, sys
10 import os, re, sys
11 import hg, util, revlog, bundlerepo, extensions, copies, context, error
11 import hg, util, revlog, bundlerepo, extensions, copies, context, error
12 import difflib, patch, time, help, mdiff, tempfile, url
12 import difflib, patch, time, help, mdiff, tempfile, url
13 import archival, changegroup, cmdutil, hgweb.server, sshserver, hbisect
13 import archival, changegroup, cmdutil, hgweb.server, sshserver, hbisect
14 import merge as merge_
14 import merge as merge_
15
15
16 # Commands start here, listed alphabetically
16 # Commands start here, listed alphabetically
17
17
18 def add(ui, repo, *pats, **opts):
18 def add(ui, repo, *pats, **opts):
19 """add the specified files on the next commit
19 """add the specified files on the next commit
20
20
21 Schedule files to be version controlled and added to the repository.
21 Schedule files to be version controlled and added to the repository.
22
22
23 The files will be added to the repository at the next commit. To
23 The files will be added to the repository at the next commit. To
24 undo an add before that, see hg revert.
24 undo an add before that, see hg revert.
25
25
26 If no names are given, add all files in the repository.
26 If no names are given, add all files to the repository.
27 """
27 """
28
28
29 rejected = None
29 rejected = None
30 exacts = {}
30 exacts = {}
31 names = []
31 names = []
32 m = cmdutil.match(repo, pats, opts)
32 m = cmdutil.match(repo, pats, opts)
33 m.bad = lambda x,y: True
33 m.bad = lambda x,y: True
34 for abs in repo.walk(m):
34 for abs in repo.walk(m):
35 if m.exact(abs):
35 if m.exact(abs):
36 if ui.verbose:
36 if ui.verbose:
37 ui.status(_('adding %s\n') % m.rel(abs))
37 ui.status(_('adding %s\n') % m.rel(abs))
38 names.append(abs)
38 names.append(abs)
39 exacts[abs] = 1
39 exacts[abs] = 1
40 elif abs not in repo.dirstate:
40 elif abs not in repo.dirstate:
41 ui.status(_('adding %s\n') % m.rel(abs))
41 ui.status(_('adding %s\n') % m.rel(abs))
42 names.append(abs)
42 names.append(abs)
43 if not opts.get('dry_run'):
43 if not opts.get('dry_run'):
44 rejected = repo.add(names)
44 rejected = repo.add(names)
45 rejected = [p for p in rejected if p in exacts]
45 rejected = [p for p in rejected if p in exacts]
46 return rejected and 1 or 0
46 return rejected and 1 or 0
47
47
48 def addremove(ui, repo, *pats, **opts):
48 def addremove(ui, repo, *pats, **opts):
49 """add all new files, delete all missing files
49 """add all new files, delete all missing files
50
50
51 Add all new files and remove all missing files from the repository.
51 Add all new files and remove all missing files from the repository.
52
52
53 New files are ignored if they match any of the patterns in .hgignore. As
53 New files are ignored if they match any of the patterns in .hgignore. As
54 with add, these changes take effect at the next commit.
54 with add, these changes take effect at the next commit.
55
55
56 Use the -s option to detect renamed files. With a parameter > 0,
56 Use the -s option to detect renamed files. With a parameter > 0,
57 this compares every removed file with every added file and records
57 this compares every removed file with every added file and records
58 those similar enough as renames. This option takes a percentage
58 those similar enough as renames. This option takes a percentage
59 between 0 (disabled) and 100 (files must be identical) as its
59 between 0 (disabled) and 100 (files must be identical) as its
60 parameter. Detecting renamed files this way can be expensive.
60 parameter. Detecting renamed files this way can be expensive.
61 """
61 """
62 try:
62 try:
63 sim = float(opts.get('similarity') or 0)
63 sim = float(opts.get('similarity') or 0)
64 except ValueError:
64 except ValueError:
65 raise util.Abort(_('similarity must be a number'))
65 raise util.Abort(_('similarity must be a number'))
66 if sim < 0 or sim > 100:
66 if sim < 0 or sim > 100:
67 raise util.Abort(_('similarity must be between 0 and 100'))
67 raise util.Abort(_('similarity must be between 0 and 100'))
68 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
68 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
69
69
70 def annotate(ui, repo, *pats, **opts):
70 def annotate(ui, repo, *pats, **opts):
71 """show changeset information per file line
71 """show changeset information per file line
72
72
73 List changes in files, showing the revision id responsible for each line
73 List changes in files, showing the revision id responsible for each line
74
74
75 This command is useful to discover who did a change or when a change took
75 This command is useful to discover who did a change or when a change took
76 place.
76 place.
77
77
78 Without the -a option, annotate will avoid processing files it
78 Without the -a option, annotate will avoid processing files it
79 detects as binary. With -a, annotate will generate an annotation
79 detects as binary. With -a, annotate will generate an annotation
80 anyway, probably with undesirable results.
80 anyway, probably with undesirable results.
81 """
81 """
82 datefunc = ui.quiet and util.shortdate or util.datestr
82 datefunc = ui.quiet and util.shortdate or util.datestr
83 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
83 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
84
84
85 if not pats:
85 if not pats:
86 raise util.Abort(_('at least one file name or pattern required'))
86 raise util.Abort(_('at least one file name or pattern required'))
87
87
88 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
88 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
89 ('number', lambda x: str(x[0].rev())),
89 ('number', lambda x: str(x[0].rev())),
90 ('changeset', lambda x: short(x[0].node())),
90 ('changeset', lambda x: short(x[0].node())),
91 ('date', getdate),
91 ('date', getdate),
92 ('follow', lambda x: x[0].path()),
92 ('follow', lambda x: x[0].path()),
93 ]
93 ]
94
94
95 if (not opts.get('user') and not opts.get('changeset') and not opts.get('date')
95 if (not opts.get('user') and not opts.get('changeset') and not opts.get('date')
96 and not opts.get('follow')):
96 and not opts.get('follow')):
97 opts['number'] = 1
97 opts['number'] = 1
98
98
99 linenumber = opts.get('line_number') is not None
99 linenumber = opts.get('line_number') is not None
100 if (linenumber and (not opts.get('changeset')) and (not opts.get('number'))):
100 if (linenumber and (not opts.get('changeset')) and (not opts.get('number'))):
101 raise util.Abort(_('at least one of -n/-c is required for -l'))
101 raise util.Abort(_('at least one of -n/-c is required for -l'))
102
102
103 funcmap = [func for op, func in opmap if opts.get(op)]
103 funcmap = [func for op, func in opmap if opts.get(op)]
104 if linenumber:
104 if linenumber:
105 lastfunc = funcmap[-1]
105 lastfunc = funcmap[-1]
106 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
106 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
107
107
108 ctx = repo[opts.get('rev')]
108 ctx = repo[opts.get('rev')]
109
109
110 m = cmdutil.match(repo, pats, opts)
110 m = cmdutil.match(repo, pats, opts)
111 for abs in ctx.walk(m):
111 for abs in ctx.walk(m):
112 fctx = ctx[abs]
112 fctx = ctx[abs]
113 if not opts.get('text') and util.binary(fctx.data()):
113 if not opts.get('text') and util.binary(fctx.data()):
114 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
114 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
115 continue
115 continue
116
116
117 lines = fctx.annotate(follow=opts.get('follow'),
117 lines = fctx.annotate(follow=opts.get('follow'),
118 linenumber=linenumber)
118 linenumber=linenumber)
119 pieces = []
119 pieces = []
120
120
121 for f in funcmap:
121 for f in funcmap:
122 l = [f(n) for n, dummy in lines]
122 l = [f(n) for n, dummy in lines]
123 if l:
123 if l:
124 ml = max(map(len, l))
124 ml = max(map(len, l))
125 pieces.append(["%*s" % (ml, x) for x in l])
125 pieces.append(["%*s" % (ml, x) for x in l])
126
126
127 if pieces:
127 if pieces:
128 for p, l in zip(zip(*pieces), lines):
128 for p, l in zip(zip(*pieces), lines):
129 ui.write("%s: %s" % (" ".join(p), l[1]))
129 ui.write("%s: %s" % (" ".join(p), l[1]))
130
130
131 def archive(ui, repo, dest, **opts):
131 def archive(ui, repo, dest, **opts):
132 '''create unversioned archive of a repository revision
132 '''create unversioned archive of a repository revision
133
133
134 By default, the revision used is the parent of the working
134 By default, the revision used is the parent of the working
135 directory; use "-r" to specify a different revision.
135 directory; use "-r" to specify a different revision.
136
136
137 To specify the type of archive to create, use "-t". Valid
137 To specify the type of archive to create, use "-t". Valid
138 types are:
138 types are:
139
139
140 "files" (default): a directory full of files
140 "files" (default): a directory full of files
141 "tar": tar archive, uncompressed
141 "tar": tar archive, uncompressed
142 "tbz2": tar archive, compressed using bzip2
142 "tbz2": tar archive, compressed using bzip2
143 "tgz": tar archive, compressed using gzip
143 "tgz": tar archive, compressed using gzip
144 "uzip": zip archive, uncompressed
144 "uzip": zip archive, uncompressed
145 "zip": zip archive, compressed using deflate
145 "zip": zip archive, compressed using deflate
146
146
147 The exact name of the destination archive or directory is given
147 The exact name of the destination archive or directory is given
148 using a format string; see "hg help export" for details.
148 using a format string; see "hg help export" for details.
149
149
150 Each member added to an archive file has a directory prefix
150 Each member added to an archive file has a directory prefix
151 prepended. Use "-p" to specify a format string for the prefix.
151 prepended. Use "-p" to specify a format string for the prefix.
152 The default is the basename of the archive, with suffixes removed.
152 The default is the basename of the archive, with suffixes removed.
153 '''
153 '''
154
154
155 ctx = repo[opts.get('rev')]
155 ctx = repo[opts.get('rev')]
156 if not ctx:
156 if not ctx:
157 raise util.Abort(_('no working directory: please specify a revision'))
157 raise util.Abort(_('no working directory: please specify a revision'))
158 node = ctx.node()
158 node = ctx.node()
159 dest = cmdutil.make_filename(repo, dest, node)
159 dest = cmdutil.make_filename(repo, dest, node)
160 if os.path.realpath(dest) == repo.root:
160 if os.path.realpath(dest) == repo.root:
161 raise util.Abort(_('repository root cannot be destination'))
161 raise util.Abort(_('repository root cannot be destination'))
162 matchfn = cmdutil.match(repo, [], opts)
162 matchfn = cmdutil.match(repo, [], opts)
163 kind = opts.get('type') or 'files'
163 kind = opts.get('type') or 'files'
164 prefix = opts.get('prefix')
164 prefix = opts.get('prefix')
165 if dest == '-':
165 if dest == '-':
166 if kind == 'files':
166 if kind == 'files':
167 raise util.Abort(_('cannot archive plain files to stdout'))
167 raise util.Abort(_('cannot archive plain files to stdout'))
168 dest = sys.stdout
168 dest = sys.stdout
169 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
169 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
170 prefix = cmdutil.make_filename(repo, prefix, node)
170 prefix = cmdutil.make_filename(repo, prefix, node)
171 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
171 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
172 matchfn, prefix)
172 matchfn, prefix)
173
173
174 def backout(ui, repo, node=None, rev=None, **opts):
174 def backout(ui, repo, node=None, rev=None, **opts):
175 '''reverse effect of earlier changeset
175 '''reverse effect of earlier changeset
176
176
177 Commit the backed out changes as a new changeset. The new
177 Commit the backed out changes as a new changeset. The new
178 changeset is a child of the backed out changeset.
178 changeset is a child of the backed out changeset.
179
179
180 If you back out a changeset other than the tip, a new head is
180 If you back out a changeset other than the tip, a new head is
181 created. This head will be the new tip and you should merge this
181 created. This head will be the new tip and you should merge this
182 backout changeset with another head (current one by default).
182 backout changeset with another head (current one by default).
183
183
184 The --merge option remembers the parent of the working directory
184 The --merge option remembers the parent of the working directory
185 before starting the backout, then merges the new head with that
185 before starting the backout, then merges the new head with that
186 changeset afterwards. This saves you from doing the merge by
186 changeset afterwards. This saves you from doing the merge by
187 hand. The result of this merge is not committed, as for a normal
187 hand. The result of this merge is not committed, as with a normal
188 merge.
188 merge.
189
189
190 See \'hg help dates\' for a list of formats valid for -d/--date.
190 See \'hg help dates\' for a list of formats valid for -d/--date.
191 '''
191 '''
192 if rev and node:
192 if rev and node:
193 raise util.Abort(_("please specify just one revision"))
193 raise util.Abort(_("please specify just one revision"))
194
194
195 if not rev:
195 if not rev:
196 rev = node
196 rev = node
197
197
198 if not rev:
198 if not rev:
199 raise util.Abort(_("please specify a revision to backout"))
199 raise util.Abort(_("please specify a revision to backout"))
200
200
201 date = opts.get('date')
201 date = opts.get('date')
202 if date:
202 if date:
203 opts['date'] = util.parsedate(date)
203 opts['date'] = util.parsedate(date)
204
204
205 cmdutil.bail_if_changed(repo)
205 cmdutil.bail_if_changed(repo)
206 node = repo.lookup(rev)
206 node = repo.lookup(rev)
207
207
208 op1, op2 = repo.dirstate.parents()
208 op1, op2 = repo.dirstate.parents()
209 a = repo.changelog.ancestor(op1, node)
209 a = repo.changelog.ancestor(op1, node)
210 if a != node:
210 if a != node:
211 raise util.Abort(_('cannot back out change on a different branch'))
211 raise util.Abort(_('cannot back out change on a different branch'))
212
212
213 p1, p2 = repo.changelog.parents(node)
213 p1, p2 = repo.changelog.parents(node)
214 if p1 == nullid:
214 if p1 == nullid:
215 raise util.Abort(_('cannot back out a change with no parents'))
215 raise util.Abort(_('cannot back out a change with no parents'))
216 if p2 != nullid:
216 if p2 != nullid:
217 if not opts.get('parent'):
217 if not opts.get('parent'):
218 raise util.Abort(_('cannot back out a merge changeset without '
218 raise util.Abort(_('cannot back out a merge changeset without '
219 '--parent'))
219 '--parent'))
220 p = repo.lookup(opts['parent'])
220 p = repo.lookup(opts['parent'])
221 if p not in (p1, p2):
221 if p not in (p1, p2):
222 raise util.Abort(_('%s is not a parent of %s') %
222 raise util.Abort(_('%s is not a parent of %s') %
223 (short(p), short(node)))
223 (short(p), short(node)))
224 parent = p
224 parent = p
225 else:
225 else:
226 if opts.get('parent'):
226 if opts.get('parent'):
227 raise util.Abort(_('cannot use --parent on non-merge changeset'))
227 raise util.Abort(_('cannot use --parent on non-merge changeset'))
228 parent = p1
228 parent = p1
229
229
230 # the backout should appear on the same branch
230 # the backout should appear on the same branch
231 branch = repo.dirstate.branch()
231 branch = repo.dirstate.branch()
232 hg.clean(repo, node, show_stats=False)
232 hg.clean(repo, node, show_stats=False)
233 repo.dirstate.setbranch(branch)
233 repo.dirstate.setbranch(branch)
234 revert_opts = opts.copy()
234 revert_opts = opts.copy()
235 revert_opts['date'] = None
235 revert_opts['date'] = None
236 revert_opts['all'] = True
236 revert_opts['all'] = True
237 revert_opts['rev'] = hex(parent)
237 revert_opts['rev'] = hex(parent)
238 revert_opts['no_backup'] = None
238 revert_opts['no_backup'] = None
239 revert(ui, repo, **revert_opts)
239 revert(ui, repo, **revert_opts)
240 commit_opts = opts.copy()
240 commit_opts = opts.copy()
241 commit_opts['addremove'] = False
241 commit_opts['addremove'] = False
242 if not commit_opts['message'] and not commit_opts['logfile']:
242 if not commit_opts['message'] and not commit_opts['logfile']:
243 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
243 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
244 commit_opts['force_editor'] = True
244 commit_opts['force_editor'] = True
245 commit(ui, repo, **commit_opts)
245 commit(ui, repo, **commit_opts)
246 def nice(node):
246 def nice(node):
247 return '%d:%s' % (repo.changelog.rev(node), short(node))
247 return '%d:%s' % (repo.changelog.rev(node), short(node))
248 ui.status(_('changeset %s backs out changeset %s\n') %
248 ui.status(_('changeset %s backs out changeset %s\n') %
249 (nice(repo.changelog.tip()), nice(node)))
249 (nice(repo.changelog.tip()), nice(node)))
250 if op1 != node:
250 if op1 != node:
251 hg.clean(repo, op1, show_stats=False)
251 hg.clean(repo, op1, show_stats=False)
252 if opts.get('merge'):
252 if opts.get('merge'):
253 ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
253 ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
254 hg.merge(repo, hex(repo.changelog.tip()))
254 hg.merge(repo, hex(repo.changelog.tip()))
255 else:
255 else:
256 ui.status(_('the backout changeset is a new head - '
256 ui.status(_('the backout changeset is a new head - '
257 'do not forget to merge\n'))
257 'do not forget to merge\n'))
258 ui.status(_('(use "backout --merge" '
258 ui.status(_('(use "backout --merge" '
259 'if you want to auto-merge)\n'))
259 'if you want to auto-merge)\n'))
260
260
261 def bisect(ui, repo, rev=None, extra=None, command=None,
261 def bisect(ui, repo, rev=None, extra=None, command=None,
262 reset=None, good=None, bad=None, skip=None, noupdate=None):
262 reset=None, good=None, bad=None, skip=None, noupdate=None):
263 """subdivision search of changesets
263 """subdivision search of changesets
264
264
265 This command helps to find changesets which introduce problems.
265 This command helps to find changesets which introduce problems.
266 To use, mark the earliest changeset you know exhibits the problem
266 To use, mark the earliest changeset you know exhibits the problem
267 as bad, then mark the latest changeset which is free from the
267 as bad, then mark the latest changeset which is free from the
268 problem as good. Bisect will update your working directory to a
268 problem as good. Bisect will update your working directory to a
269 revision for testing (unless the --noupdate option is specified).
269 revision for testing (unless the --noupdate option is specified).
270 Once you have performed tests, mark the working directory as bad
270 Once you have performed tests, mark the working directory as bad
271 or good and bisect will either update to another candidate changeset
271 or good and bisect will either update to another candidate changeset
272 or announce that it has found the bad revision.
272 or announce that it has found the bad revision.
273
273
274 As a shortcut, you can also use the revision argument to mark a
274 As a shortcut, you can also use the revision argument to mark a
275 revision as good or bad without checking it out first.
275 revision as good or bad without checking it out first.
276
276
277 If you supply a command it will be used for automatic bisection. Its exit
277 If you supply a command it will be used for automatic bisection. Its exit
278 status will be used as flag to mark revision as bad or good. In case exit
278 status will be used as flag to mark revision as bad or good. In case exit
279 status is 0 the revision is marked as good, 125 - skipped, 127 (command not
279 status is 0 the revision is marked as good, 125 - skipped, 127 (command not
280 found) - bisection will be aborted and any other status bigger than 0 will
280 found) - bisection will be aborted; any other status bigger than 0 will
281 mark revision as bad.
281 mark revision as bad.
282 """
282 """
283 def print_result(nodes, good):
283 def print_result(nodes, good):
284 displayer = cmdutil.show_changeset(ui, repo, {})
284 displayer = cmdutil.show_changeset(ui, repo, {})
285 transition = (good and "good" or "bad")
285 transition = (good and "good" or "bad")
286 if len(nodes) == 1:
286 if len(nodes) == 1:
287 # narrowed it down to a single revision
287 # narrowed it down to a single revision
288 ui.write(_("The first %s revision is:\n") % transition)
288 ui.write(_("The first %s revision is:\n") % transition)
289 displayer.show(repo[nodes[0]])
289 displayer.show(repo[nodes[0]])
290 else:
290 else:
291 # multiple possible revisions
291 # multiple possible revisions
292 ui.write(_("Due to skipped revisions, the first "
292 ui.write(_("Due to skipped revisions, the first "
293 "%s revision could be any of:\n") % transition)
293 "%s revision could be any of:\n") % transition)
294 for n in nodes:
294 for n in nodes:
295 displayer.show(repo[n])
295 displayer.show(repo[n])
296
296
297 def check_state(state, interactive=True):
297 def check_state(state, interactive=True):
298 if not state['good'] or not state['bad']:
298 if not state['good'] or not state['bad']:
299 if (good or bad or skip or reset) and interactive:
299 if (good or bad or skip or reset) and interactive:
300 return
300 return
301 if not state['good']:
301 if not state['good']:
302 raise util.Abort(_('cannot bisect (no known good revisions)'))
302 raise util.Abort(_('cannot bisect (no known good revisions)'))
303 else:
303 else:
304 raise util.Abort(_('cannot bisect (no known bad revisions)'))
304 raise util.Abort(_('cannot bisect (no known bad revisions)'))
305 return True
305 return True
306
306
307 # backward compatibility
307 # backward compatibility
308 if rev in "good bad reset init".split():
308 if rev in "good bad reset init".split():
309 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
309 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
310 cmd, rev, extra = rev, extra, None
310 cmd, rev, extra = rev, extra, None
311 if cmd == "good":
311 if cmd == "good":
312 good = True
312 good = True
313 elif cmd == "bad":
313 elif cmd == "bad":
314 bad = True
314 bad = True
315 else:
315 else:
316 reset = True
316 reset = True
317 elif extra or good + bad + skip + reset + bool(command) > 1:
317 elif extra or good + bad + skip + reset + bool(command) > 1:
318 raise util.Abort(_('incompatible arguments'))
318 raise util.Abort(_('incompatible arguments'))
319
319
320 if reset:
320 if reset:
321 p = repo.join("bisect.state")
321 p = repo.join("bisect.state")
322 if os.path.exists(p):
322 if os.path.exists(p):
323 os.unlink(p)
323 os.unlink(p)
324 return
324 return
325
325
326 state = hbisect.load_state(repo)
326 state = hbisect.load_state(repo)
327
327
328 if command:
328 if command:
329 commandpath = util.find_exe(command)
329 commandpath = util.find_exe(command)
330 changesets = 1
330 changesets = 1
331 try:
331 try:
332 while changesets:
332 while changesets:
333 # update state
333 # update state
334 status = os.spawnl(os.P_WAIT, commandpath)
334 status = os.spawnl(os.P_WAIT, commandpath)
335 if status == 125:
335 if status == 125:
336 transition = "skip"
336 transition = "skip"
337 elif status == 0:
337 elif status == 0:
338 transition = "good"
338 transition = "good"
339 # status < 0 means process was killed
339 # status < 0 means process was killed
340 elif status == 127:
340 elif status == 127:
341 raise util.Abort(_("failed to execute %s") % command)
341 raise util.Abort(_("failed to execute %s") % command)
342 elif status < 0:
342 elif status < 0:
343 raise util.Abort(_("%s killed") % command)
343 raise util.Abort(_("%s killed") % command)
344 else:
344 else:
345 transition = "bad"
345 transition = "bad"
346 node = repo.lookup(rev or '.')
346 node = repo.lookup(rev or '.')
347 state[transition].append(node)
347 state[transition].append(node)
348 ui.note(_('Changeset %s: %s\n') % (short(node), transition))
348 ui.note(_('Changeset %s: %s\n') % (short(node), transition))
349 check_state(state, interactive=False)
349 check_state(state, interactive=False)
350 # bisect
350 # bisect
351 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
351 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
352 # update to next check
352 # update to next check
353 cmdutil.bail_if_changed(repo)
353 cmdutil.bail_if_changed(repo)
354 hg.clean(repo, nodes[0], show_stats=False)
354 hg.clean(repo, nodes[0], show_stats=False)
355 finally:
355 finally:
356 hbisect.save_state(repo, state)
356 hbisect.save_state(repo, state)
357 return print_result(nodes, not status)
357 return print_result(nodes, not status)
358
358
359 # update state
359 # update state
360 node = repo.lookup(rev or '.')
360 node = repo.lookup(rev or '.')
361 if good:
361 if good:
362 state['good'].append(node)
362 state['good'].append(node)
363 elif bad:
363 elif bad:
364 state['bad'].append(node)
364 state['bad'].append(node)
365 elif skip:
365 elif skip:
366 state['skip'].append(node)
366 state['skip'].append(node)
367
367
368 hbisect.save_state(repo, state)
368 hbisect.save_state(repo, state)
369
369
370 if not check_state(state):
370 if not check_state(state):
371 return
371 return
372
372
373 # actually bisect
373 # actually bisect
374 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
374 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
375 if changesets == 0:
375 if changesets == 0:
376 print_result(nodes, good)
376 print_result(nodes, good)
377 else:
377 else:
378 assert len(nodes) == 1 # only a single node can be tested next
378 assert len(nodes) == 1 # only a single node can be tested next
379 node = nodes[0]
379 node = nodes[0]
380 # compute the approximate number of remaining tests
380 # compute the approximate number of remaining tests
381 tests, size = 0, 2
381 tests, size = 0, 2
382 while size <= changesets:
382 while size <= changesets:
383 tests, size = tests + 1, size * 2
383 tests, size = tests + 1, size * 2
384 rev = repo.changelog.rev(node)
384 rev = repo.changelog.rev(node)
385 ui.write(_("Testing changeset %s:%s "
385 ui.write(_("Testing changeset %s:%s "
386 "(%s changesets remaining, ~%s tests)\n")
386 "(%s changesets remaining, ~%s tests)\n")
387 % (rev, short(node), changesets, tests))
387 % (rev, short(node), changesets, tests))
388 if not noupdate:
388 if not noupdate:
389 cmdutil.bail_if_changed(repo)
389 cmdutil.bail_if_changed(repo)
390 return hg.clean(repo, node)
390 return hg.clean(repo, node)
391
391
392 def branch(ui, repo, label=None, **opts):
392 def branch(ui, repo, label=None, **opts):
393 """set or show the current branch name
393 """set or show the current branch name
394
394
395 With no argument, show the current branch name. With one argument,
395 With no argument, show the current branch name. With one argument,
396 set the working directory branch name (the branch does not exist in
396 set the working directory branch name (the branch does not exist in
397 the repository until the next commit).
397 the repository until the next commit).
398
398
399 Unless --force is specified, branch will not let you set a
399 Unless --force is specified, branch will not let you set a
400 branch name that shadows an existing branch.
400 branch name that shadows an existing branch.
401
401
402 Use --clean to reset the working directory branch to that of the
402 Use --clean to reset the working directory branch to that of the
403 parent of the working directory, negating a previous branch change.
403 parent of the working directory, negating a previous branch change.
404
404
405 Use the command 'hg update' to switch to an existing branch.
405 Use the command 'hg update' to switch to an existing branch.
406 """
406 """
407
407
408 if opts.get('clean'):
408 if opts.get('clean'):
409 label = repo[None].parents()[0].branch()
409 label = repo[None].parents()[0].branch()
410 repo.dirstate.setbranch(label)
410 repo.dirstate.setbranch(label)
411 ui.status(_('reset working directory to branch %s\n') % label)
411 ui.status(_('reset working directory to branch %s\n') % label)
412 elif label:
412 elif label:
413 if not opts.get('force') and label in repo.branchtags():
413 if not opts.get('force') and label in repo.branchtags():
414 if label not in [p.branch() for p in repo.parents()]:
414 if label not in [p.branch() for p in repo.parents()]:
415 raise util.Abort(_('a branch of the same name already exists'
415 raise util.Abort(_('a branch of the same name already exists'
416 ' (use --force to override)'))
416 ' (use --force to override)'))
417 repo.dirstate.setbranch(util.fromlocal(label))
417 repo.dirstate.setbranch(util.fromlocal(label))
418 ui.status(_('marked working directory as branch %s\n') % label)
418 ui.status(_('marked working directory as branch %s\n') % label)
419 else:
419 else:
420 ui.write("%s\n" % util.tolocal(repo.dirstate.branch()))
420 ui.write("%s\n" % util.tolocal(repo.dirstate.branch()))
421
421
422 def branches(ui, repo, active=False):
422 def branches(ui, repo, active=False):
423 """list repository named branches
423 """list repository named branches
424
424
425 List the repository's named branches, indicating which ones are
425 List the repository's named branches, indicating which ones are
426 inactive. If active is specified, only show active branches.
426 inactive. If active is specified, only show active branches.
427
427
428 A branch is considered active if it contains repository heads.
428 A branch is considered active if it contains repository heads.
429
429
430 Use the command 'hg update' to switch to an existing branch.
430 Use the command 'hg update' to switch to an existing branch.
431 """
431 """
432 hexfunc = ui.debugflag and hex or short
432 hexfunc = ui.debugflag and hex or short
433 activebranches = [util.tolocal(repo[n].branch())
433 activebranches = [util.tolocal(repo[n].branch())
434 for n in repo.heads(closed=False)]
434 for n in repo.heads(closed=False)]
435 branches = util.sort([(tag in activebranches, repo.changelog.rev(node), tag)
435 branches = util.sort([(tag in activebranches, repo.changelog.rev(node), tag)
436 for tag, node in repo.branchtags().items()])
436 for tag, node in repo.branchtags().items()])
437 branches.reverse()
437 branches.reverse()
438
438
439 for isactive, node, tag in branches:
439 for isactive, node, tag in branches:
440 if (not active) or isactive:
440 if (not active) or isactive:
441 if ui.quiet:
441 if ui.quiet:
442 ui.write("%s\n" % tag)
442 ui.write("%s\n" % tag)
443 else:
443 else:
444 hn = repo.lookup(node)
444 hn = repo.lookup(node)
445 if isactive:
445 if isactive:
446 notice = ''
446 notice = ''
447 elif hn not in repo.branchheads(tag, closed=False):
447 elif hn not in repo.branchheads(tag, closed=False):
448 notice = ' (closed)'
448 notice = ' (closed)'
449 else:
449 else:
450 notice = ' (inactive)'
450 notice = ' (inactive)'
451 rev = str(node).rjust(31 - util.colwidth(tag))
451 rev = str(node).rjust(31 - util.colwidth(tag))
452 data = tag, rev, hexfunc(hn), notice
452 data = tag, rev, hexfunc(hn), notice
453 ui.write("%s %s:%s%s\n" % data)
453 ui.write("%s %s:%s%s\n" % data)
454
454
455 def bundle(ui, repo, fname, dest=None, **opts):
455 def bundle(ui, repo, fname, dest=None, **opts):
456 """create a changegroup file
456 """create a changegroup file
457
457
458 Generate a compressed changegroup file collecting changesets not
458 Generate a compressed changegroup file collecting changesets not
459 found in the other repository.
459 known to be in another repository.
460
460
461 If no destination repository is specified the destination is
461 If no destination repository is specified the destination is
462 assumed to have all the nodes specified by one or more --base
462 assumed to have all the nodes specified by one or more --base
463 parameters. To create a bundle containing all changesets, use
463 parameters. To create a bundle containing all changesets, use
464 --all (or --base null). To change the compression method applied,
464 --all (or --base null). To change the compression method applied,
465 use the -t option (by default, bundles are compressed using bz2).
465 use the -t option (by default, bundles are compressed using bz2).
466
466
467 The bundle file can then be transferred using conventional means and
467 The bundle file can then be transferred using conventional means and
468 applied to another repository with the unbundle or pull command.
468 applied to another repository with the unbundle or pull command.
469 This is useful when direct push and pull are not available or when
469 This is useful when direct push and pull are not available or when
470 exporting an entire repository is undesirable.
470 exporting an entire repository is undesirable.
471
471
472 Applying bundles preserves all changeset contents including
472 Applying bundles preserves all changeset contents including
473 permissions, copy/rename information, and revision history.
473 permissions, copy/rename information, and revision history.
474 """
474 """
475 revs = opts.get('rev') or None
475 revs = opts.get('rev') or None
476 if revs:
476 if revs:
477 revs = [repo.lookup(rev) for rev in revs]
477 revs = [repo.lookup(rev) for rev in revs]
478 if opts.get('all'):
478 if opts.get('all'):
479 base = ['null']
479 base = ['null']
480 else:
480 else:
481 base = opts.get('base')
481 base = opts.get('base')
482 if base:
482 if base:
483 if dest:
483 if dest:
484 raise util.Abort(_("--base is incompatible with specifiying "
484 raise util.Abort(_("--base is incompatible with specifiying "
485 "a destination"))
485 "a destination"))
486 base = [repo.lookup(rev) for rev in base]
486 base = [repo.lookup(rev) for rev in base]
487 # create the right base
487 # create the right base
488 # XXX: nodesbetween / changegroup* should be "fixed" instead
488 # XXX: nodesbetween / changegroup* should be "fixed" instead
489 o = []
489 o = []
490 has = {nullid: None}
490 has = {nullid: None}
491 for n in base:
491 for n in base:
492 has.update(repo.changelog.reachable(n))
492 has.update(repo.changelog.reachable(n))
493 if revs:
493 if revs:
494 visit = list(revs)
494 visit = list(revs)
495 else:
495 else:
496 visit = repo.changelog.heads()
496 visit = repo.changelog.heads()
497 seen = {}
497 seen = {}
498 while visit:
498 while visit:
499 n = visit.pop(0)
499 n = visit.pop(0)
500 parents = [p for p in repo.changelog.parents(n) if p not in has]
500 parents = [p for p in repo.changelog.parents(n) if p not in has]
501 if len(parents) == 0:
501 if len(parents) == 0:
502 o.insert(0, n)
502 o.insert(0, n)
503 else:
503 else:
504 for p in parents:
504 for p in parents:
505 if p not in seen:
505 if p not in seen:
506 seen[p] = 1
506 seen[p] = 1
507 visit.append(p)
507 visit.append(p)
508 else:
508 else:
509 cmdutil.setremoteconfig(ui, opts)
509 cmdutil.setremoteconfig(ui, opts)
510 dest, revs, checkout = hg.parseurl(
510 dest, revs, checkout = hg.parseurl(
511 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
511 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
512 other = hg.repository(ui, dest)
512 other = hg.repository(ui, dest)
513 o = repo.findoutgoing(other, force=opts.get('force'))
513 o = repo.findoutgoing(other, force=opts.get('force'))
514
514
515 if revs:
515 if revs:
516 cg = repo.changegroupsubset(o, revs, 'bundle')
516 cg = repo.changegroupsubset(o, revs, 'bundle')
517 else:
517 else:
518 cg = repo.changegroup(o, 'bundle')
518 cg = repo.changegroup(o, 'bundle')
519
519
520 bundletype = opts.get('type', 'bzip2').lower()
520 bundletype = opts.get('type', 'bzip2').lower()
521 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
521 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
522 bundletype = btypes.get(bundletype)
522 bundletype = btypes.get(bundletype)
523 if bundletype not in changegroup.bundletypes:
523 if bundletype not in changegroup.bundletypes:
524 raise util.Abort(_('unknown bundle type specified with --type'))
524 raise util.Abort(_('unknown bundle type specified with --type'))
525
525
526 changegroup.writebundle(cg, fname, bundletype)
526 changegroup.writebundle(cg, fname, bundletype)
527
527
528 def cat(ui, repo, file1, *pats, **opts):
528 def cat(ui, repo, file1, *pats, **opts):
529 """output the current or given revision of files
529 """output the current or given revision of files
530
530
531 Print the specified files as they were at the given revision.
531 Print the specified files as they were at the given revision.
532 If no revision is given, the parent of the working directory is used,
532 If no revision is given, the parent of the working directory is used,
533 or tip if no revision is checked out.
533 or tip if no revision is checked out.
534
534
535 Output may be to a file, in which case the name of the file is
535 Output may be to a file, in which case the name of the file is
536 given using a format string. The formatting rules are the same as
536 given using a format string. The formatting rules are the same as
537 for the export command, with the following additions:
537 for the export command, with the following additions:
538
538
539 %s basename of file being printed
539 %s basename of file being printed
540 %d dirname of file being printed, or '.' if in repo root
540 %d dirname of file being printed, or '.' if in repo root
541 %p root-relative path name of file being printed
541 %p root-relative path name of file being printed
542 """
542 """
543 ctx = repo[opts.get('rev')]
543 ctx = repo[opts.get('rev')]
544 err = 1
544 err = 1
545 m = cmdutil.match(repo, (file1,) + pats, opts)
545 m = cmdutil.match(repo, (file1,) + pats, opts)
546 for abs in ctx.walk(m):
546 for abs in ctx.walk(m):
547 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
547 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
548 data = ctx[abs].data()
548 data = ctx[abs].data()
549 if opts.get('decode'):
549 if opts.get('decode'):
550 data = repo.wwritedata(abs, data)
550 data = repo.wwritedata(abs, data)
551 fp.write(data)
551 fp.write(data)
552 err = 0
552 err = 0
553 return err
553 return err
554
554
555 def clone(ui, source, dest=None, **opts):
555 def clone(ui, source, dest=None, **opts):
556 """make a copy of an existing repository
556 """make a copy of an existing repository
557
557
558 Create a copy of an existing repository in a new directory.
558 Create a copy of an existing repository in a new directory.
559
559
560 If no destination directory name is specified, it defaults to the
560 If no destination directory name is specified, it defaults to the
561 basename of the source.
561 basename of the source.
562
562
563 The location of the source is added to the new repository's
563 The location of the source is added to the new repository's
564 .hg/hgrc file, as the default to be used for future pulls.
564 .hg/hgrc file, as the default to be used for future pulls.
565
565
566 For efficiency, hardlinks are used for cloning whenever the source
566 For efficiency, hardlinks are used for cloning whenever the source
567 and destination are on the same filesystem (note this applies only
567 and destination are on the same filesystem (note this applies only
568 to the repository data, not to the checked out files). Some
568 to the repository data, not to the checked out files). Some
569 filesystems, such as AFS, implement hardlinking incorrectly, but
569 filesystems, such as AFS, implement hardlinking incorrectly, but
570 do not report errors. In these cases, use the --pull option to
570 do not report errors. In these cases, use the --pull option to
571 avoid hardlinking.
571 avoid hardlinking.
572
572
573 In some cases, you can clone repositories and checked out files
573 In some cases, you can clone repositories and checked out files
574 using full hardlinks with
574 using full hardlinks with
575
575
576 $ cp -al REPO REPOCLONE
576 $ cp -al REPO REPOCLONE
577
577
578 This is the fastest way to clone, but it is not always safe. The
578 This is the fastest way to clone, but it is not always safe. The
579 operation is not atomic (making sure REPO is not modified during
579 operation is not atomic (making sure REPO is not modified during
580 the operation is up to you) and you have to make sure your editor
580 the operation is up to you) and you have to make sure your editor
581 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
581 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
582 this is not compatible with certain extensions that place their
582 this is not compatible with certain extensions that place their
583 metadata under the .hg directory, such as mq.
583 metadata under the .hg directory, such as mq.
584
584
585 If you use the -r option to clone up to a specific revision, no
585 If you use the -r option to clone up to a specific revision, no
586 subsequent revisions will be present in the cloned repository.
586 subsequent revisions will be present in the cloned repository.
587 This option implies --pull, even on local repositories.
587 This option implies --pull, even on local repositories.
588
588
589 If the -U option is used, the new clone will contain only a repository
589 If the -U option is used, the new clone will contain only a repository
590 (.hg) and no working copy (the working copy parent is the null revision).
590 (.hg) and no working copy (the working copy parent is the null revision).
591
591
592 See 'hg help urls' for valid source format details.
592 See 'hg help urls' for valid source format details.
593
593
594 It is possible to specify an ssh:// URL as the destination, but no
594 It is possible to specify an ssh:// URL as the destination, but no
595 .hg/hgrc and working directory will be created on the remote side.
595 .hg/hgrc and working directory will be created on the remote side.
596 Look at the help text for urls for important details about ssh:// URLs.
596 Look at the help text for urls for important details about ssh:// URLs.
597 """
597 """
598 cmdutil.setremoteconfig(ui, opts)
598 cmdutil.setremoteconfig(ui, opts)
599 hg.clone(ui, source, dest,
599 hg.clone(ui, source, dest,
600 pull=opts.get('pull'),
600 pull=opts.get('pull'),
601 stream=opts.get('uncompressed'),
601 stream=opts.get('uncompressed'),
602 rev=opts.get('rev'),
602 rev=opts.get('rev'),
603 update=not opts.get('noupdate'))
603 update=not opts.get('noupdate'))
604
604
605 def commit(ui, repo, *pats, **opts):
605 def commit(ui, repo, *pats, **opts):
606 """commit the specified files or all outstanding changes
606 """commit the specified files or all outstanding changes
607
607
608 Commit changes to the given files into the repository.
608 Commit changes to the given files into the repository.
609
609
610 If a list of files is omitted, all changes reported by "hg status"
610 If a list of files is omitted, all changes reported by "hg status"
611 will be committed.
611 will be committed.
612
612
613 If you are committing the result of a merge, do not provide any
613 If you are committing the result of a merge, do not provide any
614 file names or -I/-X filters.
614 file names or -I/-X filters.
615
615
616 If no commit message is specified, the configured editor is started to
616 If no commit message is specified, the configured editor is started to
617 enter a message.
617 prompt you for a message.
618
618
619 See 'hg help dates' for a list of formats valid for -d/--date.
619 See 'hg help dates' for a list of formats valid for -d/--date.
620 """
620 """
621 extra = {}
621 extra = {}
622 if opts.get('close_branch'):
622 if opts.get('close_branch'):
623 extra['close'] = 1
623 extra['close'] = 1
624 def commitfunc(ui, repo, message, match, opts):
624 def commitfunc(ui, repo, message, match, opts):
625 return repo.commit(match.files(), message, opts.get('user'),
625 return repo.commit(match.files(), message, opts.get('user'),
626 opts.get('date'), match, force_editor=opts.get('force_editor'),
626 opts.get('date'), match, force_editor=opts.get('force_editor'),
627 extra=extra)
627 extra=extra)
628
628
629 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
629 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
630 if not node:
630 if not node:
631 return
631 return
632 cl = repo.changelog
632 cl = repo.changelog
633 rev = cl.rev(node)
633 rev = cl.rev(node)
634 parents = cl.parentrevs(rev)
634 parents = cl.parentrevs(rev)
635 if rev - 1 in parents:
635 if rev - 1 in parents:
636 # one of the parents was the old tip
636 # one of the parents was the old tip
637 pass
637 pass
638 elif (parents == (nullrev, nullrev) or
638 elif (parents == (nullrev, nullrev) or
639 len(cl.heads(cl.node(parents[0]))) > 1 and
639 len(cl.heads(cl.node(parents[0]))) > 1 and
640 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
640 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
641 ui.status(_('created new head\n'))
641 ui.status(_('created new head\n'))
642
642
643 if ui.debugflag:
643 if ui.debugflag:
644 ui.write(_('committed changeset %d:%s\n') % (rev,hex(node)))
644 ui.write(_('committed changeset %d:%s\n') % (rev,hex(node)))
645 elif ui.verbose:
645 elif ui.verbose:
646 ui.write(_('committed changeset %d:%s\n') % (rev,short(node)))
646 ui.write(_('committed changeset %d:%s\n') % (rev,short(node)))
647
647
648 ms = merge_.mergestate(repo)
648 ms = merge_.mergestate(repo)
649 ms.reset(node)
649 ms.reset(node)
650
650
651 def copy(ui, repo, *pats, **opts):
651 def copy(ui, repo, *pats, **opts):
652 """mark files as copied for the next commit
652 """mark files as copied for the next commit
653
653
654 Mark dest as having copies of source files. If dest is a
654 Mark dest as having copies of source files. If dest is a
655 directory, copies are put in that directory. If dest is a file,
655 directory, copies are put in that directory. If dest is a file,
656 there can only be one source.
656 the source must be a single file.
657
657
658 By default, this command copies the contents of files as they
658 By default, this command copies the contents of files as they
659 stand in the working directory. If invoked with --after, the
659 stand in the working directory. If invoked with --after, the
660 operation is recorded, but no copying is performed.
660 operation is recorded, but no copying is performed.
661
661
662 This command takes effect in the next commit. To undo a copy
662 This command takes effect with the next commit. To undo a copy
663 before that, see hg revert.
663 before that, see hg revert.
664 """
664 """
665 wlock = repo.wlock(False)
665 wlock = repo.wlock(False)
666 try:
666 try:
667 return cmdutil.copy(ui, repo, pats, opts)
667 return cmdutil.copy(ui, repo, pats, opts)
668 finally:
668 finally:
669 del wlock
669 del wlock
670
670
671 def debugancestor(ui, repo, *args):
671 def debugancestor(ui, repo, *args):
672 """find the ancestor revision of two revisions in a given index"""
672 """find the ancestor revision of two revisions in a given index"""
673 if len(args) == 3:
673 if len(args) == 3:
674 index, rev1, rev2 = args
674 index, rev1, rev2 = args
675 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
675 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
676 lookup = r.lookup
676 lookup = r.lookup
677 elif len(args) == 2:
677 elif len(args) == 2:
678 if not repo:
678 if not repo:
679 raise util.Abort(_("There is no Mercurial repository here "
679 raise util.Abort(_("There is no Mercurial repository here "
680 "(.hg not found)"))
680 "(.hg not found)"))
681 rev1, rev2 = args
681 rev1, rev2 = args
682 r = repo.changelog
682 r = repo.changelog
683 lookup = repo.lookup
683 lookup = repo.lookup
684 else:
684 else:
685 raise util.Abort(_('either two or three arguments required'))
685 raise util.Abort(_('either two or three arguments required'))
686 a = r.ancestor(lookup(rev1), lookup(rev2))
686 a = r.ancestor(lookup(rev1), lookup(rev2))
687 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
687 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
688
688
689 def debugcomplete(ui, cmd='', **opts):
689 def debugcomplete(ui, cmd='', **opts):
690 """returns the completion list associated with the given command"""
690 """returns the completion list associated with the given command"""
691
691
692 if opts.get('options'):
692 if opts.get('options'):
693 options = []
693 options = []
694 otables = [globalopts]
694 otables = [globalopts]
695 if cmd:
695 if cmd:
696 aliases, entry = cmdutil.findcmd(cmd, table, False)
696 aliases, entry = cmdutil.findcmd(cmd, table, False)
697 otables.append(entry[1])
697 otables.append(entry[1])
698 for t in otables:
698 for t in otables:
699 for o in t:
699 for o in t:
700 if o[0]:
700 if o[0]:
701 options.append('-%s' % o[0])
701 options.append('-%s' % o[0])
702 options.append('--%s' % o[1])
702 options.append('--%s' % o[1])
703 ui.write("%s\n" % "\n".join(options))
703 ui.write("%s\n" % "\n".join(options))
704 return
704 return
705
705
706 cmdlist = cmdutil.findpossible(cmd, table)
706 cmdlist = cmdutil.findpossible(cmd, table)
707 if ui.verbose:
707 if ui.verbose:
708 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
708 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
709 ui.write("%s\n" % "\n".join(util.sort(cmdlist)))
709 ui.write("%s\n" % "\n".join(util.sort(cmdlist)))
710
710
711 def debugfsinfo(ui, path = "."):
711 def debugfsinfo(ui, path = "."):
712 file('.debugfsinfo', 'w').write('')
712 file('.debugfsinfo', 'w').write('')
713 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
713 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
714 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
714 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
715 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
715 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
716 and 'yes' or 'no'))
716 and 'yes' or 'no'))
717 os.unlink('.debugfsinfo')
717 os.unlink('.debugfsinfo')
718
718
719 def debugrebuildstate(ui, repo, rev="tip"):
719 def debugrebuildstate(ui, repo, rev="tip"):
720 """rebuild the dirstate as it would look like for the given revision"""
720 """rebuild the dirstate as it would look like for the given revision"""
721 ctx = repo[rev]
721 ctx = repo[rev]
722 wlock = repo.wlock()
722 wlock = repo.wlock()
723 try:
723 try:
724 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
724 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
725 finally:
725 finally:
726 del wlock
726 del wlock
727
727
728 def debugcheckstate(ui, repo):
728 def debugcheckstate(ui, repo):
729 """validate the correctness of the current dirstate"""
729 """validate the correctness of the current dirstate"""
730 parent1, parent2 = repo.dirstate.parents()
730 parent1, parent2 = repo.dirstate.parents()
731 m1 = repo[parent1].manifest()
731 m1 = repo[parent1].manifest()
732 m2 = repo[parent2].manifest()
732 m2 = repo[parent2].manifest()
733 errors = 0
733 errors = 0
734 for f in repo.dirstate:
734 for f in repo.dirstate:
735 state = repo.dirstate[f]
735 state = repo.dirstate[f]
736 if state in "nr" and f not in m1:
736 if state in "nr" and f not in m1:
737 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
737 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
738 errors += 1
738 errors += 1
739 if state in "a" and f in m1:
739 if state in "a" and f in m1:
740 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
740 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
741 errors += 1
741 errors += 1
742 if state in "m" and f not in m1 and f not in m2:
742 if state in "m" and f not in m1 and f not in m2:
743 ui.warn(_("%s in state %s, but not in either manifest\n") %
743 ui.warn(_("%s in state %s, but not in either manifest\n") %
744 (f, state))
744 (f, state))
745 errors += 1
745 errors += 1
746 for f in m1:
746 for f in m1:
747 state = repo.dirstate[f]
747 state = repo.dirstate[f]
748 if state not in "nrm":
748 if state not in "nrm":
749 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
749 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
750 errors += 1
750 errors += 1
751 if errors:
751 if errors:
752 error = _(".hg/dirstate inconsistent with current parent's manifest")
752 error = _(".hg/dirstate inconsistent with current parent's manifest")
753 raise util.Abort(error)
753 raise util.Abort(error)
754
754
755 def showconfig(ui, repo, *values, **opts):
755 def showconfig(ui, repo, *values, **opts):
756 """show combined config settings from all hgrc files
756 """show combined config settings from all hgrc files
757
757
758 With no args, print names and values of all config items.
758 With no args, print names and values of all config items.
759
759
760 With one arg of the form section.name, print just the value of
760 With one arg of the form section.name, print just the value of
761 that config item.
761 that config item.
762
762
763 With multiple args, print names and values of all config items
763 With multiple args, print names and values of all config items
764 with matching section names."""
764 with matching section names."""
765
765
766 untrusted = bool(opts.get('untrusted'))
766 untrusted = bool(opts.get('untrusted'))
767 if values:
767 if values:
768 if len([v for v in values if '.' in v]) > 1:
768 if len([v for v in values if '.' in v]) > 1:
769 raise util.Abort(_('only one config item permitted'))
769 raise util.Abort(_('only one config item permitted'))
770 for section, name, value in ui.walkconfig(untrusted=untrusted):
770 for section, name, value in ui.walkconfig(untrusted=untrusted):
771 sectname = section + '.' + name
771 sectname = section + '.' + name
772 if values:
772 if values:
773 for v in values:
773 for v in values:
774 if v == section:
774 if v == section:
775 ui.write('%s=%s\n' % (sectname, value))
775 ui.write('%s=%s\n' % (sectname, value))
776 elif v == sectname:
776 elif v == sectname:
777 ui.write(value, '\n')
777 ui.write(value, '\n')
778 else:
778 else:
779 ui.write('%s=%s\n' % (sectname, value))
779 ui.write('%s=%s\n' % (sectname, value))
780
780
781 def debugsetparents(ui, repo, rev1, rev2=None):
781 def debugsetparents(ui, repo, rev1, rev2=None):
782 """manually set the parents of the current working directory
782 """manually set the parents of the current working directory
783
783
784 This is useful for writing repository conversion tools, but should
784 This is useful for writing repository conversion tools, but should
785 be used with care.
785 be used with care.
786 """
786 """
787
787
788 if not rev2:
788 if not rev2:
789 rev2 = hex(nullid)
789 rev2 = hex(nullid)
790
790
791 wlock = repo.wlock()
791 wlock = repo.wlock()
792 try:
792 try:
793 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
793 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
794 finally:
794 finally:
795 del wlock
795 del wlock
796
796
797 def debugstate(ui, repo, nodates=None):
797 def debugstate(ui, repo, nodates=None):
798 """show the contents of the current dirstate"""
798 """show the contents of the current dirstate"""
799 timestr = ""
799 timestr = ""
800 showdate = not nodates
800 showdate = not nodates
801 for file_, ent in util.sort(repo.dirstate._map.iteritems()):
801 for file_, ent in util.sort(repo.dirstate._map.iteritems()):
802 if showdate:
802 if showdate:
803 if ent[3] == -1:
803 if ent[3] == -1:
804 # Pad or slice to locale representation
804 # Pad or slice to locale representation
805 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0)))
805 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0)))
806 timestr = 'unset'
806 timestr = 'unset'
807 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
807 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
808 else:
808 else:
809 timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3]))
809 timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3]))
810 if ent[1] & 020000:
810 if ent[1] & 020000:
811 mode = 'lnk'
811 mode = 'lnk'
812 else:
812 else:
813 mode = '%3o' % (ent[1] & 0777)
813 mode = '%3o' % (ent[1] & 0777)
814 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
814 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
815 for f in repo.dirstate.copies():
815 for f in repo.dirstate.copies():
816 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
816 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
817
817
818 def debugdata(ui, file_, rev):
818 def debugdata(ui, file_, rev):
819 """dump the contents of a data file revision"""
819 """dump the contents of a data file revision"""
820 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
820 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
821 try:
821 try:
822 ui.write(r.revision(r.lookup(rev)))
822 ui.write(r.revision(r.lookup(rev)))
823 except KeyError:
823 except KeyError:
824 raise util.Abort(_('invalid revision identifier %s') % rev)
824 raise util.Abort(_('invalid revision identifier %s') % rev)
825
825
826 def debugdate(ui, date, range=None, **opts):
826 def debugdate(ui, date, range=None, **opts):
827 """parse and display a date"""
827 """parse and display a date"""
828 if opts["extended"]:
828 if opts["extended"]:
829 d = util.parsedate(date, util.extendeddateformats)
829 d = util.parsedate(date, util.extendeddateformats)
830 else:
830 else:
831 d = util.parsedate(date)
831 d = util.parsedate(date)
832 ui.write("internal: %s %s\n" % d)
832 ui.write("internal: %s %s\n" % d)
833 ui.write("standard: %s\n" % util.datestr(d))
833 ui.write("standard: %s\n" % util.datestr(d))
834 if range:
834 if range:
835 m = util.matchdate(range)
835 m = util.matchdate(range)
836 ui.write("match: %s\n" % m(d[0]))
836 ui.write("match: %s\n" % m(d[0]))
837
837
838 def debugindex(ui, file_):
838 def debugindex(ui, file_):
839 """dump the contents of an index file"""
839 """dump the contents of an index file"""
840 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
840 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
841 ui.write(" rev offset length base linkrev"
841 ui.write(" rev offset length base linkrev"
842 " nodeid p1 p2\n")
842 " nodeid p1 p2\n")
843 for i in r:
843 for i in r:
844 node = r.node(i)
844 node = r.node(i)
845 try:
845 try:
846 pp = r.parents(node)
846 pp = r.parents(node)
847 except:
847 except:
848 pp = [nullid, nullid]
848 pp = [nullid, nullid]
849 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
849 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
850 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
850 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
851 short(node), short(pp[0]), short(pp[1])))
851 short(node), short(pp[0]), short(pp[1])))
852
852
853 def debugindexdot(ui, file_):
853 def debugindexdot(ui, file_):
854 """dump an index DAG as a .dot file"""
854 """dump an index DAG as a .dot file"""
855 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
855 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
856 ui.write("digraph G {\n")
856 ui.write("digraph G {\n")
857 for i in r:
857 for i in r:
858 node = r.node(i)
858 node = r.node(i)
859 pp = r.parents(node)
859 pp = r.parents(node)
860 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
860 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
861 if pp[1] != nullid:
861 if pp[1] != nullid:
862 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
862 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
863 ui.write("}\n")
863 ui.write("}\n")
864
864
865 def debuginstall(ui):
865 def debuginstall(ui):
866 '''test Mercurial installation'''
866 '''test Mercurial installation'''
867
867
868 def writetemp(contents):
868 def writetemp(contents):
869 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
869 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
870 f = os.fdopen(fd, "wb")
870 f = os.fdopen(fd, "wb")
871 f.write(contents)
871 f.write(contents)
872 f.close()
872 f.close()
873 return name
873 return name
874
874
875 problems = 0
875 problems = 0
876
876
877 # encoding
877 # encoding
878 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
878 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
879 try:
879 try:
880 util.fromlocal("test")
880 util.fromlocal("test")
881 except util.Abort, inst:
881 except util.Abort, inst:
882 ui.write(" %s\n" % inst)
882 ui.write(" %s\n" % inst)
883 ui.write(_(" (check that your locale is properly set)\n"))
883 ui.write(_(" (check that your locale is properly set)\n"))
884 problems += 1
884 problems += 1
885
885
886 # compiled modules
886 # compiled modules
887 ui.status(_("Checking extensions...\n"))
887 ui.status(_("Checking extensions...\n"))
888 try:
888 try:
889 import bdiff, mpatch, base85
889 import bdiff, mpatch, base85
890 except Exception, inst:
890 except Exception, inst:
891 ui.write(" %s\n" % inst)
891 ui.write(" %s\n" % inst)
892 ui.write(_(" One or more extensions could not be found"))
892 ui.write(_(" One or more extensions could not be found"))
893 ui.write(_(" (check that you compiled the extensions)\n"))
893 ui.write(_(" (check that you compiled the extensions)\n"))
894 problems += 1
894 problems += 1
895
895
896 # templates
896 # templates
897 ui.status(_("Checking templates...\n"))
897 ui.status(_("Checking templates...\n"))
898 try:
898 try:
899 import templater
899 import templater
900 t = templater.templater(templater.templatepath("map-cmdline.default"))
900 t = templater.templater(templater.templatepath("map-cmdline.default"))
901 except Exception, inst:
901 except Exception, inst:
902 ui.write(" %s\n" % inst)
902 ui.write(" %s\n" % inst)
903 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
903 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
904 problems += 1
904 problems += 1
905
905
906 # patch
906 # patch
907 ui.status(_("Checking patch...\n"))
907 ui.status(_("Checking patch...\n"))
908 patchproblems = 0
908 patchproblems = 0
909 a = "1\n2\n3\n4\n"
909 a = "1\n2\n3\n4\n"
910 b = "1\n2\n3\ninsert\n4\n"
910 b = "1\n2\n3\ninsert\n4\n"
911 fa = writetemp(a)
911 fa = writetemp(a)
912 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
912 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
913 os.path.basename(fa))
913 os.path.basename(fa))
914 fd = writetemp(d)
914 fd = writetemp(d)
915
915
916 files = {}
916 files = {}
917 try:
917 try:
918 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
918 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
919 except util.Abort, e:
919 except util.Abort, e:
920 ui.write(_(" patch call failed:\n"))
920 ui.write(_(" patch call failed:\n"))
921 ui.write(" " + str(e) + "\n")
921 ui.write(" " + str(e) + "\n")
922 patchproblems += 1
922 patchproblems += 1
923 else:
923 else:
924 if list(files) != [os.path.basename(fa)]:
924 if list(files) != [os.path.basename(fa)]:
925 ui.write(_(" unexpected patch output!\n"))
925 ui.write(_(" unexpected patch output!\n"))
926 patchproblems += 1
926 patchproblems += 1
927 a = file(fa).read()
927 a = file(fa).read()
928 if a != b:
928 if a != b:
929 ui.write(_(" patch test failed!\n"))
929 ui.write(_(" patch test failed!\n"))
930 patchproblems += 1
930 patchproblems += 1
931
931
932 if patchproblems:
932 if patchproblems:
933 if ui.config('ui', 'patch'):
933 if ui.config('ui', 'patch'):
934 ui.write(_(" (Current patch tool may be incompatible with patch,"
934 ui.write(_(" (Current patch tool may be incompatible with patch,"
935 " or misconfigured. Please check your .hgrc file)\n"))
935 " or misconfigured. Please check your .hgrc file)\n"))
936 else:
936 else:
937 ui.write(_(" Internal patcher failure, please report this error"
937 ui.write(_(" Internal patcher failure, please report this error"
938 " to http://www.selenic.com/mercurial/bts\n"))
938 " to http://www.selenic.com/mercurial/bts\n"))
939 problems += patchproblems
939 problems += patchproblems
940
940
941 os.unlink(fa)
941 os.unlink(fa)
942 os.unlink(fd)
942 os.unlink(fd)
943
943
944 # editor
944 # editor
945 ui.status(_("Checking commit editor...\n"))
945 ui.status(_("Checking commit editor...\n"))
946 editor = ui.geteditor()
946 editor = ui.geteditor()
947 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
947 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
948 if not cmdpath:
948 if not cmdpath:
949 if editor == 'vi':
949 if editor == 'vi':
950 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
950 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
951 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
951 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
952 else:
952 else:
953 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
953 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
954 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
954 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
955 problems += 1
955 problems += 1
956
956
957 # check username
957 # check username
958 ui.status(_("Checking username...\n"))
958 ui.status(_("Checking username...\n"))
959 user = os.environ.get("HGUSER")
959 user = os.environ.get("HGUSER")
960 if user is None:
960 if user is None:
961 user = ui.config("ui", "username")
961 user = ui.config("ui", "username")
962 if user is None:
962 if user is None:
963 user = os.environ.get("EMAIL")
963 user = os.environ.get("EMAIL")
964 if not user:
964 if not user:
965 ui.warn(" ")
965 ui.warn(" ")
966 ui.username()
966 ui.username()
967 ui.write(_(" (specify a username in your .hgrc file)\n"))
967 ui.write(_(" (specify a username in your .hgrc file)\n"))
968
968
969 if not problems:
969 if not problems:
970 ui.status(_("No problems detected\n"))
970 ui.status(_("No problems detected\n"))
971 else:
971 else:
972 ui.write(_("%s problems detected,"
972 ui.write(_("%s problems detected,"
973 " please check your install!\n") % problems)
973 " please check your install!\n") % problems)
974
974
975 return problems
975 return problems
976
976
977 def debugrename(ui, repo, file1, *pats, **opts):
977 def debugrename(ui, repo, file1, *pats, **opts):
978 """dump rename information"""
978 """dump rename information"""
979
979
980 ctx = repo[opts.get('rev')]
980 ctx = repo[opts.get('rev')]
981 m = cmdutil.match(repo, (file1,) + pats, opts)
981 m = cmdutil.match(repo, (file1,) + pats, opts)
982 for abs in ctx.walk(m):
982 for abs in ctx.walk(m):
983 fctx = ctx[abs]
983 fctx = ctx[abs]
984 o = fctx.filelog().renamed(fctx.filenode())
984 o = fctx.filelog().renamed(fctx.filenode())
985 rel = m.rel(abs)
985 rel = m.rel(abs)
986 if o:
986 if o:
987 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
987 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
988 else:
988 else:
989 ui.write(_("%s not renamed\n") % rel)
989 ui.write(_("%s not renamed\n") % rel)
990
990
991 def debugwalk(ui, repo, *pats, **opts):
991 def debugwalk(ui, repo, *pats, **opts):
992 """show how files match on given patterns"""
992 """show how files match on given patterns"""
993 m = cmdutil.match(repo, pats, opts)
993 m = cmdutil.match(repo, pats, opts)
994 items = list(repo.walk(m))
994 items = list(repo.walk(m))
995 if not items:
995 if not items:
996 return
996 return
997 fmt = 'f %%-%ds %%-%ds %%s' % (
997 fmt = 'f %%-%ds %%-%ds %%s' % (
998 max([len(abs) for abs in items]),
998 max([len(abs) for abs in items]),
999 max([len(m.rel(abs)) for abs in items]))
999 max([len(m.rel(abs)) for abs in items]))
1000 for abs in items:
1000 for abs in items:
1001 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1001 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1002 ui.write("%s\n" % line.rstrip())
1002 ui.write("%s\n" % line.rstrip())
1003
1003
1004 def diff(ui, repo, *pats, **opts):
1004 def diff(ui, repo, *pats, **opts):
1005 """diff repository (or selected files)
1005 """diff repository (or selected files)
1006
1006
1007 Show differences between revisions for the specified files.
1007 Show differences between revisions for the specified files.
1008
1008
1009 Differences between files are shown using the unified diff format.
1009 Differences between files are shown using the unified diff format.
1010
1010
1011 NOTE: diff may generate unexpected results for merges, as it will
1011 NOTE: diff may generate unexpected results for merges, as it will
1012 default to comparing against the working directory's first parent
1012 default to comparing against the working directory's first parent
1013 changeset if no revisions are specified.
1013 changeset if no revisions are specified.
1014
1014
1015 When two revision arguments are given, then changes are shown
1015 When two revision arguments are given, then changes are shown
1016 between those revisions. If only one revision is specified then
1016 between those revisions. If only one revision is specified then
1017 that revision is compared to the working directory, and, when no
1017 that revision is compared to the working directory, and, when no
1018 revisions are specified, the working directory files are compared
1018 revisions are specified, the working directory files are compared
1019 to its parent.
1019 to its parent.
1020
1020
1021 Without the -a option, diff will avoid generating diffs of files
1021 Without the -a option, diff will avoid generating diffs of files
1022 it detects as binary. With -a, diff will generate a diff anyway,
1022 it detects as binary. With -a, diff will generate a diff anyway,
1023 probably with undesirable results.
1023 probably with undesirable results.
1024
1024
1025 Use the --git option to generate diffs in the git extended diff
1025 Use the --git option to generate diffs in the git extended diff
1026 format. Read the diffs help topic for more information.
1026 format. For more information, read hg help diffs.
1027 """
1027 """
1028
1028
1029 revs = opts.get('rev')
1029 revs = opts.get('rev')
1030 change = opts.get('change')
1030 change = opts.get('change')
1031
1031
1032 if revs and change:
1032 if revs and change:
1033 msg = _('cannot specify --rev and --change at the same time')
1033 msg = _('cannot specify --rev and --change at the same time')
1034 raise util.Abort(msg)
1034 raise util.Abort(msg)
1035 elif change:
1035 elif change:
1036 node2 = repo.lookup(change)
1036 node2 = repo.lookup(change)
1037 node1 = repo[node2].parents()[0].node()
1037 node1 = repo[node2].parents()[0].node()
1038 else:
1038 else:
1039 node1, node2 = cmdutil.revpair(repo, revs)
1039 node1, node2 = cmdutil.revpair(repo, revs)
1040
1040
1041 m = cmdutil.match(repo, pats, opts)
1041 m = cmdutil.match(repo, pats, opts)
1042 it = patch.diff(repo, node1, node2, match=m, opts=patch.diffopts(ui, opts))
1042 it = patch.diff(repo, node1, node2, match=m, opts=patch.diffopts(ui, opts))
1043 for chunk in it:
1043 for chunk in it:
1044 repo.ui.write(chunk)
1044 repo.ui.write(chunk)
1045
1045
1046 def export(ui, repo, *changesets, **opts):
1046 def export(ui, repo, *changesets, **opts):
1047 """dump the header and diffs for one or more changesets
1047 """dump the header and diffs for one or more changesets
1048
1048
1049 Print the changeset header and diffs for one or more revisions.
1049 Print the changeset header and diffs for one or more revisions.
1050
1050
1051 The information shown in the changeset header is: author,
1051 The information shown in the changeset header is: author,
1052 changeset hash, parent(s) and commit comment.
1052 changeset hash, parent(s) and commit comment.
1053
1053
1054 NOTE: export may generate unexpected diff output for merge changesets,
1054 NOTE: export may generate unexpected diff output for merge changesets,
1055 as it will compare the merge changeset against its first parent only.
1055 as it will compare the merge changeset against its first parent only.
1056
1056
1057 Output may be to a file, in which case the name of the file is
1057 Output may be to a file, in which case the name of the file is
1058 given using a format string. The formatting rules are as follows:
1058 given using a format string. The formatting rules are as follows:
1059
1059
1060 %% literal "%" character
1060 %% literal "%" character
1061 %H changeset hash (40 bytes of hexadecimal)
1061 %H changeset hash (40 bytes of hexadecimal)
1062 %N number of patches being generated
1062 %N number of patches being generated
1063 %R changeset revision number
1063 %R changeset revision number
1064 %b basename of the exporting repository
1064 %b basename of the exporting repository
1065 %h short-form changeset hash (12 bytes of hexadecimal)
1065 %h short-form changeset hash (12 bytes of hexadecimal)
1066 %n zero-padded sequence number, starting at 1
1066 %n zero-padded sequence number, starting at 1
1067 %r zero-padded changeset revision number
1067 %r zero-padded changeset revision number
1068
1068
1069 Without the -a option, export will avoid generating diffs of files
1069 Without the -a option, export will avoid generating diffs of files
1070 it detects as binary. With -a, export will generate a diff anyway,
1070 it detects as binary. With -a, export will generate a diff anyway,
1071 probably with undesirable results.
1071 probably with undesirable results.
1072
1072
1073 Use the --git option to generate diffs in the git extended diff
1073 Use the --git option to generate diffs in the git extended diff
1074 format. Read the diffs help topic for more information.
1074 format. Read the diffs help topic for more information.
1075
1075
1076 With the --switch-parent option, the diff will be against the second
1076 With the --switch-parent option, the diff will be against the second
1077 parent. It can be useful to review a merge.
1077 parent. It can be useful to review a merge.
1078 """
1078 """
1079 if not changesets:
1079 if not changesets:
1080 raise util.Abort(_("export requires at least one changeset"))
1080 raise util.Abort(_("export requires at least one changeset"))
1081 revs = cmdutil.revrange(repo, changesets)
1081 revs = cmdutil.revrange(repo, changesets)
1082 if len(revs) > 1:
1082 if len(revs) > 1:
1083 ui.note(_('exporting patches:\n'))
1083 ui.note(_('exporting patches:\n'))
1084 else:
1084 else:
1085 ui.note(_('exporting patch:\n'))
1085 ui.note(_('exporting patch:\n'))
1086 patch.export(repo, revs, template=opts.get('output'),
1086 patch.export(repo, revs, template=opts.get('output'),
1087 switch_parent=opts.get('switch_parent'),
1087 switch_parent=opts.get('switch_parent'),
1088 opts=patch.diffopts(ui, opts))
1088 opts=patch.diffopts(ui, opts))
1089
1089
1090 def grep(ui, repo, pattern, *pats, **opts):
1090 def grep(ui, repo, pattern, *pats, **opts):
1091 """search for a pattern in specified files and revisions
1091 """search for a pattern in specified files and revisions
1092
1092
1093 Search revisions of files for a regular expression.
1093 Search revisions of files for a regular expression.
1094
1094
1095 This command behaves differently than Unix grep. It only accepts
1095 This command behaves differently than Unix grep. It only accepts
1096 Python/Perl regexps. It searches repository history, not the
1096 Python/Perl regexps. It searches repository history, not the
1097 working directory. It always prints the revision number in which
1097 working directory. It always prints the revision number in which
1098 a match appears.
1098 a match appears.
1099
1099
1100 By default, grep only prints output for the first revision of a
1100 By default, grep only prints output for the first revision of a
1101 file in which it finds a match. To get it to print every revision
1101 file in which it finds a match. To get it to print every revision
1102 that contains a change in match status ("-" for a match that
1102 that contains a change in match status ("-" for a match that
1103 becomes a non-match, or "+" for a non-match that becomes a match),
1103 becomes a non-match, or "+" for a non-match that becomes a match),
1104 use the --all flag.
1104 use the --all flag.
1105 """
1105 """
1106 reflags = 0
1106 reflags = 0
1107 if opts.get('ignore_case'):
1107 if opts.get('ignore_case'):
1108 reflags |= re.I
1108 reflags |= re.I
1109 try:
1109 try:
1110 regexp = re.compile(pattern, reflags)
1110 regexp = re.compile(pattern, reflags)
1111 except Exception, inst:
1111 except Exception, inst:
1112 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1112 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1113 return None
1113 return None
1114 sep, eol = ':', '\n'
1114 sep, eol = ':', '\n'
1115 if opts.get('print0'):
1115 if opts.get('print0'):
1116 sep = eol = '\0'
1116 sep = eol = '\0'
1117
1117
1118 fcache = {}
1118 fcache = {}
1119 def getfile(fn):
1119 def getfile(fn):
1120 if fn not in fcache:
1120 if fn not in fcache:
1121 fcache[fn] = repo.file(fn)
1121 fcache[fn] = repo.file(fn)
1122 return fcache[fn]
1122 return fcache[fn]
1123
1123
1124 def matchlines(body):
1124 def matchlines(body):
1125 begin = 0
1125 begin = 0
1126 linenum = 0
1126 linenum = 0
1127 while True:
1127 while True:
1128 match = regexp.search(body, begin)
1128 match = regexp.search(body, begin)
1129 if not match:
1129 if not match:
1130 break
1130 break
1131 mstart, mend = match.span()
1131 mstart, mend = match.span()
1132 linenum += body.count('\n', begin, mstart) + 1
1132 linenum += body.count('\n', begin, mstart) + 1
1133 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1133 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1134 begin = body.find('\n', mend) + 1 or len(body)
1134 begin = body.find('\n', mend) + 1 or len(body)
1135 lend = begin - 1
1135 lend = begin - 1
1136 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1136 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1137
1137
1138 class linestate(object):
1138 class linestate(object):
1139 def __init__(self, line, linenum, colstart, colend):
1139 def __init__(self, line, linenum, colstart, colend):
1140 self.line = line
1140 self.line = line
1141 self.linenum = linenum
1141 self.linenum = linenum
1142 self.colstart = colstart
1142 self.colstart = colstart
1143 self.colend = colend
1143 self.colend = colend
1144
1144
1145 def __hash__(self):
1145 def __hash__(self):
1146 return hash((self.linenum, self.line))
1146 return hash((self.linenum, self.line))
1147
1147
1148 def __eq__(self, other):
1148 def __eq__(self, other):
1149 return self.line == other.line
1149 return self.line == other.line
1150
1150
1151 matches = {}
1151 matches = {}
1152 copies = {}
1152 copies = {}
1153 def grepbody(fn, rev, body):
1153 def grepbody(fn, rev, body):
1154 matches[rev].setdefault(fn, [])
1154 matches[rev].setdefault(fn, [])
1155 m = matches[rev][fn]
1155 m = matches[rev][fn]
1156 for lnum, cstart, cend, line in matchlines(body):
1156 for lnum, cstart, cend, line in matchlines(body):
1157 s = linestate(line, lnum, cstart, cend)
1157 s = linestate(line, lnum, cstart, cend)
1158 m.append(s)
1158 m.append(s)
1159
1159
1160 def difflinestates(a, b):
1160 def difflinestates(a, b):
1161 sm = difflib.SequenceMatcher(None, a, b)
1161 sm = difflib.SequenceMatcher(None, a, b)
1162 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1162 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1163 if tag == 'insert':
1163 if tag == 'insert':
1164 for i in xrange(blo, bhi):
1164 for i in xrange(blo, bhi):
1165 yield ('+', b[i])
1165 yield ('+', b[i])
1166 elif tag == 'delete':
1166 elif tag == 'delete':
1167 for i in xrange(alo, ahi):
1167 for i in xrange(alo, ahi):
1168 yield ('-', a[i])
1168 yield ('-', a[i])
1169 elif tag == 'replace':
1169 elif tag == 'replace':
1170 for i in xrange(alo, ahi):
1170 for i in xrange(alo, ahi):
1171 yield ('-', a[i])
1171 yield ('-', a[i])
1172 for i in xrange(blo, bhi):
1172 for i in xrange(blo, bhi):
1173 yield ('+', b[i])
1173 yield ('+', b[i])
1174
1174
1175 prev = {}
1175 prev = {}
1176 def display(fn, rev, states, prevstates):
1176 def display(fn, rev, states, prevstates):
1177 datefunc = ui.quiet and util.shortdate or util.datestr
1177 datefunc = ui.quiet and util.shortdate or util.datestr
1178 found = False
1178 found = False
1179 filerevmatches = {}
1179 filerevmatches = {}
1180 r = prev.get(fn, -1)
1180 r = prev.get(fn, -1)
1181 if opts.get('all'):
1181 if opts.get('all'):
1182 iter = difflinestates(states, prevstates)
1182 iter = difflinestates(states, prevstates)
1183 else:
1183 else:
1184 iter = [('', l) for l in prevstates]
1184 iter = [('', l) for l in prevstates]
1185 for change, l in iter:
1185 for change, l in iter:
1186 cols = [fn, str(r)]
1186 cols = [fn, str(r)]
1187 if opts.get('line_number'):
1187 if opts.get('line_number'):
1188 cols.append(str(l.linenum))
1188 cols.append(str(l.linenum))
1189 if opts.get('all'):
1189 if opts.get('all'):
1190 cols.append(change)
1190 cols.append(change)
1191 if opts.get('user'):
1191 if opts.get('user'):
1192 cols.append(ui.shortuser(get(r)[1]))
1192 cols.append(ui.shortuser(get(r)[1]))
1193 if opts.get('date'):
1193 if opts.get('date'):
1194 cols.append(datefunc(get(r)[2]))
1194 cols.append(datefunc(get(r)[2]))
1195 if opts.get('files_with_matches'):
1195 if opts.get('files_with_matches'):
1196 c = (fn, r)
1196 c = (fn, r)
1197 if c in filerevmatches:
1197 if c in filerevmatches:
1198 continue
1198 continue
1199 filerevmatches[c] = 1
1199 filerevmatches[c] = 1
1200 else:
1200 else:
1201 cols.append(l.line)
1201 cols.append(l.line)
1202 ui.write(sep.join(cols), eol)
1202 ui.write(sep.join(cols), eol)
1203 found = True
1203 found = True
1204 return found
1204 return found
1205
1205
1206 fstate = {}
1206 fstate = {}
1207 skip = {}
1207 skip = {}
1208 get = util.cachefunc(lambda r: repo[r].changeset())
1208 get = util.cachefunc(lambda r: repo[r].changeset())
1209 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1209 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1210 found = False
1210 found = False
1211 follow = opts.get('follow')
1211 follow = opts.get('follow')
1212 for st, rev, fns in changeiter:
1212 for st, rev, fns in changeiter:
1213 if st == 'window':
1213 if st == 'window':
1214 matches.clear()
1214 matches.clear()
1215 elif st == 'add':
1215 elif st == 'add':
1216 ctx = repo[rev]
1216 ctx = repo[rev]
1217 matches[rev] = {}
1217 matches[rev] = {}
1218 for fn in fns:
1218 for fn in fns:
1219 if fn in skip:
1219 if fn in skip:
1220 continue
1220 continue
1221 try:
1221 try:
1222 grepbody(fn, rev, getfile(fn).read(ctx.filenode(fn)))
1222 grepbody(fn, rev, getfile(fn).read(ctx.filenode(fn)))
1223 fstate.setdefault(fn, [])
1223 fstate.setdefault(fn, [])
1224 if follow:
1224 if follow:
1225 copied = getfile(fn).renamed(ctx.filenode(fn))
1225 copied = getfile(fn).renamed(ctx.filenode(fn))
1226 if copied:
1226 if copied:
1227 copies.setdefault(rev, {})[fn] = copied[0]
1227 copies.setdefault(rev, {})[fn] = copied[0]
1228 except error.LookupError:
1228 except error.LookupError:
1229 pass
1229 pass
1230 elif st == 'iter':
1230 elif st == 'iter':
1231 for fn, m in util.sort(matches[rev].items()):
1231 for fn, m in util.sort(matches[rev].items()):
1232 copy = copies.get(rev, {}).get(fn)
1232 copy = copies.get(rev, {}).get(fn)
1233 if fn in skip:
1233 if fn in skip:
1234 if copy:
1234 if copy:
1235 skip[copy] = True
1235 skip[copy] = True
1236 continue
1236 continue
1237 if fn in prev or fstate[fn]:
1237 if fn in prev or fstate[fn]:
1238 r = display(fn, rev, m, fstate[fn])
1238 r = display(fn, rev, m, fstate[fn])
1239 found = found or r
1239 found = found or r
1240 if r and not opts.get('all'):
1240 if r and not opts.get('all'):
1241 skip[fn] = True
1241 skip[fn] = True
1242 if copy:
1242 if copy:
1243 skip[copy] = True
1243 skip[copy] = True
1244 fstate[fn] = m
1244 fstate[fn] = m
1245 if copy:
1245 if copy:
1246 fstate[copy] = m
1246 fstate[copy] = m
1247 prev[fn] = rev
1247 prev[fn] = rev
1248
1248
1249 for fn, state in util.sort(fstate.items()):
1249 for fn, state in util.sort(fstate.items()):
1250 if fn in skip:
1250 if fn in skip:
1251 continue
1251 continue
1252 if fn not in copies.get(prev[fn], {}):
1252 if fn not in copies.get(prev[fn], {}):
1253 found = display(fn, rev, {}, state) or found
1253 found = display(fn, rev, {}, state) or found
1254 return (not found and 1) or 0
1254 return (not found and 1) or 0
1255
1255
1256 def heads(ui, repo, *branchrevs, **opts):
1256 def heads(ui, repo, *branchrevs, **opts):
1257 """show current repository heads or show branch heads
1257 """show current repository heads or show branch heads
1258
1258
1259 With no arguments, show all repository head changesets.
1259 With no arguments, show all repository head changesets.
1260
1260
1261 If branch or revisions names are given this will show the heads of
1261 If branch or revisions names are given this will show the heads of
1262 the specified branches or the branches those revisions are tagged
1262 the specified branches or the branches those revisions are tagged
1263 with.
1263 with.
1264
1264
1265 Repository "heads" are changesets that don't have child
1265 Repository "heads" are changesets that don't have child
1266 changesets. They are where development generally takes place and
1266 changesets. They are where development generally takes place and
1267 are the usual targets for update and merge operations.
1267 are the usual targets for update and merge operations.
1268
1268
1269 Branch heads are changesets that have a given branch tag, but have
1269 Branch heads are changesets that have a given branch tag, but have
1270 no child changesets with that tag. They are usually where
1270 no child changesets with that tag. They are usually where
1271 development on the given branch takes place.
1271 development on the given branch takes place.
1272 """
1272 """
1273 if opts.get('rev'):
1273 if opts.get('rev'):
1274 start = repo.lookup(opts['rev'])
1274 start = repo.lookup(opts['rev'])
1275 else:
1275 else:
1276 start = None
1276 start = None
1277 closed = not opts.get('active')
1277 closed = not opts.get('active')
1278 if not branchrevs:
1278 if not branchrevs:
1279 # Assume we're looking repo-wide heads if no revs were specified.
1279 # Assume we're looking repo-wide heads if no revs were specified.
1280 heads = repo.heads(start, closed=closed)
1280 heads = repo.heads(start, closed=closed)
1281 else:
1281 else:
1282 heads = []
1282 heads = []
1283 visitedset = util.set()
1283 visitedset = util.set()
1284 for branchrev in branchrevs:
1284 for branchrev in branchrevs:
1285 branch = repo[branchrev].branch()
1285 branch = repo[branchrev].branch()
1286 if branch in visitedset:
1286 if branch in visitedset:
1287 continue
1287 continue
1288 visitedset.add(branch)
1288 visitedset.add(branch)
1289 bheads = repo.branchheads(branch, start, closed=closed)
1289 bheads = repo.branchheads(branch, start, closed=closed)
1290 if not bheads:
1290 if not bheads:
1291 if branch != branchrev:
1291 if branch != branchrev:
1292 ui.warn(_("no changes on branch %s containing %s are "
1292 ui.warn(_("no changes on branch %s containing %s are "
1293 "reachable from %s\n")
1293 "reachable from %s\n")
1294 % (branch, branchrev, opts.get('rev')))
1294 % (branch, branchrev, opts.get('rev')))
1295 else:
1295 else:
1296 ui.warn(_("no changes on branch %s are reachable from %s\n")
1296 ui.warn(_("no changes on branch %s are reachable from %s\n")
1297 % (branch, opts.get('rev')))
1297 % (branch, opts.get('rev')))
1298 heads.extend(bheads)
1298 heads.extend(bheads)
1299 if not heads:
1299 if not heads:
1300 return 1
1300 return 1
1301 displayer = cmdutil.show_changeset(ui, repo, opts)
1301 displayer = cmdutil.show_changeset(ui, repo, opts)
1302 for n in heads:
1302 for n in heads:
1303 displayer.show(repo[n])
1303 displayer.show(repo[n])
1304
1304
1305 def help_(ui, name=None, with_version=False):
1305 def help_(ui, name=None, with_version=False):
1306 """show help for a given topic or a help overview
1306 """show help for a given topic or a help overview
1307
1307
1308 With no arguments, print a list of commands and short help.
1308 With no arguments, print a list of commands and short help.
1309
1309
1310 Given a topic, extension, or command name, print help for that topic."""
1310 Given a topic, extension, or command name, print help for that topic."""
1311 option_lists = []
1311 option_lists = []
1312
1312
1313 def addglobalopts(aliases):
1313 def addglobalopts(aliases):
1314 if ui.verbose:
1314 if ui.verbose:
1315 option_lists.append((_("global options:"), globalopts))
1315 option_lists.append((_("global options:"), globalopts))
1316 if name == 'shortlist':
1316 if name == 'shortlist':
1317 option_lists.append((_('use "hg help" for the full list '
1317 option_lists.append((_('use "hg help" for the full list '
1318 'of commands'), ()))
1318 'of commands'), ()))
1319 else:
1319 else:
1320 if name == 'shortlist':
1320 if name == 'shortlist':
1321 msg = _('use "hg help" for the full list of commands '
1321 msg = _('use "hg help" for the full list of commands '
1322 'or "hg -v" for details')
1322 'or "hg -v" for details')
1323 elif aliases:
1323 elif aliases:
1324 msg = _('use "hg -v help%s" to show aliases and '
1324 msg = _('use "hg -v help%s" to show aliases and '
1325 'global options') % (name and " " + name or "")
1325 'global options') % (name and " " + name or "")
1326 else:
1326 else:
1327 msg = _('use "hg -v help %s" to show global options') % name
1327 msg = _('use "hg -v help %s" to show global options') % name
1328 option_lists.append((msg, ()))
1328 option_lists.append((msg, ()))
1329
1329
1330 def helpcmd(name):
1330 def helpcmd(name):
1331 if with_version:
1331 if with_version:
1332 version_(ui)
1332 version_(ui)
1333 ui.write('\n')
1333 ui.write('\n')
1334
1334
1335 try:
1335 try:
1336 aliases, i = cmdutil.findcmd(name, table, False)
1336 aliases, i = cmdutil.findcmd(name, table, False)
1337 except error.AmbiguousCommand, inst:
1337 except error.AmbiguousCommand, inst:
1338 select = lambda c: c.lstrip('^').startswith(inst.args[0])
1338 select = lambda c: c.lstrip('^').startswith(inst.args[0])
1339 helplist(_('list of commands:\n\n'), select)
1339 helplist(_('list of commands:\n\n'), select)
1340 return
1340 return
1341
1341
1342 # synopsis
1342 # synopsis
1343 if len(i) > 2:
1343 if len(i) > 2:
1344 if i[2].startswith('hg'):
1344 if i[2].startswith('hg'):
1345 ui.write("%s\n" % i[2])
1345 ui.write("%s\n" % i[2])
1346 else:
1346 else:
1347 ui.write('hg %s %s\n' % (aliases[0], i[2]))
1347 ui.write('hg %s %s\n' % (aliases[0], i[2]))
1348 else:
1348 else:
1349 ui.write('hg %s\n' % aliases[0])
1349 ui.write('hg %s\n' % aliases[0])
1350
1350
1351 # aliases
1351 # aliases
1352 if not ui.quiet and len(aliases) > 1:
1352 if not ui.quiet and len(aliases) > 1:
1353 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1353 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1354
1354
1355 # description
1355 # description
1356 doc = gettext(i[0].__doc__)
1356 doc = gettext(i[0].__doc__)
1357 if not doc:
1357 if not doc:
1358 doc = _("(no help text available)")
1358 doc = _("(no help text available)")
1359 if ui.quiet:
1359 if ui.quiet:
1360 doc = doc.splitlines(0)[0]
1360 doc = doc.splitlines(0)[0]
1361 ui.write("\n%s\n" % doc.rstrip())
1361 ui.write("\n%s\n" % doc.rstrip())
1362
1362
1363 if not ui.quiet:
1363 if not ui.quiet:
1364 # options
1364 # options
1365 if i[1]:
1365 if i[1]:
1366 option_lists.append((_("options:\n"), i[1]))
1366 option_lists.append((_("options:\n"), i[1]))
1367
1367
1368 addglobalopts(False)
1368 addglobalopts(False)
1369
1369
1370 def helplist(header, select=None):
1370 def helplist(header, select=None):
1371 h = {}
1371 h = {}
1372 cmds = {}
1372 cmds = {}
1373 for c, e in table.iteritems():
1373 for c, e in table.iteritems():
1374 f = c.split("|", 1)[0]
1374 f = c.split("|", 1)[0]
1375 if select and not select(f):
1375 if select and not select(f):
1376 continue
1376 continue
1377 if (not select and name != 'shortlist' and
1377 if (not select and name != 'shortlist' and
1378 e[0].__module__ != __name__):
1378 e[0].__module__ != __name__):
1379 continue
1379 continue
1380 if name == "shortlist" and not f.startswith("^"):
1380 if name == "shortlist" and not f.startswith("^"):
1381 continue
1381 continue
1382 f = f.lstrip("^")
1382 f = f.lstrip("^")
1383 if not ui.debugflag and f.startswith("debug"):
1383 if not ui.debugflag and f.startswith("debug"):
1384 continue
1384 continue
1385 doc = gettext(e[0].__doc__)
1385 doc = gettext(e[0].__doc__)
1386 if not doc:
1386 if not doc:
1387 doc = _("(no help text available)")
1387 doc = _("(no help text available)")
1388 h[f] = doc.splitlines(0)[0].rstrip()
1388 h[f] = doc.splitlines(0)[0].rstrip()
1389 cmds[f] = c.lstrip("^")
1389 cmds[f] = c.lstrip("^")
1390
1390
1391 if not h:
1391 if not h:
1392 ui.status(_('no commands defined\n'))
1392 ui.status(_('no commands defined\n'))
1393 return
1393 return
1394
1394
1395 ui.status(header)
1395 ui.status(header)
1396 fns = util.sort(h)
1396 fns = util.sort(h)
1397 m = max(map(len, fns))
1397 m = max(map(len, fns))
1398 for f in fns:
1398 for f in fns:
1399 if ui.verbose:
1399 if ui.verbose:
1400 commands = cmds[f].replace("|",", ")
1400 commands = cmds[f].replace("|",", ")
1401 ui.write(" %s:\n %s\n"%(commands, h[f]))
1401 ui.write(" %s:\n %s\n"%(commands, h[f]))
1402 else:
1402 else:
1403 ui.write(' %-*s %s\n' % (m, f, h[f]))
1403 ui.write(' %-*s %s\n' % (m, f, h[f]))
1404
1404
1405 exts = list(extensions.extensions())
1405 exts = list(extensions.extensions())
1406 if exts and name != 'shortlist':
1406 if exts and name != 'shortlist':
1407 ui.write(_('\nenabled extensions:\n\n'))
1407 ui.write(_('\nenabled extensions:\n\n'))
1408 maxlength = 0
1408 maxlength = 0
1409 exthelps = []
1409 exthelps = []
1410 for ename, ext in exts:
1410 for ename, ext in exts:
1411 doc = (ext.__doc__ or _('(no help text available)'))
1411 doc = (ext.__doc__ or _('(no help text available)'))
1412 ename = ename.split('.')[-1]
1412 ename = ename.split('.')[-1]
1413 maxlength = max(len(ename), maxlength)
1413 maxlength = max(len(ename), maxlength)
1414 exthelps.append((ename, doc.splitlines(0)[0].strip()))
1414 exthelps.append((ename, doc.splitlines(0)[0].strip()))
1415 for ename, text in exthelps:
1415 for ename, text in exthelps:
1416 ui.write(_(' %s %s\n') % (ename.ljust(maxlength), text))
1416 ui.write(_(' %s %s\n') % (ename.ljust(maxlength), text))
1417
1417
1418 if not ui.quiet:
1418 if not ui.quiet:
1419 addglobalopts(True)
1419 addglobalopts(True)
1420
1420
1421 def helptopic(name):
1421 def helptopic(name):
1422 for names, header, doc in help.helptable:
1422 for names, header, doc in help.helptable:
1423 if name in names:
1423 if name in names:
1424 break
1424 break
1425 else:
1425 else:
1426 raise error.UnknownCommand(name)
1426 raise error.UnknownCommand(name)
1427
1427
1428 # description
1428 # description
1429 if not doc:
1429 if not doc:
1430 doc = _("(no help text available)")
1430 doc = _("(no help text available)")
1431 if callable(doc):
1431 if callable(doc):
1432 doc = doc()
1432 doc = doc()
1433
1433
1434 ui.write("%s\n" % header)
1434 ui.write("%s\n" % header)
1435 ui.write("%s\n" % doc.rstrip())
1435 ui.write("%s\n" % doc.rstrip())
1436
1436
1437 def helpext(name):
1437 def helpext(name):
1438 try:
1438 try:
1439 mod = extensions.find(name)
1439 mod = extensions.find(name)
1440 except KeyError:
1440 except KeyError:
1441 raise error.UnknownCommand(name)
1441 raise error.UnknownCommand(name)
1442
1442
1443 doc = gettext(mod.__doc__) or _('no help text available')
1443 doc = gettext(mod.__doc__) or _('no help text available')
1444 doc = doc.splitlines(0)
1444 doc = doc.splitlines(0)
1445 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1445 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1446 for d in doc[1:]:
1446 for d in doc[1:]:
1447 ui.write(d, '\n')
1447 ui.write(d, '\n')
1448
1448
1449 ui.status('\n')
1449 ui.status('\n')
1450
1450
1451 try:
1451 try:
1452 ct = mod.cmdtable
1452 ct = mod.cmdtable
1453 except AttributeError:
1453 except AttributeError:
1454 ct = {}
1454 ct = {}
1455
1455
1456 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1456 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1457 helplist(_('list of commands:\n\n'), modcmds.has_key)
1457 helplist(_('list of commands:\n\n'), modcmds.has_key)
1458
1458
1459 if name and name != 'shortlist':
1459 if name and name != 'shortlist':
1460 i = None
1460 i = None
1461 for f in (helptopic, helpcmd, helpext):
1461 for f in (helptopic, helpcmd, helpext):
1462 try:
1462 try:
1463 f(name)
1463 f(name)
1464 i = None
1464 i = None
1465 break
1465 break
1466 except error.UnknownCommand, inst:
1466 except error.UnknownCommand, inst:
1467 i = inst
1467 i = inst
1468 if i:
1468 if i:
1469 raise i
1469 raise i
1470
1470
1471 else:
1471 else:
1472 # program name
1472 # program name
1473 if ui.verbose or with_version:
1473 if ui.verbose or with_version:
1474 version_(ui)
1474 version_(ui)
1475 else:
1475 else:
1476 ui.status(_("Mercurial Distributed SCM\n"))
1476 ui.status(_("Mercurial Distributed SCM\n"))
1477 ui.status('\n')
1477 ui.status('\n')
1478
1478
1479 # list of commands
1479 # list of commands
1480 if name == "shortlist":
1480 if name == "shortlist":
1481 header = _('basic commands:\n\n')
1481 header = _('basic commands:\n\n')
1482 else:
1482 else:
1483 header = _('list of commands:\n\n')
1483 header = _('list of commands:\n\n')
1484
1484
1485 helplist(header)
1485 helplist(header)
1486
1486
1487 # list all option lists
1487 # list all option lists
1488 opt_output = []
1488 opt_output = []
1489 for title, options in option_lists:
1489 for title, options in option_lists:
1490 opt_output.append(("\n%s" % title, None))
1490 opt_output.append(("\n%s" % title, None))
1491 for shortopt, longopt, default, desc in options:
1491 for shortopt, longopt, default, desc in options:
1492 if "DEPRECATED" in desc and not ui.verbose: continue
1492 if "DEPRECATED" in desc and not ui.verbose: continue
1493 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1493 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1494 longopt and " --%s" % longopt),
1494 longopt and " --%s" % longopt),
1495 "%s%s" % (desc,
1495 "%s%s" % (desc,
1496 default
1496 default
1497 and _(" (default: %s)") % default
1497 and _(" (default: %s)") % default
1498 or "")))
1498 or "")))
1499
1499
1500 if not name:
1500 if not name:
1501 ui.write(_("\nadditional help topics:\n\n"))
1501 ui.write(_("\nadditional help topics:\n\n"))
1502 topics = []
1502 topics = []
1503 for names, header, doc in help.helptable:
1503 for names, header, doc in help.helptable:
1504 names = [(-len(name), name) for name in names]
1504 names = [(-len(name), name) for name in names]
1505 names.sort()
1505 names.sort()
1506 topics.append((names[0][1], header))
1506 topics.append((names[0][1], header))
1507 topics_len = max([len(s[0]) for s in topics])
1507 topics_len = max([len(s[0]) for s in topics])
1508 for t, desc in topics:
1508 for t, desc in topics:
1509 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1509 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1510
1510
1511 if opt_output:
1511 if opt_output:
1512 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1512 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1513 for first, second in opt_output:
1513 for first, second in opt_output:
1514 if second:
1514 if second:
1515 ui.write(" %-*s %s\n" % (opts_len, first, second))
1515 ui.write(" %-*s %s\n" % (opts_len, first, second))
1516 else:
1516 else:
1517 ui.write("%s\n" % first)
1517 ui.write("%s\n" % first)
1518
1518
1519 def identify(ui, repo, source=None,
1519 def identify(ui, repo, source=None,
1520 rev=None, num=None, id=None, branch=None, tags=None):
1520 rev=None, num=None, id=None, branch=None, tags=None):
1521 """identify the working copy or specified revision
1521 """identify the working copy or specified revision
1522
1522
1523 With no revision, print a summary of the current state of the repo.
1523 With no revision, print a summary of the current state of the repo.
1524
1524
1525 With a path, do a lookup in another repository.
1525 With a path, do a lookup in another repository.
1526
1526
1527 This summary identifies the repository state using one or two parent
1527 This summary identifies the repository state using one or two parent
1528 hash identifiers, followed by a "+" if there are uncommitted changes
1528 hash identifiers, followed by a "+" if there are uncommitted changes
1529 in the working directory, a list of tags for this revision and a branch
1529 in the working directory, a list of tags for this revision and a branch
1530 name for non-default branches.
1530 name for non-default branches.
1531 """
1531 """
1532
1532
1533 if not repo and not source:
1533 if not repo and not source:
1534 raise util.Abort(_("There is no Mercurial repository here "
1534 raise util.Abort(_("There is no Mercurial repository here "
1535 "(.hg not found)"))
1535 "(.hg not found)"))
1536
1536
1537 hexfunc = ui.debugflag and hex or short
1537 hexfunc = ui.debugflag and hex or short
1538 default = not (num or id or branch or tags)
1538 default = not (num or id or branch or tags)
1539 output = []
1539 output = []
1540
1540
1541 revs = []
1541 revs = []
1542 if source:
1542 if source:
1543 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1543 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1544 repo = hg.repository(ui, source)
1544 repo = hg.repository(ui, source)
1545
1545
1546 if not repo.local():
1546 if not repo.local():
1547 if not rev and revs:
1547 if not rev and revs:
1548 rev = revs[0]
1548 rev = revs[0]
1549 if not rev:
1549 if not rev:
1550 rev = "tip"
1550 rev = "tip"
1551 if num or branch or tags:
1551 if num or branch or tags:
1552 raise util.Abort(
1552 raise util.Abort(
1553 "can't query remote revision number, branch, or tags")
1553 "can't query remote revision number, branch, or tags")
1554 output = [hexfunc(repo.lookup(rev))]
1554 output = [hexfunc(repo.lookup(rev))]
1555 elif not rev:
1555 elif not rev:
1556 ctx = repo[None]
1556 ctx = repo[None]
1557 parents = ctx.parents()
1557 parents = ctx.parents()
1558 changed = False
1558 changed = False
1559 if default or id or num:
1559 if default or id or num:
1560 changed = ctx.files() + ctx.deleted()
1560 changed = ctx.files() + ctx.deleted()
1561 if default or id:
1561 if default or id:
1562 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1562 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1563 (changed) and "+" or "")]
1563 (changed) and "+" or "")]
1564 if num:
1564 if num:
1565 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1565 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1566 (changed) and "+" or ""))
1566 (changed) and "+" or ""))
1567 else:
1567 else:
1568 ctx = repo[rev]
1568 ctx = repo[rev]
1569 if default or id:
1569 if default or id:
1570 output = [hexfunc(ctx.node())]
1570 output = [hexfunc(ctx.node())]
1571 if num:
1571 if num:
1572 output.append(str(ctx.rev()))
1572 output.append(str(ctx.rev()))
1573
1573
1574 if repo.local() and default and not ui.quiet:
1574 if repo.local() and default and not ui.quiet:
1575 b = util.tolocal(ctx.branch())
1575 b = util.tolocal(ctx.branch())
1576 if b != 'default':
1576 if b != 'default':
1577 output.append("(%s)" % b)
1577 output.append("(%s)" % b)
1578
1578
1579 # multiple tags for a single parent separated by '/'
1579 # multiple tags for a single parent separated by '/'
1580 t = "/".join(ctx.tags())
1580 t = "/".join(ctx.tags())
1581 if t:
1581 if t:
1582 output.append(t)
1582 output.append(t)
1583
1583
1584 if branch:
1584 if branch:
1585 output.append(util.tolocal(ctx.branch()))
1585 output.append(util.tolocal(ctx.branch()))
1586
1586
1587 if tags:
1587 if tags:
1588 output.extend(ctx.tags())
1588 output.extend(ctx.tags())
1589
1589
1590 ui.write("%s\n" % ' '.join(output))
1590 ui.write("%s\n" % ' '.join(output))
1591
1591
1592 def import_(ui, repo, patch1, *patches, **opts):
1592 def import_(ui, repo, patch1, *patches, **opts):
1593 """import an ordered set of patches
1593 """import an ordered set of patches
1594
1594
1595 Import a list of patches and commit them individually.
1595 Import a list of patches and commit them individually.
1596
1596
1597 If there are outstanding changes in the working directory, import
1597 If there are outstanding changes in the working directory, import
1598 will abort unless given the -f flag.
1598 will abort unless given the -f flag.
1599
1599
1600 You can import a patch straight from a mail message. Even patches
1600 You can import a patch straight from a mail message. Even patches
1601 as attachments work (body part must be type text/plain or
1601 as attachments work (body part must be type text/plain or
1602 text/x-patch to be used). From and Subject headers of email
1602 text/x-patch to be used). From and Subject headers of email
1603 message are used as default committer and commit message. All
1603 message are used as default committer and commit message. All
1604 text/plain body parts before first diff are added to commit
1604 text/plain body parts before first diff are added to commit
1605 message.
1605 message.
1606
1606
1607 If the imported patch was generated by hg export, user and description
1607 If the imported patch was generated by hg export, user and description
1608 from patch override values from message headers and body. Values
1608 from patch override values from message headers and body. Values
1609 given on command line with -m and -u override these.
1609 given on command line with -m and -u override these.
1610
1610
1611 If --exact is specified, import will set the working directory
1611 If --exact is specified, import will set the working directory
1612 to the parent of each patch before applying it, and will abort
1612 to the parent of each patch before applying it, and will abort
1613 if the resulting changeset has a different ID than the one
1613 if the resulting changeset has a different ID than the one
1614 recorded in the patch. This may happen due to character set
1614 recorded in the patch. This may happen due to character set
1615 problems or other deficiencies in the text patch format.
1615 problems or other deficiencies in the text patch format.
1616
1616
1617 With --similarity, hg will attempt to discover renames and copies
1617 With --similarity, hg will attempt to discover renames and copies
1618 in the patch in the same way as 'addremove'.
1618 in the patch in the same way as 'addremove'.
1619
1619
1620 To read a patch from standard input, use patch name "-".
1620 To read a patch from standard input, use patch name "-".
1621 See 'hg help dates' for a list of formats valid for -d/--date.
1621 See 'hg help dates' for a list of formats valid for -d/--date.
1622 """
1622 """
1623 patches = (patch1,) + patches
1623 patches = (patch1,) + patches
1624
1624
1625 date = opts.get('date')
1625 date = opts.get('date')
1626 if date:
1626 if date:
1627 opts['date'] = util.parsedate(date)
1627 opts['date'] = util.parsedate(date)
1628
1628
1629 try:
1629 try:
1630 sim = float(opts.get('similarity') or 0)
1630 sim = float(opts.get('similarity') or 0)
1631 except ValueError:
1631 except ValueError:
1632 raise util.Abort(_('similarity must be a number'))
1632 raise util.Abort(_('similarity must be a number'))
1633 if sim < 0 or sim > 100:
1633 if sim < 0 or sim > 100:
1634 raise util.Abort(_('similarity must be between 0 and 100'))
1634 raise util.Abort(_('similarity must be between 0 and 100'))
1635
1635
1636 if opts.get('exact') or not opts.get('force'):
1636 if opts.get('exact') or not opts.get('force'):
1637 cmdutil.bail_if_changed(repo)
1637 cmdutil.bail_if_changed(repo)
1638
1638
1639 d = opts["base"]
1639 d = opts["base"]
1640 strip = opts["strip"]
1640 strip = opts["strip"]
1641 wlock = lock = None
1641 wlock = lock = None
1642 try:
1642 try:
1643 wlock = repo.wlock()
1643 wlock = repo.wlock()
1644 lock = repo.lock()
1644 lock = repo.lock()
1645 for p in patches:
1645 for p in patches:
1646 pf = os.path.join(d, p)
1646 pf = os.path.join(d, p)
1647
1647
1648 if pf == '-':
1648 if pf == '-':
1649 ui.status(_("applying patch from stdin\n"))
1649 ui.status(_("applying patch from stdin\n"))
1650 pf = sys.stdin
1650 pf = sys.stdin
1651 else:
1651 else:
1652 ui.status(_("applying %s\n") % p)
1652 ui.status(_("applying %s\n") % p)
1653 pf = url.open(ui, pf)
1653 pf = url.open(ui, pf)
1654 data = patch.extract(ui, pf)
1654 data = patch.extract(ui, pf)
1655 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1655 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1656
1656
1657 if tmpname is None:
1657 if tmpname is None:
1658 raise util.Abort(_('no diffs found'))
1658 raise util.Abort(_('no diffs found'))
1659
1659
1660 try:
1660 try:
1661 cmdline_message = cmdutil.logmessage(opts)
1661 cmdline_message = cmdutil.logmessage(opts)
1662 if cmdline_message:
1662 if cmdline_message:
1663 # pickup the cmdline msg
1663 # pickup the cmdline msg
1664 message = cmdline_message
1664 message = cmdline_message
1665 elif message:
1665 elif message:
1666 # pickup the patch msg
1666 # pickup the patch msg
1667 message = message.strip()
1667 message = message.strip()
1668 else:
1668 else:
1669 # launch the editor
1669 # launch the editor
1670 message = None
1670 message = None
1671 ui.debug(_('message:\n%s\n') % message)
1671 ui.debug(_('message:\n%s\n') % message)
1672
1672
1673 wp = repo.parents()
1673 wp = repo.parents()
1674 if opts.get('exact'):
1674 if opts.get('exact'):
1675 if not nodeid or not p1:
1675 if not nodeid or not p1:
1676 raise util.Abort(_('not a mercurial patch'))
1676 raise util.Abort(_('not a mercurial patch'))
1677 p1 = repo.lookup(p1)
1677 p1 = repo.lookup(p1)
1678 p2 = repo.lookup(p2 or hex(nullid))
1678 p2 = repo.lookup(p2 or hex(nullid))
1679
1679
1680 if p1 != wp[0].node():
1680 if p1 != wp[0].node():
1681 hg.clean(repo, p1)
1681 hg.clean(repo, p1)
1682 repo.dirstate.setparents(p1, p2)
1682 repo.dirstate.setparents(p1, p2)
1683 elif p2:
1683 elif p2:
1684 try:
1684 try:
1685 p1 = repo.lookup(p1)
1685 p1 = repo.lookup(p1)
1686 p2 = repo.lookup(p2)
1686 p2 = repo.lookup(p2)
1687 if p1 == wp[0].node():
1687 if p1 == wp[0].node():
1688 repo.dirstate.setparents(p1, p2)
1688 repo.dirstate.setparents(p1, p2)
1689 except error.RepoError:
1689 except error.RepoError:
1690 pass
1690 pass
1691 if opts.get('exact') or opts.get('import_branch'):
1691 if opts.get('exact') or opts.get('import_branch'):
1692 repo.dirstate.setbranch(branch or 'default')
1692 repo.dirstate.setbranch(branch or 'default')
1693
1693
1694 files = {}
1694 files = {}
1695 try:
1695 try:
1696 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1696 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1697 files=files)
1697 files=files)
1698 finally:
1698 finally:
1699 files = patch.updatedir(ui, repo, files, similarity=sim/100.)
1699 files = patch.updatedir(ui, repo, files, similarity=sim/100.)
1700 if not opts.get('no_commit'):
1700 if not opts.get('no_commit'):
1701 n = repo.commit(files, message, opts.get('user') or user,
1701 n = repo.commit(files, message, opts.get('user') or user,
1702 opts.get('date') or date)
1702 opts.get('date') or date)
1703 if opts.get('exact'):
1703 if opts.get('exact'):
1704 if hex(n) != nodeid:
1704 if hex(n) != nodeid:
1705 repo.rollback()
1705 repo.rollback()
1706 raise util.Abort(_('patch is damaged'
1706 raise util.Abort(_('patch is damaged'
1707 ' or loses information'))
1707 ' or loses information'))
1708 # Force a dirstate write so that the next transaction
1708 # Force a dirstate write so that the next transaction
1709 # backups an up-do-date file.
1709 # backups an up-do-date file.
1710 repo.dirstate.write()
1710 repo.dirstate.write()
1711 finally:
1711 finally:
1712 os.unlink(tmpname)
1712 os.unlink(tmpname)
1713 finally:
1713 finally:
1714 del lock, wlock
1714 del lock, wlock
1715
1715
1716 def incoming(ui, repo, source="default", **opts):
1716 def incoming(ui, repo, source="default", **opts):
1717 """show new changesets found in source
1717 """show new changesets found in source
1718
1718
1719 Show new changesets found in the specified path/URL or the default
1719 Show new changesets found in the specified path/URL or the default
1720 pull location. These are the changesets that would be pulled if a pull
1720 pull location. These are the changesets that would be pulled if a pull
1721 was requested.
1721 was requested.
1722
1722
1723 For remote repository, using --bundle avoids downloading the changesets
1723 For remote repository, using --bundle avoids downloading the changesets
1724 twice if the incoming is followed by a pull.
1724 twice if the incoming is followed by a pull.
1725
1725
1726 See pull for valid source format details.
1726 See pull for valid source format details.
1727 """
1727 """
1728 limit = cmdutil.loglimit(opts)
1728 limit = cmdutil.loglimit(opts)
1729 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
1729 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
1730 cmdutil.setremoteconfig(ui, opts)
1730 cmdutil.setremoteconfig(ui, opts)
1731
1731
1732 other = hg.repository(ui, source)
1732 other = hg.repository(ui, source)
1733 ui.status(_('comparing with %s\n') % url.hidepassword(source))
1733 ui.status(_('comparing with %s\n') % url.hidepassword(source))
1734 if revs:
1734 if revs:
1735 revs = [other.lookup(rev) for rev in revs]
1735 revs = [other.lookup(rev) for rev in revs]
1736 common, incoming, rheads = repo.findcommonincoming(other, heads=revs,
1736 common, incoming, rheads = repo.findcommonincoming(other, heads=revs,
1737 force=opts["force"])
1737 force=opts["force"])
1738 if not incoming:
1738 if not incoming:
1739 try:
1739 try:
1740 os.unlink(opts["bundle"])
1740 os.unlink(opts["bundle"])
1741 except:
1741 except:
1742 pass
1742 pass
1743 ui.status(_("no changes found\n"))
1743 ui.status(_("no changes found\n"))
1744 return 1
1744 return 1
1745
1745
1746 cleanup = None
1746 cleanup = None
1747 try:
1747 try:
1748 fname = opts["bundle"]
1748 fname = opts["bundle"]
1749 if fname or not other.local():
1749 if fname or not other.local():
1750 # create a bundle (uncompressed if other repo is not local)
1750 # create a bundle (uncompressed if other repo is not local)
1751
1751
1752 if revs is None and other.capable('changegroupsubset'):
1752 if revs is None and other.capable('changegroupsubset'):
1753 revs = rheads
1753 revs = rheads
1754
1754
1755 if revs is None:
1755 if revs is None:
1756 cg = other.changegroup(incoming, "incoming")
1756 cg = other.changegroup(incoming, "incoming")
1757 else:
1757 else:
1758 cg = other.changegroupsubset(incoming, revs, 'incoming')
1758 cg = other.changegroupsubset(incoming, revs, 'incoming')
1759 bundletype = other.local() and "HG10BZ" or "HG10UN"
1759 bundletype = other.local() and "HG10BZ" or "HG10UN"
1760 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1760 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1761 # keep written bundle?
1761 # keep written bundle?
1762 if opts["bundle"]:
1762 if opts["bundle"]:
1763 cleanup = None
1763 cleanup = None
1764 if not other.local():
1764 if not other.local():
1765 # use the created uncompressed bundlerepo
1765 # use the created uncompressed bundlerepo
1766 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1766 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1767
1767
1768 o = other.changelog.nodesbetween(incoming, revs)[0]
1768 o = other.changelog.nodesbetween(incoming, revs)[0]
1769 if opts.get('newest_first'):
1769 if opts.get('newest_first'):
1770 o.reverse()
1770 o.reverse()
1771 displayer = cmdutil.show_changeset(ui, other, opts)
1771 displayer = cmdutil.show_changeset(ui, other, opts)
1772 count = 0
1772 count = 0
1773 for n in o:
1773 for n in o:
1774 if count >= limit:
1774 if count >= limit:
1775 break
1775 break
1776 parents = [p for p in other.changelog.parents(n) if p != nullid]
1776 parents = [p for p in other.changelog.parents(n) if p != nullid]
1777 if opts.get('no_merges') and len(parents) == 2:
1777 if opts.get('no_merges') and len(parents) == 2:
1778 continue
1778 continue
1779 count += 1
1779 count += 1
1780 displayer.show(other[n])
1780 displayer.show(other[n])
1781 finally:
1781 finally:
1782 if hasattr(other, 'close'):
1782 if hasattr(other, 'close'):
1783 other.close()
1783 other.close()
1784 if cleanup:
1784 if cleanup:
1785 os.unlink(cleanup)
1785 os.unlink(cleanup)
1786
1786
1787 def init(ui, dest=".", **opts):
1787 def init(ui, dest=".", **opts):
1788 """create a new repository in the given directory
1788 """create a new repository in the given directory
1789
1789
1790 Initialize a new repository in the given directory. If the given
1790 Initialize a new repository in the given directory. If the given
1791 directory does not exist, it is created.
1791 directory does not exist, it is created.
1792
1792
1793 If no directory is given, the current directory is used.
1793 If no directory is given, the current directory is used.
1794
1794
1795 It is possible to specify an ssh:// URL as the destination.
1795 It is possible to specify an ssh:// URL as the destination.
1796 See 'hg help urls' for more information.
1796 See 'hg help urls' for more information.
1797 """
1797 """
1798 cmdutil.setremoteconfig(ui, opts)
1798 cmdutil.setremoteconfig(ui, opts)
1799 hg.repository(ui, dest, create=1)
1799 hg.repository(ui, dest, create=1)
1800
1800
1801 def locate(ui, repo, *pats, **opts):
1801 def locate(ui, repo, *pats, **opts):
1802 """locate files matching specific patterns
1802 """locate files matching specific patterns
1803
1803
1804 Print all files under Mercurial control whose names match the
1804 Print all files under Mercurial control whose names match the
1805 given patterns.
1805 given patterns.
1806
1806
1807 This command searches the entire repository by default. To search
1807 This command searches the entire repository by default. To search
1808 just the current directory and its subdirectories, use
1808 just the current directory and its subdirectories, use
1809 "--include .".
1809 "--include .".
1810
1810
1811 If no patterns are given to match, this command prints all file
1811 If no patterns are given to match, this command prints all file
1812 names.
1812 names.
1813
1813
1814 If you want to feed the output of this command into the "xargs"
1814 If you want to feed the output of this command into the "xargs"
1815 command, use the "-0" option to both this command and "xargs".
1815 command, use the "-0" option to both this command and "xargs".
1816 This will avoid the problem of "xargs" treating single filenames
1816 This will avoid the problem of "xargs" treating single filenames
1817 that contain white space as multiple filenames.
1817 that contain white space as multiple filenames.
1818 """
1818 """
1819 end = opts.get('print0') and '\0' or '\n'
1819 end = opts.get('print0') and '\0' or '\n'
1820 rev = opts.get('rev') or None
1820 rev = opts.get('rev') or None
1821
1821
1822 ret = 1
1822 ret = 1
1823 m = cmdutil.match(repo, pats, opts, default='relglob')
1823 m = cmdutil.match(repo, pats, opts, default='relglob')
1824 m.bad = lambda x,y: False
1824 m.bad = lambda x,y: False
1825 for abs in repo[rev].walk(m):
1825 for abs in repo[rev].walk(m):
1826 if not rev and abs not in repo.dirstate:
1826 if not rev and abs not in repo.dirstate:
1827 continue
1827 continue
1828 if opts.get('fullpath'):
1828 if opts.get('fullpath'):
1829 ui.write(repo.wjoin(abs), end)
1829 ui.write(repo.wjoin(abs), end)
1830 else:
1830 else:
1831 ui.write(((pats and m.rel(abs)) or abs), end)
1831 ui.write(((pats and m.rel(abs)) or abs), end)
1832 ret = 0
1832 ret = 0
1833
1833
1834 return ret
1834 return ret
1835
1835
1836 def log(ui, repo, *pats, **opts):
1836 def log(ui, repo, *pats, **opts):
1837 """show revision history of entire repository or files
1837 """show revision history of entire repository or files
1838
1838
1839 Print the revision history of the specified files or the entire
1839 Print the revision history of the specified files or the entire
1840 project.
1840 project.
1841
1841
1842 File history is shown without following rename or copy history of
1842 File history is shown without following rename or copy history of
1843 files. Use -f/--follow with a file name to follow history across
1843 files. Use -f/--follow with a file name to follow history across
1844 renames and copies. --follow without a file name will only show
1844 renames and copies. --follow without a file name will only show
1845 ancestors or descendants of the starting revision. --follow-first
1845 ancestors or descendants of the starting revision. --follow-first
1846 only follows the first parent of merge revisions.
1846 only follows the first parent of merge revisions.
1847
1847
1848 If no revision range is specified, the default is tip:0 unless
1848 If no revision range is specified, the default is tip:0 unless
1849 --follow is set, in which case the working directory parent is
1849 --follow is set, in which case the working directory parent is
1850 used as the starting revision.
1850 used as the starting revision.
1851
1851
1852 See 'hg help dates' for a list of formats valid for -d/--date.
1852 See 'hg help dates' for a list of formats valid for -d/--date.
1853
1853
1854 By default this command outputs: changeset id and hash, tags,
1854 By default this command outputs: changeset id and hash, tags,
1855 non-trivial parents, user, date and time, and a summary for each
1855 non-trivial parents, user, date and time, and a summary for each
1856 commit. When the -v/--verbose switch is used, the list of changed
1856 commit. When the -v/--verbose switch is used, the list of changed
1857 files and full commit message is shown.
1857 files and full commit message is shown.
1858
1858
1859 NOTE: log -p may generate unexpected diff output for merge
1859 NOTE: log -p may generate unexpected diff output for merge
1860 changesets, as it will compare the merge changeset against its
1860 changesets, as it will only compare the merge changeset against
1861 first parent only. Also, the files: list will only reflect files
1861 its first parent. Also, the files: list will only reflect files
1862 that are different from BOTH parents.
1862 that are different from BOTH parents.
1863
1863
1864 """
1864 """
1865
1865
1866 get = util.cachefunc(lambda r: repo[r].changeset())
1866 get = util.cachefunc(lambda r: repo[r].changeset())
1867 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1867 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1868
1868
1869 limit = cmdutil.loglimit(opts)
1869 limit = cmdutil.loglimit(opts)
1870 count = 0
1870 count = 0
1871
1871
1872 if opts.get('copies') and opts.get('rev'):
1872 if opts.get('copies') and opts.get('rev'):
1873 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
1873 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
1874 else:
1874 else:
1875 endrev = len(repo)
1875 endrev = len(repo)
1876 rcache = {}
1876 rcache = {}
1877 ncache = {}
1877 ncache = {}
1878 def getrenamed(fn, rev):
1878 def getrenamed(fn, rev):
1879 '''looks up all renames for a file (up to endrev) the first
1879 '''looks up all renames for a file (up to endrev) the first
1880 time the file is given. It indexes on the changerev and only
1880 time the file is given. It indexes on the changerev and only
1881 parses the manifest if linkrev != changerev.
1881 parses the manifest if linkrev != changerev.
1882 Returns rename info for fn at changerev rev.'''
1882 Returns rename info for fn at changerev rev.'''
1883 if fn not in rcache:
1883 if fn not in rcache:
1884 rcache[fn] = {}
1884 rcache[fn] = {}
1885 ncache[fn] = {}
1885 ncache[fn] = {}
1886 fl = repo.file(fn)
1886 fl = repo.file(fn)
1887 for i in fl:
1887 for i in fl:
1888 node = fl.node(i)
1888 node = fl.node(i)
1889 lr = fl.linkrev(i)
1889 lr = fl.linkrev(i)
1890 renamed = fl.renamed(node)
1890 renamed = fl.renamed(node)
1891 rcache[fn][lr] = renamed
1891 rcache[fn][lr] = renamed
1892 if renamed:
1892 if renamed:
1893 ncache[fn][node] = renamed
1893 ncache[fn][node] = renamed
1894 if lr >= endrev:
1894 if lr >= endrev:
1895 break
1895 break
1896 if rev in rcache[fn]:
1896 if rev in rcache[fn]:
1897 return rcache[fn][rev]
1897 return rcache[fn][rev]
1898
1898
1899 # If linkrev != rev (i.e. rev not found in rcache) fallback to
1899 # If linkrev != rev (i.e. rev not found in rcache) fallback to
1900 # filectx logic.
1900 # filectx logic.
1901
1901
1902 try:
1902 try:
1903 return repo[rev][fn].renamed()
1903 return repo[rev][fn].renamed()
1904 except error.LookupError:
1904 except error.LookupError:
1905 pass
1905 pass
1906 return None
1906 return None
1907
1907
1908 df = False
1908 df = False
1909 if opts["date"]:
1909 if opts["date"]:
1910 df = util.matchdate(opts["date"])
1910 df = util.matchdate(opts["date"])
1911
1911
1912 only_branches = opts.get('only_branch')
1912 only_branches = opts.get('only_branch')
1913
1913
1914 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1914 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1915 for st, rev, fns in changeiter:
1915 for st, rev, fns in changeiter:
1916 if st == 'add':
1916 if st == 'add':
1917 parents = [p for p in repo.changelog.parentrevs(rev)
1917 parents = [p for p in repo.changelog.parentrevs(rev)
1918 if p != nullrev]
1918 if p != nullrev]
1919 if opts.get('no_merges') and len(parents) == 2:
1919 if opts.get('no_merges') and len(parents) == 2:
1920 continue
1920 continue
1921 if opts.get('only_merges') and len(parents) != 2:
1921 if opts.get('only_merges') and len(parents) != 2:
1922 continue
1922 continue
1923
1923
1924 if only_branches:
1924 if only_branches:
1925 revbranch = get(rev)[5]['branch']
1925 revbranch = get(rev)[5]['branch']
1926 if revbranch not in only_branches:
1926 if revbranch not in only_branches:
1927 continue
1927 continue
1928
1928
1929 if df:
1929 if df:
1930 changes = get(rev)
1930 changes = get(rev)
1931 if not df(changes[2][0]):
1931 if not df(changes[2][0]):
1932 continue
1932 continue
1933
1933
1934 if opts.get('keyword'):
1934 if opts.get('keyword'):
1935 changes = get(rev)
1935 changes = get(rev)
1936 miss = 0
1936 miss = 0
1937 for k in [kw.lower() for kw in opts['keyword']]:
1937 for k in [kw.lower() for kw in opts['keyword']]:
1938 if not (k in changes[1].lower() or
1938 if not (k in changes[1].lower() or
1939 k in changes[4].lower() or
1939 k in changes[4].lower() or
1940 k in " ".join(changes[3]).lower()):
1940 k in " ".join(changes[3]).lower()):
1941 miss = 1
1941 miss = 1
1942 break
1942 break
1943 if miss:
1943 if miss:
1944 continue
1944 continue
1945
1945
1946 if opts['user']:
1946 if opts['user']:
1947 changes = get(rev)
1947 changes = get(rev)
1948 miss = 0
1948 miss = 0
1949 for k in opts['user']:
1949 for k in opts['user']:
1950 if k != changes[1]:
1950 if k != changes[1]:
1951 miss = 1
1951 miss = 1
1952 break
1952 break
1953 if miss:
1953 if miss:
1954 continue
1954 continue
1955
1955
1956 copies = []
1956 copies = []
1957 if opts.get('copies') and rev:
1957 if opts.get('copies') and rev:
1958 for fn in get(rev)[3]:
1958 for fn in get(rev)[3]:
1959 rename = getrenamed(fn, rev)
1959 rename = getrenamed(fn, rev)
1960 if rename:
1960 if rename:
1961 copies.append((fn, rename[0]))
1961 copies.append((fn, rename[0]))
1962 displayer.show(context.changectx(repo, rev), copies=copies)
1962 displayer.show(context.changectx(repo, rev), copies=copies)
1963 elif st == 'iter':
1963 elif st == 'iter':
1964 if count == limit: break
1964 if count == limit: break
1965 if displayer.flush(rev):
1965 if displayer.flush(rev):
1966 count += 1
1966 count += 1
1967
1967
1968 def manifest(ui, repo, node=None, rev=None):
1968 def manifest(ui, repo, node=None, rev=None):
1969 """output the current or given revision of the project manifest
1969 """output the current or given revision of the project manifest
1970
1970
1971 Print a list of version controlled files for the given revision.
1971 Print a list of version controlled files for the given revision.
1972 If no revision is given, the parent of the working directory is used,
1972 If no revision is given, the parent of the working directory is used,
1973 or tip if no revision is checked out.
1973 or tip if no revision is checked out.
1974
1974
1975 The manifest is the list of files being version controlled. If no revision
1975 The manifest is the list of files being version controlled. If no revision
1976 is given then the first parent of the working directory is used.
1976 is given then the first parent of the working directory is used.
1977
1977
1978 With -v flag, print file permissions, symlink and executable bits. With
1978 With -v flag, print file permissions, symlink and executable bits. With
1979 --debug flag, print file revision hashes.
1979 --debug flag, print file revision hashes.
1980 """
1980 """
1981
1981
1982 if rev and node:
1982 if rev and node:
1983 raise util.Abort(_("please specify just one revision"))
1983 raise util.Abort(_("please specify just one revision"))
1984
1984
1985 if not node:
1985 if not node:
1986 node = rev
1986 node = rev
1987
1987
1988 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
1988 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
1989 ctx = repo[node]
1989 ctx = repo[node]
1990 for f in ctx:
1990 for f in ctx:
1991 if ui.debugflag:
1991 if ui.debugflag:
1992 ui.write("%40s " % hex(ctx.manifest()[f]))
1992 ui.write("%40s " % hex(ctx.manifest()[f]))
1993 if ui.verbose:
1993 if ui.verbose:
1994 ui.write(decor[ctx.flags(f)])
1994 ui.write(decor[ctx.flags(f)])
1995 ui.write("%s\n" % f)
1995 ui.write("%s\n" % f)
1996
1996
1997 def merge(ui, repo, node=None, force=None, rev=None):
1997 def merge(ui, repo, node=None, force=None, rev=None):
1998 """merge working directory with another revision
1998 """merge working directory with another revision
1999
1999
2000 Merge the contents of the current working directory and the
2000 Merge the contents of the current working directory and the
2001 requested revision. Files that changed between either parent are
2001 requested revision. Files that changed between either parent are
2002 marked as changed for the next commit and a commit must be
2002 marked as changed for the next commit and a commit must be
2003 performed before any further updates are allowed.
2003 performed before any further updates are allowed.
2004
2004
2005 If no revision is specified, the working directory's parent is a
2005 If no revision is specified, the working directory's parent is a
2006 head revision, and the current branch contains exactly one other head,
2006 head revision, and the current branch contains exactly one other head,
2007 the other head is merged with by default. Otherwise, an explicit
2007 the other head is merged with by default. Otherwise, an explicit
2008 revision to merge with must be provided.
2008 revision to merge with must be provided.
2009 """
2009 """
2010
2010
2011 if rev and node:
2011 if rev and node:
2012 raise util.Abort(_("please specify just one revision"))
2012 raise util.Abort(_("please specify just one revision"))
2013 if not node:
2013 if not node:
2014 node = rev
2014 node = rev
2015
2015
2016 if not node:
2016 if not node:
2017 branch = repo.changectx(None).branch()
2017 branch = repo.changectx(None).branch()
2018 bheads = repo.branchheads(branch)
2018 bheads = repo.branchheads(branch)
2019 if len(bheads) > 2:
2019 if len(bheads) > 2:
2020 raise util.Abort(_("branch '%s' has %d heads - "
2020 raise util.Abort(_("branch '%s' has %d heads - "
2021 "please merge with an explicit rev") %
2021 "please merge with an explicit rev") %
2022 (branch, len(bheads)))
2022 (branch, len(bheads)))
2023
2023
2024 parent = repo.dirstate.parents()[0]
2024 parent = repo.dirstate.parents()[0]
2025 if len(bheads) == 1:
2025 if len(bheads) == 1:
2026 if len(repo.heads()) > 1:
2026 if len(repo.heads()) > 1:
2027 raise util.Abort(_("branch '%s' has one head - "
2027 raise util.Abort(_("branch '%s' has one head - "
2028 "please merge with an explicit rev") %
2028 "please merge with an explicit rev") %
2029 branch)
2029 branch)
2030 msg = _('there is nothing to merge')
2030 msg = _('there is nothing to merge')
2031 if parent != repo.lookup(repo[None].branch()):
2031 if parent != repo.lookup(repo[None].branch()):
2032 msg = _('%s - use "hg update" instead') % msg
2032 msg = _('%s - use "hg update" instead') % msg
2033 raise util.Abort(msg)
2033 raise util.Abort(msg)
2034
2034
2035 if parent not in bheads:
2035 if parent not in bheads:
2036 raise util.Abort(_('working dir not at a head rev - '
2036 raise util.Abort(_('working dir not at a head rev - '
2037 'use "hg update" or merge with an explicit rev'))
2037 'use "hg update" or merge with an explicit rev'))
2038 node = parent == bheads[0] and bheads[-1] or bheads[0]
2038 node = parent == bheads[0] and bheads[-1] or bheads[0]
2039 return hg.merge(repo, node, force=force)
2039 return hg.merge(repo, node, force=force)
2040
2040
2041 def outgoing(ui, repo, dest=None, **opts):
2041 def outgoing(ui, repo, dest=None, **opts):
2042 """show changesets not found in destination
2042 """show changesets not found in destination
2043
2043
2044 Show changesets not found in the specified destination repository or
2044 Show changesets not found in the specified destination repository or
2045 the default push location. These are the changesets that would be pushed
2045 the default push location. These are the changesets that would be pushed
2046 if a push was requested.
2046 if a push was requested.
2047
2047
2048 See pull for valid destination format details.
2048 See pull for valid destination format details.
2049 """
2049 """
2050 limit = cmdutil.loglimit(opts)
2050 limit = cmdutil.loglimit(opts)
2051 dest, revs, checkout = hg.parseurl(
2051 dest, revs, checkout = hg.parseurl(
2052 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2052 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2053 cmdutil.setremoteconfig(ui, opts)
2053 cmdutil.setremoteconfig(ui, opts)
2054 if revs:
2054 if revs:
2055 revs = [repo.lookup(rev) for rev in revs]
2055 revs = [repo.lookup(rev) for rev in revs]
2056
2056
2057 other = hg.repository(ui, dest)
2057 other = hg.repository(ui, dest)
2058 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2058 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2059 o = repo.findoutgoing(other, force=opts.get('force'))
2059 o = repo.findoutgoing(other, force=opts.get('force'))
2060 if not o:
2060 if not o:
2061 ui.status(_("no changes found\n"))
2061 ui.status(_("no changes found\n"))
2062 return 1
2062 return 1
2063 o = repo.changelog.nodesbetween(o, revs)[0]
2063 o = repo.changelog.nodesbetween(o, revs)[0]
2064 if opts.get('newest_first'):
2064 if opts.get('newest_first'):
2065 o.reverse()
2065 o.reverse()
2066 displayer = cmdutil.show_changeset(ui, repo, opts)
2066 displayer = cmdutil.show_changeset(ui, repo, opts)
2067 count = 0
2067 count = 0
2068 for n in o:
2068 for n in o:
2069 if count >= limit:
2069 if count >= limit:
2070 break
2070 break
2071 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2071 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2072 if opts.get('no_merges') and len(parents) == 2:
2072 if opts.get('no_merges') and len(parents) == 2:
2073 continue
2073 continue
2074 count += 1
2074 count += 1
2075 displayer.show(repo[n])
2075 displayer.show(repo[n])
2076
2076
2077 def parents(ui, repo, file_=None, **opts):
2077 def parents(ui, repo, file_=None, **opts):
2078 """show the parents of the working dir or revision
2078 """show the parents of the working dir or revision
2079
2079
2080 Print the working directory's parent revisions. If a
2080 Print the working directory's parent revisions. If a
2081 revision is given via --rev, the parent of that revision
2081 revision is given via --rev, the parent of that revision
2082 will be printed. If a file argument is given, revision in
2082 will be printed. If a file argument is given, revision in
2083 which the file was last changed (before the working directory
2083 which the file was last changed (before the working directory
2084 revision or the argument to --rev if given) is printed.
2084 revision or the argument to --rev if given) is printed.
2085 """
2085 """
2086 rev = opts.get('rev')
2086 rev = opts.get('rev')
2087 if rev:
2087 if rev:
2088 ctx = repo[rev]
2088 ctx = repo[rev]
2089 else:
2089 else:
2090 ctx = repo[None]
2090 ctx = repo[None]
2091
2091
2092 if file_:
2092 if file_:
2093 m = cmdutil.match(repo, (file_,), opts)
2093 m = cmdutil.match(repo, (file_,), opts)
2094 if m.anypats() or len(m.files()) != 1:
2094 if m.anypats() or len(m.files()) != 1:
2095 raise util.Abort(_('can only specify an explicit file name'))
2095 raise util.Abort(_('can only specify an explicit file name'))
2096 file_ = m.files()[0]
2096 file_ = m.files()[0]
2097 filenodes = []
2097 filenodes = []
2098 for cp in ctx.parents():
2098 for cp in ctx.parents():
2099 if not cp:
2099 if not cp:
2100 continue
2100 continue
2101 try:
2101 try:
2102 filenodes.append(cp.filenode(file_))
2102 filenodes.append(cp.filenode(file_))
2103 except error.LookupError:
2103 except error.LookupError:
2104 pass
2104 pass
2105 if not filenodes:
2105 if not filenodes:
2106 raise util.Abort(_("'%s' not found in manifest!") % file_)
2106 raise util.Abort(_("'%s' not found in manifest!") % file_)
2107 fl = repo.file(file_)
2107 fl = repo.file(file_)
2108 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2108 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2109 else:
2109 else:
2110 p = [cp.node() for cp in ctx.parents()]
2110 p = [cp.node() for cp in ctx.parents()]
2111
2111
2112 displayer = cmdutil.show_changeset(ui, repo, opts)
2112 displayer = cmdutil.show_changeset(ui, repo, opts)
2113 for n in p:
2113 for n in p:
2114 if n != nullid:
2114 if n != nullid:
2115 displayer.show(repo[n])
2115 displayer.show(repo[n])
2116
2116
2117 def paths(ui, repo, search=None):
2117 def paths(ui, repo, search=None):
2118 """show aliases for remote repositories
2118 """show aliases for remote repositories
2119
2119
2120 Show definition of symbolic path name NAME. If no name is given, show
2120 Show definition of symbolic path name NAME. If no name is given, show
2121 definition of available names.
2121 definition of available names.
2122
2122
2123 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2123 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2124 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2124 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2125
2125
2126 See 'hg help urls' for more information.
2126 See 'hg help urls' for more information.
2127 """
2127 """
2128 if search:
2128 if search:
2129 for name, path in ui.configitems("paths"):
2129 for name, path in ui.configitems("paths"):
2130 if name == search:
2130 if name == search:
2131 ui.write("%s\n" % url.hidepassword(path))
2131 ui.write("%s\n" % url.hidepassword(path))
2132 return
2132 return
2133 ui.warn(_("not found!\n"))
2133 ui.warn(_("not found!\n"))
2134 return 1
2134 return 1
2135 else:
2135 else:
2136 for name, path in ui.configitems("paths"):
2136 for name, path in ui.configitems("paths"):
2137 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2137 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2138
2138
2139 def postincoming(ui, repo, modheads, optupdate, checkout):
2139 def postincoming(ui, repo, modheads, optupdate, checkout):
2140 if modheads == 0:
2140 if modheads == 0:
2141 return
2141 return
2142 if optupdate:
2142 if optupdate:
2143 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2143 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2144 return hg.update(repo, checkout)
2144 return hg.update(repo, checkout)
2145 else:
2145 else:
2146 ui.status(_("not updating, since new heads added\n"))
2146 ui.status(_("not updating, since new heads added\n"))
2147 if modheads > 1:
2147 if modheads > 1:
2148 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2148 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2149 else:
2149 else:
2150 ui.status(_("(run 'hg update' to get a working copy)\n"))
2150 ui.status(_("(run 'hg update' to get a working copy)\n"))
2151
2151
2152 def pull(ui, repo, source="default", **opts):
2152 def pull(ui, repo, source="default", **opts):
2153 """pull changes from the specified source
2153 """pull changes from the specified source
2154
2154
2155 Pull changes from a remote repository to a local one.
2155 Pull changes from a remote repository to a local one.
2156
2156
2157 This finds all changes from the repository at the specified path
2157 This finds all changes from the repository at the specified path
2158 or URL and adds them to the local repository. By default, this
2158 or URL and adds them to the local repository. By default, this
2159 does not update the copy of the project in the working directory.
2159 does not update the copy of the project in the working directory.
2160
2160
2161 If SOURCE is omitted, the 'default' path will be used.
2161 If SOURCE is omitted, the 'default' path will be used.
2162 See 'hg help urls' for more information.
2162 See 'hg help urls' for more information.
2163 """
2163 """
2164 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
2164 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
2165 cmdutil.setremoteconfig(ui, opts)
2165 cmdutil.setremoteconfig(ui, opts)
2166
2166
2167 other = hg.repository(ui, source)
2167 other = hg.repository(ui, source)
2168 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2168 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2169 if revs:
2169 if revs:
2170 try:
2170 try:
2171 revs = [other.lookup(rev) for rev in revs]
2171 revs = [other.lookup(rev) for rev in revs]
2172 except error.CapabilityError:
2172 except error.CapabilityError:
2173 err = _("Other repository doesn't support revision lookup, "
2173 err = _("Other repository doesn't support revision lookup, "
2174 "so a rev cannot be specified.")
2174 "so a rev cannot be specified.")
2175 raise util.Abort(err)
2175 raise util.Abort(err)
2176
2176
2177 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2177 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2178 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2178 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2179
2179
2180 def push(ui, repo, dest=None, **opts):
2180 def push(ui, repo, dest=None, **opts):
2181 """push changes to the specified destination
2181 """push changes to the specified destination
2182
2182
2183 Push changes from the local repository to the given destination.
2183 Push changes from the local repository to the given destination.
2184
2184
2185 This is the symmetrical operation for pull. It helps to move
2185 This is the symmetrical operation for pull. It helps to move
2186 changes from the current repository to a different one. If the
2186 changes from the current repository to a different one. If the
2187 destination is local this is identical to a pull in that directory
2187 destination is local this is identical to a pull in that directory
2188 from the current one.
2188 from the current one.
2189
2189
2190 By default, push will refuse to run if it detects the result would
2190 By default, push will refuse to run if it detects the result would
2191 increase the number of remote heads. This generally indicates the
2191 increase the number of remote heads. This generally indicates the
2192 the client has forgotten to pull and merge before pushing.
2192 the client has forgotten to pull and merge before pushing.
2193
2193
2194 If -r is used, the named changeset and all its ancestors will be pushed
2194 If -r is used, the named changeset and all its ancestors will be pushed
2195 to the remote repository.
2195 to the remote repository.
2196
2196
2197 Look at the help text for urls for important details about ssh:// URLs.
2197 Look at the help text for urls for important details about ssh:// URLs.
2198 If DESTINATION is omitted, a default path will be used.
2198 If DESTINATION is omitted, a default path will be used.
2199 See 'hg help urls' for more information.
2199 See 'hg help urls' for more information.
2200 """
2200 """
2201 dest, revs, checkout = hg.parseurl(
2201 dest, revs, checkout = hg.parseurl(
2202 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2202 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2203 cmdutil.setremoteconfig(ui, opts)
2203 cmdutil.setremoteconfig(ui, opts)
2204
2204
2205 other = hg.repository(ui, dest)
2205 other = hg.repository(ui, dest)
2206 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2206 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2207 if revs:
2207 if revs:
2208 revs = [repo.lookup(rev) for rev in revs]
2208 revs = [repo.lookup(rev) for rev in revs]
2209 r = repo.push(other, opts.get('force'), revs=revs)
2209 r = repo.push(other, opts.get('force'), revs=revs)
2210 return r == 0
2210 return r == 0
2211
2211
2212 def rawcommit(ui, repo, *pats, **opts):
2212 def rawcommit(ui, repo, *pats, **opts):
2213 """raw commit interface (DEPRECATED)
2213 """raw commit interface (DEPRECATED)
2214
2214
2215 (DEPRECATED)
2215 (DEPRECATED)
2216 Lowlevel commit, for use in helper scripts.
2216 Lowlevel commit, for use in helper scripts.
2217
2217
2218 This command is not intended to be used by normal users, as it is
2218 This command is not intended to be used by normal users, as it is
2219 primarily useful for importing from other SCMs.
2219 primarily useful for importing from other SCMs.
2220
2220
2221 This command is now deprecated and will be removed in a future
2221 This command is now deprecated and will be removed in a future
2222 release, please use debugsetparents and commit instead.
2222 release, please use debugsetparents and commit instead.
2223 """
2223 """
2224
2224
2225 ui.warn(_("(the rawcommit command is deprecated)\n"))
2225 ui.warn(_("(the rawcommit command is deprecated)\n"))
2226
2226
2227 message = cmdutil.logmessage(opts)
2227 message = cmdutil.logmessage(opts)
2228
2228
2229 files = cmdutil.match(repo, pats, opts).files()
2229 files = cmdutil.match(repo, pats, opts).files()
2230 if opts.get('files'):
2230 if opts.get('files'):
2231 files += open(opts['files']).read().splitlines()
2231 files += open(opts['files']).read().splitlines()
2232
2232
2233 parents = [repo.lookup(p) for p in opts['parent']]
2233 parents = [repo.lookup(p) for p in opts['parent']]
2234
2234
2235 try:
2235 try:
2236 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2236 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2237 except ValueError, inst:
2237 except ValueError, inst:
2238 raise util.Abort(str(inst))
2238 raise util.Abort(str(inst))
2239
2239
2240 def recover(ui, repo):
2240 def recover(ui, repo):
2241 """roll back an interrupted transaction
2241 """roll back an interrupted transaction
2242
2242
2243 Recover from an interrupted commit or pull.
2243 Recover from an interrupted commit or pull.
2244
2244
2245 This command tries to fix the repository status after an interrupted
2245 This command tries to fix the repository status after an interrupted
2246 operation. It should only be necessary when Mercurial suggests it.
2246 operation. It should only be necessary when Mercurial suggests it.
2247 """
2247 """
2248 if repo.recover():
2248 if repo.recover():
2249 return hg.verify(repo)
2249 return hg.verify(repo)
2250 return 1
2250 return 1
2251
2251
2252 def remove(ui, repo, *pats, **opts):
2252 def remove(ui, repo, *pats, **opts):
2253 """remove the specified files on the next commit
2253 """remove the specified files on the next commit
2254
2254
2255 Schedule the indicated files for removal from the repository.
2255 Schedule the indicated files for removal from the repository.
2256
2256
2257 This only removes files from the current branch, not from the entire
2257 This only removes files from the current branch, not from the entire
2258 project history. -A can be used to remove only files that have already
2258 project history. -A can be used to remove only files that have already
2259 been deleted, -f can be used to force deletion, and -Af can be used
2259 been deleted, -f can be used to force deletion, and -Af can be used
2260 to remove files from the next revision without deleting them.
2260 to remove files from the next revision without deleting them.
2261
2261
2262 The following table details the behavior of remove for different file
2262 The following table details the behavior of remove for different file
2263 states (columns) and option combinations (rows). The file states are
2263 states (columns) and option combinations (rows). The file states are
2264 Added, Clean, Modified and Missing (as reported by hg status). The
2264 Added, Clean, Modified and Missing (as reported by hg status). The
2265 actions are Warn, Remove (from branch) and Delete (from disk).
2265 actions are Warn, Remove (from branch) and Delete (from disk).
2266
2266
2267 A C M !
2267 A C M !
2268 none W RD W R
2268 none W RD W R
2269 -f R RD RD R
2269 -f R RD RD R
2270 -A W W W R
2270 -A W W W R
2271 -Af R R R R
2271 -Af R R R R
2272
2272
2273 This command schedules the files to be removed at the next commit.
2273 This command schedules the files to be removed at the next commit.
2274 To undo a remove before that, see hg revert.
2274 To undo a remove before that, see hg revert.
2275 """
2275 """
2276
2276
2277 after, force = opts.get('after'), opts.get('force')
2277 after, force = opts.get('after'), opts.get('force')
2278 if not pats and not after:
2278 if not pats and not after:
2279 raise util.Abort(_('no files specified'))
2279 raise util.Abort(_('no files specified'))
2280
2280
2281 m = cmdutil.match(repo, pats, opts)
2281 m = cmdutil.match(repo, pats, opts)
2282 s = repo.status(match=m, clean=True)
2282 s = repo.status(match=m, clean=True)
2283 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2283 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2284
2284
2285 def warn(files, reason):
2285 def warn(files, reason):
2286 for f in files:
2286 for f in files:
2287 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2287 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2288 % (m.rel(f), reason))
2288 % (m.rel(f), reason))
2289
2289
2290 if force:
2290 if force:
2291 remove, forget = modified + deleted + clean, added
2291 remove, forget = modified + deleted + clean, added
2292 elif after:
2292 elif after:
2293 remove, forget = deleted, []
2293 remove, forget = deleted, []
2294 warn(modified + added + clean, _('still exists'))
2294 warn(modified + added + clean, _('still exists'))
2295 else:
2295 else:
2296 remove, forget = deleted + clean, []
2296 remove, forget = deleted + clean, []
2297 warn(modified, _('is modified'))
2297 warn(modified, _('is modified'))
2298 warn(added, _('has been marked for add'))
2298 warn(added, _('has been marked for add'))
2299
2299
2300 for f in util.sort(remove + forget):
2300 for f in util.sort(remove + forget):
2301 if ui.verbose or not m.exact(f):
2301 if ui.verbose or not m.exact(f):
2302 ui.status(_('removing %s\n') % m.rel(f))
2302 ui.status(_('removing %s\n') % m.rel(f))
2303
2303
2304 repo.forget(forget)
2304 repo.forget(forget)
2305 repo.remove(remove, unlink=not after)
2305 repo.remove(remove, unlink=not after)
2306
2306
2307 def rename(ui, repo, *pats, **opts):
2307 def rename(ui, repo, *pats, **opts):
2308 """rename files; equivalent of copy + remove
2308 """rename files; equivalent of copy + remove
2309
2309
2310 Mark dest as copies of sources; mark sources for deletion. If
2310 Mark dest as copies of sources; mark sources for deletion. If
2311 dest is a directory, copies are put in that directory. If dest is
2311 dest is a directory, copies are put in that directory. If dest is
2312 a file, there can only be one source.
2312 a file, there can only be one source.
2313
2313
2314 By default, this command copies the contents of files as they
2314 By default, this command copies the contents of files as they
2315 stand in the working directory. If invoked with --after, the
2315 exist in the working directory. If invoked with --after, the
2316 operation is recorded, but no copying is performed.
2316 operation is recorded, but no copying is performed.
2317
2317
2318 This command takes effect in the next commit. To undo a rename
2318 This command takes effect at the next commit. To undo a rename
2319 before that, see hg revert.
2319 before that, see hg revert.
2320 """
2320 """
2321 wlock = repo.wlock(False)
2321 wlock = repo.wlock(False)
2322 try:
2322 try:
2323 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2323 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2324 finally:
2324 finally:
2325 del wlock
2325 del wlock
2326
2326
2327 def resolve(ui, repo, *pats, **opts):
2327 def resolve(ui, repo, *pats, **opts):
2328 """retry file merges from a merge or update
2328 """retry file merges from a merge or update
2329
2329
2330 This command will cleanly retry unresolved file merges using file
2330 This command will cleanly retry unresolved file merges using file
2331 revisions preserved from the last update or merge. To attempt to
2331 revisions preserved from the last update or merge. To attempt to
2332 resolve all unresolved files, use the -a switch.
2332 resolve all unresolved files, use the -a switch.
2333
2333
2334 This command will also allow listing resolved files and manually
2334 This command will also allow listing resolved files and manually
2335 marking and unmarking files as resolved.
2335 marking and unmarking files as resolved.
2336
2336
2337 The codes used to show the status of files are:
2337 The codes used to show the status of files are:
2338 U = unresolved
2338 U = unresolved
2339 R = resolved
2339 R = resolved
2340 """
2340 """
2341
2341
2342 all, mark, unmark, show = [opts.get(o) for o in 'all mark unmark list'.split()]
2342 all, mark, unmark, show = [opts.get(o) for o in 'all mark unmark list'.split()]
2343
2343
2344 if (show and (mark or unmark)) or (mark and unmark):
2344 if (show and (mark or unmark)) or (mark and unmark):
2345 raise util.Abort(_("too many options specified"))
2345 raise util.Abort(_("too many options specified"))
2346 if pats and all:
2346 if pats and all:
2347 raise util.Abort(_("can't specify --all and patterns"))
2347 raise util.Abort(_("can't specify --all and patterns"))
2348 if not (all or pats or show or mark or unmark):
2348 if not (all or pats or show or mark or unmark):
2349 raise util.Abort(_('no files or directories specified; '
2349 raise util.Abort(_('no files or directories specified; '
2350 'use --all to remerge all files'))
2350 'use --all to remerge all files'))
2351
2351
2352 ms = merge_.mergestate(repo)
2352 ms = merge_.mergestate(repo)
2353 m = cmdutil.match(repo, pats, opts)
2353 m = cmdutil.match(repo, pats, opts)
2354
2354
2355 for f in ms:
2355 for f in ms:
2356 if m(f):
2356 if m(f):
2357 if show:
2357 if show:
2358 ui.write("%s %s\n" % (ms[f].upper(), f))
2358 ui.write("%s %s\n" % (ms[f].upper(), f))
2359 elif mark:
2359 elif mark:
2360 ms.mark(f, "r")
2360 ms.mark(f, "r")
2361 elif unmark:
2361 elif unmark:
2362 ms.mark(f, "u")
2362 ms.mark(f, "u")
2363 else:
2363 else:
2364 wctx = repo[None]
2364 wctx = repo[None]
2365 mctx = wctx.parents()[-1]
2365 mctx = wctx.parents()[-1]
2366 ms.resolve(f, wctx, mctx)
2366 ms.resolve(f, wctx, mctx)
2367
2367
2368 def revert(ui, repo, *pats, **opts):
2368 def revert(ui, repo, *pats, **opts):
2369 """restore individual files or dirs to an earlier state
2369 """restore individual files or dirs to an earlier state
2370
2370
2371 (use update -r to check out earlier revisions, revert does not
2371 (use update -r to check out earlier revisions, revert does not
2372 change the working dir parents)
2372 change the working dir parents)
2373
2373
2374 With no revision specified, revert the named files or directories
2374 With no revision specified, revert the named files or directories
2375 to the contents they had in the parent of the working directory.
2375 to the contents they had in the parent of the working directory.
2376 This restores the contents of the affected files to an unmodified
2376 This restores the contents of the affected files to an unmodified
2377 state and unschedules adds, removes, copies, and renames. If the
2377 state and unschedules adds, removes, copies, and renames. If the
2378 working directory has two parents, you must explicitly specify the
2378 working directory has two parents, you must explicitly specify the
2379 revision to revert to.
2379 revision to revert to.
2380
2380
2381 Using the -r option, revert the given files or directories to their
2381 Using the -r option, revert the given files or directories to their
2382 contents as of a specific revision. This can be helpful to "roll
2382 contents as of a specific revision. This can be helpful to "roll
2383 back" some or all of an earlier change.
2383 back" some or all of an earlier change.
2384 See 'hg help dates' for a list of formats valid for -d/--date.
2384 See 'hg help dates' for a list of formats valid for -d/--date.
2385
2385
2386 Revert modifies the working directory. It does not commit any
2386 Revert modifies the working directory. It does not commit any
2387 changes, or change the parent of the working directory. If you
2387 changes, or change the parent of the working directory. If you
2388 revert to a revision other than the parent of the working
2388 revert to a revision other than the parent of the working
2389 directory, the reverted files will thus appear modified
2389 directory, the reverted files will thus appear modified
2390 afterwards.
2390 afterwards.
2391
2391
2392 If a file has been deleted, it is restored. If the executable
2392 If a file has been deleted, it is restored. If the executable
2393 mode of a file was changed, it is reset.
2393 mode of a file was changed, it is reset.
2394
2394
2395 If names are given, all files matching the names are reverted.
2395 If names are given, all files matching the names are reverted.
2396 If no arguments are given, no files are reverted.
2396 If no arguments are given, no files are reverted.
2397
2397
2398 Modified files are saved with a .orig suffix before reverting.
2398 Modified files are saved with a .orig suffix before reverting.
2399 To disable these backups, use --no-backup.
2399 To disable these backups, use --no-backup.
2400 """
2400 """
2401
2401
2402 if opts["date"]:
2402 if opts["date"]:
2403 if opts["rev"]:
2403 if opts["rev"]:
2404 raise util.Abort(_("you can't specify a revision and a date"))
2404 raise util.Abort(_("you can't specify a revision and a date"))
2405 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2405 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2406
2406
2407 if not pats and not opts.get('all'):
2407 if not pats and not opts.get('all'):
2408 raise util.Abort(_('no files or directories specified; '
2408 raise util.Abort(_('no files or directories specified; '
2409 'use --all to revert the whole repo'))
2409 'use --all to revert the whole repo'))
2410
2410
2411 parent, p2 = repo.dirstate.parents()
2411 parent, p2 = repo.dirstate.parents()
2412 if not opts.get('rev') and p2 != nullid:
2412 if not opts.get('rev') and p2 != nullid:
2413 raise util.Abort(_('uncommitted merge - please provide a '
2413 raise util.Abort(_('uncommitted merge - please provide a '
2414 'specific revision'))
2414 'specific revision'))
2415 ctx = repo[opts.get('rev')]
2415 ctx = repo[opts.get('rev')]
2416 node = ctx.node()
2416 node = ctx.node()
2417 mf = ctx.manifest()
2417 mf = ctx.manifest()
2418 if node == parent:
2418 if node == parent:
2419 pmf = mf
2419 pmf = mf
2420 else:
2420 else:
2421 pmf = None
2421 pmf = None
2422
2422
2423 # need all matching names in dirstate and manifest of target rev,
2423 # need all matching names in dirstate and manifest of target rev,
2424 # so have to walk both. do not print errors if files exist in one
2424 # so have to walk both. do not print errors if files exist in one
2425 # but not other.
2425 # but not other.
2426
2426
2427 names = {}
2427 names = {}
2428
2428
2429 wlock = repo.wlock()
2429 wlock = repo.wlock()
2430 try:
2430 try:
2431 # walk dirstate.
2431 # walk dirstate.
2432 files = []
2432 files = []
2433
2433
2434 m = cmdutil.match(repo, pats, opts)
2434 m = cmdutil.match(repo, pats, opts)
2435 m.bad = lambda x,y: False
2435 m.bad = lambda x,y: False
2436 for abs in repo.walk(m):
2436 for abs in repo.walk(m):
2437 names[abs] = m.rel(abs), m.exact(abs)
2437 names[abs] = m.rel(abs), m.exact(abs)
2438
2438
2439 # walk target manifest.
2439 # walk target manifest.
2440
2440
2441 def badfn(path, msg):
2441 def badfn(path, msg):
2442 if path in names:
2442 if path in names:
2443 return False
2443 return False
2444 path_ = path + '/'
2444 path_ = path + '/'
2445 for f in names:
2445 for f in names:
2446 if f.startswith(path_):
2446 if f.startswith(path_):
2447 return False
2447 return False
2448 repo.ui.warn("%s: %s\n" % (m.rel(path), msg))
2448 repo.ui.warn("%s: %s\n" % (m.rel(path), msg))
2449 return False
2449 return False
2450
2450
2451 m = cmdutil.match(repo, pats, opts)
2451 m = cmdutil.match(repo, pats, opts)
2452 m.bad = badfn
2452 m.bad = badfn
2453 for abs in repo[node].walk(m):
2453 for abs in repo[node].walk(m):
2454 if abs not in names:
2454 if abs not in names:
2455 names[abs] = m.rel(abs), m.exact(abs)
2455 names[abs] = m.rel(abs), m.exact(abs)
2456
2456
2457 m = cmdutil.matchfiles(repo, names)
2457 m = cmdutil.matchfiles(repo, names)
2458 changes = repo.status(match=m)[:4]
2458 changes = repo.status(match=m)[:4]
2459 modified, added, removed, deleted = map(dict.fromkeys, changes)
2459 modified, added, removed, deleted = map(dict.fromkeys, changes)
2460
2460
2461 # if f is a rename, also revert the source
2461 # if f is a rename, also revert the source
2462 cwd = repo.getcwd()
2462 cwd = repo.getcwd()
2463 for f in added:
2463 for f in added:
2464 src = repo.dirstate.copied(f)
2464 src = repo.dirstate.copied(f)
2465 if src and src not in names and repo.dirstate[src] == 'r':
2465 if src and src not in names and repo.dirstate[src] == 'r':
2466 removed[src] = None
2466 removed[src] = None
2467 names[src] = (repo.pathto(src, cwd), True)
2467 names[src] = (repo.pathto(src, cwd), True)
2468
2468
2469 def removeforget(abs):
2469 def removeforget(abs):
2470 if repo.dirstate[abs] == 'a':
2470 if repo.dirstate[abs] == 'a':
2471 return _('forgetting %s\n')
2471 return _('forgetting %s\n')
2472 return _('removing %s\n')
2472 return _('removing %s\n')
2473
2473
2474 revert = ([], _('reverting %s\n'))
2474 revert = ([], _('reverting %s\n'))
2475 add = ([], _('adding %s\n'))
2475 add = ([], _('adding %s\n'))
2476 remove = ([], removeforget)
2476 remove = ([], removeforget)
2477 undelete = ([], _('undeleting %s\n'))
2477 undelete = ([], _('undeleting %s\n'))
2478
2478
2479 disptable = (
2479 disptable = (
2480 # dispatch table:
2480 # dispatch table:
2481 # file state
2481 # file state
2482 # action if in target manifest
2482 # action if in target manifest
2483 # action if not in target manifest
2483 # action if not in target manifest
2484 # make backup if in target manifest
2484 # make backup if in target manifest
2485 # make backup if not in target manifest
2485 # make backup if not in target manifest
2486 (modified, revert, remove, True, True),
2486 (modified, revert, remove, True, True),
2487 (added, revert, remove, True, False),
2487 (added, revert, remove, True, False),
2488 (removed, undelete, None, False, False),
2488 (removed, undelete, None, False, False),
2489 (deleted, revert, remove, False, False),
2489 (deleted, revert, remove, False, False),
2490 )
2490 )
2491
2491
2492 for abs, (rel, exact) in util.sort(names.items()):
2492 for abs, (rel, exact) in util.sort(names.items()):
2493 mfentry = mf.get(abs)
2493 mfentry = mf.get(abs)
2494 target = repo.wjoin(abs)
2494 target = repo.wjoin(abs)
2495 def handle(xlist, dobackup):
2495 def handle(xlist, dobackup):
2496 xlist[0].append(abs)
2496 xlist[0].append(abs)
2497 if dobackup and not opts.get('no_backup') and util.lexists(target):
2497 if dobackup and not opts.get('no_backup') and util.lexists(target):
2498 bakname = "%s.orig" % rel
2498 bakname = "%s.orig" % rel
2499 ui.note(_('saving current version of %s as %s\n') %
2499 ui.note(_('saving current version of %s as %s\n') %
2500 (rel, bakname))
2500 (rel, bakname))
2501 if not opts.get('dry_run'):
2501 if not opts.get('dry_run'):
2502 util.copyfile(target, bakname)
2502 util.copyfile(target, bakname)
2503 if ui.verbose or not exact:
2503 if ui.verbose or not exact:
2504 msg = xlist[1]
2504 msg = xlist[1]
2505 if not isinstance(msg, basestring):
2505 if not isinstance(msg, basestring):
2506 msg = msg(abs)
2506 msg = msg(abs)
2507 ui.status(msg % rel)
2507 ui.status(msg % rel)
2508 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2508 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2509 if abs not in table: continue
2509 if abs not in table: continue
2510 # file has changed in dirstate
2510 # file has changed in dirstate
2511 if mfentry:
2511 if mfentry:
2512 handle(hitlist, backuphit)
2512 handle(hitlist, backuphit)
2513 elif misslist is not None:
2513 elif misslist is not None:
2514 handle(misslist, backupmiss)
2514 handle(misslist, backupmiss)
2515 break
2515 break
2516 else:
2516 else:
2517 if abs not in repo.dirstate:
2517 if abs not in repo.dirstate:
2518 if mfentry:
2518 if mfentry:
2519 handle(add, True)
2519 handle(add, True)
2520 elif exact:
2520 elif exact:
2521 ui.warn(_('file not managed: %s\n') % rel)
2521 ui.warn(_('file not managed: %s\n') % rel)
2522 continue
2522 continue
2523 # file has not changed in dirstate
2523 # file has not changed in dirstate
2524 if node == parent:
2524 if node == parent:
2525 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2525 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2526 continue
2526 continue
2527 if pmf is None:
2527 if pmf is None:
2528 # only need parent manifest in this unlikely case,
2528 # only need parent manifest in this unlikely case,
2529 # so do not read by default
2529 # so do not read by default
2530 pmf = repo[parent].manifest()
2530 pmf = repo[parent].manifest()
2531 if abs in pmf:
2531 if abs in pmf:
2532 if mfentry:
2532 if mfentry:
2533 # if version of file is same in parent and target
2533 # if version of file is same in parent and target
2534 # manifests, do nothing
2534 # manifests, do nothing
2535 if (pmf[abs] != mfentry or
2535 if (pmf[abs] != mfentry or
2536 pmf.flags(abs) != mf.flags(abs)):
2536 pmf.flags(abs) != mf.flags(abs)):
2537 handle(revert, False)
2537 handle(revert, False)
2538 else:
2538 else:
2539 handle(remove, False)
2539 handle(remove, False)
2540
2540
2541 if not opts.get('dry_run'):
2541 if not opts.get('dry_run'):
2542 def checkout(f):
2542 def checkout(f):
2543 fc = ctx[f]
2543 fc = ctx[f]
2544 repo.wwrite(f, fc.data(), fc.flags())
2544 repo.wwrite(f, fc.data(), fc.flags())
2545
2545
2546 audit_path = util.path_auditor(repo.root)
2546 audit_path = util.path_auditor(repo.root)
2547 for f in remove[0]:
2547 for f in remove[0]:
2548 if repo.dirstate[f] == 'a':
2548 if repo.dirstate[f] == 'a':
2549 repo.dirstate.forget(f)
2549 repo.dirstate.forget(f)
2550 continue
2550 continue
2551 audit_path(f)
2551 audit_path(f)
2552 try:
2552 try:
2553 util.unlink(repo.wjoin(f))
2553 util.unlink(repo.wjoin(f))
2554 except OSError:
2554 except OSError:
2555 pass
2555 pass
2556 repo.dirstate.remove(f)
2556 repo.dirstate.remove(f)
2557
2557
2558 normal = None
2558 normal = None
2559 if node == parent:
2559 if node == parent:
2560 # We're reverting to our parent. If possible, we'd like status
2560 # We're reverting to our parent. If possible, we'd like status
2561 # to report the file as clean. We have to use normallookup for
2561 # to report the file as clean. We have to use normallookup for
2562 # merges to avoid losing information about merged/dirty files.
2562 # merges to avoid losing information about merged/dirty files.
2563 if p2 != nullid:
2563 if p2 != nullid:
2564 normal = repo.dirstate.normallookup
2564 normal = repo.dirstate.normallookup
2565 else:
2565 else:
2566 normal = repo.dirstate.normal
2566 normal = repo.dirstate.normal
2567 for f in revert[0]:
2567 for f in revert[0]:
2568 checkout(f)
2568 checkout(f)
2569 if normal:
2569 if normal:
2570 normal(f)
2570 normal(f)
2571
2571
2572 for f in add[0]:
2572 for f in add[0]:
2573 checkout(f)
2573 checkout(f)
2574 repo.dirstate.add(f)
2574 repo.dirstate.add(f)
2575
2575
2576 normal = repo.dirstate.normallookup
2576 normal = repo.dirstate.normallookup
2577 if node == parent and p2 == nullid:
2577 if node == parent and p2 == nullid:
2578 normal = repo.dirstate.normal
2578 normal = repo.dirstate.normal
2579 for f in undelete[0]:
2579 for f in undelete[0]:
2580 checkout(f)
2580 checkout(f)
2581 normal(f)
2581 normal(f)
2582
2582
2583 finally:
2583 finally:
2584 del wlock
2584 del wlock
2585
2585
2586 def rollback(ui, repo):
2586 def rollback(ui, repo):
2587 """roll back the last transaction
2587 """roll back the last transaction
2588
2588
2589 This command should be used with care. There is only one level of
2589 This command should be used with care. There is only one level of
2590 rollback, and there is no way to undo a rollback. It will also
2590 rollback, and there is no way to undo a rollback. It will also
2591 restore the dirstate at the time of the last transaction, losing
2591 restore the dirstate at the time of the last transaction, losing
2592 any dirstate changes since that time.
2592 any dirstate changes since that time.
2593
2593
2594 Transactions are used to encapsulate the effects of all commands
2594 Transactions are used to encapsulate the effects of all commands
2595 that create new changesets or propagate existing changesets into a
2595 that create new changesets or propagate existing changesets into a
2596 repository. For example, the following commands are transactional,
2596 repository. For example, the following commands are transactional,
2597 and their effects can be rolled back:
2597 and their effects can be rolled back:
2598
2598
2599 commit
2599 commit
2600 import
2600 import
2601 pull
2601 pull
2602 push (with this repository as destination)
2602 push (with this repository as destination)
2603 unbundle
2603 unbundle
2604
2604
2605 This command is not intended for use on public repositories. Once
2605 This command is not intended for use on public repositories. Once
2606 changes are visible for pull by other users, rolling a transaction
2606 changes are visible for pull by other users, rolling a transaction
2607 back locally is ineffective (someone else may already have pulled
2607 back locally is ineffective (someone else may already have pulled
2608 the changes). Furthermore, a race is possible with readers of the
2608 the changes). Furthermore, a race is possible with readers of the
2609 repository; for example an in-progress pull from the repository
2609 repository; for example an in-progress pull from the repository
2610 may fail if a rollback is performed.
2610 may fail if a rollback is performed.
2611 """
2611 """
2612 repo.rollback()
2612 repo.rollback()
2613
2613
2614 def root(ui, repo):
2614 def root(ui, repo):
2615 """print the root (top) of the current working dir
2615 """print the root (top) of the current working dir
2616
2616
2617 Print the root directory of the current repository.
2617 Print the root directory of the current repository.
2618 """
2618 """
2619 ui.write(repo.root + "\n")
2619 ui.write(repo.root + "\n")
2620
2620
2621 def serve(ui, repo, **opts):
2621 def serve(ui, repo, **opts):
2622 """export the repository via HTTP
2622 """export the repository via HTTP
2623
2623
2624 Start a local HTTP repository browser and pull server.
2624 Start a local HTTP repository browser and pull server.
2625
2625
2626 By default, the server logs accesses to stdout and errors to
2626 By default, the server logs accesses to stdout and errors to
2627 stderr. Use the "-A" and "-E" options to log to files.
2627 stderr. Use the "-A" and "-E" options to log to files.
2628 """
2628 """
2629
2629
2630 if opts["stdio"]:
2630 if opts["stdio"]:
2631 if repo is None:
2631 if repo is None:
2632 raise error.RepoError(_("There is no Mercurial repository here"
2632 raise error.RepoError(_("There is no Mercurial repository here"
2633 " (.hg not found)"))
2633 " (.hg not found)"))
2634 s = sshserver.sshserver(ui, repo)
2634 s = sshserver.sshserver(ui, repo)
2635 s.serve_forever()
2635 s.serve_forever()
2636
2636
2637 parentui = ui.parentui or ui
2637 parentui = ui.parentui or ui
2638 optlist = ("name templates style address port prefix ipv6"
2638 optlist = ("name templates style address port prefix ipv6"
2639 " accesslog errorlog webdir_conf certificate")
2639 " accesslog errorlog webdir_conf certificate")
2640 for o in optlist.split():
2640 for o in optlist.split():
2641 if opts[o]:
2641 if opts[o]:
2642 parentui.setconfig("web", o, str(opts[o]))
2642 parentui.setconfig("web", o, str(opts[o]))
2643 if (repo is not None) and (repo.ui != parentui):
2643 if (repo is not None) and (repo.ui != parentui):
2644 repo.ui.setconfig("web", o, str(opts[o]))
2644 repo.ui.setconfig("web", o, str(opts[o]))
2645
2645
2646 if repo is None and not ui.config("web", "webdir_conf"):
2646 if repo is None and not ui.config("web", "webdir_conf"):
2647 raise error.RepoError(_("There is no Mercurial repository here"
2647 raise error.RepoError(_("There is no Mercurial repository here"
2648 " (.hg not found)"))
2648 " (.hg not found)"))
2649
2649
2650 class service:
2650 class service:
2651 def init(self):
2651 def init(self):
2652 util.set_signal_handler()
2652 util.set_signal_handler()
2653 self.httpd = hgweb.server.create_server(parentui, repo)
2653 self.httpd = hgweb.server.create_server(parentui, repo)
2654
2654
2655 if not ui.verbose: return
2655 if not ui.verbose: return
2656
2656
2657 if self.httpd.prefix:
2657 if self.httpd.prefix:
2658 prefix = self.httpd.prefix.strip('/') + '/'
2658 prefix = self.httpd.prefix.strip('/') + '/'
2659 else:
2659 else:
2660 prefix = ''
2660 prefix = ''
2661
2661
2662 port = ':%d' % self.httpd.port
2662 port = ':%d' % self.httpd.port
2663 if port == ':80':
2663 if port == ':80':
2664 port = ''
2664 port = ''
2665
2665
2666 bindaddr = self.httpd.addr
2666 bindaddr = self.httpd.addr
2667 if bindaddr == '0.0.0.0':
2667 if bindaddr == '0.0.0.0':
2668 bindaddr = '*'
2668 bindaddr = '*'
2669 elif ':' in bindaddr: # IPv6
2669 elif ':' in bindaddr: # IPv6
2670 bindaddr = '[%s]' % bindaddr
2670 bindaddr = '[%s]' % bindaddr
2671
2671
2672 fqaddr = self.httpd.fqaddr
2672 fqaddr = self.httpd.fqaddr
2673 if ':' in fqaddr:
2673 if ':' in fqaddr:
2674 fqaddr = '[%s]' % fqaddr
2674 fqaddr = '[%s]' % fqaddr
2675 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2675 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2676 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2676 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2677
2677
2678 def run(self):
2678 def run(self):
2679 self.httpd.serve_forever()
2679 self.httpd.serve_forever()
2680
2680
2681 service = service()
2681 service = service()
2682
2682
2683 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2683 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2684
2684
2685 def status(ui, repo, *pats, **opts):
2685 def status(ui, repo, *pats, **opts):
2686 """show changed files in the working directory
2686 """show changed files in the working directory
2687
2687
2688 Show status of files in the repository. If names are given, only
2688 Show status of files in the repository. If names are given, only
2689 files that match are shown. Files that are clean or ignored or
2689 files that match are shown. Files that are clean or ignored or
2690 source of a copy/move operation, are not listed unless -c (clean),
2690 source of a copy/move operation, are not listed unless -c (clean),
2691 -i (ignored), -C (copies) or -A is given. Unless options described
2691 -i (ignored), -C (copies) or -A is given. Unless options described
2692 with "show only ..." are given, the options -mardu are used.
2692 with "show only ..." are given, the options -mardu are used.
2693
2693
2694 Option -q/--quiet hides untracked (unknown and ignored) files
2694 Option -q/--quiet hides untracked (unknown and ignored) files
2695 unless explicitly requested with -u/--unknown or -i/-ignored.
2695 unless explicitly requested with -u/--unknown or -i/-ignored.
2696
2696
2697 NOTE: status may appear to disagree with diff if permissions have
2697 NOTE: status may appear to disagree with diff if permissions have
2698 changed or a merge has occurred. The standard diff format does not
2698 changed or a merge has occurred. The standard diff format does not
2699 report permission changes and diff only reports changes relative
2699 report permission changes and diff only reports changes relative
2700 to one merge parent.
2700 to one merge parent.
2701
2701
2702 If one revision is given, it is used as the base revision.
2702 If one revision is given, it is used as the base revision.
2703 If two revisions are given, the difference between them is shown.
2703 If two revisions are given, the difference between them is shown.
2704
2704
2705 The codes used to show the status of files are:
2705 The codes used to show the status of files are:
2706 M = modified
2706 M = modified
2707 A = added
2707 A = added
2708 R = removed
2708 R = removed
2709 C = clean
2709 C = clean
2710 ! = deleted, but still tracked
2710 ! = deleted, but still tracked
2711 ? = not tracked
2711 ? = not tracked
2712 I = ignored
2712 I = ignored
2713 = the previous added file was copied from here
2713 = the previous added file was copied from here
2714 """
2714 """
2715
2715
2716 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2716 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2717 cwd = (pats and repo.getcwd()) or ''
2717 cwd = (pats and repo.getcwd()) or ''
2718 end = opts.get('print0') and '\0' or '\n'
2718 end = opts.get('print0') and '\0' or '\n'
2719 copy = {}
2719 copy = {}
2720 states = 'modified added removed deleted unknown ignored clean'.split()
2720 states = 'modified added removed deleted unknown ignored clean'.split()
2721 show = [k for k in states if opts.get(k)]
2721 show = [k for k in states if opts.get(k)]
2722 if opts.get('all'):
2722 if opts.get('all'):
2723 show += ui.quiet and (states[:4] + ['clean']) or states
2723 show += ui.quiet and (states[:4] + ['clean']) or states
2724 if not show:
2724 if not show:
2725 show = ui.quiet and states[:4] or states[:5]
2725 show = ui.quiet and states[:4] or states[:5]
2726
2726
2727 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
2727 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
2728 'ignored' in show, 'clean' in show, 'unknown' in show)
2728 'ignored' in show, 'clean' in show, 'unknown' in show)
2729 changestates = zip(states, 'MAR!?IC', stat)
2729 changestates = zip(states, 'MAR!?IC', stat)
2730
2730
2731 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
2731 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
2732 ctxn = repo[nullid]
2732 ctxn = repo[nullid]
2733 ctx1 = repo[node1]
2733 ctx1 = repo[node1]
2734 ctx2 = repo[node2]
2734 ctx2 = repo[node2]
2735 added = stat[1]
2735 added = stat[1]
2736 if node2 is None:
2736 if node2 is None:
2737 added = stat[0] + stat[1] # merged?
2737 added = stat[0] + stat[1] # merged?
2738
2738
2739 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
2739 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
2740 if k in added:
2740 if k in added:
2741 copy[k] = v
2741 copy[k] = v
2742 elif v in added:
2742 elif v in added:
2743 copy[v] = k
2743 copy[v] = k
2744
2744
2745 for state, char, files in changestates:
2745 for state, char, files in changestates:
2746 if state in show:
2746 if state in show:
2747 format = "%s %%s%s" % (char, end)
2747 format = "%s %%s%s" % (char, end)
2748 if opts.get('no_status'):
2748 if opts.get('no_status'):
2749 format = "%%s%s" % end
2749 format = "%%s%s" % end
2750
2750
2751 for f in files:
2751 for f in files:
2752 ui.write(format % repo.pathto(f, cwd))
2752 ui.write(format % repo.pathto(f, cwd))
2753 if f in copy:
2753 if f in copy:
2754 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2754 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2755
2755
2756 def tag(ui, repo, name1, *names, **opts):
2756 def tag(ui, repo, name1, *names, **opts):
2757 """add one or more tags for the current or given revision
2757 """add one or more tags for the current or given revision
2758
2758
2759 Name a particular revision using <name>.
2759 Name a particular revision using <name>.
2760
2760
2761 Tags are used to name particular revisions of the repository and are
2761 Tags are used to name particular revisions of the repository and are
2762 very useful to compare different revisions, to go back to significant
2762 very useful to compare different revisions, to go back to significant
2763 earlier versions or to mark branch points as releases, etc.
2763 earlier versions or to mark branch points as releases, etc.
2764
2764
2765 If no revision is given, the parent of the working directory is used,
2765 If no revision is given, the parent of the working directory is used,
2766 or tip if no revision is checked out.
2766 or tip if no revision is checked out.
2767
2767
2768 To facilitate version control, distribution, and merging of tags,
2768 To facilitate version control, distribution, and merging of tags,
2769 they are stored as a file named ".hgtags" which is managed
2769 they are stored as a file named ".hgtags" which is managed
2770 similarly to other project files and can be hand-edited if
2770 similarly to other project files and can be hand-edited if
2771 necessary. The file '.hg/localtags' is used for local tags (not
2771 necessary. The file '.hg/localtags' is used for local tags (not
2772 shared among repositories).
2772 shared among repositories).
2773
2773
2774 See 'hg help dates' for a list of formats valid for -d/--date.
2774 See 'hg help dates' for a list of formats valid for -d/--date.
2775 """
2775 """
2776
2776
2777 rev_ = "."
2777 rev_ = "."
2778 names = (name1,) + names
2778 names = (name1,) + names
2779 if len(names) != len(dict.fromkeys(names)):
2779 if len(names) != len(dict.fromkeys(names)):
2780 raise util.Abort(_('tag names must be unique'))
2780 raise util.Abort(_('tag names must be unique'))
2781 for n in names:
2781 for n in names:
2782 if n in ['tip', '.', 'null']:
2782 if n in ['tip', '.', 'null']:
2783 raise util.Abort(_('the name \'%s\' is reserved') % n)
2783 raise util.Abort(_('the name \'%s\' is reserved') % n)
2784 if opts.get('rev') and opts.get('remove'):
2784 if opts.get('rev') and opts.get('remove'):
2785 raise util.Abort(_("--rev and --remove are incompatible"))
2785 raise util.Abort(_("--rev and --remove are incompatible"))
2786 if opts.get('rev'):
2786 if opts.get('rev'):
2787 rev_ = opts['rev']
2787 rev_ = opts['rev']
2788 message = opts.get('message')
2788 message = opts.get('message')
2789 if opts.get('remove'):
2789 if opts.get('remove'):
2790 expectedtype = opts.get('local') and 'local' or 'global'
2790 expectedtype = opts.get('local') and 'local' or 'global'
2791 for n in names:
2791 for n in names:
2792 if not repo.tagtype(n):
2792 if not repo.tagtype(n):
2793 raise util.Abort(_('tag \'%s\' does not exist') % n)
2793 raise util.Abort(_('tag \'%s\' does not exist') % n)
2794 if repo.tagtype(n) != expectedtype:
2794 if repo.tagtype(n) != expectedtype:
2795 raise util.Abort(_('tag \'%s\' is not a %s tag') %
2795 raise util.Abort(_('tag \'%s\' is not a %s tag') %
2796 (n, expectedtype))
2796 (n, expectedtype))
2797 rev_ = nullid
2797 rev_ = nullid
2798 if not message:
2798 if not message:
2799 message = _('Removed tag %s') % ', '.join(names)
2799 message = _('Removed tag %s') % ', '.join(names)
2800 elif not opts.get('force'):
2800 elif not opts.get('force'):
2801 for n in names:
2801 for n in names:
2802 if n in repo.tags():
2802 if n in repo.tags():
2803 raise util.Abort(_('tag \'%s\' already exists '
2803 raise util.Abort(_('tag \'%s\' already exists '
2804 '(use -f to force)') % n)
2804 '(use -f to force)') % n)
2805 if not rev_ and repo.dirstate.parents()[1] != nullid:
2805 if not rev_ and repo.dirstate.parents()[1] != nullid:
2806 raise util.Abort(_('uncommitted merge - please provide a '
2806 raise util.Abort(_('uncommitted merge - please provide a '
2807 'specific revision'))
2807 'specific revision'))
2808 r = repo[rev_].node()
2808 r = repo[rev_].node()
2809
2809
2810 if not message:
2810 if not message:
2811 message = (_('Added tag %s for changeset %s') %
2811 message = (_('Added tag %s for changeset %s') %
2812 (', '.join(names), short(r)))
2812 (', '.join(names), short(r)))
2813
2813
2814 date = opts.get('date')
2814 date = opts.get('date')
2815 if date:
2815 if date:
2816 date = util.parsedate(date)
2816 date = util.parsedate(date)
2817
2817
2818 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
2818 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
2819
2819
2820 def tags(ui, repo):
2820 def tags(ui, repo):
2821 """list repository tags
2821 """list repository tags
2822
2822
2823 This lists both regular and local tags. When the -v/--verbose switch
2823 This lists both regular and local tags. When the -v/--verbose switch
2824 is used, a third column "local" is printed for local tags.
2824 is used, a third column "local" is printed for local tags.
2825 """
2825 """
2826
2826
2827 l = repo.tagslist()
2827 l = repo.tagslist()
2828 l.reverse()
2828 l.reverse()
2829 hexfunc = ui.debugflag and hex or short
2829 hexfunc = ui.debugflag and hex or short
2830 tagtype = ""
2830 tagtype = ""
2831
2831
2832 for t, n in l:
2832 for t, n in l:
2833 if ui.quiet:
2833 if ui.quiet:
2834 ui.write("%s\n" % t)
2834 ui.write("%s\n" % t)
2835 continue
2835 continue
2836
2836
2837 try:
2837 try:
2838 hn = hexfunc(n)
2838 hn = hexfunc(n)
2839 r = "%5d:%s" % (repo.changelog.rev(n), hn)
2839 r = "%5d:%s" % (repo.changelog.rev(n), hn)
2840 except error.LookupError:
2840 except error.LookupError:
2841 r = " ?:%s" % hn
2841 r = " ?:%s" % hn
2842 else:
2842 else:
2843 spaces = " " * (30 - util.colwidth(t))
2843 spaces = " " * (30 - util.colwidth(t))
2844 if ui.verbose:
2844 if ui.verbose:
2845 if repo.tagtype(t) == 'local':
2845 if repo.tagtype(t) == 'local':
2846 tagtype = " local"
2846 tagtype = " local"
2847 else:
2847 else:
2848 tagtype = ""
2848 tagtype = ""
2849 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
2849 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
2850
2850
2851 def tip(ui, repo, **opts):
2851 def tip(ui, repo, **opts):
2852 """show the tip revision
2852 """show the tip revision
2853
2853
2854 The tip revision (usually just called the tip) is the most
2854 The tip revision (usually just called the tip) is the most
2855 recently added changeset in the repository, the most recently
2855 recently added changeset in the repository, the most recently
2856 changed head.
2856 changed head.
2857
2857
2858 If you have just made a commit, that commit will be the tip. If
2858 If you have just made a commit, that commit will be the tip. If
2859 you have just pulled changes from another repository, the tip of
2859 you have just pulled changes from another repository, the tip of
2860 that repository becomes the current tip. The "tip" tag is special
2860 that repository becomes the current tip. The "tip" tag is special
2861 and cannot be renamed or assigned to a different changeset.
2861 and cannot be renamed or assigned to a different changeset.
2862 """
2862 """
2863 cmdutil.show_changeset(ui, repo, opts).show(repo[len(repo) - 1])
2863 cmdutil.show_changeset(ui, repo, opts).show(repo[len(repo) - 1])
2864
2864
2865 def unbundle(ui, repo, fname1, *fnames, **opts):
2865 def unbundle(ui, repo, fname1, *fnames, **opts):
2866 """apply one or more changegroup files
2866 """apply one or more changegroup files
2867
2867
2868 Apply one or more compressed changegroup files generated by the
2868 Apply one or more compressed changegroup files generated by the
2869 bundle command.
2869 bundle command.
2870 """
2870 """
2871 fnames = (fname1,) + fnames
2871 fnames = (fname1,) + fnames
2872
2872
2873 lock = None
2873 lock = None
2874 try:
2874 try:
2875 lock = repo.lock()
2875 lock = repo.lock()
2876 for fname in fnames:
2876 for fname in fnames:
2877 f = url.open(ui, fname)
2877 f = url.open(ui, fname)
2878 gen = changegroup.readbundle(f, fname)
2878 gen = changegroup.readbundle(f, fname)
2879 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2879 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2880 finally:
2880 finally:
2881 del lock
2881 del lock
2882
2882
2883 return postincoming(ui, repo, modheads, opts.get('update'), None)
2883 return postincoming(ui, repo, modheads, opts.get('update'), None)
2884
2884
2885 def update(ui, repo, node=None, rev=None, clean=False, date=None):
2885 def update(ui, repo, node=None, rev=None, clean=False, date=None):
2886 """update working directory
2886 """update working directory
2887
2887
2888 Update the repository's working directory to the specified revision,
2888 Update the repository's working directory to the specified revision,
2889 or the tip of the current branch if none is specified. Use null as
2889 or the tip of the current branch if none is specified. Use null as
2890 the revision to remove the working copy (like 'hg clone -U').
2890 the revision to remove the working copy (like 'hg clone -U').
2891
2891
2892 When the working dir contains no uncommitted changes, it will be
2892 When the working dir contains no uncommitted changes, it will be
2893 replaced by the state of the requested revision from the repo. When
2893 replaced by the state of the requested revision from the repo. When
2894 the requested revision is on a different branch, the working dir
2894 the requested revision is on a different branch, the working dir
2895 will additionally be switched to that branch.
2895 will additionally be switched to that branch.
2896
2896
2897 When there are uncommitted changes, use option -C to discard them,
2897 When there are uncommitted changes, use option -C to discard them,
2898 forcibly replacing the state of the working dir with the requested
2898 forcibly replacing the state of the working dir with the requested
2899 revision.
2899 revision.
2900
2900
2901 When there are uncommitted changes and option -C is not used, and
2901 When there are uncommitted changes and option -C is not used, and
2902 the parent revision and requested revision are on the same branch,
2902 the parent revision and requested revision are on the same branch,
2903 and one of them is an ancestor of the other, then the new working
2903 and one of them is an ancestor of the other, then the new working
2904 directory will contain the requested revision merged with the
2904 directory will contain the requested revision merged with the
2905 uncommitted changes. Otherwise, the update will fail with a
2905 uncommitted changes. Otherwise, the update will fail with a
2906 suggestion to use 'merge' or 'update -C' instead.
2906 suggestion to use 'merge' or 'update -C' instead.
2907
2907
2908 If you want to update just one file to an older revision, use revert.
2908 If you want to update just one file to an older revision, use revert.
2909
2909
2910 See 'hg help dates' for a list of formats valid for --date.
2910 See 'hg help dates' for a list of formats valid for --date.
2911 """
2911 """
2912 if rev and node:
2912 if rev and node:
2913 raise util.Abort(_("please specify just one revision"))
2913 raise util.Abort(_("please specify just one revision"))
2914
2914
2915 if not rev:
2915 if not rev:
2916 rev = node
2916 rev = node
2917
2917
2918 if date:
2918 if date:
2919 if rev:
2919 if rev:
2920 raise util.Abort(_("you can't specify a revision and a date"))
2920 raise util.Abort(_("you can't specify a revision and a date"))
2921 rev = cmdutil.finddate(ui, repo, date)
2921 rev = cmdutil.finddate(ui, repo, date)
2922
2922
2923 if clean:
2923 if clean:
2924 return hg.clean(repo, rev)
2924 return hg.clean(repo, rev)
2925 else:
2925 else:
2926 return hg.update(repo, rev)
2926 return hg.update(repo, rev)
2927
2927
2928 def verify(ui, repo):
2928 def verify(ui, repo):
2929 """verify the integrity of the repository
2929 """verify the integrity of the repository
2930
2930
2931 Verify the integrity of the current repository.
2931 Verify the integrity of the current repository.
2932
2932
2933 This will perform an extensive check of the repository's
2933 This will perform an extensive check of the repository's
2934 integrity, validating the hashes and checksums of each entry in
2934 integrity, validating the hashes and checksums of each entry in
2935 the changelog, manifest, and tracked files, as well as the
2935 the changelog, manifest, and tracked files, as well as the
2936 integrity of their crosslinks and indices.
2936 integrity of their crosslinks and indices.
2937 """
2937 """
2938 return hg.verify(repo)
2938 return hg.verify(repo)
2939
2939
2940 def version_(ui):
2940 def version_(ui):
2941 """output version and copyright information"""
2941 """output version and copyright information"""
2942 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2942 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2943 % util.version())
2943 % util.version())
2944 ui.status(_(
2944 ui.status(_(
2945 "\nCopyright (C) 2005-2008 Matt Mackall <mpm@selenic.com> and others\n"
2945 "\nCopyright (C) 2005-2008 Matt Mackall <mpm@selenic.com> and others\n"
2946 "This is free software; see the source for copying conditions. "
2946 "This is free software; see the source for copying conditions. "
2947 "There is NO\nwarranty; "
2947 "There is NO\nwarranty; "
2948 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2948 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2949 ))
2949 ))
2950
2950
2951 # Command options and aliases are listed here, alphabetically
2951 # Command options and aliases are listed here, alphabetically
2952
2952
2953 globalopts = [
2953 globalopts = [
2954 ('R', 'repository', '',
2954 ('R', 'repository', '',
2955 _('repository root directory or symbolic path name')),
2955 _('repository root directory or symbolic path name')),
2956 ('', 'cwd', '', _('change working directory')),
2956 ('', 'cwd', '', _('change working directory')),
2957 ('y', 'noninteractive', None,
2957 ('y', 'noninteractive', None,
2958 _('do not prompt, assume \'yes\' for any required answers')),
2958 _('do not prompt, assume \'yes\' for any required answers')),
2959 ('q', 'quiet', None, _('suppress output')),
2959 ('q', 'quiet', None, _('suppress output')),
2960 ('v', 'verbose', None, _('enable additional output')),
2960 ('v', 'verbose', None, _('enable additional output')),
2961 ('', 'config', [], _('set/override config option')),
2961 ('', 'config', [], _('set/override config option')),
2962 ('', 'debug', None, _('enable debugging output')),
2962 ('', 'debug', None, _('enable debugging output')),
2963 ('', 'debugger', None, _('start debugger')),
2963 ('', 'debugger', None, _('start debugger')),
2964 ('', 'encoding', util._encoding, _('set the charset encoding')),
2964 ('', 'encoding', util._encoding, _('set the charset encoding')),
2965 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2965 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2966 ('', 'lsprof', None, _('print improved command execution profile')),
2966 ('', 'lsprof', None, _('print improved command execution profile')),
2967 ('', 'traceback', None, _('print traceback on exception')),
2967 ('', 'traceback', None, _('print traceback on exception')),
2968 ('', 'time', None, _('time how long the command takes')),
2968 ('', 'time', None, _('time how long the command takes')),
2969 ('', 'profile', None, _('print command execution profile')),
2969 ('', 'profile', None, _('print command execution profile')),
2970 ('', 'version', None, _('output version information and exit')),
2970 ('', 'version', None, _('output version information and exit')),
2971 ('h', 'help', None, _('display help and exit')),
2971 ('h', 'help', None, _('display help and exit')),
2972 ]
2972 ]
2973
2973
2974 dryrunopts = [('n', 'dry-run', None,
2974 dryrunopts = [('n', 'dry-run', None,
2975 _('do not perform actions, just print output'))]
2975 _('do not perform actions, just print output'))]
2976
2976
2977 remoteopts = [
2977 remoteopts = [
2978 ('e', 'ssh', '', _('specify ssh command to use')),
2978 ('e', 'ssh', '', _('specify ssh command to use')),
2979 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2979 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2980 ]
2980 ]
2981
2981
2982 walkopts = [
2982 walkopts = [
2983 ('I', 'include', [], _('include names matching the given patterns')),
2983 ('I', 'include', [], _('include names matching the given patterns')),
2984 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2984 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2985 ]
2985 ]
2986
2986
2987 commitopts = [
2987 commitopts = [
2988 ('m', 'message', '', _('use <text> as commit message')),
2988 ('m', 'message', '', _('use <text> as commit message')),
2989 ('l', 'logfile', '', _('read commit message from <file>')),
2989 ('l', 'logfile', '', _('read commit message from <file>')),
2990 ]
2990 ]
2991
2991
2992 commitopts2 = [
2992 commitopts2 = [
2993 ('d', 'date', '', _('record datecode as commit date')),
2993 ('d', 'date', '', _('record datecode as commit date')),
2994 ('u', 'user', '', _('record user as committer')),
2994 ('u', 'user', '', _('record user as committer')),
2995 ]
2995 ]
2996
2996
2997 templateopts = [
2997 templateopts = [
2998 ('', 'style', '', _('display using template map file')),
2998 ('', 'style', '', _('display using template map file')),
2999 ('', 'template', '', _('display with template')),
2999 ('', 'template', '', _('display with template')),
3000 ]
3000 ]
3001
3001
3002 logopts = [
3002 logopts = [
3003 ('p', 'patch', None, _('show patch')),
3003 ('p', 'patch', None, _('show patch')),
3004 ('g', 'git', None, _('use git extended diff format')),
3004 ('g', 'git', None, _('use git extended diff format')),
3005 ('l', 'limit', '', _('limit number of changes displayed')),
3005 ('l', 'limit', '', _('limit number of changes displayed')),
3006 ('M', 'no-merges', None, _('do not show merges')),
3006 ('M', 'no-merges', None, _('do not show merges')),
3007 ] + templateopts
3007 ] + templateopts
3008
3008
3009 diffopts = [
3009 diffopts = [
3010 ('a', 'text', None, _('treat all files as text')),
3010 ('a', 'text', None, _('treat all files as text')),
3011 ('g', 'git', None, _('use git extended diff format')),
3011 ('g', 'git', None, _('use git extended diff format')),
3012 ('', 'nodates', None, _("don't include dates in diff headers"))
3012 ('', 'nodates', None, _("don't include dates in diff headers"))
3013 ]
3013 ]
3014
3014
3015 diffopts2 = [
3015 diffopts2 = [
3016 ('p', 'show-function', None, _('show which function each change is in')),
3016 ('p', 'show-function', None, _('show which function each change is in')),
3017 ('w', 'ignore-all-space', None,
3017 ('w', 'ignore-all-space', None,
3018 _('ignore white space when comparing lines')),
3018 _('ignore white space when comparing lines')),
3019 ('b', 'ignore-space-change', None,
3019 ('b', 'ignore-space-change', None,
3020 _('ignore changes in the amount of white space')),
3020 _('ignore changes in the amount of white space')),
3021 ('B', 'ignore-blank-lines', None,
3021 ('B', 'ignore-blank-lines', None,
3022 _('ignore changes whose lines are all blank')),
3022 _('ignore changes whose lines are all blank')),
3023 ('U', 'unified', '', _('number of lines of context to show'))
3023 ('U', 'unified', '', _('number of lines of context to show'))
3024 ]
3024 ]
3025
3025
3026 similarityopts = [
3026 similarityopts = [
3027 ('s', 'similarity', '',
3027 ('s', 'similarity', '',
3028 _('guess renamed files by similarity (0<=s<=100)'))
3028 _('guess renamed files by similarity (0<=s<=100)'))
3029 ]
3029 ]
3030
3030
3031 table = {
3031 table = {
3032 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3032 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3033 "addremove":
3033 "addremove":
3034 (addremove, similarityopts + walkopts + dryrunopts,
3034 (addremove, similarityopts + walkopts + dryrunopts,
3035 _('[OPTION]... [FILE]...')),
3035 _('[OPTION]... [FILE]...')),
3036 "^annotate|blame":
3036 "^annotate|blame":
3037 (annotate,
3037 (annotate,
3038 [('r', 'rev', '', _('annotate the specified revision')),
3038 [('r', 'rev', '', _('annotate the specified revision')),
3039 ('f', 'follow', None, _('follow file copies and renames')),
3039 ('f', 'follow', None, _('follow file copies and renames')),
3040 ('a', 'text', None, _('treat all files as text')),
3040 ('a', 'text', None, _('treat all files as text')),
3041 ('u', 'user', None, _('list the author (long with -v)')),
3041 ('u', 'user', None, _('list the author (long with -v)')),
3042 ('d', 'date', None, _('list the date (short with -q)')),
3042 ('d', 'date', None, _('list the date (short with -q)')),
3043 ('n', 'number', None, _('list the revision number (default)')),
3043 ('n', 'number', None, _('list the revision number (default)')),
3044 ('c', 'changeset', None, _('list the changeset')),
3044 ('c', 'changeset', None, _('list the changeset')),
3045 ('l', 'line-number', None,
3045 ('l', 'line-number', None,
3046 _('show line number at the first appearance'))
3046 _('show line number at the first appearance'))
3047 ] + walkopts,
3047 ] + walkopts,
3048 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3048 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3049 "archive":
3049 "archive":
3050 (archive,
3050 (archive,
3051 [('', 'no-decode', None, _('do not pass files through decoders')),
3051 [('', 'no-decode', None, _('do not pass files through decoders')),
3052 ('p', 'prefix', '', _('directory prefix for files in archive')),
3052 ('p', 'prefix', '', _('directory prefix for files in archive')),
3053 ('r', 'rev', '', _('revision to distribute')),
3053 ('r', 'rev', '', _('revision to distribute')),
3054 ('t', 'type', '', _('type of distribution to create')),
3054 ('t', 'type', '', _('type of distribution to create')),
3055 ] + walkopts,
3055 ] + walkopts,
3056 _('[OPTION]... DEST')),
3056 _('[OPTION]... DEST')),
3057 "backout":
3057 "backout":
3058 (backout,
3058 (backout,
3059 [('', 'merge', None,
3059 [('', 'merge', None,
3060 _('merge with old dirstate parent after backout')),
3060 _('merge with old dirstate parent after backout')),
3061 ('', 'parent', '', _('parent to choose when backing out merge')),
3061 ('', 'parent', '', _('parent to choose when backing out merge')),
3062 ('r', 'rev', '', _('revision to backout')),
3062 ('r', 'rev', '', _('revision to backout')),
3063 ] + walkopts + commitopts + commitopts2,
3063 ] + walkopts + commitopts + commitopts2,
3064 _('[OPTION]... [-r] REV')),
3064 _('[OPTION]... [-r] REV')),
3065 "bisect":
3065 "bisect":
3066 (bisect,
3066 (bisect,
3067 [('r', 'reset', False, _('reset bisect state')),
3067 [('r', 'reset', False, _('reset bisect state')),
3068 ('g', 'good', False, _('mark changeset good')),
3068 ('g', 'good', False, _('mark changeset good')),
3069 ('b', 'bad', False, _('mark changeset bad')),
3069 ('b', 'bad', False, _('mark changeset bad')),
3070 ('s', 'skip', False, _('skip testing changeset')),
3070 ('s', 'skip', False, _('skip testing changeset')),
3071 ('c', 'command', '', _('use command to check changeset state')),
3071 ('c', 'command', '', _('use command to check changeset state')),
3072 ('U', 'noupdate', False, _('do not update to target'))],
3072 ('U', 'noupdate', False, _('do not update to target'))],
3073 _("[-gbsr] [-c CMD] [REV]")),
3073 _("[-gbsr] [-c CMD] [REV]")),
3074 "branch":
3074 "branch":
3075 (branch,
3075 (branch,
3076 [('f', 'force', None,
3076 [('f', 'force', None,
3077 _('set branch name even if it shadows an existing branch')),
3077 _('set branch name even if it shadows an existing branch')),
3078 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3078 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3079 _('[-fC] [NAME]')),
3079 _('[-fC] [NAME]')),
3080 "branches":
3080 "branches":
3081 (branches,
3081 (branches,
3082 [('a', 'active', False,
3082 [('a', 'active', False,
3083 _('show only branches that have unmerged heads'))],
3083 _('show only branches that have unmerged heads'))],
3084 _('[-a]')),
3084 _('[-a]')),
3085 "bundle":
3085 "bundle":
3086 (bundle,
3086 (bundle,
3087 [('f', 'force', None,
3087 [('f', 'force', None,
3088 _('run even when remote repository is unrelated')),
3088 _('run even when remote repository is unrelated')),
3089 ('r', 'rev', [],
3089 ('r', 'rev', [],
3090 _('a changeset up to which you would like to bundle')),
3090 _('a changeset up to which you would like to bundle')),
3091 ('', 'base', [],
3091 ('', 'base', [],
3092 _('a base changeset to specify instead of a destination')),
3092 _('a base changeset to specify instead of a destination')),
3093 ('a', 'all', None, _('bundle all changesets in the repository')),
3093 ('a', 'all', None, _('bundle all changesets in the repository')),
3094 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3094 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3095 ] + remoteopts,
3095 ] + remoteopts,
3096 _('[-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3096 _('[-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3097 "cat":
3097 "cat":
3098 (cat,
3098 (cat,
3099 [('o', 'output', '', _('print output to file with formatted name')),
3099 [('o', 'output', '', _('print output to file with formatted name')),
3100 ('r', 'rev', '', _('print the given revision')),
3100 ('r', 'rev', '', _('print the given revision')),
3101 ('', 'decode', None, _('apply any matching decode filter')),
3101 ('', 'decode', None, _('apply any matching decode filter')),
3102 ] + walkopts,
3102 ] + walkopts,
3103 _('[OPTION]... FILE...')),
3103 _('[OPTION]... FILE...')),
3104 "^clone":
3104 "^clone":
3105 (clone,
3105 (clone,
3106 [('U', 'noupdate', None,
3106 [('U', 'noupdate', None,
3107 _('the clone will only contain a repository (no working copy)')),
3107 _('the clone will only contain a repository (no working copy)')),
3108 ('r', 'rev', [],
3108 ('r', 'rev', [],
3109 _('a changeset you would like to have after cloning')),
3109 _('a changeset you would like to have after cloning')),
3110 ('', 'pull', None, _('use pull protocol to copy metadata')),
3110 ('', 'pull', None, _('use pull protocol to copy metadata')),
3111 ('', 'uncompressed', None,
3111 ('', 'uncompressed', None,
3112 _('use uncompressed transfer (fast over LAN)')),
3112 _('use uncompressed transfer (fast over LAN)')),
3113 ] + remoteopts,
3113 ] + remoteopts,
3114 _('[OPTION]... SOURCE [DEST]')),
3114 _('[OPTION]... SOURCE [DEST]')),
3115 "^commit|ci":
3115 "^commit|ci":
3116 (commit,
3116 (commit,
3117 [('A', 'addremove', None,
3117 [('A', 'addremove', None,
3118 _('mark new/missing files as added/removed before committing')),
3118 _('mark new/missing files as added/removed before committing')),
3119 ('', 'close-branch', None,
3119 ('', 'close-branch', None,
3120 _('mark a branch as closed, hiding it from the branch list')),
3120 _('mark a branch as closed, hiding it from the branch list')),
3121 ] + walkopts + commitopts + commitopts2,
3121 ] + walkopts + commitopts + commitopts2,
3122 _('[OPTION]... [FILE]...')),
3122 _('[OPTION]... [FILE]...')),
3123 "copy|cp":
3123 "copy|cp":
3124 (copy,
3124 (copy,
3125 [('A', 'after', None, _('record a copy that has already occurred')),
3125 [('A', 'after', None, _('record a copy that has already occurred')),
3126 ('f', 'force', None,
3126 ('f', 'force', None,
3127 _('forcibly copy over an existing managed file')),
3127 _('forcibly copy over an existing managed file')),
3128 ] + walkopts + dryrunopts,
3128 ] + walkopts + dryrunopts,
3129 _('[OPTION]... [SOURCE]... DEST')),
3129 _('[OPTION]... [SOURCE]... DEST')),
3130 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3130 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3131 "debugcheckstate": (debugcheckstate, []),
3131 "debugcheckstate": (debugcheckstate, []),
3132 "debugcomplete":
3132 "debugcomplete":
3133 (debugcomplete,
3133 (debugcomplete,
3134 [('o', 'options', None, _('show the command options'))],
3134 [('o', 'options', None, _('show the command options'))],
3135 _('[-o] CMD')),
3135 _('[-o] CMD')),
3136 "debugdate":
3136 "debugdate":
3137 (debugdate,
3137 (debugdate,
3138 [('e', 'extended', None, _('try extended date formats'))],
3138 [('e', 'extended', None, _('try extended date formats'))],
3139 _('[-e] DATE [RANGE]')),
3139 _('[-e] DATE [RANGE]')),
3140 "debugdata": (debugdata, [], _('FILE REV')),
3140 "debugdata": (debugdata, [], _('FILE REV')),
3141 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3141 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3142 "debugindex": (debugindex, [], _('FILE')),
3142 "debugindex": (debugindex, [], _('FILE')),
3143 "debugindexdot": (debugindexdot, [], _('FILE')),
3143 "debugindexdot": (debugindexdot, [], _('FILE')),
3144 "debuginstall": (debuginstall, []),
3144 "debuginstall": (debuginstall, []),
3145 "debugrawcommit|rawcommit":
3145 "debugrawcommit|rawcommit":
3146 (rawcommit,
3146 (rawcommit,
3147 [('p', 'parent', [], _('parent')),
3147 [('p', 'parent', [], _('parent')),
3148 ('F', 'files', '', _('file list'))
3148 ('F', 'files', '', _('file list'))
3149 ] + commitopts + commitopts2,
3149 ] + commitopts + commitopts2,
3150 _('[OPTION]... [FILE]...')),
3150 _('[OPTION]... [FILE]...')),
3151 "debugrebuildstate":
3151 "debugrebuildstate":
3152 (debugrebuildstate,
3152 (debugrebuildstate,
3153 [('r', 'rev', '', _('revision to rebuild to'))],
3153 [('r', 'rev', '', _('revision to rebuild to'))],
3154 _('[-r REV] [REV]')),
3154 _('[-r REV] [REV]')),
3155 "debugrename":
3155 "debugrename":
3156 (debugrename,
3156 (debugrename,
3157 [('r', 'rev', '', _('revision to debug'))],
3157 [('r', 'rev', '', _('revision to debug'))],
3158 _('[-r REV] FILE')),
3158 _('[-r REV] FILE')),
3159 "debugsetparents":
3159 "debugsetparents":
3160 (debugsetparents, [], _('REV1 [REV2]')),
3160 (debugsetparents, [], _('REV1 [REV2]')),
3161 "debugstate":
3161 "debugstate":
3162 (debugstate,
3162 (debugstate,
3163 [('', 'nodates', None, _('do not display the saved mtime'))],
3163 [('', 'nodates', None, _('do not display the saved mtime'))],
3164 _('[OPTION]...')),
3164 _('[OPTION]...')),
3165 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3165 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3166 "^diff":
3166 "^diff":
3167 (diff,
3167 (diff,
3168 [('r', 'rev', [], _('revision')),
3168 [('r', 'rev', [], _('revision')),
3169 ('c', 'change', '', _('change made by revision'))
3169 ('c', 'change', '', _('change made by revision'))
3170 ] + diffopts + diffopts2 + walkopts,
3170 ] + diffopts + diffopts2 + walkopts,
3171 _('[OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3171 _('[OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3172 "^export":
3172 "^export":
3173 (export,
3173 (export,
3174 [('o', 'output', '', _('print output to file with formatted name')),
3174 [('o', 'output', '', _('print output to file with formatted name')),
3175 ('', 'switch-parent', None, _('diff against the second parent'))
3175 ('', 'switch-parent', None, _('diff against the second parent'))
3176 ] + diffopts,
3176 ] + diffopts,
3177 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3177 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3178 "grep":
3178 "grep":
3179 (grep,
3179 (grep,
3180 [('0', 'print0', None, _('end fields with NUL')),
3180 [('0', 'print0', None, _('end fields with NUL')),
3181 ('', 'all', None, _('print all revisions that match')),
3181 ('', 'all', None, _('print all revisions that match')),
3182 ('f', 'follow', None,
3182 ('f', 'follow', None,
3183 _('follow changeset history, or file history across copies and renames')),
3183 _('follow changeset history, or file history across copies and renames')),
3184 ('i', 'ignore-case', None, _('ignore case when matching')),
3184 ('i', 'ignore-case', None, _('ignore case when matching')),
3185 ('l', 'files-with-matches', None,
3185 ('l', 'files-with-matches', None,
3186 _('print only filenames and revs that match')),
3186 _('print only filenames and revs that match')),
3187 ('n', 'line-number', None, _('print matching line numbers')),
3187 ('n', 'line-number', None, _('print matching line numbers')),
3188 ('r', 'rev', [], _('search in given revision range')),
3188 ('r', 'rev', [], _('search in given revision range')),
3189 ('u', 'user', None, _('list the author (long with -v)')),
3189 ('u', 'user', None, _('list the author (long with -v)')),
3190 ('d', 'date', None, _('list the date (short with -q)')),
3190 ('d', 'date', None, _('list the date (short with -q)')),
3191 ] + walkopts,
3191 ] + walkopts,
3192 _('[OPTION]... PATTERN [FILE]...')),
3192 _('[OPTION]... PATTERN [FILE]...')),
3193 "heads":
3193 "heads":
3194 (heads,
3194 (heads,
3195 [('r', 'rev', '', _('show only heads which are descendants of rev')),
3195 [('r', 'rev', '', _('show only heads which are descendants of rev')),
3196 ('a', 'active', False,
3196 ('a', 'active', False,
3197 _('show only the active heads from open branches')),
3197 _('show only the active heads from open branches')),
3198 ] + templateopts,
3198 ] + templateopts,
3199 _('[-r REV] [REV]...')),
3199 _('[-r REV] [REV]...')),
3200 "help": (help_, [], _('[TOPIC]')),
3200 "help": (help_, [], _('[TOPIC]')),
3201 "identify|id":
3201 "identify|id":
3202 (identify,
3202 (identify,
3203 [('r', 'rev', '', _('identify the specified rev')),
3203 [('r', 'rev', '', _('identify the specified rev')),
3204 ('n', 'num', None, _('show local revision number')),
3204 ('n', 'num', None, _('show local revision number')),
3205 ('i', 'id', None, _('show global revision id')),
3205 ('i', 'id', None, _('show global revision id')),
3206 ('b', 'branch', None, _('show branch')),
3206 ('b', 'branch', None, _('show branch')),
3207 ('t', 'tags', None, _('show tags'))],
3207 ('t', 'tags', None, _('show tags'))],
3208 _('[-nibt] [-r REV] [SOURCE]')),
3208 _('[-nibt] [-r REV] [SOURCE]')),
3209 "import|patch":
3209 "import|patch":
3210 (import_,
3210 (import_,
3211 [('p', 'strip', 1,
3211 [('p', 'strip', 1,
3212 _('directory strip option for patch. This has the same\n'
3212 _('directory strip option for patch. This has the same\n'
3213 'meaning as the corresponding patch option')),
3213 'meaning as the corresponding patch option')),
3214 ('b', 'base', '', _('base path')),
3214 ('b', 'base', '', _('base path')),
3215 ('f', 'force', None,
3215 ('f', 'force', None,
3216 _('skip check for outstanding uncommitted changes')),
3216 _('skip check for outstanding uncommitted changes')),
3217 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3217 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3218 ('', 'exact', None,
3218 ('', 'exact', None,
3219 _('apply patch to the nodes from which it was generated')),
3219 _('apply patch to the nodes from which it was generated')),
3220 ('', 'import-branch', None,
3220 ('', 'import-branch', None,
3221 _('Use any branch information in patch (implied by --exact)'))] +
3221 _('Use any branch information in patch (implied by --exact)'))] +
3222 commitopts + commitopts2 + similarityopts,
3222 commitopts + commitopts2 + similarityopts,
3223 _('[OPTION]... PATCH...')),
3223 _('[OPTION]... PATCH...')),
3224 "incoming|in":
3224 "incoming|in":
3225 (incoming,
3225 (incoming,
3226 [('f', 'force', None,
3226 [('f', 'force', None,
3227 _('run even when remote repository is unrelated')),
3227 _('run even when remote repository is unrelated')),
3228 ('n', 'newest-first', None, _('show newest record first')),
3228 ('n', 'newest-first', None, _('show newest record first')),
3229 ('', 'bundle', '', _('file to store the bundles into')),
3229 ('', 'bundle', '', _('file to store the bundles into')),
3230 ('r', 'rev', [],
3230 ('r', 'rev', [],
3231 _('a specific revision up to which you would like to pull')),
3231 _('a specific revision up to which you would like to pull')),
3232 ] + logopts + remoteopts,
3232 ] + logopts + remoteopts,
3233 _('[-p] [-n] [-M] [-f] [-r REV]...'
3233 _('[-p] [-n] [-M] [-f] [-r REV]...'
3234 ' [--bundle FILENAME] [SOURCE]')),
3234 ' [--bundle FILENAME] [SOURCE]')),
3235 "^init":
3235 "^init":
3236 (init,
3236 (init,
3237 remoteopts,
3237 remoteopts,
3238 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3238 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3239 "locate":
3239 "locate":
3240 (locate,
3240 (locate,
3241 [('r', 'rev', '', _('search the repository as it stood at rev')),
3241 [('r', 'rev', '', _('search the repository as it stood at rev')),
3242 ('0', 'print0', None,
3242 ('0', 'print0', None,
3243 _('end filenames with NUL, for use with xargs')),
3243 _('end filenames with NUL, for use with xargs')),
3244 ('f', 'fullpath', None,
3244 ('f', 'fullpath', None,
3245 _('print complete paths from the filesystem root')),
3245 _('print complete paths from the filesystem root')),
3246 ] + walkopts,
3246 ] + walkopts,
3247 _('[OPTION]... [PATTERN]...')),
3247 _('[OPTION]... [PATTERN]...')),
3248 "^log|history":
3248 "^log|history":
3249 (log,
3249 (log,
3250 [('f', 'follow', None,
3250 [('f', 'follow', None,
3251 _('follow changeset history, or file history across copies and renames')),
3251 _('follow changeset history, or file history across copies and renames')),
3252 ('', 'follow-first', None,
3252 ('', 'follow-first', None,
3253 _('only follow the first parent of merge changesets')),
3253 _('only follow the first parent of merge changesets')),
3254 ('d', 'date', '', _('show revs matching date spec')),
3254 ('d', 'date', '', _('show revs matching date spec')),
3255 ('C', 'copies', None, _('show copied files')),
3255 ('C', 'copies', None, _('show copied files')),
3256 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3256 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3257 ('r', 'rev', [], _('show the specified revision or range')),
3257 ('r', 'rev', [], _('show the specified revision or range')),
3258 ('', 'removed', None, _('include revs where files were removed')),
3258 ('', 'removed', None, _('include revs where files were removed')),
3259 ('m', 'only-merges', None, _('show only merges')),
3259 ('m', 'only-merges', None, _('show only merges')),
3260 ('u', 'user', [], _('revs committed by user')),
3260 ('u', 'user', [], _('revs committed by user')),
3261 ('b', 'only-branch', [],
3261 ('b', 'only-branch', [],
3262 _('show only changesets within the given named branch')),
3262 _('show only changesets within the given named branch')),
3263 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3263 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3264 ] + logopts + walkopts,
3264 ] + logopts + walkopts,
3265 _('[OPTION]... [FILE]')),
3265 _('[OPTION]... [FILE]')),
3266 "manifest":
3266 "manifest":
3267 (manifest,
3267 (manifest,
3268 [('r', 'rev', '', _('revision to display'))],
3268 [('r', 'rev', '', _('revision to display'))],
3269 _('[-r REV]')),
3269 _('[-r REV]')),
3270 "^merge":
3270 "^merge":
3271 (merge,
3271 (merge,
3272 [('f', 'force', None, _('force a merge with outstanding changes')),
3272 [('f', 'force', None, _('force a merge with outstanding changes')),
3273 ('r', 'rev', '', _('revision to merge')),
3273 ('r', 'rev', '', _('revision to merge')),
3274 ],
3274 ],
3275 _('[-f] [[-r] REV]')),
3275 _('[-f] [[-r] REV]')),
3276 "outgoing|out":
3276 "outgoing|out":
3277 (outgoing,
3277 (outgoing,
3278 [('f', 'force', None,
3278 [('f', 'force', None,
3279 _('run even when remote repository is unrelated')),
3279 _('run even when remote repository is unrelated')),
3280 ('r', 'rev', [],
3280 ('r', 'rev', [],
3281 _('a specific revision up to which you would like to push')),
3281 _('a specific revision up to which you would like to push')),
3282 ('n', 'newest-first', None, _('show newest record first')),
3282 ('n', 'newest-first', None, _('show newest record first')),
3283 ] + logopts + remoteopts,
3283 ] + logopts + remoteopts,
3284 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3284 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3285 "^parents":
3285 "^parents":
3286 (parents,
3286 (parents,
3287 [('r', 'rev', '', _('show parents from the specified rev')),
3287 [('r', 'rev', '', _('show parents from the specified rev')),
3288 ] + templateopts,
3288 ] + templateopts,
3289 _('hg parents [-r REV] [FILE]')),
3289 _('hg parents [-r REV] [FILE]')),
3290 "paths": (paths, [], _('[NAME]')),
3290 "paths": (paths, [], _('[NAME]')),
3291 "^pull":
3291 "^pull":
3292 (pull,
3292 (pull,
3293 [('u', 'update', None,
3293 [('u', 'update', None,
3294 _('update to new tip if changesets were pulled')),
3294 _('update to new tip if changesets were pulled')),
3295 ('f', 'force', None,
3295 ('f', 'force', None,
3296 _('run even when remote repository is unrelated')),
3296 _('run even when remote repository is unrelated')),
3297 ('r', 'rev', [],
3297 ('r', 'rev', [],
3298 _('a specific revision up to which you would like to pull')),
3298 _('a specific revision up to which you would like to pull')),
3299 ] + remoteopts,
3299 ] + remoteopts,
3300 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3300 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3301 "^push":
3301 "^push":
3302 (push,
3302 (push,
3303 [('f', 'force', None, _('force push')),
3303 [('f', 'force', None, _('force push')),
3304 ('r', 'rev', [],
3304 ('r', 'rev', [],
3305 _('a specific revision up to which you would like to push')),
3305 _('a specific revision up to which you would like to push')),
3306 ] + remoteopts,
3306 ] + remoteopts,
3307 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3307 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3308 "recover": (recover, []),
3308 "recover": (recover, []),
3309 "^remove|rm":
3309 "^remove|rm":
3310 (remove,
3310 (remove,
3311 [('A', 'after', None, _('record delete for missing files')),
3311 [('A', 'after', None, _('record delete for missing files')),
3312 ('f', 'force', None,
3312 ('f', 'force', None,
3313 _('remove (and delete) file even if added or modified')),
3313 _('remove (and delete) file even if added or modified')),
3314 ] + walkopts,
3314 ] + walkopts,
3315 _('[OPTION]... FILE...')),
3315 _('[OPTION]... FILE...')),
3316 "rename|mv":
3316 "rename|mv":
3317 (rename,
3317 (rename,
3318 [('A', 'after', None, _('record a rename that has already occurred')),
3318 [('A', 'after', None, _('record a rename that has already occurred')),
3319 ('f', 'force', None,
3319 ('f', 'force', None,
3320 _('forcibly copy over an existing managed file')),
3320 _('forcibly copy over an existing managed file')),
3321 ] + walkopts + dryrunopts,
3321 ] + walkopts + dryrunopts,
3322 _('[OPTION]... SOURCE... DEST')),
3322 _('[OPTION]... SOURCE... DEST')),
3323 "resolve":
3323 "resolve":
3324 (resolve,
3324 (resolve,
3325 [('a', 'all', None, _('remerge all unresolved files')),
3325 [('a', 'all', None, _('remerge all unresolved files')),
3326 ('l', 'list', None, _('list state of files needing merge')),
3326 ('l', 'list', None, _('list state of files needing merge')),
3327 ('m', 'mark', None, _('mark files as resolved')),
3327 ('m', 'mark', None, _('mark files as resolved')),
3328 ('u', 'unmark', None, _('unmark files as resolved'))]
3328 ('u', 'unmark', None, _('unmark files as resolved'))]
3329 + walkopts,
3329 + walkopts,
3330 _('[OPTION]... [FILE]...')),
3330 _('[OPTION]... [FILE]...')),
3331 "revert":
3331 "revert":
3332 (revert,
3332 (revert,
3333 [('a', 'all', None, _('revert all changes when no arguments given')),
3333 [('a', 'all', None, _('revert all changes when no arguments given')),
3334 ('d', 'date', '', _('tipmost revision matching date')),
3334 ('d', 'date', '', _('tipmost revision matching date')),
3335 ('r', 'rev', '', _('revision to revert to')),
3335 ('r', 'rev', '', _('revision to revert to')),
3336 ('', 'no-backup', None, _('do not save backup copies of files')),
3336 ('', 'no-backup', None, _('do not save backup copies of files')),
3337 ] + walkopts + dryrunopts,
3337 ] + walkopts + dryrunopts,
3338 _('[OPTION]... [-r REV] [NAME]...')),
3338 _('[OPTION]... [-r REV] [NAME]...')),
3339 "rollback": (rollback, []),
3339 "rollback": (rollback, []),
3340 "root": (root, []),
3340 "root": (root, []),
3341 "^serve":
3341 "^serve":
3342 (serve,
3342 (serve,
3343 [('A', 'accesslog', '', _('name of access log file to write to')),
3343 [('A', 'accesslog', '', _('name of access log file to write to')),
3344 ('d', 'daemon', None, _('run server in background')),
3344 ('d', 'daemon', None, _('run server in background')),
3345 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3345 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3346 ('E', 'errorlog', '', _('name of error log file to write to')),
3346 ('E', 'errorlog', '', _('name of error log file to write to')),
3347 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3347 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3348 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3348 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3349 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3349 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3350 ('n', 'name', '',
3350 ('n', 'name', '',
3351 _('name to show in web pages (default: working dir)')),
3351 _('name to show in web pages (default: working dir)')),
3352 ('', 'webdir-conf', '', _('name of the webdir config file'
3352 ('', 'webdir-conf', '', _('name of the webdir config file'
3353 ' (serve more than one repo)')),
3353 ' (serve more than one repo)')),
3354 ('', 'pid-file', '', _('name of file to write process ID to')),
3354 ('', 'pid-file', '', _('name of file to write process ID to')),
3355 ('', 'stdio', None, _('for remote clients')),
3355 ('', 'stdio', None, _('for remote clients')),
3356 ('t', 'templates', '', _('web templates to use')),
3356 ('t', 'templates', '', _('web templates to use')),
3357 ('', 'style', '', _('template style to use')),
3357 ('', 'style', '', _('template style to use')),
3358 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3358 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3359 ('', 'certificate', '', _('SSL certificate file'))],
3359 ('', 'certificate', '', _('SSL certificate file'))],
3360 _('[OPTION]...')),
3360 _('[OPTION]...')),
3361 "showconfig|debugconfig":
3361 "showconfig|debugconfig":
3362 (showconfig,
3362 (showconfig,
3363 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3363 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3364 _('[-u] [NAME]...')),
3364 _('[-u] [NAME]...')),
3365 "^status|st":
3365 "^status|st":
3366 (status,
3366 (status,
3367 [('A', 'all', None, _('show status of all files')),
3367 [('A', 'all', None, _('show status of all files')),
3368 ('m', 'modified', None, _('show only modified files')),
3368 ('m', 'modified', None, _('show only modified files')),
3369 ('a', 'added', None, _('show only added files')),
3369 ('a', 'added', None, _('show only added files')),
3370 ('r', 'removed', None, _('show only removed files')),
3370 ('r', 'removed', None, _('show only removed files')),
3371 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3371 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3372 ('c', 'clean', None, _('show only files without changes')),
3372 ('c', 'clean', None, _('show only files without changes')),
3373 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3373 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3374 ('i', 'ignored', None, _('show only ignored files')),
3374 ('i', 'ignored', None, _('show only ignored files')),
3375 ('n', 'no-status', None, _('hide status prefix')),
3375 ('n', 'no-status', None, _('hide status prefix')),
3376 ('C', 'copies', None, _('show source of copied files')),
3376 ('C', 'copies', None, _('show source of copied files')),
3377 ('0', 'print0', None,
3377 ('0', 'print0', None,
3378 _('end filenames with NUL, for use with xargs')),
3378 _('end filenames with NUL, for use with xargs')),
3379 ('', 'rev', [], _('show difference from revision')),
3379 ('', 'rev', [], _('show difference from revision')),
3380 ] + walkopts,
3380 ] + walkopts,
3381 _('[OPTION]... [FILE]...')),
3381 _('[OPTION]... [FILE]...')),
3382 "tag":
3382 "tag":
3383 (tag,
3383 (tag,
3384 [('f', 'force', None, _('replace existing tag')),
3384 [('f', 'force', None, _('replace existing tag')),
3385 ('l', 'local', None, _('make the tag local')),
3385 ('l', 'local', None, _('make the tag local')),
3386 ('r', 'rev', '', _('revision to tag')),
3386 ('r', 'rev', '', _('revision to tag')),
3387 ('', 'remove', None, _('remove a tag')),
3387 ('', 'remove', None, _('remove a tag')),
3388 # -l/--local is already there, commitopts cannot be used
3388 # -l/--local is already there, commitopts cannot be used
3389 ('m', 'message', '', _('use <text> as commit message')),
3389 ('m', 'message', '', _('use <text> as commit message')),
3390 ] + commitopts2,
3390 ] + commitopts2,
3391 _('[-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3391 _('[-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3392 "tags": (tags, []),
3392 "tags": (tags, []),
3393 "tip":
3393 "tip":
3394 (tip,
3394 (tip,
3395 [('p', 'patch', None, _('show patch')),
3395 [('p', 'patch', None, _('show patch')),
3396 ('g', 'git', None, _('use git extended diff format')),
3396 ('g', 'git', None, _('use git extended diff format')),
3397 ] + templateopts,
3397 ] + templateopts,
3398 _('[-p]')),
3398 _('[-p]')),
3399 "unbundle":
3399 "unbundle":
3400 (unbundle,
3400 (unbundle,
3401 [('u', 'update', None,
3401 [('u', 'update', None,
3402 _('update to new tip if changesets were unbundled'))],
3402 _('update to new tip if changesets were unbundled'))],
3403 _('[-u] FILE...')),
3403 _('[-u] FILE...')),
3404 "^update|up|checkout|co":
3404 "^update|up|checkout|co":
3405 (update,
3405 (update,
3406 [('C', 'clean', None, _('overwrite locally modified files (no backup)')),
3406 [('C', 'clean', None, _('overwrite locally modified files (no backup)')),
3407 ('d', 'date', '', _('tipmost revision matching date')),
3407 ('d', 'date', '', _('tipmost revision matching date')),
3408 ('r', 'rev', '', _('revision'))],
3408 ('r', 'rev', '', _('revision'))],
3409 _('[-C] [-d DATE] [[-r] REV]')),
3409 _('[-C] [-d DATE] [[-r] REV]')),
3410 "verify": (verify, []),
3410 "verify": (verify, []),
3411 "version": (version_, []),
3411 "version": (version_, []),
3412 }
3412 }
3413
3413
3414 norepo = ("clone init version help debugcomplete debugdata"
3414 norepo = ("clone init version help debugcomplete debugdata"
3415 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3415 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3416 optionalrepo = ("identify paths serve showconfig debugancestor")
3416 optionalrepo = ("identify paths serve showconfig debugancestor")
@@ -1,340 +1,340 b''
1 Mercurial Distributed SCM
1 Mercurial Distributed SCM
2
2
3 basic commands:
3 basic commands:
4
4
5 add add the specified files on the next commit
5 add add the specified files on the next commit
6 annotate show changeset information per file line
6 annotate show changeset information per file line
7 clone make a copy of an existing repository
7 clone make a copy of an existing repository
8 commit commit the specified files or all outstanding changes
8 commit commit the specified files or all outstanding changes
9 diff diff repository (or selected files)
9 diff diff repository (or selected files)
10 export dump the header and diffs for one or more changesets
10 export dump the header and diffs for one or more changesets
11 init create a new repository in the given directory
11 init create a new repository in the given directory
12 log show revision history of entire repository or files
12 log show revision history of entire repository or files
13 merge merge working directory with another revision
13 merge merge working directory with another revision
14 parents show the parents of the working dir or revision
14 parents show the parents of the working dir or revision
15 pull pull changes from the specified source
15 pull pull changes from the specified source
16 push push changes to the specified destination
16 push push changes to the specified destination
17 remove remove the specified files on the next commit
17 remove remove the specified files on the next commit
18 serve export the repository via HTTP
18 serve export the repository via HTTP
19 status show changed files in the working directory
19 status show changed files in the working directory
20 update update working directory
20 update update working directory
21
21
22 use "hg help" for the full list of commands or "hg -v" for details
22 use "hg help" for the full list of commands or "hg -v" for details
23 add add the specified files on the next commit
23 add add the specified files on the next commit
24 annotate show changeset information per file line
24 annotate show changeset information per file line
25 clone make a copy of an existing repository
25 clone make a copy of an existing repository
26 commit commit the specified files or all outstanding changes
26 commit commit the specified files or all outstanding changes
27 diff diff repository (or selected files)
27 diff diff repository (or selected files)
28 export dump the header and diffs for one or more changesets
28 export dump the header and diffs for one or more changesets
29 init create a new repository in the given directory
29 init create a new repository in the given directory
30 log show revision history of entire repository or files
30 log show revision history of entire repository or files
31 merge merge working directory with another revision
31 merge merge working directory with another revision
32 parents show the parents of the working dir or revision
32 parents show the parents of the working dir or revision
33 pull pull changes from the specified source
33 pull pull changes from the specified source
34 push push changes to the specified destination
34 push push changes to the specified destination
35 remove remove the specified files on the next commit
35 remove remove the specified files on the next commit
36 serve export the repository via HTTP
36 serve export the repository via HTTP
37 status show changed files in the working directory
37 status show changed files in the working directory
38 update update working directory
38 update update working directory
39 Mercurial Distributed SCM
39 Mercurial Distributed SCM
40
40
41 list of commands:
41 list of commands:
42
42
43 add add the specified files on the next commit
43 add add the specified files on the next commit
44 addremove add all new files, delete all missing files
44 addremove add all new files, delete all missing files
45 annotate show changeset information per file line
45 annotate show changeset information per file line
46 archive create unversioned archive of a repository revision
46 archive create unversioned archive of a repository revision
47 backout reverse effect of earlier changeset
47 backout reverse effect of earlier changeset
48 bisect subdivision search of changesets
48 bisect subdivision search of changesets
49 branch set or show the current branch name
49 branch set or show the current branch name
50 branches list repository named branches
50 branches list repository named branches
51 bundle create a changegroup file
51 bundle create a changegroup file
52 cat output the current or given revision of files
52 cat output the current or given revision of files
53 clone make a copy of an existing repository
53 clone make a copy of an existing repository
54 commit commit the specified files or all outstanding changes
54 commit commit the specified files or all outstanding changes
55 copy mark files as copied for the next commit
55 copy mark files as copied for the next commit
56 diff diff repository (or selected files)
56 diff diff repository (or selected files)
57 export dump the header and diffs for one or more changesets
57 export dump the header and diffs for one or more changesets
58 grep search for a pattern in specified files and revisions
58 grep search for a pattern in specified files and revisions
59 heads show current repository heads or show branch heads
59 heads show current repository heads or show branch heads
60 help show help for a given topic or a help overview
60 help show help for a given topic or a help overview
61 identify identify the working copy or specified revision
61 identify identify the working copy or specified revision
62 import import an ordered set of patches
62 import import an ordered set of patches
63 incoming show new changesets found in source
63 incoming show new changesets found in source
64 init create a new repository in the given directory
64 init create a new repository in the given directory
65 locate locate files matching specific patterns
65 locate locate files matching specific patterns
66 log show revision history of entire repository or files
66 log show revision history of entire repository or files
67 manifest output the current or given revision of the project manifest
67 manifest output the current or given revision of the project manifest
68 merge merge working directory with another revision
68 merge merge working directory with another revision
69 outgoing show changesets not found in destination
69 outgoing show changesets not found in destination
70 parents show the parents of the working dir or revision
70 parents show the parents of the working dir or revision
71 paths show aliases for remote repositories
71 paths show aliases for remote repositories
72 pull pull changes from the specified source
72 pull pull changes from the specified source
73 push push changes to the specified destination
73 push push changes to the specified destination
74 recover roll back an interrupted transaction
74 recover roll back an interrupted transaction
75 remove remove the specified files on the next commit
75 remove remove the specified files on the next commit
76 rename rename files; equivalent of copy + remove
76 rename rename files; equivalent of copy + remove
77 resolve retry file merges from a merge or update
77 resolve retry file merges from a merge or update
78 revert restore individual files or dirs to an earlier state
78 revert restore individual files or dirs to an earlier state
79 rollback roll back the last transaction
79 rollback roll back the last transaction
80 root print the root (top) of the current working dir
80 root print the root (top) of the current working dir
81 serve export the repository via HTTP
81 serve export the repository via HTTP
82 showconfig show combined config settings from all hgrc files
82 showconfig show combined config settings from all hgrc files
83 status show changed files in the working directory
83 status show changed files in the working directory
84 tag add one or more tags for the current or given revision
84 tag add one or more tags for the current or given revision
85 tags list repository tags
85 tags list repository tags
86 tip show the tip revision
86 tip show the tip revision
87 unbundle apply one or more changegroup files
87 unbundle apply one or more changegroup files
88 update update working directory
88 update update working directory
89 verify verify the integrity of the repository
89 verify verify the integrity of the repository
90 version output version and copyright information
90 version output version and copyright information
91
91
92 additional help topics:
92 additional help topics:
93
93
94 dates Date Formats
94 dates Date Formats
95 patterns File Name Patterns
95 patterns File Name Patterns
96 environment Environment Variables
96 environment Environment Variables
97 revisions Specifying Single Revisions
97 revisions Specifying Single Revisions
98 multirevs Specifying Multiple Revisions
98 multirevs Specifying Multiple Revisions
99 diffs Diff Formats
99 diffs Diff Formats
100 templating Template Usage
100 templating Template Usage
101 urls Url Paths
101 urls Url Paths
102
102
103 use "hg -v help" to show aliases and global options
103 use "hg -v help" to show aliases and global options
104 add add the specified files on the next commit
104 add add the specified files on the next commit
105 addremove add all new files, delete all missing files
105 addremove add all new files, delete all missing files
106 annotate show changeset information per file line
106 annotate show changeset information per file line
107 archive create unversioned archive of a repository revision
107 archive create unversioned archive of a repository revision
108 backout reverse effect of earlier changeset
108 backout reverse effect of earlier changeset
109 bisect subdivision search of changesets
109 bisect subdivision search of changesets
110 branch set or show the current branch name
110 branch set or show the current branch name
111 branches list repository named branches
111 branches list repository named branches
112 bundle create a changegroup file
112 bundle create a changegroup file
113 cat output the current or given revision of files
113 cat output the current or given revision of files
114 clone make a copy of an existing repository
114 clone make a copy of an existing repository
115 commit commit the specified files or all outstanding changes
115 commit commit the specified files or all outstanding changes
116 copy mark files as copied for the next commit
116 copy mark files as copied for the next commit
117 diff diff repository (or selected files)
117 diff diff repository (or selected files)
118 export dump the header and diffs for one or more changesets
118 export dump the header and diffs for one or more changesets
119 grep search for a pattern in specified files and revisions
119 grep search for a pattern in specified files and revisions
120 heads show current repository heads or show branch heads
120 heads show current repository heads or show branch heads
121 help show help for a given topic or a help overview
121 help show help for a given topic or a help overview
122 identify identify the working copy or specified revision
122 identify identify the working copy or specified revision
123 import import an ordered set of patches
123 import import an ordered set of patches
124 incoming show new changesets found in source
124 incoming show new changesets found in source
125 init create a new repository in the given directory
125 init create a new repository in the given directory
126 locate locate files matching specific patterns
126 locate locate files matching specific patterns
127 log show revision history of entire repository or files
127 log show revision history of entire repository or files
128 manifest output the current or given revision of the project manifest
128 manifest output the current or given revision of the project manifest
129 merge merge working directory with another revision
129 merge merge working directory with another revision
130 outgoing show changesets not found in destination
130 outgoing show changesets not found in destination
131 parents show the parents of the working dir or revision
131 parents show the parents of the working dir or revision
132 paths show aliases for remote repositories
132 paths show aliases for remote repositories
133 pull pull changes from the specified source
133 pull pull changes from the specified source
134 push push changes to the specified destination
134 push push changes to the specified destination
135 recover roll back an interrupted transaction
135 recover roll back an interrupted transaction
136 remove remove the specified files on the next commit
136 remove remove the specified files on the next commit
137 rename rename files; equivalent of copy + remove
137 rename rename files; equivalent of copy + remove
138 resolve retry file merges from a merge or update
138 resolve retry file merges from a merge or update
139 revert restore individual files or dirs to an earlier state
139 revert restore individual files or dirs to an earlier state
140 rollback roll back the last transaction
140 rollback roll back the last transaction
141 root print the root (top) of the current working dir
141 root print the root (top) of the current working dir
142 serve export the repository via HTTP
142 serve export the repository via HTTP
143 showconfig show combined config settings from all hgrc files
143 showconfig show combined config settings from all hgrc files
144 status show changed files in the working directory
144 status show changed files in the working directory
145 tag add one or more tags for the current or given revision
145 tag add one or more tags for the current or given revision
146 tags list repository tags
146 tags list repository tags
147 tip show the tip revision
147 tip show the tip revision
148 unbundle apply one or more changegroup files
148 unbundle apply one or more changegroup files
149 update update working directory
149 update update working directory
150 verify verify the integrity of the repository
150 verify verify the integrity of the repository
151 version output version and copyright information
151 version output version and copyright information
152
152
153 additional help topics:
153 additional help topics:
154
154
155 dates Date Formats
155 dates Date Formats
156 patterns File Name Patterns
156 patterns File Name Patterns
157 environment Environment Variables
157 environment Environment Variables
158 revisions Specifying Single Revisions
158 revisions Specifying Single Revisions
159 multirevs Specifying Multiple Revisions
159 multirevs Specifying Multiple Revisions
160 diffs Diff Formats
160 diffs Diff Formats
161 templating Template Usage
161 templating Template Usage
162 urls Url Paths
162 urls Url Paths
163 hg add [OPTION]... [FILE]...
163 hg add [OPTION]... [FILE]...
164
164
165 add the specified files on the next commit
165 add the specified files on the next commit
166
166
167 Schedule files to be version controlled and added to the repository.
167 Schedule files to be version controlled and added to the repository.
168
168
169 The files will be added to the repository at the next commit. To
169 The files will be added to the repository at the next commit. To
170 undo an add before that, see hg revert.
170 undo an add before that, see hg revert.
171
171
172 If no names are given, add all files in the repository.
172 If no names are given, add all files to the repository.
173
173
174 options:
174 options:
175
175
176 -I --include include names matching the given patterns
176 -I --include include names matching the given patterns
177 -X --exclude exclude names matching the given patterns
177 -X --exclude exclude names matching the given patterns
178 -n --dry-run do not perform actions, just print output
178 -n --dry-run do not perform actions, just print output
179
179
180 use "hg -v help add" to show global options
180 use "hg -v help add" to show global options
181 hg add: option --skjdfks not recognized
181 hg add: option --skjdfks not recognized
182 hg add [OPTION]... [FILE]...
182 hg add [OPTION]... [FILE]...
183
183
184 add the specified files on the next commit
184 add the specified files on the next commit
185
185
186 Schedule files to be version controlled and added to the repository.
186 Schedule files to be version controlled and added to the repository.
187
187
188 The files will be added to the repository at the next commit. To
188 The files will be added to the repository at the next commit. To
189 undo an add before that, see hg revert.
189 undo an add before that, see hg revert.
190
190
191 If no names are given, add all files in the repository.
191 If no names are given, add all files to the repository.
192
192
193 options:
193 options:
194
194
195 -I --include include names matching the given patterns
195 -I --include include names matching the given patterns
196 -X --exclude exclude names matching the given patterns
196 -X --exclude exclude names matching the given patterns
197 -n --dry-run do not perform actions, just print output
197 -n --dry-run do not perform actions, just print output
198
198
199 use "hg -v help add" to show global options
199 use "hg -v help add" to show global options
200 hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...
200 hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...
201
201
202 diff repository (or selected files)
202 diff repository (or selected files)
203
203
204 Show differences between revisions for the specified files.
204 Show differences between revisions for the specified files.
205
205
206 Differences between files are shown using the unified diff format.
206 Differences between files are shown using the unified diff format.
207
207
208 NOTE: diff may generate unexpected results for merges, as it will
208 NOTE: diff may generate unexpected results for merges, as it will
209 default to comparing against the working directory's first parent
209 default to comparing against the working directory's first parent
210 changeset if no revisions are specified.
210 changeset if no revisions are specified.
211
211
212 When two revision arguments are given, then changes are shown
212 When two revision arguments are given, then changes are shown
213 between those revisions. If only one revision is specified then
213 between those revisions. If only one revision is specified then
214 that revision is compared to the working directory, and, when no
214 that revision is compared to the working directory, and, when no
215 revisions are specified, the working directory files are compared
215 revisions are specified, the working directory files are compared
216 to its parent.
216 to its parent.
217
217
218 Without the -a option, diff will avoid generating diffs of files
218 Without the -a option, diff will avoid generating diffs of files
219 it detects as binary. With -a, diff will generate a diff anyway,
219 it detects as binary. With -a, diff will generate a diff anyway,
220 probably with undesirable results.
220 probably with undesirable results.
221
221
222 Use the --git option to generate diffs in the git extended diff
222 Use the --git option to generate diffs in the git extended diff
223 format. Read the diffs help topic for more information.
223 format. For more information, read hg help diffs.
224
224
225 options:
225 options:
226
226
227 -r --rev revision
227 -r --rev revision
228 -c --change change made by revision
228 -c --change change made by revision
229 -a --text treat all files as text
229 -a --text treat all files as text
230 -g --git use git extended diff format
230 -g --git use git extended diff format
231 --nodates don't include dates in diff headers
231 --nodates don't include dates in diff headers
232 -p --show-function show which function each change is in
232 -p --show-function show which function each change is in
233 -w --ignore-all-space ignore white space when comparing lines
233 -w --ignore-all-space ignore white space when comparing lines
234 -b --ignore-space-change ignore changes in the amount of white space
234 -b --ignore-space-change ignore changes in the amount of white space
235 -B --ignore-blank-lines ignore changes whose lines are all blank
235 -B --ignore-blank-lines ignore changes whose lines are all blank
236 -U --unified number of lines of context to show
236 -U --unified number of lines of context to show
237 -I --include include names matching the given patterns
237 -I --include include names matching the given patterns
238 -X --exclude exclude names matching the given patterns
238 -X --exclude exclude names matching the given patterns
239
239
240 use "hg -v help diff" to show global options
240 use "hg -v help diff" to show global options
241 hg status [OPTION]... [FILE]...
241 hg status [OPTION]... [FILE]...
242
242
243 aliases: st
243 aliases: st
244
244
245 show changed files in the working directory
245 show changed files in the working directory
246
246
247 Show status of files in the repository. If names are given, only
247 Show status of files in the repository. If names are given, only
248 files that match are shown. Files that are clean or ignored or
248 files that match are shown. Files that are clean or ignored or
249 source of a copy/move operation, are not listed unless -c (clean),
249 source of a copy/move operation, are not listed unless -c (clean),
250 -i (ignored), -C (copies) or -A is given. Unless options described
250 -i (ignored), -C (copies) or -A is given. Unless options described
251 with "show only ..." are given, the options -mardu are used.
251 with "show only ..." are given, the options -mardu are used.
252
252
253 Option -q/--quiet hides untracked (unknown and ignored) files
253 Option -q/--quiet hides untracked (unknown and ignored) files
254 unless explicitly requested with -u/--unknown or -i/-ignored.
254 unless explicitly requested with -u/--unknown or -i/-ignored.
255
255
256 NOTE: status may appear to disagree with diff if permissions have
256 NOTE: status may appear to disagree with diff if permissions have
257 changed or a merge has occurred. The standard diff format does not
257 changed or a merge has occurred. The standard diff format does not
258 report permission changes and diff only reports changes relative
258 report permission changes and diff only reports changes relative
259 to one merge parent.
259 to one merge parent.
260
260
261 If one revision is given, it is used as the base revision.
261 If one revision is given, it is used as the base revision.
262 If two revisions are given, the difference between them is shown.
262 If two revisions are given, the difference between them is shown.
263
263
264 The codes used to show the status of files are:
264 The codes used to show the status of files are:
265 M = modified
265 M = modified
266 A = added
266 A = added
267 R = removed
267 R = removed
268 C = clean
268 C = clean
269 ! = deleted, but still tracked
269 ! = deleted, but still tracked
270 ? = not tracked
270 ? = not tracked
271 I = ignored
271 I = ignored
272 = the previous added file was copied from here
272 = the previous added file was copied from here
273
273
274 options:
274 options:
275
275
276 -A --all show status of all files
276 -A --all show status of all files
277 -m --modified show only modified files
277 -m --modified show only modified files
278 -a --added show only added files
278 -a --added show only added files
279 -r --removed show only removed files
279 -r --removed show only removed files
280 -d --deleted show only deleted (but tracked) files
280 -d --deleted show only deleted (but tracked) files
281 -c --clean show only files without changes
281 -c --clean show only files without changes
282 -u --unknown show only unknown (not tracked) files
282 -u --unknown show only unknown (not tracked) files
283 -i --ignored show only ignored files
283 -i --ignored show only ignored files
284 -n --no-status hide status prefix
284 -n --no-status hide status prefix
285 -C --copies show source of copied files
285 -C --copies show source of copied files
286 -0 --print0 end filenames with NUL, for use with xargs
286 -0 --print0 end filenames with NUL, for use with xargs
287 --rev show difference from revision
287 --rev show difference from revision
288 -I --include include names matching the given patterns
288 -I --include include names matching the given patterns
289 -X --exclude exclude names matching the given patterns
289 -X --exclude exclude names matching the given patterns
290
290
291 use "hg -v help status" to show global options
291 use "hg -v help status" to show global options
292 hg status [OPTION]... [FILE]...
292 hg status [OPTION]... [FILE]...
293
293
294 show changed files in the working directory
294 show changed files in the working directory
295 hg: unknown command 'foo'
295 hg: unknown command 'foo'
296 Mercurial Distributed SCM
296 Mercurial Distributed SCM
297
297
298 basic commands:
298 basic commands:
299
299
300 add add the specified files on the next commit
300 add add the specified files on the next commit
301 annotate show changeset information per file line
301 annotate show changeset information per file line
302 clone make a copy of an existing repository
302 clone make a copy of an existing repository
303 commit commit the specified files or all outstanding changes
303 commit commit the specified files or all outstanding changes
304 diff diff repository (or selected files)
304 diff diff repository (or selected files)
305 export dump the header and diffs for one or more changesets
305 export dump the header and diffs for one or more changesets
306 init create a new repository in the given directory
306 init create a new repository in the given directory
307 log show revision history of entire repository or files
307 log show revision history of entire repository or files
308 merge merge working directory with another revision
308 merge merge working directory with another revision
309 parents show the parents of the working dir or revision
309 parents show the parents of the working dir or revision
310 pull pull changes from the specified source
310 pull pull changes from the specified source
311 push push changes to the specified destination
311 push push changes to the specified destination
312 remove remove the specified files on the next commit
312 remove remove the specified files on the next commit
313 serve export the repository via HTTP
313 serve export the repository via HTTP
314 status show changed files in the working directory
314 status show changed files in the working directory
315 update update working directory
315 update update working directory
316
316
317 use "hg help" for the full list of commands or "hg -v" for details
317 use "hg help" for the full list of commands or "hg -v" for details
318 hg: unknown command 'skjdfks'
318 hg: unknown command 'skjdfks'
319 Mercurial Distributed SCM
319 Mercurial Distributed SCM
320
320
321 basic commands:
321 basic commands:
322
322
323 add add the specified files on the next commit
323 add add the specified files on the next commit
324 annotate show changeset information per file line
324 annotate show changeset information per file line
325 clone make a copy of an existing repository
325 clone make a copy of an existing repository
326 commit commit the specified files or all outstanding changes
326 commit commit the specified files or all outstanding changes
327 diff diff repository (or selected files)
327 diff diff repository (or selected files)
328 export dump the header and diffs for one or more changesets
328 export dump the header and diffs for one or more changesets
329 init create a new repository in the given directory
329 init create a new repository in the given directory
330 log show revision history of entire repository or files
330 log show revision history of entire repository or files
331 merge merge working directory with another revision
331 merge merge working directory with another revision
332 parents show the parents of the working dir or revision
332 parents show the parents of the working dir or revision
333 pull pull changes from the specified source
333 pull pull changes from the specified source
334 push push changes to the specified destination
334 push push changes to the specified destination
335 remove remove the specified files on the next commit
335 remove remove the specified files on the next commit
336 serve export the repository via HTTP
336 serve export the repository via HTTP
337 status show changed files in the working directory
337 status show changed files in the working directory
338 update update working directory
338 update update working directory
339
339
340 use "hg help" for the full list of commands or "hg -v" for details
340 use "hg help" for the full list of commands or "hg -v" for details
General Comments 0
You need to be logged in to leave comments. Login now