##// END OF EJS Templates
discovery: drop findoutgoing and simplify findcommonincoming's api...
Peter Arrenbrecht -
r14073:72c84f24 default
parent child Browse files
Show More
@@ -1,563 +1,562
1 # patchbomb.py - sending Mercurial changesets as patch emails
1 # patchbomb.py - sending Mercurial changesets as patch emails
2 #
2 #
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 '''command to send changesets as (a series of) patch emails
8 '''command to send changesets as (a series of) patch emails
9
9
10 The series is started off with a "[PATCH 0 of N]" introduction, which
10 The series is started off with a "[PATCH 0 of N]" introduction, which
11 describes the series as a whole.
11 describes the series as a whole.
12
12
13 Each patch email has a Subject line of "[PATCH M of N] ...", using the
13 Each patch email has a Subject line of "[PATCH M of N] ...", using the
14 first line of the changeset description as the subject text. The
14 first line of the changeset description as the subject text. The
15 message contains two or three body parts:
15 message contains two or three body parts:
16
16
17 - The changeset description.
17 - The changeset description.
18 - [Optional] The result of running diffstat on the patch.
18 - [Optional] The result of running diffstat on the patch.
19 - The patch itself, as generated by :hg:`export`.
19 - The patch itself, as generated by :hg:`export`.
20
20
21 Each message refers to the first in the series using the In-Reply-To
21 Each message refers to the first in the series using the In-Reply-To
22 and References headers, so they will show up as a sequence in threaded
22 and References headers, so they will show up as a sequence in threaded
23 mail and news readers, and in mail archives.
23 mail and news readers, and in mail archives.
24
24
25 To configure other defaults, add a section like this to your
25 To configure other defaults, add a section like this to your
26 configuration file::
26 configuration file::
27
27
28 [email]
28 [email]
29 from = My Name <my@email>
29 from = My Name <my@email>
30 to = recipient1, recipient2, ...
30 to = recipient1, recipient2, ...
31 cc = cc1, cc2, ...
31 cc = cc1, cc2, ...
32 bcc = bcc1, bcc2, ...
32 bcc = bcc1, bcc2, ...
33 reply-to = address1, address2, ...
33 reply-to = address1, address2, ...
34
34
35 Use ``[patchbomb]`` as configuration section name if you need to
35 Use ``[patchbomb]`` as configuration section name if you need to
36 override global ``[email]`` address settings.
36 override global ``[email]`` address settings.
37
37
38 Then you can use the :hg:`email` command to mail a series of
38 Then you can use the :hg:`email` command to mail a series of
39 changesets as a patchbomb.
39 changesets as a patchbomb.
40
40
41 You can also either configure the method option in the email section
41 You can also either configure the method option in the email section
42 to be a sendmail compatible mailer or fill out the [smtp] section so
42 to be a sendmail compatible mailer or fill out the [smtp] section so
43 that the patchbomb extension can automatically send patchbombs
43 that the patchbomb extension can automatically send patchbombs
44 directly from the commandline. See the [email] and [smtp] sections in
44 directly from the commandline. See the [email] and [smtp] sections in
45 hgrc(5) for details.
45 hgrc(5) for details.
46 '''
46 '''
47
47
48 import os, errno, socket, tempfile, cStringIO, time
48 import os, errno, socket, tempfile, cStringIO, time
49 import email.MIMEMultipart, email.MIMEBase
49 import email.MIMEMultipart, email.MIMEBase
50 import email.Utils, email.Encoders, email.Generator
50 import email.Utils, email.Encoders, email.Generator
51 from mercurial import cmdutil, commands, hg, mail, patch, util, discovery, url
51 from mercurial import cmdutil, commands, hg, mail, patch, util, discovery, url
52 from mercurial.i18n import _
52 from mercurial.i18n import _
53 from mercurial.node import bin
53 from mercurial.node import bin
54
54
55 def prompt(ui, prompt, default=None, rest=':'):
55 def prompt(ui, prompt, default=None, rest=':'):
56 if not ui.interactive() and default is None:
56 if not ui.interactive() and default is None:
57 raise util.Abort(_("%s Please enter a valid value" % (prompt + rest)))
57 raise util.Abort(_("%s Please enter a valid value" % (prompt + rest)))
58 if default:
58 if default:
59 prompt += ' [%s]' % default
59 prompt += ' [%s]' % default
60 prompt += rest
60 prompt += rest
61 while True:
61 while True:
62 r = ui.prompt(prompt, default=default)
62 r = ui.prompt(prompt, default=default)
63 if r:
63 if r:
64 return r
64 return r
65 if default is not None:
65 if default is not None:
66 return default
66 return default
67 ui.warn(_('Please enter a valid value.\n'))
67 ui.warn(_('Please enter a valid value.\n'))
68
68
69 def introneeded(opts, number):
69 def introneeded(opts, number):
70 '''is an introductory message required?'''
70 '''is an introductory message required?'''
71 return number > 1 or opts.get('intro') or opts.get('desc')
71 return number > 1 or opts.get('intro') or opts.get('desc')
72
72
73 def makepatch(ui, repo, patchlines, opts, _charsets, idx, total,
73 def makepatch(ui, repo, patchlines, opts, _charsets, idx, total,
74 patchname=None):
74 patchname=None):
75
75
76 desc = []
76 desc = []
77 node = None
77 node = None
78 body = ''
78 body = ''
79
79
80 for line in patchlines:
80 for line in patchlines:
81 if line.startswith('#'):
81 if line.startswith('#'):
82 if line.startswith('# Node ID'):
82 if line.startswith('# Node ID'):
83 node = line.split()[-1]
83 node = line.split()[-1]
84 continue
84 continue
85 if line.startswith('diff -r') or line.startswith('diff --git'):
85 if line.startswith('diff -r') or line.startswith('diff --git'):
86 break
86 break
87 desc.append(line)
87 desc.append(line)
88
88
89 if not patchname and not node:
89 if not patchname and not node:
90 raise ValueError
90 raise ValueError
91
91
92 if opts.get('attach'):
92 if opts.get('attach'):
93 body = ('\n'.join(desc[1:]).strip() or
93 body = ('\n'.join(desc[1:]).strip() or
94 'Patch subject is complete summary.')
94 'Patch subject is complete summary.')
95 body += '\n\n\n'
95 body += '\n\n\n'
96
96
97 if opts.get('plain'):
97 if opts.get('plain'):
98 while patchlines and patchlines[0].startswith('# '):
98 while patchlines and patchlines[0].startswith('# '):
99 patchlines.pop(0)
99 patchlines.pop(0)
100 if patchlines:
100 if patchlines:
101 patchlines.pop(0)
101 patchlines.pop(0)
102 while patchlines and not patchlines[0].strip():
102 while patchlines and not patchlines[0].strip():
103 patchlines.pop(0)
103 patchlines.pop(0)
104
104
105 ds = patch.diffstat(patchlines)
105 ds = patch.diffstat(patchlines)
106 if opts.get('diffstat'):
106 if opts.get('diffstat'):
107 body += ds + '\n\n'
107 body += ds + '\n\n'
108
108
109 if opts.get('attach') or opts.get('inline'):
109 if opts.get('attach') or opts.get('inline'):
110 msg = email.MIMEMultipart.MIMEMultipart()
110 msg = email.MIMEMultipart.MIMEMultipart()
111 if body:
111 if body:
112 msg.attach(mail.mimeencode(ui, body, _charsets, opts.get('test')))
112 msg.attach(mail.mimeencode(ui, body, _charsets, opts.get('test')))
113 p = mail.mimetextpatch('\n'.join(patchlines), 'x-patch', opts.get('test'))
113 p = mail.mimetextpatch('\n'.join(patchlines), 'x-patch', opts.get('test'))
114 binnode = bin(node)
114 binnode = bin(node)
115 # if node is mq patch, it will have the patch file's name as a tag
115 # if node is mq patch, it will have the patch file's name as a tag
116 if not patchname:
116 if not patchname:
117 patchtags = [t for t in repo.nodetags(binnode)
117 patchtags = [t for t in repo.nodetags(binnode)
118 if t.endswith('.patch') or t.endswith('.diff')]
118 if t.endswith('.patch') or t.endswith('.diff')]
119 if patchtags:
119 if patchtags:
120 patchname = patchtags[0]
120 patchname = patchtags[0]
121 elif total > 1:
121 elif total > 1:
122 patchname = cmdutil.make_filename(repo, '%b-%n.patch',
122 patchname = cmdutil.make_filename(repo, '%b-%n.patch',
123 binnode, seqno=idx, total=total)
123 binnode, seqno=idx, total=total)
124 else:
124 else:
125 patchname = cmdutil.make_filename(repo, '%b.patch', binnode)
125 patchname = cmdutil.make_filename(repo, '%b.patch', binnode)
126 disposition = 'inline'
126 disposition = 'inline'
127 if opts.get('attach'):
127 if opts.get('attach'):
128 disposition = 'attachment'
128 disposition = 'attachment'
129 p['Content-Disposition'] = disposition + '; filename=' + patchname
129 p['Content-Disposition'] = disposition + '; filename=' + patchname
130 msg.attach(p)
130 msg.attach(p)
131 else:
131 else:
132 body += '\n'.join(patchlines)
132 body += '\n'.join(patchlines)
133 msg = mail.mimetextpatch(body, display=opts.get('test'))
133 msg = mail.mimetextpatch(body, display=opts.get('test'))
134
134
135 flag = ' '.join(opts.get('flag'))
135 flag = ' '.join(opts.get('flag'))
136 if flag:
136 if flag:
137 flag = ' ' + flag
137 flag = ' ' + flag
138
138
139 subj = desc[0].strip().rstrip('. ')
139 subj = desc[0].strip().rstrip('. ')
140 if not introneeded(opts, total):
140 if not introneeded(opts, total):
141 subj = '[PATCH%s] %s' % (flag, opts.get('subject') or subj)
141 subj = '[PATCH%s] %s' % (flag, opts.get('subject') or subj)
142 else:
142 else:
143 tlen = len(str(total))
143 tlen = len(str(total))
144 subj = '[PATCH %0*d of %d%s] %s' % (tlen, idx, total, flag, subj)
144 subj = '[PATCH %0*d of %d%s] %s' % (tlen, idx, total, flag, subj)
145 msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test'))
145 msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test'))
146 msg['X-Mercurial-Node'] = node
146 msg['X-Mercurial-Node'] = node
147 return msg, subj, ds
147 return msg, subj, ds
148
148
149 def patchbomb(ui, repo, *revs, **opts):
149 def patchbomb(ui, repo, *revs, **opts):
150 '''send changesets by email
150 '''send changesets by email
151
151
152 By default, diffs are sent in the format generated by
152 By default, diffs are sent in the format generated by
153 :hg:`export`, one per message. The series starts with a "[PATCH 0
153 :hg:`export`, one per message. The series starts with a "[PATCH 0
154 of N]" introduction, which describes the series as a whole.
154 of N]" introduction, which describes the series as a whole.
155
155
156 Each patch email has a Subject line of "[PATCH M of N] ...", using
156 Each patch email has a Subject line of "[PATCH M of N] ...", using
157 the first line of the changeset description as the subject text.
157 the first line of the changeset description as the subject text.
158 The message contains two or three parts. First, the changeset
158 The message contains two or three parts. First, the changeset
159 description.
159 description.
160
160
161 With the -d/--diffstat option, if the diffstat program is
161 With the -d/--diffstat option, if the diffstat program is
162 installed, the result of running diffstat on the patch is inserted.
162 installed, the result of running diffstat on the patch is inserted.
163
163
164 Finally, the patch itself, as generated by :hg:`export`.
164 Finally, the patch itself, as generated by :hg:`export`.
165
165
166 With the -d/--diffstat or -c/--confirm options, you will be presented
166 With the -d/--diffstat or -c/--confirm options, you will be presented
167 with a final summary of all messages and asked for confirmation before
167 with a final summary of all messages and asked for confirmation before
168 the messages are sent.
168 the messages are sent.
169
169
170 By default the patch is included as text in the email body for
170 By default the patch is included as text in the email body for
171 easy reviewing. Using the -a/--attach option will instead create
171 easy reviewing. Using the -a/--attach option will instead create
172 an attachment for the patch. With -i/--inline an inline attachment
172 an attachment for the patch. With -i/--inline an inline attachment
173 will be created.
173 will be created.
174
174
175 With -o/--outgoing, emails will be generated for patches not found
175 With -o/--outgoing, emails will be generated for patches not found
176 in the destination repository (or only those which are ancestors
176 in the destination repository (or only those which are ancestors
177 of the specified revisions if any are provided)
177 of the specified revisions if any are provided)
178
178
179 With -b/--bundle, changesets are selected as for --outgoing, but a
179 With -b/--bundle, changesets are selected as for --outgoing, but a
180 single email containing a binary Mercurial bundle as an attachment
180 single email containing a binary Mercurial bundle as an attachment
181 will be sent.
181 will be sent.
182
182
183 With -m/--mbox, instead of previewing each patchbomb message in a
183 With -m/--mbox, instead of previewing each patchbomb message in a
184 pager or sending the messages directly, it will create a UNIX
184 pager or sending the messages directly, it will create a UNIX
185 mailbox file with the patch emails. This mailbox file can be
185 mailbox file with the patch emails. This mailbox file can be
186 previewed with any mail user agent which supports UNIX mbox
186 previewed with any mail user agent which supports UNIX mbox
187 files.
187 files.
188
188
189 With -n/--test, all steps will run, but mail will not be sent.
189 With -n/--test, all steps will run, but mail will not be sent.
190 You will be prompted for an email recipient address, a subject and
190 You will be prompted for an email recipient address, a subject and
191 an introductory message describing the patches of your patchbomb.
191 an introductory message describing the patches of your patchbomb.
192 Then when all is done, patchbomb messages are displayed. If the
192 Then when all is done, patchbomb messages are displayed. If the
193 PAGER environment variable is set, your pager will be fired up once
193 PAGER environment variable is set, your pager will be fired up once
194 for each patchbomb message, so you can verify everything is alright.
194 for each patchbomb message, so you can verify everything is alright.
195
195
196 In case email sending fails, you will find a backup of your series
196 In case email sending fails, you will find a backup of your series
197 introductory message in ``.hg/last-email.txt``.
197 introductory message in ``.hg/last-email.txt``.
198
198
199 Examples::
199 Examples::
200
200
201 hg email -r 3000 # send patch 3000 only
201 hg email -r 3000 # send patch 3000 only
202 hg email -r 3000 -r 3001 # send patches 3000 and 3001
202 hg email -r 3000 -r 3001 # send patches 3000 and 3001
203 hg email -r 3000:3005 # send patches 3000 through 3005
203 hg email -r 3000:3005 # send patches 3000 through 3005
204 hg email 3000 # send patch 3000 (deprecated)
204 hg email 3000 # send patch 3000 (deprecated)
205
205
206 hg email -o # send all patches not in default
206 hg email -o # send all patches not in default
207 hg email -o DEST # send all patches not in DEST
207 hg email -o DEST # send all patches not in DEST
208 hg email -o -r 3000 # send all ancestors of 3000 not in default
208 hg email -o -r 3000 # send all ancestors of 3000 not in default
209 hg email -o -r 3000 DEST # send all ancestors of 3000 not in DEST
209 hg email -o -r 3000 DEST # send all ancestors of 3000 not in DEST
210
210
211 hg email -b # send bundle of all patches not in default
211 hg email -b # send bundle of all patches not in default
212 hg email -b DEST # send bundle of all patches not in DEST
212 hg email -b DEST # send bundle of all patches not in DEST
213 hg email -b -r 3000 # bundle of all ancestors of 3000 not in default
213 hg email -b -r 3000 # bundle of all ancestors of 3000 not in default
214 hg email -b -r 3000 DEST # bundle of all ancestors of 3000 not in DEST
214 hg email -b -r 3000 DEST # bundle of all ancestors of 3000 not in DEST
215
215
216 hg email -o -m mbox && # generate an mbox file...
216 hg email -o -m mbox && # generate an mbox file...
217 mutt -R -f mbox # ... and view it with mutt
217 mutt -R -f mbox # ... and view it with mutt
218 hg email -o -m mbox && # generate an mbox file ...
218 hg email -o -m mbox && # generate an mbox file ...
219 formail -s sendmail \\ # ... and use formail to send from the mbox
219 formail -s sendmail \\ # ... and use formail to send from the mbox
220 -bm -t < mbox # ... using sendmail
220 -bm -t < mbox # ... using sendmail
221
221
222 Before using this command, you will need to enable email in your
222 Before using this command, you will need to enable email in your
223 hgrc. See the [email] section in hgrc(5) for details.
223 hgrc. See the [email] section in hgrc(5) for details.
224 '''
224 '''
225
225
226 _charsets = mail._charsets(ui)
226 _charsets = mail._charsets(ui)
227
227
228 bundle = opts.get('bundle')
228 bundle = opts.get('bundle')
229 date = opts.get('date')
229 date = opts.get('date')
230 mbox = opts.get('mbox')
230 mbox = opts.get('mbox')
231 outgoing = opts.get('outgoing')
231 outgoing = opts.get('outgoing')
232 rev = opts.get('rev')
232 rev = opts.get('rev')
233 # internal option used by pbranches
233 # internal option used by pbranches
234 patches = opts.get('patches')
234 patches = opts.get('patches')
235
235
236 def getoutgoing(dest, revs):
236 def getoutgoing(dest, revs):
237 '''Return the revisions present locally but not in dest'''
237 '''Return the revisions present locally but not in dest'''
238 dest = ui.expandpath(dest or 'default-push', dest or 'default')
238 dest = ui.expandpath(dest or 'default-push', dest or 'default')
239 dest, branches = hg.parseurl(dest)
239 dest, branches = hg.parseurl(dest)
240 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
240 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
241 if revs:
242 revs = [repo.lookup(rev) for rev in revs]
243 other = hg.repository(hg.remoteui(repo, opts), dest)
241 other = hg.repository(hg.remoteui(repo, opts), dest)
244 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
242 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
245 o = discovery.findoutgoing(repo, other)
243 common, _anyinc, _heads = discovery.findcommonincoming(repo, other)
244 nodes = revs and map(repo.lookup, revs) or revs
245 o = repo.changelog.findmissing(common, heads=nodes)
246 if not o:
246 if not o:
247 ui.status(_("no changes found\n"))
247 ui.status(_("no changes found\n"))
248 return []
248 return []
249 o = repo.changelog.nodesbetween(o, revs)[0]
250 return [str(repo.changelog.rev(r)) for r in o]
249 return [str(repo.changelog.rev(r)) for r in o]
251
250
252 def getpatches(revs):
251 def getpatches(revs):
253 for r in cmdutil.revrange(repo, revs):
252 for r in cmdutil.revrange(repo, revs):
254 output = cStringIO.StringIO()
253 output = cStringIO.StringIO()
255 cmdutil.export(repo, [r], fp=output,
254 cmdutil.export(repo, [r], fp=output,
256 opts=patch.diffopts(ui, opts))
255 opts=patch.diffopts(ui, opts))
257 yield output.getvalue().split('\n')
256 yield output.getvalue().split('\n')
258
257
259 def getbundle(dest):
258 def getbundle(dest):
260 tmpdir = tempfile.mkdtemp(prefix='hg-email-bundle-')
259 tmpdir = tempfile.mkdtemp(prefix='hg-email-bundle-')
261 tmpfn = os.path.join(tmpdir, 'bundle')
260 tmpfn = os.path.join(tmpdir, 'bundle')
262 try:
261 try:
263 commands.bundle(ui, repo, tmpfn, dest, **opts)
262 commands.bundle(ui, repo, tmpfn, dest, **opts)
264 fp = open(tmpfn, 'rb')
263 fp = open(tmpfn, 'rb')
265 data = fp.read()
264 data = fp.read()
266 fp.close()
265 fp.close()
267 return data
266 return data
268 finally:
267 finally:
269 try:
268 try:
270 os.unlink(tmpfn)
269 os.unlink(tmpfn)
271 except:
270 except:
272 pass
271 pass
273 os.rmdir(tmpdir)
272 os.rmdir(tmpdir)
274
273
275 if not (opts.get('test') or mbox):
274 if not (opts.get('test') or mbox):
276 # really sending
275 # really sending
277 mail.validateconfig(ui)
276 mail.validateconfig(ui)
278
277
279 if not (revs or rev or outgoing or bundle or patches):
278 if not (revs or rev or outgoing or bundle or patches):
280 raise util.Abort(_('specify at least one changeset with -r or -o'))
279 raise util.Abort(_('specify at least one changeset with -r or -o'))
281
280
282 if outgoing and bundle:
281 if outgoing and bundle:
283 raise util.Abort(_("--outgoing mode always on with --bundle;"
282 raise util.Abort(_("--outgoing mode always on with --bundle;"
284 " do not re-specify --outgoing"))
283 " do not re-specify --outgoing"))
285
284
286 if outgoing or bundle:
285 if outgoing or bundle:
287 if len(revs) > 1:
286 if len(revs) > 1:
288 raise util.Abort(_("too many destinations"))
287 raise util.Abort(_("too many destinations"))
289 dest = revs and revs[0] or None
288 dest = revs and revs[0] or None
290 revs = []
289 revs = []
291
290
292 if rev:
291 if rev:
293 if revs:
292 if revs:
294 raise util.Abort(_('use only one form to specify the revision'))
293 raise util.Abort(_('use only one form to specify the revision'))
295 revs = rev
294 revs = rev
296
295
297 if outgoing:
296 if outgoing:
298 revs = getoutgoing(dest, rev)
297 revs = getoutgoing(dest, rev)
299 if bundle:
298 if bundle:
300 opts['revs'] = revs
299 opts['revs'] = revs
301
300
302 # start
301 # start
303 if date:
302 if date:
304 start_time = util.parsedate(date)
303 start_time = util.parsedate(date)
305 else:
304 else:
306 start_time = util.makedate()
305 start_time = util.makedate()
307
306
308 def genmsgid(id):
307 def genmsgid(id):
309 return '<%s.%s@%s>' % (id[:20], int(start_time[0]), socket.getfqdn())
308 return '<%s.%s@%s>' % (id[:20], int(start_time[0]), socket.getfqdn())
310
309
311 def getdescription(body, sender):
310 def getdescription(body, sender):
312 if opts.get('desc'):
311 if opts.get('desc'):
313 body = open(opts.get('desc')).read()
312 body = open(opts.get('desc')).read()
314 else:
313 else:
315 ui.write(_('\nWrite the introductory message for the '
314 ui.write(_('\nWrite the introductory message for the '
316 'patch series.\n\n'))
315 'patch series.\n\n'))
317 body = ui.edit(body, sender)
316 body = ui.edit(body, sender)
318 # Save serie description in case sendmail fails
317 # Save serie description in case sendmail fails
319 msgfile = repo.opener('last-email.txt', 'wb')
318 msgfile = repo.opener('last-email.txt', 'wb')
320 msgfile.write(body)
319 msgfile.write(body)
321 msgfile.close()
320 msgfile.close()
322 return body
321 return body
323
322
324 def getpatchmsgs(patches, patchnames=None):
323 def getpatchmsgs(patches, patchnames=None):
325 jumbo = []
324 jumbo = []
326 msgs = []
325 msgs = []
327
326
328 ui.write(_('This patch series consists of %d patches.\n\n')
327 ui.write(_('This patch series consists of %d patches.\n\n')
329 % len(patches))
328 % len(patches))
330
329
331 name = None
330 name = None
332 for i, p in enumerate(patches):
331 for i, p in enumerate(patches):
333 jumbo.extend(p)
332 jumbo.extend(p)
334 if patchnames:
333 if patchnames:
335 name = patchnames[i]
334 name = patchnames[i]
336 msg = makepatch(ui, repo, p, opts, _charsets, i + 1,
335 msg = makepatch(ui, repo, p, opts, _charsets, i + 1,
337 len(patches), name)
336 len(patches), name)
338 msgs.append(msg)
337 msgs.append(msg)
339
338
340 if introneeded(opts, len(patches)):
339 if introneeded(opts, len(patches)):
341 tlen = len(str(len(patches)))
340 tlen = len(str(len(patches)))
342
341
343 flag = ' '.join(opts.get('flag'))
342 flag = ' '.join(opts.get('flag'))
344 if flag:
343 if flag:
345 subj = '[PATCH %0*d of %d %s]' % (tlen, 0, len(patches), flag)
344 subj = '[PATCH %0*d of %d %s]' % (tlen, 0, len(patches), flag)
346 else:
345 else:
347 subj = '[PATCH %0*d of %d]' % (tlen, 0, len(patches))
346 subj = '[PATCH %0*d of %d]' % (tlen, 0, len(patches))
348 subj += ' ' + (opts.get('subject') or
347 subj += ' ' + (opts.get('subject') or
349 prompt(ui, 'Subject: ', rest=subj))
348 prompt(ui, 'Subject: ', rest=subj))
350
349
351 body = ''
350 body = ''
352 ds = patch.diffstat(jumbo)
351 ds = patch.diffstat(jumbo)
353 if ds and opts.get('diffstat'):
352 if ds and opts.get('diffstat'):
354 body = '\n' + ds
353 body = '\n' + ds
355
354
356 body = getdescription(body, sender)
355 body = getdescription(body, sender)
357 msg = mail.mimeencode(ui, body, _charsets, opts.get('test'))
356 msg = mail.mimeencode(ui, body, _charsets, opts.get('test'))
358 msg['Subject'] = mail.headencode(ui, subj, _charsets,
357 msg['Subject'] = mail.headencode(ui, subj, _charsets,
359 opts.get('test'))
358 opts.get('test'))
360
359
361 msgs.insert(0, (msg, subj, ds))
360 msgs.insert(0, (msg, subj, ds))
362 return msgs
361 return msgs
363
362
364 def getbundlemsgs(bundle):
363 def getbundlemsgs(bundle):
365 subj = (opts.get('subject')
364 subj = (opts.get('subject')
366 or prompt(ui, 'Subject:', 'A bundle for your repository'))
365 or prompt(ui, 'Subject:', 'A bundle for your repository'))
367
366
368 body = getdescription('', sender)
367 body = getdescription('', sender)
369 msg = email.MIMEMultipart.MIMEMultipart()
368 msg = email.MIMEMultipart.MIMEMultipart()
370 if body:
369 if body:
371 msg.attach(mail.mimeencode(ui, body, _charsets, opts.get('test')))
370 msg.attach(mail.mimeencode(ui, body, _charsets, opts.get('test')))
372 datapart = email.MIMEBase.MIMEBase('application', 'x-mercurial-bundle')
371 datapart = email.MIMEBase.MIMEBase('application', 'x-mercurial-bundle')
373 datapart.set_payload(bundle)
372 datapart.set_payload(bundle)
374 bundlename = '%s.hg' % opts.get('bundlename', 'bundle')
373 bundlename = '%s.hg' % opts.get('bundlename', 'bundle')
375 datapart.add_header('Content-Disposition', 'attachment',
374 datapart.add_header('Content-Disposition', 'attachment',
376 filename=bundlename)
375 filename=bundlename)
377 email.Encoders.encode_base64(datapart)
376 email.Encoders.encode_base64(datapart)
378 msg.attach(datapart)
377 msg.attach(datapart)
379 msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test'))
378 msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test'))
380 return [(msg, subj, None)]
379 return [(msg, subj, None)]
381
380
382 sender = (opts.get('from') or ui.config('email', 'from') or
381 sender = (opts.get('from') or ui.config('email', 'from') or
383 ui.config('patchbomb', 'from') or
382 ui.config('patchbomb', 'from') or
384 prompt(ui, 'From', ui.username()))
383 prompt(ui, 'From', ui.username()))
385
384
386 if patches:
385 if patches:
387 msgs = getpatchmsgs(patches, opts.get('patchnames'))
386 msgs = getpatchmsgs(patches, opts.get('patchnames'))
388 elif bundle:
387 elif bundle:
389 msgs = getbundlemsgs(getbundle(dest))
388 msgs = getbundlemsgs(getbundle(dest))
390 else:
389 else:
391 msgs = getpatchmsgs(list(getpatches(revs)))
390 msgs = getpatchmsgs(list(getpatches(revs)))
392
391
393 showaddrs = []
392 showaddrs = []
394
393
395 def getaddrs(opt, prpt=None, default=None):
394 def getaddrs(opt, prpt=None, default=None):
396 addrs = opts.get(opt.replace('-', '_'))
395 addrs = opts.get(opt.replace('-', '_'))
397 if opt != 'reply-to':
396 if opt != 'reply-to':
398 showaddr = '%s:' % opt.capitalize()
397 showaddr = '%s:' % opt.capitalize()
399 else:
398 else:
400 showaddr = 'Reply-To:'
399 showaddr = 'Reply-To:'
401
400
402 if addrs:
401 if addrs:
403 showaddrs.append('%s %s' % (showaddr, ', '.join(addrs)))
402 showaddrs.append('%s %s' % (showaddr, ', '.join(addrs)))
404 return mail.addrlistencode(ui, addrs, _charsets, opts.get('test'))
403 return mail.addrlistencode(ui, addrs, _charsets, opts.get('test'))
405
404
406 addrs = ui.config('email', opt) or ui.config('patchbomb', opt) or ''
405 addrs = ui.config('email', opt) or ui.config('patchbomb', opt) or ''
407 if not addrs and prpt:
406 if not addrs and prpt:
408 addrs = prompt(ui, prpt, default)
407 addrs = prompt(ui, prpt, default)
409
408
410 if addrs:
409 if addrs:
411 showaddrs.append('%s %s' % (showaddr, addrs))
410 showaddrs.append('%s %s' % (showaddr, addrs))
412 return mail.addrlistencode(ui, [addrs], _charsets, opts.get('test'))
411 return mail.addrlistencode(ui, [addrs], _charsets, opts.get('test'))
413
412
414 to = getaddrs('to', 'To')
413 to = getaddrs('to', 'To')
415 cc = getaddrs('cc', 'Cc', '')
414 cc = getaddrs('cc', 'Cc', '')
416 bcc = getaddrs('bcc')
415 bcc = getaddrs('bcc')
417 replyto = getaddrs('reply-to')
416 replyto = getaddrs('reply-to')
418
417
419 if opts.get('diffstat') or opts.get('confirm'):
418 if opts.get('diffstat') or opts.get('confirm'):
420 ui.write(_('\nFinal summary:\n\n'))
419 ui.write(_('\nFinal summary:\n\n'))
421 ui.write('From: %s\n' % sender)
420 ui.write('From: %s\n' % sender)
422 for addr in showaddrs:
421 for addr in showaddrs:
423 ui.write('%s\n' % addr)
422 ui.write('%s\n' % addr)
424 for m, subj, ds in msgs:
423 for m, subj, ds in msgs:
425 ui.write('Subject: %s\n' % subj)
424 ui.write('Subject: %s\n' % subj)
426 if ds:
425 if ds:
427 ui.write(ds)
426 ui.write(ds)
428 ui.write('\n')
427 ui.write('\n')
429 if ui.promptchoice(_('are you sure you want to send (yn)?'),
428 if ui.promptchoice(_('are you sure you want to send (yn)?'),
430 (_('&Yes'), _('&No'))):
429 (_('&Yes'), _('&No'))):
431 raise util.Abort(_('patchbomb canceled'))
430 raise util.Abort(_('patchbomb canceled'))
432
431
433 ui.write('\n')
432 ui.write('\n')
434
433
435 parent = opts.get('in_reply_to') or None
434 parent = opts.get('in_reply_to') or None
436 # angle brackets may be omitted, they're not semantically part of the msg-id
435 # angle brackets may be omitted, they're not semantically part of the msg-id
437 if parent is not None:
436 if parent is not None:
438 if not parent.startswith('<'):
437 if not parent.startswith('<'):
439 parent = '<' + parent
438 parent = '<' + parent
440 if not parent.endswith('>'):
439 if not parent.endswith('>'):
441 parent += '>'
440 parent += '>'
442
441
443 first = True
442 first = True
444
443
445 sender_addr = email.Utils.parseaddr(sender)[1]
444 sender_addr = email.Utils.parseaddr(sender)[1]
446 sender = mail.addressencode(ui, sender, _charsets, opts.get('test'))
445 sender = mail.addressencode(ui, sender, _charsets, opts.get('test'))
447 sendmail = None
446 sendmail = None
448 for i, (m, subj, ds) in enumerate(msgs):
447 for i, (m, subj, ds) in enumerate(msgs):
449 try:
448 try:
450 m['Message-Id'] = genmsgid(m['X-Mercurial-Node'])
449 m['Message-Id'] = genmsgid(m['X-Mercurial-Node'])
451 except TypeError:
450 except TypeError:
452 m['Message-Id'] = genmsgid('patchbomb')
451 m['Message-Id'] = genmsgid('patchbomb')
453 if parent:
452 if parent:
454 m['In-Reply-To'] = parent
453 m['In-Reply-To'] = parent
455 m['References'] = parent
454 m['References'] = parent
456 if first:
455 if first:
457 parent = m['Message-Id']
456 parent = m['Message-Id']
458 first = False
457 first = False
459
458
460 m['User-Agent'] = 'Mercurial-patchbomb/%s' % util.version()
459 m['User-Agent'] = 'Mercurial-patchbomb/%s' % util.version()
461 m['Date'] = email.Utils.formatdate(start_time[0], localtime=True)
460 m['Date'] = email.Utils.formatdate(start_time[0], localtime=True)
462
461
463 start_time = (start_time[0] + 1, start_time[1])
462 start_time = (start_time[0] + 1, start_time[1])
464 m['From'] = sender
463 m['From'] = sender
465 m['To'] = ', '.join(to)
464 m['To'] = ', '.join(to)
466 if cc:
465 if cc:
467 m['Cc'] = ', '.join(cc)
466 m['Cc'] = ', '.join(cc)
468 if bcc:
467 if bcc:
469 m['Bcc'] = ', '.join(bcc)
468 m['Bcc'] = ', '.join(bcc)
470 if replyto:
469 if replyto:
471 m['Reply-To'] = ', '.join(replyto)
470 m['Reply-To'] = ', '.join(replyto)
472 if opts.get('test'):
471 if opts.get('test'):
473 ui.status(_('Displaying '), subj, ' ...\n')
472 ui.status(_('Displaying '), subj, ' ...\n')
474 ui.flush()
473 ui.flush()
475 if 'PAGER' in os.environ and not ui.plain():
474 if 'PAGER' in os.environ and not ui.plain():
476 fp = util.popen(os.environ['PAGER'], 'w')
475 fp = util.popen(os.environ['PAGER'], 'w')
477 else:
476 else:
478 fp = ui
477 fp = ui
479 generator = email.Generator.Generator(fp, mangle_from_=False)
478 generator = email.Generator.Generator(fp, mangle_from_=False)
480 try:
479 try:
481 generator.flatten(m, 0)
480 generator.flatten(m, 0)
482 fp.write('\n')
481 fp.write('\n')
483 except IOError, inst:
482 except IOError, inst:
484 if inst.errno != errno.EPIPE:
483 if inst.errno != errno.EPIPE:
485 raise
484 raise
486 if fp is not ui:
485 if fp is not ui:
487 fp.close()
486 fp.close()
488 elif mbox:
487 elif mbox:
489 ui.status(_('Writing '), subj, ' ...\n')
488 ui.status(_('Writing '), subj, ' ...\n')
490 ui.progress(_('writing'), i, item=subj, total=len(msgs))
489 ui.progress(_('writing'), i, item=subj, total=len(msgs))
491 fp = open(mbox, 'In-Reply-To' in m and 'ab+' or 'wb+')
490 fp = open(mbox, 'In-Reply-To' in m and 'ab+' or 'wb+')
492 generator = email.Generator.Generator(fp, mangle_from_=True)
491 generator = email.Generator.Generator(fp, mangle_from_=True)
493 # Should be time.asctime(), but Windows prints 2-characters day
492 # Should be time.asctime(), but Windows prints 2-characters day
494 # of month instead of one. Make them print the same thing.
493 # of month instead of one. Make them print the same thing.
495 date = time.strftime('%a %b %d %H:%M:%S %Y',
494 date = time.strftime('%a %b %d %H:%M:%S %Y',
496 time.localtime(start_time[0]))
495 time.localtime(start_time[0]))
497 fp.write('From %s %s\n' % (sender_addr, date))
496 fp.write('From %s %s\n' % (sender_addr, date))
498 generator.flatten(m, 0)
497 generator.flatten(m, 0)
499 fp.write('\n\n')
498 fp.write('\n\n')
500 fp.close()
499 fp.close()
501 else:
500 else:
502 if not sendmail:
501 if not sendmail:
503 sendmail = mail.connect(ui)
502 sendmail = mail.connect(ui)
504 ui.status(_('Sending '), subj, ' ...\n')
503 ui.status(_('Sending '), subj, ' ...\n')
505 ui.progress(_('sending'), i, item=subj, total=len(msgs))
504 ui.progress(_('sending'), i, item=subj, total=len(msgs))
506 # Exim does not remove the Bcc field
505 # Exim does not remove the Bcc field
507 del m['Bcc']
506 del m['Bcc']
508 fp = cStringIO.StringIO()
507 fp = cStringIO.StringIO()
509 generator = email.Generator.Generator(fp, mangle_from_=False)
508 generator = email.Generator.Generator(fp, mangle_from_=False)
510 generator.flatten(m, 0)
509 generator.flatten(m, 0)
511 sendmail(sender, to + bcc + cc, fp.getvalue())
510 sendmail(sender, to + bcc + cc, fp.getvalue())
512
511
513 ui.progress(_('writing'), None)
512 ui.progress(_('writing'), None)
514 ui.progress(_('sending'), None)
513 ui.progress(_('sending'), None)
515
514
516 emailopts = [
515 emailopts = [
517 ('a', 'attach', None, _('send patches as attachments')),
516 ('a', 'attach', None, _('send patches as attachments')),
518 ('i', 'inline', None, _('send patches as inline attachments')),
517 ('i', 'inline', None, _('send patches as inline attachments')),
519 ('', 'bcc', [], _('email addresses of blind carbon copy recipients')),
518 ('', 'bcc', [], _('email addresses of blind carbon copy recipients')),
520 ('c', 'cc', [], _('email addresses of copy recipients')),
519 ('c', 'cc', [], _('email addresses of copy recipients')),
521 ('', 'confirm', None, _('ask for confirmation before sending')),
520 ('', 'confirm', None, _('ask for confirmation before sending')),
522 ('d', 'diffstat', None, _('add diffstat output to messages')),
521 ('d', 'diffstat', None, _('add diffstat output to messages')),
523 ('', 'date', '', _('use the given date as the sending date')),
522 ('', 'date', '', _('use the given date as the sending date')),
524 ('', 'desc', '', _('use the given file as the series description')),
523 ('', 'desc', '', _('use the given file as the series description')),
525 ('f', 'from', '', _('email address of sender')),
524 ('f', 'from', '', _('email address of sender')),
526 ('n', 'test', None, _('print messages that would be sent')),
525 ('n', 'test', None, _('print messages that would be sent')),
527 ('m', 'mbox', '',
526 ('m', 'mbox', '',
528 _('write messages to mbox file instead of sending them')),
527 _('write messages to mbox file instead of sending them')),
529 ('', 'reply-to', [], _('email addresses replies should be sent to')),
528 ('', 'reply-to', [], _('email addresses replies should be sent to')),
530 ('s', 'subject', '',
529 ('s', 'subject', '',
531 _('subject of first message (intro or single patch)')),
530 _('subject of first message (intro or single patch)')),
532 ('', 'in-reply-to', '',
531 ('', 'in-reply-to', '',
533 _('message identifier to reply to')),
532 _('message identifier to reply to')),
534 ('', 'flag', [], _('flags to add in subject prefixes')),
533 ('', 'flag', [], _('flags to add in subject prefixes')),
535 ('t', 'to', [], _('email addresses of recipients')),
534 ('t', 'to', [], _('email addresses of recipients')),
536 ]
535 ]
537
536
538
537
539 cmdtable = {
538 cmdtable = {
540 "email":
539 "email":
541 (patchbomb,
540 (patchbomb,
542 [('g', 'git', None, _('use git extended diff format')),
541 [('g', 'git', None, _('use git extended diff format')),
543 ('', 'plain', None, _('omit hg patch header')),
542 ('', 'plain', None, _('omit hg patch header')),
544 ('o', 'outgoing', None,
543 ('o', 'outgoing', None,
545 _('send changes not found in the target repository')),
544 _('send changes not found in the target repository')),
546 ('b', 'bundle', None,
545 ('b', 'bundle', None,
547 _('send changes not in target as a binary bundle')),
546 _('send changes not in target as a binary bundle')),
548 ('', 'bundlename', 'bundle',
547 ('', 'bundlename', 'bundle',
549 _('name of the bundle attachment file'), _('NAME')),
548 _('name of the bundle attachment file'), _('NAME')),
550 ('r', 'rev', [],
549 ('r', 'rev', [],
551 _('a revision to send'), _('REV')),
550 _('a revision to send'), _('REV')),
552 ('', 'force', None,
551 ('', 'force', None,
553 _('run even when remote repository is unrelated '
552 _('run even when remote repository is unrelated '
554 '(with -b/--bundle)')),
553 '(with -b/--bundle)')),
555 ('', 'base', [],
554 ('', 'base', [],
556 _('a base changeset to specify instead of a destination '
555 _('a base changeset to specify instead of a destination '
557 '(with -b/--bundle)'),
556 '(with -b/--bundle)'),
558 _('REV')),
557 _('REV')),
559 ('', 'intro', None,
558 ('', 'intro', None,
560 _('send an introduction email for a single patch')),
559 _('send an introduction email for a single patch')),
561 ] + emailopts + commands.remoteopts,
560 ] + emailopts + commands.remoteopts,
562 _('hg email [OPTION]... [DEST]...'))
561 _('hg email [OPTION]... [DEST]...'))
563 }
562 }
@@ -1,647 +1,647
1 # Patch transplanting extension for Mercurial
1 # Patch transplanting extension for Mercurial
2 #
2 #
3 # Copyright 2006, 2007 Brendan Cully <brendan@kublai.com>
3 # Copyright 2006, 2007 Brendan Cully <brendan@kublai.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 '''command to transplant changesets from another branch
8 '''command to transplant changesets from another branch
9
9
10 This extension allows you to transplant patches from another branch.
10 This extension allows you to transplant patches from another branch.
11
11
12 Transplanted patches are recorded in .hg/transplant/transplants, as a
12 Transplanted patches are recorded in .hg/transplant/transplants, as a
13 map from a changeset hash to its hash in the source repository.
13 map from a changeset hash to its hash in the source repository.
14 '''
14 '''
15
15
16 from mercurial.i18n import _
16 from mercurial.i18n import _
17 import os, tempfile
17 import os, tempfile
18 from mercurial import bundlerepo, cmdutil, hg, merge, match
18 from mercurial import bundlerepo, cmdutil, hg, merge, match
19 from mercurial import patch, revlog, scmutil, util, error
19 from mercurial import patch, revlog, scmutil, util, error
20 from mercurial import revset, templatekw
20 from mercurial import revset, templatekw
21
21
22 class transplantentry(object):
22 class transplantentry(object):
23 def __init__(self, lnode, rnode):
23 def __init__(self, lnode, rnode):
24 self.lnode = lnode
24 self.lnode = lnode
25 self.rnode = rnode
25 self.rnode = rnode
26
26
27 class transplants(object):
27 class transplants(object):
28 def __init__(self, path=None, transplantfile=None, opener=None):
28 def __init__(self, path=None, transplantfile=None, opener=None):
29 self.path = path
29 self.path = path
30 self.transplantfile = transplantfile
30 self.transplantfile = transplantfile
31 self.opener = opener
31 self.opener = opener
32
32
33 if not opener:
33 if not opener:
34 self.opener = scmutil.opener(self.path)
34 self.opener = scmutil.opener(self.path)
35 self.transplants = {}
35 self.transplants = {}
36 self.dirty = False
36 self.dirty = False
37 self.read()
37 self.read()
38
38
39 def read(self):
39 def read(self):
40 abspath = os.path.join(self.path, self.transplantfile)
40 abspath = os.path.join(self.path, self.transplantfile)
41 if self.transplantfile and os.path.exists(abspath):
41 if self.transplantfile and os.path.exists(abspath):
42 for line in self.opener(self.transplantfile).read().splitlines():
42 for line in self.opener(self.transplantfile).read().splitlines():
43 lnode, rnode = map(revlog.bin, line.split(':'))
43 lnode, rnode = map(revlog.bin, line.split(':'))
44 list = self.transplants.setdefault(rnode, [])
44 list = self.transplants.setdefault(rnode, [])
45 list.append(transplantentry(lnode, rnode))
45 list.append(transplantentry(lnode, rnode))
46
46
47 def write(self):
47 def write(self):
48 if self.dirty and self.transplantfile:
48 if self.dirty and self.transplantfile:
49 if not os.path.isdir(self.path):
49 if not os.path.isdir(self.path):
50 os.mkdir(self.path)
50 os.mkdir(self.path)
51 fp = self.opener(self.transplantfile, 'w')
51 fp = self.opener(self.transplantfile, 'w')
52 for list in self.transplants.itervalues():
52 for list in self.transplants.itervalues():
53 for t in list:
53 for t in list:
54 l, r = map(revlog.hex, (t.lnode, t.rnode))
54 l, r = map(revlog.hex, (t.lnode, t.rnode))
55 fp.write(l + ':' + r + '\n')
55 fp.write(l + ':' + r + '\n')
56 fp.close()
56 fp.close()
57 self.dirty = False
57 self.dirty = False
58
58
59 def get(self, rnode):
59 def get(self, rnode):
60 return self.transplants.get(rnode) or []
60 return self.transplants.get(rnode) or []
61
61
62 def set(self, lnode, rnode):
62 def set(self, lnode, rnode):
63 list = self.transplants.setdefault(rnode, [])
63 list = self.transplants.setdefault(rnode, [])
64 list.append(transplantentry(lnode, rnode))
64 list.append(transplantentry(lnode, rnode))
65 self.dirty = True
65 self.dirty = True
66
66
67 def remove(self, transplant):
67 def remove(self, transplant):
68 list = self.transplants.get(transplant.rnode)
68 list = self.transplants.get(transplant.rnode)
69 if list:
69 if list:
70 del list[list.index(transplant)]
70 del list[list.index(transplant)]
71 self.dirty = True
71 self.dirty = True
72
72
73 class transplanter(object):
73 class transplanter(object):
74 def __init__(self, ui, repo):
74 def __init__(self, ui, repo):
75 self.ui = ui
75 self.ui = ui
76 self.path = repo.join('transplant')
76 self.path = repo.join('transplant')
77 self.opener = scmutil.opener(self.path)
77 self.opener = scmutil.opener(self.path)
78 self.transplants = transplants(self.path, 'transplants',
78 self.transplants = transplants(self.path, 'transplants',
79 opener=self.opener)
79 opener=self.opener)
80
80
81 def applied(self, repo, node, parent):
81 def applied(self, repo, node, parent):
82 '''returns True if a node is already an ancestor of parent
82 '''returns True if a node is already an ancestor of parent
83 or has already been transplanted'''
83 or has already been transplanted'''
84 if hasnode(repo, node):
84 if hasnode(repo, node):
85 if node in repo.changelog.reachable(parent, stop=node):
85 if node in repo.changelog.reachable(parent, stop=node):
86 return True
86 return True
87 for t in self.transplants.get(node):
87 for t in self.transplants.get(node):
88 # it might have been stripped
88 # it might have been stripped
89 if not hasnode(repo, t.lnode):
89 if not hasnode(repo, t.lnode):
90 self.transplants.remove(t)
90 self.transplants.remove(t)
91 return False
91 return False
92 if t.lnode in repo.changelog.reachable(parent, stop=t.lnode):
92 if t.lnode in repo.changelog.reachable(parent, stop=t.lnode):
93 return True
93 return True
94 return False
94 return False
95
95
96 def apply(self, repo, source, revmap, merges, opts={}):
96 def apply(self, repo, source, revmap, merges, opts={}):
97 '''apply the revisions in revmap one by one in revision order'''
97 '''apply the revisions in revmap one by one in revision order'''
98 revs = sorted(revmap)
98 revs = sorted(revmap)
99 p1, p2 = repo.dirstate.parents()
99 p1, p2 = repo.dirstate.parents()
100 pulls = []
100 pulls = []
101 diffopts = patch.diffopts(self.ui, opts)
101 diffopts = patch.diffopts(self.ui, opts)
102 diffopts.git = True
102 diffopts.git = True
103
103
104 lock = wlock = None
104 lock = wlock = None
105 try:
105 try:
106 wlock = repo.wlock()
106 wlock = repo.wlock()
107 lock = repo.lock()
107 lock = repo.lock()
108 for rev in revs:
108 for rev in revs:
109 node = revmap[rev]
109 node = revmap[rev]
110 revstr = '%s:%s' % (rev, revlog.short(node))
110 revstr = '%s:%s' % (rev, revlog.short(node))
111
111
112 if self.applied(repo, node, p1):
112 if self.applied(repo, node, p1):
113 self.ui.warn(_('skipping already applied revision %s\n') %
113 self.ui.warn(_('skipping already applied revision %s\n') %
114 revstr)
114 revstr)
115 continue
115 continue
116
116
117 parents = source.changelog.parents(node)
117 parents = source.changelog.parents(node)
118 if not opts.get('filter'):
118 if not opts.get('filter'):
119 # If the changeset parent is the same as the
119 # If the changeset parent is the same as the
120 # wdir's parent, just pull it.
120 # wdir's parent, just pull it.
121 if parents[0] == p1:
121 if parents[0] == p1:
122 pulls.append(node)
122 pulls.append(node)
123 p1 = node
123 p1 = node
124 continue
124 continue
125 if pulls:
125 if pulls:
126 if source != repo:
126 if source != repo:
127 repo.pull(source, heads=pulls)
127 repo.pull(source, heads=pulls)
128 merge.update(repo, pulls[-1], False, False, None)
128 merge.update(repo, pulls[-1], False, False, None)
129 p1, p2 = repo.dirstate.parents()
129 p1, p2 = repo.dirstate.parents()
130 pulls = []
130 pulls = []
131
131
132 domerge = False
132 domerge = False
133 if node in merges:
133 if node in merges:
134 # pulling all the merge revs at once would mean we
134 # pulling all the merge revs at once would mean we
135 # couldn't transplant after the latest even if
135 # couldn't transplant after the latest even if
136 # transplants before them fail.
136 # transplants before them fail.
137 domerge = True
137 domerge = True
138 if not hasnode(repo, node):
138 if not hasnode(repo, node):
139 repo.pull(source, heads=[node])
139 repo.pull(source, heads=[node])
140
140
141 if parents[1] != revlog.nullid:
141 if parents[1] != revlog.nullid:
142 self.ui.note(_('skipping merge changeset %s:%s\n')
142 self.ui.note(_('skipping merge changeset %s:%s\n')
143 % (rev, revlog.short(node)))
143 % (rev, revlog.short(node)))
144 patchfile = None
144 patchfile = None
145 else:
145 else:
146 fd, patchfile = tempfile.mkstemp(prefix='hg-transplant-')
146 fd, patchfile = tempfile.mkstemp(prefix='hg-transplant-')
147 fp = os.fdopen(fd, 'w')
147 fp = os.fdopen(fd, 'w')
148 gen = patch.diff(source, parents[0], node, opts=diffopts)
148 gen = patch.diff(source, parents[0], node, opts=diffopts)
149 for chunk in gen:
149 for chunk in gen:
150 fp.write(chunk)
150 fp.write(chunk)
151 fp.close()
151 fp.close()
152
152
153 del revmap[rev]
153 del revmap[rev]
154 if patchfile or domerge:
154 if patchfile or domerge:
155 try:
155 try:
156 n = self.applyone(repo, node,
156 n = self.applyone(repo, node,
157 source.changelog.read(node),
157 source.changelog.read(node),
158 patchfile, merge=domerge,
158 patchfile, merge=domerge,
159 log=opts.get('log'),
159 log=opts.get('log'),
160 filter=opts.get('filter'))
160 filter=opts.get('filter'))
161 if n and domerge:
161 if n and domerge:
162 self.ui.status(_('%s merged at %s\n') % (revstr,
162 self.ui.status(_('%s merged at %s\n') % (revstr,
163 revlog.short(n)))
163 revlog.short(n)))
164 elif n:
164 elif n:
165 self.ui.status(_('%s transplanted to %s\n')
165 self.ui.status(_('%s transplanted to %s\n')
166 % (revlog.short(node),
166 % (revlog.short(node),
167 revlog.short(n)))
167 revlog.short(n)))
168 finally:
168 finally:
169 if patchfile:
169 if patchfile:
170 os.unlink(patchfile)
170 os.unlink(patchfile)
171 if pulls:
171 if pulls:
172 repo.pull(source, heads=pulls)
172 repo.pull(source, heads=pulls)
173 merge.update(repo, pulls[-1], False, False, None)
173 merge.update(repo, pulls[-1], False, False, None)
174 finally:
174 finally:
175 self.saveseries(revmap, merges)
175 self.saveseries(revmap, merges)
176 self.transplants.write()
176 self.transplants.write()
177 lock.release()
177 lock.release()
178 wlock.release()
178 wlock.release()
179
179
180 def filter(self, filter, node, changelog, patchfile):
180 def filter(self, filter, node, changelog, patchfile):
181 '''arbitrarily rewrite changeset before applying it'''
181 '''arbitrarily rewrite changeset before applying it'''
182
182
183 self.ui.status(_('filtering %s\n') % patchfile)
183 self.ui.status(_('filtering %s\n') % patchfile)
184 user, date, msg = (changelog[1], changelog[2], changelog[4])
184 user, date, msg = (changelog[1], changelog[2], changelog[4])
185 fd, headerfile = tempfile.mkstemp(prefix='hg-transplant-')
185 fd, headerfile = tempfile.mkstemp(prefix='hg-transplant-')
186 fp = os.fdopen(fd, 'w')
186 fp = os.fdopen(fd, 'w')
187 fp.write("# HG changeset patch\n")
187 fp.write("# HG changeset patch\n")
188 fp.write("# User %s\n" % user)
188 fp.write("# User %s\n" % user)
189 fp.write("# Date %d %d\n" % date)
189 fp.write("# Date %d %d\n" % date)
190 fp.write(msg + '\n')
190 fp.write(msg + '\n')
191 fp.close()
191 fp.close()
192
192
193 try:
193 try:
194 util.system('%s %s %s' % (filter, util.shellquote(headerfile),
194 util.system('%s %s %s' % (filter, util.shellquote(headerfile),
195 util.shellquote(patchfile)),
195 util.shellquote(patchfile)),
196 environ={'HGUSER': changelog[1],
196 environ={'HGUSER': changelog[1],
197 'HGREVISION': revlog.hex(node),
197 'HGREVISION': revlog.hex(node),
198 },
198 },
199 onerr=util.Abort, errprefix=_('filter failed'))
199 onerr=util.Abort, errprefix=_('filter failed'))
200 user, date, msg = self.parselog(file(headerfile))[1:4]
200 user, date, msg = self.parselog(file(headerfile))[1:4]
201 finally:
201 finally:
202 os.unlink(headerfile)
202 os.unlink(headerfile)
203
203
204 return (user, date, msg)
204 return (user, date, msg)
205
205
206 def applyone(self, repo, node, cl, patchfile, merge=False, log=False,
206 def applyone(self, repo, node, cl, patchfile, merge=False, log=False,
207 filter=None):
207 filter=None):
208 '''apply the patch in patchfile to the repository as a transplant'''
208 '''apply the patch in patchfile to the repository as a transplant'''
209 (manifest, user, (time, timezone), files, message) = cl[:5]
209 (manifest, user, (time, timezone), files, message) = cl[:5]
210 date = "%d %d" % (time, timezone)
210 date = "%d %d" % (time, timezone)
211 extra = {'transplant_source': node}
211 extra = {'transplant_source': node}
212 if filter:
212 if filter:
213 (user, date, message) = self.filter(filter, node, cl, patchfile)
213 (user, date, message) = self.filter(filter, node, cl, patchfile)
214
214
215 if log:
215 if log:
216 # we don't translate messages inserted into commits
216 # we don't translate messages inserted into commits
217 message += '\n(transplanted from %s)' % revlog.hex(node)
217 message += '\n(transplanted from %s)' % revlog.hex(node)
218
218
219 self.ui.status(_('applying %s\n') % revlog.short(node))
219 self.ui.status(_('applying %s\n') % revlog.short(node))
220 self.ui.note('%s %s\n%s\n' % (user, date, message))
220 self.ui.note('%s %s\n%s\n' % (user, date, message))
221
221
222 if not patchfile and not merge:
222 if not patchfile and not merge:
223 raise util.Abort(_('can only omit patchfile if merging'))
223 raise util.Abort(_('can only omit patchfile if merging'))
224 if patchfile:
224 if patchfile:
225 try:
225 try:
226 files = {}
226 files = {}
227 try:
227 try:
228 patch.patch(patchfile, self.ui, cwd=repo.root,
228 patch.patch(patchfile, self.ui, cwd=repo.root,
229 files=files, eolmode=None)
229 files=files, eolmode=None)
230 if not files:
230 if not files:
231 self.ui.warn(_('%s: empty changeset')
231 self.ui.warn(_('%s: empty changeset')
232 % revlog.hex(node))
232 % revlog.hex(node))
233 return None
233 return None
234 finally:
234 finally:
235 files = cmdutil.updatedir(self.ui, repo, files)
235 files = cmdutil.updatedir(self.ui, repo, files)
236 except Exception, inst:
236 except Exception, inst:
237 seriespath = os.path.join(self.path, 'series')
237 seriespath = os.path.join(self.path, 'series')
238 if os.path.exists(seriespath):
238 if os.path.exists(seriespath):
239 os.unlink(seriespath)
239 os.unlink(seriespath)
240 p1 = repo.dirstate.p1()
240 p1 = repo.dirstate.p1()
241 p2 = node
241 p2 = node
242 self.log(user, date, message, p1, p2, merge=merge)
242 self.log(user, date, message, p1, p2, merge=merge)
243 self.ui.write(str(inst) + '\n')
243 self.ui.write(str(inst) + '\n')
244 raise util.Abort(_('fix up the merge and run '
244 raise util.Abort(_('fix up the merge and run '
245 'hg transplant --continue'))
245 'hg transplant --continue'))
246 else:
246 else:
247 files = None
247 files = None
248 if merge:
248 if merge:
249 p1, p2 = repo.dirstate.parents()
249 p1, p2 = repo.dirstate.parents()
250 repo.dirstate.setparents(p1, node)
250 repo.dirstate.setparents(p1, node)
251 m = match.always(repo.root, '')
251 m = match.always(repo.root, '')
252 else:
252 else:
253 m = match.exact(repo.root, '', files)
253 m = match.exact(repo.root, '', files)
254
254
255 n = repo.commit(message, user, date, extra=extra, match=m)
255 n = repo.commit(message, user, date, extra=extra, match=m)
256 if not n:
256 if not n:
257 # Crash here to prevent an unclear crash later, in
257 # Crash here to prevent an unclear crash later, in
258 # transplants.write(). This can happen if patch.patch()
258 # transplants.write(). This can happen if patch.patch()
259 # does nothing but claims success or if repo.status() fails
259 # does nothing but claims success or if repo.status() fails
260 # to report changes done by patch.patch(). These both
260 # to report changes done by patch.patch(). These both
261 # appear to be bugs in other parts of Mercurial, but dying
261 # appear to be bugs in other parts of Mercurial, but dying
262 # here, as soon as we can detect the problem, is preferable
262 # here, as soon as we can detect the problem, is preferable
263 # to silently dropping changesets on the floor.
263 # to silently dropping changesets on the floor.
264 raise RuntimeError('nothing committed after transplant')
264 raise RuntimeError('nothing committed after transplant')
265 if not merge:
265 if not merge:
266 self.transplants.set(n, node)
266 self.transplants.set(n, node)
267
267
268 return n
268 return n
269
269
270 def resume(self, repo, source, opts=None):
270 def resume(self, repo, source, opts=None):
271 '''recover last transaction and apply remaining changesets'''
271 '''recover last transaction and apply remaining changesets'''
272 if os.path.exists(os.path.join(self.path, 'journal')):
272 if os.path.exists(os.path.join(self.path, 'journal')):
273 n, node = self.recover(repo)
273 n, node = self.recover(repo)
274 self.ui.status(_('%s transplanted as %s\n') % (revlog.short(node),
274 self.ui.status(_('%s transplanted as %s\n') % (revlog.short(node),
275 revlog.short(n)))
275 revlog.short(n)))
276 seriespath = os.path.join(self.path, 'series')
276 seriespath = os.path.join(self.path, 'series')
277 if not os.path.exists(seriespath):
277 if not os.path.exists(seriespath):
278 self.transplants.write()
278 self.transplants.write()
279 return
279 return
280 nodes, merges = self.readseries()
280 nodes, merges = self.readseries()
281 revmap = {}
281 revmap = {}
282 for n in nodes:
282 for n in nodes:
283 revmap[source.changelog.rev(n)] = n
283 revmap[source.changelog.rev(n)] = n
284 os.unlink(seriespath)
284 os.unlink(seriespath)
285
285
286 self.apply(repo, source, revmap, merges, opts)
286 self.apply(repo, source, revmap, merges, opts)
287
287
288 def recover(self, repo):
288 def recover(self, repo):
289 '''commit working directory using journal metadata'''
289 '''commit working directory using journal metadata'''
290 node, user, date, message, parents = self.readlog()
290 node, user, date, message, parents = self.readlog()
291 merge = len(parents) == 2
291 merge = len(parents) == 2
292
292
293 if not user or not date or not message or not parents[0]:
293 if not user or not date or not message or not parents[0]:
294 raise util.Abort(_('transplant log file is corrupt'))
294 raise util.Abort(_('transplant log file is corrupt'))
295
295
296 extra = {'transplant_source': node}
296 extra = {'transplant_source': node}
297 wlock = repo.wlock()
297 wlock = repo.wlock()
298 try:
298 try:
299 p1, p2 = repo.dirstate.parents()
299 p1, p2 = repo.dirstate.parents()
300 if p1 != parents[0]:
300 if p1 != parents[0]:
301 raise util.Abort(
301 raise util.Abort(
302 _('working dir not at transplant parent %s') %
302 _('working dir not at transplant parent %s') %
303 revlog.hex(parents[0]))
303 revlog.hex(parents[0]))
304 if merge:
304 if merge:
305 repo.dirstate.setparents(p1, parents[1])
305 repo.dirstate.setparents(p1, parents[1])
306 n = repo.commit(message, user, date, extra=extra)
306 n = repo.commit(message, user, date, extra=extra)
307 if not n:
307 if not n:
308 raise util.Abort(_('commit failed'))
308 raise util.Abort(_('commit failed'))
309 if not merge:
309 if not merge:
310 self.transplants.set(n, node)
310 self.transplants.set(n, node)
311 self.unlog()
311 self.unlog()
312
312
313 return n, node
313 return n, node
314 finally:
314 finally:
315 wlock.release()
315 wlock.release()
316
316
317 def readseries(self):
317 def readseries(self):
318 nodes = []
318 nodes = []
319 merges = []
319 merges = []
320 cur = nodes
320 cur = nodes
321 for line in self.opener('series').read().splitlines():
321 for line in self.opener('series').read().splitlines():
322 if line.startswith('# Merges'):
322 if line.startswith('# Merges'):
323 cur = merges
323 cur = merges
324 continue
324 continue
325 cur.append(revlog.bin(line))
325 cur.append(revlog.bin(line))
326
326
327 return (nodes, merges)
327 return (nodes, merges)
328
328
329 def saveseries(self, revmap, merges):
329 def saveseries(self, revmap, merges):
330 if not revmap:
330 if not revmap:
331 return
331 return
332
332
333 if not os.path.isdir(self.path):
333 if not os.path.isdir(self.path):
334 os.mkdir(self.path)
334 os.mkdir(self.path)
335 series = self.opener('series', 'w')
335 series = self.opener('series', 'w')
336 for rev in sorted(revmap):
336 for rev in sorted(revmap):
337 series.write(revlog.hex(revmap[rev]) + '\n')
337 series.write(revlog.hex(revmap[rev]) + '\n')
338 if merges:
338 if merges:
339 series.write('# Merges\n')
339 series.write('# Merges\n')
340 for m in merges:
340 for m in merges:
341 series.write(revlog.hex(m) + '\n')
341 series.write(revlog.hex(m) + '\n')
342 series.close()
342 series.close()
343
343
344 def parselog(self, fp):
344 def parselog(self, fp):
345 parents = []
345 parents = []
346 message = []
346 message = []
347 node = revlog.nullid
347 node = revlog.nullid
348 inmsg = False
348 inmsg = False
349 user = None
349 user = None
350 date = None
350 date = None
351 for line in fp.read().splitlines():
351 for line in fp.read().splitlines():
352 if inmsg:
352 if inmsg:
353 message.append(line)
353 message.append(line)
354 elif line.startswith('# User '):
354 elif line.startswith('# User '):
355 user = line[7:]
355 user = line[7:]
356 elif line.startswith('# Date '):
356 elif line.startswith('# Date '):
357 date = line[7:]
357 date = line[7:]
358 elif line.startswith('# Node ID '):
358 elif line.startswith('# Node ID '):
359 node = revlog.bin(line[10:])
359 node = revlog.bin(line[10:])
360 elif line.startswith('# Parent '):
360 elif line.startswith('# Parent '):
361 parents.append(revlog.bin(line[9:]))
361 parents.append(revlog.bin(line[9:]))
362 elif not line.startswith('# '):
362 elif not line.startswith('# '):
363 inmsg = True
363 inmsg = True
364 message.append(line)
364 message.append(line)
365 if None in (user, date):
365 if None in (user, date):
366 raise util.Abort(_("filter corrupted changeset (no user or date)"))
366 raise util.Abort(_("filter corrupted changeset (no user or date)"))
367 return (node, user, date, '\n'.join(message), parents)
367 return (node, user, date, '\n'.join(message), parents)
368
368
369 def log(self, user, date, message, p1, p2, merge=False):
369 def log(self, user, date, message, p1, p2, merge=False):
370 '''journal changelog metadata for later recover'''
370 '''journal changelog metadata for later recover'''
371
371
372 if not os.path.isdir(self.path):
372 if not os.path.isdir(self.path):
373 os.mkdir(self.path)
373 os.mkdir(self.path)
374 fp = self.opener('journal', 'w')
374 fp = self.opener('journal', 'w')
375 fp.write('# User %s\n' % user)
375 fp.write('# User %s\n' % user)
376 fp.write('# Date %s\n' % date)
376 fp.write('# Date %s\n' % date)
377 fp.write('# Node ID %s\n' % revlog.hex(p2))
377 fp.write('# Node ID %s\n' % revlog.hex(p2))
378 fp.write('# Parent ' + revlog.hex(p1) + '\n')
378 fp.write('# Parent ' + revlog.hex(p1) + '\n')
379 if merge:
379 if merge:
380 fp.write('# Parent ' + revlog.hex(p2) + '\n')
380 fp.write('# Parent ' + revlog.hex(p2) + '\n')
381 fp.write(message.rstrip() + '\n')
381 fp.write(message.rstrip() + '\n')
382 fp.close()
382 fp.close()
383
383
384 def readlog(self):
384 def readlog(self):
385 return self.parselog(self.opener('journal'))
385 return self.parselog(self.opener('journal'))
386
386
387 def unlog(self):
387 def unlog(self):
388 '''remove changelog journal'''
388 '''remove changelog journal'''
389 absdst = os.path.join(self.path, 'journal')
389 absdst = os.path.join(self.path, 'journal')
390 if os.path.exists(absdst):
390 if os.path.exists(absdst):
391 os.unlink(absdst)
391 os.unlink(absdst)
392
392
393 def transplantfilter(self, repo, source, root):
393 def transplantfilter(self, repo, source, root):
394 def matchfn(node):
394 def matchfn(node):
395 if self.applied(repo, node, root):
395 if self.applied(repo, node, root):
396 return False
396 return False
397 if source.changelog.parents(node)[1] != revlog.nullid:
397 if source.changelog.parents(node)[1] != revlog.nullid:
398 return False
398 return False
399 extra = source.changelog.read(node)[5]
399 extra = source.changelog.read(node)[5]
400 cnode = extra.get('transplant_source')
400 cnode = extra.get('transplant_source')
401 if cnode and self.applied(repo, cnode, root):
401 if cnode and self.applied(repo, cnode, root):
402 return False
402 return False
403 return True
403 return True
404
404
405 return matchfn
405 return matchfn
406
406
407 def hasnode(repo, node):
407 def hasnode(repo, node):
408 try:
408 try:
409 return repo.changelog.rev(node) is not None
409 return repo.changelog.rev(node) is not None
410 except error.RevlogError:
410 except error.RevlogError:
411 return False
411 return False
412
412
413 def browserevs(ui, repo, nodes, opts):
413 def browserevs(ui, repo, nodes, opts):
414 '''interactively transplant changesets'''
414 '''interactively transplant changesets'''
415 def browsehelp(ui):
415 def browsehelp(ui):
416 ui.write(_('y: transplant this changeset\n'
416 ui.write(_('y: transplant this changeset\n'
417 'n: skip this changeset\n'
417 'n: skip this changeset\n'
418 'm: merge at this changeset\n'
418 'm: merge at this changeset\n'
419 'p: show patch\n'
419 'p: show patch\n'
420 'c: commit selected changesets\n'
420 'c: commit selected changesets\n'
421 'q: cancel transplant\n'
421 'q: cancel transplant\n'
422 '?: show this help\n'))
422 '?: show this help\n'))
423
423
424 displayer = cmdutil.show_changeset(ui, repo, opts)
424 displayer = cmdutil.show_changeset(ui, repo, opts)
425 transplants = []
425 transplants = []
426 merges = []
426 merges = []
427 for node in nodes:
427 for node in nodes:
428 displayer.show(repo[node])
428 displayer.show(repo[node])
429 action = None
429 action = None
430 while not action:
430 while not action:
431 action = ui.prompt(_('apply changeset? [ynmpcq?]:'))
431 action = ui.prompt(_('apply changeset? [ynmpcq?]:'))
432 if action == '?':
432 if action == '?':
433 browsehelp(ui)
433 browsehelp(ui)
434 action = None
434 action = None
435 elif action == 'p':
435 elif action == 'p':
436 parent = repo.changelog.parents(node)[0]
436 parent = repo.changelog.parents(node)[0]
437 for chunk in patch.diff(repo, parent, node):
437 for chunk in patch.diff(repo, parent, node):
438 ui.write(chunk)
438 ui.write(chunk)
439 action = None
439 action = None
440 elif action not in ('y', 'n', 'm', 'c', 'q'):
440 elif action not in ('y', 'n', 'm', 'c', 'q'):
441 ui.write(_('no such option\n'))
441 ui.write(_('no such option\n'))
442 action = None
442 action = None
443 if action == 'y':
443 if action == 'y':
444 transplants.append(node)
444 transplants.append(node)
445 elif action == 'm':
445 elif action == 'm':
446 merges.append(node)
446 merges.append(node)
447 elif action == 'c':
447 elif action == 'c':
448 break
448 break
449 elif action == 'q':
449 elif action == 'q':
450 transplants = ()
450 transplants = ()
451 merges = ()
451 merges = ()
452 break
452 break
453 displayer.close()
453 displayer.close()
454 return (transplants, merges)
454 return (transplants, merges)
455
455
456 def transplant(ui, repo, *revs, **opts):
456 def transplant(ui, repo, *revs, **opts):
457 '''transplant changesets from another branch
457 '''transplant changesets from another branch
458
458
459 Selected changesets will be applied on top of the current working
459 Selected changesets will be applied on top of the current working
460 directory with the log of the original changeset. The changesets
460 directory with the log of the original changeset. The changesets
461 are copied and will thus appear twice in the history. Use the
461 are copied and will thus appear twice in the history. Use the
462 rebase extension instead if you want to move a whole branch of
462 rebase extension instead if you want to move a whole branch of
463 unpublished changesets.
463 unpublished changesets.
464
464
465 If --log is specified, log messages will have a comment appended
465 If --log is specified, log messages will have a comment appended
466 of the form::
466 of the form::
467
467
468 (transplanted from CHANGESETHASH)
468 (transplanted from CHANGESETHASH)
469
469
470 You can rewrite the changelog message with the --filter option.
470 You can rewrite the changelog message with the --filter option.
471 Its argument will be invoked with the current changelog message as
471 Its argument will be invoked with the current changelog message as
472 $1 and the patch as $2.
472 $1 and the patch as $2.
473
473
474 If --source/-s is specified, selects changesets from the named
474 If --source/-s is specified, selects changesets from the named
475 repository. If --branch/-b is specified, selects changesets from
475 repository. If --branch/-b is specified, selects changesets from
476 the branch holding the named revision, up to that revision. If
476 the branch holding the named revision, up to that revision. If
477 --all/-a is specified, all changesets on the branch will be
477 --all/-a is specified, all changesets on the branch will be
478 transplanted, otherwise you will be prompted to select the
478 transplanted, otherwise you will be prompted to select the
479 changesets you want.
479 changesets you want.
480
480
481 :hg:`transplant --branch REVISION --all` will transplant the
481 :hg:`transplant --branch REVISION --all` will transplant the
482 selected branch (up to the named revision) onto your current
482 selected branch (up to the named revision) onto your current
483 working directory.
483 working directory.
484
484
485 You can optionally mark selected transplanted changesets as merge
485 You can optionally mark selected transplanted changesets as merge
486 changesets. You will not be prompted to transplant any ancestors
486 changesets. You will not be prompted to transplant any ancestors
487 of a merged transplant, and you can merge descendants of them
487 of a merged transplant, and you can merge descendants of them
488 normally instead of transplanting them.
488 normally instead of transplanting them.
489
489
490 If no merges or revisions are provided, :hg:`transplant` will
490 If no merges or revisions are provided, :hg:`transplant` will
491 start an interactive changeset browser.
491 start an interactive changeset browser.
492
492
493 If a changeset application fails, you can fix the merge by hand
493 If a changeset application fails, you can fix the merge by hand
494 and then resume where you left off by calling :hg:`transplant
494 and then resume where you left off by calling :hg:`transplant
495 --continue/-c`.
495 --continue/-c`.
496 '''
496 '''
497 def incwalk(repo, incoming, branches, match=util.always):
497 def incwalk(repo, commmon, branches, match=util.always):
498 if not branches:
498 if not branches:
499 branches = None
499 branches = None
500 for node in repo.changelog.nodesbetween(incoming, branches)[0]:
500 for node in repo.changelog.findmissing(common, branches):
501 if match(node):
501 if match(node):
502 yield node
502 yield node
503
503
504 def transplantwalk(repo, root, branches, match=util.always):
504 def transplantwalk(repo, root, branches, match=util.always):
505 if not branches:
505 if not branches:
506 branches = repo.heads()
506 branches = repo.heads()
507 ancestors = []
507 ancestors = []
508 for branch in branches:
508 for branch in branches:
509 ancestors.append(repo.changelog.ancestor(root, branch))
509 ancestors.append(repo.changelog.ancestor(root, branch))
510 for node in repo.changelog.nodesbetween(ancestors, branches)[0]:
510 for node in repo.changelog.nodesbetween(ancestors, branches)[0]:
511 if match(node):
511 if match(node):
512 yield node
512 yield node
513
513
514 def checkopts(opts, revs):
514 def checkopts(opts, revs):
515 if opts.get('continue'):
515 if opts.get('continue'):
516 if opts.get('branch') or opts.get('all') or opts.get('merge'):
516 if opts.get('branch') or opts.get('all') or opts.get('merge'):
517 raise util.Abort(_('--continue is incompatible with '
517 raise util.Abort(_('--continue is incompatible with '
518 'branch, all or merge'))
518 'branch, all or merge'))
519 return
519 return
520 if not (opts.get('source') or revs or
520 if not (opts.get('source') or revs or
521 opts.get('merge') or opts.get('branch')):
521 opts.get('merge') or opts.get('branch')):
522 raise util.Abort(_('no source URL, branch tag or revision '
522 raise util.Abort(_('no source URL, branch tag or revision '
523 'list provided'))
523 'list provided'))
524 if opts.get('all'):
524 if opts.get('all'):
525 if not opts.get('branch'):
525 if not opts.get('branch'):
526 raise util.Abort(_('--all requires a branch revision'))
526 raise util.Abort(_('--all requires a branch revision'))
527 if revs:
527 if revs:
528 raise util.Abort(_('--all is incompatible with a '
528 raise util.Abort(_('--all is incompatible with a '
529 'revision list'))
529 'revision list'))
530
530
531 checkopts(opts, revs)
531 checkopts(opts, revs)
532
532
533 if not opts.get('log'):
533 if not opts.get('log'):
534 opts['log'] = ui.config('transplant', 'log')
534 opts['log'] = ui.config('transplant', 'log')
535 if not opts.get('filter'):
535 if not opts.get('filter'):
536 opts['filter'] = ui.config('transplant', 'filter')
536 opts['filter'] = ui.config('transplant', 'filter')
537
537
538 tp = transplanter(ui, repo)
538 tp = transplanter(ui, repo)
539
539
540 p1, p2 = repo.dirstate.parents()
540 p1, p2 = repo.dirstate.parents()
541 if len(repo) > 0 and p1 == revlog.nullid:
541 if len(repo) > 0 and p1 == revlog.nullid:
542 raise util.Abort(_('no revision checked out'))
542 raise util.Abort(_('no revision checked out'))
543 if not opts.get('continue'):
543 if not opts.get('continue'):
544 if p2 != revlog.nullid:
544 if p2 != revlog.nullid:
545 raise util.Abort(_('outstanding uncommitted merges'))
545 raise util.Abort(_('outstanding uncommitted merges'))
546 m, a, r, d = repo.status()[:4]
546 m, a, r, d = repo.status()[:4]
547 if m or a or r or d:
547 if m or a or r or d:
548 raise util.Abort(_('outstanding local changes'))
548 raise util.Abort(_('outstanding local changes'))
549
549
550 bundle = None
550 bundle = None
551 source = opts.get('source')
551 source = opts.get('source')
552 if source:
552 if source:
553 sourcerepo = ui.expandpath(source)
553 sourcerepo = ui.expandpath(source)
554 source = hg.repository(ui, sourcerepo)
554 source = hg.repository(ui, sourcerepo)
555 source, common, incoming, bundle = bundlerepo.getremotechanges(ui, repo,
555 source, common, anyinc, bundle = bundlerepo.getremotechanges(ui, repo,
556 source, force=True)
556 source, force=True)
557 else:
557 else:
558 source = repo
558 source = repo
559
559
560 try:
560 try:
561 if opts.get('continue'):
561 if opts.get('continue'):
562 tp.resume(repo, source, opts)
562 tp.resume(repo, source, opts)
563 return
563 return
564
564
565 tf = tp.transplantfilter(repo, source, p1)
565 tf = tp.transplantfilter(repo, source, p1)
566 if opts.get('prune'):
566 if opts.get('prune'):
567 prune = [source.lookup(r)
567 prune = [source.lookup(r)
568 for r in cmdutil.revrange(source, opts.get('prune'))]
568 for r in cmdutil.revrange(source, opts.get('prune'))]
569 matchfn = lambda x: tf(x) and x not in prune
569 matchfn = lambda x: tf(x) and x not in prune
570 else:
570 else:
571 matchfn = tf
571 matchfn = tf
572 branches = map(source.lookup, opts.get('branch', ()))
572 branches = map(source.lookup, opts.get('branch', ()))
573 merges = map(source.lookup, opts.get('merge', ()))
573 merges = map(source.lookup, opts.get('merge', ()))
574 revmap = {}
574 revmap = {}
575 if revs:
575 if revs:
576 for r in cmdutil.revrange(source, revs):
576 for r in cmdutil.revrange(source, revs):
577 revmap[int(r)] = source.lookup(r)
577 revmap[int(r)] = source.lookup(r)
578 elif opts.get('all') or not merges:
578 elif opts.get('all') or not merges:
579 if source != repo:
579 if source != repo:
580 alltransplants = incwalk(source, incoming, branches,
580 alltransplants = incwalk(source, common, branches,
581 match=matchfn)
581 match=matchfn)
582 else:
582 else:
583 alltransplants = transplantwalk(source, p1, branches,
583 alltransplants = transplantwalk(source, p1, branches,
584 match=matchfn)
584 match=matchfn)
585 if opts.get('all'):
585 if opts.get('all'):
586 revs = alltransplants
586 revs = alltransplants
587 else:
587 else:
588 revs, newmerges = browserevs(ui, source, alltransplants, opts)
588 revs, newmerges = browserevs(ui, source, alltransplants, opts)
589 merges.extend(newmerges)
589 merges.extend(newmerges)
590 for r in revs:
590 for r in revs:
591 revmap[source.changelog.rev(r)] = r
591 revmap[source.changelog.rev(r)] = r
592 for r in merges:
592 for r in merges:
593 revmap[source.changelog.rev(r)] = r
593 revmap[source.changelog.rev(r)] = r
594
594
595 tp.apply(repo, source, revmap, merges, opts)
595 tp.apply(repo, source, revmap, merges, opts)
596 finally:
596 finally:
597 if bundle:
597 if bundle:
598 source.close()
598 source.close()
599 os.unlink(bundle)
599 os.unlink(bundle)
600
600
601 def revsettransplanted(repo, subset, x):
601 def revsettransplanted(repo, subset, x):
602 """``transplanted(set)``
602 """``transplanted(set)``
603 Transplanted changesets in set.
603 Transplanted changesets in set.
604 """
604 """
605 if x:
605 if x:
606 s = revset.getset(repo, subset, x)
606 s = revset.getset(repo, subset, x)
607 else:
607 else:
608 s = subset
608 s = subset
609 cs = set()
609 cs = set()
610 for r in xrange(0, len(repo)):
610 for r in xrange(0, len(repo)):
611 if repo[r].extra().get('transplant_source'):
611 if repo[r].extra().get('transplant_source'):
612 cs.add(r)
612 cs.add(r)
613 return [r for r in s if r in cs]
613 return [r for r in s if r in cs]
614
614
615 def kwtransplanted(repo, ctx, **args):
615 def kwtransplanted(repo, ctx, **args):
616 """:transplanted: String. The node identifier of the transplanted
616 """:transplanted: String. The node identifier of the transplanted
617 changeset if any."""
617 changeset if any."""
618 n = ctx.extra().get('transplant_source')
618 n = ctx.extra().get('transplant_source')
619 return n and revlog.hex(n) or ''
619 return n and revlog.hex(n) or ''
620
620
621 def extsetup(ui):
621 def extsetup(ui):
622 revset.symbols['transplanted'] = revsettransplanted
622 revset.symbols['transplanted'] = revsettransplanted
623 templatekw.keywords['transplanted'] = kwtransplanted
623 templatekw.keywords['transplanted'] = kwtransplanted
624
624
625 cmdtable = {
625 cmdtable = {
626 "transplant":
626 "transplant":
627 (transplant,
627 (transplant,
628 [('s', 'source', '',
628 [('s', 'source', '',
629 _('pull patches from REPO'), _('REPO')),
629 _('pull patches from REPO'), _('REPO')),
630 ('b', 'branch', [],
630 ('b', 'branch', [],
631 _('pull patches from branch BRANCH'), _('BRANCH')),
631 _('pull patches from branch BRANCH'), _('BRANCH')),
632 ('a', 'all', None, _('pull all changesets up to BRANCH')),
632 ('a', 'all', None, _('pull all changesets up to BRANCH')),
633 ('p', 'prune', [],
633 ('p', 'prune', [],
634 _('skip over REV'), _('REV')),
634 _('skip over REV'), _('REV')),
635 ('m', 'merge', [],
635 ('m', 'merge', [],
636 _('merge at REV'), _('REV')),
636 _('merge at REV'), _('REV')),
637 ('', 'log', None, _('append transplant info to log message')),
637 ('', 'log', None, _('append transplant info to log message')),
638 ('c', 'continue', None, _('continue last transplant session '
638 ('c', 'continue', None, _('continue last transplant session '
639 'after repair')),
639 'after repair')),
640 ('', 'filter', '',
640 ('', 'filter', '',
641 _('filter changesets through command'), _('CMD'))],
641 _('filter changesets through command'), _('CMD'))],
642 _('hg transplant [-s REPO] [-b BRANCH [-a]] [-p REV] '
642 _('hg transplant [-s REPO] [-b BRANCH [-a]] [-p REV] '
643 '[-m REV] [REV]...'))
643 '[-m REV] [REV]...'))
644 }
644 }
645
645
646 # tell hggettext to extract docstrings from these functions:
646 # tell hggettext to extract docstrings from these functions:
647 i18nfunctions = [revsettransplanted, kwtransplanted]
647 i18nfunctions = [revsettransplanted, kwtransplanted]
@@ -1,323 +1,322
1 # bundlerepo.py - repository class for viewing uncompressed bundles
1 # bundlerepo.py - repository class for viewing uncompressed bundles
2 #
2 #
3 # Copyright 2006, 2007 Benoit Boissinot <bboissin@gmail.com>
3 # Copyright 2006, 2007 Benoit Boissinot <bboissin@gmail.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 """Repository class for viewing uncompressed bundles.
8 """Repository class for viewing uncompressed bundles.
9
9
10 This provides a read-only repository interface to bundles as if they
10 This provides a read-only repository interface to bundles as if they
11 were part of the actual repository.
11 were part of the actual repository.
12 """
12 """
13
13
14 from node import nullid
14 from node import nullid
15 from i18n import _
15 from i18n import _
16 import os, struct, tempfile, shutil
16 import os, struct, tempfile, shutil
17 import changegroup, util, mdiff, discovery
17 import changegroup, util, mdiff, discovery
18 import localrepo, changelog, manifest, filelog, revlog, error, url
18 import localrepo, changelog, manifest, filelog, revlog, error, url
19
19
20 class bundlerevlog(revlog.revlog):
20 class bundlerevlog(revlog.revlog):
21 def __init__(self, opener, indexfile, bundle,
21 def __init__(self, opener, indexfile, bundle,
22 linkmapper=None):
22 linkmapper=None):
23 # How it works:
23 # How it works:
24 # to retrieve a revision, we need to know the offset of
24 # to retrieve a revision, we need to know the offset of
25 # the revision in the bundle (an unbundle object).
25 # the revision in the bundle (an unbundle object).
26 #
26 #
27 # We store this offset in the index (start), to differentiate a
27 # We store this offset in the index (start), to differentiate a
28 # rev in the bundle and from a rev in the revlog, we check
28 # rev in the bundle and from a rev in the revlog, we check
29 # len(index[r]). If the tuple is bigger than 7, it is a bundle
29 # len(index[r]). If the tuple is bigger than 7, it is a bundle
30 # (it is bigger since we store the node to which the delta is)
30 # (it is bigger since we store the node to which the delta is)
31 #
31 #
32 revlog.revlog.__init__(self, opener, indexfile)
32 revlog.revlog.__init__(self, opener, indexfile)
33 self.bundle = bundle
33 self.bundle = bundle
34 self.basemap = {}
34 self.basemap = {}
35 def chunkpositer():
35 def chunkpositer():
36 while 1:
36 while 1:
37 chunk = bundle.chunk()
37 chunk = bundle.chunk()
38 if not chunk:
38 if not chunk:
39 break
39 break
40 pos = bundle.tell()
40 pos = bundle.tell()
41 yield chunk, pos - len(chunk)
41 yield chunk, pos - len(chunk)
42 n = len(self)
42 n = len(self)
43 prev = None
43 prev = None
44 for chunk, start in chunkpositer():
44 for chunk, start in chunkpositer():
45 size = len(chunk)
45 size = len(chunk)
46 if size < 80:
46 if size < 80:
47 raise util.Abort(_("invalid changegroup"))
47 raise util.Abort(_("invalid changegroup"))
48 start += 80
48 start += 80
49 size -= 80
49 size -= 80
50 node, p1, p2, cs = struct.unpack("20s20s20s20s", chunk[:80])
50 node, p1, p2, cs = struct.unpack("20s20s20s20s", chunk[:80])
51 if node in self.nodemap:
51 if node in self.nodemap:
52 prev = node
52 prev = node
53 continue
53 continue
54 for p in (p1, p2):
54 for p in (p1, p2):
55 if not p in self.nodemap:
55 if not p in self.nodemap:
56 raise error.LookupError(p, self.indexfile,
56 raise error.LookupError(p, self.indexfile,
57 _("unknown parent"))
57 _("unknown parent"))
58 if linkmapper is None:
58 if linkmapper is None:
59 link = n
59 link = n
60 else:
60 else:
61 link = linkmapper(cs)
61 link = linkmapper(cs)
62
62
63 if not prev:
63 if not prev:
64 prev = p1
64 prev = p1
65 # start, size, full unc. size, base (unused), link, p1, p2, node
65 # start, size, full unc. size, base (unused), link, p1, p2, node
66 e = (revlog.offset_type(start, 0), size, -1, -1, link,
66 e = (revlog.offset_type(start, 0), size, -1, -1, link,
67 self.rev(p1), self.rev(p2), node)
67 self.rev(p1), self.rev(p2), node)
68 self.basemap[n] = prev
68 self.basemap[n] = prev
69 self.index.insert(-1, e)
69 self.index.insert(-1, e)
70 self.nodemap[node] = n
70 self.nodemap[node] = n
71 prev = node
71 prev = node
72 n += 1
72 n += 1
73
73
74 def inbundle(self, rev):
74 def inbundle(self, rev):
75 """is rev from the bundle"""
75 """is rev from the bundle"""
76 if rev < 0:
76 if rev < 0:
77 return False
77 return False
78 return rev in self.basemap
78 return rev in self.basemap
79 def bundlebase(self, rev):
79 def bundlebase(self, rev):
80 return self.basemap[rev]
80 return self.basemap[rev]
81 def _chunk(self, rev):
81 def _chunk(self, rev):
82 # Warning: in case of bundle, the diff is against bundlebase,
82 # Warning: in case of bundle, the diff is against bundlebase,
83 # not against rev - 1
83 # not against rev - 1
84 # XXX: could use some caching
84 # XXX: could use some caching
85 if not self.inbundle(rev):
85 if not self.inbundle(rev):
86 return revlog.revlog._chunk(self, rev)
86 return revlog.revlog._chunk(self, rev)
87 self.bundle.seek(self.start(rev))
87 self.bundle.seek(self.start(rev))
88 return self.bundle.read(self.length(rev))
88 return self.bundle.read(self.length(rev))
89
89
90 def revdiff(self, rev1, rev2):
90 def revdiff(self, rev1, rev2):
91 """return or calculate a delta between two revisions"""
91 """return or calculate a delta between two revisions"""
92 if self.inbundle(rev1) and self.inbundle(rev2):
92 if self.inbundle(rev1) and self.inbundle(rev2):
93 # hot path for bundle
93 # hot path for bundle
94 revb = self.rev(self.bundlebase(rev2))
94 revb = self.rev(self.bundlebase(rev2))
95 if revb == rev1:
95 if revb == rev1:
96 return self._chunk(rev2)
96 return self._chunk(rev2)
97 elif not self.inbundle(rev1) and not self.inbundle(rev2):
97 elif not self.inbundle(rev1) and not self.inbundle(rev2):
98 return revlog.revlog.revdiff(self, rev1, rev2)
98 return revlog.revlog.revdiff(self, rev1, rev2)
99
99
100 return mdiff.textdiff(self.revision(self.node(rev1)),
100 return mdiff.textdiff(self.revision(self.node(rev1)),
101 self.revision(self.node(rev2)))
101 self.revision(self.node(rev2)))
102
102
103 def revision(self, node):
103 def revision(self, node):
104 """return an uncompressed revision of a given"""
104 """return an uncompressed revision of a given"""
105 if node == nullid:
105 if node == nullid:
106 return ""
106 return ""
107
107
108 text = None
108 text = None
109 chain = []
109 chain = []
110 iter_node = node
110 iter_node = node
111 rev = self.rev(iter_node)
111 rev = self.rev(iter_node)
112 # reconstruct the revision if it is from a changegroup
112 # reconstruct the revision if it is from a changegroup
113 while self.inbundle(rev):
113 while self.inbundle(rev):
114 if self._cache and self._cache[0] == iter_node:
114 if self._cache and self._cache[0] == iter_node:
115 text = self._cache[2]
115 text = self._cache[2]
116 break
116 break
117 chain.append(rev)
117 chain.append(rev)
118 iter_node = self.bundlebase(rev)
118 iter_node = self.bundlebase(rev)
119 rev = self.rev(iter_node)
119 rev = self.rev(iter_node)
120 if text is None:
120 if text is None:
121 text = revlog.revlog.revision(self, iter_node)
121 text = revlog.revlog.revision(self, iter_node)
122
122
123 while chain:
123 while chain:
124 delta = self._chunk(chain.pop())
124 delta = self._chunk(chain.pop())
125 text = mdiff.patches(text, [delta])
125 text = mdiff.patches(text, [delta])
126
126
127 p1, p2 = self.parents(node)
127 p1, p2 = self.parents(node)
128 if node != revlog.hash(text, p1, p2):
128 if node != revlog.hash(text, p1, p2):
129 raise error.RevlogError(_("integrity check failed on %s:%d")
129 raise error.RevlogError(_("integrity check failed on %s:%d")
130 % (self.datafile, self.rev(node)))
130 % (self.datafile, self.rev(node)))
131
131
132 self._cache = (node, self.rev(node), text)
132 self._cache = (node, self.rev(node), text)
133 return text
133 return text
134
134
135 def addrevision(self, text, transaction, link, p1=None, p2=None, d=None):
135 def addrevision(self, text, transaction, link, p1=None, p2=None, d=None):
136 raise NotImplementedError
136 raise NotImplementedError
137 def addgroup(self, revs, linkmapper, transaction):
137 def addgroup(self, revs, linkmapper, transaction):
138 raise NotImplementedError
138 raise NotImplementedError
139 def strip(self, rev, minlink):
139 def strip(self, rev, minlink):
140 raise NotImplementedError
140 raise NotImplementedError
141 def checksize(self):
141 def checksize(self):
142 raise NotImplementedError
142 raise NotImplementedError
143
143
144 class bundlechangelog(bundlerevlog, changelog.changelog):
144 class bundlechangelog(bundlerevlog, changelog.changelog):
145 def __init__(self, opener, bundle):
145 def __init__(self, opener, bundle):
146 changelog.changelog.__init__(self, opener)
146 changelog.changelog.__init__(self, opener)
147 bundlerevlog.__init__(self, opener, self.indexfile, bundle)
147 bundlerevlog.__init__(self, opener, self.indexfile, bundle)
148
148
149 class bundlemanifest(bundlerevlog, manifest.manifest):
149 class bundlemanifest(bundlerevlog, manifest.manifest):
150 def __init__(self, opener, bundle, linkmapper):
150 def __init__(self, opener, bundle, linkmapper):
151 manifest.manifest.__init__(self, opener)
151 manifest.manifest.__init__(self, opener)
152 bundlerevlog.__init__(self, opener, self.indexfile, bundle,
152 bundlerevlog.__init__(self, opener, self.indexfile, bundle,
153 linkmapper)
153 linkmapper)
154
154
155 class bundlefilelog(bundlerevlog, filelog.filelog):
155 class bundlefilelog(bundlerevlog, filelog.filelog):
156 def __init__(self, opener, path, bundle, linkmapper):
156 def __init__(self, opener, path, bundle, linkmapper):
157 filelog.filelog.__init__(self, opener, path)
157 filelog.filelog.__init__(self, opener, path)
158 bundlerevlog.__init__(self, opener, self.indexfile, bundle,
158 bundlerevlog.__init__(self, opener, self.indexfile, bundle,
159 linkmapper)
159 linkmapper)
160
160
161 class bundlerepository(localrepo.localrepository):
161 class bundlerepository(localrepo.localrepository):
162 def __init__(self, ui, path, bundlename):
162 def __init__(self, ui, path, bundlename):
163 self._tempparent = None
163 self._tempparent = None
164 try:
164 try:
165 localrepo.localrepository.__init__(self, ui, path)
165 localrepo.localrepository.__init__(self, ui, path)
166 except error.RepoError:
166 except error.RepoError:
167 self._tempparent = tempfile.mkdtemp()
167 self._tempparent = tempfile.mkdtemp()
168 localrepo.instance(ui, self._tempparent, 1)
168 localrepo.instance(ui, self._tempparent, 1)
169 localrepo.localrepository.__init__(self, ui, self._tempparent)
169 localrepo.localrepository.__init__(self, ui, self._tempparent)
170
170
171 if path:
171 if path:
172 self._url = 'bundle:' + util.expandpath(path) + '+' + bundlename
172 self._url = 'bundle:' + util.expandpath(path) + '+' + bundlename
173 else:
173 else:
174 self._url = 'bundle:' + bundlename
174 self._url = 'bundle:' + bundlename
175
175
176 self.tempfile = None
176 self.tempfile = None
177 f = util.posixfile(bundlename, "rb")
177 f = util.posixfile(bundlename, "rb")
178 self.bundle = changegroup.readbundle(f, bundlename)
178 self.bundle = changegroup.readbundle(f, bundlename)
179 if self.bundle.compressed():
179 if self.bundle.compressed():
180 fdtemp, temp = tempfile.mkstemp(prefix="hg-bundle-",
180 fdtemp, temp = tempfile.mkstemp(prefix="hg-bundle-",
181 suffix=".hg10un", dir=self.path)
181 suffix=".hg10un", dir=self.path)
182 self.tempfile = temp
182 self.tempfile = temp
183 fptemp = os.fdopen(fdtemp, 'wb')
183 fptemp = os.fdopen(fdtemp, 'wb')
184
184
185 try:
185 try:
186 fptemp.write("HG10UN")
186 fptemp.write("HG10UN")
187 while 1:
187 while 1:
188 chunk = self.bundle.read(2**18)
188 chunk = self.bundle.read(2**18)
189 if not chunk:
189 if not chunk:
190 break
190 break
191 fptemp.write(chunk)
191 fptemp.write(chunk)
192 finally:
192 finally:
193 fptemp.close()
193 fptemp.close()
194
194
195 f = util.posixfile(self.tempfile, "rb")
195 f = util.posixfile(self.tempfile, "rb")
196 self.bundle = changegroup.readbundle(f, bundlename)
196 self.bundle = changegroup.readbundle(f, bundlename)
197
197
198 # dict with the mapping 'filename' -> position in the bundle
198 # dict with the mapping 'filename' -> position in the bundle
199 self.bundlefilespos = {}
199 self.bundlefilespos = {}
200
200
201 @util.propertycache
201 @util.propertycache
202 def changelog(self):
202 def changelog(self):
203 c = bundlechangelog(self.sopener, self.bundle)
203 c = bundlechangelog(self.sopener, self.bundle)
204 self.manstart = self.bundle.tell()
204 self.manstart = self.bundle.tell()
205 return c
205 return c
206
206
207 @util.propertycache
207 @util.propertycache
208 def manifest(self):
208 def manifest(self):
209 self.bundle.seek(self.manstart)
209 self.bundle.seek(self.manstart)
210 m = bundlemanifest(self.sopener, self.bundle, self.changelog.rev)
210 m = bundlemanifest(self.sopener, self.bundle, self.changelog.rev)
211 self.filestart = self.bundle.tell()
211 self.filestart = self.bundle.tell()
212 return m
212 return m
213
213
214 @util.propertycache
214 @util.propertycache
215 def manstart(self):
215 def manstart(self):
216 self.changelog
216 self.changelog
217 return self.manstart
217 return self.manstart
218
218
219 @util.propertycache
219 @util.propertycache
220 def filestart(self):
220 def filestart(self):
221 self.manifest
221 self.manifest
222 return self.filestart
222 return self.filestart
223
223
224 def url(self):
224 def url(self):
225 return self._url
225 return self._url
226
226
227 def file(self, f):
227 def file(self, f):
228 if not self.bundlefilespos:
228 if not self.bundlefilespos:
229 self.bundle.seek(self.filestart)
229 self.bundle.seek(self.filestart)
230 while 1:
230 while 1:
231 chunk = self.bundle.chunk()
231 chunk = self.bundle.chunk()
232 if not chunk:
232 if not chunk:
233 break
233 break
234 self.bundlefilespos[chunk] = self.bundle.tell()
234 self.bundlefilespos[chunk] = self.bundle.tell()
235 while 1:
235 while 1:
236 c = self.bundle.chunk()
236 c = self.bundle.chunk()
237 if not c:
237 if not c:
238 break
238 break
239
239
240 if f[0] == '/':
240 if f[0] == '/':
241 f = f[1:]
241 f = f[1:]
242 if f in self.bundlefilespos:
242 if f in self.bundlefilespos:
243 self.bundle.seek(self.bundlefilespos[f])
243 self.bundle.seek(self.bundlefilespos[f])
244 return bundlefilelog(self.sopener, f, self.bundle,
244 return bundlefilelog(self.sopener, f, self.bundle,
245 self.changelog.rev)
245 self.changelog.rev)
246 else:
246 else:
247 return filelog.filelog(self.sopener, f)
247 return filelog.filelog(self.sopener, f)
248
248
249 def close(self):
249 def close(self):
250 """Close assigned bundle file immediately."""
250 """Close assigned bundle file immediately."""
251 self.bundle.close()
251 self.bundle.close()
252 if self.tempfile is not None:
252 if self.tempfile is not None:
253 os.unlink(self.tempfile)
253 os.unlink(self.tempfile)
254 if self._tempparent:
254 if self._tempparent:
255 shutil.rmtree(self._tempparent, True)
255 shutil.rmtree(self._tempparent, True)
256
256
257 def cancopy(self):
257 def cancopy(self):
258 return False
258 return False
259
259
260 def getcwd(self):
260 def getcwd(self):
261 return os.getcwd() # always outside the repo
261 return os.getcwd() # always outside the repo
262
262
263 def instance(ui, path, create):
263 def instance(ui, path, create):
264 if create:
264 if create:
265 raise util.Abort(_('cannot create new bundle repository'))
265 raise util.Abort(_('cannot create new bundle repository'))
266 parentpath = ui.config("bundle", "mainreporoot", "")
266 parentpath = ui.config("bundle", "mainreporoot", "")
267 if parentpath:
267 if parentpath:
268 # Try to make the full path relative so we get a nice, short URL.
268 # Try to make the full path relative so we get a nice, short URL.
269 # In particular, we don't want temp dir names in test outputs.
269 # In particular, we don't want temp dir names in test outputs.
270 cwd = os.getcwd()
270 cwd = os.getcwd()
271 if parentpath == cwd:
271 if parentpath == cwd:
272 parentpath = ''
272 parentpath = ''
273 else:
273 else:
274 cwd = os.path.join(cwd,'')
274 cwd = os.path.join(cwd,'')
275 if parentpath.startswith(cwd):
275 if parentpath.startswith(cwd):
276 parentpath = parentpath[len(cwd):]
276 parentpath = parentpath[len(cwd):]
277 u = url.url(path)
277 u = url.url(path)
278 path = u.localpath()
278 path = u.localpath()
279 if u.scheme == 'bundle':
279 if u.scheme == 'bundle':
280 s = path.split("+", 1)
280 s = path.split("+", 1)
281 if len(s) == 1:
281 if len(s) == 1:
282 repopath, bundlename = parentpath, s[0]
282 repopath, bundlename = parentpath, s[0]
283 else:
283 else:
284 repopath, bundlename = s
284 repopath, bundlename = s
285 else:
285 else:
286 repopath, bundlename = parentpath, path
286 repopath, bundlename = parentpath, path
287 return bundlerepository(ui, repopath, bundlename)
287 return bundlerepository(ui, repopath, bundlename)
288
288
289 def getremotechanges(ui, repo, other, revs=None, bundlename=None,
289 def getremotechanges(ui, repo, other, revs=None, bundlename=None,
290 force=False, usecommon=False):
290 force=False):
291 tmp = discovery.findcommonincoming(repo, other, heads=revs, force=force,
291 tmp = discovery.findcommonincoming(repo, other, heads=revs, force=force)
292 commononly=usecommon)
293 common, incoming, rheads = tmp
292 common, incoming, rheads = tmp
294 if not incoming:
293 if not incoming:
295 try:
294 try:
296 os.unlink(bundlename)
295 os.unlink(bundlename)
297 except OSError:
296 except OSError:
298 pass
297 pass
299 return other, None, None, None
298 return other, None, None, None
300
299
301 bundle = None
300 bundle = None
302 if bundlename or not other.local():
301 if bundlename or not other.local():
303 # create a bundle (uncompressed if other repo is not local)
302 # create a bundle (uncompressed if other repo is not local)
304
303
305 if revs is None and other.capable('changegroupsubset'):
304 if revs is None and other.capable('changegroupsubset'):
306 revs = rheads
305 revs = rheads
307
306
308 if usecommon:
307 if other.capable('getbundle'):
309 cg = other.getbundle('incoming', common=common, heads=revs)
308 cg = other.getbundle('incoming', common=common, heads=revs)
310 elif revs is None:
309 elif revs is None:
311 cg = other.changegroup(incoming, "incoming")
310 cg = other.changegroup(incoming, "incoming")
312 else:
311 else:
313 cg = other.changegroupsubset(incoming, revs, 'incoming')
312 cg = other.changegroupsubset(incoming, revs, 'incoming')
314 bundletype = other.local() and "HG10BZ" or "HG10UN"
313 bundletype = other.local() and "HG10BZ" or "HG10UN"
315 fname = bundle = changegroup.writebundle(cg, bundlename, bundletype)
314 fname = bundle = changegroup.writebundle(cg, bundlename, bundletype)
316 # keep written bundle?
315 # keep written bundle?
317 if bundlename:
316 if bundlename:
318 bundle = None
317 bundle = None
319 if not other.local():
318 if not other.local():
320 # use the created uncompressed bundlerepo
319 # use the created uncompressed bundlerepo
321 other = bundlerepository(ui, repo.root, fname)
320 other = bundlerepository(ui, repo.root, fname)
322 return (other, common, incoming, bundle)
321 return (other, common, incoming, bundle)
323
322
@@ -1,4900 +1,4872
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from node import hex, bin, nullid, nullrev, short
8 from node import hex, bin, nullid, nullrev, short
9 from lock import release
9 from lock import release
10 from i18n import _, gettext
10 from i18n import _, gettext
11 import os, re, sys, difflib, time, tempfile
11 import os, re, sys, difflib, time, tempfile
12 import hg, scmutil, util, revlog, extensions, copies, error, bookmarks
12 import hg, scmutil, util, revlog, extensions, copies, error, bookmarks
13 import patch, help, url, encoding, templatekw, discovery
13 import patch, help, url, encoding, templatekw, discovery
14 import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
14 import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
15 import merge as mergemod
15 import merge as mergemod
16 import minirst, revset, templatefilters
16 import minirst, revset, templatefilters
17 import dagparser
17 import dagparser
18
18
19 # Commands start here, listed alphabetically
19 # Commands start here, listed alphabetically
20
20
21 def add(ui, repo, *pats, **opts):
21 def add(ui, repo, *pats, **opts):
22 """add the specified files on the next commit
22 """add the specified files on the next commit
23
23
24 Schedule files to be version controlled and added to the
24 Schedule files to be version controlled and added to the
25 repository.
25 repository.
26
26
27 The files will be added to the repository at the next commit. To
27 The files will be added to the repository at the next commit. To
28 undo an add before that, see :hg:`forget`.
28 undo an add before that, see :hg:`forget`.
29
29
30 If no names are given, add all files to the repository.
30 If no names are given, add all files to the repository.
31
31
32 .. container:: verbose
32 .. container:: verbose
33
33
34 An example showing how new (unknown) files are added
34 An example showing how new (unknown) files are added
35 automatically by :hg:`add`::
35 automatically by :hg:`add`::
36
36
37 $ ls
37 $ ls
38 foo.c
38 foo.c
39 $ hg status
39 $ hg status
40 ? foo.c
40 ? foo.c
41 $ hg add
41 $ hg add
42 adding foo.c
42 adding foo.c
43 $ hg status
43 $ hg status
44 A foo.c
44 A foo.c
45
45
46 Returns 0 if all files are successfully added.
46 Returns 0 if all files are successfully added.
47 """
47 """
48
48
49 m = cmdutil.match(repo, pats, opts)
49 m = cmdutil.match(repo, pats, opts)
50 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
50 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
51 opts.get('subrepos'), prefix="")
51 opts.get('subrepos'), prefix="")
52 return rejected and 1 or 0
52 return rejected and 1 or 0
53
53
54 def addremove(ui, repo, *pats, **opts):
54 def addremove(ui, repo, *pats, **opts):
55 """add all new files, delete all missing files
55 """add all new files, delete all missing files
56
56
57 Add all new files and remove all missing files from the
57 Add all new files and remove all missing files from the
58 repository.
58 repository.
59
59
60 New files are ignored if they match any of the patterns in
60 New files are ignored if they match any of the patterns in
61 ``.hgignore``. As with add, these changes take effect at the next
61 ``.hgignore``. As with add, these changes take effect at the next
62 commit.
62 commit.
63
63
64 Use the -s/--similarity option to detect renamed files. With a
64 Use the -s/--similarity option to detect renamed files. With a
65 parameter greater than 0, this compares every removed file with
65 parameter greater than 0, this compares every removed file with
66 every added file and records those similar enough as renames. This
66 every added file and records those similar enough as renames. This
67 option takes a percentage between 0 (disabled) and 100 (files must
67 option takes a percentage between 0 (disabled) and 100 (files must
68 be identical) as its parameter. Detecting renamed files this way
68 be identical) as its parameter. Detecting renamed files this way
69 can be expensive. After using this option, :hg:`status -C` can be
69 can be expensive. After using this option, :hg:`status -C` can be
70 used to check which files were identified as moved or renamed.
70 used to check which files were identified as moved or renamed.
71
71
72 Returns 0 if all files are successfully added.
72 Returns 0 if all files are successfully added.
73 """
73 """
74 try:
74 try:
75 sim = float(opts.get('similarity') or 100)
75 sim = float(opts.get('similarity') or 100)
76 except ValueError:
76 except ValueError:
77 raise util.Abort(_('similarity must be a number'))
77 raise util.Abort(_('similarity must be a number'))
78 if sim < 0 or sim > 100:
78 if sim < 0 or sim > 100:
79 raise util.Abort(_('similarity must be between 0 and 100'))
79 raise util.Abort(_('similarity must be between 0 and 100'))
80 return cmdutil.addremove(repo, pats, opts, similarity=sim / 100.0)
80 return cmdutil.addremove(repo, pats, opts, similarity=sim / 100.0)
81
81
82 def annotate(ui, repo, *pats, **opts):
82 def annotate(ui, repo, *pats, **opts):
83 """show changeset information by line for each file
83 """show changeset information by line for each file
84
84
85 List changes in files, showing the revision id responsible for
85 List changes in files, showing the revision id responsible for
86 each line
86 each line
87
87
88 This command is useful for discovering when a change was made and
88 This command is useful for discovering when a change was made and
89 by whom.
89 by whom.
90
90
91 Without the -a/--text option, annotate will avoid processing files
91 Without the -a/--text option, annotate will avoid processing files
92 it detects as binary. With -a, annotate will annotate the file
92 it detects as binary. With -a, annotate will annotate the file
93 anyway, although the results will probably be neither useful
93 anyway, although the results will probably be neither useful
94 nor desirable.
94 nor desirable.
95
95
96 Returns 0 on success.
96 Returns 0 on success.
97 """
97 """
98 if opts.get('follow'):
98 if opts.get('follow'):
99 # --follow is deprecated and now just an alias for -f/--file
99 # --follow is deprecated and now just an alias for -f/--file
100 # to mimic the behavior of Mercurial before version 1.5
100 # to mimic the behavior of Mercurial before version 1.5
101 opts['file'] = 1
101 opts['file'] = 1
102
102
103 datefunc = ui.quiet and util.shortdate or util.datestr
103 datefunc = ui.quiet and util.shortdate or util.datestr
104 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
104 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
105
105
106 if not pats:
106 if not pats:
107 raise util.Abort(_('at least one filename or pattern is required'))
107 raise util.Abort(_('at least one filename or pattern is required'))
108
108
109 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
109 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
110 ('number', lambda x: str(x[0].rev())),
110 ('number', lambda x: str(x[0].rev())),
111 ('changeset', lambda x: short(x[0].node())),
111 ('changeset', lambda x: short(x[0].node())),
112 ('date', getdate),
112 ('date', getdate),
113 ('file', lambda x: x[0].path()),
113 ('file', lambda x: x[0].path()),
114 ]
114 ]
115
115
116 if (not opts.get('user') and not opts.get('changeset')
116 if (not opts.get('user') and not opts.get('changeset')
117 and not opts.get('date') and not opts.get('file')):
117 and not opts.get('date') and not opts.get('file')):
118 opts['number'] = 1
118 opts['number'] = 1
119
119
120 linenumber = opts.get('line_number') is not None
120 linenumber = opts.get('line_number') is not None
121 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
121 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
122 raise util.Abort(_('at least one of -n/-c is required for -l'))
122 raise util.Abort(_('at least one of -n/-c is required for -l'))
123
123
124 funcmap = [func for op, func in opmap if opts.get(op)]
124 funcmap = [func for op, func in opmap if opts.get(op)]
125 if linenumber:
125 if linenumber:
126 lastfunc = funcmap[-1]
126 lastfunc = funcmap[-1]
127 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
127 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
128
128
129 def bad(x, y):
129 def bad(x, y):
130 raise util.Abort("%s: %s" % (x, y))
130 raise util.Abort("%s: %s" % (x, y))
131
131
132 ctx = cmdutil.revsingle(repo, opts.get('rev'))
132 ctx = cmdutil.revsingle(repo, opts.get('rev'))
133 m = cmdutil.match(repo, pats, opts)
133 m = cmdutil.match(repo, pats, opts)
134 m.bad = bad
134 m.bad = bad
135 follow = not opts.get('no_follow')
135 follow = not opts.get('no_follow')
136 for abs in ctx.walk(m):
136 for abs in ctx.walk(m):
137 fctx = ctx[abs]
137 fctx = ctx[abs]
138 if not opts.get('text') and util.binary(fctx.data()):
138 if not opts.get('text') and util.binary(fctx.data()):
139 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
139 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
140 continue
140 continue
141
141
142 lines = fctx.annotate(follow=follow, linenumber=linenumber)
142 lines = fctx.annotate(follow=follow, linenumber=linenumber)
143 pieces = []
143 pieces = []
144
144
145 for f in funcmap:
145 for f in funcmap:
146 l = [f(n) for n, dummy in lines]
146 l = [f(n) for n, dummy in lines]
147 if l:
147 if l:
148 sized = [(x, encoding.colwidth(x)) for x in l]
148 sized = [(x, encoding.colwidth(x)) for x in l]
149 ml = max([w for x, w in sized])
149 ml = max([w for x, w in sized])
150 pieces.append(["%s%s" % (' ' * (ml - w), x) for x, w in sized])
150 pieces.append(["%s%s" % (' ' * (ml - w), x) for x, w in sized])
151
151
152 if pieces:
152 if pieces:
153 for p, l in zip(zip(*pieces), lines):
153 for p, l in zip(zip(*pieces), lines):
154 ui.write("%s: %s" % (" ".join(p), l[1]))
154 ui.write("%s: %s" % (" ".join(p), l[1]))
155
155
156 def archive(ui, repo, dest, **opts):
156 def archive(ui, repo, dest, **opts):
157 '''create an unversioned archive of a repository revision
157 '''create an unversioned archive of a repository revision
158
158
159 By default, the revision used is the parent of the working
159 By default, the revision used is the parent of the working
160 directory; use -r/--rev to specify a different revision.
160 directory; use -r/--rev to specify a different revision.
161
161
162 The archive type is automatically detected based on file
162 The archive type is automatically detected based on file
163 extension (or override using -t/--type).
163 extension (or override using -t/--type).
164
164
165 Valid types are:
165 Valid types are:
166
166
167 :``files``: a directory full of files (default)
167 :``files``: a directory full of files (default)
168 :``tar``: tar archive, uncompressed
168 :``tar``: tar archive, uncompressed
169 :``tbz2``: tar archive, compressed using bzip2
169 :``tbz2``: tar archive, compressed using bzip2
170 :``tgz``: tar archive, compressed using gzip
170 :``tgz``: tar archive, compressed using gzip
171 :``uzip``: zip archive, uncompressed
171 :``uzip``: zip archive, uncompressed
172 :``zip``: zip archive, compressed using deflate
172 :``zip``: zip archive, compressed using deflate
173
173
174 The exact name of the destination archive or directory is given
174 The exact name of the destination archive or directory is given
175 using a format string; see :hg:`help export` for details.
175 using a format string; see :hg:`help export` for details.
176
176
177 Each member added to an archive file has a directory prefix
177 Each member added to an archive file has a directory prefix
178 prepended. Use -p/--prefix to specify a format string for the
178 prepended. Use -p/--prefix to specify a format string for the
179 prefix. The default is the basename of the archive, with suffixes
179 prefix. The default is the basename of the archive, with suffixes
180 removed.
180 removed.
181
181
182 Returns 0 on success.
182 Returns 0 on success.
183 '''
183 '''
184
184
185 ctx = cmdutil.revsingle(repo, opts.get('rev'))
185 ctx = cmdutil.revsingle(repo, opts.get('rev'))
186 if not ctx:
186 if not ctx:
187 raise util.Abort(_('no working directory: please specify a revision'))
187 raise util.Abort(_('no working directory: please specify a revision'))
188 node = ctx.node()
188 node = ctx.node()
189 dest = cmdutil.make_filename(repo, dest, node)
189 dest = cmdutil.make_filename(repo, dest, node)
190 if os.path.realpath(dest) == repo.root:
190 if os.path.realpath(dest) == repo.root:
191 raise util.Abort(_('repository root cannot be destination'))
191 raise util.Abort(_('repository root cannot be destination'))
192
192
193 kind = opts.get('type') or archival.guesskind(dest) or 'files'
193 kind = opts.get('type') or archival.guesskind(dest) or 'files'
194 prefix = opts.get('prefix')
194 prefix = opts.get('prefix')
195
195
196 if dest == '-':
196 if dest == '-':
197 if kind == 'files':
197 if kind == 'files':
198 raise util.Abort(_('cannot archive plain files to stdout'))
198 raise util.Abort(_('cannot archive plain files to stdout'))
199 dest = sys.stdout
199 dest = sys.stdout
200 if not prefix:
200 if not prefix:
201 prefix = os.path.basename(repo.root) + '-%h'
201 prefix = os.path.basename(repo.root) + '-%h'
202
202
203 prefix = cmdutil.make_filename(repo, prefix, node)
203 prefix = cmdutil.make_filename(repo, prefix, node)
204 matchfn = cmdutil.match(repo, [], opts)
204 matchfn = cmdutil.match(repo, [], opts)
205 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
205 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
206 matchfn, prefix, subrepos=opts.get('subrepos'))
206 matchfn, prefix, subrepos=opts.get('subrepos'))
207
207
208 def backout(ui, repo, node=None, rev=None, **opts):
208 def backout(ui, repo, node=None, rev=None, **opts):
209 '''reverse effect of earlier changeset
209 '''reverse effect of earlier changeset
210
210
211 Prepare a new changeset with the effect of REV undone in the
211 Prepare a new changeset with the effect of REV undone in the
212 current working directory.
212 current working directory.
213
213
214 If REV is the parent of the working directory, then this new changeset
214 If REV is the parent of the working directory, then this new changeset
215 is committed automatically. Otherwise, hg needs to merge the
215 is committed automatically. Otherwise, hg needs to merge the
216 changes and the merged result is left uncommitted.
216 changes and the merged result is left uncommitted.
217
217
218 By default, the pending changeset will have one parent,
218 By default, the pending changeset will have one parent,
219 maintaining a linear history. With --merge, the pending changeset
219 maintaining a linear history. With --merge, the pending changeset
220 will instead have two parents: the old parent of the working
220 will instead have two parents: the old parent of the working
221 directory and a new child of REV that simply undoes REV.
221 directory and a new child of REV that simply undoes REV.
222
222
223 Before version 1.7, the behavior without --merge was equivalent to
223 Before version 1.7, the behavior without --merge was equivalent to
224 specifying --merge followed by :hg:`update --clean .` to cancel
224 specifying --merge followed by :hg:`update --clean .` to cancel
225 the merge and leave the child of REV as a head to be merged
225 the merge and leave the child of REV as a head to be merged
226 separately.
226 separately.
227
227
228 See :hg:`help dates` for a list of formats valid for -d/--date.
228 See :hg:`help dates` for a list of formats valid for -d/--date.
229
229
230 Returns 0 on success.
230 Returns 0 on success.
231 '''
231 '''
232 if rev and node:
232 if rev and node:
233 raise util.Abort(_("please specify just one revision"))
233 raise util.Abort(_("please specify just one revision"))
234
234
235 if not rev:
235 if not rev:
236 rev = node
236 rev = node
237
237
238 if not rev:
238 if not rev:
239 raise util.Abort(_("please specify a revision to backout"))
239 raise util.Abort(_("please specify a revision to backout"))
240
240
241 date = opts.get('date')
241 date = opts.get('date')
242 if date:
242 if date:
243 opts['date'] = util.parsedate(date)
243 opts['date'] = util.parsedate(date)
244
244
245 cmdutil.bail_if_changed(repo)
245 cmdutil.bail_if_changed(repo)
246 node = cmdutil.revsingle(repo, rev).node()
246 node = cmdutil.revsingle(repo, rev).node()
247
247
248 op1, op2 = repo.dirstate.parents()
248 op1, op2 = repo.dirstate.parents()
249 a = repo.changelog.ancestor(op1, node)
249 a = repo.changelog.ancestor(op1, node)
250 if a != node:
250 if a != node:
251 raise util.Abort(_('cannot backout change on a different branch'))
251 raise util.Abort(_('cannot backout change on a different branch'))
252
252
253 p1, p2 = repo.changelog.parents(node)
253 p1, p2 = repo.changelog.parents(node)
254 if p1 == nullid:
254 if p1 == nullid:
255 raise util.Abort(_('cannot backout a change with no parents'))
255 raise util.Abort(_('cannot backout a change with no parents'))
256 if p2 != nullid:
256 if p2 != nullid:
257 if not opts.get('parent'):
257 if not opts.get('parent'):
258 raise util.Abort(_('cannot backout a merge changeset without '
258 raise util.Abort(_('cannot backout a merge changeset without '
259 '--parent'))
259 '--parent'))
260 p = repo.lookup(opts['parent'])
260 p = repo.lookup(opts['parent'])
261 if p not in (p1, p2):
261 if p not in (p1, p2):
262 raise util.Abort(_('%s is not a parent of %s') %
262 raise util.Abort(_('%s is not a parent of %s') %
263 (short(p), short(node)))
263 (short(p), short(node)))
264 parent = p
264 parent = p
265 else:
265 else:
266 if opts.get('parent'):
266 if opts.get('parent'):
267 raise util.Abort(_('cannot use --parent on non-merge changeset'))
267 raise util.Abort(_('cannot use --parent on non-merge changeset'))
268 parent = p1
268 parent = p1
269
269
270 # the backout should appear on the same branch
270 # the backout should appear on the same branch
271 branch = repo.dirstate.branch()
271 branch = repo.dirstate.branch()
272 hg.clean(repo, node, show_stats=False)
272 hg.clean(repo, node, show_stats=False)
273 repo.dirstate.setbranch(branch)
273 repo.dirstate.setbranch(branch)
274 revert_opts = opts.copy()
274 revert_opts = opts.copy()
275 revert_opts['date'] = None
275 revert_opts['date'] = None
276 revert_opts['all'] = True
276 revert_opts['all'] = True
277 revert_opts['rev'] = hex(parent)
277 revert_opts['rev'] = hex(parent)
278 revert_opts['no_backup'] = None
278 revert_opts['no_backup'] = None
279 revert(ui, repo, **revert_opts)
279 revert(ui, repo, **revert_opts)
280 if not opts.get('merge') and op1 != node:
280 if not opts.get('merge') and op1 != node:
281 try:
281 try:
282 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
282 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
283 return hg.update(repo, op1)
283 return hg.update(repo, op1)
284 finally:
284 finally:
285 ui.setconfig('ui', 'forcemerge', '')
285 ui.setconfig('ui', 'forcemerge', '')
286
286
287 commit_opts = opts.copy()
287 commit_opts = opts.copy()
288 commit_opts['addremove'] = False
288 commit_opts['addremove'] = False
289 if not commit_opts['message'] and not commit_opts['logfile']:
289 if not commit_opts['message'] and not commit_opts['logfile']:
290 # we don't translate commit messages
290 # we don't translate commit messages
291 commit_opts['message'] = "Backed out changeset %s" % short(node)
291 commit_opts['message'] = "Backed out changeset %s" % short(node)
292 commit_opts['force_editor'] = True
292 commit_opts['force_editor'] = True
293 commit(ui, repo, **commit_opts)
293 commit(ui, repo, **commit_opts)
294 def nice(node):
294 def nice(node):
295 return '%d:%s' % (repo.changelog.rev(node), short(node))
295 return '%d:%s' % (repo.changelog.rev(node), short(node))
296 ui.status(_('changeset %s backs out changeset %s\n') %
296 ui.status(_('changeset %s backs out changeset %s\n') %
297 (nice(repo.changelog.tip()), nice(node)))
297 (nice(repo.changelog.tip()), nice(node)))
298 if opts.get('merge') and op1 != node:
298 if opts.get('merge') and op1 != node:
299 hg.clean(repo, op1, show_stats=False)
299 hg.clean(repo, op1, show_stats=False)
300 ui.status(_('merging with changeset %s\n')
300 ui.status(_('merging with changeset %s\n')
301 % nice(repo.changelog.tip()))
301 % nice(repo.changelog.tip()))
302 try:
302 try:
303 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
303 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
304 return hg.merge(repo, hex(repo.changelog.tip()))
304 return hg.merge(repo, hex(repo.changelog.tip()))
305 finally:
305 finally:
306 ui.setconfig('ui', 'forcemerge', '')
306 ui.setconfig('ui', 'forcemerge', '')
307 return 0
307 return 0
308
308
309 def bisect(ui, repo, rev=None, extra=None, command=None,
309 def bisect(ui, repo, rev=None, extra=None, command=None,
310 reset=None, good=None, bad=None, skip=None, extend=None,
310 reset=None, good=None, bad=None, skip=None, extend=None,
311 noupdate=None):
311 noupdate=None):
312 """subdivision search of changesets
312 """subdivision search of changesets
313
313
314 This command helps to find changesets which introduce problems. To
314 This command helps to find changesets which introduce problems. To
315 use, mark the earliest changeset you know exhibits the problem as
315 use, mark the earliest changeset you know exhibits the problem as
316 bad, then mark the latest changeset which is free from the problem
316 bad, then mark the latest changeset which is free from the problem
317 as good. Bisect will update your working directory to a revision
317 as good. Bisect will update your working directory to a revision
318 for testing (unless the -U/--noupdate option is specified). Once
318 for testing (unless the -U/--noupdate option is specified). Once
319 you have performed tests, mark the working directory as good or
319 you have performed tests, mark the working directory as good or
320 bad, and bisect will either update to another candidate changeset
320 bad, and bisect will either update to another candidate changeset
321 or announce that it has found the bad revision.
321 or announce that it has found the bad revision.
322
322
323 As a shortcut, you can also use the revision argument to mark a
323 As a shortcut, you can also use the revision argument to mark a
324 revision as good or bad without checking it out first.
324 revision as good or bad without checking it out first.
325
325
326 If you supply a command, it will be used for automatic bisection.
326 If you supply a command, it will be used for automatic bisection.
327 Its exit status will be used to mark revisions as good or bad:
327 Its exit status will be used to mark revisions as good or bad:
328 status 0 means good, 125 means to skip the revision, 127
328 status 0 means good, 125 means to skip the revision, 127
329 (command not found) will abort the bisection, and any other
329 (command not found) will abort the bisection, and any other
330 non-zero exit status means the revision is bad.
330 non-zero exit status means the revision is bad.
331
331
332 Returns 0 on success.
332 Returns 0 on success.
333 """
333 """
334 def extendbisectrange(nodes, good):
334 def extendbisectrange(nodes, good):
335 # bisect is incomplete when it ends on a merge node and
335 # bisect is incomplete when it ends on a merge node and
336 # one of the parent was not checked.
336 # one of the parent was not checked.
337 parents = repo[nodes[0]].parents()
337 parents = repo[nodes[0]].parents()
338 if len(parents) > 1:
338 if len(parents) > 1:
339 side = good and state['bad'] or state['good']
339 side = good and state['bad'] or state['good']
340 num = len(set(i.node() for i in parents) & set(side))
340 num = len(set(i.node() for i in parents) & set(side))
341 if num == 1:
341 if num == 1:
342 return parents[0].ancestor(parents[1])
342 return parents[0].ancestor(parents[1])
343 return None
343 return None
344
344
345 def print_result(nodes, good):
345 def print_result(nodes, good):
346 displayer = cmdutil.show_changeset(ui, repo, {})
346 displayer = cmdutil.show_changeset(ui, repo, {})
347 if len(nodes) == 1:
347 if len(nodes) == 1:
348 # narrowed it down to a single revision
348 # narrowed it down to a single revision
349 if good:
349 if good:
350 ui.write(_("The first good revision is:\n"))
350 ui.write(_("The first good revision is:\n"))
351 else:
351 else:
352 ui.write(_("The first bad revision is:\n"))
352 ui.write(_("The first bad revision is:\n"))
353 displayer.show(repo[nodes[0]])
353 displayer.show(repo[nodes[0]])
354 extendnode = extendbisectrange(nodes, good)
354 extendnode = extendbisectrange(nodes, good)
355 if extendnode is not None:
355 if extendnode is not None:
356 ui.write(_('Not all ancestors of this changeset have been'
356 ui.write(_('Not all ancestors of this changeset have been'
357 ' checked.\nUse bisect --extend to continue the '
357 ' checked.\nUse bisect --extend to continue the '
358 'bisection from\nthe common ancestor, %s.\n')
358 'bisection from\nthe common ancestor, %s.\n')
359 % extendnode)
359 % extendnode)
360 else:
360 else:
361 # multiple possible revisions
361 # multiple possible revisions
362 if good:
362 if good:
363 ui.write(_("Due to skipped revisions, the first "
363 ui.write(_("Due to skipped revisions, the first "
364 "good revision could be any of:\n"))
364 "good revision could be any of:\n"))
365 else:
365 else:
366 ui.write(_("Due to skipped revisions, the first "
366 ui.write(_("Due to skipped revisions, the first "
367 "bad revision could be any of:\n"))
367 "bad revision could be any of:\n"))
368 for n in nodes:
368 for n in nodes:
369 displayer.show(repo[n])
369 displayer.show(repo[n])
370 displayer.close()
370 displayer.close()
371
371
372 def check_state(state, interactive=True):
372 def check_state(state, interactive=True):
373 if not state['good'] or not state['bad']:
373 if not state['good'] or not state['bad']:
374 if (good or bad or skip or reset) and interactive:
374 if (good or bad or skip or reset) and interactive:
375 return
375 return
376 if not state['good']:
376 if not state['good']:
377 raise util.Abort(_('cannot bisect (no known good revisions)'))
377 raise util.Abort(_('cannot bisect (no known good revisions)'))
378 else:
378 else:
379 raise util.Abort(_('cannot bisect (no known bad revisions)'))
379 raise util.Abort(_('cannot bisect (no known bad revisions)'))
380 return True
380 return True
381
381
382 # backward compatibility
382 # backward compatibility
383 if rev in "good bad reset init".split():
383 if rev in "good bad reset init".split():
384 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
384 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
385 cmd, rev, extra = rev, extra, None
385 cmd, rev, extra = rev, extra, None
386 if cmd == "good":
386 if cmd == "good":
387 good = True
387 good = True
388 elif cmd == "bad":
388 elif cmd == "bad":
389 bad = True
389 bad = True
390 else:
390 else:
391 reset = True
391 reset = True
392 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
392 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
393 raise util.Abort(_('incompatible arguments'))
393 raise util.Abort(_('incompatible arguments'))
394
394
395 if reset:
395 if reset:
396 p = repo.join("bisect.state")
396 p = repo.join("bisect.state")
397 if os.path.exists(p):
397 if os.path.exists(p):
398 os.unlink(p)
398 os.unlink(p)
399 return
399 return
400
400
401 state = hbisect.load_state(repo)
401 state = hbisect.load_state(repo)
402
402
403 if command:
403 if command:
404 changesets = 1
404 changesets = 1
405 try:
405 try:
406 while changesets:
406 while changesets:
407 # update state
407 # update state
408 status = util.system(command)
408 status = util.system(command)
409 if status == 125:
409 if status == 125:
410 transition = "skip"
410 transition = "skip"
411 elif status == 0:
411 elif status == 0:
412 transition = "good"
412 transition = "good"
413 # status < 0 means process was killed
413 # status < 0 means process was killed
414 elif status == 127:
414 elif status == 127:
415 raise util.Abort(_("failed to execute %s") % command)
415 raise util.Abort(_("failed to execute %s") % command)
416 elif status < 0:
416 elif status < 0:
417 raise util.Abort(_("%s killed") % command)
417 raise util.Abort(_("%s killed") % command)
418 else:
418 else:
419 transition = "bad"
419 transition = "bad"
420 ctx = cmdutil.revsingle(repo, rev)
420 ctx = cmdutil.revsingle(repo, rev)
421 rev = None # clear for future iterations
421 rev = None # clear for future iterations
422 state[transition].append(ctx.node())
422 state[transition].append(ctx.node())
423 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
423 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
424 check_state(state, interactive=False)
424 check_state(state, interactive=False)
425 # bisect
425 # bisect
426 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
426 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
427 # update to next check
427 # update to next check
428 cmdutil.bail_if_changed(repo)
428 cmdutil.bail_if_changed(repo)
429 hg.clean(repo, nodes[0], show_stats=False)
429 hg.clean(repo, nodes[0], show_stats=False)
430 finally:
430 finally:
431 hbisect.save_state(repo, state)
431 hbisect.save_state(repo, state)
432 print_result(nodes, good)
432 print_result(nodes, good)
433 return
433 return
434
434
435 # update state
435 # update state
436
436
437 if rev:
437 if rev:
438 nodes = [repo.lookup(i) for i in cmdutil.revrange(repo, [rev])]
438 nodes = [repo.lookup(i) for i in cmdutil.revrange(repo, [rev])]
439 else:
439 else:
440 nodes = [repo.lookup('.')]
440 nodes = [repo.lookup('.')]
441
441
442 if good or bad or skip:
442 if good or bad or skip:
443 if good:
443 if good:
444 state['good'] += nodes
444 state['good'] += nodes
445 elif bad:
445 elif bad:
446 state['bad'] += nodes
446 state['bad'] += nodes
447 elif skip:
447 elif skip:
448 state['skip'] += nodes
448 state['skip'] += nodes
449 hbisect.save_state(repo, state)
449 hbisect.save_state(repo, state)
450
450
451 if not check_state(state):
451 if not check_state(state):
452 return
452 return
453
453
454 # actually bisect
454 # actually bisect
455 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
455 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
456 if extend:
456 if extend:
457 if not changesets:
457 if not changesets:
458 extendnode = extendbisectrange(nodes, good)
458 extendnode = extendbisectrange(nodes, good)
459 if extendnode is not None:
459 if extendnode is not None:
460 ui.write(_("Extending search to changeset %d:%s\n"
460 ui.write(_("Extending search to changeset %d:%s\n"
461 % (extendnode.rev(), extendnode)))
461 % (extendnode.rev(), extendnode)))
462 if noupdate:
462 if noupdate:
463 return
463 return
464 cmdutil.bail_if_changed(repo)
464 cmdutil.bail_if_changed(repo)
465 return hg.clean(repo, extendnode.node())
465 return hg.clean(repo, extendnode.node())
466 raise util.Abort(_("nothing to extend"))
466 raise util.Abort(_("nothing to extend"))
467
467
468 if changesets == 0:
468 if changesets == 0:
469 print_result(nodes, good)
469 print_result(nodes, good)
470 else:
470 else:
471 assert len(nodes) == 1 # only a single node can be tested next
471 assert len(nodes) == 1 # only a single node can be tested next
472 node = nodes[0]
472 node = nodes[0]
473 # compute the approximate number of remaining tests
473 # compute the approximate number of remaining tests
474 tests, size = 0, 2
474 tests, size = 0, 2
475 while size <= changesets:
475 while size <= changesets:
476 tests, size = tests + 1, size * 2
476 tests, size = tests + 1, size * 2
477 rev = repo.changelog.rev(node)
477 rev = repo.changelog.rev(node)
478 ui.write(_("Testing changeset %d:%s "
478 ui.write(_("Testing changeset %d:%s "
479 "(%d changesets remaining, ~%d tests)\n")
479 "(%d changesets remaining, ~%d tests)\n")
480 % (rev, short(node), changesets, tests))
480 % (rev, short(node), changesets, tests))
481 if not noupdate:
481 if not noupdate:
482 cmdutil.bail_if_changed(repo)
482 cmdutil.bail_if_changed(repo)
483 return hg.clean(repo, node)
483 return hg.clean(repo, node)
484
484
485 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False, rename=None):
485 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False, rename=None):
486 '''track a line of development with movable markers
486 '''track a line of development with movable markers
487
487
488 Bookmarks are pointers to certain commits that move when
488 Bookmarks are pointers to certain commits that move when
489 committing. Bookmarks are local. They can be renamed, copied and
489 committing. Bookmarks are local. They can be renamed, copied and
490 deleted. It is possible to use bookmark names in :hg:`merge` and
490 deleted. It is possible to use bookmark names in :hg:`merge` and
491 :hg:`update` to merge and update respectively to a given bookmark.
491 :hg:`update` to merge and update respectively to a given bookmark.
492
492
493 You can use :hg:`bookmark NAME` to set a bookmark on the working
493 You can use :hg:`bookmark NAME` to set a bookmark on the working
494 directory's parent revision with the given name. If you specify
494 directory's parent revision with the given name. If you specify
495 a revision using -r REV (where REV may be an existing bookmark),
495 a revision using -r REV (where REV may be an existing bookmark),
496 the bookmark is assigned to that revision.
496 the bookmark is assigned to that revision.
497
497
498 Bookmarks can be pushed and pulled between repositories (see :hg:`help
498 Bookmarks can be pushed and pulled between repositories (see :hg:`help
499 push` and :hg:`help pull`). This requires both the local and remote
499 push` and :hg:`help pull`). This requires both the local and remote
500 repositories to support bookmarks. For versions prior to 1.8, this means
500 repositories to support bookmarks. For versions prior to 1.8, this means
501 the bookmarks extension must be enabled.
501 the bookmarks extension must be enabled.
502 '''
502 '''
503 hexfn = ui.debugflag and hex or short
503 hexfn = ui.debugflag and hex or short
504 marks = repo._bookmarks
504 marks = repo._bookmarks
505 cur = repo.changectx('.').node()
505 cur = repo.changectx('.').node()
506
506
507 if rename:
507 if rename:
508 if rename not in marks:
508 if rename not in marks:
509 raise util.Abort(_("bookmark '%s' does not exist") % rename)
509 raise util.Abort(_("bookmark '%s' does not exist") % rename)
510 if mark in marks and not force:
510 if mark in marks and not force:
511 raise util.Abort(_("bookmark '%s' already exists "
511 raise util.Abort(_("bookmark '%s' already exists "
512 "(use -f to force)") % mark)
512 "(use -f to force)") % mark)
513 if mark is None:
513 if mark is None:
514 raise util.Abort(_("new bookmark name required"))
514 raise util.Abort(_("new bookmark name required"))
515 marks[mark] = marks[rename]
515 marks[mark] = marks[rename]
516 if repo._bookmarkcurrent == rename:
516 if repo._bookmarkcurrent == rename:
517 bookmarks.setcurrent(repo, mark)
517 bookmarks.setcurrent(repo, mark)
518 del marks[rename]
518 del marks[rename]
519 bookmarks.write(repo)
519 bookmarks.write(repo)
520 return
520 return
521
521
522 if delete:
522 if delete:
523 if mark is None:
523 if mark is None:
524 raise util.Abort(_("bookmark name required"))
524 raise util.Abort(_("bookmark name required"))
525 if mark not in marks:
525 if mark not in marks:
526 raise util.Abort(_("bookmark '%s' does not exist") % mark)
526 raise util.Abort(_("bookmark '%s' does not exist") % mark)
527 if mark == repo._bookmarkcurrent:
527 if mark == repo._bookmarkcurrent:
528 bookmarks.setcurrent(repo, None)
528 bookmarks.setcurrent(repo, None)
529 del marks[mark]
529 del marks[mark]
530 bookmarks.write(repo)
530 bookmarks.write(repo)
531 return
531 return
532
532
533 if mark is not None:
533 if mark is not None:
534 if "\n" in mark:
534 if "\n" in mark:
535 raise util.Abort(_("bookmark name cannot contain newlines"))
535 raise util.Abort(_("bookmark name cannot contain newlines"))
536 mark = mark.strip()
536 mark = mark.strip()
537 if not mark:
537 if not mark:
538 raise util.Abort(_("bookmark names cannot consist entirely of "
538 raise util.Abort(_("bookmark names cannot consist entirely of "
539 "whitespace"))
539 "whitespace"))
540 if mark in marks and not force:
540 if mark in marks and not force:
541 raise util.Abort(_("bookmark '%s' already exists "
541 raise util.Abort(_("bookmark '%s' already exists "
542 "(use -f to force)") % mark)
542 "(use -f to force)") % mark)
543 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
543 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
544 and not force):
544 and not force):
545 raise util.Abort(
545 raise util.Abort(
546 _("a bookmark cannot have the name of an existing branch"))
546 _("a bookmark cannot have the name of an existing branch"))
547 if rev:
547 if rev:
548 marks[mark] = repo.lookup(rev)
548 marks[mark] = repo.lookup(rev)
549 else:
549 else:
550 marks[mark] = repo.changectx('.').node()
550 marks[mark] = repo.changectx('.').node()
551 if repo.changectx('.').node() == marks[mark]:
551 if repo.changectx('.').node() == marks[mark]:
552 bookmarks.setcurrent(repo, mark)
552 bookmarks.setcurrent(repo, mark)
553 bookmarks.write(repo)
553 bookmarks.write(repo)
554 return
554 return
555
555
556 if mark is None:
556 if mark is None:
557 if rev:
557 if rev:
558 raise util.Abort(_("bookmark name required"))
558 raise util.Abort(_("bookmark name required"))
559 if len(marks) == 0:
559 if len(marks) == 0:
560 ui.status(_("no bookmarks set\n"))
560 ui.status(_("no bookmarks set\n"))
561 else:
561 else:
562 for bmark, n in sorted(marks.iteritems()):
562 for bmark, n in sorted(marks.iteritems()):
563 current = repo._bookmarkcurrent
563 current = repo._bookmarkcurrent
564 if bmark == current and n == cur:
564 if bmark == current and n == cur:
565 prefix, label = '*', 'bookmarks.current'
565 prefix, label = '*', 'bookmarks.current'
566 else:
566 else:
567 prefix, label = ' ', ''
567 prefix, label = ' ', ''
568
568
569 if ui.quiet:
569 if ui.quiet:
570 ui.write("%s\n" % bmark, label=label)
570 ui.write("%s\n" % bmark, label=label)
571 else:
571 else:
572 ui.write(" %s %-25s %d:%s\n" % (
572 ui.write(" %s %-25s %d:%s\n" % (
573 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
573 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
574 label=label)
574 label=label)
575 return
575 return
576
576
577 def branch(ui, repo, label=None, **opts):
577 def branch(ui, repo, label=None, **opts):
578 """set or show the current branch name
578 """set or show the current branch name
579
579
580 With no argument, show the current branch name. With one argument,
580 With no argument, show the current branch name. With one argument,
581 set the working directory branch name (the branch will not exist
581 set the working directory branch name (the branch will not exist
582 in the repository until the next commit). Standard practice
582 in the repository until the next commit). Standard practice
583 recommends that primary development take place on the 'default'
583 recommends that primary development take place on the 'default'
584 branch.
584 branch.
585
585
586 Unless -f/--force is specified, branch will not let you set a
586 Unless -f/--force is specified, branch will not let you set a
587 branch name that already exists, even if it's inactive.
587 branch name that already exists, even if it's inactive.
588
588
589 Use -C/--clean to reset the working directory branch to that of
589 Use -C/--clean to reset the working directory branch to that of
590 the parent of the working directory, negating a previous branch
590 the parent of the working directory, negating a previous branch
591 change.
591 change.
592
592
593 Use the command :hg:`update` to switch to an existing branch. Use
593 Use the command :hg:`update` to switch to an existing branch. Use
594 :hg:`commit --close-branch` to mark this branch as closed.
594 :hg:`commit --close-branch` to mark this branch as closed.
595
595
596 Returns 0 on success.
596 Returns 0 on success.
597 """
597 """
598
598
599 if opts.get('clean'):
599 if opts.get('clean'):
600 label = repo[None].p1().branch()
600 label = repo[None].p1().branch()
601 repo.dirstate.setbranch(label)
601 repo.dirstate.setbranch(label)
602 ui.status(_('reset working directory to branch %s\n') % label)
602 ui.status(_('reset working directory to branch %s\n') % label)
603 elif label:
603 elif label:
604 if not opts.get('force') and label in repo.branchtags():
604 if not opts.get('force') and label in repo.branchtags():
605 if label not in [p.branch() for p in repo.parents()]:
605 if label not in [p.branch() for p in repo.parents()]:
606 raise util.Abort(_('a branch of the same name already exists'
606 raise util.Abort(_('a branch of the same name already exists'
607 " (use 'hg update' to switch to it)"))
607 " (use 'hg update' to switch to it)"))
608 repo.dirstate.setbranch(label)
608 repo.dirstate.setbranch(label)
609 ui.status(_('marked working directory as branch %s\n') % label)
609 ui.status(_('marked working directory as branch %s\n') % label)
610 else:
610 else:
611 ui.write("%s\n" % repo.dirstate.branch())
611 ui.write("%s\n" % repo.dirstate.branch())
612
612
613 def branches(ui, repo, active=False, closed=False):
613 def branches(ui, repo, active=False, closed=False):
614 """list repository named branches
614 """list repository named branches
615
615
616 List the repository's named branches, indicating which ones are
616 List the repository's named branches, indicating which ones are
617 inactive. If -c/--closed is specified, also list branches which have
617 inactive. If -c/--closed is specified, also list branches which have
618 been marked closed (see :hg:`commit --close-branch`).
618 been marked closed (see :hg:`commit --close-branch`).
619
619
620 If -a/--active is specified, only show active branches. A branch
620 If -a/--active is specified, only show active branches. A branch
621 is considered active if it contains repository heads.
621 is considered active if it contains repository heads.
622
622
623 Use the command :hg:`update` to switch to an existing branch.
623 Use the command :hg:`update` to switch to an existing branch.
624
624
625 Returns 0.
625 Returns 0.
626 """
626 """
627
627
628 hexfunc = ui.debugflag and hex or short
628 hexfunc = ui.debugflag and hex or short
629 activebranches = [repo[n].branch() for n in repo.heads()]
629 activebranches = [repo[n].branch() for n in repo.heads()]
630 def testactive(tag, node):
630 def testactive(tag, node):
631 realhead = tag in activebranches
631 realhead = tag in activebranches
632 open = node in repo.branchheads(tag, closed=False)
632 open = node in repo.branchheads(tag, closed=False)
633 return realhead and open
633 return realhead and open
634 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
634 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
635 for tag, node in repo.branchtags().items()],
635 for tag, node in repo.branchtags().items()],
636 reverse=True)
636 reverse=True)
637
637
638 for isactive, node, tag in branches:
638 for isactive, node, tag in branches:
639 if (not active) or isactive:
639 if (not active) or isactive:
640 if ui.quiet:
640 if ui.quiet:
641 ui.write("%s\n" % tag)
641 ui.write("%s\n" % tag)
642 else:
642 else:
643 hn = repo.lookup(node)
643 hn = repo.lookup(node)
644 if isactive:
644 if isactive:
645 label = 'branches.active'
645 label = 'branches.active'
646 notice = ''
646 notice = ''
647 elif hn not in repo.branchheads(tag, closed=False):
647 elif hn not in repo.branchheads(tag, closed=False):
648 if not closed:
648 if not closed:
649 continue
649 continue
650 label = 'branches.closed'
650 label = 'branches.closed'
651 notice = _(' (closed)')
651 notice = _(' (closed)')
652 else:
652 else:
653 label = 'branches.inactive'
653 label = 'branches.inactive'
654 notice = _(' (inactive)')
654 notice = _(' (inactive)')
655 if tag == repo.dirstate.branch():
655 if tag == repo.dirstate.branch():
656 label = 'branches.current'
656 label = 'branches.current'
657 rev = str(node).rjust(31 - encoding.colwidth(tag))
657 rev = str(node).rjust(31 - encoding.colwidth(tag))
658 rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
658 rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
659 tag = ui.label(tag, label)
659 tag = ui.label(tag, label)
660 ui.write("%s %s%s\n" % (tag, rev, notice))
660 ui.write("%s %s%s\n" % (tag, rev, notice))
661
661
662 def bundle(ui, repo, fname, dest=None, **opts):
662 def bundle(ui, repo, fname, dest=None, **opts):
663 """create a changegroup file
663 """create a changegroup file
664
664
665 Generate a compressed changegroup file collecting changesets not
665 Generate a compressed changegroup file collecting changesets not
666 known to be in another repository.
666 known to be in another repository.
667
667
668 If you omit the destination repository, then hg assumes the
668 If you omit the destination repository, then hg assumes the
669 destination will have all the nodes you specify with --base
669 destination will have all the nodes you specify with --base
670 parameters. To create a bundle containing all changesets, use
670 parameters. To create a bundle containing all changesets, use
671 -a/--all (or --base null).
671 -a/--all (or --base null).
672
672
673 You can change compression method with the -t/--type option.
673 You can change compression method with the -t/--type option.
674 The available compression methods are: none, bzip2, and
674 The available compression methods are: none, bzip2, and
675 gzip (by default, bundles are compressed using bzip2).
675 gzip (by default, bundles are compressed using bzip2).
676
676
677 The bundle file can then be transferred using conventional means
677 The bundle file can then be transferred using conventional means
678 and applied to another repository with the unbundle or pull
678 and applied to another repository with the unbundle or pull
679 command. This is useful when direct push and pull are not
679 command. This is useful when direct push and pull are not
680 available or when exporting an entire repository is undesirable.
680 available or when exporting an entire repository is undesirable.
681
681
682 Applying bundles preserves all changeset contents including
682 Applying bundles preserves all changeset contents including
683 permissions, copy/rename information, and revision history.
683 permissions, copy/rename information, and revision history.
684
684
685 Returns 0 on success, 1 if no changes found.
685 Returns 0 on success, 1 if no changes found.
686 """
686 """
687 revs = None
687 revs = None
688 if 'rev' in opts:
688 if 'rev' in opts:
689 revs = cmdutil.revrange(repo, opts['rev'])
689 revs = cmdutil.revrange(repo, opts['rev'])
690
690
691 if opts.get('all'):
691 if opts.get('all'):
692 base = ['null']
692 base = ['null']
693 else:
693 else:
694 base = cmdutil.revrange(repo, opts.get('base'))
694 base = cmdutil.revrange(repo, opts.get('base'))
695 if base:
695 if base:
696 if dest:
696 if dest:
697 raise util.Abort(_("--base is incompatible with specifying "
697 raise util.Abort(_("--base is incompatible with specifying "
698 "a destination"))
698 "a destination"))
699 base = [repo.lookup(rev) for rev in base]
699 common = [repo.lookup(rev) for rev in base]
700 # create the right base
701 # XXX: nodesbetween / changegroup* should be "fixed" instead
702 o = []
703 has = set((nullid,))
704 for n in base:
705 has.update(repo.changelog.reachable(n))
706 if revs:
707 revs = [repo.lookup(rev) for rev in revs]
708 visit = revs[:]
709 has.difference_update(visit)
710 else:
711 visit = repo.changelog.heads()
712 seen = {}
713 while visit:
714 n = visit.pop(0)
715 parents = [p for p in repo.changelog.parents(n) if p not in has]
716 if len(parents) == 0:
717 if n not in has:
718 o.append(n)
719 else:
720 for p in parents:
721 if p not in seen:
722 seen[p] = 1
723 visit.append(p)
724 else:
700 else:
725 dest = ui.expandpath(dest or 'default-push', dest or 'default')
701 dest = ui.expandpath(dest or 'default-push', dest or 'default')
726 dest, branches = hg.parseurl(dest, opts.get('branch'))
702 dest, branches = hg.parseurl(dest, opts.get('branch'))
727 other = hg.repository(hg.remoteui(repo, opts), dest)
703 other = hg.repository(hg.remoteui(repo, opts), dest)
728 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
704 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
729 if revs:
705 inc = discovery.findcommonincoming(repo, other, force=opts.get('force'))
730 revs = [repo.lookup(rev) for rev in revs]
706 common, _anyinc, _heads = inc
731 o = discovery.findoutgoing(repo, other, force=opts.get('force'))
707
732
708 nodes = revs and map(repo.lookup, revs) or revs
733 if not o:
709 cg = repo.getbundle('bundle', common=common, heads=nodes)
710 if not cg:
734 ui.status(_("no changes found\n"))
711 ui.status(_("no changes found\n"))
735 return 1
712 return 1
736
713
737 if revs:
738 cg = repo.changegroupsubset(o, revs, 'bundle')
739 else:
740 cg = repo.changegroup(o, 'bundle')
741
742 bundletype = opts.get('type', 'bzip2').lower()
714 bundletype = opts.get('type', 'bzip2').lower()
743 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
715 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
744 bundletype = btypes.get(bundletype)
716 bundletype = btypes.get(bundletype)
745 if bundletype not in changegroup.bundletypes:
717 if bundletype not in changegroup.bundletypes:
746 raise util.Abort(_('unknown bundle type specified with --type'))
718 raise util.Abort(_('unknown bundle type specified with --type'))
747
719
748 changegroup.writebundle(cg, fname, bundletype)
720 changegroup.writebundle(cg, fname, bundletype)
749
721
750 def cat(ui, repo, file1, *pats, **opts):
722 def cat(ui, repo, file1, *pats, **opts):
751 """output the current or given revision of files
723 """output the current or given revision of files
752
724
753 Print the specified files as they were at the given revision. If
725 Print the specified files as they were at the given revision. If
754 no revision is given, the parent of the working directory is used,
726 no revision is given, the parent of the working directory is used,
755 or tip if no revision is checked out.
727 or tip if no revision is checked out.
756
728
757 Output may be to a file, in which case the name of the file is
729 Output may be to a file, in which case the name of the file is
758 given using a format string. The formatting rules are the same as
730 given using a format string. The formatting rules are the same as
759 for the export command, with the following additions:
731 for the export command, with the following additions:
760
732
761 :``%s``: basename of file being printed
733 :``%s``: basename of file being printed
762 :``%d``: dirname of file being printed, or '.' if in repository root
734 :``%d``: dirname of file being printed, or '.' if in repository root
763 :``%p``: root-relative path name of file being printed
735 :``%p``: root-relative path name of file being printed
764
736
765 Returns 0 on success.
737 Returns 0 on success.
766 """
738 """
767 ctx = cmdutil.revsingle(repo, opts.get('rev'))
739 ctx = cmdutil.revsingle(repo, opts.get('rev'))
768 err = 1
740 err = 1
769 m = cmdutil.match(repo, (file1,) + pats, opts)
741 m = cmdutil.match(repo, (file1,) + pats, opts)
770 for abs in ctx.walk(m):
742 for abs in ctx.walk(m):
771 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
743 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
772 data = ctx[abs].data()
744 data = ctx[abs].data()
773 if opts.get('decode'):
745 if opts.get('decode'):
774 data = repo.wwritedata(abs, data)
746 data = repo.wwritedata(abs, data)
775 fp.write(data)
747 fp.write(data)
776 fp.close()
748 fp.close()
777 err = 0
749 err = 0
778 return err
750 return err
779
751
780 def clone(ui, source, dest=None, **opts):
752 def clone(ui, source, dest=None, **opts):
781 """make a copy of an existing repository
753 """make a copy of an existing repository
782
754
783 Create a copy of an existing repository in a new directory.
755 Create a copy of an existing repository in a new directory.
784
756
785 If no destination directory name is specified, it defaults to the
757 If no destination directory name is specified, it defaults to the
786 basename of the source.
758 basename of the source.
787
759
788 The location of the source is added to the new repository's
760 The location of the source is added to the new repository's
789 ``.hg/hgrc`` file, as the default to be used for future pulls.
761 ``.hg/hgrc`` file, as the default to be used for future pulls.
790
762
791 See :hg:`help urls` for valid source format details.
763 See :hg:`help urls` for valid source format details.
792
764
793 It is possible to specify an ``ssh://`` URL as the destination, but no
765 It is possible to specify an ``ssh://`` URL as the destination, but no
794 ``.hg/hgrc`` and working directory will be created on the remote side.
766 ``.hg/hgrc`` and working directory will be created on the remote side.
795 Please see :hg:`help urls` for important details about ``ssh://`` URLs.
767 Please see :hg:`help urls` for important details about ``ssh://`` URLs.
796
768
797 A set of changesets (tags, or branch names) to pull may be specified
769 A set of changesets (tags, or branch names) to pull may be specified
798 by listing each changeset (tag, or branch name) with -r/--rev.
770 by listing each changeset (tag, or branch name) with -r/--rev.
799 If -r/--rev is used, the cloned repository will contain only a subset
771 If -r/--rev is used, the cloned repository will contain only a subset
800 of the changesets of the source repository. Only the set of changesets
772 of the changesets of the source repository. Only the set of changesets
801 defined by all -r/--rev options (including all their ancestors)
773 defined by all -r/--rev options (including all their ancestors)
802 will be pulled into the destination repository.
774 will be pulled into the destination repository.
803 No subsequent changesets (including subsequent tags) will be present
775 No subsequent changesets (including subsequent tags) will be present
804 in the destination.
776 in the destination.
805
777
806 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
778 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
807 local source repositories.
779 local source repositories.
808
780
809 For efficiency, hardlinks are used for cloning whenever the source
781 For efficiency, hardlinks are used for cloning whenever the source
810 and destination are on the same filesystem (note this applies only
782 and destination are on the same filesystem (note this applies only
811 to the repository data, not to the working directory). Some
783 to the repository data, not to the working directory). Some
812 filesystems, such as AFS, implement hardlinking incorrectly, but
784 filesystems, such as AFS, implement hardlinking incorrectly, but
813 do not report errors. In these cases, use the --pull option to
785 do not report errors. In these cases, use the --pull option to
814 avoid hardlinking.
786 avoid hardlinking.
815
787
816 In some cases, you can clone repositories and the working directory
788 In some cases, you can clone repositories and the working directory
817 using full hardlinks with ::
789 using full hardlinks with ::
818
790
819 $ cp -al REPO REPOCLONE
791 $ cp -al REPO REPOCLONE
820
792
821 This is the fastest way to clone, but it is not always safe. The
793 This is the fastest way to clone, but it is not always safe. The
822 operation is not atomic (making sure REPO is not modified during
794 operation is not atomic (making sure REPO is not modified during
823 the operation is up to you) and you have to make sure your editor
795 the operation is up to you) and you have to make sure your editor
824 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
796 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
825 this is not compatible with certain extensions that place their
797 this is not compatible with certain extensions that place their
826 metadata under the .hg directory, such as mq.
798 metadata under the .hg directory, such as mq.
827
799
828 Mercurial will update the working directory to the first applicable
800 Mercurial will update the working directory to the first applicable
829 revision from this list:
801 revision from this list:
830
802
831 a) null if -U or the source repository has no changesets
803 a) null if -U or the source repository has no changesets
832 b) if -u . and the source repository is local, the first parent of
804 b) if -u . and the source repository is local, the first parent of
833 the source repository's working directory
805 the source repository's working directory
834 c) the changeset specified with -u (if a branch name, this means the
806 c) the changeset specified with -u (if a branch name, this means the
835 latest head of that branch)
807 latest head of that branch)
836 d) the changeset specified with -r
808 d) the changeset specified with -r
837 e) the tipmost head specified with -b
809 e) the tipmost head specified with -b
838 f) the tipmost head specified with the url#branch source syntax
810 f) the tipmost head specified with the url#branch source syntax
839 g) the tipmost head of the default branch
811 g) the tipmost head of the default branch
840 h) tip
812 h) tip
841
813
842 Returns 0 on success.
814 Returns 0 on success.
843 """
815 """
844 if opts.get('noupdate') and opts.get('updaterev'):
816 if opts.get('noupdate') and opts.get('updaterev'):
845 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
817 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
846
818
847 r = hg.clone(hg.remoteui(ui, opts), source, dest,
819 r = hg.clone(hg.remoteui(ui, opts), source, dest,
848 pull=opts.get('pull'),
820 pull=opts.get('pull'),
849 stream=opts.get('uncompressed'),
821 stream=opts.get('uncompressed'),
850 rev=opts.get('rev'),
822 rev=opts.get('rev'),
851 update=opts.get('updaterev') or not opts.get('noupdate'),
823 update=opts.get('updaterev') or not opts.get('noupdate'),
852 branch=opts.get('branch'))
824 branch=opts.get('branch'))
853
825
854 return r is None
826 return r is None
855
827
856 def commit(ui, repo, *pats, **opts):
828 def commit(ui, repo, *pats, **opts):
857 """commit the specified files or all outstanding changes
829 """commit the specified files or all outstanding changes
858
830
859 Commit changes to the given files into the repository. Unlike a
831 Commit changes to the given files into the repository. Unlike a
860 centralized SCM, this operation is a local operation. See
832 centralized SCM, this operation is a local operation. See
861 :hg:`push` for a way to actively distribute your changes.
833 :hg:`push` for a way to actively distribute your changes.
862
834
863 If a list of files is omitted, all changes reported by :hg:`status`
835 If a list of files is omitted, all changes reported by :hg:`status`
864 will be committed.
836 will be committed.
865
837
866 If you are committing the result of a merge, do not provide any
838 If you are committing the result of a merge, do not provide any
867 filenames or -I/-X filters.
839 filenames or -I/-X filters.
868
840
869 If no commit message is specified, Mercurial starts your
841 If no commit message is specified, Mercurial starts your
870 configured editor where you can enter a message. In case your
842 configured editor where you can enter a message. In case your
871 commit fails, you will find a backup of your message in
843 commit fails, you will find a backup of your message in
872 ``.hg/last-message.txt``.
844 ``.hg/last-message.txt``.
873
845
874 See :hg:`help dates` for a list of formats valid for -d/--date.
846 See :hg:`help dates` for a list of formats valid for -d/--date.
875
847
876 Returns 0 on success, 1 if nothing changed.
848 Returns 0 on success, 1 if nothing changed.
877 """
849 """
878 extra = {}
850 extra = {}
879 if opts.get('close_branch'):
851 if opts.get('close_branch'):
880 if repo['.'].node() not in repo.branchheads():
852 if repo['.'].node() not in repo.branchheads():
881 # The topo heads set is included in the branch heads set of the
853 # The topo heads set is included in the branch heads set of the
882 # current branch, so it's sufficient to test branchheads
854 # current branch, so it's sufficient to test branchheads
883 raise util.Abort(_('can only close branch heads'))
855 raise util.Abort(_('can only close branch heads'))
884 extra['close'] = 1
856 extra['close'] = 1
885 e = cmdutil.commiteditor
857 e = cmdutil.commiteditor
886 if opts.get('force_editor'):
858 if opts.get('force_editor'):
887 e = cmdutil.commitforceeditor
859 e = cmdutil.commitforceeditor
888
860
889 def commitfunc(ui, repo, message, match, opts):
861 def commitfunc(ui, repo, message, match, opts):
890 return repo.commit(message, opts.get('user'), opts.get('date'), match,
862 return repo.commit(message, opts.get('user'), opts.get('date'), match,
891 editor=e, extra=extra)
863 editor=e, extra=extra)
892
864
893 branch = repo[None].branch()
865 branch = repo[None].branch()
894 bheads = repo.branchheads(branch)
866 bheads = repo.branchheads(branch)
895
867
896 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
868 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
897 if not node:
869 if not node:
898 stat = repo.status(match=cmdutil.match(repo, pats, opts))
870 stat = repo.status(match=cmdutil.match(repo, pats, opts))
899 if stat[3]:
871 if stat[3]:
900 ui.status(_("nothing changed (%d missing files, see 'hg status')\n")
872 ui.status(_("nothing changed (%d missing files, see 'hg status')\n")
901 % len(stat[3]))
873 % len(stat[3]))
902 else:
874 else:
903 ui.status(_("nothing changed\n"))
875 ui.status(_("nothing changed\n"))
904 return 1
876 return 1
905
877
906 ctx = repo[node]
878 ctx = repo[node]
907 parents = ctx.parents()
879 parents = ctx.parents()
908
880
909 if bheads and not [x for x in parents
881 if bheads and not [x for x in parents
910 if x.node() in bheads and x.branch() == branch]:
882 if x.node() in bheads and x.branch() == branch]:
911 ui.status(_('created new head\n'))
883 ui.status(_('created new head\n'))
912 # The message is not printed for initial roots. For the other
884 # The message is not printed for initial roots. For the other
913 # changesets, it is printed in the following situations:
885 # changesets, it is printed in the following situations:
914 #
886 #
915 # Par column: for the 2 parents with ...
887 # Par column: for the 2 parents with ...
916 # N: null or no parent
888 # N: null or no parent
917 # B: parent is on another named branch
889 # B: parent is on another named branch
918 # C: parent is a regular non head changeset
890 # C: parent is a regular non head changeset
919 # H: parent was a branch head of the current branch
891 # H: parent was a branch head of the current branch
920 # Msg column: whether we print "created new head" message
892 # Msg column: whether we print "created new head" message
921 # In the following, it is assumed that there already exists some
893 # In the following, it is assumed that there already exists some
922 # initial branch heads of the current branch, otherwise nothing is
894 # initial branch heads of the current branch, otherwise nothing is
923 # printed anyway.
895 # printed anyway.
924 #
896 #
925 # Par Msg Comment
897 # Par Msg Comment
926 # NN y additional topo root
898 # NN y additional topo root
927 #
899 #
928 # BN y additional branch root
900 # BN y additional branch root
929 # CN y additional topo head
901 # CN y additional topo head
930 # HN n usual case
902 # HN n usual case
931 #
903 #
932 # BB y weird additional branch root
904 # BB y weird additional branch root
933 # CB y branch merge
905 # CB y branch merge
934 # HB n merge with named branch
906 # HB n merge with named branch
935 #
907 #
936 # CC y additional head from merge
908 # CC y additional head from merge
937 # CH n merge with a head
909 # CH n merge with a head
938 #
910 #
939 # HH n head merge: head count decreases
911 # HH n head merge: head count decreases
940
912
941 if not opts.get('close_branch'):
913 if not opts.get('close_branch'):
942 for r in parents:
914 for r in parents:
943 if r.extra().get('close') and r.branch() == branch:
915 if r.extra().get('close') and r.branch() == branch:
944 ui.status(_('reopening closed branch head %d\n') % r)
916 ui.status(_('reopening closed branch head %d\n') % r)
945
917
946 if ui.debugflag:
918 if ui.debugflag:
947 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
919 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
948 elif ui.verbose:
920 elif ui.verbose:
949 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
921 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
950
922
951 def copy(ui, repo, *pats, **opts):
923 def copy(ui, repo, *pats, **opts):
952 """mark files as copied for the next commit
924 """mark files as copied for the next commit
953
925
954 Mark dest as having copies of source files. If dest is a
926 Mark dest as having copies of source files. If dest is a
955 directory, copies are put in that directory. If dest is a file,
927 directory, copies are put in that directory. If dest is a file,
956 the source must be a single file.
928 the source must be a single file.
957
929
958 By default, this command copies the contents of files as they
930 By default, this command copies the contents of files as they
959 exist in the working directory. If invoked with -A/--after, the
931 exist in the working directory. If invoked with -A/--after, the
960 operation is recorded, but no copying is performed.
932 operation is recorded, but no copying is performed.
961
933
962 This command takes effect with the next commit. To undo a copy
934 This command takes effect with the next commit. To undo a copy
963 before that, see :hg:`revert`.
935 before that, see :hg:`revert`.
964
936
965 Returns 0 on success, 1 if errors are encountered.
937 Returns 0 on success, 1 if errors are encountered.
966 """
938 """
967 wlock = repo.wlock(False)
939 wlock = repo.wlock(False)
968 try:
940 try:
969 return cmdutil.copy(ui, repo, pats, opts)
941 return cmdutil.copy(ui, repo, pats, opts)
970 finally:
942 finally:
971 wlock.release()
943 wlock.release()
972
944
973 def debugancestor(ui, repo, *args):
945 def debugancestor(ui, repo, *args):
974 """find the ancestor revision of two revisions in a given index"""
946 """find the ancestor revision of two revisions in a given index"""
975 if len(args) == 3:
947 if len(args) == 3:
976 index, rev1, rev2 = args
948 index, rev1, rev2 = args
977 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
949 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
978 lookup = r.lookup
950 lookup = r.lookup
979 elif len(args) == 2:
951 elif len(args) == 2:
980 if not repo:
952 if not repo:
981 raise util.Abort(_("there is no Mercurial repository here "
953 raise util.Abort(_("there is no Mercurial repository here "
982 "(.hg not found)"))
954 "(.hg not found)"))
983 rev1, rev2 = args
955 rev1, rev2 = args
984 r = repo.changelog
956 r = repo.changelog
985 lookup = repo.lookup
957 lookup = repo.lookup
986 else:
958 else:
987 raise util.Abort(_('either two or three arguments required'))
959 raise util.Abort(_('either two or three arguments required'))
988 a = r.ancestor(lookup(rev1), lookup(rev2))
960 a = r.ancestor(lookup(rev1), lookup(rev2))
989 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
961 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
990
962
991 def debugbuilddag(ui, repo, text,
963 def debugbuilddag(ui, repo, text,
992 mergeable_file=False,
964 mergeable_file=False,
993 appended_file=False,
965 appended_file=False,
994 overwritten_file=False,
966 overwritten_file=False,
995 new_file=False):
967 new_file=False):
996 """builds a repo with a given dag from scratch in the current empty repo
968 """builds a repo with a given dag from scratch in the current empty repo
997
969
998 Elements:
970 Elements:
999
971
1000 - "+n" is a linear run of n nodes based on the current default parent
972 - "+n" is a linear run of n nodes based on the current default parent
1001 - "." is a single node based on the current default parent
973 - "." is a single node based on the current default parent
1002 - "$" resets the default parent to null (implied at the start);
974 - "$" resets the default parent to null (implied at the start);
1003 otherwise the default parent is always the last node created
975 otherwise the default parent is always the last node created
1004 - "<p" sets the default parent to the backref p
976 - "<p" sets the default parent to the backref p
1005 - "*p" is a fork at parent p, which is a backref
977 - "*p" is a fork at parent p, which is a backref
1006 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
978 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1007 - "/p2" is a merge of the preceding node and p2
979 - "/p2" is a merge of the preceding node and p2
1008 - ":tag" defines a local tag for the preceding node
980 - ":tag" defines a local tag for the preceding node
1009 - "@branch" sets the named branch for subsequent nodes
981 - "@branch" sets the named branch for subsequent nodes
1010 - "!command" runs the command using your shell
982 - "!command" runs the command using your shell
1011 - "!!my command\\n" is like "!", but to the end of the line
983 - "!!my command\\n" is like "!", but to the end of the line
1012 - "#...\\n" is a comment up to the end of the line
984 - "#...\\n" is a comment up to the end of the line
1013
985
1014 Whitespace between the above elements is ignored.
986 Whitespace between the above elements is ignored.
1015
987
1016 A backref is either
988 A backref is either
1017
989
1018 - a number n, which references the node curr-n, where curr is the current
990 - a number n, which references the node curr-n, where curr is the current
1019 node, or
991 node, or
1020 - the name of a local tag you placed earlier using ":tag", or
992 - the name of a local tag you placed earlier using ":tag", or
1021 - empty to denote the default parent.
993 - empty to denote the default parent.
1022
994
1023 All string valued-elements are either strictly alphanumeric, or must
995 All string valued-elements are either strictly alphanumeric, or must
1024 be enclosed in double quotes ("..."), with "\\" as escape character.
996 be enclosed in double quotes ("..."), with "\\" as escape character.
1025
997
1026 Note that the --overwritten-file and --appended-file options imply the
998 Note that the --overwritten-file and --appended-file options imply the
1027 use of "HGMERGE=internal:local" during DAG buildup.
999 use of "HGMERGE=internal:local" during DAG buildup.
1028 """
1000 """
1029
1001
1030 if not (mergeable_file or appended_file or overwritten_file or new_file):
1002 if not (mergeable_file or appended_file or overwritten_file or new_file):
1031 raise util.Abort(_('need at least one of -m, -a, -o, -n'))
1003 raise util.Abort(_('need at least one of -m, -a, -o, -n'))
1032
1004
1033 if len(repo.changelog) > 0:
1005 if len(repo.changelog) > 0:
1034 raise util.Abort(_('repository is not empty'))
1006 raise util.Abort(_('repository is not empty'))
1035
1007
1036 if overwritten_file or appended_file:
1008 if overwritten_file or appended_file:
1037 # we don't want to fail in merges during buildup
1009 # we don't want to fail in merges during buildup
1038 os.environ['HGMERGE'] = 'internal:local'
1010 os.environ['HGMERGE'] = 'internal:local'
1039
1011
1040 def writefile(fname, text, fmode="wb"):
1012 def writefile(fname, text, fmode="wb"):
1041 f = open(fname, fmode)
1013 f = open(fname, fmode)
1042 try:
1014 try:
1043 f.write(text)
1015 f.write(text)
1044 finally:
1016 finally:
1045 f.close()
1017 f.close()
1046
1018
1047 if mergeable_file:
1019 if mergeable_file:
1048 linesperrev = 2
1020 linesperrev = 2
1049 # determine number of revs in DAG
1021 # determine number of revs in DAG
1050 n = 0
1022 n = 0
1051 for type, data in dagparser.parsedag(text):
1023 for type, data in dagparser.parsedag(text):
1052 if type == 'n':
1024 if type == 'n':
1053 n += 1
1025 n += 1
1054 # make a file with k lines per rev
1026 # make a file with k lines per rev
1055 writefile("mf", "\n".join(str(i) for i in xrange(0, n * linesperrev))
1027 writefile("mf", "\n".join(str(i) for i in xrange(0, n * linesperrev))
1056 + "\n")
1028 + "\n")
1057
1029
1058 at = -1
1030 at = -1
1059 atbranch = 'default'
1031 atbranch = 'default'
1060 for type, data in dagparser.parsedag(text):
1032 for type, data in dagparser.parsedag(text):
1061 if type == 'n':
1033 if type == 'n':
1062 ui.status('node %s\n' % str(data))
1034 ui.status('node %s\n' % str(data))
1063 id, ps = data
1035 id, ps = data
1064 p1 = ps[0]
1036 p1 = ps[0]
1065 if p1 != at:
1037 if p1 != at:
1066 update(ui, repo, node=str(p1), clean=True)
1038 update(ui, repo, node=str(p1), clean=True)
1067 at = p1
1039 at = p1
1068 if repo.dirstate.branch() != atbranch:
1040 if repo.dirstate.branch() != atbranch:
1069 branch(ui, repo, atbranch, force=True)
1041 branch(ui, repo, atbranch, force=True)
1070 if len(ps) > 1:
1042 if len(ps) > 1:
1071 p2 = ps[1]
1043 p2 = ps[1]
1072 merge(ui, repo, node=p2)
1044 merge(ui, repo, node=p2)
1073
1045
1074 if mergeable_file:
1046 if mergeable_file:
1075 f = open("mf", "rb+")
1047 f = open("mf", "rb+")
1076 try:
1048 try:
1077 lines = f.read().split("\n")
1049 lines = f.read().split("\n")
1078 lines[id * linesperrev] += " r%i" % id
1050 lines[id * linesperrev] += " r%i" % id
1079 f.seek(0)
1051 f.seek(0)
1080 f.write("\n".join(lines))
1052 f.write("\n".join(lines))
1081 finally:
1053 finally:
1082 f.close()
1054 f.close()
1083
1055
1084 if appended_file:
1056 if appended_file:
1085 writefile("af", "r%i\n" % id, "ab")
1057 writefile("af", "r%i\n" % id, "ab")
1086
1058
1087 if overwritten_file:
1059 if overwritten_file:
1088 writefile("of", "r%i\n" % id)
1060 writefile("of", "r%i\n" % id)
1089
1061
1090 if new_file:
1062 if new_file:
1091 writefile("nf%i" % id, "r%i\n" % id)
1063 writefile("nf%i" % id, "r%i\n" % id)
1092
1064
1093 commit(ui, repo, addremove=True, message="r%i" % id, date=(id, 0))
1065 commit(ui, repo, addremove=True, message="r%i" % id, date=(id, 0))
1094 at = id
1066 at = id
1095 elif type == 'l':
1067 elif type == 'l':
1096 id, name = data
1068 id, name = data
1097 ui.status('tag %s\n' % name)
1069 ui.status('tag %s\n' % name)
1098 tag(ui, repo, name, local=True)
1070 tag(ui, repo, name, local=True)
1099 elif type == 'a':
1071 elif type == 'a':
1100 ui.status('branch %s\n' % data)
1072 ui.status('branch %s\n' % data)
1101 atbranch = data
1073 atbranch = data
1102 elif type in 'cC':
1074 elif type in 'cC':
1103 r = util.system(data, cwd=repo.root)
1075 r = util.system(data, cwd=repo.root)
1104 if r:
1076 if r:
1105 desc, r = util.explain_exit(r)
1077 desc, r = util.explain_exit(r)
1106 raise util.Abort(_('%s command %s') % (data, desc))
1078 raise util.Abort(_('%s command %s') % (data, desc))
1107
1079
1108 def debugcommands(ui, cmd='', *args):
1080 def debugcommands(ui, cmd='', *args):
1109 """list all available commands and options"""
1081 """list all available commands and options"""
1110 for cmd, vals in sorted(table.iteritems()):
1082 for cmd, vals in sorted(table.iteritems()):
1111 cmd = cmd.split('|')[0].strip('^')
1083 cmd = cmd.split('|')[0].strip('^')
1112 opts = ', '.join([i[1] for i in vals[1]])
1084 opts = ', '.join([i[1] for i in vals[1]])
1113 ui.write('%s: %s\n' % (cmd, opts))
1085 ui.write('%s: %s\n' % (cmd, opts))
1114
1086
1115 def debugcomplete(ui, cmd='', **opts):
1087 def debugcomplete(ui, cmd='', **opts):
1116 """returns the completion list associated with the given command"""
1088 """returns the completion list associated with the given command"""
1117
1089
1118 if opts.get('options'):
1090 if opts.get('options'):
1119 options = []
1091 options = []
1120 otables = [globalopts]
1092 otables = [globalopts]
1121 if cmd:
1093 if cmd:
1122 aliases, entry = cmdutil.findcmd(cmd, table, False)
1094 aliases, entry = cmdutil.findcmd(cmd, table, False)
1123 otables.append(entry[1])
1095 otables.append(entry[1])
1124 for t in otables:
1096 for t in otables:
1125 for o in t:
1097 for o in t:
1126 if "(DEPRECATED)" in o[3]:
1098 if "(DEPRECATED)" in o[3]:
1127 continue
1099 continue
1128 if o[0]:
1100 if o[0]:
1129 options.append('-%s' % o[0])
1101 options.append('-%s' % o[0])
1130 options.append('--%s' % o[1])
1102 options.append('--%s' % o[1])
1131 ui.write("%s\n" % "\n".join(options))
1103 ui.write("%s\n" % "\n".join(options))
1132 return
1104 return
1133
1105
1134 cmdlist = cmdutil.findpossible(cmd, table)
1106 cmdlist = cmdutil.findpossible(cmd, table)
1135 if ui.verbose:
1107 if ui.verbose:
1136 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1108 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1137 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1109 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1138
1110
1139 def debugfsinfo(ui, path = "."):
1111 def debugfsinfo(ui, path = "."):
1140 """show information detected about current filesystem"""
1112 """show information detected about current filesystem"""
1141 open('.debugfsinfo', 'w').write('')
1113 open('.debugfsinfo', 'w').write('')
1142 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1114 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1143 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1115 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1144 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1116 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1145 and 'yes' or 'no'))
1117 and 'yes' or 'no'))
1146 os.unlink('.debugfsinfo')
1118 os.unlink('.debugfsinfo')
1147
1119
1148 def debugrebuildstate(ui, repo, rev="tip"):
1120 def debugrebuildstate(ui, repo, rev="tip"):
1149 """rebuild the dirstate as it would look like for the given revision"""
1121 """rebuild the dirstate as it would look like for the given revision"""
1150 ctx = cmdutil.revsingle(repo, rev)
1122 ctx = cmdutil.revsingle(repo, rev)
1151 wlock = repo.wlock()
1123 wlock = repo.wlock()
1152 try:
1124 try:
1153 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1125 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1154 finally:
1126 finally:
1155 wlock.release()
1127 wlock.release()
1156
1128
1157 def debugcheckstate(ui, repo):
1129 def debugcheckstate(ui, repo):
1158 """validate the correctness of the current dirstate"""
1130 """validate the correctness of the current dirstate"""
1159 parent1, parent2 = repo.dirstate.parents()
1131 parent1, parent2 = repo.dirstate.parents()
1160 m1 = repo[parent1].manifest()
1132 m1 = repo[parent1].manifest()
1161 m2 = repo[parent2].manifest()
1133 m2 = repo[parent2].manifest()
1162 errors = 0
1134 errors = 0
1163 for f in repo.dirstate:
1135 for f in repo.dirstate:
1164 state = repo.dirstate[f]
1136 state = repo.dirstate[f]
1165 if state in "nr" and f not in m1:
1137 if state in "nr" and f not in m1:
1166 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1138 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1167 errors += 1
1139 errors += 1
1168 if state in "a" and f in m1:
1140 if state in "a" and f in m1:
1169 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1141 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1170 errors += 1
1142 errors += 1
1171 if state in "m" and f not in m1 and f not in m2:
1143 if state in "m" and f not in m1 and f not in m2:
1172 ui.warn(_("%s in state %s, but not in either manifest\n") %
1144 ui.warn(_("%s in state %s, but not in either manifest\n") %
1173 (f, state))
1145 (f, state))
1174 errors += 1
1146 errors += 1
1175 for f in m1:
1147 for f in m1:
1176 state = repo.dirstate[f]
1148 state = repo.dirstate[f]
1177 if state not in "nrm":
1149 if state not in "nrm":
1178 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1150 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1179 errors += 1
1151 errors += 1
1180 if errors:
1152 if errors:
1181 error = _(".hg/dirstate inconsistent with current parent's manifest")
1153 error = _(".hg/dirstate inconsistent with current parent's manifest")
1182 raise util.Abort(error)
1154 raise util.Abort(error)
1183
1155
1184 def showconfig(ui, repo, *values, **opts):
1156 def showconfig(ui, repo, *values, **opts):
1185 """show combined config settings from all hgrc files
1157 """show combined config settings from all hgrc files
1186
1158
1187 With no arguments, print names and values of all config items.
1159 With no arguments, print names and values of all config items.
1188
1160
1189 With one argument of the form section.name, print just the value
1161 With one argument of the form section.name, print just the value
1190 of that config item.
1162 of that config item.
1191
1163
1192 With multiple arguments, print names and values of all config
1164 With multiple arguments, print names and values of all config
1193 items with matching section names.
1165 items with matching section names.
1194
1166
1195 With --debug, the source (filename and line number) is printed
1167 With --debug, the source (filename and line number) is printed
1196 for each config item.
1168 for each config item.
1197
1169
1198 Returns 0 on success.
1170 Returns 0 on success.
1199 """
1171 """
1200
1172
1201 for f in scmutil.rcpath():
1173 for f in scmutil.rcpath():
1202 ui.debug(_('read config from: %s\n') % f)
1174 ui.debug(_('read config from: %s\n') % f)
1203 untrusted = bool(opts.get('untrusted'))
1175 untrusted = bool(opts.get('untrusted'))
1204 if values:
1176 if values:
1205 sections = [v for v in values if '.' not in v]
1177 sections = [v for v in values if '.' not in v]
1206 items = [v for v in values if '.' in v]
1178 items = [v for v in values if '.' in v]
1207 if len(items) > 1 or items and sections:
1179 if len(items) > 1 or items and sections:
1208 raise util.Abort(_('only one config item permitted'))
1180 raise util.Abort(_('only one config item permitted'))
1209 for section, name, value in ui.walkconfig(untrusted=untrusted):
1181 for section, name, value in ui.walkconfig(untrusted=untrusted):
1210 value = str(value).replace('\n', '\\n')
1182 value = str(value).replace('\n', '\\n')
1211 sectname = section + '.' + name
1183 sectname = section + '.' + name
1212 if values:
1184 if values:
1213 for v in values:
1185 for v in values:
1214 if v == section:
1186 if v == section:
1215 ui.debug('%s: ' %
1187 ui.debug('%s: ' %
1216 ui.configsource(section, name, untrusted))
1188 ui.configsource(section, name, untrusted))
1217 ui.write('%s=%s\n' % (sectname, value))
1189 ui.write('%s=%s\n' % (sectname, value))
1218 elif v == sectname:
1190 elif v == sectname:
1219 ui.debug('%s: ' %
1191 ui.debug('%s: ' %
1220 ui.configsource(section, name, untrusted))
1192 ui.configsource(section, name, untrusted))
1221 ui.write(value, '\n')
1193 ui.write(value, '\n')
1222 else:
1194 else:
1223 ui.debug('%s: ' %
1195 ui.debug('%s: ' %
1224 ui.configsource(section, name, untrusted))
1196 ui.configsource(section, name, untrusted))
1225 ui.write('%s=%s\n' % (sectname, value))
1197 ui.write('%s=%s\n' % (sectname, value))
1226
1198
1227 def debugknown(ui, repopath, *ids, **opts):
1199 def debugknown(ui, repopath, *ids, **opts):
1228 """test whether node ids are known to a repo
1200 """test whether node ids are known to a repo
1229
1201
1230 Every ID must be a full-length hex node id string. Returns a list of 0s and 1s
1202 Every ID must be a full-length hex node id string. Returns a list of 0s and 1s
1231 indicating unknown/known.
1203 indicating unknown/known.
1232 """
1204 """
1233 repo = hg.repository(ui, repopath)
1205 repo = hg.repository(ui, repopath)
1234 if not repo.capable('known'):
1206 if not repo.capable('known'):
1235 raise util.Abort("known() not supported by target repository")
1207 raise util.Abort("known() not supported by target repository")
1236 flags = repo.known([bin(s) for s in ids])
1208 flags = repo.known([bin(s) for s in ids])
1237 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
1209 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
1238
1210
1239 def debugbundle(ui, bundlepath, all=None, **opts):
1211 def debugbundle(ui, bundlepath, all=None, **opts):
1240 """lists the contents of a bundle"""
1212 """lists the contents of a bundle"""
1241 f = url.open(ui, bundlepath)
1213 f = url.open(ui, bundlepath)
1242 try:
1214 try:
1243 gen = changegroup.readbundle(f, bundlepath)
1215 gen = changegroup.readbundle(f, bundlepath)
1244 if all:
1216 if all:
1245 ui.write("format: id, p1, p2, cset, len(delta)\n")
1217 ui.write("format: id, p1, p2, cset, len(delta)\n")
1246
1218
1247 def showchunks(named):
1219 def showchunks(named):
1248 ui.write("\n%s\n" % named)
1220 ui.write("\n%s\n" % named)
1249 while 1:
1221 while 1:
1250 chunkdata = gen.parsechunk()
1222 chunkdata = gen.parsechunk()
1251 if not chunkdata:
1223 if not chunkdata:
1252 break
1224 break
1253 node = chunkdata['node']
1225 node = chunkdata['node']
1254 p1 = chunkdata['p1']
1226 p1 = chunkdata['p1']
1255 p2 = chunkdata['p2']
1227 p2 = chunkdata['p2']
1256 cs = chunkdata['cs']
1228 cs = chunkdata['cs']
1257 delta = chunkdata['data']
1229 delta = chunkdata['data']
1258 ui.write("%s %s %s %s %s\n" %
1230 ui.write("%s %s %s %s %s\n" %
1259 (hex(node), hex(p1), hex(p2),
1231 (hex(node), hex(p1), hex(p2),
1260 hex(cs), len(delta)))
1232 hex(cs), len(delta)))
1261
1233
1262 showchunks("changelog")
1234 showchunks("changelog")
1263 showchunks("manifest")
1235 showchunks("manifest")
1264 while 1:
1236 while 1:
1265 fname = gen.chunk()
1237 fname = gen.chunk()
1266 if not fname:
1238 if not fname:
1267 break
1239 break
1268 showchunks(fname)
1240 showchunks(fname)
1269 else:
1241 else:
1270 while 1:
1242 while 1:
1271 chunkdata = gen.parsechunk()
1243 chunkdata = gen.parsechunk()
1272 if not chunkdata:
1244 if not chunkdata:
1273 break
1245 break
1274 node = chunkdata['node']
1246 node = chunkdata['node']
1275 ui.write("%s\n" % hex(node))
1247 ui.write("%s\n" % hex(node))
1276 finally:
1248 finally:
1277 f.close()
1249 f.close()
1278
1250
1279 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
1251 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
1280 """retrieves a bundle from a repo
1252 """retrieves a bundle from a repo
1281
1253
1282 Every ID must be a full-length hex node id string. Saves the bundle to the
1254 Every ID must be a full-length hex node id string. Saves the bundle to the
1283 given file.
1255 given file.
1284 """
1256 """
1285 repo = hg.repository(ui, repopath)
1257 repo = hg.repository(ui, repopath)
1286 if not repo.capable('getbundle'):
1258 if not repo.capable('getbundle'):
1287 raise util.Abort("getbundle() not supported by target repository")
1259 raise util.Abort("getbundle() not supported by target repository")
1288 args = {}
1260 args = {}
1289 if common:
1261 if common:
1290 args['common'] = [bin(s) for s in common]
1262 args['common'] = [bin(s) for s in common]
1291 if head:
1263 if head:
1292 args['heads'] = [bin(s) for s in head]
1264 args['heads'] = [bin(s) for s in head]
1293 bundle = repo.getbundle('debug', **args)
1265 bundle = repo.getbundle('debug', **args)
1294
1266
1295 bundletype = opts.get('type', 'bzip2').lower()
1267 bundletype = opts.get('type', 'bzip2').lower()
1296 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1268 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1297 bundletype = btypes.get(bundletype)
1269 bundletype = btypes.get(bundletype)
1298 if bundletype not in changegroup.bundletypes:
1270 if bundletype not in changegroup.bundletypes:
1299 raise util.Abort(_('unknown bundle type specified with --type'))
1271 raise util.Abort(_('unknown bundle type specified with --type'))
1300 changegroup.writebundle(bundle, bundlepath, bundletype)
1272 changegroup.writebundle(bundle, bundlepath, bundletype)
1301
1273
1302 def debugpushkey(ui, repopath, namespace, *keyinfo):
1274 def debugpushkey(ui, repopath, namespace, *keyinfo):
1303 '''access the pushkey key/value protocol
1275 '''access the pushkey key/value protocol
1304
1276
1305 With two args, list the keys in the given namespace.
1277 With two args, list the keys in the given namespace.
1306
1278
1307 With five args, set a key to new if it currently is set to old.
1279 With five args, set a key to new if it currently is set to old.
1308 Reports success or failure.
1280 Reports success or failure.
1309 '''
1281 '''
1310
1282
1311 target = hg.repository(ui, repopath)
1283 target = hg.repository(ui, repopath)
1312 if keyinfo:
1284 if keyinfo:
1313 key, old, new = keyinfo
1285 key, old, new = keyinfo
1314 r = target.pushkey(namespace, key, old, new)
1286 r = target.pushkey(namespace, key, old, new)
1315 ui.status(str(r) + '\n')
1287 ui.status(str(r) + '\n')
1316 return not r
1288 return not r
1317 else:
1289 else:
1318 for k, v in target.listkeys(namespace).iteritems():
1290 for k, v in target.listkeys(namespace).iteritems():
1319 ui.write("%s\t%s\n" % (k.encode('string-escape'),
1291 ui.write("%s\t%s\n" % (k.encode('string-escape'),
1320 v.encode('string-escape')))
1292 v.encode('string-escape')))
1321
1293
1322 def debugrevspec(ui, repo, expr):
1294 def debugrevspec(ui, repo, expr):
1323 '''parse and apply a revision specification'''
1295 '''parse and apply a revision specification'''
1324 if ui.verbose:
1296 if ui.verbose:
1325 tree = revset.parse(expr)[0]
1297 tree = revset.parse(expr)[0]
1326 ui.note(tree, "\n")
1298 ui.note(tree, "\n")
1327 func = revset.match(expr)
1299 func = revset.match(expr)
1328 for c in func(repo, range(len(repo))):
1300 for c in func(repo, range(len(repo))):
1329 ui.write("%s\n" % c)
1301 ui.write("%s\n" % c)
1330
1302
1331 def debugsetparents(ui, repo, rev1, rev2=None):
1303 def debugsetparents(ui, repo, rev1, rev2=None):
1332 """manually set the parents of the current working directory
1304 """manually set the parents of the current working directory
1333
1305
1334 This is useful for writing repository conversion tools, but should
1306 This is useful for writing repository conversion tools, but should
1335 be used with care.
1307 be used with care.
1336
1308
1337 Returns 0 on success.
1309 Returns 0 on success.
1338 """
1310 """
1339
1311
1340 r1 = cmdutil.revsingle(repo, rev1).node()
1312 r1 = cmdutil.revsingle(repo, rev1).node()
1341 r2 = cmdutil.revsingle(repo, rev2, 'null').node()
1313 r2 = cmdutil.revsingle(repo, rev2, 'null').node()
1342
1314
1343 wlock = repo.wlock()
1315 wlock = repo.wlock()
1344 try:
1316 try:
1345 repo.dirstate.setparents(r1, r2)
1317 repo.dirstate.setparents(r1, r2)
1346 finally:
1318 finally:
1347 wlock.release()
1319 wlock.release()
1348
1320
1349 def debugstate(ui, repo, nodates=None, datesort=None):
1321 def debugstate(ui, repo, nodates=None, datesort=None):
1350 """show the contents of the current dirstate"""
1322 """show the contents of the current dirstate"""
1351 timestr = ""
1323 timestr = ""
1352 showdate = not nodates
1324 showdate = not nodates
1353 if datesort:
1325 if datesort:
1354 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
1326 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
1355 else:
1327 else:
1356 keyfunc = None # sort by filename
1328 keyfunc = None # sort by filename
1357 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
1329 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
1358 if showdate:
1330 if showdate:
1359 if ent[3] == -1:
1331 if ent[3] == -1:
1360 # Pad or slice to locale representation
1332 # Pad or slice to locale representation
1361 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
1333 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
1362 time.localtime(0)))
1334 time.localtime(0)))
1363 timestr = 'unset'
1335 timestr = 'unset'
1364 timestr = (timestr[:locale_len] +
1336 timestr = (timestr[:locale_len] +
1365 ' ' * (locale_len - len(timestr)))
1337 ' ' * (locale_len - len(timestr)))
1366 else:
1338 else:
1367 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
1339 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
1368 time.localtime(ent[3]))
1340 time.localtime(ent[3]))
1369 if ent[1] & 020000:
1341 if ent[1] & 020000:
1370 mode = 'lnk'
1342 mode = 'lnk'
1371 else:
1343 else:
1372 mode = '%3o' % (ent[1] & 0777)
1344 mode = '%3o' % (ent[1] & 0777)
1373 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
1345 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
1374 for f in repo.dirstate.copies():
1346 for f in repo.dirstate.copies():
1375 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
1347 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
1376
1348
1377 def debugsub(ui, repo, rev=None):
1349 def debugsub(ui, repo, rev=None):
1378 ctx = cmdutil.revsingle(repo, rev, None)
1350 ctx = cmdutil.revsingle(repo, rev, None)
1379 for k, v in sorted(ctx.substate.items()):
1351 for k, v in sorted(ctx.substate.items()):
1380 ui.write('path %s\n' % k)
1352 ui.write('path %s\n' % k)
1381 ui.write(' source %s\n' % v[0])
1353 ui.write(' source %s\n' % v[0])
1382 ui.write(' revision %s\n' % v[1])
1354 ui.write(' revision %s\n' % v[1])
1383
1355
1384 def debugdag(ui, repo, file_=None, *revs, **opts):
1356 def debugdag(ui, repo, file_=None, *revs, **opts):
1385 """format the changelog or an index DAG as a concise textual description
1357 """format the changelog or an index DAG as a concise textual description
1386
1358
1387 If you pass a revlog index, the revlog's DAG is emitted. If you list
1359 If you pass a revlog index, the revlog's DAG is emitted. If you list
1388 revision numbers, they get labelled in the output as rN.
1360 revision numbers, they get labelled in the output as rN.
1389
1361
1390 Otherwise, the changelog DAG of the current repo is emitted.
1362 Otherwise, the changelog DAG of the current repo is emitted.
1391 """
1363 """
1392 spaces = opts.get('spaces')
1364 spaces = opts.get('spaces')
1393 dots = opts.get('dots')
1365 dots = opts.get('dots')
1394 if file_:
1366 if file_:
1395 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1367 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1396 revs = set((int(r) for r in revs))
1368 revs = set((int(r) for r in revs))
1397 def events():
1369 def events():
1398 for r in rlog:
1370 for r in rlog:
1399 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1371 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1400 if r in revs:
1372 if r in revs:
1401 yield 'l', (r, "r%i" % r)
1373 yield 'l', (r, "r%i" % r)
1402 elif repo:
1374 elif repo:
1403 cl = repo.changelog
1375 cl = repo.changelog
1404 tags = opts.get('tags')
1376 tags = opts.get('tags')
1405 branches = opts.get('branches')
1377 branches = opts.get('branches')
1406 if tags:
1378 if tags:
1407 labels = {}
1379 labels = {}
1408 for l, n in repo.tags().items():
1380 for l, n in repo.tags().items():
1409 labels.setdefault(cl.rev(n), []).append(l)
1381 labels.setdefault(cl.rev(n), []).append(l)
1410 def events():
1382 def events():
1411 b = "default"
1383 b = "default"
1412 for r in cl:
1384 for r in cl:
1413 if branches:
1385 if branches:
1414 newb = cl.read(cl.node(r))[5]['branch']
1386 newb = cl.read(cl.node(r))[5]['branch']
1415 if newb != b:
1387 if newb != b:
1416 yield 'a', newb
1388 yield 'a', newb
1417 b = newb
1389 b = newb
1418 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1390 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1419 if tags:
1391 if tags:
1420 ls = labels.get(r)
1392 ls = labels.get(r)
1421 if ls:
1393 if ls:
1422 for l in ls:
1394 for l in ls:
1423 yield 'l', (r, l)
1395 yield 'l', (r, l)
1424 else:
1396 else:
1425 raise util.Abort(_('need repo for changelog dag'))
1397 raise util.Abort(_('need repo for changelog dag'))
1426
1398
1427 for line in dagparser.dagtextlines(events(),
1399 for line in dagparser.dagtextlines(events(),
1428 addspaces=spaces,
1400 addspaces=spaces,
1429 wraplabels=True,
1401 wraplabels=True,
1430 wrapannotations=True,
1402 wrapannotations=True,
1431 wrapnonlinear=dots,
1403 wrapnonlinear=dots,
1432 usedots=dots,
1404 usedots=dots,
1433 maxlinewidth=70):
1405 maxlinewidth=70):
1434 ui.write(line)
1406 ui.write(line)
1435 ui.write("\n")
1407 ui.write("\n")
1436
1408
1437 def debugdata(ui, repo, file_, rev):
1409 def debugdata(ui, repo, file_, rev):
1438 """dump the contents of a data file revision"""
1410 """dump the contents of a data file revision"""
1439 r = None
1411 r = None
1440 if repo:
1412 if repo:
1441 filelog = repo.file(file_)
1413 filelog = repo.file(file_)
1442 if len(filelog):
1414 if len(filelog):
1443 r = filelog
1415 r = filelog
1444 if not r:
1416 if not r:
1445 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
1417 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
1446 file_[:-2] + ".i")
1418 file_[:-2] + ".i")
1447 try:
1419 try:
1448 ui.write(r.revision(r.lookup(rev)))
1420 ui.write(r.revision(r.lookup(rev)))
1449 except KeyError:
1421 except KeyError:
1450 raise util.Abort(_('invalid revision identifier %s') % rev)
1422 raise util.Abort(_('invalid revision identifier %s') % rev)
1451
1423
1452 def debugdate(ui, date, range=None, **opts):
1424 def debugdate(ui, date, range=None, **opts):
1453 """parse and display a date"""
1425 """parse and display a date"""
1454 if opts["extended"]:
1426 if opts["extended"]:
1455 d = util.parsedate(date, util.extendeddateformats)
1427 d = util.parsedate(date, util.extendeddateformats)
1456 else:
1428 else:
1457 d = util.parsedate(date)
1429 d = util.parsedate(date)
1458 ui.write("internal: %s %s\n" % d)
1430 ui.write("internal: %s %s\n" % d)
1459 ui.write("standard: %s\n" % util.datestr(d))
1431 ui.write("standard: %s\n" % util.datestr(d))
1460 if range:
1432 if range:
1461 m = util.matchdate(range)
1433 m = util.matchdate(range)
1462 ui.write("match: %s\n" % m(d[0]))
1434 ui.write("match: %s\n" % m(d[0]))
1463
1435
1464 def debugignore(ui, repo, *values, **opts):
1436 def debugignore(ui, repo, *values, **opts):
1465 """display the combined ignore pattern"""
1437 """display the combined ignore pattern"""
1466 ignore = repo.dirstate._ignore
1438 ignore = repo.dirstate._ignore
1467 if hasattr(ignore, 'includepat'):
1439 if hasattr(ignore, 'includepat'):
1468 ui.write("%s\n" % ignore.includepat)
1440 ui.write("%s\n" % ignore.includepat)
1469 else:
1441 else:
1470 raise util.Abort(_("no ignore patterns found"))
1442 raise util.Abort(_("no ignore patterns found"))
1471
1443
1472 def debugindex(ui, repo, file_, **opts):
1444 def debugindex(ui, repo, file_, **opts):
1473 """dump the contents of an index file"""
1445 """dump the contents of an index file"""
1474 r = None
1446 r = None
1475 if repo:
1447 if repo:
1476 filelog = repo.file(file_)
1448 filelog = repo.file(file_)
1477 if len(filelog):
1449 if len(filelog):
1478 r = filelog
1450 r = filelog
1479
1451
1480 format = opts.get('format', 0)
1452 format = opts.get('format', 0)
1481 if format not in (0, 1):
1453 if format not in (0, 1):
1482 raise util.Abort(_("unknown format %d") % format)
1454 raise util.Abort(_("unknown format %d") % format)
1483
1455
1484 if not r:
1456 if not r:
1485 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1457 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1486
1458
1487 if format == 0:
1459 if format == 0:
1488 ui.write(" rev offset length base linkrev"
1460 ui.write(" rev offset length base linkrev"
1489 " nodeid p1 p2\n")
1461 " nodeid p1 p2\n")
1490 elif format == 1:
1462 elif format == 1:
1491 ui.write(" rev flag offset length"
1463 ui.write(" rev flag offset length"
1492 " size base link p1 p2 nodeid\n")
1464 " size base link p1 p2 nodeid\n")
1493
1465
1494 for i in r:
1466 for i in r:
1495 node = r.node(i)
1467 node = r.node(i)
1496 if format == 0:
1468 if format == 0:
1497 try:
1469 try:
1498 pp = r.parents(node)
1470 pp = r.parents(node)
1499 except:
1471 except:
1500 pp = [nullid, nullid]
1472 pp = [nullid, nullid]
1501 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1473 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1502 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
1474 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
1503 short(node), short(pp[0]), short(pp[1])))
1475 short(node), short(pp[0]), short(pp[1])))
1504 elif format == 1:
1476 elif format == 1:
1505 pr = r.parentrevs(i)
1477 pr = r.parentrevs(i)
1506 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1478 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1507 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1479 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1508 r.base(i), r.linkrev(i), pr[0], pr[1], short(node)))
1480 r.base(i), r.linkrev(i), pr[0], pr[1], short(node)))
1509
1481
1510 def debugindexdot(ui, repo, file_):
1482 def debugindexdot(ui, repo, file_):
1511 """dump an index DAG as a graphviz dot file"""
1483 """dump an index DAG as a graphviz dot file"""
1512 r = None
1484 r = None
1513 if repo:
1485 if repo:
1514 filelog = repo.file(file_)
1486 filelog = repo.file(file_)
1515 if len(filelog):
1487 if len(filelog):
1516 r = filelog
1488 r = filelog
1517 if not r:
1489 if not r:
1518 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1490 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1519 ui.write("digraph G {\n")
1491 ui.write("digraph G {\n")
1520 for i in r:
1492 for i in r:
1521 node = r.node(i)
1493 node = r.node(i)
1522 pp = r.parents(node)
1494 pp = r.parents(node)
1523 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1495 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1524 if pp[1] != nullid:
1496 if pp[1] != nullid:
1525 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1497 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1526 ui.write("}\n")
1498 ui.write("}\n")
1527
1499
1528 def debuginstall(ui):
1500 def debuginstall(ui):
1529 '''test Mercurial installation
1501 '''test Mercurial installation
1530
1502
1531 Returns 0 on success.
1503 Returns 0 on success.
1532 '''
1504 '''
1533
1505
1534 def writetemp(contents):
1506 def writetemp(contents):
1535 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1507 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1536 f = os.fdopen(fd, "wb")
1508 f = os.fdopen(fd, "wb")
1537 f.write(contents)
1509 f.write(contents)
1538 f.close()
1510 f.close()
1539 return name
1511 return name
1540
1512
1541 problems = 0
1513 problems = 0
1542
1514
1543 # encoding
1515 # encoding
1544 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1516 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1545 try:
1517 try:
1546 encoding.fromlocal("test")
1518 encoding.fromlocal("test")
1547 except util.Abort, inst:
1519 except util.Abort, inst:
1548 ui.write(" %s\n" % inst)
1520 ui.write(" %s\n" % inst)
1549 ui.write(_(" (check that your locale is properly set)\n"))
1521 ui.write(_(" (check that your locale is properly set)\n"))
1550 problems += 1
1522 problems += 1
1551
1523
1552 # compiled modules
1524 # compiled modules
1553 ui.status(_("Checking installed modules (%s)...\n")
1525 ui.status(_("Checking installed modules (%s)...\n")
1554 % os.path.dirname(__file__))
1526 % os.path.dirname(__file__))
1555 try:
1527 try:
1556 import bdiff, mpatch, base85, osutil
1528 import bdiff, mpatch, base85, osutil
1557 except Exception, inst:
1529 except Exception, inst:
1558 ui.write(" %s\n" % inst)
1530 ui.write(" %s\n" % inst)
1559 ui.write(_(" One or more extensions could not be found"))
1531 ui.write(_(" One or more extensions could not be found"))
1560 ui.write(_(" (check that you compiled the extensions)\n"))
1532 ui.write(_(" (check that you compiled the extensions)\n"))
1561 problems += 1
1533 problems += 1
1562
1534
1563 # templates
1535 # templates
1564 ui.status(_("Checking templates...\n"))
1536 ui.status(_("Checking templates...\n"))
1565 try:
1537 try:
1566 import templater
1538 import templater
1567 templater.templater(templater.templatepath("map-cmdline.default"))
1539 templater.templater(templater.templatepath("map-cmdline.default"))
1568 except Exception, inst:
1540 except Exception, inst:
1569 ui.write(" %s\n" % inst)
1541 ui.write(" %s\n" % inst)
1570 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1542 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1571 problems += 1
1543 problems += 1
1572
1544
1573 # editor
1545 # editor
1574 ui.status(_("Checking commit editor...\n"))
1546 ui.status(_("Checking commit editor...\n"))
1575 editor = ui.geteditor()
1547 editor = ui.geteditor()
1576 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1548 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1577 if not cmdpath:
1549 if not cmdpath:
1578 if editor == 'vi':
1550 if editor == 'vi':
1579 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1551 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1580 ui.write(_(" (specify a commit editor in your configuration"
1552 ui.write(_(" (specify a commit editor in your configuration"
1581 " file)\n"))
1553 " file)\n"))
1582 else:
1554 else:
1583 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1555 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1584 ui.write(_(" (specify a commit editor in your configuration"
1556 ui.write(_(" (specify a commit editor in your configuration"
1585 " file)\n"))
1557 " file)\n"))
1586 problems += 1
1558 problems += 1
1587
1559
1588 # check username
1560 # check username
1589 ui.status(_("Checking username...\n"))
1561 ui.status(_("Checking username...\n"))
1590 try:
1562 try:
1591 ui.username()
1563 ui.username()
1592 except util.Abort, e:
1564 except util.Abort, e:
1593 ui.write(" %s\n" % e)
1565 ui.write(" %s\n" % e)
1594 ui.write(_(" (specify a username in your configuration file)\n"))
1566 ui.write(_(" (specify a username in your configuration file)\n"))
1595 problems += 1
1567 problems += 1
1596
1568
1597 if not problems:
1569 if not problems:
1598 ui.status(_("No problems detected\n"))
1570 ui.status(_("No problems detected\n"))
1599 else:
1571 else:
1600 ui.write(_("%s problems detected,"
1572 ui.write(_("%s problems detected,"
1601 " please check your install!\n") % problems)
1573 " please check your install!\n") % problems)
1602
1574
1603 return problems
1575 return problems
1604
1576
1605 def debugrename(ui, repo, file1, *pats, **opts):
1577 def debugrename(ui, repo, file1, *pats, **opts):
1606 """dump rename information"""
1578 """dump rename information"""
1607
1579
1608 ctx = cmdutil.revsingle(repo, opts.get('rev'))
1580 ctx = cmdutil.revsingle(repo, opts.get('rev'))
1609 m = cmdutil.match(repo, (file1,) + pats, opts)
1581 m = cmdutil.match(repo, (file1,) + pats, opts)
1610 for abs in ctx.walk(m):
1582 for abs in ctx.walk(m):
1611 fctx = ctx[abs]
1583 fctx = ctx[abs]
1612 o = fctx.filelog().renamed(fctx.filenode())
1584 o = fctx.filelog().renamed(fctx.filenode())
1613 rel = m.rel(abs)
1585 rel = m.rel(abs)
1614 if o:
1586 if o:
1615 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1587 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1616 else:
1588 else:
1617 ui.write(_("%s not renamed\n") % rel)
1589 ui.write(_("%s not renamed\n") % rel)
1618
1590
1619 def debugwalk(ui, repo, *pats, **opts):
1591 def debugwalk(ui, repo, *pats, **opts):
1620 """show how files match on given patterns"""
1592 """show how files match on given patterns"""
1621 m = cmdutil.match(repo, pats, opts)
1593 m = cmdutil.match(repo, pats, opts)
1622 items = list(repo.walk(m))
1594 items = list(repo.walk(m))
1623 if not items:
1595 if not items:
1624 return
1596 return
1625 fmt = 'f %%-%ds %%-%ds %%s' % (
1597 fmt = 'f %%-%ds %%-%ds %%s' % (
1626 max([len(abs) for abs in items]),
1598 max([len(abs) for abs in items]),
1627 max([len(m.rel(abs)) for abs in items]))
1599 max([len(m.rel(abs)) for abs in items]))
1628 for abs in items:
1600 for abs in items:
1629 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1601 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1630 ui.write("%s\n" % line.rstrip())
1602 ui.write("%s\n" % line.rstrip())
1631
1603
1632 def debugwireargs(ui, repopath, *vals, **opts):
1604 def debugwireargs(ui, repopath, *vals, **opts):
1633 repo = hg.repository(hg.remoteui(ui, opts), repopath)
1605 repo = hg.repository(hg.remoteui(ui, opts), repopath)
1634 for opt in remoteopts:
1606 for opt in remoteopts:
1635 del opts[opt[1]]
1607 del opts[opt[1]]
1636 args = {}
1608 args = {}
1637 for k, v in opts.iteritems():
1609 for k, v in opts.iteritems():
1638 if v:
1610 if v:
1639 args[k] = v
1611 args[k] = v
1640 # run twice to check that we don't mess up the stream for the next command
1612 # run twice to check that we don't mess up the stream for the next command
1641 res1 = repo.debugwireargs(*vals, **args)
1613 res1 = repo.debugwireargs(*vals, **args)
1642 res2 = repo.debugwireargs(*vals, **args)
1614 res2 = repo.debugwireargs(*vals, **args)
1643 ui.write("%s\n" % res1)
1615 ui.write("%s\n" % res1)
1644 if res1 != res2:
1616 if res1 != res2:
1645 ui.warn("%s\n" % res2)
1617 ui.warn("%s\n" % res2)
1646
1618
1647 def diff(ui, repo, *pats, **opts):
1619 def diff(ui, repo, *pats, **opts):
1648 """diff repository (or selected files)
1620 """diff repository (or selected files)
1649
1621
1650 Show differences between revisions for the specified files.
1622 Show differences between revisions for the specified files.
1651
1623
1652 Differences between files are shown using the unified diff format.
1624 Differences between files are shown using the unified diff format.
1653
1625
1654 .. note::
1626 .. note::
1655 diff may generate unexpected results for merges, as it will
1627 diff may generate unexpected results for merges, as it will
1656 default to comparing against the working directory's first
1628 default to comparing against the working directory's first
1657 parent changeset if no revisions are specified.
1629 parent changeset if no revisions are specified.
1658
1630
1659 When two revision arguments are given, then changes are shown
1631 When two revision arguments are given, then changes are shown
1660 between those revisions. If only one revision is specified then
1632 between those revisions. If only one revision is specified then
1661 that revision is compared to the working directory, and, when no
1633 that revision is compared to the working directory, and, when no
1662 revisions are specified, the working directory files are compared
1634 revisions are specified, the working directory files are compared
1663 to its parent.
1635 to its parent.
1664
1636
1665 Alternatively you can specify -c/--change with a revision to see
1637 Alternatively you can specify -c/--change with a revision to see
1666 the changes in that changeset relative to its first parent.
1638 the changes in that changeset relative to its first parent.
1667
1639
1668 Without the -a/--text option, diff will avoid generating diffs of
1640 Without the -a/--text option, diff will avoid generating diffs of
1669 files it detects as binary. With -a, diff will generate a diff
1641 files it detects as binary. With -a, diff will generate a diff
1670 anyway, probably with undesirable results.
1642 anyway, probably with undesirable results.
1671
1643
1672 Use the -g/--git option to generate diffs in the git extended diff
1644 Use the -g/--git option to generate diffs in the git extended diff
1673 format. For more information, read :hg:`help diffs`.
1645 format. For more information, read :hg:`help diffs`.
1674
1646
1675 Returns 0 on success.
1647 Returns 0 on success.
1676 """
1648 """
1677
1649
1678 revs = opts.get('rev')
1650 revs = opts.get('rev')
1679 change = opts.get('change')
1651 change = opts.get('change')
1680 stat = opts.get('stat')
1652 stat = opts.get('stat')
1681 reverse = opts.get('reverse')
1653 reverse = opts.get('reverse')
1682
1654
1683 if revs and change:
1655 if revs and change:
1684 msg = _('cannot specify --rev and --change at the same time')
1656 msg = _('cannot specify --rev and --change at the same time')
1685 raise util.Abort(msg)
1657 raise util.Abort(msg)
1686 elif change:
1658 elif change:
1687 node2 = cmdutil.revsingle(repo, change, None).node()
1659 node2 = cmdutil.revsingle(repo, change, None).node()
1688 node1 = repo[node2].p1().node()
1660 node1 = repo[node2].p1().node()
1689 else:
1661 else:
1690 node1, node2 = cmdutil.revpair(repo, revs)
1662 node1, node2 = cmdutil.revpair(repo, revs)
1691
1663
1692 if reverse:
1664 if reverse:
1693 node1, node2 = node2, node1
1665 node1, node2 = node2, node1
1694
1666
1695 diffopts = patch.diffopts(ui, opts)
1667 diffopts = patch.diffopts(ui, opts)
1696 m = cmdutil.match(repo, pats, opts)
1668 m = cmdutil.match(repo, pats, opts)
1697 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1669 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1698 listsubrepos=opts.get('subrepos'))
1670 listsubrepos=opts.get('subrepos'))
1699
1671
1700 def export(ui, repo, *changesets, **opts):
1672 def export(ui, repo, *changesets, **opts):
1701 """dump the header and diffs for one or more changesets
1673 """dump the header and diffs for one or more changesets
1702
1674
1703 Print the changeset header and diffs for one or more revisions.
1675 Print the changeset header and diffs for one or more revisions.
1704
1676
1705 The information shown in the changeset header is: author, date,
1677 The information shown in the changeset header is: author, date,
1706 branch name (if non-default), changeset hash, parent(s) and commit
1678 branch name (if non-default), changeset hash, parent(s) and commit
1707 comment.
1679 comment.
1708
1680
1709 .. note::
1681 .. note::
1710 export may generate unexpected diff output for merge
1682 export may generate unexpected diff output for merge
1711 changesets, as it will compare the merge changeset against its
1683 changesets, as it will compare the merge changeset against its
1712 first parent only.
1684 first parent only.
1713
1685
1714 Output may be to a file, in which case the name of the file is
1686 Output may be to a file, in which case the name of the file is
1715 given using a format string. The formatting rules are as follows:
1687 given using a format string. The formatting rules are as follows:
1716
1688
1717 :``%%``: literal "%" character
1689 :``%%``: literal "%" character
1718 :``%H``: changeset hash (40 hexadecimal digits)
1690 :``%H``: changeset hash (40 hexadecimal digits)
1719 :``%N``: number of patches being generated
1691 :``%N``: number of patches being generated
1720 :``%R``: changeset revision number
1692 :``%R``: changeset revision number
1721 :``%b``: basename of the exporting repository
1693 :``%b``: basename of the exporting repository
1722 :``%h``: short-form changeset hash (12 hexadecimal digits)
1694 :``%h``: short-form changeset hash (12 hexadecimal digits)
1723 :``%n``: zero-padded sequence number, starting at 1
1695 :``%n``: zero-padded sequence number, starting at 1
1724 :``%r``: zero-padded changeset revision number
1696 :``%r``: zero-padded changeset revision number
1725
1697
1726 Without the -a/--text option, export will avoid generating diffs
1698 Without the -a/--text option, export will avoid generating diffs
1727 of files it detects as binary. With -a, export will generate a
1699 of files it detects as binary. With -a, export will generate a
1728 diff anyway, probably with undesirable results.
1700 diff anyway, probably with undesirable results.
1729
1701
1730 Use the -g/--git option to generate diffs in the git extended diff
1702 Use the -g/--git option to generate diffs in the git extended diff
1731 format. See :hg:`help diffs` for more information.
1703 format. See :hg:`help diffs` for more information.
1732
1704
1733 With the --switch-parent option, the diff will be against the
1705 With the --switch-parent option, the diff will be against the
1734 second parent. It can be useful to review a merge.
1706 second parent. It can be useful to review a merge.
1735
1707
1736 Returns 0 on success.
1708 Returns 0 on success.
1737 """
1709 """
1738 changesets += tuple(opts.get('rev', []))
1710 changesets += tuple(opts.get('rev', []))
1739 if not changesets:
1711 if not changesets:
1740 raise util.Abort(_("export requires at least one changeset"))
1712 raise util.Abort(_("export requires at least one changeset"))
1741 revs = cmdutil.revrange(repo, changesets)
1713 revs = cmdutil.revrange(repo, changesets)
1742 if len(revs) > 1:
1714 if len(revs) > 1:
1743 ui.note(_('exporting patches:\n'))
1715 ui.note(_('exporting patches:\n'))
1744 else:
1716 else:
1745 ui.note(_('exporting patch:\n'))
1717 ui.note(_('exporting patch:\n'))
1746 cmdutil.export(repo, revs, template=opts.get('output'),
1718 cmdutil.export(repo, revs, template=opts.get('output'),
1747 switch_parent=opts.get('switch_parent'),
1719 switch_parent=opts.get('switch_parent'),
1748 opts=patch.diffopts(ui, opts))
1720 opts=patch.diffopts(ui, opts))
1749
1721
1750 def forget(ui, repo, *pats, **opts):
1722 def forget(ui, repo, *pats, **opts):
1751 """forget the specified files on the next commit
1723 """forget the specified files on the next commit
1752
1724
1753 Mark the specified files so they will no longer be tracked
1725 Mark the specified files so they will no longer be tracked
1754 after the next commit.
1726 after the next commit.
1755
1727
1756 This only removes files from the current branch, not from the
1728 This only removes files from the current branch, not from the
1757 entire project history, and it does not delete them from the
1729 entire project history, and it does not delete them from the
1758 working directory.
1730 working directory.
1759
1731
1760 To undo a forget before the next commit, see :hg:`add`.
1732 To undo a forget before the next commit, see :hg:`add`.
1761
1733
1762 Returns 0 on success.
1734 Returns 0 on success.
1763 """
1735 """
1764
1736
1765 if not pats:
1737 if not pats:
1766 raise util.Abort(_('no files specified'))
1738 raise util.Abort(_('no files specified'))
1767
1739
1768 m = cmdutil.match(repo, pats, opts)
1740 m = cmdutil.match(repo, pats, opts)
1769 s = repo.status(match=m, clean=True)
1741 s = repo.status(match=m, clean=True)
1770 forget = sorted(s[0] + s[1] + s[3] + s[6])
1742 forget = sorted(s[0] + s[1] + s[3] + s[6])
1771 errs = 0
1743 errs = 0
1772
1744
1773 for f in m.files():
1745 for f in m.files():
1774 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1746 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1775 ui.warn(_('not removing %s: file is already untracked\n')
1747 ui.warn(_('not removing %s: file is already untracked\n')
1776 % m.rel(f))
1748 % m.rel(f))
1777 errs = 1
1749 errs = 1
1778
1750
1779 for f in forget:
1751 for f in forget:
1780 if ui.verbose or not m.exact(f):
1752 if ui.verbose or not m.exact(f):
1781 ui.status(_('removing %s\n') % m.rel(f))
1753 ui.status(_('removing %s\n') % m.rel(f))
1782
1754
1783 repo[None].remove(forget, unlink=False)
1755 repo[None].remove(forget, unlink=False)
1784 return errs
1756 return errs
1785
1757
1786 def grep(ui, repo, pattern, *pats, **opts):
1758 def grep(ui, repo, pattern, *pats, **opts):
1787 """search for a pattern in specified files and revisions
1759 """search for a pattern in specified files and revisions
1788
1760
1789 Search revisions of files for a regular expression.
1761 Search revisions of files for a regular expression.
1790
1762
1791 This command behaves differently than Unix grep. It only accepts
1763 This command behaves differently than Unix grep. It only accepts
1792 Python/Perl regexps. It searches repository history, not the
1764 Python/Perl regexps. It searches repository history, not the
1793 working directory. It always prints the revision number in which a
1765 working directory. It always prints the revision number in which a
1794 match appears.
1766 match appears.
1795
1767
1796 By default, grep only prints output for the first revision of a
1768 By default, grep only prints output for the first revision of a
1797 file in which it finds a match. To get it to print every revision
1769 file in which it finds a match. To get it to print every revision
1798 that contains a change in match status ("-" for a match that
1770 that contains a change in match status ("-" for a match that
1799 becomes a non-match, or "+" for a non-match that becomes a match),
1771 becomes a non-match, or "+" for a non-match that becomes a match),
1800 use the --all flag.
1772 use the --all flag.
1801
1773
1802 Returns 0 if a match is found, 1 otherwise.
1774 Returns 0 if a match is found, 1 otherwise.
1803 """
1775 """
1804 reflags = 0
1776 reflags = 0
1805 if opts.get('ignore_case'):
1777 if opts.get('ignore_case'):
1806 reflags |= re.I
1778 reflags |= re.I
1807 try:
1779 try:
1808 regexp = re.compile(pattern, reflags)
1780 regexp = re.compile(pattern, reflags)
1809 except re.error, inst:
1781 except re.error, inst:
1810 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1782 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1811 return 1
1783 return 1
1812 sep, eol = ':', '\n'
1784 sep, eol = ':', '\n'
1813 if opts.get('print0'):
1785 if opts.get('print0'):
1814 sep = eol = '\0'
1786 sep = eol = '\0'
1815
1787
1816 getfile = util.lrucachefunc(repo.file)
1788 getfile = util.lrucachefunc(repo.file)
1817
1789
1818 def matchlines(body):
1790 def matchlines(body):
1819 begin = 0
1791 begin = 0
1820 linenum = 0
1792 linenum = 0
1821 while True:
1793 while True:
1822 match = regexp.search(body, begin)
1794 match = regexp.search(body, begin)
1823 if not match:
1795 if not match:
1824 break
1796 break
1825 mstart, mend = match.span()
1797 mstart, mend = match.span()
1826 linenum += body.count('\n', begin, mstart) + 1
1798 linenum += body.count('\n', begin, mstart) + 1
1827 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1799 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1828 begin = body.find('\n', mend) + 1 or len(body)
1800 begin = body.find('\n', mend) + 1 or len(body)
1829 lend = begin - 1
1801 lend = begin - 1
1830 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1802 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1831
1803
1832 class linestate(object):
1804 class linestate(object):
1833 def __init__(self, line, linenum, colstart, colend):
1805 def __init__(self, line, linenum, colstart, colend):
1834 self.line = line
1806 self.line = line
1835 self.linenum = linenum
1807 self.linenum = linenum
1836 self.colstart = colstart
1808 self.colstart = colstart
1837 self.colend = colend
1809 self.colend = colend
1838
1810
1839 def __hash__(self):
1811 def __hash__(self):
1840 return hash((self.linenum, self.line))
1812 return hash((self.linenum, self.line))
1841
1813
1842 def __eq__(self, other):
1814 def __eq__(self, other):
1843 return self.line == other.line
1815 return self.line == other.line
1844
1816
1845 matches = {}
1817 matches = {}
1846 copies = {}
1818 copies = {}
1847 def grepbody(fn, rev, body):
1819 def grepbody(fn, rev, body):
1848 matches[rev].setdefault(fn, [])
1820 matches[rev].setdefault(fn, [])
1849 m = matches[rev][fn]
1821 m = matches[rev][fn]
1850 for lnum, cstart, cend, line in matchlines(body):
1822 for lnum, cstart, cend, line in matchlines(body):
1851 s = linestate(line, lnum, cstart, cend)
1823 s = linestate(line, lnum, cstart, cend)
1852 m.append(s)
1824 m.append(s)
1853
1825
1854 def difflinestates(a, b):
1826 def difflinestates(a, b):
1855 sm = difflib.SequenceMatcher(None, a, b)
1827 sm = difflib.SequenceMatcher(None, a, b)
1856 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1828 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1857 if tag == 'insert':
1829 if tag == 'insert':
1858 for i in xrange(blo, bhi):
1830 for i in xrange(blo, bhi):
1859 yield ('+', b[i])
1831 yield ('+', b[i])
1860 elif tag == 'delete':
1832 elif tag == 'delete':
1861 for i in xrange(alo, ahi):
1833 for i in xrange(alo, ahi):
1862 yield ('-', a[i])
1834 yield ('-', a[i])
1863 elif tag == 'replace':
1835 elif tag == 'replace':
1864 for i in xrange(alo, ahi):
1836 for i in xrange(alo, ahi):
1865 yield ('-', a[i])
1837 yield ('-', a[i])
1866 for i in xrange(blo, bhi):
1838 for i in xrange(blo, bhi):
1867 yield ('+', b[i])
1839 yield ('+', b[i])
1868
1840
1869 def display(fn, ctx, pstates, states):
1841 def display(fn, ctx, pstates, states):
1870 rev = ctx.rev()
1842 rev = ctx.rev()
1871 datefunc = ui.quiet and util.shortdate or util.datestr
1843 datefunc = ui.quiet and util.shortdate or util.datestr
1872 found = False
1844 found = False
1873 filerevmatches = {}
1845 filerevmatches = {}
1874 def binary():
1846 def binary():
1875 flog = getfile(fn)
1847 flog = getfile(fn)
1876 return util.binary(flog.read(ctx.filenode(fn)))
1848 return util.binary(flog.read(ctx.filenode(fn)))
1877
1849
1878 if opts.get('all'):
1850 if opts.get('all'):
1879 iter = difflinestates(pstates, states)
1851 iter = difflinestates(pstates, states)
1880 else:
1852 else:
1881 iter = [('', l) for l in states]
1853 iter = [('', l) for l in states]
1882 for change, l in iter:
1854 for change, l in iter:
1883 cols = [fn, str(rev)]
1855 cols = [fn, str(rev)]
1884 before, match, after = None, None, None
1856 before, match, after = None, None, None
1885 if opts.get('line_number'):
1857 if opts.get('line_number'):
1886 cols.append(str(l.linenum))
1858 cols.append(str(l.linenum))
1887 if opts.get('all'):
1859 if opts.get('all'):
1888 cols.append(change)
1860 cols.append(change)
1889 if opts.get('user'):
1861 if opts.get('user'):
1890 cols.append(ui.shortuser(ctx.user()))
1862 cols.append(ui.shortuser(ctx.user()))
1891 if opts.get('date'):
1863 if opts.get('date'):
1892 cols.append(datefunc(ctx.date()))
1864 cols.append(datefunc(ctx.date()))
1893 if opts.get('files_with_matches'):
1865 if opts.get('files_with_matches'):
1894 c = (fn, rev)
1866 c = (fn, rev)
1895 if c in filerevmatches:
1867 if c in filerevmatches:
1896 continue
1868 continue
1897 filerevmatches[c] = 1
1869 filerevmatches[c] = 1
1898 else:
1870 else:
1899 before = l.line[:l.colstart]
1871 before = l.line[:l.colstart]
1900 match = l.line[l.colstart:l.colend]
1872 match = l.line[l.colstart:l.colend]
1901 after = l.line[l.colend:]
1873 after = l.line[l.colend:]
1902 ui.write(sep.join(cols))
1874 ui.write(sep.join(cols))
1903 if before is not None:
1875 if before is not None:
1904 if not opts.get('text') and binary():
1876 if not opts.get('text') and binary():
1905 ui.write(sep + " Binary file matches")
1877 ui.write(sep + " Binary file matches")
1906 else:
1878 else:
1907 ui.write(sep + before)
1879 ui.write(sep + before)
1908 ui.write(match, label='grep.match')
1880 ui.write(match, label='grep.match')
1909 ui.write(after)
1881 ui.write(after)
1910 ui.write(eol)
1882 ui.write(eol)
1911 found = True
1883 found = True
1912 return found
1884 return found
1913
1885
1914 skip = {}
1886 skip = {}
1915 revfiles = {}
1887 revfiles = {}
1916 matchfn = cmdutil.match(repo, pats, opts)
1888 matchfn = cmdutil.match(repo, pats, opts)
1917 found = False
1889 found = False
1918 follow = opts.get('follow')
1890 follow = opts.get('follow')
1919
1891
1920 def prep(ctx, fns):
1892 def prep(ctx, fns):
1921 rev = ctx.rev()
1893 rev = ctx.rev()
1922 pctx = ctx.p1()
1894 pctx = ctx.p1()
1923 parent = pctx.rev()
1895 parent = pctx.rev()
1924 matches.setdefault(rev, {})
1896 matches.setdefault(rev, {})
1925 matches.setdefault(parent, {})
1897 matches.setdefault(parent, {})
1926 files = revfiles.setdefault(rev, [])
1898 files = revfiles.setdefault(rev, [])
1927 for fn in fns:
1899 for fn in fns:
1928 flog = getfile(fn)
1900 flog = getfile(fn)
1929 try:
1901 try:
1930 fnode = ctx.filenode(fn)
1902 fnode = ctx.filenode(fn)
1931 except error.LookupError:
1903 except error.LookupError:
1932 continue
1904 continue
1933
1905
1934 copied = flog.renamed(fnode)
1906 copied = flog.renamed(fnode)
1935 copy = follow and copied and copied[0]
1907 copy = follow and copied and copied[0]
1936 if copy:
1908 if copy:
1937 copies.setdefault(rev, {})[fn] = copy
1909 copies.setdefault(rev, {})[fn] = copy
1938 if fn in skip:
1910 if fn in skip:
1939 if copy:
1911 if copy:
1940 skip[copy] = True
1912 skip[copy] = True
1941 continue
1913 continue
1942 files.append(fn)
1914 files.append(fn)
1943
1915
1944 if fn not in matches[rev]:
1916 if fn not in matches[rev]:
1945 grepbody(fn, rev, flog.read(fnode))
1917 grepbody(fn, rev, flog.read(fnode))
1946
1918
1947 pfn = copy or fn
1919 pfn = copy or fn
1948 if pfn not in matches[parent]:
1920 if pfn not in matches[parent]:
1949 try:
1921 try:
1950 fnode = pctx.filenode(pfn)
1922 fnode = pctx.filenode(pfn)
1951 grepbody(pfn, parent, flog.read(fnode))
1923 grepbody(pfn, parent, flog.read(fnode))
1952 except error.LookupError:
1924 except error.LookupError:
1953 pass
1925 pass
1954
1926
1955 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
1927 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
1956 rev = ctx.rev()
1928 rev = ctx.rev()
1957 parent = ctx.p1().rev()
1929 parent = ctx.p1().rev()
1958 for fn in sorted(revfiles.get(rev, [])):
1930 for fn in sorted(revfiles.get(rev, [])):
1959 states = matches[rev][fn]
1931 states = matches[rev][fn]
1960 copy = copies.get(rev, {}).get(fn)
1932 copy = copies.get(rev, {}).get(fn)
1961 if fn in skip:
1933 if fn in skip:
1962 if copy:
1934 if copy:
1963 skip[copy] = True
1935 skip[copy] = True
1964 continue
1936 continue
1965 pstates = matches.get(parent, {}).get(copy or fn, [])
1937 pstates = matches.get(parent, {}).get(copy or fn, [])
1966 if pstates or states:
1938 if pstates or states:
1967 r = display(fn, ctx, pstates, states)
1939 r = display(fn, ctx, pstates, states)
1968 found = found or r
1940 found = found or r
1969 if r and not opts.get('all'):
1941 if r and not opts.get('all'):
1970 skip[fn] = True
1942 skip[fn] = True
1971 if copy:
1943 if copy:
1972 skip[copy] = True
1944 skip[copy] = True
1973 del matches[rev]
1945 del matches[rev]
1974 del revfiles[rev]
1946 del revfiles[rev]
1975
1947
1976 return not found
1948 return not found
1977
1949
1978 def heads(ui, repo, *branchrevs, **opts):
1950 def heads(ui, repo, *branchrevs, **opts):
1979 """show current repository heads or show branch heads
1951 """show current repository heads or show branch heads
1980
1952
1981 With no arguments, show all repository branch heads.
1953 With no arguments, show all repository branch heads.
1982
1954
1983 Repository "heads" are changesets with no child changesets. They are
1955 Repository "heads" are changesets with no child changesets. They are
1984 where development generally takes place and are the usual targets
1956 where development generally takes place and are the usual targets
1985 for update and merge operations. Branch heads are changesets that have
1957 for update and merge operations. Branch heads are changesets that have
1986 no child changeset on the same branch.
1958 no child changeset on the same branch.
1987
1959
1988 If one or more REVs are given, only branch heads on the branches
1960 If one or more REVs are given, only branch heads on the branches
1989 associated with the specified changesets are shown.
1961 associated with the specified changesets are shown.
1990
1962
1991 If -c/--closed is specified, also show branch heads marked closed
1963 If -c/--closed is specified, also show branch heads marked closed
1992 (see :hg:`commit --close-branch`).
1964 (see :hg:`commit --close-branch`).
1993
1965
1994 If STARTREV is specified, only those heads that are descendants of
1966 If STARTREV is specified, only those heads that are descendants of
1995 STARTREV will be displayed.
1967 STARTREV will be displayed.
1996
1968
1997 If -t/--topo is specified, named branch mechanics will be ignored and only
1969 If -t/--topo is specified, named branch mechanics will be ignored and only
1998 changesets without children will be shown.
1970 changesets without children will be shown.
1999
1971
2000 Returns 0 if matching heads are found, 1 if not.
1972 Returns 0 if matching heads are found, 1 if not.
2001 """
1973 """
2002
1974
2003 start = None
1975 start = None
2004 if 'rev' in opts:
1976 if 'rev' in opts:
2005 start = cmdutil.revsingle(repo, opts['rev'], None).node()
1977 start = cmdutil.revsingle(repo, opts['rev'], None).node()
2006
1978
2007 if opts.get('topo'):
1979 if opts.get('topo'):
2008 heads = [repo[h] for h in repo.heads(start)]
1980 heads = [repo[h] for h in repo.heads(start)]
2009 else:
1981 else:
2010 heads = []
1982 heads = []
2011 for b, ls in repo.branchmap().iteritems():
1983 for b, ls in repo.branchmap().iteritems():
2012 if start is None:
1984 if start is None:
2013 heads += [repo[h] for h in ls]
1985 heads += [repo[h] for h in ls]
2014 continue
1986 continue
2015 startrev = repo.changelog.rev(start)
1987 startrev = repo.changelog.rev(start)
2016 descendants = set(repo.changelog.descendants(startrev))
1988 descendants = set(repo.changelog.descendants(startrev))
2017 descendants.add(startrev)
1989 descendants.add(startrev)
2018 rev = repo.changelog.rev
1990 rev = repo.changelog.rev
2019 heads += [repo[h] for h in ls if rev(h) in descendants]
1991 heads += [repo[h] for h in ls if rev(h) in descendants]
2020
1992
2021 if branchrevs:
1993 if branchrevs:
2022 branches = set(repo[br].branch() for br in branchrevs)
1994 branches = set(repo[br].branch() for br in branchrevs)
2023 heads = [h for h in heads if h.branch() in branches]
1995 heads = [h for h in heads if h.branch() in branches]
2024
1996
2025 if not opts.get('closed'):
1997 if not opts.get('closed'):
2026 heads = [h for h in heads if not h.extra().get('close')]
1998 heads = [h for h in heads if not h.extra().get('close')]
2027
1999
2028 if opts.get('active') and branchrevs:
2000 if opts.get('active') and branchrevs:
2029 dagheads = repo.heads(start)
2001 dagheads = repo.heads(start)
2030 heads = [h for h in heads if h.node() in dagheads]
2002 heads = [h for h in heads if h.node() in dagheads]
2031
2003
2032 if branchrevs:
2004 if branchrevs:
2033 haveheads = set(h.branch() for h in heads)
2005 haveheads = set(h.branch() for h in heads)
2034 if branches - haveheads:
2006 if branches - haveheads:
2035 headless = ', '.join(b for b in branches - haveheads)
2007 headless = ', '.join(b for b in branches - haveheads)
2036 msg = _('no open branch heads found on branches %s')
2008 msg = _('no open branch heads found on branches %s')
2037 if opts.get('rev'):
2009 if opts.get('rev'):
2038 msg += _(' (started at %s)' % opts['rev'])
2010 msg += _(' (started at %s)' % opts['rev'])
2039 ui.warn((msg + '\n') % headless)
2011 ui.warn((msg + '\n') % headless)
2040
2012
2041 if not heads:
2013 if not heads:
2042 return 1
2014 return 1
2043
2015
2044 heads = sorted(heads, key=lambda x: -x.rev())
2016 heads = sorted(heads, key=lambda x: -x.rev())
2045 displayer = cmdutil.show_changeset(ui, repo, opts)
2017 displayer = cmdutil.show_changeset(ui, repo, opts)
2046 for ctx in heads:
2018 for ctx in heads:
2047 displayer.show(ctx)
2019 displayer.show(ctx)
2048 displayer.close()
2020 displayer.close()
2049
2021
2050 def help_(ui, name=None, with_version=False, unknowncmd=False, full=True):
2022 def help_(ui, name=None, with_version=False, unknowncmd=False, full=True):
2051 """show help for a given topic or a help overview
2023 """show help for a given topic or a help overview
2052
2024
2053 With no arguments, print a list of commands with short help messages.
2025 With no arguments, print a list of commands with short help messages.
2054
2026
2055 Given a topic, extension, or command name, print help for that
2027 Given a topic, extension, or command name, print help for that
2056 topic.
2028 topic.
2057
2029
2058 Returns 0 if successful.
2030 Returns 0 if successful.
2059 """
2031 """
2060 option_lists = []
2032 option_lists = []
2061 textwidth = min(ui.termwidth(), 80) - 2
2033 textwidth = min(ui.termwidth(), 80) - 2
2062
2034
2063 def addglobalopts(aliases):
2035 def addglobalopts(aliases):
2064 if ui.verbose:
2036 if ui.verbose:
2065 option_lists.append((_("global options:"), globalopts))
2037 option_lists.append((_("global options:"), globalopts))
2066 if name == 'shortlist':
2038 if name == 'shortlist':
2067 option_lists.append((_('use "hg help" for the full list '
2039 option_lists.append((_('use "hg help" for the full list '
2068 'of commands'), ()))
2040 'of commands'), ()))
2069 else:
2041 else:
2070 if name == 'shortlist':
2042 if name == 'shortlist':
2071 msg = _('use "hg help" for the full list of commands '
2043 msg = _('use "hg help" for the full list of commands '
2072 'or "hg -v" for details')
2044 'or "hg -v" for details')
2073 elif name and not full:
2045 elif name and not full:
2074 msg = _('use "hg help %s" to show the full help text' % name)
2046 msg = _('use "hg help %s" to show the full help text' % name)
2075 elif aliases:
2047 elif aliases:
2076 msg = _('use "hg -v help%s" to show builtin aliases and '
2048 msg = _('use "hg -v help%s" to show builtin aliases and '
2077 'global options') % (name and " " + name or "")
2049 'global options') % (name and " " + name or "")
2078 else:
2050 else:
2079 msg = _('use "hg -v help %s" to show global options') % name
2051 msg = _('use "hg -v help %s" to show global options') % name
2080 option_lists.append((msg, ()))
2052 option_lists.append((msg, ()))
2081
2053
2082 def helpcmd(name):
2054 def helpcmd(name):
2083 if with_version:
2055 if with_version:
2084 version_(ui)
2056 version_(ui)
2085 ui.write('\n')
2057 ui.write('\n')
2086
2058
2087 try:
2059 try:
2088 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
2060 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
2089 except error.AmbiguousCommand, inst:
2061 except error.AmbiguousCommand, inst:
2090 # py3k fix: except vars can't be used outside the scope of the
2062 # py3k fix: except vars can't be used outside the scope of the
2091 # except block, nor can be used inside a lambda. python issue4617
2063 # except block, nor can be used inside a lambda. python issue4617
2092 prefix = inst.args[0]
2064 prefix = inst.args[0]
2093 select = lambda c: c.lstrip('^').startswith(prefix)
2065 select = lambda c: c.lstrip('^').startswith(prefix)
2094 helplist(_('list of commands:\n\n'), select)
2066 helplist(_('list of commands:\n\n'), select)
2095 return
2067 return
2096
2068
2097 # check if it's an invalid alias and display its error if it is
2069 # check if it's an invalid alias and display its error if it is
2098 if getattr(entry[0], 'badalias', False):
2070 if getattr(entry[0], 'badalias', False):
2099 if not unknowncmd:
2071 if not unknowncmd:
2100 entry[0](ui)
2072 entry[0](ui)
2101 return
2073 return
2102
2074
2103 # synopsis
2075 # synopsis
2104 if len(entry) > 2:
2076 if len(entry) > 2:
2105 if entry[2].startswith('hg'):
2077 if entry[2].startswith('hg'):
2106 ui.write("%s\n" % entry[2])
2078 ui.write("%s\n" % entry[2])
2107 else:
2079 else:
2108 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
2080 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
2109 else:
2081 else:
2110 ui.write('hg %s\n' % aliases[0])
2082 ui.write('hg %s\n' % aliases[0])
2111
2083
2112 # aliases
2084 # aliases
2113 if full and not ui.quiet and len(aliases) > 1:
2085 if full and not ui.quiet and len(aliases) > 1:
2114 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
2086 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
2115
2087
2116 # description
2088 # description
2117 doc = gettext(entry[0].__doc__)
2089 doc = gettext(entry[0].__doc__)
2118 if not doc:
2090 if not doc:
2119 doc = _("(no help text available)")
2091 doc = _("(no help text available)")
2120 if hasattr(entry[0], 'definition'): # aliased command
2092 if hasattr(entry[0], 'definition'): # aliased command
2121 if entry[0].definition.startswith('!'): # shell alias
2093 if entry[0].definition.startswith('!'): # shell alias
2122 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
2094 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
2123 else:
2095 else:
2124 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
2096 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
2125 if ui.quiet or not full:
2097 if ui.quiet or not full:
2126 doc = doc.splitlines()[0]
2098 doc = doc.splitlines()[0]
2127 keep = ui.verbose and ['verbose'] or []
2099 keep = ui.verbose and ['verbose'] or []
2128 formatted, pruned = minirst.format(doc, textwidth, keep=keep)
2100 formatted, pruned = minirst.format(doc, textwidth, keep=keep)
2129 ui.write("\n%s\n" % formatted)
2101 ui.write("\n%s\n" % formatted)
2130 if pruned:
2102 if pruned:
2131 ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
2103 ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
2132
2104
2133 if not ui.quiet:
2105 if not ui.quiet:
2134 # options
2106 # options
2135 if entry[1]:
2107 if entry[1]:
2136 option_lists.append((_("options:\n"), entry[1]))
2108 option_lists.append((_("options:\n"), entry[1]))
2137
2109
2138 addglobalopts(False)
2110 addglobalopts(False)
2139
2111
2140 def helplist(header, select=None):
2112 def helplist(header, select=None):
2141 h = {}
2113 h = {}
2142 cmds = {}
2114 cmds = {}
2143 for c, e in table.iteritems():
2115 for c, e in table.iteritems():
2144 f = c.split("|", 1)[0]
2116 f = c.split("|", 1)[0]
2145 if select and not select(f):
2117 if select and not select(f):
2146 continue
2118 continue
2147 if (not select and name != 'shortlist' and
2119 if (not select and name != 'shortlist' and
2148 e[0].__module__ != __name__):
2120 e[0].__module__ != __name__):
2149 continue
2121 continue
2150 if name == "shortlist" and not f.startswith("^"):
2122 if name == "shortlist" and not f.startswith("^"):
2151 continue
2123 continue
2152 f = f.lstrip("^")
2124 f = f.lstrip("^")
2153 if not ui.debugflag and f.startswith("debug"):
2125 if not ui.debugflag and f.startswith("debug"):
2154 continue
2126 continue
2155 doc = e[0].__doc__
2127 doc = e[0].__doc__
2156 if doc and 'DEPRECATED' in doc and not ui.verbose:
2128 if doc and 'DEPRECATED' in doc and not ui.verbose:
2157 continue
2129 continue
2158 doc = gettext(doc)
2130 doc = gettext(doc)
2159 if not doc:
2131 if not doc:
2160 doc = _("(no help text available)")
2132 doc = _("(no help text available)")
2161 h[f] = doc.splitlines()[0].rstrip()
2133 h[f] = doc.splitlines()[0].rstrip()
2162 cmds[f] = c.lstrip("^")
2134 cmds[f] = c.lstrip("^")
2163
2135
2164 if not h:
2136 if not h:
2165 ui.status(_('no commands defined\n'))
2137 ui.status(_('no commands defined\n'))
2166 return
2138 return
2167
2139
2168 ui.status(header)
2140 ui.status(header)
2169 fns = sorted(h)
2141 fns = sorted(h)
2170 m = max(map(len, fns))
2142 m = max(map(len, fns))
2171 for f in fns:
2143 for f in fns:
2172 if ui.verbose:
2144 if ui.verbose:
2173 commands = cmds[f].replace("|",", ")
2145 commands = cmds[f].replace("|",", ")
2174 ui.write(" %s:\n %s\n"%(commands, h[f]))
2146 ui.write(" %s:\n %s\n"%(commands, h[f]))
2175 else:
2147 else:
2176 ui.write('%s\n' % (util.wrap(h[f], textwidth,
2148 ui.write('%s\n' % (util.wrap(h[f], textwidth,
2177 initindent=' %-*s ' % (m, f),
2149 initindent=' %-*s ' % (m, f),
2178 hangindent=' ' * (m + 4))))
2150 hangindent=' ' * (m + 4))))
2179
2151
2180 if not ui.quiet:
2152 if not ui.quiet:
2181 addglobalopts(True)
2153 addglobalopts(True)
2182
2154
2183 def helptopic(name):
2155 def helptopic(name):
2184 for names, header, doc in help.helptable:
2156 for names, header, doc in help.helptable:
2185 if name in names:
2157 if name in names:
2186 break
2158 break
2187 else:
2159 else:
2188 raise error.UnknownCommand(name)
2160 raise error.UnknownCommand(name)
2189
2161
2190 # description
2162 # description
2191 if not doc:
2163 if not doc:
2192 doc = _("(no help text available)")
2164 doc = _("(no help text available)")
2193 if hasattr(doc, '__call__'):
2165 if hasattr(doc, '__call__'):
2194 doc = doc()
2166 doc = doc()
2195
2167
2196 ui.write("%s\n\n" % header)
2168 ui.write("%s\n\n" % header)
2197 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
2169 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
2198
2170
2199 def helpext(name):
2171 def helpext(name):
2200 try:
2172 try:
2201 mod = extensions.find(name)
2173 mod = extensions.find(name)
2202 doc = gettext(mod.__doc__) or _('no help text available')
2174 doc = gettext(mod.__doc__) or _('no help text available')
2203 except KeyError:
2175 except KeyError:
2204 mod = None
2176 mod = None
2205 doc = extensions.disabledext(name)
2177 doc = extensions.disabledext(name)
2206 if not doc:
2178 if not doc:
2207 raise error.UnknownCommand(name)
2179 raise error.UnknownCommand(name)
2208
2180
2209 if '\n' not in doc:
2181 if '\n' not in doc:
2210 head, tail = doc, ""
2182 head, tail = doc, ""
2211 else:
2183 else:
2212 head, tail = doc.split('\n', 1)
2184 head, tail = doc.split('\n', 1)
2213 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
2185 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
2214 if tail:
2186 if tail:
2215 ui.write(minirst.format(tail, textwidth))
2187 ui.write(minirst.format(tail, textwidth))
2216 ui.status('\n\n')
2188 ui.status('\n\n')
2217
2189
2218 if mod:
2190 if mod:
2219 try:
2191 try:
2220 ct = mod.cmdtable
2192 ct = mod.cmdtable
2221 except AttributeError:
2193 except AttributeError:
2222 ct = {}
2194 ct = {}
2223 modcmds = set([c.split('|', 1)[0] for c in ct])
2195 modcmds = set([c.split('|', 1)[0] for c in ct])
2224 helplist(_('list of commands:\n\n'), modcmds.__contains__)
2196 helplist(_('list of commands:\n\n'), modcmds.__contains__)
2225 else:
2197 else:
2226 ui.write(_('use "hg help extensions" for information on enabling '
2198 ui.write(_('use "hg help extensions" for information on enabling '
2227 'extensions\n'))
2199 'extensions\n'))
2228
2200
2229 def helpextcmd(name):
2201 def helpextcmd(name):
2230 cmd, ext, mod = extensions.disabledcmd(ui, name, ui.config('ui', 'strict'))
2202 cmd, ext, mod = extensions.disabledcmd(ui, name, ui.config('ui', 'strict'))
2231 doc = gettext(mod.__doc__).splitlines()[0]
2203 doc = gettext(mod.__doc__).splitlines()[0]
2232
2204
2233 msg = help.listexts(_("'%s' is provided by the following "
2205 msg = help.listexts(_("'%s' is provided by the following "
2234 "extension:") % cmd, {ext: doc}, len(ext),
2206 "extension:") % cmd, {ext: doc}, len(ext),
2235 indent=4)
2207 indent=4)
2236 ui.write(minirst.format(msg, textwidth))
2208 ui.write(minirst.format(msg, textwidth))
2237 ui.write('\n\n')
2209 ui.write('\n\n')
2238 ui.write(_('use "hg help extensions" for information on enabling '
2210 ui.write(_('use "hg help extensions" for information on enabling '
2239 'extensions\n'))
2211 'extensions\n'))
2240
2212
2241 help.addtopichook('revsets', revset.makedoc)
2213 help.addtopichook('revsets', revset.makedoc)
2242 help.addtopichook('templates', templatekw.makedoc)
2214 help.addtopichook('templates', templatekw.makedoc)
2243 help.addtopichook('templates', templatefilters.makedoc)
2215 help.addtopichook('templates', templatefilters.makedoc)
2244
2216
2245 if name and name != 'shortlist':
2217 if name and name != 'shortlist':
2246 i = None
2218 i = None
2247 if unknowncmd:
2219 if unknowncmd:
2248 queries = (helpextcmd,)
2220 queries = (helpextcmd,)
2249 else:
2221 else:
2250 queries = (helptopic, helpcmd, helpext, helpextcmd)
2222 queries = (helptopic, helpcmd, helpext, helpextcmd)
2251 for f in queries:
2223 for f in queries:
2252 try:
2224 try:
2253 f(name)
2225 f(name)
2254 i = None
2226 i = None
2255 break
2227 break
2256 except error.UnknownCommand, inst:
2228 except error.UnknownCommand, inst:
2257 i = inst
2229 i = inst
2258 if i:
2230 if i:
2259 raise i
2231 raise i
2260
2232
2261 else:
2233 else:
2262 # program name
2234 # program name
2263 if ui.verbose or with_version:
2235 if ui.verbose or with_version:
2264 version_(ui)
2236 version_(ui)
2265 else:
2237 else:
2266 ui.status(_("Mercurial Distributed SCM\n"))
2238 ui.status(_("Mercurial Distributed SCM\n"))
2267 ui.status('\n')
2239 ui.status('\n')
2268
2240
2269 # list of commands
2241 # list of commands
2270 if name == "shortlist":
2242 if name == "shortlist":
2271 header = _('basic commands:\n\n')
2243 header = _('basic commands:\n\n')
2272 else:
2244 else:
2273 header = _('list of commands:\n\n')
2245 header = _('list of commands:\n\n')
2274
2246
2275 helplist(header)
2247 helplist(header)
2276 if name != 'shortlist':
2248 if name != 'shortlist':
2277 exts, maxlength = extensions.enabled()
2249 exts, maxlength = extensions.enabled()
2278 text = help.listexts(_('enabled extensions:'), exts, maxlength)
2250 text = help.listexts(_('enabled extensions:'), exts, maxlength)
2279 if text:
2251 if text:
2280 ui.write("\n%s\n" % minirst.format(text, textwidth))
2252 ui.write("\n%s\n" % minirst.format(text, textwidth))
2281
2253
2282 # list all option lists
2254 # list all option lists
2283 opt_output = []
2255 opt_output = []
2284 multioccur = False
2256 multioccur = False
2285 for title, options in option_lists:
2257 for title, options in option_lists:
2286 opt_output.append(("\n%s" % title, None))
2258 opt_output.append(("\n%s" % title, None))
2287 for option in options:
2259 for option in options:
2288 if len(option) == 5:
2260 if len(option) == 5:
2289 shortopt, longopt, default, desc, optlabel = option
2261 shortopt, longopt, default, desc, optlabel = option
2290 else:
2262 else:
2291 shortopt, longopt, default, desc = option
2263 shortopt, longopt, default, desc = option
2292 optlabel = _("VALUE") # default label
2264 optlabel = _("VALUE") # default label
2293
2265
2294 if _("DEPRECATED") in desc and not ui.verbose:
2266 if _("DEPRECATED") in desc and not ui.verbose:
2295 continue
2267 continue
2296 if isinstance(default, list):
2268 if isinstance(default, list):
2297 numqualifier = " %s [+]" % optlabel
2269 numqualifier = " %s [+]" % optlabel
2298 multioccur = True
2270 multioccur = True
2299 elif (default is not None) and not isinstance(default, bool):
2271 elif (default is not None) and not isinstance(default, bool):
2300 numqualifier = " %s" % optlabel
2272 numqualifier = " %s" % optlabel
2301 else:
2273 else:
2302 numqualifier = ""
2274 numqualifier = ""
2303 opt_output.append(("%2s%s" %
2275 opt_output.append(("%2s%s" %
2304 (shortopt and "-%s" % shortopt,
2276 (shortopt and "-%s" % shortopt,
2305 longopt and " --%s%s" %
2277 longopt and " --%s%s" %
2306 (longopt, numqualifier)),
2278 (longopt, numqualifier)),
2307 "%s%s" % (desc,
2279 "%s%s" % (desc,
2308 default
2280 default
2309 and _(" (default: %s)") % default
2281 and _(" (default: %s)") % default
2310 or "")))
2282 or "")))
2311 if multioccur:
2283 if multioccur:
2312 msg = _("\n[+] marked option can be specified multiple times")
2284 msg = _("\n[+] marked option can be specified multiple times")
2313 if ui.verbose and name != 'shortlist':
2285 if ui.verbose and name != 'shortlist':
2314 opt_output.append((msg, None))
2286 opt_output.append((msg, None))
2315 else:
2287 else:
2316 opt_output.insert(-1, (msg, None))
2288 opt_output.insert(-1, (msg, None))
2317
2289
2318 if not name:
2290 if not name:
2319 ui.write(_("\nadditional help topics:\n\n"))
2291 ui.write(_("\nadditional help topics:\n\n"))
2320 topics = []
2292 topics = []
2321 for names, header, doc in help.helptable:
2293 for names, header, doc in help.helptable:
2322 topics.append((sorted(names, key=len, reverse=True)[0], header))
2294 topics.append((sorted(names, key=len, reverse=True)[0], header))
2323 topics_len = max([len(s[0]) for s in topics])
2295 topics_len = max([len(s[0]) for s in topics])
2324 for t, desc in topics:
2296 for t, desc in topics:
2325 ui.write(" %-*s %s\n" % (topics_len, t, desc))
2297 ui.write(" %-*s %s\n" % (topics_len, t, desc))
2326
2298
2327 if opt_output:
2299 if opt_output:
2328 colwidth = encoding.colwidth
2300 colwidth = encoding.colwidth
2329 # normalize: (opt or message, desc or None, width of opt)
2301 # normalize: (opt or message, desc or None, width of opt)
2330 entries = [desc and (opt, desc, colwidth(opt)) or (opt, None, 0)
2302 entries = [desc and (opt, desc, colwidth(opt)) or (opt, None, 0)
2331 for opt, desc in opt_output]
2303 for opt, desc in opt_output]
2332 hanging = max([e[2] for e in entries])
2304 hanging = max([e[2] for e in entries])
2333 for opt, desc, width in entries:
2305 for opt, desc, width in entries:
2334 if desc:
2306 if desc:
2335 initindent = ' %s%s ' % (opt, ' ' * (hanging - width))
2307 initindent = ' %s%s ' % (opt, ' ' * (hanging - width))
2336 hangindent = ' ' * (hanging + 3)
2308 hangindent = ' ' * (hanging + 3)
2337 ui.write('%s\n' % (util.wrap(desc, textwidth,
2309 ui.write('%s\n' % (util.wrap(desc, textwidth,
2338 initindent=initindent,
2310 initindent=initindent,
2339 hangindent=hangindent)))
2311 hangindent=hangindent)))
2340 else:
2312 else:
2341 ui.write("%s\n" % opt)
2313 ui.write("%s\n" % opt)
2342
2314
2343 def identify(ui, repo, source=None, rev=None,
2315 def identify(ui, repo, source=None, rev=None,
2344 num=None, id=None, branch=None, tags=None, bookmarks=None):
2316 num=None, id=None, branch=None, tags=None, bookmarks=None):
2345 """identify the working copy or specified revision
2317 """identify the working copy or specified revision
2346
2318
2347 Print a summary identifying the repository state at REV using one or
2319 Print a summary identifying the repository state at REV using one or
2348 two parent hash identifiers, followed by a "+" if the working
2320 two parent hash identifiers, followed by a "+" if the working
2349 directory has uncommitted changes, the branch name (if not default),
2321 directory has uncommitted changes, the branch name (if not default),
2350 a list of tags, and a list of bookmarks.
2322 a list of tags, and a list of bookmarks.
2351
2323
2352 When REV is not given, print a summary of the current state of the
2324 When REV is not given, print a summary of the current state of the
2353 repository.
2325 repository.
2354
2326
2355 Specifying a path to a repository root or Mercurial bundle will
2327 Specifying a path to a repository root or Mercurial bundle will
2356 cause lookup to operate on that repository/bundle.
2328 cause lookup to operate on that repository/bundle.
2357
2329
2358 Returns 0 if successful.
2330 Returns 0 if successful.
2359 """
2331 """
2360
2332
2361 if not repo and not source:
2333 if not repo and not source:
2362 raise util.Abort(_("there is no Mercurial repository here "
2334 raise util.Abort(_("there is no Mercurial repository here "
2363 "(.hg not found)"))
2335 "(.hg not found)"))
2364
2336
2365 hexfunc = ui.debugflag and hex or short
2337 hexfunc = ui.debugflag and hex or short
2366 default = not (num or id or branch or tags or bookmarks)
2338 default = not (num or id or branch or tags or bookmarks)
2367 output = []
2339 output = []
2368 revs = []
2340 revs = []
2369
2341
2370 if source:
2342 if source:
2371 source, branches = hg.parseurl(ui.expandpath(source))
2343 source, branches = hg.parseurl(ui.expandpath(source))
2372 repo = hg.repository(ui, source)
2344 repo = hg.repository(ui, source)
2373 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
2345 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
2374
2346
2375 if not repo.local():
2347 if not repo.local():
2376 if num or branch or tags:
2348 if num or branch or tags:
2377 raise util.Abort(
2349 raise util.Abort(
2378 _("can't query remote revision number, branch, or tags"))
2350 _("can't query remote revision number, branch, or tags"))
2379 if not rev and revs:
2351 if not rev and revs:
2380 rev = revs[0]
2352 rev = revs[0]
2381 if not rev:
2353 if not rev:
2382 rev = "tip"
2354 rev = "tip"
2383
2355
2384 remoterev = repo.lookup(rev)
2356 remoterev = repo.lookup(rev)
2385 if default or id:
2357 if default or id:
2386 output = [hexfunc(remoterev)]
2358 output = [hexfunc(remoterev)]
2387
2359
2388 def getbms():
2360 def getbms():
2389 bms = []
2361 bms = []
2390
2362
2391 if 'bookmarks' in repo.listkeys('namespaces'):
2363 if 'bookmarks' in repo.listkeys('namespaces'):
2392 hexremoterev = hex(remoterev)
2364 hexremoterev = hex(remoterev)
2393 bms = [bm for bm, bmr in repo.listkeys('bookmarks').iteritems()
2365 bms = [bm for bm, bmr in repo.listkeys('bookmarks').iteritems()
2394 if bmr == hexremoterev]
2366 if bmr == hexremoterev]
2395
2367
2396 return bms
2368 return bms
2397
2369
2398 if bookmarks:
2370 if bookmarks:
2399 output.extend(getbms())
2371 output.extend(getbms())
2400 elif default and not ui.quiet:
2372 elif default and not ui.quiet:
2401 # multiple bookmarks for a single parent separated by '/'
2373 # multiple bookmarks for a single parent separated by '/'
2402 bm = '/'.join(getbms())
2374 bm = '/'.join(getbms())
2403 if bm:
2375 if bm:
2404 output.append(bm)
2376 output.append(bm)
2405 else:
2377 else:
2406 if not rev:
2378 if not rev:
2407 ctx = repo[None]
2379 ctx = repo[None]
2408 parents = ctx.parents()
2380 parents = ctx.parents()
2409 changed = ""
2381 changed = ""
2410 if default or id or num:
2382 if default or id or num:
2411 changed = util.any(repo.status()) and "+" or ""
2383 changed = util.any(repo.status()) and "+" or ""
2412 if default or id:
2384 if default or id:
2413 output = ["%s%s" %
2385 output = ["%s%s" %
2414 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
2386 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
2415 if num:
2387 if num:
2416 output.append("%s%s" %
2388 output.append("%s%s" %
2417 ('+'.join([str(p.rev()) for p in parents]), changed))
2389 ('+'.join([str(p.rev()) for p in parents]), changed))
2418 else:
2390 else:
2419 ctx = cmdutil.revsingle(repo, rev)
2391 ctx = cmdutil.revsingle(repo, rev)
2420 if default or id:
2392 if default or id:
2421 output = [hexfunc(ctx.node())]
2393 output = [hexfunc(ctx.node())]
2422 if num:
2394 if num:
2423 output.append(str(ctx.rev()))
2395 output.append(str(ctx.rev()))
2424
2396
2425 if default and not ui.quiet:
2397 if default and not ui.quiet:
2426 b = ctx.branch()
2398 b = ctx.branch()
2427 if b != 'default':
2399 if b != 'default':
2428 output.append("(%s)" % b)
2400 output.append("(%s)" % b)
2429
2401
2430 # multiple tags for a single parent separated by '/'
2402 # multiple tags for a single parent separated by '/'
2431 t = '/'.join(ctx.tags())
2403 t = '/'.join(ctx.tags())
2432 if t:
2404 if t:
2433 output.append(t)
2405 output.append(t)
2434
2406
2435 # multiple bookmarks for a single parent separated by '/'
2407 # multiple bookmarks for a single parent separated by '/'
2436 bm = '/'.join(ctx.bookmarks())
2408 bm = '/'.join(ctx.bookmarks())
2437 if bm:
2409 if bm:
2438 output.append(bm)
2410 output.append(bm)
2439 else:
2411 else:
2440 if branch:
2412 if branch:
2441 output.append(ctx.branch())
2413 output.append(ctx.branch())
2442
2414
2443 if tags:
2415 if tags:
2444 output.extend(ctx.tags())
2416 output.extend(ctx.tags())
2445
2417
2446 if bookmarks:
2418 if bookmarks:
2447 output.extend(ctx.bookmarks())
2419 output.extend(ctx.bookmarks())
2448
2420
2449 ui.write("%s\n" % ' '.join(output))
2421 ui.write("%s\n" % ' '.join(output))
2450
2422
2451 def import_(ui, repo, patch1, *patches, **opts):
2423 def import_(ui, repo, patch1, *patches, **opts):
2452 """import an ordered set of patches
2424 """import an ordered set of patches
2453
2425
2454 Import a list of patches and commit them individually (unless
2426 Import a list of patches and commit them individually (unless
2455 --no-commit is specified).
2427 --no-commit is specified).
2456
2428
2457 If there are outstanding changes in the working directory, import
2429 If there are outstanding changes in the working directory, import
2458 will abort unless given the -f/--force flag.
2430 will abort unless given the -f/--force flag.
2459
2431
2460 You can import a patch straight from a mail message. Even patches
2432 You can import a patch straight from a mail message. Even patches
2461 as attachments work (to use the body part, it must have type
2433 as attachments work (to use the body part, it must have type
2462 text/plain or text/x-patch). From and Subject headers of email
2434 text/plain or text/x-patch). From and Subject headers of email
2463 message are used as default committer and commit message. All
2435 message are used as default committer and commit message. All
2464 text/plain body parts before first diff are added to commit
2436 text/plain body parts before first diff are added to commit
2465 message.
2437 message.
2466
2438
2467 If the imported patch was generated by :hg:`export`, user and
2439 If the imported patch was generated by :hg:`export`, user and
2468 description from patch override values from message headers and
2440 description from patch override values from message headers and
2469 body. Values given on command line with -m/--message and -u/--user
2441 body. Values given on command line with -m/--message and -u/--user
2470 override these.
2442 override these.
2471
2443
2472 If --exact is specified, import will set the working directory to
2444 If --exact is specified, import will set the working directory to
2473 the parent of each patch before applying it, and will abort if the
2445 the parent of each patch before applying it, and will abort if the
2474 resulting changeset has a different ID than the one recorded in
2446 resulting changeset has a different ID than the one recorded in
2475 the patch. This may happen due to character set problems or other
2447 the patch. This may happen due to character set problems or other
2476 deficiencies in the text patch format.
2448 deficiencies in the text patch format.
2477
2449
2478 With -s/--similarity, hg will attempt to discover renames and
2450 With -s/--similarity, hg will attempt to discover renames and
2479 copies in the patch in the same way as 'addremove'.
2451 copies in the patch in the same way as 'addremove'.
2480
2452
2481 To read a patch from standard input, use "-" as the patch name. If
2453 To read a patch from standard input, use "-" as the patch name. If
2482 a URL is specified, the patch will be downloaded from it.
2454 a URL is specified, the patch will be downloaded from it.
2483 See :hg:`help dates` for a list of formats valid for -d/--date.
2455 See :hg:`help dates` for a list of formats valid for -d/--date.
2484
2456
2485 Returns 0 on success.
2457 Returns 0 on success.
2486 """
2458 """
2487 patches = (patch1,) + patches
2459 patches = (patch1,) + patches
2488
2460
2489 date = opts.get('date')
2461 date = opts.get('date')
2490 if date:
2462 if date:
2491 opts['date'] = util.parsedate(date)
2463 opts['date'] = util.parsedate(date)
2492
2464
2493 try:
2465 try:
2494 sim = float(opts.get('similarity') or 0)
2466 sim = float(opts.get('similarity') or 0)
2495 except ValueError:
2467 except ValueError:
2496 raise util.Abort(_('similarity must be a number'))
2468 raise util.Abort(_('similarity must be a number'))
2497 if sim < 0 or sim > 100:
2469 if sim < 0 or sim > 100:
2498 raise util.Abort(_('similarity must be between 0 and 100'))
2470 raise util.Abort(_('similarity must be between 0 and 100'))
2499
2471
2500 if opts.get('exact') or not opts.get('force'):
2472 if opts.get('exact') or not opts.get('force'):
2501 cmdutil.bail_if_changed(repo)
2473 cmdutil.bail_if_changed(repo)
2502
2474
2503 d = opts["base"]
2475 d = opts["base"]
2504 strip = opts["strip"]
2476 strip = opts["strip"]
2505 wlock = lock = None
2477 wlock = lock = None
2506 msgs = []
2478 msgs = []
2507
2479
2508 def tryone(ui, hunk):
2480 def tryone(ui, hunk):
2509 tmpname, message, user, date, branch, nodeid, p1, p2 = \
2481 tmpname, message, user, date, branch, nodeid, p1, p2 = \
2510 patch.extract(ui, hunk)
2482 patch.extract(ui, hunk)
2511
2483
2512 if not tmpname:
2484 if not tmpname:
2513 return None
2485 return None
2514 commitid = _('to working directory')
2486 commitid = _('to working directory')
2515
2487
2516 try:
2488 try:
2517 cmdline_message = cmdutil.logmessage(opts)
2489 cmdline_message = cmdutil.logmessage(opts)
2518 if cmdline_message:
2490 if cmdline_message:
2519 # pickup the cmdline msg
2491 # pickup the cmdline msg
2520 message = cmdline_message
2492 message = cmdline_message
2521 elif message:
2493 elif message:
2522 # pickup the patch msg
2494 # pickup the patch msg
2523 message = message.strip()
2495 message = message.strip()
2524 else:
2496 else:
2525 # launch the editor
2497 # launch the editor
2526 message = None
2498 message = None
2527 ui.debug('message:\n%s\n' % message)
2499 ui.debug('message:\n%s\n' % message)
2528
2500
2529 wp = repo.parents()
2501 wp = repo.parents()
2530 if opts.get('exact'):
2502 if opts.get('exact'):
2531 if not nodeid or not p1:
2503 if not nodeid or not p1:
2532 raise util.Abort(_('not a Mercurial patch'))
2504 raise util.Abort(_('not a Mercurial patch'))
2533 p1 = repo.lookup(p1)
2505 p1 = repo.lookup(p1)
2534 p2 = repo.lookup(p2 or hex(nullid))
2506 p2 = repo.lookup(p2 or hex(nullid))
2535
2507
2536 if p1 != wp[0].node():
2508 if p1 != wp[0].node():
2537 hg.clean(repo, p1)
2509 hg.clean(repo, p1)
2538 repo.dirstate.setparents(p1, p2)
2510 repo.dirstate.setparents(p1, p2)
2539 elif p2:
2511 elif p2:
2540 try:
2512 try:
2541 p1 = repo.lookup(p1)
2513 p1 = repo.lookup(p1)
2542 p2 = repo.lookup(p2)
2514 p2 = repo.lookup(p2)
2543 if p1 == wp[0].node():
2515 if p1 == wp[0].node():
2544 repo.dirstate.setparents(p1, p2)
2516 repo.dirstate.setparents(p1, p2)
2545 except error.RepoError:
2517 except error.RepoError:
2546 pass
2518 pass
2547 if opts.get('exact') or opts.get('import_branch'):
2519 if opts.get('exact') or opts.get('import_branch'):
2548 repo.dirstate.setbranch(branch or 'default')
2520 repo.dirstate.setbranch(branch or 'default')
2549
2521
2550 files = {}
2522 files = {}
2551 try:
2523 try:
2552 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
2524 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
2553 files=files, eolmode=None)
2525 files=files, eolmode=None)
2554 finally:
2526 finally:
2555 files = cmdutil.updatedir(ui, repo, files,
2527 files = cmdutil.updatedir(ui, repo, files,
2556 similarity=sim / 100.0)
2528 similarity=sim / 100.0)
2557 if opts.get('no_commit'):
2529 if opts.get('no_commit'):
2558 if message:
2530 if message:
2559 msgs.append(message)
2531 msgs.append(message)
2560 else:
2532 else:
2561 if opts.get('exact'):
2533 if opts.get('exact'):
2562 m = None
2534 m = None
2563 else:
2535 else:
2564 m = cmdutil.matchfiles(repo, files or [])
2536 m = cmdutil.matchfiles(repo, files or [])
2565 n = repo.commit(message, opts.get('user') or user,
2537 n = repo.commit(message, opts.get('user') or user,
2566 opts.get('date') or date, match=m,
2538 opts.get('date') or date, match=m,
2567 editor=cmdutil.commiteditor)
2539 editor=cmdutil.commiteditor)
2568 if opts.get('exact'):
2540 if opts.get('exact'):
2569 if hex(n) != nodeid:
2541 if hex(n) != nodeid:
2570 repo.rollback()
2542 repo.rollback()
2571 raise util.Abort(_('patch is damaged'
2543 raise util.Abort(_('patch is damaged'
2572 ' or loses information'))
2544 ' or loses information'))
2573 # Force a dirstate write so that the next transaction
2545 # Force a dirstate write so that the next transaction
2574 # backups an up-do-date file.
2546 # backups an up-do-date file.
2575 repo.dirstate.write()
2547 repo.dirstate.write()
2576 if n:
2548 if n:
2577 commitid = short(n)
2549 commitid = short(n)
2578
2550
2579 return commitid
2551 return commitid
2580 finally:
2552 finally:
2581 os.unlink(tmpname)
2553 os.unlink(tmpname)
2582
2554
2583 try:
2555 try:
2584 wlock = repo.wlock()
2556 wlock = repo.wlock()
2585 lock = repo.lock()
2557 lock = repo.lock()
2586 lastcommit = None
2558 lastcommit = None
2587 for p in patches:
2559 for p in patches:
2588 pf = os.path.join(d, p)
2560 pf = os.path.join(d, p)
2589
2561
2590 if pf == '-':
2562 if pf == '-':
2591 ui.status(_("applying patch from stdin\n"))
2563 ui.status(_("applying patch from stdin\n"))
2592 pf = sys.stdin
2564 pf = sys.stdin
2593 else:
2565 else:
2594 ui.status(_("applying %s\n") % p)
2566 ui.status(_("applying %s\n") % p)
2595 pf = url.open(ui, pf)
2567 pf = url.open(ui, pf)
2596
2568
2597 haspatch = False
2569 haspatch = False
2598 for hunk in patch.split(pf):
2570 for hunk in patch.split(pf):
2599 commitid = tryone(ui, hunk)
2571 commitid = tryone(ui, hunk)
2600 if commitid:
2572 if commitid:
2601 haspatch = True
2573 haspatch = True
2602 if lastcommit:
2574 if lastcommit:
2603 ui.status(_('applied %s\n') % lastcommit)
2575 ui.status(_('applied %s\n') % lastcommit)
2604 lastcommit = commitid
2576 lastcommit = commitid
2605
2577
2606 if not haspatch:
2578 if not haspatch:
2607 raise util.Abort(_('no diffs found'))
2579 raise util.Abort(_('no diffs found'))
2608
2580
2609 if msgs:
2581 if msgs:
2610 repo.opener('last-message.txt', 'wb').write('\n* * *\n'.join(msgs))
2582 repo.opener('last-message.txt', 'wb').write('\n* * *\n'.join(msgs))
2611 finally:
2583 finally:
2612 release(lock, wlock)
2584 release(lock, wlock)
2613
2585
2614 def incoming(ui, repo, source="default", **opts):
2586 def incoming(ui, repo, source="default", **opts):
2615 """show new changesets found in source
2587 """show new changesets found in source
2616
2588
2617 Show new changesets found in the specified path/URL or the default
2589 Show new changesets found in the specified path/URL or the default
2618 pull location. These are the changesets that would have been pulled
2590 pull location. These are the changesets that would have been pulled
2619 if a pull at the time you issued this command.
2591 if a pull at the time you issued this command.
2620
2592
2621 For remote repository, using --bundle avoids downloading the
2593 For remote repository, using --bundle avoids downloading the
2622 changesets twice if the incoming is followed by a pull.
2594 changesets twice if the incoming is followed by a pull.
2623
2595
2624 See pull for valid source format details.
2596 See pull for valid source format details.
2625
2597
2626 Returns 0 if there are incoming changes, 1 otherwise.
2598 Returns 0 if there are incoming changes, 1 otherwise.
2627 """
2599 """
2628 if opts.get('bundle') and opts.get('subrepos'):
2600 if opts.get('bundle') and opts.get('subrepos'):
2629 raise util.Abort(_('cannot combine --bundle and --subrepos'))
2601 raise util.Abort(_('cannot combine --bundle and --subrepos'))
2630
2602
2631 if opts.get('bookmarks'):
2603 if opts.get('bookmarks'):
2632 source, branches = hg.parseurl(ui.expandpath(source),
2604 source, branches = hg.parseurl(ui.expandpath(source),
2633 opts.get('branch'))
2605 opts.get('branch'))
2634 other = hg.repository(hg.remoteui(repo, opts), source)
2606 other = hg.repository(hg.remoteui(repo, opts), source)
2635 if 'bookmarks' not in other.listkeys('namespaces'):
2607 if 'bookmarks' not in other.listkeys('namespaces'):
2636 ui.warn(_("remote doesn't support bookmarks\n"))
2608 ui.warn(_("remote doesn't support bookmarks\n"))
2637 return 0
2609 return 0
2638 ui.status(_('comparing with %s\n') % url.hidepassword(source))
2610 ui.status(_('comparing with %s\n') % url.hidepassword(source))
2639 return bookmarks.diff(ui, repo, other)
2611 return bookmarks.diff(ui, repo, other)
2640
2612
2641 ret = hg.incoming(ui, repo, source, opts)
2613 ret = hg.incoming(ui, repo, source, opts)
2642 return ret
2614 return ret
2643
2615
2644 def init(ui, dest=".", **opts):
2616 def init(ui, dest=".", **opts):
2645 """create a new repository in the given directory
2617 """create a new repository in the given directory
2646
2618
2647 Initialize a new repository in the given directory. If the given
2619 Initialize a new repository in the given directory. If the given
2648 directory does not exist, it will be created.
2620 directory does not exist, it will be created.
2649
2621
2650 If no directory is given, the current directory is used.
2622 If no directory is given, the current directory is used.
2651
2623
2652 It is possible to specify an ``ssh://`` URL as the destination.
2624 It is possible to specify an ``ssh://`` URL as the destination.
2653 See :hg:`help urls` for more information.
2625 See :hg:`help urls` for more information.
2654
2626
2655 Returns 0 on success.
2627 Returns 0 on success.
2656 """
2628 """
2657 hg.repository(hg.remoteui(ui, opts), ui.expandpath(dest), create=1)
2629 hg.repository(hg.remoteui(ui, opts), ui.expandpath(dest), create=1)
2658
2630
2659 def locate(ui, repo, *pats, **opts):
2631 def locate(ui, repo, *pats, **opts):
2660 """locate files matching specific patterns
2632 """locate files matching specific patterns
2661
2633
2662 Print files under Mercurial control in the working directory whose
2634 Print files under Mercurial control in the working directory whose
2663 names match the given patterns.
2635 names match the given patterns.
2664
2636
2665 By default, this command searches all directories in the working
2637 By default, this command searches all directories in the working
2666 directory. To search just the current directory and its
2638 directory. To search just the current directory and its
2667 subdirectories, use "--include .".
2639 subdirectories, use "--include .".
2668
2640
2669 If no patterns are given to match, this command prints the names
2641 If no patterns are given to match, this command prints the names
2670 of all files under Mercurial control in the working directory.
2642 of all files under Mercurial control in the working directory.
2671
2643
2672 If you want to feed the output of this command into the "xargs"
2644 If you want to feed the output of this command into the "xargs"
2673 command, use the -0 option to both this command and "xargs". This
2645 command, use the -0 option to both this command and "xargs". This
2674 will avoid the problem of "xargs" treating single filenames that
2646 will avoid the problem of "xargs" treating single filenames that
2675 contain whitespace as multiple filenames.
2647 contain whitespace as multiple filenames.
2676
2648
2677 Returns 0 if a match is found, 1 otherwise.
2649 Returns 0 if a match is found, 1 otherwise.
2678 """
2650 """
2679 end = opts.get('print0') and '\0' or '\n'
2651 end = opts.get('print0') and '\0' or '\n'
2680 rev = cmdutil.revsingle(repo, opts.get('rev'), None).node()
2652 rev = cmdutil.revsingle(repo, opts.get('rev'), None).node()
2681
2653
2682 ret = 1
2654 ret = 1
2683 m = cmdutil.match(repo, pats, opts, default='relglob')
2655 m = cmdutil.match(repo, pats, opts, default='relglob')
2684 m.bad = lambda x, y: False
2656 m.bad = lambda x, y: False
2685 for abs in repo[rev].walk(m):
2657 for abs in repo[rev].walk(m):
2686 if not rev and abs not in repo.dirstate:
2658 if not rev and abs not in repo.dirstate:
2687 continue
2659 continue
2688 if opts.get('fullpath'):
2660 if opts.get('fullpath'):
2689 ui.write(repo.wjoin(abs), end)
2661 ui.write(repo.wjoin(abs), end)
2690 else:
2662 else:
2691 ui.write(((pats and m.rel(abs)) or abs), end)
2663 ui.write(((pats and m.rel(abs)) or abs), end)
2692 ret = 0
2664 ret = 0
2693
2665
2694 return ret
2666 return ret
2695
2667
2696 def log(ui, repo, *pats, **opts):
2668 def log(ui, repo, *pats, **opts):
2697 """show revision history of entire repository or files
2669 """show revision history of entire repository or files
2698
2670
2699 Print the revision history of the specified files or the entire
2671 Print the revision history of the specified files or the entire
2700 project.
2672 project.
2701
2673
2702 File history is shown without following rename or copy history of
2674 File history is shown without following rename or copy history of
2703 files. Use -f/--follow with a filename to follow history across
2675 files. Use -f/--follow with a filename to follow history across
2704 renames and copies. --follow without a filename will only show
2676 renames and copies. --follow without a filename will only show
2705 ancestors or descendants of the starting revision. --follow-first
2677 ancestors or descendants of the starting revision. --follow-first
2706 only follows the first parent of merge revisions.
2678 only follows the first parent of merge revisions.
2707
2679
2708 If no revision range is specified, the default is ``tip:0`` unless
2680 If no revision range is specified, the default is ``tip:0`` unless
2709 --follow is set, in which case the working directory parent is
2681 --follow is set, in which case the working directory parent is
2710 used as the starting revision. You can specify a revision set for
2682 used as the starting revision. You can specify a revision set for
2711 log, see :hg:`help revsets` for more information.
2683 log, see :hg:`help revsets` for more information.
2712
2684
2713 See :hg:`help dates` for a list of formats valid for -d/--date.
2685 See :hg:`help dates` for a list of formats valid for -d/--date.
2714
2686
2715 By default this command prints revision number and changeset id,
2687 By default this command prints revision number and changeset id,
2716 tags, non-trivial parents, user, date and time, and a summary for
2688 tags, non-trivial parents, user, date and time, and a summary for
2717 each commit. When the -v/--verbose switch is used, the list of
2689 each commit. When the -v/--verbose switch is used, the list of
2718 changed files and full commit message are shown.
2690 changed files and full commit message are shown.
2719
2691
2720 .. note::
2692 .. note::
2721 log -p/--patch may generate unexpected diff output for merge
2693 log -p/--patch may generate unexpected diff output for merge
2722 changesets, as it will only compare the merge changeset against
2694 changesets, as it will only compare the merge changeset against
2723 its first parent. Also, only files different from BOTH parents
2695 its first parent. Also, only files different from BOTH parents
2724 will appear in files:.
2696 will appear in files:.
2725
2697
2726 Returns 0 on success.
2698 Returns 0 on success.
2727 """
2699 """
2728
2700
2729 matchfn = cmdutil.match(repo, pats, opts)
2701 matchfn = cmdutil.match(repo, pats, opts)
2730 limit = cmdutil.loglimit(opts)
2702 limit = cmdutil.loglimit(opts)
2731 count = 0
2703 count = 0
2732
2704
2733 endrev = None
2705 endrev = None
2734 if opts.get('copies') and opts.get('rev'):
2706 if opts.get('copies') and opts.get('rev'):
2735 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
2707 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
2736
2708
2737 df = False
2709 df = False
2738 if opts["date"]:
2710 if opts["date"]:
2739 df = util.matchdate(opts["date"])
2711 df = util.matchdate(opts["date"])
2740
2712
2741 branches = opts.get('branch', []) + opts.get('only_branch', [])
2713 branches = opts.get('branch', []) + opts.get('only_branch', [])
2742 opts['branch'] = [repo.lookupbranch(b) for b in branches]
2714 opts['branch'] = [repo.lookupbranch(b) for b in branches]
2743
2715
2744 displayer = cmdutil.show_changeset(ui, repo, opts, True)
2716 displayer = cmdutil.show_changeset(ui, repo, opts, True)
2745 def prep(ctx, fns):
2717 def prep(ctx, fns):
2746 rev = ctx.rev()
2718 rev = ctx.rev()
2747 parents = [p for p in repo.changelog.parentrevs(rev)
2719 parents = [p for p in repo.changelog.parentrevs(rev)
2748 if p != nullrev]
2720 if p != nullrev]
2749 if opts.get('no_merges') and len(parents) == 2:
2721 if opts.get('no_merges') and len(parents) == 2:
2750 return
2722 return
2751 if opts.get('only_merges') and len(parents) != 2:
2723 if opts.get('only_merges') and len(parents) != 2:
2752 return
2724 return
2753 if opts.get('branch') and ctx.branch() not in opts['branch']:
2725 if opts.get('branch') and ctx.branch() not in opts['branch']:
2754 return
2726 return
2755 if df and not df(ctx.date()[0]):
2727 if df and not df(ctx.date()[0]):
2756 return
2728 return
2757 if opts['user'] and not [k for k in opts['user']
2729 if opts['user'] and not [k for k in opts['user']
2758 if k.lower() in ctx.user().lower()]:
2730 if k.lower() in ctx.user().lower()]:
2759 return
2731 return
2760 if opts.get('keyword'):
2732 if opts.get('keyword'):
2761 for k in [kw.lower() for kw in opts['keyword']]:
2733 for k in [kw.lower() for kw in opts['keyword']]:
2762 if (k in ctx.user().lower() or
2734 if (k in ctx.user().lower() or
2763 k in ctx.description().lower() or
2735 k in ctx.description().lower() or
2764 k in " ".join(ctx.files()).lower()):
2736 k in " ".join(ctx.files()).lower()):
2765 break
2737 break
2766 else:
2738 else:
2767 return
2739 return
2768
2740
2769 copies = None
2741 copies = None
2770 if opts.get('copies') and rev:
2742 if opts.get('copies') and rev:
2771 copies = []
2743 copies = []
2772 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2744 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2773 for fn in ctx.files():
2745 for fn in ctx.files():
2774 rename = getrenamed(fn, rev)
2746 rename = getrenamed(fn, rev)
2775 if rename:
2747 if rename:
2776 copies.append((fn, rename[0]))
2748 copies.append((fn, rename[0]))
2777
2749
2778 revmatchfn = None
2750 revmatchfn = None
2779 if opts.get('patch') or opts.get('stat'):
2751 if opts.get('patch') or opts.get('stat'):
2780 if opts.get('follow') or opts.get('follow_first'):
2752 if opts.get('follow') or opts.get('follow_first'):
2781 # note: this might be wrong when following through merges
2753 # note: this might be wrong when following through merges
2782 revmatchfn = cmdutil.match(repo, fns, default='path')
2754 revmatchfn = cmdutil.match(repo, fns, default='path')
2783 else:
2755 else:
2784 revmatchfn = matchfn
2756 revmatchfn = matchfn
2785
2757
2786 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2758 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2787
2759
2788 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2760 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2789 if count == limit:
2761 if count == limit:
2790 break
2762 break
2791 if displayer.flush(ctx.rev()):
2763 if displayer.flush(ctx.rev()):
2792 count += 1
2764 count += 1
2793 displayer.close()
2765 displayer.close()
2794
2766
2795 def manifest(ui, repo, node=None, rev=None):
2767 def manifest(ui, repo, node=None, rev=None):
2796 """output the current or given revision of the project manifest
2768 """output the current or given revision of the project manifest
2797
2769
2798 Print a list of version controlled files for the given revision.
2770 Print a list of version controlled files for the given revision.
2799 If no revision is given, the first parent of the working directory
2771 If no revision is given, the first parent of the working directory
2800 is used, or the null revision if no revision is checked out.
2772 is used, or the null revision if no revision is checked out.
2801
2773
2802 With -v, print file permissions, symlink and executable bits.
2774 With -v, print file permissions, symlink and executable bits.
2803 With --debug, print file revision hashes.
2775 With --debug, print file revision hashes.
2804
2776
2805 Returns 0 on success.
2777 Returns 0 on success.
2806 """
2778 """
2807
2779
2808 if rev and node:
2780 if rev and node:
2809 raise util.Abort(_("please specify just one revision"))
2781 raise util.Abort(_("please specify just one revision"))
2810
2782
2811 if not node:
2783 if not node:
2812 node = rev
2784 node = rev
2813
2785
2814 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2786 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2815 ctx = cmdutil.revsingle(repo, node)
2787 ctx = cmdutil.revsingle(repo, node)
2816 for f in ctx:
2788 for f in ctx:
2817 if ui.debugflag:
2789 if ui.debugflag:
2818 ui.write("%40s " % hex(ctx.manifest()[f]))
2790 ui.write("%40s " % hex(ctx.manifest()[f]))
2819 if ui.verbose:
2791 if ui.verbose:
2820 ui.write(decor[ctx.flags(f)])
2792 ui.write(decor[ctx.flags(f)])
2821 ui.write("%s\n" % f)
2793 ui.write("%s\n" % f)
2822
2794
2823 def merge(ui, repo, node=None, **opts):
2795 def merge(ui, repo, node=None, **opts):
2824 """merge working directory with another revision
2796 """merge working directory with another revision
2825
2797
2826 The current working directory is updated with all changes made in
2798 The current working directory is updated with all changes made in
2827 the requested revision since the last common predecessor revision.
2799 the requested revision since the last common predecessor revision.
2828
2800
2829 Files that changed between either parent are marked as changed for
2801 Files that changed between either parent are marked as changed for
2830 the next commit and a commit must be performed before any further
2802 the next commit and a commit must be performed before any further
2831 updates to the repository are allowed. The next commit will have
2803 updates to the repository are allowed. The next commit will have
2832 two parents.
2804 two parents.
2833
2805
2834 ``--tool`` can be used to specify the merge tool used for file
2806 ``--tool`` can be used to specify the merge tool used for file
2835 merges. It overrides the HGMERGE environment variable and your
2807 merges. It overrides the HGMERGE environment variable and your
2836 configuration files. See :hg:`help merge-tools` for options.
2808 configuration files. See :hg:`help merge-tools` for options.
2837
2809
2838 If no revision is specified, the working directory's parent is a
2810 If no revision is specified, the working directory's parent is a
2839 head revision, and the current branch contains exactly one other
2811 head revision, and the current branch contains exactly one other
2840 head, the other head is merged with by default. Otherwise, an
2812 head, the other head is merged with by default. Otherwise, an
2841 explicit revision with which to merge with must be provided.
2813 explicit revision with which to merge with must be provided.
2842
2814
2843 :hg:`resolve` must be used to resolve unresolved files.
2815 :hg:`resolve` must be used to resolve unresolved files.
2844
2816
2845 To undo an uncommitted merge, use :hg:`update --clean .` which
2817 To undo an uncommitted merge, use :hg:`update --clean .` which
2846 will check out a clean copy of the original merge parent, losing
2818 will check out a clean copy of the original merge parent, losing
2847 all changes.
2819 all changes.
2848
2820
2849 Returns 0 on success, 1 if there are unresolved files.
2821 Returns 0 on success, 1 if there are unresolved files.
2850 """
2822 """
2851
2823
2852 if opts.get('rev') and node:
2824 if opts.get('rev') and node:
2853 raise util.Abort(_("please specify just one revision"))
2825 raise util.Abort(_("please specify just one revision"))
2854 if not node:
2826 if not node:
2855 node = opts.get('rev')
2827 node = opts.get('rev')
2856
2828
2857 if not node:
2829 if not node:
2858 branch = repo[None].branch()
2830 branch = repo[None].branch()
2859 bheads = repo.branchheads(branch)
2831 bheads = repo.branchheads(branch)
2860 if len(bheads) > 2:
2832 if len(bheads) > 2:
2861 raise util.Abort(_(
2833 raise util.Abort(_(
2862 'branch \'%s\' has %d heads - '
2834 'branch \'%s\' has %d heads - '
2863 'please merge with an explicit rev\n'
2835 'please merge with an explicit rev\n'
2864 '(run \'hg heads .\' to see heads)')
2836 '(run \'hg heads .\' to see heads)')
2865 % (branch, len(bheads)))
2837 % (branch, len(bheads)))
2866
2838
2867 parent = repo.dirstate.p1()
2839 parent = repo.dirstate.p1()
2868 if len(bheads) == 1:
2840 if len(bheads) == 1:
2869 if len(repo.heads()) > 1:
2841 if len(repo.heads()) > 1:
2870 raise util.Abort(_(
2842 raise util.Abort(_(
2871 'branch \'%s\' has one head - '
2843 'branch \'%s\' has one head - '
2872 'please merge with an explicit rev\n'
2844 'please merge with an explicit rev\n'
2873 '(run \'hg heads\' to see all heads)')
2845 '(run \'hg heads\' to see all heads)')
2874 % branch)
2846 % branch)
2875 msg = _('there is nothing to merge')
2847 msg = _('there is nothing to merge')
2876 if parent != repo.lookup(repo[None].branch()):
2848 if parent != repo.lookup(repo[None].branch()):
2877 msg = _('%s - use "hg update" instead') % msg
2849 msg = _('%s - use "hg update" instead') % msg
2878 raise util.Abort(msg)
2850 raise util.Abort(msg)
2879
2851
2880 if parent not in bheads:
2852 if parent not in bheads:
2881 raise util.Abort(_('working dir not at a head rev - '
2853 raise util.Abort(_('working dir not at a head rev - '
2882 'use "hg update" or merge with an explicit rev'))
2854 'use "hg update" or merge with an explicit rev'))
2883 node = parent == bheads[0] and bheads[-1] or bheads[0]
2855 node = parent == bheads[0] and bheads[-1] or bheads[0]
2884 else:
2856 else:
2885 node = cmdutil.revsingle(repo, node).node()
2857 node = cmdutil.revsingle(repo, node).node()
2886
2858
2887 if opts.get('preview'):
2859 if opts.get('preview'):
2888 # find nodes that are ancestors of p2 but not of p1
2860 # find nodes that are ancestors of p2 but not of p1
2889 p1 = repo.lookup('.')
2861 p1 = repo.lookup('.')
2890 p2 = repo.lookup(node)
2862 p2 = repo.lookup(node)
2891 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
2863 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
2892
2864
2893 displayer = cmdutil.show_changeset(ui, repo, opts)
2865 displayer = cmdutil.show_changeset(ui, repo, opts)
2894 for node in nodes:
2866 for node in nodes:
2895 displayer.show(repo[node])
2867 displayer.show(repo[node])
2896 displayer.close()
2868 displayer.close()
2897 return 0
2869 return 0
2898
2870
2899 try:
2871 try:
2900 # ui.forcemerge is an internal variable, do not document
2872 # ui.forcemerge is an internal variable, do not document
2901 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
2873 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
2902 return hg.merge(repo, node, force=opts.get('force'))
2874 return hg.merge(repo, node, force=opts.get('force'))
2903 finally:
2875 finally:
2904 ui.setconfig('ui', 'forcemerge', '')
2876 ui.setconfig('ui', 'forcemerge', '')
2905
2877
2906 def outgoing(ui, repo, dest=None, **opts):
2878 def outgoing(ui, repo, dest=None, **opts):
2907 """show changesets not found in the destination
2879 """show changesets not found in the destination
2908
2880
2909 Show changesets not found in the specified destination repository
2881 Show changesets not found in the specified destination repository
2910 or the default push location. These are the changesets that would
2882 or the default push location. These are the changesets that would
2911 be pushed if a push was requested.
2883 be pushed if a push was requested.
2912
2884
2913 See pull for details of valid destination formats.
2885 See pull for details of valid destination formats.
2914
2886
2915 Returns 0 if there are outgoing changes, 1 otherwise.
2887 Returns 0 if there are outgoing changes, 1 otherwise.
2916 """
2888 """
2917
2889
2918 if opts.get('bookmarks'):
2890 if opts.get('bookmarks'):
2919 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2891 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2920 dest, branches = hg.parseurl(dest, opts.get('branch'))
2892 dest, branches = hg.parseurl(dest, opts.get('branch'))
2921 other = hg.repository(hg.remoteui(repo, opts), dest)
2893 other = hg.repository(hg.remoteui(repo, opts), dest)
2922 if 'bookmarks' not in other.listkeys('namespaces'):
2894 if 'bookmarks' not in other.listkeys('namespaces'):
2923 ui.warn(_("remote doesn't support bookmarks\n"))
2895 ui.warn(_("remote doesn't support bookmarks\n"))
2924 return 0
2896 return 0
2925 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2897 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2926 return bookmarks.diff(ui, other, repo)
2898 return bookmarks.diff(ui, other, repo)
2927
2899
2928 ret = hg.outgoing(ui, repo, dest, opts)
2900 ret = hg.outgoing(ui, repo, dest, opts)
2929 return ret
2901 return ret
2930
2902
2931 def parents(ui, repo, file_=None, **opts):
2903 def parents(ui, repo, file_=None, **opts):
2932 """show the parents of the working directory or revision
2904 """show the parents of the working directory or revision
2933
2905
2934 Print the working directory's parent revisions. If a revision is
2906 Print the working directory's parent revisions. If a revision is
2935 given via -r/--rev, the parent of that revision will be printed.
2907 given via -r/--rev, the parent of that revision will be printed.
2936 If a file argument is given, the revision in which the file was
2908 If a file argument is given, the revision in which the file was
2937 last changed (before the working directory revision or the
2909 last changed (before the working directory revision or the
2938 argument to --rev if given) is printed.
2910 argument to --rev if given) is printed.
2939
2911
2940 Returns 0 on success.
2912 Returns 0 on success.
2941 """
2913 """
2942
2914
2943 ctx = cmdutil.revsingle(repo, opts.get('rev'), None)
2915 ctx = cmdutil.revsingle(repo, opts.get('rev'), None)
2944
2916
2945 if file_:
2917 if file_:
2946 m = cmdutil.match(repo, (file_,), opts)
2918 m = cmdutil.match(repo, (file_,), opts)
2947 if m.anypats() or len(m.files()) != 1:
2919 if m.anypats() or len(m.files()) != 1:
2948 raise util.Abort(_('can only specify an explicit filename'))
2920 raise util.Abort(_('can only specify an explicit filename'))
2949 file_ = m.files()[0]
2921 file_ = m.files()[0]
2950 filenodes = []
2922 filenodes = []
2951 for cp in ctx.parents():
2923 for cp in ctx.parents():
2952 if not cp:
2924 if not cp:
2953 continue
2925 continue
2954 try:
2926 try:
2955 filenodes.append(cp.filenode(file_))
2927 filenodes.append(cp.filenode(file_))
2956 except error.LookupError:
2928 except error.LookupError:
2957 pass
2929 pass
2958 if not filenodes:
2930 if not filenodes:
2959 raise util.Abort(_("'%s' not found in manifest!") % file_)
2931 raise util.Abort(_("'%s' not found in manifest!") % file_)
2960 fl = repo.file(file_)
2932 fl = repo.file(file_)
2961 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2933 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2962 else:
2934 else:
2963 p = [cp.node() for cp in ctx.parents()]
2935 p = [cp.node() for cp in ctx.parents()]
2964
2936
2965 displayer = cmdutil.show_changeset(ui, repo, opts)
2937 displayer = cmdutil.show_changeset(ui, repo, opts)
2966 for n in p:
2938 for n in p:
2967 if n != nullid:
2939 if n != nullid:
2968 displayer.show(repo[n])
2940 displayer.show(repo[n])
2969 displayer.close()
2941 displayer.close()
2970
2942
2971 def paths(ui, repo, search=None):
2943 def paths(ui, repo, search=None):
2972 """show aliases for remote repositories
2944 """show aliases for remote repositories
2973
2945
2974 Show definition of symbolic path name NAME. If no name is given,
2946 Show definition of symbolic path name NAME. If no name is given,
2975 show definition of all available names.
2947 show definition of all available names.
2976
2948
2977 Path names are defined in the [paths] section of your
2949 Path names are defined in the [paths] section of your
2978 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
2950 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
2979 repository, ``.hg/hgrc`` is used, too.
2951 repository, ``.hg/hgrc`` is used, too.
2980
2952
2981 The path names ``default`` and ``default-push`` have a special
2953 The path names ``default`` and ``default-push`` have a special
2982 meaning. When performing a push or pull operation, they are used
2954 meaning. When performing a push or pull operation, they are used
2983 as fallbacks if no location is specified on the command-line.
2955 as fallbacks if no location is specified on the command-line.
2984 When ``default-push`` is set, it will be used for push and
2956 When ``default-push`` is set, it will be used for push and
2985 ``default`` will be used for pull; otherwise ``default`` is used
2957 ``default`` will be used for pull; otherwise ``default`` is used
2986 as the fallback for both. When cloning a repository, the clone
2958 as the fallback for both. When cloning a repository, the clone
2987 source is written as ``default`` in ``.hg/hgrc``. Note that
2959 source is written as ``default`` in ``.hg/hgrc``. Note that
2988 ``default`` and ``default-push`` apply to all inbound (e.g.
2960 ``default`` and ``default-push`` apply to all inbound (e.g.
2989 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
2961 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
2990 :hg:`bundle`) operations.
2962 :hg:`bundle`) operations.
2991
2963
2992 See :hg:`help urls` for more information.
2964 See :hg:`help urls` for more information.
2993
2965
2994 Returns 0 on success.
2966 Returns 0 on success.
2995 """
2967 """
2996 if search:
2968 if search:
2997 for name, path in ui.configitems("paths"):
2969 for name, path in ui.configitems("paths"):
2998 if name == search:
2970 if name == search:
2999 ui.write("%s\n" % url.hidepassword(path))
2971 ui.write("%s\n" % url.hidepassword(path))
3000 return
2972 return
3001 ui.warn(_("not found!\n"))
2973 ui.warn(_("not found!\n"))
3002 return 1
2974 return 1
3003 else:
2975 else:
3004 for name, path in ui.configitems("paths"):
2976 for name, path in ui.configitems("paths"):
3005 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2977 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
3006
2978
3007 def postincoming(ui, repo, modheads, optupdate, checkout):
2979 def postincoming(ui, repo, modheads, optupdate, checkout):
3008 if modheads == 0:
2980 if modheads == 0:
3009 return
2981 return
3010 if optupdate:
2982 if optupdate:
3011 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2983 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
3012 return hg.update(repo, checkout)
2984 return hg.update(repo, checkout)
3013 else:
2985 else:
3014 ui.status(_("not updating, since new heads added\n"))
2986 ui.status(_("not updating, since new heads added\n"))
3015 if modheads > 1:
2987 if modheads > 1:
3016 currentbranchheads = len(repo.branchheads())
2988 currentbranchheads = len(repo.branchheads())
3017 if currentbranchheads == modheads:
2989 if currentbranchheads == modheads:
3018 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2990 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
3019 elif currentbranchheads > 1:
2991 elif currentbranchheads > 1:
3020 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to merge)\n"))
2992 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to merge)\n"))
3021 else:
2993 else:
3022 ui.status(_("(run 'hg heads' to see heads)\n"))
2994 ui.status(_("(run 'hg heads' to see heads)\n"))
3023 else:
2995 else:
3024 ui.status(_("(run 'hg update' to get a working copy)\n"))
2996 ui.status(_("(run 'hg update' to get a working copy)\n"))
3025
2997
3026 def pull(ui, repo, source="default", **opts):
2998 def pull(ui, repo, source="default", **opts):
3027 """pull changes from the specified source
2999 """pull changes from the specified source
3028
3000
3029 Pull changes from a remote repository to a local one.
3001 Pull changes from a remote repository to a local one.
3030
3002
3031 This finds all changes from the repository at the specified path
3003 This finds all changes from the repository at the specified path
3032 or URL and adds them to a local repository (the current one unless
3004 or URL and adds them to a local repository (the current one unless
3033 -R is specified). By default, this does not update the copy of the
3005 -R is specified). By default, this does not update the copy of the
3034 project in the working directory.
3006 project in the working directory.
3035
3007
3036 Use :hg:`incoming` if you want to see what would have been added
3008 Use :hg:`incoming` if you want to see what would have been added
3037 by a pull at the time you issued this command. If you then decide
3009 by a pull at the time you issued this command. If you then decide
3038 to add those changes to the repository, you should use :hg:`pull
3010 to add those changes to the repository, you should use :hg:`pull
3039 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
3011 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
3040
3012
3041 If SOURCE is omitted, the 'default' path will be used.
3013 If SOURCE is omitted, the 'default' path will be used.
3042 See :hg:`help urls` for more information.
3014 See :hg:`help urls` for more information.
3043
3015
3044 Returns 0 on success, 1 if an update had unresolved files.
3016 Returns 0 on success, 1 if an update had unresolved files.
3045 """
3017 """
3046 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
3018 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
3047 other = hg.repository(hg.remoteui(repo, opts), source)
3019 other = hg.repository(hg.remoteui(repo, opts), source)
3048 ui.status(_('pulling from %s\n') % url.hidepassword(source))
3020 ui.status(_('pulling from %s\n') % url.hidepassword(source))
3049 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3021 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3050
3022
3051 if opts.get('bookmark'):
3023 if opts.get('bookmark'):
3052 if not revs:
3024 if not revs:
3053 revs = []
3025 revs = []
3054 rb = other.listkeys('bookmarks')
3026 rb = other.listkeys('bookmarks')
3055 for b in opts['bookmark']:
3027 for b in opts['bookmark']:
3056 if b not in rb:
3028 if b not in rb:
3057 raise util.Abort(_('remote bookmark %s not found!') % b)
3029 raise util.Abort(_('remote bookmark %s not found!') % b)
3058 revs.append(rb[b])
3030 revs.append(rb[b])
3059
3031
3060 if revs:
3032 if revs:
3061 try:
3033 try:
3062 revs = [other.lookup(rev) for rev in revs]
3034 revs = [other.lookup(rev) for rev in revs]
3063 except error.CapabilityError:
3035 except error.CapabilityError:
3064 err = _("other repository doesn't support revision lookup, "
3036 err = _("other repository doesn't support revision lookup, "
3065 "so a rev cannot be specified.")
3037 "so a rev cannot be specified.")
3066 raise util.Abort(err)
3038 raise util.Abort(err)
3067
3039
3068 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
3040 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
3069 bookmarks.updatefromremote(ui, repo, other)
3041 bookmarks.updatefromremote(ui, repo, other)
3070 if checkout:
3042 if checkout:
3071 checkout = str(repo.changelog.rev(other.lookup(checkout)))
3043 checkout = str(repo.changelog.rev(other.lookup(checkout)))
3072 repo._subtoppath = source
3044 repo._subtoppath = source
3073 try:
3045 try:
3074 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
3046 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
3075
3047
3076 finally:
3048 finally:
3077 del repo._subtoppath
3049 del repo._subtoppath
3078
3050
3079 # update specified bookmarks
3051 # update specified bookmarks
3080 if opts.get('bookmark'):
3052 if opts.get('bookmark'):
3081 for b in opts['bookmark']:
3053 for b in opts['bookmark']:
3082 # explicit pull overrides local bookmark if any
3054 # explicit pull overrides local bookmark if any
3083 ui.status(_("importing bookmark %s\n") % b)
3055 ui.status(_("importing bookmark %s\n") % b)
3084 repo._bookmarks[b] = repo[rb[b]].node()
3056 repo._bookmarks[b] = repo[rb[b]].node()
3085 bookmarks.write(repo)
3057 bookmarks.write(repo)
3086
3058
3087 return ret
3059 return ret
3088
3060
3089 def push(ui, repo, dest=None, **opts):
3061 def push(ui, repo, dest=None, **opts):
3090 """push changes to the specified destination
3062 """push changes to the specified destination
3091
3063
3092 Push changesets from the local repository to the specified
3064 Push changesets from the local repository to the specified
3093 destination.
3065 destination.
3094
3066
3095 This operation is symmetrical to pull: it is identical to a pull
3067 This operation is symmetrical to pull: it is identical to a pull
3096 in the destination repository from the current one.
3068 in the destination repository from the current one.
3097
3069
3098 By default, push will not allow creation of new heads at the
3070 By default, push will not allow creation of new heads at the
3099 destination, since multiple heads would make it unclear which head
3071 destination, since multiple heads would make it unclear which head
3100 to use. In this situation, it is recommended to pull and merge
3072 to use. In this situation, it is recommended to pull and merge
3101 before pushing.
3073 before pushing.
3102
3074
3103 Use --new-branch if you want to allow push to create a new named
3075 Use --new-branch if you want to allow push to create a new named
3104 branch that is not present at the destination. This allows you to
3076 branch that is not present at the destination. This allows you to
3105 only create a new branch without forcing other changes.
3077 only create a new branch without forcing other changes.
3106
3078
3107 Use -f/--force to override the default behavior and push all
3079 Use -f/--force to override the default behavior and push all
3108 changesets on all branches.
3080 changesets on all branches.
3109
3081
3110 If -r/--rev is used, the specified revision and all its ancestors
3082 If -r/--rev is used, the specified revision and all its ancestors
3111 will be pushed to the remote repository.
3083 will be pushed to the remote repository.
3112
3084
3113 Please see :hg:`help urls` for important details about ``ssh://``
3085 Please see :hg:`help urls` for important details about ``ssh://``
3114 URLs. If DESTINATION is omitted, a default path will be used.
3086 URLs. If DESTINATION is omitted, a default path will be used.
3115
3087
3116 Returns 0 if push was successful, 1 if nothing to push.
3088 Returns 0 if push was successful, 1 if nothing to push.
3117 """
3089 """
3118
3090
3119 if opts.get('bookmark'):
3091 if opts.get('bookmark'):
3120 for b in opts['bookmark']:
3092 for b in opts['bookmark']:
3121 # translate -B options to -r so changesets get pushed
3093 # translate -B options to -r so changesets get pushed
3122 if b in repo._bookmarks:
3094 if b in repo._bookmarks:
3123 opts.setdefault('rev', []).append(b)
3095 opts.setdefault('rev', []).append(b)
3124 else:
3096 else:
3125 # if we try to push a deleted bookmark, translate it to null
3097 # if we try to push a deleted bookmark, translate it to null
3126 # this lets simultaneous -r, -b options continue working
3098 # this lets simultaneous -r, -b options continue working
3127 opts.setdefault('rev', []).append("null")
3099 opts.setdefault('rev', []).append("null")
3128
3100
3129 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3101 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3130 dest, branches = hg.parseurl(dest, opts.get('branch'))
3102 dest, branches = hg.parseurl(dest, opts.get('branch'))
3131 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
3103 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
3132 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
3104 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
3133 other = hg.repository(hg.remoteui(repo, opts), dest)
3105 other = hg.repository(hg.remoteui(repo, opts), dest)
3134 if revs:
3106 if revs:
3135 revs = [repo.lookup(rev) for rev in revs]
3107 revs = [repo.lookup(rev) for rev in revs]
3136
3108
3137 repo._subtoppath = dest
3109 repo._subtoppath = dest
3138 try:
3110 try:
3139 # push subrepos depth-first for coherent ordering
3111 # push subrepos depth-first for coherent ordering
3140 c = repo['']
3112 c = repo['']
3141 subs = c.substate # only repos that are committed
3113 subs = c.substate # only repos that are committed
3142 for s in sorted(subs):
3114 for s in sorted(subs):
3143 if not c.sub(s).push(opts.get('force')):
3115 if not c.sub(s).push(opts.get('force')):
3144 return False
3116 return False
3145 finally:
3117 finally:
3146 del repo._subtoppath
3118 del repo._subtoppath
3147 result = repo.push(other, opts.get('force'), revs=revs,
3119 result = repo.push(other, opts.get('force'), revs=revs,
3148 newbranch=opts.get('new_branch'))
3120 newbranch=opts.get('new_branch'))
3149
3121
3150 result = (result == 0)
3122 result = (result == 0)
3151
3123
3152 if opts.get('bookmark'):
3124 if opts.get('bookmark'):
3153 rb = other.listkeys('bookmarks')
3125 rb = other.listkeys('bookmarks')
3154 for b in opts['bookmark']:
3126 for b in opts['bookmark']:
3155 # explicit push overrides remote bookmark if any
3127 # explicit push overrides remote bookmark if any
3156 if b in repo._bookmarks:
3128 if b in repo._bookmarks:
3157 ui.status(_("exporting bookmark %s\n") % b)
3129 ui.status(_("exporting bookmark %s\n") % b)
3158 new = repo[b].hex()
3130 new = repo[b].hex()
3159 elif b in rb:
3131 elif b in rb:
3160 ui.status(_("deleting remote bookmark %s\n") % b)
3132 ui.status(_("deleting remote bookmark %s\n") % b)
3161 new = '' # delete
3133 new = '' # delete
3162 else:
3134 else:
3163 ui.warn(_('bookmark %s does not exist on the local '
3135 ui.warn(_('bookmark %s does not exist on the local '
3164 'or remote repository!\n') % b)
3136 'or remote repository!\n') % b)
3165 return 2
3137 return 2
3166 old = rb.get(b, '')
3138 old = rb.get(b, '')
3167 r = other.pushkey('bookmarks', b, old, new)
3139 r = other.pushkey('bookmarks', b, old, new)
3168 if not r:
3140 if not r:
3169 ui.warn(_('updating bookmark %s failed!\n') % b)
3141 ui.warn(_('updating bookmark %s failed!\n') % b)
3170 if not result:
3142 if not result:
3171 result = 2
3143 result = 2
3172
3144
3173 return result
3145 return result
3174
3146
3175 def recover(ui, repo):
3147 def recover(ui, repo):
3176 """roll back an interrupted transaction
3148 """roll back an interrupted transaction
3177
3149
3178 Recover from an interrupted commit or pull.
3150 Recover from an interrupted commit or pull.
3179
3151
3180 This command tries to fix the repository status after an
3152 This command tries to fix the repository status after an
3181 interrupted operation. It should only be necessary when Mercurial
3153 interrupted operation. It should only be necessary when Mercurial
3182 suggests it.
3154 suggests it.
3183
3155
3184 Returns 0 if successful, 1 if nothing to recover or verify fails.
3156 Returns 0 if successful, 1 if nothing to recover or verify fails.
3185 """
3157 """
3186 if repo.recover():
3158 if repo.recover():
3187 return hg.verify(repo)
3159 return hg.verify(repo)
3188 return 1
3160 return 1
3189
3161
3190 def remove(ui, repo, *pats, **opts):
3162 def remove(ui, repo, *pats, **opts):
3191 """remove the specified files on the next commit
3163 """remove the specified files on the next commit
3192
3164
3193 Schedule the indicated files for removal from the repository.
3165 Schedule the indicated files for removal from the repository.
3194
3166
3195 This only removes files from the current branch, not from the
3167 This only removes files from the current branch, not from the
3196 entire project history. -A/--after can be used to remove only
3168 entire project history. -A/--after can be used to remove only
3197 files that have already been deleted, -f/--force can be used to
3169 files that have already been deleted, -f/--force can be used to
3198 force deletion, and -Af can be used to remove files from the next
3170 force deletion, and -Af can be used to remove files from the next
3199 revision without deleting them from the working directory.
3171 revision without deleting them from the working directory.
3200
3172
3201 The following table details the behavior of remove for different
3173 The following table details the behavior of remove for different
3202 file states (columns) and option combinations (rows). The file
3174 file states (columns) and option combinations (rows). The file
3203 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
3175 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
3204 reported by :hg:`status`). The actions are Warn, Remove (from
3176 reported by :hg:`status`). The actions are Warn, Remove (from
3205 branch) and Delete (from disk)::
3177 branch) and Delete (from disk)::
3206
3178
3207 A C M !
3179 A C M !
3208 none W RD W R
3180 none W RD W R
3209 -f R RD RD R
3181 -f R RD RD R
3210 -A W W W R
3182 -A W W W R
3211 -Af R R R R
3183 -Af R R R R
3212
3184
3213 This command schedules the files to be removed at the next commit.
3185 This command schedules the files to be removed at the next commit.
3214 To undo a remove before that, see :hg:`revert`.
3186 To undo a remove before that, see :hg:`revert`.
3215
3187
3216 Returns 0 on success, 1 if any warnings encountered.
3188 Returns 0 on success, 1 if any warnings encountered.
3217 """
3189 """
3218
3190
3219 ret = 0
3191 ret = 0
3220 after, force = opts.get('after'), opts.get('force')
3192 after, force = opts.get('after'), opts.get('force')
3221 if not pats and not after:
3193 if not pats and not after:
3222 raise util.Abort(_('no files specified'))
3194 raise util.Abort(_('no files specified'))
3223
3195
3224 m = cmdutil.match(repo, pats, opts)
3196 m = cmdutil.match(repo, pats, opts)
3225 s = repo.status(match=m, clean=True)
3197 s = repo.status(match=m, clean=True)
3226 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
3198 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
3227
3199
3228 for f in m.files():
3200 for f in m.files():
3229 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
3201 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
3230 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
3202 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
3231 ret = 1
3203 ret = 1
3232
3204
3233 if force:
3205 if force:
3234 remove, forget = modified + deleted + clean, added
3206 remove, forget = modified + deleted + clean, added
3235 elif after:
3207 elif after:
3236 remove, forget = deleted, []
3208 remove, forget = deleted, []
3237 for f in modified + added + clean:
3209 for f in modified + added + clean:
3238 ui.warn(_('not removing %s: file still exists (use -f'
3210 ui.warn(_('not removing %s: file still exists (use -f'
3239 ' to force removal)\n') % m.rel(f))
3211 ' to force removal)\n') % m.rel(f))
3240 ret = 1
3212 ret = 1
3241 else:
3213 else:
3242 remove, forget = deleted + clean, []
3214 remove, forget = deleted + clean, []
3243 for f in modified:
3215 for f in modified:
3244 ui.warn(_('not removing %s: file is modified (use -f'
3216 ui.warn(_('not removing %s: file is modified (use -f'
3245 ' to force removal)\n') % m.rel(f))
3217 ' to force removal)\n') % m.rel(f))
3246 ret = 1
3218 ret = 1
3247 for f in added:
3219 for f in added:
3248 ui.warn(_('not removing %s: file has been marked for add (use -f'
3220 ui.warn(_('not removing %s: file has been marked for add (use -f'
3249 ' to force removal)\n') % m.rel(f))
3221 ' to force removal)\n') % m.rel(f))
3250 ret = 1
3222 ret = 1
3251
3223
3252 for f in sorted(remove + forget):
3224 for f in sorted(remove + forget):
3253 if ui.verbose or not m.exact(f):
3225 if ui.verbose or not m.exact(f):
3254 ui.status(_('removing %s\n') % m.rel(f))
3226 ui.status(_('removing %s\n') % m.rel(f))
3255
3227
3256 repo[None].forget(forget)
3228 repo[None].forget(forget)
3257 repo[None].remove(remove, unlink=not after)
3229 repo[None].remove(remove, unlink=not after)
3258 return ret
3230 return ret
3259
3231
3260 def rename(ui, repo, *pats, **opts):
3232 def rename(ui, repo, *pats, **opts):
3261 """rename files; equivalent of copy + remove
3233 """rename files; equivalent of copy + remove
3262
3234
3263 Mark dest as copies of sources; mark sources for deletion. If dest
3235 Mark dest as copies of sources; mark sources for deletion. If dest
3264 is a directory, copies are put in that directory. If dest is a
3236 is a directory, copies are put in that directory. If dest is a
3265 file, there can only be one source.
3237 file, there can only be one source.
3266
3238
3267 By default, this command copies the contents of files as they
3239 By default, this command copies the contents of files as they
3268 exist in the working directory. If invoked with -A/--after, the
3240 exist in the working directory. If invoked with -A/--after, the
3269 operation is recorded, but no copying is performed.
3241 operation is recorded, but no copying is performed.
3270
3242
3271 This command takes effect at the next commit. To undo a rename
3243 This command takes effect at the next commit. To undo a rename
3272 before that, see :hg:`revert`.
3244 before that, see :hg:`revert`.
3273
3245
3274 Returns 0 on success, 1 if errors are encountered.
3246 Returns 0 on success, 1 if errors are encountered.
3275 """
3247 """
3276 wlock = repo.wlock(False)
3248 wlock = repo.wlock(False)
3277 try:
3249 try:
3278 return cmdutil.copy(ui, repo, pats, opts, rename=True)
3250 return cmdutil.copy(ui, repo, pats, opts, rename=True)
3279 finally:
3251 finally:
3280 wlock.release()
3252 wlock.release()
3281
3253
3282 def resolve(ui, repo, *pats, **opts):
3254 def resolve(ui, repo, *pats, **opts):
3283 """redo merges or set/view the merge status of files
3255 """redo merges or set/view the merge status of files
3284
3256
3285 Merges with unresolved conflicts are often the result of
3257 Merges with unresolved conflicts are often the result of
3286 non-interactive merging using the ``internal:merge`` configuration
3258 non-interactive merging using the ``internal:merge`` configuration
3287 setting, or a command-line merge tool like ``diff3``. The resolve
3259 setting, or a command-line merge tool like ``diff3``. The resolve
3288 command is used to manage the files involved in a merge, after
3260 command is used to manage the files involved in a merge, after
3289 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
3261 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
3290 working directory must have two parents).
3262 working directory must have two parents).
3291
3263
3292 The resolve command can be used in the following ways:
3264 The resolve command can be used in the following ways:
3293
3265
3294 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
3266 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
3295 files, discarding any previous merge attempts. Re-merging is not
3267 files, discarding any previous merge attempts. Re-merging is not
3296 performed for files already marked as resolved. Use ``--all/-a``
3268 performed for files already marked as resolved. Use ``--all/-a``
3297 to selects all unresolved files. ``--tool`` can be used to specify
3269 to selects all unresolved files. ``--tool`` can be used to specify
3298 the merge tool used for the given files. It overrides the HGMERGE
3270 the merge tool used for the given files. It overrides the HGMERGE
3299 environment variable and your configuration files.
3271 environment variable and your configuration files.
3300
3272
3301 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
3273 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
3302 (e.g. after having manually fixed-up the files). The default is
3274 (e.g. after having manually fixed-up the files). The default is
3303 to mark all unresolved files.
3275 to mark all unresolved files.
3304
3276
3305 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
3277 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
3306 default is to mark all resolved files.
3278 default is to mark all resolved files.
3307
3279
3308 - :hg:`resolve -l`: list files which had or still have conflicts.
3280 - :hg:`resolve -l`: list files which had or still have conflicts.
3309 In the printed list, ``U`` = unresolved and ``R`` = resolved.
3281 In the printed list, ``U`` = unresolved and ``R`` = resolved.
3310
3282
3311 Note that Mercurial will not let you commit files with unresolved
3283 Note that Mercurial will not let you commit files with unresolved
3312 merge conflicts. You must use :hg:`resolve -m ...` before you can
3284 merge conflicts. You must use :hg:`resolve -m ...` before you can
3313 commit after a conflicting merge.
3285 commit after a conflicting merge.
3314
3286
3315 Returns 0 on success, 1 if any files fail a resolve attempt.
3287 Returns 0 on success, 1 if any files fail a resolve attempt.
3316 """
3288 """
3317
3289
3318 all, mark, unmark, show, nostatus = \
3290 all, mark, unmark, show, nostatus = \
3319 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
3291 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
3320
3292
3321 if (show and (mark or unmark)) or (mark and unmark):
3293 if (show and (mark or unmark)) or (mark and unmark):
3322 raise util.Abort(_("too many options specified"))
3294 raise util.Abort(_("too many options specified"))
3323 if pats and all:
3295 if pats and all:
3324 raise util.Abort(_("can't specify --all and patterns"))
3296 raise util.Abort(_("can't specify --all and patterns"))
3325 if not (all or pats or show or mark or unmark):
3297 if not (all or pats or show or mark or unmark):
3326 raise util.Abort(_('no files or directories specified; '
3298 raise util.Abort(_('no files or directories specified; '
3327 'use --all to remerge all files'))
3299 'use --all to remerge all files'))
3328
3300
3329 ms = mergemod.mergestate(repo)
3301 ms = mergemod.mergestate(repo)
3330 m = cmdutil.match(repo, pats, opts)
3302 m = cmdutil.match(repo, pats, opts)
3331 ret = 0
3303 ret = 0
3332
3304
3333 for f in ms:
3305 for f in ms:
3334 if m(f):
3306 if m(f):
3335 if show:
3307 if show:
3336 if nostatus:
3308 if nostatus:
3337 ui.write("%s\n" % f)
3309 ui.write("%s\n" % f)
3338 else:
3310 else:
3339 ui.write("%s %s\n" % (ms[f].upper(), f),
3311 ui.write("%s %s\n" % (ms[f].upper(), f),
3340 label='resolve.' +
3312 label='resolve.' +
3341 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
3313 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
3342 elif mark:
3314 elif mark:
3343 ms.mark(f, "r")
3315 ms.mark(f, "r")
3344 elif unmark:
3316 elif unmark:
3345 ms.mark(f, "u")
3317 ms.mark(f, "u")
3346 else:
3318 else:
3347 wctx = repo[None]
3319 wctx = repo[None]
3348 mctx = wctx.parents()[-1]
3320 mctx = wctx.parents()[-1]
3349
3321
3350 # backup pre-resolve (merge uses .orig for its own purposes)
3322 # backup pre-resolve (merge uses .orig for its own purposes)
3351 a = repo.wjoin(f)
3323 a = repo.wjoin(f)
3352 util.copyfile(a, a + ".resolve")
3324 util.copyfile(a, a + ".resolve")
3353
3325
3354 try:
3326 try:
3355 # resolve file
3327 # resolve file
3356 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
3328 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
3357 if ms.resolve(f, wctx, mctx):
3329 if ms.resolve(f, wctx, mctx):
3358 ret = 1
3330 ret = 1
3359 finally:
3331 finally:
3360 ui.setconfig('ui', 'forcemerge', '')
3332 ui.setconfig('ui', 'forcemerge', '')
3361
3333
3362 # replace filemerge's .orig file with our resolve file
3334 # replace filemerge's .orig file with our resolve file
3363 util.rename(a + ".resolve", a + ".orig")
3335 util.rename(a + ".resolve", a + ".orig")
3364
3336
3365 ms.commit()
3337 ms.commit()
3366 return ret
3338 return ret
3367
3339
3368 def revert(ui, repo, *pats, **opts):
3340 def revert(ui, repo, *pats, **opts):
3369 """restore individual files or directories to an earlier state
3341 """restore individual files or directories to an earlier state
3370
3342
3371 .. note::
3343 .. note::
3372 This command is most likely not what you are looking for.
3344 This command is most likely not what you are looking for.
3373 Revert will partially overwrite content in the working
3345 Revert will partially overwrite content in the working
3374 directory without changing the working directory parents. Use
3346 directory without changing the working directory parents. Use
3375 :hg:`update -r rev` to check out earlier revisions, or
3347 :hg:`update -r rev` to check out earlier revisions, or
3376 :hg:`update --clean .` to undo a merge which has added another
3348 :hg:`update --clean .` to undo a merge which has added another
3377 parent.
3349 parent.
3378
3350
3379 With no revision specified, revert the named files or directories
3351 With no revision specified, revert the named files or directories
3380 to the contents they had in the parent of the working directory.
3352 to the contents they had in the parent of the working directory.
3381 This restores the contents of the affected files to an unmodified
3353 This restores the contents of the affected files to an unmodified
3382 state and unschedules adds, removes, copies, and renames. If the
3354 state and unschedules adds, removes, copies, and renames. If the
3383 working directory has two parents, you must explicitly specify a
3355 working directory has two parents, you must explicitly specify a
3384 revision.
3356 revision.
3385
3357
3386 Using the -r/--rev option, revert the given files or directories
3358 Using the -r/--rev option, revert the given files or directories
3387 to their contents as of a specific revision. This can be helpful
3359 to their contents as of a specific revision. This can be helpful
3388 to "roll back" some or all of an earlier change. See :hg:`help
3360 to "roll back" some or all of an earlier change. See :hg:`help
3389 dates` for a list of formats valid for -d/--date.
3361 dates` for a list of formats valid for -d/--date.
3390
3362
3391 Revert modifies the working directory. It does not commit any
3363 Revert modifies the working directory. It does not commit any
3392 changes, or change the parent of the working directory. If you
3364 changes, or change the parent of the working directory. If you
3393 revert to a revision other than the parent of the working
3365 revert to a revision other than the parent of the working
3394 directory, the reverted files will thus appear modified
3366 directory, the reverted files will thus appear modified
3395 afterwards.
3367 afterwards.
3396
3368
3397 If a file has been deleted, it is restored. Files scheduled for
3369 If a file has been deleted, it is restored. Files scheduled for
3398 addition are just unscheduled and left as they are. If the
3370 addition are just unscheduled and left as they are. If the
3399 executable mode of a file was changed, it is reset.
3371 executable mode of a file was changed, it is reset.
3400
3372
3401 If names are given, all files matching the names are reverted.
3373 If names are given, all files matching the names are reverted.
3402 If no arguments are given, no files are reverted.
3374 If no arguments are given, no files are reverted.
3403
3375
3404 Modified files are saved with a .orig suffix before reverting.
3376 Modified files are saved with a .orig suffix before reverting.
3405 To disable these backups, use --no-backup.
3377 To disable these backups, use --no-backup.
3406
3378
3407 Returns 0 on success.
3379 Returns 0 on success.
3408 """
3380 """
3409
3381
3410 if opts.get("date"):
3382 if opts.get("date"):
3411 if opts.get("rev"):
3383 if opts.get("rev"):
3412 raise util.Abort(_("you can't specify a revision and a date"))
3384 raise util.Abort(_("you can't specify a revision and a date"))
3413 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
3385 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
3414
3386
3415 parent, p2 = repo.dirstate.parents()
3387 parent, p2 = repo.dirstate.parents()
3416 if not opts.get('rev') and p2 != nullid:
3388 if not opts.get('rev') and p2 != nullid:
3417 raise util.Abort(_('uncommitted merge - '
3389 raise util.Abort(_('uncommitted merge - '
3418 'use "hg update", see "hg help revert"'))
3390 'use "hg update", see "hg help revert"'))
3419
3391
3420 if not pats and not opts.get('all'):
3392 if not pats and not opts.get('all'):
3421 raise util.Abort(_('no files or directories specified; '
3393 raise util.Abort(_('no files or directories specified; '
3422 'use --all to revert the whole repo'))
3394 'use --all to revert the whole repo'))
3423
3395
3424 ctx = cmdutil.revsingle(repo, opts.get('rev'))
3396 ctx = cmdutil.revsingle(repo, opts.get('rev'))
3425 node = ctx.node()
3397 node = ctx.node()
3426 mf = ctx.manifest()
3398 mf = ctx.manifest()
3427 if node == parent:
3399 if node == parent:
3428 pmf = mf
3400 pmf = mf
3429 else:
3401 else:
3430 pmf = None
3402 pmf = None
3431
3403
3432 # need all matching names in dirstate and manifest of target rev,
3404 # need all matching names in dirstate and manifest of target rev,
3433 # so have to walk both. do not print errors if files exist in one
3405 # so have to walk both. do not print errors if files exist in one
3434 # but not other.
3406 # but not other.
3435
3407
3436 names = {}
3408 names = {}
3437
3409
3438 wlock = repo.wlock()
3410 wlock = repo.wlock()
3439 try:
3411 try:
3440 # walk dirstate.
3412 # walk dirstate.
3441
3413
3442 m = cmdutil.match(repo, pats, opts)
3414 m = cmdutil.match(repo, pats, opts)
3443 m.bad = lambda x, y: False
3415 m.bad = lambda x, y: False
3444 for abs in repo.walk(m):
3416 for abs in repo.walk(m):
3445 names[abs] = m.rel(abs), m.exact(abs)
3417 names[abs] = m.rel(abs), m.exact(abs)
3446
3418
3447 # walk target manifest.
3419 # walk target manifest.
3448
3420
3449 def badfn(path, msg):
3421 def badfn(path, msg):
3450 if path in names:
3422 if path in names:
3451 return
3423 return
3452 path_ = path + '/'
3424 path_ = path + '/'
3453 for f in names:
3425 for f in names:
3454 if f.startswith(path_):
3426 if f.startswith(path_):
3455 return
3427 return
3456 ui.warn("%s: %s\n" % (m.rel(path), msg))
3428 ui.warn("%s: %s\n" % (m.rel(path), msg))
3457
3429
3458 m = cmdutil.match(repo, pats, opts)
3430 m = cmdutil.match(repo, pats, opts)
3459 m.bad = badfn
3431 m.bad = badfn
3460 for abs in repo[node].walk(m):
3432 for abs in repo[node].walk(m):
3461 if abs not in names:
3433 if abs not in names:
3462 names[abs] = m.rel(abs), m.exact(abs)
3434 names[abs] = m.rel(abs), m.exact(abs)
3463
3435
3464 m = cmdutil.matchfiles(repo, names)
3436 m = cmdutil.matchfiles(repo, names)
3465 changes = repo.status(match=m)[:4]
3437 changes = repo.status(match=m)[:4]
3466 modified, added, removed, deleted = map(set, changes)
3438 modified, added, removed, deleted = map(set, changes)
3467
3439
3468 # if f is a rename, also revert the source
3440 # if f is a rename, also revert the source
3469 cwd = repo.getcwd()
3441 cwd = repo.getcwd()
3470 for f in added:
3442 for f in added:
3471 src = repo.dirstate.copied(f)
3443 src = repo.dirstate.copied(f)
3472 if src and src not in names and repo.dirstate[src] == 'r':
3444 if src and src not in names and repo.dirstate[src] == 'r':
3473 removed.add(src)
3445 removed.add(src)
3474 names[src] = (repo.pathto(src, cwd), True)
3446 names[src] = (repo.pathto(src, cwd), True)
3475
3447
3476 def removeforget(abs):
3448 def removeforget(abs):
3477 if repo.dirstate[abs] == 'a':
3449 if repo.dirstate[abs] == 'a':
3478 return _('forgetting %s\n')
3450 return _('forgetting %s\n')
3479 return _('removing %s\n')
3451 return _('removing %s\n')
3480
3452
3481 revert = ([], _('reverting %s\n'))
3453 revert = ([], _('reverting %s\n'))
3482 add = ([], _('adding %s\n'))
3454 add = ([], _('adding %s\n'))
3483 remove = ([], removeforget)
3455 remove = ([], removeforget)
3484 undelete = ([], _('undeleting %s\n'))
3456 undelete = ([], _('undeleting %s\n'))
3485
3457
3486 disptable = (
3458 disptable = (
3487 # dispatch table:
3459 # dispatch table:
3488 # file state
3460 # file state
3489 # action if in target manifest
3461 # action if in target manifest
3490 # action if not in target manifest
3462 # action if not in target manifest
3491 # make backup if in target manifest
3463 # make backup if in target manifest
3492 # make backup if not in target manifest
3464 # make backup if not in target manifest
3493 (modified, revert, remove, True, True),
3465 (modified, revert, remove, True, True),
3494 (added, revert, remove, True, False),
3466 (added, revert, remove, True, False),
3495 (removed, undelete, None, False, False),
3467 (removed, undelete, None, False, False),
3496 (deleted, revert, remove, False, False),
3468 (deleted, revert, remove, False, False),
3497 )
3469 )
3498
3470
3499 for abs, (rel, exact) in sorted(names.items()):
3471 for abs, (rel, exact) in sorted(names.items()):
3500 mfentry = mf.get(abs)
3472 mfentry = mf.get(abs)
3501 target = repo.wjoin(abs)
3473 target = repo.wjoin(abs)
3502 def handle(xlist, dobackup):
3474 def handle(xlist, dobackup):
3503 xlist[0].append(abs)
3475 xlist[0].append(abs)
3504 if (dobackup and not opts.get('no_backup') and
3476 if (dobackup and not opts.get('no_backup') and
3505 os.path.lexists(target)):
3477 os.path.lexists(target)):
3506 bakname = "%s.orig" % rel
3478 bakname = "%s.orig" % rel
3507 ui.note(_('saving current version of %s as %s\n') %
3479 ui.note(_('saving current version of %s as %s\n') %
3508 (rel, bakname))
3480 (rel, bakname))
3509 if not opts.get('dry_run'):
3481 if not opts.get('dry_run'):
3510 util.rename(target, bakname)
3482 util.rename(target, bakname)
3511 if ui.verbose or not exact:
3483 if ui.verbose or not exact:
3512 msg = xlist[1]
3484 msg = xlist[1]
3513 if not isinstance(msg, basestring):
3485 if not isinstance(msg, basestring):
3514 msg = msg(abs)
3486 msg = msg(abs)
3515 ui.status(msg % rel)
3487 ui.status(msg % rel)
3516 for table, hitlist, misslist, backuphit, backupmiss in disptable:
3488 for table, hitlist, misslist, backuphit, backupmiss in disptable:
3517 if abs not in table:
3489 if abs not in table:
3518 continue
3490 continue
3519 # file has changed in dirstate
3491 # file has changed in dirstate
3520 if mfentry:
3492 if mfentry:
3521 handle(hitlist, backuphit)
3493 handle(hitlist, backuphit)
3522 elif misslist is not None:
3494 elif misslist is not None:
3523 handle(misslist, backupmiss)
3495 handle(misslist, backupmiss)
3524 break
3496 break
3525 else:
3497 else:
3526 if abs not in repo.dirstate:
3498 if abs not in repo.dirstate:
3527 if mfentry:
3499 if mfentry:
3528 handle(add, True)
3500 handle(add, True)
3529 elif exact:
3501 elif exact:
3530 ui.warn(_('file not managed: %s\n') % rel)
3502 ui.warn(_('file not managed: %s\n') % rel)
3531 continue
3503 continue
3532 # file has not changed in dirstate
3504 # file has not changed in dirstate
3533 if node == parent:
3505 if node == parent:
3534 if exact:
3506 if exact:
3535 ui.warn(_('no changes needed to %s\n') % rel)
3507 ui.warn(_('no changes needed to %s\n') % rel)
3536 continue
3508 continue
3537 if pmf is None:
3509 if pmf is None:
3538 # only need parent manifest in this unlikely case,
3510 # only need parent manifest in this unlikely case,
3539 # so do not read by default
3511 # so do not read by default
3540 pmf = repo[parent].manifest()
3512 pmf = repo[parent].manifest()
3541 if abs in pmf:
3513 if abs in pmf:
3542 if mfentry:
3514 if mfentry:
3543 # if version of file is same in parent and target
3515 # if version of file is same in parent and target
3544 # manifests, do nothing
3516 # manifests, do nothing
3545 if (pmf[abs] != mfentry or
3517 if (pmf[abs] != mfentry or
3546 pmf.flags(abs) != mf.flags(abs)):
3518 pmf.flags(abs) != mf.flags(abs)):
3547 handle(revert, False)
3519 handle(revert, False)
3548 else:
3520 else:
3549 handle(remove, False)
3521 handle(remove, False)
3550
3522
3551 if not opts.get('dry_run'):
3523 if not opts.get('dry_run'):
3552 def checkout(f):
3524 def checkout(f):
3553 fc = ctx[f]
3525 fc = ctx[f]
3554 repo.wwrite(f, fc.data(), fc.flags())
3526 repo.wwrite(f, fc.data(), fc.flags())
3555
3527
3556 audit_path = scmutil.path_auditor(repo.root)
3528 audit_path = scmutil.path_auditor(repo.root)
3557 for f in remove[0]:
3529 for f in remove[0]:
3558 if repo.dirstate[f] == 'a':
3530 if repo.dirstate[f] == 'a':
3559 repo.dirstate.forget(f)
3531 repo.dirstate.forget(f)
3560 continue
3532 continue
3561 audit_path(f)
3533 audit_path(f)
3562 try:
3534 try:
3563 util.unlinkpath(repo.wjoin(f))
3535 util.unlinkpath(repo.wjoin(f))
3564 except OSError:
3536 except OSError:
3565 pass
3537 pass
3566 repo.dirstate.remove(f)
3538 repo.dirstate.remove(f)
3567
3539
3568 normal = None
3540 normal = None
3569 if node == parent:
3541 if node == parent:
3570 # We're reverting to our parent. If possible, we'd like status
3542 # We're reverting to our parent. If possible, we'd like status
3571 # to report the file as clean. We have to use normallookup for
3543 # to report the file as clean. We have to use normallookup for
3572 # merges to avoid losing information about merged/dirty files.
3544 # merges to avoid losing information about merged/dirty files.
3573 if p2 != nullid:
3545 if p2 != nullid:
3574 normal = repo.dirstate.normallookup
3546 normal = repo.dirstate.normallookup
3575 else:
3547 else:
3576 normal = repo.dirstate.normal
3548 normal = repo.dirstate.normal
3577 for f in revert[0]:
3549 for f in revert[0]:
3578 checkout(f)
3550 checkout(f)
3579 if normal:
3551 if normal:
3580 normal(f)
3552 normal(f)
3581
3553
3582 for f in add[0]:
3554 for f in add[0]:
3583 checkout(f)
3555 checkout(f)
3584 repo.dirstate.add(f)
3556 repo.dirstate.add(f)
3585
3557
3586 normal = repo.dirstate.normallookup
3558 normal = repo.dirstate.normallookup
3587 if node == parent and p2 == nullid:
3559 if node == parent and p2 == nullid:
3588 normal = repo.dirstate.normal
3560 normal = repo.dirstate.normal
3589 for f in undelete[0]:
3561 for f in undelete[0]:
3590 checkout(f)
3562 checkout(f)
3591 normal(f)
3563 normal(f)
3592
3564
3593 finally:
3565 finally:
3594 wlock.release()
3566 wlock.release()
3595
3567
3596 def rollback(ui, repo, **opts):
3568 def rollback(ui, repo, **opts):
3597 """roll back the last transaction (dangerous)
3569 """roll back the last transaction (dangerous)
3598
3570
3599 This command should be used with care. There is only one level of
3571 This command should be used with care. There is only one level of
3600 rollback, and there is no way to undo a rollback. It will also
3572 rollback, and there is no way to undo a rollback. It will also
3601 restore the dirstate at the time of the last transaction, losing
3573 restore the dirstate at the time of the last transaction, losing
3602 any dirstate changes since that time. This command does not alter
3574 any dirstate changes since that time. This command does not alter
3603 the working directory.
3575 the working directory.
3604
3576
3605 Transactions are used to encapsulate the effects of all commands
3577 Transactions are used to encapsulate the effects of all commands
3606 that create new changesets or propagate existing changesets into a
3578 that create new changesets or propagate existing changesets into a
3607 repository. For example, the following commands are transactional,
3579 repository. For example, the following commands are transactional,
3608 and their effects can be rolled back:
3580 and their effects can be rolled back:
3609
3581
3610 - commit
3582 - commit
3611 - import
3583 - import
3612 - pull
3584 - pull
3613 - push (with this repository as the destination)
3585 - push (with this repository as the destination)
3614 - unbundle
3586 - unbundle
3615
3587
3616 This command is not intended for use on public repositories. Once
3588 This command is not intended for use on public repositories. Once
3617 changes are visible for pull by other users, rolling a transaction
3589 changes are visible for pull by other users, rolling a transaction
3618 back locally is ineffective (someone else may already have pulled
3590 back locally is ineffective (someone else may already have pulled
3619 the changes). Furthermore, a race is possible with readers of the
3591 the changes). Furthermore, a race is possible with readers of the
3620 repository; for example an in-progress pull from the repository
3592 repository; for example an in-progress pull from the repository
3621 may fail if a rollback is performed.
3593 may fail if a rollback is performed.
3622
3594
3623 Returns 0 on success, 1 if no rollback data is available.
3595 Returns 0 on success, 1 if no rollback data is available.
3624 """
3596 """
3625 return repo.rollback(opts.get('dry_run'))
3597 return repo.rollback(opts.get('dry_run'))
3626
3598
3627 def root(ui, repo):
3599 def root(ui, repo):
3628 """print the root (top) of the current working directory
3600 """print the root (top) of the current working directory
3629
3601
3630 Print the root directory of the current repository.
3602 Print the root directory of the current repository.
3631
3603
3632 Returns 0 on success.
3604 Returns 0 on success.
3633 """
3605 """
3634 ui.write(repo.root + "\n")
3606 ui.write(repo.root + "\n")
3635
3607
3636 def serve(ui, repo, **opts):
3608 def serve(ui, repo, **opts):
3637 """start stand-alone webserver
3609 """start stand-alone webserver
3638
3610
3639 Start a local HTTP repository browser and pull server. You can use
3611 Start a local HTTP repository browser and pull server. You can use
3640 this for ad-hoc sharing and browsing of repositories. It is
3612 this for ad-hoc sharing and browsing of repositories. It is
3641 recommended to use a real web server to serve a repository for
3613 recommended to use a real web server to serve a repository for
3642 longer periods of time.
3614 longer periods of time.
3643
3615
3644 Please note that the server does not implement access control.
3616 Please note that the server does not implement access control.
3645 This means that, by default, anybody can read from the server and
3617 This means that, by default, anybody can read from the server and
3646 nobody can write to it by default. Set the ``web.allow_push``
3618 nobody can write to it by default. Set the ``web.allow_push``
3647 option to ``*`` to allow everybody to push to the server. You
3619 option to ``*`` to allow everybody to push to the server. You
3648 should use a real web server if you need to authenticate users.
3620 should use a real web server if you need to authenticate users.
3649
3621
3650 By default, the server logs accesses to stdout and errors to
3622 By default, the server logs accesses to stdout and errors to
3651 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
3623 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
3652 files.
3624 files.
3653
3625
3654 To have the server choose a free port number to listen on, specify
3626 To have the server choose a free port number to listen on, specify
3655 a port number of 0; in this case, the server will print the port
3627 a port number of 0; in this case, the server will print the port
3656 number it uses.
3628 number it uses.
3657
3629
3658 Returns 0 on success.
3630 Returns 0 on success.
3659 """
3631 """
3660
3632
3661 if opts["stdio"]:
3633 if opts["stdio"]:
3662 if repo is None:
3634 if repo is None:
3663 raise error.RepoError(_("There is no Mercurial repository here"
3635 raise error.RepoError(_("There is no Mercurial repository here"
3664 " (.hg not found)"))
3636 " (.hg not found)"))
3665 s = sshserver.sshserver(ui, repo)
3637 s = sshserver.sshserver(ui, repo)
3666 s.serve_forever()
3638 s.serve_forever()
3667
3639
3668 # this way we can check if something was given in the command-line
3640 # this way we can check if something was given in the command-line
3669 if opts.get('port'):
3641 if opts.get('port'):
3670 opts['port'] = util.getport(opts.get('port'))
3642 opts['port'] = util.getport(opts.get('port'))
3671
3643
3672 baseui = repo and repo.baseui or ui
3644 baseui = repo and repo.baseui or ui
3673 optlist = ("name templates style address port prefix ipv6"
3645 optlist = ("name templates style address port prefix ipv6"
3674 " accesslog errorlog certificate encoding")
3646 " accesslog errorlog certificate encoding")
3675 for o in optlist.split():
3647 for o in optlist.split():
3676 val = opts.get(o, '')
3648 val = opts.get(o, '')
3677 if val in (None, ''): # should check against default options instead
3649 if val in (None, ''): # should check against default options instead
3678 continue
3650 continue
3679 baseui.setconfig("web", o, val)
3651 baseui.setconfig("web", o, val)
3680 if repo and repo.ui != baseui:
3652 if repo and repo.ui != baseui:
3681 repo.ui.setconfig("web", o, val)
3653 repo.ui.setconfig("web", o, val)
3682
3654
3683 o = opts.get('web_conf') or opts.get('webdir_conf')
3655 o = opts.get('web_conf') or opts.get('webdir_conf')
3684 if not o:
3656 if not o:
3685 if not repo:
3657 if not repo:
3686 raise error.RepoError(_("There is no Mercurial repository"
3658 raise error.RepoError(_("There is no Mercurial repository"
3687 " here (.hg not found)"))
3659 " here (.hg not found)"))
3688 o = repo.root
3660 o = repo.root
3689
3661
3690 app = hgweb.hgweb(o, baseui=ui)
3662 app = hgweb.hgweb(o, baseui=ui)
3691
3663
3692 class service(object):
3664 class service(object):
3693 def init(self):
3665 def init(self):
3694 util.set_signal_handler()
3666 util.set_signal_handler()
3695 self.httpd = hgweb.server.create_server(ui, app)
3667 self.httpd = hgweb.server.create_server(ui, app)
3696
3668
3697 if opts['port'] and not ui.verbose:
3669 if opts['port'] and not ui.verbose:
3698 return
3670 return
3699
3671
3700 if self.httpd.prefix:
3672 if self.httpd.prefix:
3701 prefix = self.httpd.prefix.strip('/') + '/'
3673 prefix = self.httpd.prefix.strip('/') + '/'
3702 else:
3674 else:
3703 prefix = ''
3675 prefix = ''
3704
3676
3705 port = ':%d' % self.httpd.port
3677 port = ':%d' % self.httpd.port
3706 if port == ':80':
3678 if port == ':80':
3707 port = ''
3679 port = ''
3708
3680
3709 bindaddr = self.httpd.addr
3681 bindaddr = self.httpd.addr
3710 if bindaddr == '0.0.0.0':
3682 if bindaddr == '0.0.0.0':
3711 bindaddr = '*'
3683 bindaddr = '*'
3712 elif ':' in bindaddr: # IPv6
3684 elif ':' in bindaddr: # IPv6
3713 bindaddr = '[%s]' % bindaddr
3685 bindaddr = '[%s]' % bindaddr
3714
3686
3715 fqaddr = self.httpd.fqaddr
3687 fqaddr = self.httpd.fqaddr
3716 if ':' in fqaddr:
3688 if ':' in fqaddr:
3717 fqaddr = '[%s]' % fqaddr
3689 fqaddr = '[%s]' % fqaddr
3718 if opts['port']:
3690 if opts['port']:
3719 write = ui.status
3691 write = ui.status
3720 else:
3692 else:
3721 write = ui.write
3693 write = ui.write
3722 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
3694 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
3723 (fqaddr, port, prefix, bindaddr, self.httpd.port))
3695 (fqaddr, port, prefix, bindaddr, self.httpd.port))
3724
3696
3725 def run(self):
3697 def run(self):
3726 self.httpd.serve_forever()
3698 self.httpd.serve_forever()
3727
3699
3728 service = service()
3700 service = service()
3729
3701
3730 cmdutil.service(opts, initfn=service.init, runfn=service.run)
3702 cmdutil.service(opts, initfn=service.init, runfn=service.run)
3731
3703
3732 def status(ui, repo, *pats, **opts):
3704 def status(ui, repo, *pats, **opts):
3733 """show changed files in the working directory
3705 """show changed files in the working directory
3734
3706
3735 Show status of files in the repository. If names are given, only
3707 Show status of files in the repository. If names are given, only
3736 files that match are shown. Files that are clean or ignored or
3708 files that match are shown. Files that are clean or ignored or
3737 the source of a copy/move operation, are not listed unless
3709 the source of a copy/move operation, are not listed unless
3738 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
3710 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
3739 Unless options described with "show only ..." are given, the
3711 Unless options described with "show only ..." are given, the
3740 options -mardu are used.
3712 options -mardu are used.
3741
3713
3742 Option -q/--quiet hides untracked (unknown and ignored) files
3714 Option -q/--quiet hides untracked (unknown and ignored) files
3743 unless explicitly requested with -u/--unknown or -i/--ignored.
3715 unless explicitly requested with -u/--unknown or -i/--ignored.
3744
3716
3745 .. note::
3717 .. note::
3746 status may appear to disagree with diff if permissions have
3718 status may appear to disagree with diff if permissions have
3747 changed or a merge has occurred. The standard diff format does
3719 changed or a merge has occurred. The standard diff format does
3748 not report permission changes and diff only reports changes
3720 not report permission changes and diff only reports changes
3749 relative to one merge parent.
3721 relative to one merge parent.
3750
3722
3751 If one revision is given, it is used as the base revision.
3723 If one revision is given, it is used as the base revision.
3752 If two revisions are given, the differences between them are
3724 If two revisions are given, the differences between them are
3753 shown. The --change option can also be used as a shortcut to list
3725 shown. The --change option can also be used as a shortcut to list
3754 the changed files of a revision from its first parent.
3726 the changed files of a revision from its first parent.
3755
3727
3756 The codes used to show the status of files are::
3728 The codes used to show the status of files are::
3757
3729
3758 M = modified
3730 M = modified
3759 A = added
3731 A = added
3760 R = removed
3732 R = removed
3761 C = clean
3733 C = clean
3762 ! = missing (deleted by non-hg command, but still tracked)
3734 ! = missing (deleted by non-hg command, but still tracked)
3763 ? = not tracked
3735 ? = not tracked
3764 I = ignored
3736 I = ignored
3765 = origin of the previous file listed as A (added)
3737 = origin of the previous file listed as A (added)
3766
3738
3767 Returns 0 on success.
3739 Returns 0 on success.
3768 """
3740 """
3769
3741
3770 revs = opts.get('rev')
3742 revs = opts.get('rev')
3771 change = opts.get('change')
3743 change = opts.get('change')
3772
3744
3773 if revs and change:
3745 if revs and change:
3774 msg = _('cannot specify --rev and --change at the same time')
3746 msg = _('cannot specify --rev and --change at the same time')
3775 raise util.Abort(msg)
3747 raise util.Abort(msg)
3776 elif change:
3748 elif change:
3777 node2 = repo.lookup(change)
3749 node2 = repo.lookup(change)
3778 node1 = repo[node2].p1().node()
3750 node1 = repo[node2].p1().node()
3779 else:
3751 else:
3780 node1, node2 = cmdutil.revpair(repo, revs)
3752 node1, node2 = cmdutil.revpair(repo, revs)
3781
3753
3782 cwd = (pats and repo.getcwd()) or ''
3754 cwd = (pats and repo.getcwd()) or ''
3783 end = opts.get('print0') and '\0' or '\n'
3755 end = opts.get('print0') and '\0' or '\n'
3784 copy = {}
3756 copy = {}
3785 states = 'modified added removed deleted unknown ignored clean'.split()
3757 states = 'modified added removed deleted unknown ignored clean'.split()
3786 show = [k for k in states if opts.get(k)]
3758 show = [k for k in states if opts.get(k)]
3787 if opts.get('all'):
3759 if opts.get('all'):
3788 show += ui.quiet and (states[:4] + ['clean']) or states
3760 show += ui.quiet and (states[:4] + ['clean']) or states
3789 if not show:
3761 if not show:
3790 show = ui.quiet and states[:4] or states[:5]
3762 show = ui.quiet and states[:4] or states[:5]
3791
3763
3792 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
3764 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
3793 'ignored' in show, 'clean' in show, 'unknown' in show,
3765 'ignored' in show, 'clean' in show, 'unknown' in show,
3794 opts.get('subrepos'))
3766 opts.get('subrepos'))
3795 changestates = zip(states, 'MAR!?IC', stat)
3767 changestates = zip(states, 'MAR!?IC', stat)
3796
3768
3797 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
3769 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
3798 ctxn = repo[nullid]
3770 ctxn = repo[nullid]
3799 ctx1 = repo[node1]
3771 ctx1 = repo[node1]
3800 ctx2 = repo[node2]
3772 ctx2 = repo[node2]
3801 added = stat[1]
3773 added = stat[1]
3802 if node2 is None:
3774 if node2 is None:
3803 added = stat[0] + stat[1] # merged?
3775 added = stat[0] + stat[1] # merged?
3804
3776
3805 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
3777 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
3806 if k in added:
3778 if k in added:
3807 copy[k] = v
3779 copy[k] = v
3808 elif v in added:
3780 elif v in added:
3809 copy[v] = k
3781 copy[v] = k
3810
3782
3811 for state, char, files in changestates:
3783 for state, char, files in changestates:
3812 if state in show:
3784 if state in show:
3813 format = "%s %%s%s" % (char, end)
3785 format = "%s %%s%s" % (char, end)
3814 if opts.get('no_status'):
3786 if opts.get('no_status'):
3815 format = "%%s%s" % end
3787 format = "%%s%s" % end
3816
3788
3817 for f in files:
3789 for f in files:
3818 ui.write(format % repo.pathto(f, cwd),
3790 ui.write(format % repo.pathto(f, cwd),
3819 label='status.' + state)
3791 label='status.' + state)
3820 if f in copy:
3792 if f in copy:
3821 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
3793 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
3822 label='status.copied')
3794 label='status.copied')
3823
3795
3824 def summary(ui, repo, **opts):
3796 def summary(ui, repo, **opts):
3825 """summarize working directory state
3797 """summarize working directory state
3826
3798
3827 This generates a brief summary of the working directory state,
3799 This generates a brief summary of the working directory state,
3828 including parents, branch, commit status, and available updates.
3800 including parents, branch, commit status, and available updates.
3829
3801
3830 With the --remote option, this will check the default paths for
3802 With the --remote option, this will check the default paths for
3831 incoming and outgoing changes. This can be time-consuming.
3803 incoming and outgoing changes. This can be time-consuming.
3832
3804
3833 Returns 0 on success.
3805 Returns 0 on success.
3834 """
3806 """
3835
3807
3836 ctx = repo[None]
3808 ctx = repo[None]
3837 parents = ctx.parents()
3809 parents = ctx.parents()
3838 pnode = parents[0].node()
3810 pnode = parents[0].node()
3839
3811
3840 for p in parents:
3812 for p in parents:
3841 # label with log.changeset (instead of log.parent) since this
3813 # label with log.changeset (instead of log.parent) since this
3842 # shows a working directory parent *changeset*:
3814 # shows a working directory parent *changeset*:
3843 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
3815 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
3844 label='log.changeset')
3816 label='log.changeset')
3845 ui.write(' '.join(p.tags()), label='log.tag')
3817 ui.write(' '.join(p.tags()), label='log.tag')
3846 if p.bookmarks():
3818 if p.bookmarks():
3847 ui.write(' ' + ' '.join(p.bookmarks()), label='log.bookmark')
3819 ui.write(' ' + ' '.join(p.bookmarks()), label='log.bookmark')
3848 if p.rev() == -1:
3820 if p.rev() == -1:
3849 if not len(repo):
3821 if not len(repo):
3850 ui.write(_(' (empty repository)'))
3822 ui.write(_(' (empty repository)'))
3851 else:
3823 else:
3852 ui.write(_(' (no revision checked out)'))
3824 ui.write(_(' (no revision checked out)'))
3853 ui.write('\n')
3825 ui.write('\n')
3854 if p.description():
3826 if p.description():
3855 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
3827 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
3856 label='log.summary')
3828 label='log.summary')
3857
3829
3858 branch = ctx.branch()
3830 branch = ctx.branch()
3859 bheads = repo.branchheads(branch)
3831 bheads = repo.branchheads(branch)
3860 m = _('branch: %s\n') % branch
3832 m = _('branch: %s\n') % branch
3861 if branch != 'default':
3833 if branch != 'default':
3862 ui.write(m, label='log.branch')
3834 ui.write(m, label='log.branch')
3863 else:
3835 else:
3864 ui.status(m, label='log.branch')
3836 ui.status(m, label='log.branch')
3865
3837
3866 st = list(repo.status(unknown=True))[:6]
3838 st = list(repo.status(unknown=True))[:6]
3867
3839
3868 c = repo.dirstate.copies()
3840 c = repo.dirstate.copies()
3869 copied, renamed = [], []
3841 copied, renamed = [], []
3870 for d, s in c.iteritems():
3842 for d, s in c.iteritems():
3871 if s in st[2]:
3843 if s in st[2]:
3872 st[2].remove(s)
3844 st[2].remove(s)
3873 renamed.append(d)
3845 renamed.append(d)
3874 else:
3846 else:
3875 copied.append(d)
3847 copied.append(d)
3876 if d in st[1]:
3848 if d in st[1]:
3877 st[1].remove(d)
3849 st[1].remove(d)
3878 st.insert(3, renamed)
3850 st.insert(3, renamed)
3879 st.insert(4, copied)
3851 st.insert(4, copied)
3880
3852
3881 ms = mergemod.mergestate(repo)
3853 ms = mergemod.mergestate(repo)
3882 st.append([f for f in ms if ms[f] == 'u'])
3854 st.append([f for f in ms if ms[f] == 'u'])
3883
3855
3884 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
3856 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
3885 st.append(subs)
3857 st.append(subs)
3886
3858
3887 labels = [ui.label(_('%d modified'), 'status.modified'),
3859 labels = [ui.label(_('%d modified'), 'status.modified'),
3888 ui.label(_('%d added'), 'status.added'),
3860 ui.label(_('%d added'), 'status.added'),
3889 ui.label(_('%d removed'), 'status.removed'),
3861 ui.label(_('%d removed'), 'status.removed'),
3890 ui.label(_('%d renamed'), 'status.copied'),
3862 ui.label(_('%d renamed'), 'status.copied'),
3891 ui.label(_('%d copied'), 'status.copied'),
3863 ui.label(_('%d copied'), 'status.copied'),
3892 ui.label(_('%d deleted'), 'status.deleted'),
3864 ui.label(_('%d deleted'), 'status.deleted'),
3893 ui.label(_('%d unknown'), 'status.unknown'),
3865 ui.label(_('%d unknown'), 'status.unknown'),
3894 ui.label(_('%d ignored'), 'status.ignored'),
3866 ui.label(_('%d ignored'), 'status.ignored'),
3895 ui.label(_('%d unresolved'), 'resolve.unresolved'),
3867 ui.label(_('%d unresolved'), 'resolve.unresolved'),
3896 ui.label(_('%d subrepos'), 'status.modified')]
3868 ui.label(_('%d subrepos'), 'status.modified')]
3897 t = []
3869 t = []
3898 for s, l in zip(st, labels):
3870 for s, l in zip(st, labels):
3899 if s:
3871 if s:
3900 t.append(l % len(s))
3872 t.append(l % len(s))
3901
3873
3902 t = ', '.join(t)
3874 t = ', '.join(t)
3903 cleanworkdir = False
3875 cleanworkdir = False
3904
3876
3905 if len(parents) > 1:
3877 if len(parents) > 1:
3906 t += _(' (merge)')
3878 t += _(' (merge)')
3907 elif branch != parents[0].branch():
3879 elif branch != parents[0].branch():
3908 t += _(' (new branch)')
3880 t += _(' (new branch)')
3909 elif (parents[0].extra().get('close') and
3881 elif (parents[0].extra().get('close') and
3910 pnode in repo.branchheads(branch, closed=True)):
3882 pnode in repo.branchheads(branch, closed=True)):
3911 t += _(' (head closed)')
3883 t += _(' (head closed)')
3912 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
3884 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
3913 t += _(' (clean)')
3885 t += _(' (clean)')
3914 cleanworkdir = True
3886 cleanworkdir = True
3915 elif pnode not in bheads:
3887 elif pnode not in bheads:
3916 t += _(' (new branch head)')
3888 t += _(' (new branch head)')
3917
3889
3918 if cleanworkdir:
3890 if cleanworkdir:
3919 ui.status(_('commit: %s\n') % t.strip())
3891 ui.status(_('commit: %s\n') % t.strip())
3920 else:
3892 else:
3921 ui.write(_('commit: %s\n') % t.strip())
3893 ui.write(_('commit: %s\n') % t.strip())
3922
3894
3923 # all ancestors of branch heads - all ancestors of parent = new csets
3895 # all ancestors of branch heads - all ancestors of parent = new csets
3924 new = [0] * len(repo)
3896 new = [0] * len(repo)
3925 cl = repo.changelog
3897 cl = repo.changelog
3926 for a in [cl.rev(n) for n in bheads]:
3898 for a in [cl.rev(n) for n in bheads]:
3927 new[a] = 1
3899 new[a] = 1
3928 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
3900 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
3929 new[a] = 1
3901 new[a] = 1
3930 for a in [p.rev() for p in parents]:
3902 for a in [p.rev() for p in parents]:
3931 if a >= 0:
3903 if a >= 0:
3932 new[a] = 0
3904 new[a] = 0
3933 for a in cl.ancestors(*[p.rev() for p in parents]):
3905 for a in cl.ancestors(*[p.rev() for p in parents]):
3934 new[a] = 0
3906 new[a] = 0
3935 new = sum(new)
3907 new = sum(new)
3936
3908
3937 if new == 0:
3909 if new == 0:
3938 ui.status(_('update: (current)\n'))
3910 ui.status(_('update: (current)\n'))
3939 elif pnode not in bheads:
3911 elif pnode not in bheads:
3940 ui.write(_('update: %d new changesets (update)\n') % new)
3912 ui.write(_('update: %d new changesets (update)\n') % new)
3941 else:
3913 else:
3942 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
3914 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
3943 (new, len(bheads)))
3915 (new, len(bheads)))
3944
3916
3945 if opts.get('remote'):
3917 if opts.get('remote'):
3946 t = []
3918 t = []
3947 source, branches = hg.parseurl(ui.expandpath('default'))
3919 source, branches = hg.parseurl(ui.expandpath('default'))
3948 other = hg.repository(hg.remoteui(repo, {}), source)
3920 other = hg.repository(hg.remoteui(repo, {}), source)
3949 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3921 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3950 ui.debug('comparing with %s\n' % url.hidepassword(source))
3922 ui.debug('comparing with %s\n' % url.hidepassword(source))
3951 repo.ui.pushbuffer()
3923 repo.ui.pushbuffer()
3952 common, incoming, rheads = discovery.findcommonincoming(repo, other)
3924 common, incoming, rheads = discovery.findcommonincoming(repo, other)
3953 repo.ui.popbuffer()
3925 repo.ui.popbuffer()
3954 if incoming:
3926 if incoming:
3955 t.append(_('1 or more incoming'))
3927 t.append(_('1 or more incoming'))
3956
3928
3957 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
3929 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
3958 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3930 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3959 other = hg.repository(hg.remoteui(repo, {}), dest)
3931 other = hg.repository(hg.remoteui(repo, {}), dest)
3960 ui.debug('comparing with %s\n' % url.hidepassword(dest))
3932 ui.debug('comparing with %s\n' % url.hidepassword(dest))
3961 repo.ui.pushbuffer()
3933 repo.ui.pushbuffer()
3962 o = discovery.findoutgoing(repo, other)
3934 common, _anyinc, _heads = discovery.findcommonincoming(repo, other)
3963 repo.ui.popbuffer()
3935 repo.ui.popbuffer()
3964 o = repo.changelog.nodesbetween(o, None)[0]
3936 o = repo.changelog.findmissing(common=common)
3965 if o:
3937 if o:
3966 t.append(_('%d outgoing') % len(o))
3938 t.append(_('%d outgoing') % len(o))
3967 if 'bookmarks' in other.listkeys('namespaces'):
3939 if 'bookmarks' in other.listkeys('namespaces'):
3968 lmarks = repo.listkeys('bookmarks')
3940 lmarks = repo.listkeys('bookmarks')
3969 rmarks = other.listkeys('bookmarks')
3941 rmarks = other.listkeys('bookmarks')
3970 diff = set(rmarks) - set(lmarks)
3942 diff = set(rmarks) - set(lmarks)
3971 if len(diff) > 0:
3943 if len(diff) > 0:
3972 t.append(_('%d incoming bookmarks') % len(diff))
3944 t.append(_('%d incoming bookmarks') % len(diff))
3973 diff = set(lmarks) - set(rmarks)
3945 diff = set(lmarks) - set(rmarks)
3974 if len(diff) > 0:
3946 if len(diff) > 0:
3975 t.append(_('%d outgoing bookmarks') % len(diff))
3947 t.append(_('%d outgoing bookmarks') % len(diff))
3976
3948
3977 if t:
3949 if t:
3978 ui.write(_('remote: %s\n') % (', '.join(t)))
3950 ui.write(_('remote: %s\n') % (', '.join(t)))
3979 else:
3951 else:
3980 ui.status(_('remote: (synced)\n'))
3952 ui.status(_('remote: (synced)\n'))
3981
3953
3982 def tag(ui, repo, name1, *names, **opts):
3954 def tag(ui, repo, name1, *names, **opts):
3983 """add one or more tags for the current or given revision
3955 """add one or more tags for the current or given revision
3984
3956
3985 Name a particular revision using <name>.
3957 Name a particular revision using <name>.
3986
3958
3987 Tags are used to name particular revisions of the repository and are
3959 Tags are used to name particular revisions of the repository and are
3988 very useful to compare different revisions, to go back to significant
3960 very useful to compare different revisions, to go back to significant
3989 earlier versions or to mark branch points as releases, etc. Changing
3961 earlier versions or to mark branch points as releases, etc. Changing
3990 an existing tag is normally disallowed; use -f/--force to override.
3962 an existing tag is normally disallowed; use -f/--force to override.
3991
3963
3992 If no revision is given, the parent of the working directory is
3964 If no revision is given, the parent of the working directory is
3993 used, or tip if no revision is checked out.
3965 used, or tip if no revision is checked out.
3994
3966
3995 To facilitate version control, distribution, and merging of tags,
3967 To facilitate version control, distribution, and merging of tags,
3996 they are stored as a file named ".hgtags" which is managed similarly
3968 they are stored as a file named ".hgtags" which is managed similarly
3997 to other project files and can be hand-edited if necessary. This
3969 to other project files and can be hand-edited if necessary. This
3998 also means that tagging creates a new commit. The file
3970 also means that tagging creates a new commit. The file
3999 ".hg/localtags" is used for local tags (not shared among
3971 ".hg/localtags" is used for local tags (not shared among
4000 repositories).
3972 repositories).
4001
3973
4002 Tag commits are usually made at the head of a branch. If the parent
3974 Tag commits are usually made at the head of a branch. If the parent
4003 of the working directory is not a branch head, :hg:`tag` aborts; use
3975 of the working directory is not a branch head, :hg:`tag` aborts; use
4004 -f/--force to force the tag commit to be based on a non-head
3976 -f/--force to force the tag commit to be based on a non-head
4005 changeset.
3977 changeset.
4006
3978
4007 See :hg:`help dates` for a list of formats valid for -d/--date.
3979 See :hg:`help dates` for a list of formats valid for -d/--date.
4008
3980
4009 Since tag names have priority over branch names during revision
3981 Since tag names have priority over branch names during revision
4010 lookup, using an existing branch name as a tag name is discouraged.
3982 lookup, using an existing branch name as a tag name is discouraged.
4011
3983
4012 Returns 0 on success.
3984 Returns 0 on success.
4013 """
3985 """
4014
3986
4015 rev_ = "."
3987 rev_ = "."
4016 names = [t.strip() for t in (name1,) + names]
3988 names = [t.strip() for t in (name1,) + names]
4017 if len(names) != len(set(names)):
3989 if len(names) != len(set(names)):
4018 raise util.Abort(_('tag names must be unique'))
3990 raise util.Abort(_('tag names must be unique'))
4019 for n in names:
3991 for n in names:
4020 if n in ['tip', '.', 'null']:
3992 if n in ['tip', '.', 'null']:
4021 raise util.Abort(_('the name \'%s\' is reserved') % n)
3993 raise util.Abort(_('the name \'%s\' is reserved') % n)
4022 if not n:
3994 if not n:
4023 raise util.Abort(_('tag names cannot consist entirely of whitespace'))
3995 raise util.Abort(_('tag names cannot consist entirely of whitespace'))
4024 if opts.get('rev') and opts.get('remove'):
3996 if opts.get('rev') and opts.get('remove'):
4025 raise util.Abort(_("--rev and --remove are incompatible"))
3997 raise util.Abort(_("--rev and --remove are incompatible"))
4026 if opts.get('rev'):
3998 if opts.get('rev'):
4027 rev_ = opts['rev']
3999 rev_ = opts['rev']
4028 message = opts.get('message')
4000 message = opts.get('message')
4029 if opts.get('remove'):
4001 if opts.get('remove'):
4030 expectedtype = opts.get('local') and 'local' or 'global'
4002 expectedtype = opts.get('local') and 'local' or 'global'
4031 for n in names:
4003 for n in names:
4032 if not repo.tagtype(n):
4004 if not repo.tagtype(n):
4033 raise util.Abort(_('tag \'%s\' does not exist') % n)
4005 raise util.Abort(_('tag \'%s\' does not exist') % n)
4034 if repo.tagtype(n) != expectedtype:
4006 if repo.tagtype(n) != expectedtype:
4035 if expectedtype == 'global':
4007 if expectedtype == 'global':
4036 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
4008 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
4037 else:
4009 else:
4038 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
4010 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
4039 rev_ = nullid
4011 rev_ = nullid
4040 if not message:
4012 if not message:
4041 # we don't translate commit messages
4013 # we don't translate commit messages
4042 message = 'Removed tag %s' % ', '.join(names)
4014 message = 'Removed tag %s' % ', '.join(names)
4043 elif not opts.get('force'):
4015 elif not opts.get('force'):
4044 for n in names:
4016 for n in names:
4045 if n in repo.tags():
4017 if n in repo.tags():
4046 raise util.Abort(_('tag \'%s\' already exists '
4018 raise util.Abort(_('tag \'%s\' already exists '
4047 '(use -f to force)') % n)
4019 '(use -f to force)') % n)
4048 if not opts.get('local'):
4020 if not opts.get('local'):
4049 p1, p2 = repo.dirstate.parents()
4021 p1, p2 = repo.dirstate.parents()
4050 if p2 != nullid:
4022 if p2 != nullid:
4051 raise util.Abort(_('uncommitted merge'))
4023 raise util.Abort(_('uncommitted merge'))
4052 bheads = repo.branchheads()
4024 bheads = repo.branchheads()
4053 if not opts.get('force') and bheads and p1 not in bheads:
4025 if not opts.get('force') and bheads and p1 not in bheads:
4054 raise util.Abort(_('not at a branch head (use -f to force)'))
4026 raise util.Abort(_('not at a branch head (use -f to force)'))
4055 r = cmdutil.revsingle(repo, rev_).node()
4027 r = cmdutil.revsingle(repo, rev_).node()
4056
4028
4057 if not message:
4029 if not message:
4058 # we don't translate commit messages
4030 # we don't translate commit messages
4059 message = ('Added tag %s for changeset %s' %
4031 message = ('Added tag %s for changeset %s' %
4060 (', '.join(names), short(r)))
4032 (', '.join(names), short(r)))
4061
4033
4062 date = opts.get('date')
4034 date = opts.get('date')
4063 if date:
4035 if date:
4064 date = util.parsedate(date)
4036 date = util.parsedate(date)
4065
4037
4066 if opts.get('edit'):
4038 if opts.get('edit'):
4067 message = ui.edit(message, ui.username())
4039 message = ui.edit(message, ui.username())
4068
4040
4069 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
4041 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
4070
4042
4071 def tags(ui, repo):
4043 def tags(ui, repo):
4072 """list repository tags
4044 """list repository tags
4073
4045
4074 This lists both regular and local tags. When the -v/--verbose
4046 This lists both regular and local tags. When the -v/--verbose
4075 switch is used, a third column "local" is printed for local tags.
4047 switch is used, a third column "local" is printed for local tags.
4076
4048
4077 Returns 0 on success.
4049 Returns 0 on success.
4078 """
4050 """
4079
4051
4080 hexfunc = ui.debugflag and hex or short
4052 hexfunc = ui.debugflag and hex or short
4081 tagtype = ""
4053 tagtype = ""
4082
4054
4083 for t, n in reversed(repo.tagslist()):
4055 for t, n in reversed(repo.tagslist()):
4084 if ui.quiet:
4056 if ui.quiet:
4085 ui.write("%s\n" % t)
4057 ui.write("%s\n" % t)
4086 continue
4058 continue
4087
4059
4088 hn = hexfunc(n)
4060 hn = hexfunc(n)
4089 r = "%5d:%s" % (repo.changelog.rev(n), hn)
4061 r = "%5d:%s" % (repo.changelog.rev(n), hn)
4090 spaces = " " * (30 - encoding.colwidth(t))
4062 spaces = " " * (30 - encoding.colwidth(t))
4091
4063
4092 if ui.verbose:
4064 if ui.verbose:
4093 if repo.tagtype(t) == 'local':
4065 if repo.tagtype(t) == 'local':
4094 tagtype = " local"
4066 tagtype = " local"
4095 else:
4067 else:
4096 tagtype = ""
4068 tagtype = ""
4097 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
4069 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
4098
4070
4099 def tip(ui, repo, **opts):
4071 def tip(ui, repo, **opts):
4100 """show the tip revision
4072 """show the tip revision
4101
4073
4102 The tip revision (usually just called the tip) is the changeset
4074 The tip revision (usually just called the tip) is the changeset
4103 most recently added to the repository (and therefore the most
4075 most recently added to the repository (and therefore the most
4104 recently changed head).
4076 recently changed head).
4105
4077
4106 If you have just made a commit, that commit will be the tip. If
4078 If you have just made a commit, that commit will be the tip. If
4107 you have just pulled changes from another repository, the tip of
4079 you have just pulled changes from another repository, the tip of
4108 that repository becomes the current tip. The "tip" tag is special
4080 that repository becomes the current tip. The "tip" tag is special
4109 and cannot be renamed or assigned to a different changeset.
4081 and cannot be renamed or assigned to a different changeset.
4110
4082
4111 Returns 0 on success.
4083 Returns 0 on success.
4112 """
4084 """
4113 displayer = cmdutil.show_changeset(ui, repo, opts)
4085 displayer = cmdutil.show_changeset(ui, repo, opts)
4114 displayer.show(repo[len(repo) - 1])
4086 displayer.show(repo[len(repo) - 1])
4115 displayer.close()
4087 displayer.close()
4116
4088
4117 def unbundle(ui, repo, fname1, *fnames, **opts):
4089 def unbundle(ui, repo, fname1, *fnames, **opts):
4118 """apply one or more changegroup files
4090 """apply one or more changegroup files
4119
4091
4120 Apply one or more compressed changegroup files generated by the
4092 Apply one or more compressed changegroup files generated by the
4121 bundle command.
4093 bundle command.
4122
4094
4123 Returns 0 on success, 1 if an update has unresolved files.
4095 Returns 0 on success, 1 if an update has unresolved files.
4124 """
4096 """
4125 fnames = (fname1,) + fnames
4097 fnames = (fname1,) + fnames
4126
4098
4127 lock = repo.lock()
4099 lock = repo.lock()
4128 wc = repo['.']
4100 wc = repo['.']
4129 try:
4101 try:
4130 for fname in fnames:
4102 for fname in fnames:
4131 f = url.open(ui, fname)
4103 f = url.open(ui, fname)
4132 gen = changegroup.readbundle(f, fname)
4104 gen = changegroup.readbundle(f, fname)
4133 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname,
4105 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname,
4134 lock=lock)
4106 lock=lock)
4135 bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
4107 bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
4136 finally:
4108 finally:
4137 lock.release()
4109 lock.release()
4138 return postincoming(ui, repo, modheads, opts.get('update'), None)
4110 return postincoming(ui, repo, modheads, opts.get('update'), None)
4139
4111
4140 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
4112 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
4141 """update working directory (or switch revisions)
4113 """update working directory (or switch revisions)
4142
4114
4143 Update the repository's working directory to the specified
4115 Update the repository's working directory to the specified
4144 changeset. If no changeset is specified, update to the tip of the
4116 changeset. If no changeset is specified, update to the tip of the
4145 current named branch.
4117 current named branch.
4146
4118
4147 If the changeset is not a descendant of the working directory's
4119 If the changeset is not a descendant of the working directory's
4148 parent, the update is aborted. With the -c/--check option, the
4120 parent, the update is aborted. With the -c/--check option, the
4149 working directory is checked for uncommitted changes; if none are
4121 working directory is checked for uncommitted changes; if none are
4150 found, the working directory is updated to the specified
4122 found, the working directory is updated to the specified
4151 changeset.
4123 changeset.
4152
4124
4153 The following rules apply when the working directory contains
4125 The following rules apply when the working directory contains
4154 uncommitted changes:
4126 uncommitted changes:
4155
4127
4156 1. If neither -c/--check nor -C/--clean is specified, and if
4128 1. If neither -c/--check nor -C/--clean is specified, and if
4157 the requested changeset is an ancestor or descendant of
4129 the requested changeset is an ancestor or descendant of
4158 the working directory's parent, the uncommitted changes
4130 the working directory's parent, the uncommitted changes
4159 are merged into the requested changeset and the merged
4131 are merged into the requested changeset and the merged
4160 result is left uncommitted. If the requested changeset is
4132 result is left uncommitted. If the requested changeset is
4161 not an ancestor or descendant (that is, it is on another
4133 not an ancestor or descendant (that is, it is on another
4162 branch), the update is aborted and the uncommitted changes
4134 branch), the update is aborted and the uncommitted changes
4163 are preserved.
4135 are preserved.
4164
4136
4165 2. With the -c/--check option, the update is aborted and the
4137 2. With the -c/--check option, the update is aborted and the
4166 uncommitted changes are preserved.
4138 uncommitted changes are preserved.
4167
4139
4168 3. With the -C/--clean option, uncommitted changes are discarded and
4140 3. With the -C/--clean option, uncommitted changes are discarded and
4169 the working directory is updated to the requested changeset.
4141 the working directory is updated to the requested changeset.
4170
4142
4171 Use null as the changeset to remove the working directory (like
4143 Use null as the changeset to remove the working directory (like
4172 :hg:`clone -U`).
4144 :hg:`clone -U`).
4173
4145
4174 If you want to update just one file to an older changeset, use
4146 If you want to update just one file to an older changeset, use
4175 :hg:`revert`.
4147 :hg:`revert`.
4176
4148
4177 See :hg:`help dates` for a list of formats valid for -d/--date.
4149 See :hg:`help dates` for a list of formats valid for -d/--date.
4178
4150
4179 Returns 0 on success, 1 if there are unresolved files.
4151 Returns 0 on success, 1 if there are unresolved files.
4180 """
4152 """
4181 if rev and node:
4153 if rev and node:
4182 raise util.Abort(_("please specify just one revision"))
4154 raise util.Abort(_("please specify just one revision"))
4183
4155
4184 if rev is None or rev == '':
4156 if rev is None or rev == '':
4185 rev = node
4157 rev = node
4186
4158
4187 # if we defined a bookmark, we have to remember the original bookmark name
4159 # if we defined a bookmark, we have to remember the original bookmark name
4188 brev = rev
4160 brev = rev
4189 rev = cmdutil.revsingle(repo, rev, rev).rev()
4161 rev = cmdutil.revsingle(repo, rev, rev).rev()
4190
4162
4191 if check and clean:
4163 if check and clean:
4192 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
4164 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
4193
4165
4194 if check:
4166 if check:
4195 # we could use dirty() but we can ignore merge and branch trivia
4167 # we could use dirty() but we can ignore merge and branch trivia
4196 c = repo[None]
4168 c = repo[None]
4197 if c.modified() or c.added() or c.removed():
4169 if c.modified() or c.added() or c.removed():
4198 raise util.Abort(_("uncommitted local changes"))
4170 raise util.Abort(_("uncommitted local changes"))
4199
4171
4200 if date:
4172 if date:
4201 if rev is not None:
4173 if rev is not None:
4202 raise util.Abort(_("you can't specify a revision and a date"))
4174 raise util.Abort(_("you can't specify a revision and a date"))
4203 rev = cmdutil.finddate(ui, repo, date)
4175 rev = cmdutil.finddate(ui, repo, date)
4204
4176
4205 if clean or check:
4177 if clean or check:
4206 ret = hg.clean(repo, rev)
4178 ret = hg.clean(repo, rev)
4207 else:
4179 else:
4208 ret = hg.update(repo, rev)
4180 ret = hg.update(repo, rev)
4209
4181
4210 if brev in repo._bookmarks:
4182 if brev in repo._bookmarks:
4211 bookmarks.setcurrent(repo, brev)
4183 bookmarks.setcurrent(repo, brev)
4212
4184
4213 return ret
4185 return ret
4214
4186
4215 def verify(ui, repo):
4187 def verify(ui, repo):
4216 """verify the integrity of the repository
4188 """verify the integrity of the repository
4217
4189
4218 Verify the integrity of the current repository.
4190 Verify the integrity of the current repository.
4219
4191
4220 This will perform an extensive check of the repository's
4192 This will perform an extensive check of the repository's
4221 integrity, validating the hashes and checksums of each entry in
4193 integrity, validating the hashes and checksums of each entry in
4222 the changelog, manifest, and tracked files, as well as the
4194 the changelog, manifest, and tracked files, as well as the
4223 integrity of their crosslinks and indices.
4195 integrity of their crosslinks and indices.
4224
4196
4225 Returns 0 on success, 1 if errors are encountered.
4197 Returns 0 on success, 1 if errors are encountered.
4226 """
4198 """
4227 return hg.verify(repo)
4199 return hg.verify(repo)
4228
4200
4229 def version_(ui):
4201 def version_(ui):
4230 """output version and copyright information"""
4202 """output version and copyright information"""
4231 ui.write(_("Mercurial Distributed SCM (version %s)\n")
4203 ui.write(_("Mercurial Distributed SCM (version %s)\n")
4232 % util.version())
4204 % util.version())
4233 ui.status(_(
4205 ui.status(_(
4234 "(see http://mercurial.selenic.com for more information)\n"
4206 "(see http://mercurial.selenic.com for more information)\n"
4235 "\nCopyright (C) 2005-2011 Matt Mackall and others\n"
4207 "\nCopyright (C) 2005-2011 Matt Mackall and others\n"
4236 "This is free software; see the source for copying conditions. "
4208 "This is free software; see the source for copying conditions. "
4237 "There is NO\nwarranty; "
4209 "There is NO\nwarranty; "
4238 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
4210 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
4239 ))
4211 ))
4240
4212
4241 # Command options and aliases are listed here, alphabetically
4213 # Command options and aliases are listed here, alphabetically
4242
4214
4243 globalopts = [
4215 globalopts = [
4244 ('R', 'repository', '',
4216 ('R', 'repository', '',
4245 _('repository root directory or name of overlay bundle file'),
4217 _('repository root directory or name of overlay bundle file'),
4246 _('REPO')),
4218 _('REPO')),
4247 ('', 'cwd', '',
4219 ('', 'cwd', '',
4248 _('change working directory'), _('DIR')),
4220 _('change working directory'), _('DIR')),
4249 ('y', 'noninteractive', None,
4221 ('y', 'noninteractive', None,
4250 _('do not prompt, assume \'yes\' for any required answers')),
4222 _('do not prompt, assume \'yes\' for any required answers')),
4251 ('q', 'quiet', None, _('suppress output')),
4223 ('q', 'quiet', None, _('suppress output')),
4252 ('v', 'verbose', None, _('enable additional output')),
4224 ('v', 'verbose', None, _('enable additional output')),
4253 ('', 'config', [],
4225 ('', 'config', [],
4254 _('set/override config option (use \'section.name=value\')'),
4226 _('set/override config option (use \'section.name=value\')'),
4255 _('CONFIG')),
4227 _('CONFIG')),
4256 ('', 'debug', None, _('enable debugging output')),
4228 ('', 'debug', None, _('enable debugging output')),
4257 ('', 'debugger', None, _('start debugger')),
4229 ('', 'debugger', None, _('start debugger')),
4258 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
4230 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
4259 _('ENCODE')),
4231 _('ENCODE')),
4260 ('', 'encodingmode', encoding.encodingmode,
4232 ('', 'encodingmode', encoding.encodingmode,
4261 _('set the charset encoding mode'), _('MODE')),
4233 _('set the charset encoding mode'), _('MODE')),
4262 ('', 'traceback', None, _('always print a traceback on exception')),
4234 ('', 'traceback', None, _('always print a traceback on exception')),
4263 ('', 'time', None, _('time how long the command takes')),
4235 ('', 'time', None, _('time how long the command takes')),
4264 ('', 'profile', None, _('print command execution profile')),
4236 ('', 'profile', None, _('print command execution profile')),
4265 ('', 'version', None, _('output version information and exit')),
4237 ('', 'version', None, _('output version information and exit')),
4266 ('h', 'help', None, _('display help and exit')),
4238 ('h', 'help', None, _('display help and exit')),
4267 ]
4239 ]
4268
4240
4269 dryrunopts = [('n', 'dry-run', None,
4241 dryrunopts = [('n', 'dry-run', None,
4270 _('do not perform actions, just print output'))]
4242 _('do not perform actions, just print output'))]
4271
4243
4272 remoteopts = [
4244 remoteopts = [
4273 ('e', 'ssh', '',
4245 ('e', 'ssh', '',
4274 _('specify ssh command to use'), _('CMD')),
4246 _('specify ssh command to use'), _('CMD')),
4275 ('', 'remotecmd', '',
4247 ('', 'remotecmd', '',
4276 _('specify hg command to run on the remote side'), _('CMD')),
4248 _('specify hg command to run on the remote side'), _('CMD')),
4277 ('', 'insecure', None,
4249 ('', 'insecure', None,
4278 _('do not verify server certificate (ignoring web.cacerts config)')),
4250 _('do not verify server certificate (ignoring web.cacerts config)')),
4279 ]
4251 ]
4280
4252
4281 walkopts = [
4253 walkopts = [
4282 ('I', 'include', [],
4254 ('I', 'include', [],
4283 _('include names matching the given patterns'), _('PATTERN')),
4255 _('include names matching the given patterns'), _('PATTERN')),
4284 ('X', 'exclude', [],
4256 ('X', 'exclude', [],
4285 _('exclude names matching the given patterns'), _('PATTERN')),
4257 _('exclude names matching the given patterns'), _('PATTERN')),
4286 ]
4258 ]
4287
4259
4288 commitopts = [
4260 commitopts = [
4289 ('m', 'message', '',
4261 ('m', 'message', '',
4290 _('use text as commit message'), _('TEXT')),
4262 _('use text as commit message'), _('TEXT')),
4291 ('l', 'logfile', '',
4263 ('l', 'logfile', '',
4292 _('read commit message from file'), _('FILE')),
4264 _('read commit message from file'), _('FILE')),
4293 ]
4265 ]
4294
4266
4295 commitopts2 = [
4267 commitopts2 = [
4296 ('d', 'date', '',
4268 ('d', 'date', '',
4297 _('record the specified date as commit date'), _('DATE')),
4269 _('record the specified date as commit date'), _('DATE')),
4298 ('u', 'user', '',
4270 ('u', 'user', '',
4299 _('record the specified user as committer'), _('USER')),
4271 _('record the specified user as committer'), _('USER')),
4300 ]
4272 ]
4301
4273
4302 templateopts = [
4274 templateopts = [
4303 ('', 'style', '',
4275 ('', 'style', '',
4304 _('display using template map file'), _('STYLE')),
4276 _('display using template map file'), _('STYLE')),
4305 ('', 'template', '',
4277 ('', 'template', '',
4306 _('display with template'), _('TEMPLATE')),
4278 _('display with template'), _('TEMPLATE')),
4307 ]
4279 ]
4308
4280
4309 logopts = [
4281 logopts = [
4310 ('p', 'patch', None, _('show patch')),
4282 ('p', 'patch', None, _('show patch')),
4311 ('g', 'git', None, _('use git extended diff format')),
4283 ('g', 'git', None, _('use git extended diff format')),
4312 ('l', 'limit', '',
4284 ('l', 'limit', '',
4313 _('limit number of changes displayed'), _('NUM')),
4285 _('limit number of changes displayed'), _('NUM')),
4314 ('M', 'no-merges', None, _('do not show merges')),
4286 ('M', 'no-merges', None, _('do not show merges')),
4315 ('', 'stat', None, _('output diffstat-style summary of changes')),
4287 ('', 'stat', None, _('output diffstat-style summary of changes')),
4316 ] + templateopts
4288 ] + templateopts
4317
4289
4318 diffopts = [
4290 diffopts = [
4319 ('a', 'text', None, _('treat all files as text')),
4291 ('a', 'text', None, _('treat all files as text')),
4320 ('g', 'git', None, _('use git extended diff format')),
4292 ('g', 'git', None, _('use git extended diff format')),
4321 ('', 'nodates', None, _('omit dates from diff headers'))
4293 ('', 'nodates', None, _('omit dates from diff headers'))
4322 ]
4294 ]
4323
4295
4324 diffopts2 = [
4296 diffopts2 = [
4325 ('p', 'show-function', None, _('show which function each change is in')),
4297 ('p', 'show-function', None, _('show which function each change is in')),
4326 ('', 'reverse', None, _('produce a diff that undoes the changes')),
4298 ('', 'reverse', None, _('produce a diff that undoes the changes')),
4327 ('w', 'ignore-all-space', None,
4299 ('w', 'ignore-all-space', None,
4328 _('ignore white space when comparing lines')),
4300 _('ignore white space when comparing lines')),
4329 ('b', 'ignore-space-change', None,
4301 ('b', 'ignore-space-change', None,
4330 _('ignore changes in the amount of white space')),
4302 _('ignore changes in the amount of white space')),
4331 ('B', 'ignore-blank-lines', None,
4303 ('B', 'ignore-blank-lines', None,
4332 _('ignore changes whose lines are all blank')),
4304 _('ignore changes whose lines are all blank')),
4333 ('U', 'unified', '',
4305 ('U', 'unified', '',
4334 _('number of lines of context to show'), _('NUM')),
4306 _('number of lines of context to show'), _('NUM')),
4335 ('', 'stat', None, _('output diffstat-style summary of changes')),
4307 ('', 'stat', None, _('output diffstat-style summary of changes')),
4336 ]
4308 ]
4337
4309
4338 similarityopts = [
4310 similarityopts = [
4339 ('s', 'similarity', '',
4311 ('s', 'similarity', '',
4340 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
4312 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
4341 ]
4313 ]
4342
4314
4343 subrepoopts = [
4315 subrepoopts = [
4344 ('S', 'subrepos', None,
4316 ('S', 'subrepos', None,
4345 _('recurse into subrepositories'))
4317 _('recurse into subrepositories'))
4346 ]
4318 ]
4347
4319
4348 table = {
4320 table = {
4349 "^add": (add, walkopts + subrepoopts + dryrunopts,
4321 "^add": (add, walkopts + subrepoopts + dryrunopts,
4350 _('[OPTION]... [FILE]...')),
4322 _('[OPTION]... [FILE]...')),
4351 "addremove":
4323 "addremove":
4352 (addremove, similarityopts + walkopts + dryrunopts,
4324 (addremove, similarityopts + walkopts + dryrunopts,
4353 _('[OPTION]... [FILE]...')),
4325 _('[OPTION]... [FILE]...')),
4354 "^annotate|blame":
4326 "^annotate|blame":
4355 (annotate,
4327 (annotate,
4356 [('r', 'rev', '',
4328 [('r', 'rev', '',
4357 _('annotate the specified revision'), _('REV')),
4329 _('annotate the specified revision'), _('REV')),
4358 ('', 'follow', None,
4330 ('', 'follow', None,
4359 _('follow copies/renames and list the filename (DEPRECATED)')),
4331 _('follow copies/renames and list the filename (DEPRECATED)')),
4360 ('', 'no-follow', None, _("don't follow copies and renames")),
4332 ('', 'no-follow', None, _("don't follow copies and renames")),
4361 ('a', 'text', None, _('treat all files as text')),
4333 ('a', 'text', None, _('treat all files as text')),
4362 ('u', 'user', None, _('list the author (long with -v)')),
4334 ('u', 'user', None, _('list the author (long with -v)')),
4363 ('f', 'file', None, _('list the filename')),
4335 ('f', 'file', None, _('list the filename')),
4364 ('d', 'date', None, _('list the date (short with -q)')),
4336 ('d', 'date', None, _('list the date (short with -q)')),
4365 ('n', 'number', None, _('list the revision number (default)')),
4337 ('n', 'number', None, _('list the revision number (default)')),
4366 ('c', 'changeset', None, _('list the changeset')),
4338 ('c', 'changeset', None, _('list the changeset')),
4367 ('l', 'line-number', None,
4339 ('l', 'line-number', None,
4368 _('show line number at the first appearance'))
4340 _('show line number at the first appearance'))
4369 ] + walkopts,
4341 ] + walkopts,
4370 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
4342 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
4371 "archive":
4343 "archive":
4372 (archive,
4344 (archive,
4373 [('', 'no-decode', None, _('do not pass files through decoders')),
4345 [('', 'no-decode', None, _('do not pass files through decoders')),
4374 ('p', 'prefix', '',
4346 ('p', 'prefix', '',
4375 _('directory prefix for files in archive'), _('PREFIX')),
4347 _('directory prefix for files in archive'), _('PREFIX')),
4376 ('r', 'rev', '',
4348 ('r', 'rev', '',
4377 _('revision to distribute'), _('REV')),
4349 _('revision to distribute'), _('REV')),
4378 ('t', 'type', '',
4350 ('t', 'type', '',
4379 _('type of distribution to create'), _('TYPE')),
4351 _('type of distribution to create'), _('TYPE')),
4380 ] + subrepoopts + walkopts,
4352 ] + subrepoopts + walkopts,
4381 _('[OPTION]... DEST')),
4353 _('[OPTION]... DEST')),
4382 "backout":
4354 "backout":
4383 (backout,
4355 (backout,
4384 [('', 'merge', None,
4356 [('', 'merge', None,
4385 _('merge with old dirstate parent after backout')),
4357 _('merge with old dirstate parent after backout')),
4386 ('', 'parent', '',
4358 ('', 'parent', '',
4387 _('parent to choose when backing out merge'), _('REV')),
4359 _('parent to choose when backing out merge'), _('REV')),
4388 ('t', 'tool', '',
4360 ('t', 'tool', '',
4389 _('specify merge tool')),
4361 _('specify merge tool')),
4390 ('r', 'rev', '',
4362 ('r', 'rev', '',
4391 _('revision to backout'), _('REV')),
4363 _('revision to backout'), _('REV')),
4392 ] + walkopts + commitopts + commitopts2,
4364 ] + walkopts + commitopts + commitopts2,
4393 _('[OPTION]... [-r] REV')),
4365 _('[OPTION]... [-r] REV')),
4394 "bisect":
4366 "bisect":
4395 (bisect,
4367 (bisect,
4396 [('r', 'reset', False, _('reset bisect state')),
4368 [('r', 'reset', False, _('reset bisect state')),
4397 ('g', 'good', False, _('mark changeset good')),
4369 ('g', 'good', False, _('mark changeset good')),
4398 ('b', 'bad', False, _('mark changeset bad')),
4370 ('b', 'bad', False, _('mark changeset bad')),
4399 ('s', 'skip', False, _('skip testing changeset')),
4371 ('s', 'skip', False, _('skip testing changeset')),
4400 ('e', 'extend', False, _('extend the bisect range')),
4372 ('e', 'extend', False, _('extend the bisect range')),
4401 ('c', 'command', '',
4373 ('c', 'command', '',
4402 _('use command to check changeset state'), _('CMD')),
4374 _('use command to check changeset state'), _('CMD')),
4403 ('U', 'noupdate', False, _('do not update to target'))],
4375 ('U', 'noupdate', False, _('do not update to target'))],
4404 _("[-gbsr] [-U] [-c CMD] [REV]")),
4376 _("[-gbsr] [-U] [-c CMD] [REV]")),
4405 "bookmarks":
4377 "bookmarks":
4406 (bookmark,
4378 (bookmark,
4407 [('f', 'force', False, _('force')),
4379 [('f', 'force', False, _('force')),
4408 ('r', 'rev', '', _('revision'), _('REV')),
4380 ('r', 'rev', '', _('revision'), _('REV')),
4409 ('d', 'delete', False, _('delete a given bookmark')),
4381 ('d', 'delete', False, _('delete a given bookmark')),
4410 ('m', 'rename', '', _('rename a given bookmark'), _('NAME'))],
4382 ('m', 'rename', '', _('rename a given bookmark'), _('NAME'))],
4411 _('hg bookmarks [-f] [-d] [-m NAME] [-r REV] [NAME]')),
4383 _('hg bookmarks [-f] [-d] [-m NAME] [-r REV] [NAME]')),
4412 "branch":
4384 "branch":
4413 (branch,
4385 (branch,
4414 [('f', 'force', None,
4386 [('f', 'force', None,
4415 _('set branch name even if it shadows an existing branch')),
4387 _('set branch name even if it shadows an existing branch')),
4416 ('C', 'clean', None, _('reset branch name to parent branch name'))],
4388 ('C', 'clean', None, _('reset branch name to parent branch name'))],
4417 _('[-fC] [NAME]')),
4389 _('[-fC] [NAME]')),
4418 "branches":
4390 "branches":
4419 (branches,
4391 (branches,
4420 [('a', 'active', False,
4392 [('a', 'active', False,
4421 _('show only branches that have unmerged heads')),
4393 _('show only branches that have unmerged heads')),
4422 ('c', 'closed', False,
4394 ('c', 'closed', False,
4423 _('show normal and closed branches'))],
4395 _('show normal and closed branches'))],
4424 _('[-ac]')),
4396 _('[-ac]')),
4425 "bundle":
4397 "bundle":
4426 (bundle,
4398 (bundle,
4427 [('f', 'force', None,
4399 [('f', 'force', None,
4428 _('run even when the destination is unrelated')),
4400 _('run even when the destination is unrelated')),
4429 ('r', 'rev', [],
4401 ('r', 'rev', [],
4430 _('a changeset intended to be added to the destination'),
4402 _('a changeset intended to be added to the destination'),
4431 _('REV')),
4403 _('REV')),
4432 ('b', 'branch', [],
4404 ('b', 'branch', [],
4433 _('a specific branch you would like to bundle'),
4405 _('a specific branch you would like to bundle'),
4434 _('BRANCH')),
4406 _('BRANCH')),
4435 ('', 'base', [],
4407 ('', 'base', [],
4436 _('a base changeset assumed to be available at the destination'),
4408 _('a base changeset assumed to be available at the destination'),
4437 _('REV')),
4409 _('REV')),
4438 ('a', 'all', None, _('bundle all changesets in the repository')),
4410 ('a', 'all', None, _('bundle all changesets in the repository')),
4439 ('t', 'type', 'bzip2',
4411 ('t', 'type', 'bzip2',
4440 _('bundle compression type to use'), _('TYPE')),
4412 _('bundle compression type to use'), _('TYPE')),
4441 ] + remoteopts,
4413 ] + remoteopts,
4442 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
4414 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
4443 "cat":
4415 "cat":
4444 (cat,
4416 (cat,
4445 [('o', 'output', '',
4417 [('o', 'output', '',
4446 _('print output to file with formatted name'), _('FORMAT')),
4418 _('print output to file with formatted name'), _('FORMAT')),
4447 ('r', 'rev', '',
4419 ('r', 'rev', '',
4448 _('print the given revision'), _('REV')),
4420 _('print the given revision'), _('REV')),
4449 ('', 'decode', None, _('apply any matching decode filter')),
4421 ('', 'decode', None, _('apply any matching decode filter')),
4450 ] + walkopts,
4422 ] + walkopts,
4451 _('[OPTION]... FILE...')),
4423 _('[OPTION]... FILE...')),
4452 "^clone":
4424 "^clone":
4453 (clone,
4425 (clone,
4454 [('U', 'noupdate', None,
4426 [('U', 'noupdate', None,
4455 _('the clone will include an empty working copy (only a repository)')),
4427 _('the clone will include an empty working copy (only a repository)')),
4456 ('u', 'updaterev', '',
4428 ('u', 'updaterev', '',
4457 _('revision, tag or branch to check out'), _('REV')),
4429 _('revision, tag or branch to check out'), _('REV')),
4458 ('r', 'rev', [],
4430 ('r', 'rev', [],
4459 _('include the specified changeset'), _('REV')),
4431 _('include the specified changeset'), _('REV')),
4460 ('b', 'branch', [],
4432 ('b', 'branch', [],
4461 _('clone only the specified branch'), _('BRANCH')),
4433 _('clone only the specified branch'), _('BRANCH')),
4462 ('', 'pull', None, _('use pull protocol to copy metadata')),
4434 ('', 'pull', None, _('use pull protocol to copy metadata')),
4463 ('', 'uncompressed', None,
4435 ('', 'uncompressed', None,
4464 _('use uncompressed transfer (fast over LAN)')),
4436 _('use uncompressed transfer (fast over LAN)')),
4465 ] + remoteopts,
4437 ] + remoteopts,
4466 _('[OPTION]... SOURCE [DEST]')),
4438 _('[OPTION]... SOURCE [DEST]')),
4467 "^commit|ci":
4439 "^commit|ci":
4468 (commit,
4440 (commit,
4469 [('A', 'addremove', None,
4441 [('A', 'addremove', None,
4470 _('mark new/missing files as added/removed before committing')),
4442 _('mark new/missing files as added/removed before committing')),
4471 ('', 'close-branch', None,
4443 ('', 'close-branch', None,
4472 _('mark a branch as closed, hiding it from the branch list')),
4444 _('mark a branch as closed, hiding it from the branch list')),
4473 ] + walkopts + commitopts + commitopts2,
4445 ] + walkopts + commitopts + commitopts2,
4474 _('[OPTION]... [FILE]...')),
4446 _('[OPTION]... [FILE]...')),
4475 "copy|cp":
4447 "copy|cp":
4476 (copy,
4448 (copy,
4477 [('A', 'after', None, _('record a copy that has already occurred')),
4449 [('A', 'after', None, _('record a copy that has already occurred')),
4478 ('f', 'force', None,
4450 ('f', 'force', None,
4479 _('forcibly copy over an existing managed file')),
4451 _('forcibly copy over an existing managed file')),
4480 ] + walkopts + dryrunopts,
4452 ] + walkopts + dryrunopts,
4481 _('[OPTION]... [SOURCE]... DEST')),
4453 _('[OPTION]... [SOURCE]... DEST')),
4482 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
4454 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
4483 "debugbuilddag":
4455 "debugbuilddag":
4484 (debugbuilddag,
4456 (debugbuilddag,
4485 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
4457 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
4486 ('a', 'appended-file', None, _('add single file all revs append to')),
4458 ('a', 'appended-file', None, _('add single file all revs append to')),
4487 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
4459 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
4488 ('n', 'new-file', None, _('add new file at each rev')),
4460 ('n', 'new-file', None, _('add new file at each rev')),
4489 ],
4461 ],
4490 _('[OPTION]... TEXT')),
4462 _('[OPTION]... TEXT')),
4491 "debugbundle":
4463 "debugbundle":
4492 (debugbundle,
4464 (debugbundle,
4493 [('a', 'all', None, _('show all details')),
4465 [('a', 'all', None, _('show all details')),
4494 ],
4466 ],
4495 _('FILE')),
4467 _('FILE')),
4496 "debugcheckstate": (debugcheckstate, [], ''),
4468 "debugcheckstate": (debugcheckstate, [], ''),
4497 "debugcommands": (debugcommands, [], _('[COMMAND]')),
4469 "debugcommands": (debugcommands, [], _('[COMMAND]')),
4498 "debugcomplete":
4470 "debugcomplete":
4499 (debugcomplete,
4471 (debugcomplete,
4500 [('o', 'options', None, _('show the command options'))],
4472 [('o', 'options', None, _('show the command options'))],
4501 _('[-o] CMD')),
4473 _('[-o] CMD')),
4502 "debugdag":
4474 "debugdag":
4503 (debugdag,
4475 (debugdag,
4504 [('t', 'tags', None, _('use tags as labels')),
4476 [('t', 'tags', None, _('use tags as labels')),
4505 ('b', 'branches', None, _('annotate with branch names')),
4477 ('b', 'branches', None, _('annotate with branch names')),
4506 ('', 'dots', None, _('use dots for runs')),
4478 ('', 'dots', None, _('use dots for runs')),
4507 ('s', 'spaces', None, _('separate elements by spaces')),
4479 ('s', 'spaces', None, _('separate elements by spaces')),
4508 ],
4480 ],
4509 _('[OPTION]... [FILE [REV]...]')),
4481 _('[OPTION]... [FILE [REV]...]')),
4510 "debugdate":
4482 "debugdate":
4511 (debugdate,
4483 (debugdate,
4512 [('e', 'extended', None, _('try extended date formats'))],
4484 [('e', 'extended', None, _('try extended date formats'))],
4513 _('[-e] DATE [RANGE]')),
4485 _('[-e] DATE [RANGE]')),
4514 "debugdata": (debugdata, [], _('FILE REV')),
4486 "debugdata": (debugdata, [], _('FILE REV')),
4515 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
4487 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
4516 "debuggetbundle":
4488 "debuggetbundle":
4517 (debuggetbundle,
4489 (debuggetbundle,
4518 [('H', 'head', [], _('id of head node'), _('ID')),
4490 [('H', 'head', [], _('id of head node'), _('ID')),
4519 ('C', 'common', [], _('id of common node'), _('ID')),
4491 ('C', 'common', [], _('id of common node'), _('ID')),
4520 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
4492 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
4521 ],
4493 ],
4522 _('REPO FILE [-H|-C ID]...')),
4494 _('REPO FILE [-H|-C ID]...')),
4523 "debugignore": (debugignore, [], ''),
4495 "debugignore": (debugignore, [], ''),
4524 "debugindex": (debugindex,
4496 "debugindex": (debugindex,
4525 [('f', 'format', 0, _('revlog format'), _('FORMAT'))],
4497 [('f', 'format', 0, _('revlog format'), _('FORMAT'))],
4526 _('FILE')),
4498 _('FILE')),
4527 "debugindexdot": (debugindexdot, [], _('FILE')),
4499 "debugindexdot": (debugindexdot, [], _('FILE')),
4528 "debuginstall": (debuginstall, [], ''),
4500 "debuginstall": (debuginstall, [], ''),
4529 "debugknown": (debugknown, [], _('REPO ID...')),
4501 "debugknown": (debugknown, [], _('REPO ID...')),
4530 "debugpushkey": (debugpushkey, [], _('REPO NAMESPACE [KEY OLD NEW]')),
4502 "debugpushkey": (debugpushkey, [], _('REPO NAMESPACE [KEY OLD NEW]')),
4531 "debugrebuildstate":
4503 "debugrebuildstate":
4532 (debugrebuildstate,
4504 (debugrebuildstate,
4533 [('r', 'rev', '',
4505 [('r', 'rev', '',
4534 _('revision to rebuild to'), _('REV'))],
4506 _('revision to rebuild to'), _('REV'))],
4535 _('[-r REV] [REV]')),
4507 _('[-r REV] [REV]')),
4536 "debugrename":
4508 "debugrename":
4537 (debugrename,
4509 (debugrename,
4538 [('r', 'rev', '',
4510 [('r', 'rev', '',
4539 _('revision to debug'), _('REV'))],
4511 _('revision to debug'), _('REV'))],
4540 _('[-r REV] FILE')),
4512 _('[-r REV] FILE')),
4541 "debugrevspec":
4513 "debugrevspec":
4542 (debugrevspec, [], ('REVSPEC')),
4514 (debugrevspec, [], ('REVSPEC')),
4543 "debugsetparents":
4515 "debugsetparents":
4544 (debugsetparents, [], _('REV1 [REV2]')),
4516 (debugsetparents, [], _('REV1 [REV2]')),
4545 "debugstate":
4517 "debugstate":
4546 (debugstate,
4518 (debugstate,
4547 [('', 'nodates', None, _('do not display the saved mtime')),
4519 [('', 'nodates', None, _('do not display the saved mtime')),
4548 ('', 'datesort', None, _('sort by saved mtime'))],
4520 ('', 'datesort', None, _('sort by saved mtime'))],
4549 _('[OPTION]...')),
4521 _('[OPTION]...')),
4550 "debugsub":
4522 "debugsub":
4551 (debugsub,
4523 (debugsub,
4552 [('r', 'rev', '',
4524 [('r', 'rev', '',
4553 _('revision to check'), _('REV'))],
4525 _('revision to check'), _('REV'))],
4554 _('[-r REV] [REV]')),
4526 _('[-r REV] [REV]')),
4555 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
4527 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
4556 "debugwireargs":
4528 "debugwireargs":
4557 (debugwireargs,
4529 (debugwireargs,
4558 [('', 'three', '', 'three'),
4530 [('', 'three', '', 'three'),
4559 ('', 'four', '', 'four'),
4531 ('', 'four', '', 'four'),
4560 ('', 'five', '', 'five'),
4532 ('', 'five', '', 'five'),
4561 ] + remoteopts,
4533 ] + remoteopts,
4562 _('REPO [OPTIONS]... [ONE [TWO]]')),
4534 _('REPO [OPTIONS]... [ONE [TWO]]')),
4563 "^diff":
4535 "^diff":
4564 (diff,
4536 (diff,
4565 [('r', 'rev', [],
4537 [('r', 'rev', [],
4566 _('revision'), _('REV')),
4538 _('revision'), _('REV')),
4567 ('c', 'change', '',
4539 ('c', 'change', '',
4568 _('change made by revision'), _('REV'))
4540 _('change made by revision'), _('REV'))
4569 ] + diffopts + diffopts2 + walkopts + subrepoopts,
4541 ] + diffopts + diffopts2 + walkopts + subrepoopts,
4570 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...')),
4542 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...')),
4571 "^export":
4543 "^export":
4572 (export,
4544 (export,
4573 [('o', 'output', '',
4545 [('o', 'output', '',
4574 _('print output to file with formatted name'), _('FORMAT')),
4546 _('print output to file with formatted name'), _('FORMAT')),
4575 ('', 'switch-parent', None, _('diff against the second parent')),
4547 ('', 'switch-parent', None, _('diff against the second parent')),
4576 ('r', 'rev', [],
4548 ('r', 'rev', [],
4577 _('revisions to export'), _('REV')),
4549 _('revisions to export'), _('REV')),
4578 ] + diffopts,
4550 ] + diffopts,
4579 _('[OPTION]... [-o OUTFILESPEC] REV...')),
4551 _('[OPTION]... [-o OUTFILESPEC] REV...')),
4580 "^forget":
4552 "^forget":
4581 (forget,
4553 (forget,
4582 [] + walkopts,
4554 [] + walkopts,
4583 _('[OPTION]... FILE...')),
4555 _('[OPTION]... FILE...')),
4584 "grep":
4556 "grep":
4585 (grep,
4557 (grep,
4586 [('0', 'print0', None, _('end fields with NUL')),
4558 [('0', 'print0', None, _('end fields with NUL')),
4587 ('', 'all', None, _('print all revisions that match')),
4559 ('', 'all', None, _('print all revisions that match')),
4588 ('a', 'text', None, _('treat all files as text')),
4560 ('a', 'text', None, _('treat all files as text')),
4589 ('f', 'follow', None,
4561 ('f', 'follow', None,
4590 _('follow changeset history,'
4562 _('follow changeset history,'
4591 ' or file history across copies and renames')),
4563 ' or file history across copies and renames')),
4592 ('i', 'ignore-case', None, _('ignore case when matching')),
4564 ('i', 'ignore-case', None, _('ignore case when matching')),
4593 ('l', 'files-with-matches', None,
4565 ('l', 'files-with-matches', None,
4594 _('print only filenames and revisions that match')),
4566 _('print only filenames and revisions that match')),
4595 ('n', 'line-number', None, _('print matching line numbers')),
4567 ('n', 'line-number', None, _('print matching line numbers')),
4596 ('r', 'rev', [],
4568 ('r', 'rev', [],
4597 _('only search files changed within revision range'), _('REV')),
4569 _('only search files changed within revision range'), _('REV')),
4598 ('u', 'user', None, _('list the author (long with -v)')),
4570 ('u', 'user', None, _('list the author (long with -v)')),
4599 ('d', 'date', None, _('list the date (short with -q)')),
4571 ('d', 'date', None, _('list the date (short with -q)')),
4600 ] + walkopts,
4572 ] + walkopts,
4601 _('[OPTION]... PATTERN [FILE]...')),
4573 _('[OPTION]... PATTERN [FILE]...')),
4602 "heads":
4574 "heads":
4603 (heads,
4575 (heads,
4604 [('r', 'rev', '',
4576 [('r', 'rev', '',
4605 _('show only heads which are descendants of STARTREV'),
4577 _('show only heads which are descendants of STARTREV'),
4606 _('STARTREV')),
4578 _('STARTREV')),
4607 ('t', 'topo', False, _('show topological heads only')),
4579 ('t', 'topo', False, _('show topological heads only')),
4608 ('a', 'active', False,
4580 ('a', 'active', False,
4609 _('show active branchheads only (DEPRECATED)')),
4581 _('show active branchheads only (DEPRECATED)')),
4610 ('c', 'closed', False,
4582 ('c', 'closed', False,
4611 _('show normal and closed branch heads')),
4583 _('show normal and closed branch heads')),
4612 ] + templateopts,
4584 ] + templateopts,
4613 _('[-ac] [-r STARTREV] [REV]...')),
4585 _('[-ac] [-r STARTREV] [REV]...')),
4614 "help": (help_, [], _('[TOPIC]')),
4586 "help": (help_, [], _('[TOPIC]')),
4615 "identify|id":
4587 "identify|id":
4616 (identify,
4588 (identify,
4617 [('r', 'rev', '',
4589 [('r', 'rev', '',
4618 _('identify the specified revision'), _('REV')),
4590 _('identify the specified revision'), _('REV')),
4619 ('n', 'num', None, _('show local revision number')),
4591 ('n', 'num', None, _('show local revision number')),
4620 ('i', 'id', None, _('show global revision id')),
4592 ('i', 'id', None, _('show global revision id')),
4621 ('b', 'branch', None, _('show branch')),
4593 ('b', 'branch', None, _('show branch')),
4622 ('t', 'tags', None, _('show tags')),
4594 ('t', 'tags', None, _('show tags')),
4623 ('B', 'bookmarks', None, _('show bookmarks'))],
4595 ('B', 'bookmarks', None, _('show bookmarks'))],
4624 _('[-nibtB] [-r REV] [SOURCE]')),
4596 _('[-nibtB] [-r REV] [SOURCE]')),
4625 "import|patch":
4597 "import|patch":
4626 (import_,
4598 (import_,
4627 [('p', 'strip', 1,
4599 [('p', 'strip', 1,
4628 _('directory strip option for patch. This has the same '
4600 _('directory strip option for patch. This has the same '
4629 'meaning as the corresponding patch option'),
4601 'meaning as the corresponding patch option'),
4630 _('NUM')),
4602 _('NUM')),
4631 ('b', 'base', '',
4603 ('b', 'base', '',
4632 _('base path'), _('PATH')),
4604 _('base path'), _('PATH')),
4633 ('f', 'force', None,
4605 ('f', 'force', None,
4634 _('skip check for outstanding uncommitted changes')),
4606 _('skip check for outstanding uncommitted changes')),
4635 ('', 'no-commit', None,
4607 ('', 'no-commit', None,
4636 _("don't commit, just update the working directory")),
4608 _("don't commit, just update the working directory")),
4637 ('', 'exact', None,
4609 ('', 'exact', None,
4638 _('apply patch to the nodes from which it was generated')),
4610 _('apply patch to the nodes from which it was generated')),
4639 ('', 'import-branch', None,
4611 ('', 'import-branch', None,
4640 _('use any branch information in patch (implied by --exact)'))] +
4612 _('use any branch information in patch (implied by --exact)'))] +
4641 commitopts + commitopts2 + similarityopts,
4613 commitopts + commitopts2 + similarityopts,
4642 _('[OPTION]... PATCH...')),
4614 _('[OPTION]... PATCH...')),
4643 "incoming|in":
4615 "incoming|in":
4644 (incoming,
4616 (incoming,
4645 [('f', 'force', None,
4617 [('f', 'force', None,
4646 _('run even if remote repository is unrelated')),
4618 _('run even if remote repository is unrelated')),
4647 ('n', 'newest-first', None, _('show newest record first')),
4619 ('n', 'newest-first', None, _('show newest record first')),
4648 ('', 'bundle', '',
4620 ('', 'bundle', '',
4649 _('file to store the bundles into'), _('FILE')),
4621 _('file to store the bundles into'), _('FILE')),
4650 ('r', 'rev', [],
4622 ('r', 'rev', [],
4651 _('a remote changeset intended to be added'), _('REV')),
4623 _('a remote changeset intended to be added'), _('REV')),
4652 ('B', 'bookmarks', False, _("compare bookmarks")),
4624 ('B', 'bookmarks', False, _("compare bookmarks")),
4653 ('b', 'branch', [],
4625 ('b', 'branch', [],
4654 _('a specific branch you would like to pull'), _('BRANCH')),
4626 _('a specific branch you would like to pull'), _('BRANCH')),
4655 ] + logopts + remoteopts + subrepoopts,
4627 ] + logopts + remoteopts + subrepoopts,
4656 _('[-p] [-n] [-M] [-f] [-r REV]...'
4628 _('[-p] [-n] [-M] [-f] [-r REV]...'
4657 ' [--bundle FILENAME] [SOURCE]')),
4629 ' [--bundle FILENAME] [SOURCE]')),
4658 "^init":
4630 "^init":
4659 (init,
4631 (init,
4660 remoteopts,
4632 remoteopts,
4661 _('[-e CMD] [--remotecmd CMD] [DEST]')),
4633 _('[-e CMD] [--remotecmd CMD] [DEST]')),
4662 "locate":
4634 "locate":
4663 (locate,
4635 (locate,
4664 [('r', 'rev', '',
4636 [('r', 'rev', '',
4665 _('search the repository as it is in REV'), _('REV')),
4637 _('search the repository as it is in REV'), _('REV')),
4666 ('0', 'print0', None,
4638 ('0', 'print0', None,
4667 _('end filenames with NUL, for use with xargs')),
4639 _('end filenames with NUL, for use with xargs')),
4668 ('f', 'fullpath', None,
4640 ('f', 'fullpath', None,
4669 _('print complete paths from the filesystem root')),
4641 _('print complete paths from the filesystem root')),
4670 ] + walkopts,
4642 ] + walkopts,
4671 _('[OPTION]... [PATTERN]...')),
4643 _('[OPTION]... [PATTERN]...')),
4672 "^log|history":
4644 "^log|history":
4673 (log,
4645 (log,
4674 [('f', 'follow', None,
4646 [('f', 'follow', None,
4675 _('follow changeset history,'
4647 _('follow changeset history,'
4676 ' or file history across copies and renames')),
4648 ' or file history across copies and renames')),
4677 ('', 'follow-first', None,
4649 ('', 'follow-first', None,
4678 _('only follow the first parent of merge changesets')),
4650 _('only follow the first parent of merge changesets')),
4679 ('d', 'date', '',
4651 ('d', 'date', '',
4680 _('show revisions matching date spec'), _('DATE')),
4652 _('show revisions matching date spec'), _('DATE')),
4681 ('C', 'copies', None, _('show copied files')),
4653 ('C', 'copies', None, _('show copied files')),
4682 ('k', 'keyword', [],
4654 ('k', 'keyword', [],
4683 _('do case-insensitive search for a given text'), _('TEXT')),
4655 _('do case-insensitive search for a given text'), _('TEXT')),
4684 ('r', 'rev', [],
4656 ('r', 'rev', [],
4685 _('show the specified revision or range'), _('REV')),
4657 _('show the specified revision or range'), _('REV')),
4686 ('', 'removed', None, _('include revisions where files were removed')),
4658 ('', 'removed', None, _('include revisions where files were removed')),
4687 ('m', 'only-merges', None, _('show only merges')),
4659 ('m', 'only-merges', None, _('show only merges')),
4688 ('u', 'user', [],
4660 ('u', 'user', [],
4689 _('revisions committed by user'), _('USER')),
4661 _('revisions committed by user'), _('USER')),
4690 ('', 'only-branch', [],
4662 ('', 'only-branch', [],
4691 _('show only changesets within the given named branch (DEPRECATED)'),
4663 _('show only changesets within the given named branch (DEPRECATED)'),
4692 _('BRANCH')),
4664 _('BRANCH')),
4693 ('b', 'branch', [],
4665 ('b', 'branch', [],
4694 _('show changesets within the given named branch'), _('BRANCH')),
4666 _('show changesets within the given named branch'), _('BRANCH')),
4695 ('P', 'prune', [],
4667 ('P', 'prune', [],
4696 _('do not display revision or any of its ancestors'), _('REV')),
4668 _('do not display revision or any of its ancestors'), _('REV')),
4697 ] + logopts + walkopts,
4669 ] + logopts + walkopts,
4698 _('[OPTION]... [FILE]')),
4670 _('[OPTION]... [FILE]')),
4699 "manifest":
4671 "manifest":
4700 (manifest,
4672 (manifest,
4701 [('r', 'rev', '',
4673 [('r', 'rev', '',
4702 _('revision to display'), _('REV'))],
4674 _('revision to display'), _('REV'))],
4703 _('[-r REV]')),
4675 _('[-r REV]')),
4704 "^merge":
4676 "^merge":
4705 (merge,
4677 (merge,
4706 [('f', 'force', None, _('force a merge with outstanding changes')),
4678 [('f', 'force', None, _('force a merge with outstanding changes')),
4707 ('t', 'tool', '', _('specify merge tool')),
4679 ('t', 'tool', '', _('specify merge tool')),
4708 ('r', 'rev', '',
4680 ('r', 'rev', '',
4709 _('revision to merge'), _('REV')),
4681 _('revision to merge'), _('REV')),
4710 ('P', 'preview', None,
4682 ('P', 'preview', None,
4711 _('review revisions to merge (no merge is performed)'))],
4683 _('review revisions to merge (no merge is performed)'))],
4712 _('[-P] [-f] [[-r] REV]')),
4684 _('[-P] [-f] [[-r] REV]')),
4713 "outgoing|out":
4685 "outgoing|out":
4714 (outgoing,
4686 (outgoing,
4715 [('f', 'force', None,
4687 [('f', 'force', None,
4716 _('run even when the destination is unrelated')),
4688 _('run even when the destination is unrelated')),
4717 ('r', 'rev', [],
4689 ('r', 'rev', [],
4718 _('a changeset intended to be included in the destination'),
4690 _('a changeset intended to be included in the destination'),
4719 _('REV')),
4691 _('REV')),
4720 ('n', 'newest-first', None, _('show newest record first')),
4692 ('n', 'newest-first', None, _('show newest record first')),
4721 ('B', 'bookmarks', False, _("compare bookmarks")),
4693 ('B', 'bookmarks', False, _("compare bookmarks")),
4722 ('b', 'branch', [],
4694 ('b', 'branch', [],
4723 _('a specific branch you would like to push'), _('BRANCH')),
4695 _('a specific branch you would like to push'), _('BRANCH')),
4724 ] + logopts + remoteopts + subrepoopts,
4696 ] + logopts + remoteopts + subrepoopts,
4725 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
4697 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
4726 "parents":
4698 "parents":
4727 (parents,
4699 (parents,
4728 [('r', 'rev', '',
4700 [('r', 'rev', '',
4729 _('show parents of the specified revision'), _('REV')),
4701 _('show parents of the specified revision'), _('REV')),
4730 ] + templateopts,
4702 ] + templateopts,
4731 _('[-r REV] [FILE]')),
4703 _('[-r REV] [FILE]')),
4732 "paths": (paths, [], _('[NAME]')),
4704 "paths": (paths, [], _('[NAME]')),
4733 "^pull":
4705 "^pull":
4734 (pull,
4706 (pull,
4735 [('u', 'update', None,
4707 [('u', 'update', None,
4736 _('update to new branch head if changesets were pulled')),
4708 _('update to new branch head if changesets were pulled')),
4737 ('f', 'force', None,
4709 ('f', 'force', None,
4738 _('run even when remote repository is unrelated')),
4710 _('run even when remote repository is unrelated')),
4739 ('r', 'rev', [],
4711 ('r', 'rev', [],
4740 _('a remote changeset intended to be added'), _('REV')),
4712 _('a remote changeset intended to be added'), _('REV')),
4741 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4713 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4742 ('b', 'branch', [],
4714 ('b', 'branch', [],
4743 _('a specific branch you would like to pull'), _('BRANCH')),
4715 _('a specific branch you would like to pull'), _('BRANCH')),
4744 ] + remoteopts,
4716 ] + remoteopts,
4745 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
4717 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
4746 "^push":
4718 "^push":
4747 (push,
4719 (push,
4748 [('f', 'force', None, _('force push')),
4720 [('f', 'force', None, _('force push')),
4749 ('r', 'rev', [],
4721 ('r', 'rev', [],
4750 _('a changeset intended to be included in the destination'),
4722 _('a changeset intended to be included in the destination'),
4751 _('REV')),
4723 _('REV')),
4752 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4724 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4753 ('b', 'branch', [],
4725 ('b', 'branch', [],
4754 _('a specific branch you would like to push'), _('BRANCH')),
4726 _('a specific branch you would like to push'), _('BRANCH')),
4755 ('', 'new-branch', False, _('allow pushing a new branch')),
4727 ('', 'new-branch', False, _('allow pushing a new branch')),
4756 ] + remoteopts,
4728 ] + remoteopts,
4757 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
4729 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
4758 "recover": (recover, []),
4730 "recover": (recover, []),
4759 "^remove|rm":
4731 "^remove|rm":
4760 (remove,
4732 (remove,
4761 [('A', 'after', None, _('record delete for missing files')),
4733 [('A', 'after', None, _('record delete for missing files')),
4762 ('f', 'force', None,
4734 ('f', 'force', None,
4763 _('remove (and delete) file even if added or modified')),
4735 _('remove (and delete) file even if added or modified')),
4764 ] + walkopts,
4736 ] + walkopts,
4765 _('[OPTION]... FILE...')),
4737 _('[OPTION]... FILE...')),
4766 "rename|move|mv":
4738 "rename|move|mv":
4767 (rename,
4739 (rename,
4768 [('A', 'after', None, _('record a rename that has already occurred')),
4740 [('A', 'after', None, _('record a rename that has already occurred')),
4769 ('f', 'force', None,
4741 ('f', 'force', None,
4770 _('forcibly copy over an existing managed file')),
4742 _('forcibly copy over an existing managed file')),
4771 ] + walkopts + dryrunopts,
4743 ] + walkopts + dryrunopts,
4772 _('[OPTION]... SOURCE... DEST')),
4744 _('[OPTION]... SOURCE... DEST')),
4773 "resolve":
4745 "resolve":
4774 (resolve,
4746 (resolve,
4775 [('a', 'all', None, _('select all unresolved files')),
4747 [('a', 'all', None, _('select all unresolved files')),
4776 ('l', 'list', None, _('list state of files needing merge')),
4748 ('l', 'list', None, _('list state of files needing merge')),
4777 ('m', 'mark', None, _('mark files as resolved')),
4749 ('m', 'mark', None, _('mark files as resolved')),
4778 ('u', 'unmark', None, _('mark files as unresolved')),
4750 ('u', 'unmark', None, _('mark files as unresolved')),
4779 ('t', 'tool', '', _('specify merge tool')),
4751 ('t', 'tool', '', _('specify merge tool')),
4780 ('n', 'no-status', None, _('hide status prefix'))]
4752 ('n', 'no-status', None, _('hide status prefix'))]
4781 + walkopts,
4753 + walkopts,
4782 _('[OPTION]... [FILE]...')),
4754 _('[OPTION]... [FILE]...')),
4783 "revert":
4755 "revert":
4784 (revert,
4756 (revert,
4785 [('a', 'all', None, _('revert all changes when no arguments given')),
4757 [('a', 'all', None, _('revert all changes when no arguments given')),
4786 ('d', 'date', '',
4758 ('d', 'date', '',
4787 _('tipmost revision matching date'), _('DATE')),
4759 _('tipmost revision matching date'), _('DATE')),
4788 ('r', 'rev', '',
4760 ('r', 'rev', '',
4789 _('revert to the specified revision'), _('REV')),
4761 _('revert to the specified revision'), _('REV')),
4790 ('', 'no-backup', None, _('do not save backup copies of files')),
4762 ('', 'no-backup', None, _('do not save backup copies of files')),
4791 ] + walkopts + dryrunopts,
4763 ] + walkopts + dryrunopts,
4792 _('[OPTION]... [-r REV] [NAME]...')),
4764 _('[OPTION]... [-r REV] [NAME]...')),
4793 "rollback": (rollback, dryrunopts),
4765 "rollback": (rollback, dryrunopts),
4794 "root": (root, []),
4766 "root": (root, []),
4795 "^serve":
4767 "^serve":
4796 (serve,
4768 (serve,
4797 [('A', 'accesslog', '',
4769 [('A', 'accesslog', '',
4798 _('name of access log file to write to'), _('FILE')),
4770 _('name of access log file to write to'), _('FILE')),
4799 ('d', 'daemon', None, _('run server in background')),
4771 ('d', 'daemon', None, _('run server in background')),
4800 ('', 'daemon-pipefds', '',
4772 ('', 'daemon-pipefds', '',
4801 _('used internally by daemon mode'), _('NUM')),
4773 _('used internally by daemon mode'), _('NUM')),
4802 ('E', 'errorlog', '',
4774 ('E', 'errorlog', '',
4803 _('name of error log file to write to'), _('FILE')),
4775 _('name of error log file to write to'), _('FILE')),
4804 # use string type, then we can check if something was passed
4776 # use string type, then we can check if something was passed
4805 ('p', 'port', '',
4777 ('p', 'port', '',
4806 _('port to listen on (default: 8000)'), _('PORT')),
4778 _('port to listen on (default: 8000)'), _('PORT')),
4807 ('a', 'address', '',
4779 ('a', 'address', '',
4808 _('address to listen on (default: all interfaces)'), _('ADDR')),
4780 _('address to listen on (default: all interfaces)'), _('ADDR')),
4809 ('', 'prefix', '',
4781 ('', 'prefix', '',
4810 _('prefix path to serve from (default: server root)'), _('PREFIX')),
4782 _('prefix path to serve from (default: server root)'), _('PREFIX')),
4811 ('n', 'name', '',
4783 ('n', 'name', '',
4812 _('name to show in web pages (default: working directory)'),
4784 _('name to show in web pages (default: working directory)'),
4813 _('NAME')),
4785 _('NAME')),
4814 ('', 'web-conf', '',
4786 ('', 'web-conf', '',
4815 _('name of the hgweb config file (see "hg help hgweb")'),
4787 _('name of the hgweb config file (see "hg help hgweb")'),
4816 _('FILE')),
4788 _('FILE')),
4817 ('', 'webdir-conf', '',
4789 ('', 'webdir-conf', '',
4818 _('name of the hgweb config file (DEPRECATED)'), _('FILE')),
4790 _('name of the hgweb config file (DEPRECATED)'), _('FILE')),
4819 ('', 'pid-file', '',
4791 ('', 'pid-file', '',
4820 _('name of file to write process ID to'), _('FILE')),
4792 _('name of file to write process ID to'), _('FILE')),
4821 ('', 'stdio', None, _('for remote clients')),
4793 ('', 'stdio', None, _('for remote clients')),
4822 ('t', 'templates', '',
4794 ('t', 'templates', '',
4823 _('web templates to use'), _('TEMPLATE')),
4795 _('web templates to use'), _('TEMPLATE')),
4824 ('', 'style', '',
4796 ('', 'style', '',
4825 _('template style to use'), _('STYLE')),
4797 _('template style to use'), _('STYLE')),
4826 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4798 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4827 ('', 'certificate', '',
4799 ('', 'certificate', '',
4828 _('SSL certificate file'), _('FILE'))],
4800 _('SSL certificate file'), _('FILE'))],
4829 _('[OPTION]...')),
4801 _('[OPTION]...')),
4830 "showconfig|debugconfig":
4802 "showconfig|debugconfig":
4831 (showconfig,
4803 (showconfig,
4832 [('u', 'untrusted', None, _('show untrusted configuration options'))],
4804 [('u', 'untrusted', None, _('show untrusted configuration options'))],
4833 _('[-u] [NAME]...')),
4805 _('[-u] [NAME]...')),
4834 "^summary|sum":
4806 "^summary|sum":
4835 (summary,
4807 (summary,
4836 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
4808 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
4837 "^status|st":
4809 "^status|st":
4838 (status,
4810 (status,
4839 [('A', 'all', None, _('show status of all files')),
4811 [('A', 'all', None, _('show status of all files')),
4840 ('m', 'modified', None, _('show only modified files')),
4812 ('m', 'modified', None, _('show only modified files')),
4841 ('a', 'added', None, _('show only added files')),
4813 ('a', 'added', None, _('show only added files')),
4842 ('r', 'removed', None, _('show only removed files')),
4814 ('r', 'removed', None, _('show only removed files')),
4843 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4815 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4844 ('c', 'clean', None, _('show only files without changes')),
4816 ('c', 'clean', None, _('show only files without changes')),
4845 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4817 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4846 ('i', 'ignored', None, _('show only ignored files')),
4818 ('i', 'ignored', None, _('show only ignored files')),
4847 ('n', 'no-status', None, _('hide status prefix')),
4819 ('n', 'no-status', None, _('hide status prefix')),
4848 ('C', 'copies', None, _('show source of copied files')),
4820 ('C', 'copies', None, _('show source of copied files')),
4849 ('0', 'print0', None,
4821 ('0', 'print0', None,
4850 _('end filenames with NUL, for use with xargs')),
4822 _('end filenames with NUL, for use with xargs')),
4851 ('', 'rev', [],
4823 ('', 'rev', [],
4852 _('show difference from revision'), _('REV')),
4824 _('show difference from revision'), _('REV')),
4853 ('', 'change', '',
4825 ('', 'change', '',
4854 _('list the changed files of a revision'), _('REV')),
4826 _('list the changed files of a revision'), _('REV')),
4855 ] + walkopts + subrepoopts,
4827 ] + walkopts + subrepoopts,
4856 _('[OPTION]... [FILE]...')),
4828 _('[OPTION]... [FILE]...')),
4857 "tag":
4829 "tag":
4858 (tag,
4830 (tag,
4859 [('f', 'force', None, _('force tag')),
4831 [('f', 'force', None, _('force tag')),
4860 ('l', 'local', None, _('make the tag local')),
4832 ('l', 'local', None, _('make the tag local')),
4861 ('r', 'rev', '',
4833 ('r', 'rev', '',
4862 _('revision to tag'), _('REV')),
4834 _('revision to tag'), _('REV')),
4863 ('', 'remove', None, _('remove a tag')),
4835 ('', 'remove', None, _('remove a tag')),
4864 # -l/--local is already there, commitopts cannot be used
4836 # -l/--local is already there, commitopts cannot be used
4865 ('e', 'edit', None, _('edit commit message')),
4837 ('e', 'edit', None, _('edit commit message')),
4866 ('m', 'message', '',
4838 ('m', 'message', '',
4867 _('use <text> as commit message'), _('TEXT')),
4839 _('use <text> as commit message'), _('TEXT')),
4868 ] + commitopts2,
4840 ] + commitopts2,
4869 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
4841 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
4870 "tags": (tags, [], ''),
4842 "tags": (tags, [], ''),
4871 "tip":
4843 "tip":
4872 (tip,
4844 (tip,
4873 [('p', 'patch', None, _('show patch')),
4845 [('p', 'patch', None, _('show patch')),
4874 ('g', 'git', None, _('use git extended diff format')),
4846 ('g', 'git', None, _('use git extended diff format')),
4875 ] + templateopts,
4847 ] + templateopts,
4876 _('[-p] [-g]')),
4848 _('[-p] [-g]')),
4877 "unbundle":
4849 "unbundle":
4878 (unbundle,
4850 (unbundle,
4879 [('u', 'update', None,
4851 [('u', 'update', None,
4880 _('update to new branch head if changesets were unbundled'))],
4852 _('update to new branch head if changesets were unbundled'))],
4881 _('[-u] FILE...')),
4853 _('[-u] FILE...')),
4882 "^update|up|checkout|co":
4854 "^update|up|checkout|co":
4883 (update,
4855 (update,
4884 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
4856 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
4885 ('c', 'check', None,
4857 ('c', 'check', None,
4886 _('update across branches if no uncommitted changes')),
4858 _('update across branches if no uncommitted changes')),
4887 ('d', 'date', '',
4859 ('d', 'date', '',
4888 _('tipmost revision matching date'), _('DATE')),
4860 _('tipmost revision matching date'), _('DATE')),
4889 ('r', 'rev', '',
4861 ('r', 'rev', '',
4890 _('revision'), _('REV'))],
4862 _('revision'), _('REV'))],
4891 _('[-c] [-C] [-d DATE] [[-r] REV]')),
4863 _('[-c] [-C] [-d DATE] [[-r] REV]')),
4892 "verify": (verify, []),
4864 "verify": (verify, []),
4893 "version": (version_, []),
4865 "version": (version_, []),
4894 }
4866 }
4895
4867
4896 norepo = ("clone init version help debugcommands debugcomplete"
4868 norepo = ("clone init version help debugcommands debugcomplete"
4897 " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
4869 " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
4898 " debugknown debuggetbundle debugbundle")
4870 " debugknown debuggetbundle debugbundle")
4899 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
4871 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
4900 " debugdata debugindex debugindexdot")
4872 " debugdata debugindex debugindexdot")
@@ -1,324 +1,288
1 # discovery.py - protocol changeset discovery functions
1 # discovery.py - protocol changeset discovery functions
2 #
2 #
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from node import nullid, short
8 from node import nullid, short
9 from i18n import _
9 from i18n import _
10 import util, error
10 import util, error
11
11
12 def findcommonincoming(repo, remote, heads=None, force=False, commononly=False):
12 def findcommonincoming(repo, remote, heads=None, force=False):
13 """Return a tuple (common, missing, heads) used to identify missing nodes
13 """Return a tuple (common, anyincoming, heads) used to identify the common
14 from remote. "missing" is either a boolean indicating if any nodes are missing
14 subset of nodes between repo and remote.
15 (when commononly=True), or else a list of the root nodes of the missing set.
16
15
17 If a list of heads is specified, return only nodes which are heads
16 "common" is a list of (at least) the heads of the common subset.
18 or ancestors of these heads.
17 "anyincoming" is testable as a boolean indicating if any nodes are missing
18 locally. If remote does not support getbundle, this actually is a list of
19 roots of the nodes that would be incoming, to be supplied to
20 changegroupsubset. No code except for pull should be relying on this fact
21 any longer.
22 "heads" is either the supplied heads, or else the remote's heads.
19 """
23 """
24
20 m = repo.changelog.nodemap
25 m = repo.changelog.nodemap
21 search = []
26 search = []
22 fetch = set()
27 fetch = set()
23 seen = set()
28 seen = set()
24 seenbranch = set()
29 seenbranch = set()
25 base = set()
30 base = set()
26
31
27 if not heads:
32 if not heads:
28 heads = remote.heads()
33 heads = remote.heads()
29
34
30 if repo.changelog.tip() == nullid:
35 if repo.changelog.tip() == nullid:
31 base.add(nullid)
36 base.add(nullid)
32 if heads != [nullid]:
37 if heads != [nullid]:
33 return [nullid], [nullid], list(heads)
38 return [nullid], [nullid], list(heads)
34 return [nullid], [], []
39 return [nullid], [], []
35
40
36 # assume we're closer to the tip than the root
41 # assume we're closer to the tip than the root
37 # and start by examining the heads
42 # and start by examining the heads
38 repo.ui.status(_("searching for changes\n"))
43 repo.ui.status(_("searching for changes\n"))
39
44
40 if commononly:
45 if remote.capable('getbundle'):
41 myheads = repo.heads()
46 myheads = repo.heads()
42 known = remote.known(myheads)
47 known = remote.known(myheads)
43 if util.all(known):
48 if util.all(known):
44 hasincoming = set(heads).difference(set(myheads)) and True
49 hasincoming = set(heads).difference(set(myheads)) and True
45 return myheads, hasincoming, heads
50 return myheads, hasincoming, heads
46
51
47 unknown = []
52 unknown = []
48 for h in heads:
53 for h in heads:
49 if h not in m:
54 if h not in m:
50 unknown.append(h)
55 unknown.append(h)
51 else:
56 else:
52 base.add(h)
57 base.add(h)
53
58
54 heads = unknown
59 heads = unknown
55 if not unknown:
60 if not unknown:
56 return list(base), [], []
61 return list(base), [], []
57
62
58 req = set(unknown)
63 req = set(unknown)
59 reqcnt = 0
64 reqcnt = 0
60
65
61 # search through remote branches
66 # search through remote branches
62 # a 'branch' here is a linear segment of history, with four parts:
67 # a 'branch' here is a linear segment of history, with four parts:
63 # head, root, first parent, second parent
68 # head, root, first parent, second parent
64 # (a branch always has two parents (or none) by definition)
69 # (a branch always has two parents (or none) by definition)
65 unknown = remote.branches(unknown)
70 unknown = remote.branches(unknown)
66 while unknown:
71 while unknown:
67 r = []
72 r = []
68 while unknown:
73 while unknown:
69 n = unknown.pop(0)
74 n = unknown.pop(0)
70 if n[0] in seen:
75 if n[0] in seen:
71 continue
76 continue
72
77
73 repo.ui.debug("examining %s:%s\n"
78 repo.ui.debug("examining %s:%s\n"
74 % (short(n[0]), short(n[1])))
79 % (short(n[0]), short(n[1])))
75 if n[0] == nullid: # found the end of the branch
80 if n[0] == nullid: # found the end of the branch
76 pass
81 pass
77 elif n in seenbranch:
82 elif n in seenbranch:
78 repo.ui.debug("branch already found\n")
83 repo.ui.debug("branch already found\n")
79 continue
84 continue
80 elif n[1] and n[1] in m: # do we know the base?
85 elif n[1] and n[1] in m: # do we know the base?
81 repo.ui.debug("found incomplete branch %s:%s\n"
86 repo.ui.debug("found incomplete branch %s:%s\n"
82 % (short(n[0]), short(n[1])))
87 % (short(n[0]), short(n[1])))
83 search.append(n[0:2]) # schedule branch range for scanning
88 search.append(n[0:2]) # schedule branch range for scanning
84 seenbranch.add(n)
89 seenbranch.add(n)
85 else:
90 else:
86 if n[1] not in seen and n[1] not in fetch:
91 if n[1] not in seen and n[1] not in fetch:
87 if n[2] in m and n[3] in m:
92 if n[2] in m and n[3] in m:
88 repo.ui.debug("found new changeset %s\n" %
93 repo.ui.debug("found new changeset %s\n" %
89 short(n[1]))
94 short(n[1]))
90 fetch.add(n[1]) # earliest unknown
95 fetch.add(n[1]) # earliest unknown
91 for p in n[2:4]:
96 for p in n[2:4]:
92 if p in m:
97 if p in m:
93 base.add(p) # latest known
98 base.add(p) # latest known
94
99
95 for p in n[2:4]:
100 for p in n[2:4]:
96 if p not in req and p not in m:
101 if p not in req and p not in m:
97 r.append(p)
102 r.append(p)
98 req.add(p)
103 req.add(p)
99 seen.add(n[0])
104 seen.add(n[0])
100
105
101 if r:
106 if r:
102 reqcnt += 1
107 reqcnt += 1
103 repo.ui.progress(_('searching'), reqcnt, unit=_('queries'))
108 repo.ui.progress(_('searching'), reqcnt, unit=_('queries'))
104 repo.ui.debug("request %d: %s\n" %
109 repo.ui.debug("request %d: %s\n" %
105 (reqcnt, " ".join(map(short, r))))
110 (reqcnt, " ".join(map(short, r))))
106 for p in xrange(0, len(r), 10):
111 for p in xrange(0, len(r), 10):
107 for b in remote.branches(r[p:p + 10]):
112 for b in remote.branches(r[p:p + 10]):
108 repo.ui.debug("received %s:%s\n" %
113 repo.ui.debug("received %s:%s\n" %
109 (short(b[0]), short(b[1])))
114 (short(b[0]), short(b[1])))
110 unknown.append(b)
115 unknown.append(b)
111
116
112 # do binary search on the branches we found
117 # do binary search on the branches we found
113 while search:
118 while search:
114 newsearch = []
119 newsearch = []
115 reqcnt += 1
120 reqcnt += 1
116 repo.ui.progress(_('searching'), reqcnt, unit=_('queries'))
121 repo.ui.progress(_('searching'), reqcnt, unit=_('queries'))
117 for n, l in zip(search, remote.between(search)):
122 for n, l in zip(search, remote.between(search)):
118 l.append(n[1])
123 l.append(n[1])
119 p = n[0]
124 p = n[0]
120 f = 1
125 f = 1
121 for i in l:
126 for i in l:
122 repo.ui.debug("narrowing %d:%d %s\n" % (f, len(l), short(i)))
127 repo.ui.debug("narrowing %d:%d %s\n" % (f, len(l), short(i)))
123 if i in m:
128 if i in m:
124 if f <= 2:
129 if f <= 2:
125 repo.ui.debug("found new branch changeset %s\n" %
130 repo.ui.debug("found new branch changeset %s\n" %
126 short(p))
131 short(p))
127 fetch.add(p)
132 fetch.add(p)
128 base.add(i)
133 base.add(i)
129 else:
134 else:
130 repo.ui.debug("narrowed branch search to %s:%s\n"
135 repo.ui.debug("narrowed branch search to %s:%s\n"
131 % (short(p), short(i)))
136 % (short(p), short(i)))
132 newsearch.append((p, i))
137 newsearch.append((p, i))
133 break
138 break
134 p, f = i, f * 2
139 p, f = i, f * 2
135 search = newsearch
140 search = newsearch
136
141
137 # sanity check our fetch list
142 # sanity check our fetch list
138 for f in fetch:
143 for f in fetch:
139 if f in m:
144 if f in m:
140 raise error.RepoError(_("already have changeset ")
145 raise error.RepoError(_("already have changeset ")
141 + short(f[:4]))
146 + short(f[:4]))
142
147
143 base = list(base)
148 base = list(base)
144 if base == [nullid]:
149 if base == [nullid]:
145 if force:
150 if force:
146 repo.ui.warn(_("warning: repository is unrelated\n"))
151 repo.ui.warn(_("warning: repository is unrelated\n"))
147 else:
152 else:
148 raise util.Abort(_("repository is unrelated"))
153 raise util.Abort(_("repository is unrelated"))
149
154
150 repo.ui.debug("found new changesets starting at " +
155 repo.ui.debug("found new changesets starting at " +
151 " ".join([short(f) for f in fetch]) + "\n")
156 " ".join([short(f) for f in fetch]) + "\n")
152
157
153 repo.ui.progress(_('searching'), None)
158 repo.ui.progress(_('searching'), None)
154 repo.ui.debug("%d total queries\n" % reqcnt)
159 repo.ui.debug("%d total queries\n" % reqcnt)
155
160
156 return base, list(fetch), heads
161 return base, list(fetch), heads
157
162
158 def findoutgoing(repo, remote, base=None, remoteheads=None, force=False):
159 """Return list of nodes that are roots of subsets not in remote
160
161 If base dict is specified, assume that these nodes and their parents
162 exist on the remote side.
163 If remotehead is specified, assume it is the list of the heads from
164 the remote repository.
165 """
166 if base is None:
167 base = findcommonincoming(repo, remote, heads=remoteheads,
168 force=force)[0]
169 else:
170 base = list(base)
171
172 repo.ui.debug("common changesets up to "
173 + " ".join(map(short, base)) + "\n")
174
175 remain = set(repo.changelog.nodemap)
176
177 # prune everything remote has from the tree
178 remain.remove(nullid)
179 remove = base
180 while remove:
181 n = remove.pop(0)
182 if n in remain:
183 remain.remove(n)
184 for p in repo.changelog.parents(n):
185 remove.append(p)
186
187 # find every node whose parents have been pruned
188 subset = []
189 # find every remote head that will get new children
190 for n in remain:
191 p1, p2 = repo.changelog.parents(n)
192 if p1 not in remain and p2 not in remain:
193 subset.append(n)
194
195 return subset
196
197 def prepush(repo, remote, force, revs, newbranch):
163 def prepush(repo, remote, force, revs, newbranch):
198 '''Analyze the local and remote repositories and determine which
164 '''Analyze the local and remote repositories and determine which
199 changesets need to be pushed to the remote. Return value depends
165 changesets need to be pushed to the remote. Return value depends
200 on circumstances:
166 on circumstances:
201
167
202 If we are not going to push anything, return a tuple (None,
168 If we are not going to push anything, return a tuple (None,
203 outgoing) where outgoing is 0 if there are no outgoing
169 outgoing) where outgoing is 0 if there are no outgoing
204 changesets and 1 if there are, but we refuse to push them
170 changesets and 1 if there are, but we refuse to push them
205 (e.g. would create new remote heads).
171 (e.g. would create new remote heads).
206
172
207 Otherwise, return a tuple (changegroup, remoteheads), where
173 Otherwise, return a tuple (changegroup, remoteheads), where
208 changegroup is a readable file-like object whose read() returns
174 changegroup is a readable file-like object whose read() returns
209 successive changegroup chunks ready to be sent over the wire and
175 successive changegroup chunks ready to be sent over the wire and
210 remoteheads is the list of remote heads.'''
176 remoteheads is the list of remote heads.'''
211 remoteheads = remote.heads()
177 remoteheads = remote.heads()
212 common, inc, rheads = findcommonincoming(repo, remote, heads=remoteheads,
178 common, inc, _rheads = findcommonincoming(repo, remote, heads=remoteheads,
213 force=force)
179 force=force)
214
180
215 cl = repo.changelog
181 cl = repo.changelog
216 update = findoutgoing(repo, remote, common, remoteheads)
182 outg = cl.findmissing(common, revs)
217 outg, bases, heads = cl.nodesbetween(update, revs)
218
183
219 if not bases:
184 if not outg:
220 repo.ui.status(_("no changes found\n"))
185 repo.ui.status(_("no changes found\n"))
221 return None, 1
186 return None, 1
222
187
223 if not force and remoteheads != [nullid]:
188 if not force and remoteheads != [nullid]:
224 if remote.capable('branchmap'):
189 if remote.capable('branchmap'):
225 # Check for each named branch if we're creating new remote heads.
190 # Check for each named branch if we're creating new remote heads.
226 # To be a remote head after push, node must be either:
191 # To be a remote head after push, node must be either:
227 # - unknown locally
192 # - unknown locally
228 # - a local outgoing head descended from update
193 # - a local outgoing head descended from update
229 # - a remote head that's known locally and not
194 # - a remote head that's known locally and not
230 # ancestral to an outgoing head
195 # ancestral to an outgoing head
231
196
232 # 1. Create set of branches involved in the push.
197 # 1. Create set of branches involved in the push.
233 branches = set(repo[n].branch() for n in outg)
198 branches = set(repo[n].branch() for n in outg)
234
199
235 # 2. Check for new branches on the remote.
200 # 2. Check for new branches on the remote.
236 remotemap = remote.branchmap()
201 remotemap = remote.branchmap()
237 newbranches = branches - set(remotemap)
202 newbranches = branches - set(remotemap)
238 if newbranches and not newbranch: # new branch requires --new-branch
203 if newbranches and not newbranch: # new branch requires --new-branch
239 branchnames = ', '.join(sorted(newbranches))
204 branchnames = ', '.join(sorted(newbranches))
240 raise util.Abort(_("push creates new remote branches: %s!")
205 raise util.Abort(_("push creates new remote branches: %s!")
241 % branchnames,
206 % branchnames,
242 hint=_("use 'hg push --new-branch' to create"
207 hint=_("use 'hg push --new-branch' to create"
243 " new remote branches"))
208 " new remote branches"))
244 branches.difference_update(newbranches)
209 branches.difference_update(newbranches)
245
210
246 # 3. Construct the initial oldmap and newmap dicts.
211 # 3. Construct the initial oldmap and newmap dicts.
247 # They contain information about the remote heads before and
212 # They contain information about the remote heads before and
248 # after the push, respectively.
213 # after the push, respectively.
249 # Heads not found locally are not included in either dict,
214 # Heads not found locally are not included in either dict,
250 # since they won't be affected by the push.
215 # since they won't be affected by the push.
251 # unsynced contains all branches with incoming changesets.
216 # unsynced contains all branches with incoming changesets.
252 oldmap = {}
217 oldmap = {}
253 newmap = {}
218 newmap = {}
254 unsynced = set()
219 unsynced = set()
255 for branch in branches:
220 for branch in branches:
256 remotebrheads = remotemap[branch]
221 remotebrheads = remotemap[branch]
257 prunedbrheads = [h for h in remotebrheads if h in cl.nodemap]
222 prunedbrheads = [h for h in remotebrheads if h in cl.nodemap]
258 oldmap[branch] = prunedbrheads
223 oldmap[branch] = prunedbrheads
259 newmap[branch] = list(prunedbrheads)
224 newmap[branch] = list(prunedbrheads)
260 if len(remotebrheads) > len(prunedbrheads):
225 if len(remotebrheads) > len(prunedbrheads):
261 unsynced.add(branch)
226 unsynced.add(branch)
262
227
263 # 4. Update newmap with outgoing changes.
228 # 4. Update newmap with outgoing changes.
264 # This will possibly add new heads and remove existing ones.
229 # This will possibly add new heads and remove existing ones.
265 ctxgen = (repo[n] for n in outg)
230 ctxgen = (repo[n] for n in outg)
266 repo._updatebranchcache(newmap, ctxgen)
231 repo._updatebranchcache(newmap, ctxgen)
267
232
268 else:
233 else:
269 # 1-4b. old servers: Check for new topological heads.
234 # 1-4b. old servers: Check for new topological heads.
270 # Construct {old,new}map with branch = None (topological branch).
235 # Construct {old,new}map with branch = None (topological branch).
271 # (code based on _updatebranchcache)
236 # (code based on _updatebranchcache)
272 oldheads = set(h for h in remoteheads if h in cl.nodemap)
237 oldheads = set(h for h in remoteheads if h in cl.nodemap)
273 newheads = oldheads.union(outg)
238 newheads = oldheads.union(outg)
274 if len(newheads) > 1:
239 if len(newheads) > 1:
275 for latest in reversed(outg):
240 for latest in reversed(outg):
276 if latest not in newheads:
241 if latest not in newheads:
277 continue
242 continue
278 minhrev = min(cl.rev(h) for h in newheads)
243 minhrev = min(cl.rev(h) for h in newheads)
279 reachable = cl.reachable(latest, cl.node(minhrev))
244 reachable = cl.reachable(latest, cl.node(minhrev))
280 reachable.remove(latest)
245 reachable.remove(latest)
281 newheads.difference_update(reachable)
246 newheads.difference_update(reachable)
282 branches = set([None])
247 branches = set([None])
283 newmap = {None: newheads}
248 newmap = {None: newheads}
284 oldmap = {None: oldheads}
249 oldmap = {None: oldheads}
285 unsynced = inc and branches or set()
250 unsynced = inc and branches or set()
286
251
287 # 5. Check for new heads.
252 # 5. Check for new heads.
288 # If there are more heads after the push than before, a suitable
253 # If there are more heads after the push than before, a suitable
289 # error message, depending on unsynced status, is displayed.
254 # error message, depending on unsynced status, is displayed.
290 error = None
255 error = None
291 for branch in branches:
256 for branch in branches:
292 newhs = set(newmap[branch])
257 newhs = set(newmap[branch])
293 oldhs = set(oldmap[branch])
258 oldhs = set(oldmap[branch])
294 if len(newhs) > len(oldhs):
259 if len(newhs) > len(oldhs):
295 if error is None:
260 if error is None:
296 if branch:
261 if branch:
297 error = _("push creates new remote heads "
262 error = _("push creates new remote heads "
298 "on branch '%s'!") % branch
263 "on branch '%s'!") % branch
299 else:
264 else:
300 error = _("push creates new remote heads!")
265 error = _("push creates new remote heads!")
301 if branch in unsynced:
266 if branch in unsynced:
302 hint = _("you should pull and merge or "
267 hint = _("you should pull and merge or "
303 "use push -f to force")
268 "use push -f to force")
304 else:
269 else:
305 hint = _("did you forget to merge? "
270 hint = _("did you forget to merge? "
306 "use push -f to force")
271 "use push -f to force")
307 if branch:
272 if branch:
308 repo.ui.debug("new remote heads on branch '%s'\n" % branch)
273 repo.ui.debug("new remote heads on branch '%s'\n" % branch)
309 for h in (newhs - oldhs):
274 for h in (newhs - oldhs):
310 repo.ui.debug("new remote head %s\n" % short(h))
275 repo.ui.debug("new remote head %s\n" % short(h))
311 if error:
276 if error:
312 raise util.Abort(error, hint=hint)
277 raise util.Abort(error, hint=hint)
313
278
314 # 6. Check for unsynced changes on involved branches.
279 # 6. Check for unsynced changes on involved branches.
315 if unsynced:
280 if unsynced:
316 repo.ui.warn(_("note: unsynced remote changes!\n"))
281 repo.ui.warn(_("note: unsynced remote changes!\n"))
317
282
318 if revs is None:
283 if revs is None:
319 # use the fast path, no race possible on push
284 # use the fast path, no race possible on push
320 nodes = repo.changelog.findmissing(common)
285 cg = repo._changegroup(outg, 'push')
321 cg = repo._changegroup(nodes, 'push')
322 else:
286 else:
323 cg = repo.changegroupsubset(update, revs, 'push')
287 cg = repo.getbundle('push', heads=revs, common=common)
324 return cg, remoteheads
288 return cg, remoteheads
@@ -1,564 +1,560
1 # hg.py - repository classes for mercurial
1 # hg.py - repository classes for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 from i18n import _
9 from i18n import _
10 from lock import release
10 from lock import release
11 from node import hex, nullid
11 from node import hex, nullid
12 import localrepo, bundlerepo, httprepo, sshrepo, statichttprepo, bookmarks
12 import localrepo, bundlerepo, httprepo, sshrepo, statichttprepo, bookmarks
13 import lock, util, extensions, error, node
13 import lock, util, extensions, error, node
14 import cmdutil, discovery, url
14 import cmdutil, discovery, url
15 import merge as mergemod
15 import merge as mergemod
16 import verify as verifymod
16 import verify as verifymod
17 import errno, os, shutil
17 import errno, os, shutil
18
18
19 def _local(path):
19 def _local(path):
20 path = util.expandpath(url.localpath(path))
20 path = util.expandpath(url.localpath(path))
21 return (os.path.isfile(path) and bundlerepo or localrepo)
21 return (os.path.isfile(path) and bundlerepo or localrepo)
22
22
23 def addbranchrevs(lrepo, repo, branches, revs):
23 def addbranchrevs(lrepo, repo, branches, revs):
24 hashbranch, branches = branches
24 hashbranch, branches = branches
25 if not hashbranch and not branches:
25 if not hashbranch and not branches:
26 return revs or None, revs and revs[0] or None
26 return revs or None, revs and revs[0] or None
27 revs = revs and list(revs) or []
27 revs = revs and list(revs) or []
28 if not repo.capable('branchmap'):
28 if not repo.capable('branchmap'):
29 if branches:
29 if branches:
30 raise util.Abort(_("remote branch lookup not supported"))
30 raise util.Abort(_("remote branch lookup not supported"))
31 revs.append(hashbranch)
31 revs.append(hashbranch)
32 return revs, revs[0]
32 return revs, revs[0]
33 branchmap = repo.branchmap()
33 branchmap = repo.branchmap()
34
34
35 def primary(branch):
35 def primary(branch):
36 if branch == '.':
36 if branch == '.':
37 if not lrepo or not lrepo.local():
37 if not lrepo or not lrepo.local():
38 raise util.Abort(_("dirstate branch not accessible"))
38 raise util.Abort(_("dirstate branch not accessible"))
39 branch = lrepo.dirstate.branch()
39 branch = lrepo.dirstate.branch()
40 if branch in branchmap:
40 if branch in branchmap:
41 revs.extend(node.hex(r) for r in reversed(branchmap[branch]))
41 revs.extend(node.hex(r) for r in reversed(branchmap[branch]))
42 return True
42 return True
43 else:
43 else:
44 return False
44 return False
45
45
46 for branch in branches:
46 for branch in branches:
47 if not primary(branch):
47 if not primary(branch):
48 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
48 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
49 if hashbranch:
49 if hashbranch:
50 if not primary(hashbranch):
50 if not primary(hashbranch):
51 revs.append(hashbranch)
51 revs.append(hashbranch)
52 return revs, revs[0]
52 return revs, revs[0]
53
53
54 def parseurl(path, branches=None):
54 def parseurl(path, branches=None):
55 '''parse url#branch, returning (url, (branch, branches))'''
55 '''parse url#branch, returning (url, (branch, branches))'''
56
56
57 u = url.url(path)
57 u = url.url(path)
58 branch = None
58 branch = None
59 if u.fragment:
59 if u.fragment:
60 branch = u.fragment
60 branch = u.fragment
61 u.fragment = None
61 u.fragment = None
62 return str(u), (branch, branches or [])
62 return str(u), (branch, branches or [])
63
63
64 schemes = {
64 schemes = {
65 'bundle': bundlerepo,
65 'bundle': bundlerepo,
66 'file': _local,
66 'file': _local,
67 'http': httprepo,
67 'http': httprepo,
68 'https': httprepo,
68 'https': httprepo,
69 'ssh': sshrepo,
69 'ssh': sshrepo,
70 'static-http': statichttprepo,
70 'static-http': statichttprepo,
71 }
71 }
72
72
73 def _lookup(path):
73 def _lookup(path):
74 u = url.url(path)
74 u = url.url(path)
75 scheme = u.scheme or 'file'
75 scheme = u.scheme or 'file'
76 thing = schemes.get(scheme) or schemes['file']
76 thing = schemes.get(scheme) or schemes['file']
77 try:
77 try:
78 return thing(path)
78 return thing(path)
79 except TypeError:
79 except TypeError:
80 return thing
80 return thing
81
81
82 def islocal(repo):
82 def islocal(repo):
83 '''return true if repo or path is local'''
83 '''return true if repo or path is local'''
84 if isinstance(repo, str):
84 if isinstance(repo, str):
85 try:
85 try:
86 return _lookup(repo).islocal(repo)
86 return _lookup(repo).islocal(repo)
87 except AttributeError:
87 except AttributeError:
88 return False
88 return False
89 return repo.local()
89 return repo.local()
90
90
91 def repository(ui, path='', create=False):
91 def repository(ui, path='', create=False):
92 """return a repository object for the specified path"""
92 """return a repository object for the specified path"""
93 repo = _lookup(path).instance(ui, path, create)
93 repo = _lookup(path).instance(ui, path, create)
94 ui = getattr(repo, "ui", ui)
94 ui = getattr(repo, "ui", ui)
95 for name, module in extensions.extensions():
95 for name, module in extensions.extensions():
96 hook = getattr(module, 'reposetup', None)
96 hook = getattr(module, 'reposetup', None)
97 if hook:
97 if hook:
98 hook(ui, repo)
98 hook(ui, repo)
99 return repo
99 return repo
100
100
101 def defaultdest(source):
101 def defaultdest(source):
102 '''return default destination of clone if none is given'''
102 '''return default destination of clone if none is given'''
103 return os.path.basename(os.path.normpath(source))
103 return os.path.basename(os.path.normpath(source))
104
104
105 def share(ui, source, dest=None, update=True):
105 def share(ui, source, dest=None, update=True):
106 '''create a shared repository'''
106 '''create a shared repository'''
107
107
108 if not islocal(source):
108 if not islocal(source):
109 raise util.Abort(_('can only share local repositories'))
109 raise util.Abort(_('can only share local repositories'))
110
110
111 if not dest:
111 if not dest:
112 dest = defaultdest(source)
112 dest = defaultdest(source)
113 else:
113 else:
114 dest = ui.expandpath(dest)
114 dest = ui.expandpath(dest)
115
115
116 if isinstance(source, str):
116 if isinstance(source, str):
117 origsource = ui.expandpath(source)
117 origsource = ui.expandpath(source)
118 source, branches = parseurl(origsource)
118 source, branches = parseurl(origsource)
119 srcrepo = repository(ui, source)
119 srcrepo = repository(ui, source)
120 rev, checkout = addbranchrevs(srcrepo, srcrepo, branches, None)
120 rev, checkout = addbranchrevs(srcrepo, srcrepo, branches, None)
121 else:
121 else:
122 srcrepo = source
122 srcrepo = source
123 origsource = source = srcrepo.url()
123 origsource = source = srcrepo.url()
124 checkout = None
124 checkout = None
125
125
126 sharedpath = srcrepo.sharedpath # if our source is already sharing
126 sharedpath = srcrepo.sharedpath # if our source is already sharing
127
127
128 root = os.path.realpath(dest)
128 root = os.path.realpath(dest)
129 roothg = os.path.join(root, '.hg')
129 roothg = os.path.join(root, '.hg')
130
130
131 if os.path.exists(roothg):
131 if os.path.exists(roothg):
132 raise util.Abort(_('destination already exists'))
132 raise util.Abort(_('destination already exists'))
133
133
134 if not os.path.isdir(root):
134 if not os.path.isdir(root):
135 os.mkdir(root)
135 os.mkdir(root)
136 util.makedir(roothg, notindexed=True)
136 util.makedir(roothg, notindexed=True)
137
137
138 requirements = ''
138 requirements = ''
139 try:
139 try:
140 requirements = srcrepo.opener('requires').read()
140 requirements = srcrepo.opener('requires').read()
141 except IOError, inst:
141 except IOError, inst:
142 if inst.errno != errno.ENOENT:
142 if inst.errno != errno.ENOENT:
143 raise
143 raise
144
144
145 requirements += 'shared\n'
145 requirements += 'shared\n'
146 file(os.path.join(roothg, 'requires'), 'w').write(requirements)
146 file(os.path.join(roothg, 'requires'), 'w').write(requirements)
147 file(os.path.join(roothg, 'sharedpath'), 'w').write(sharedpath)
147 file(os.path.join(roothg, 'sharedpath'), 'w').write(sharedpath)
148
148
149 default = srcrepo.ui.config('paths', 'default')
149 default = srcrepo.ui.config('paths', 'default')
150 if default:
150 if default:
151 f = file(os.path.join(roothg, 'hgrc'), 'w')
151 f = file(os.path.join(roothg, 'hgrc'), 'w')
152 f.write('[paths]\ndefault = %s\n' % default)
152 f.write('[paths]\ndefault = %s\n' % default)
153 f.close()
153 f.close()
154
154
155 r = repository(ui, root)
155 r = repository(ui, root)
156
156
157 if update:
157 if update:
158 r.ui.status(_("updating working directory\n"))
158 r.ui.status(_("updating working directory\n"))
159 if update is not True:
159 if update is not True:
160 checkout = update
160 checkout = update
161 for test in (checkout, 'default', 'tip'):
161 for test in (checkout, 'default', 'tip'):
162 if test is None:
162 if test is None:
163 continue
163 continue
164 try:
164 try:
165 uprev = r.lookup(test)
165 uprev = r.lookup(test)
166 break
166 break
167 except error.RepoLookupError:
167 except error.RepoLookupError:
168 continue
168 continue
169 _update(r, uprev)
169 _update(r, uprev)
170
170
171 def clone(ui, source, dest=None, pull=False, rev=None, update=True,
171 def clone(ui, source, dest=None, pull=False, rev=None, update=True,
172 stream=False, branch=None):
172 stream=False, branch=None):
173 """Make a copy of an existing repository.
173 """Make a copy of an existing repository.
174
174
175 Create a copy of an existing repository in a new directory. The
175 Create a copy of an existing repository in a new directory. The
176 source and destination are URLs, as passed to the repository
176 source and destination are URLs, as passed to the repository
177 function. Returns a pair of repository objects, the source and
177 function. Returns a pair of repository objects, the source and
178 newly created destination.
178 newly created destination.
179
179
180 The location of the source is added to the new repository's
180 The location of the source is added to the new repository's
181 .hg/hgrc file, as the default to be used for future pulls and
181 .hg/hgrc file, as the default to be used for future pulls and
182 pushes.
182 pushes.
183
183
184 If an exception is raised, the partly cloned/updated destination
184 If an exception is raised, the partly cloned/updated destination
185 repository will be deleted.
185 repository will be deleted.
186
186
187 Arguments:
187 Arguments:
188
188
189 source: repository object or URL
189 source: repository object or URL
190
190
191 dest: URL of destination repository to create (defaults to base
191 dest: URL of destination repository to create (defaults to base
192 name of source repository)
192 name of source repository)
193
193
194 pull: always pull from source repository, even in local case
194 pull: always pull from source repository, even in local case
195
195
196 stream: stream raw data uncompressed from repository (fast over
196 stream: stream raw data uncompressed from repository (fast over
197 LAN, slow over WAN)
197 LAN, slow over WAN)
198
198
199 rev: revision to clone up to (implies pull=True)
199 rev: revision to clone up to (implies pull=True)
200
200
201 update: update working directory after clone completes, if
201 update: update working directory after clone completes, if
202 destination is local repository (True means update to default rev,
202 destination is local repository (True means update to default rev,
203 anything else is treated as a revision)
203 anything else is treated as a revision)
204
204
205 branch: branches to clone
205 branch: branches to clone
206 """
206 """
207
207
208 if isinstance(source, str):
208 if isinstance(source, str):
209 origsource = ui.expandpath(source)
209 origsource = ui.expandpath(source)
210 source, branch = parseurl(origsource, branch)
210 source, branch = parseurl(origsource, branch)
211 src_repo = repository(ui, source)
211 src_repo = repository(ui, source)
212 else:
212 else:
213 src_repo = source
213 src_repo = source
214 branch = (None, branch or [])
214 branch = (None, branch or [])
215 origsource = source = src_repo.url()
215 origsource = source = src_repo.url()
216 rev, checkout = addbranchrevs(src_repo, src_repo, branch, rev)
216 rev, checkout = addbranchrevs(src_repo, src_repo, branch, rev)
217
217
218 if dest is None:
218 if dest is None:
219 dest = defaultdest(source)
219 dest = defaultdest(source)
220 ui.status(_("destination directory: %s\n") % dest)
220 ui.status(_("destination directory: %s\n") % dest)
221 else:
221 else:
222 dest = ui.expandpath(dest)
222 dest = ui.expandpath(dest)
223
223
224 dest = url.localpath(dest)
224 dest = url.localpath(dest)
225 source = url.localpath(source)
225 source = url.localpath(source)
226
226
227 if os.path.exists(dest):
227 if os.path.exists(dest):
228 if not os.path.isdir(dest):
228 if not os.path.isdir(dest):
229 raise util.Abort(_("destination '%s' already exists") % dest)
229 raise util.Abort(_("destination '%s' already exists") % dest)
230 elif os.listdir(dest):
230 elif os.listdir(dest):
231 raise util.Abort(_("destination '%s' is not empty") % dest)
231 raise util.Abort(_("destination '%s' is not empty") % dest)
232
232
233 class DirCleanup(object):
233 class DirCleanup(object):
234 def __init__(self, dir_):
234 def __init__(self, dir_):
235 self.rmtree = shutil.rmtree
235 self.rmtree = shutil.rmtree
236 self.dir_ = dir_
236 self.dir_ = dir_
237 def close(self):
237 def close(self):
238 self.dir_ = None
238 self.dir_ = None
239 def cleanup(self):
239 def cleanup(self):
240 if self.dir_:
240 if self.dir_:
241 self.rmtree(self.dir_, True)
241 self.rmtree(self.dir_, True)
242
242
243 src_lock = dest_lock = dir_cleanup = None
243 src_lock = dest_lock = dir_cleanup = None
244 try:
244 try:
245 if islocal(dest):
245 if islocal(dest):
246 dir_cleanup = DirCleanup(dest)
246 dir_cleanup = DirCleanup(dest)
247
247
248 abspath = origsource
248 abspath = origsource
249 copy = False
249 copy = False
250 if src_repo.cancopy() and islocal(dest):
250 if src_repo.cancopy() and islocal(dest):
251 abspath = os.path.abspath(url.localpath(origsource))
251 abspath = os.path.abspath(url.localpath(origsource))
252 copy = not pull and not rev
252 copy = not pull and not rev
253
253
254 if copy:
254 if copy:
255 try:
255 try:
256 # we use a lock here because if we race with commit, we
256 # we use a lock here because if we race with commit, we
257 # can end up with extra data in the cloned revlogs that's
257 # can end up with extra data in the cloned revlogs that's
258 # not pointed to by changesets, thus causing verify to
258 # not pointed to by changesets, thus causing verify to
259 # fail
259 # fail
260 src_lock = src_repo.lock(wait=False)
260 src_lock = src_repo.lock(wait=False)
261 except error.LockError:
261 except error.LockError:
262 copy = False
262 copy = False
263
263
264 if copy:
264 if copy:
265 src_repo.hook('preoutgoing', throw=True, source='clone')
265 src_repo.hook('preoutgoing', throw=True, source='clone')
266 hgdir = os.path.realpath(os.path.join(dest, ".hg"))
266 hgdir = os.path.realpath(os.path.join(dest, ".hg"))
267 if not os.path.exists(dest):
267 if not os.path.exists(dest):
268 os.mkdir(dest)
268 os.mkdir(dest)
269 else:
269 else:
270 # only clean up directories we create ourselves
270 # only clean up directories we create ourselves
271 dir_cleanup.dir_ = hgdir
271 dir_cleanup.dir_ = hgdir
272 try:
272 try:
273 dest_path = hgdir
273 dest_path = hgdir
274 util.makedir(dest_path, notindexed=True)
274 util.makedir(dest_path, notindexed=True)
275 except OSError, inst:
275 except OSError, inst:
276 if inst.errno == errno.EEXIST:
276 if inst.errno == errno.EEXIST:
277 dir_cleanup.close()
277 dir_cleanup.close()
278 raise util.Abort(_("destination '%s' already exists")
278 raise util.Abort(_("destination '%s' already exists")
279 % dest)
279 % dest)
280 raise
280 raise
281
281
282 hardlink = None
282 hardlink = None
283 num = 0
283 num = 0
284 for f in src_repo.store.copylist():
284 for f in src_repo.store.copylist():
285 src = os.path.join(src_repo.sharedpath, f)
285 src = os.path.join(src_repo.sharedpath, f)
286 dst = os.path.join(dest_path, f)
286 dst = os.path.join(dest_path, f)
287 dstbase = os.path.dirname(dst)
287 dstbase = os.path.dirname(dst)
288 if dstbase and not os.path.exists(dstbase):
288 if dstbase and not os.path.exists(dstbase):
289 os.mkdir(dstbase)
289 os.mkdir(dstbase)
290 if os.path.exists(src):
290 if os.path.exists(src):
291 if dst.endswith('data'):
291 if dst.endswith('data'):
292 # lock to avoid premature writing to the target
292 # lock to avoid premature writing to the target
293 dest_lock = lock.lock(os.path.join(dstbase, "lock"))
293 dest_lock = lock.lock(os.path.join(dstbase, "lock"))
294 hardlink, n = util.copyfiles(src, dst, hardlink)
294 hardlink, n = util.copyfiles(src, dst, hardlink)
295 num += n
295 num += n
296 if hardlink:
296 if hardlink:
297 ui.debug("linked %d files\n" % num)
297 ui.debug("linked %d files\n" % num)
298 else:
298 else:
299 ui.debug("copied %d files\n" % num)
299 ui.debug("copied %d files\n" % num)
300
300
301 # we need to re-init the repo after manually copying the data
301 # we need to re-init the repo after manually copying the data
302 # into it
302 # into it
303 dest_repo = repository(ui, dest)
303 dest_repo = repository(ui, dest)
304 src_repo.hook('outgoing', source='clone',
304 src_repo.hook('outgoing', source='clone',
305 node=node.hex(node.nullid))
305 node=node.hex(node.nullid))
306 else:
306 else:
307 try:
307 try:
308 dest_repo = repository(ui, dest, create=True)
308 dest_repo = repository(ui, dest, create=True)
309 except OSError, inst:
309 except OSError, inst:
310 if inst.errno == errno.EEXIST:
310 if inst.errno == errno.EEXIST:
311 dir_cleanup.close()
311 dir_cleanup.close()
312 raise util.Abort(_("destination '%s' already exists")
312 raise util.Abort(_("destination '%s' already exists")
313 % dest)
313 % dest)
314 raise
314 raise
315
315
316 revs = None
316 revs = None
317 if rev:
317 if rev:
318 if 'lookup' not in src_repo.capabilities:
318 if 'lookup' not in src_repo.capabilities:
319 raise util.Abort(_("src repository does not support "
319 raise util.Abort(_("src repository does not support "
320 "revision lookup and so doesn't "
320 "revision lookup and so doesn't "
321 "support clone by revision"))
321 "support clone by revision"))
322 revs = [src_repo.lookup(r) for r in rev]
322 revs = [src_repo.lookup(r) for r in rev]
323 checkout = revs[0]
323 checkout = revs[0]
324 if dest_repo.local():
324 if dest_repo.local():
325 dest_repo.clone(src_repo, heads=revs, stream=stream)
325 dest_repo.clone(src_repo, heads=revs, stream=stream)
326 elif src_repo.local():
326 elif src_repo.local():
327 src_repo.push(dest_repo, revs=revs)
327 src_repo.push(dest_repo, revs=revs)
328 else:
328 else:
329 raise util.Abort(_("clone from remote to remote not supported"))
329 raise util.Abort(_("clone from remote to remote not supported"))
330
330
331 if dir_cleanup:
331 if dir_cleanup:
332 dir_cleanup.close()
332 dir_cleanup.close()
333
333
334 if dest_repo.local():
334 if dest_repo.local():
335 fp = dest_repo.opener("hgrc", "w", text=True)
335 fp = dest_repo.opener("hgrc", "w", text=True)
336 fp.write("[paths]\n")
336 fp.write("[paths]\n")
337 fp.write("default = %s\n" % abspath)
337 fp.write("default = %s\n" % abspath)
338 fp.close()
338 fp.close()
339
339
340 dest_repo.ui.setconfig('paths', 'default', abspath)
340 dest_repo.ui.setconfig('paths', 'default', abspath)
341
341
342 if update:
342 if update:
343 if update is not True:
343 if update is not True:
344 checkout = update
344 checkout = update
345 if src_repo.local():
345 if src_repo.local():
346 checkout = src_repo.lookup(update)
346 checkout = src_repo.lookup(update)
347 for test in (checkout, 'default', 'tip'):
347 for test in (checkout, 'default', 'tip'):
348 if test is None:
348 if test is None:
349 continue
349 continue
350 try:
350 try:
351 uprev = dest_repo.lookup(test)
351 uprev = dest_repo.lookup(test)
352 break
352 break
353 except error.RepoLookupError:
353 except error.RepoLookupError:
354 continue
354 continue
355 bn = dest_repo[uprev].branch()
355 bn = dest_repo[uprev].branch()
356 dest_repo.ui.status(_("updating to branch %s\n") % bn)
356 dest_repo.ui.status(_("updating to branch %s\n") % bn)
357 _update(dest_repo, uprev)
357 _update(dest_repo, uprev)
358
358
359 # clone all bookmarks
359 # clone all bookmarks
360 if dest_repo.local() and src_repo.capable("pushkey"):
360 if dest_repo.local() and src_repo.capable("pushkey"):
361 rb = src_repo.listkeys('bookmarks')
361 rb = src_repo.listkeys('bookmarks')
362 for k, n in rb.iteritems():
362 for k, n in rb.iteritems():
363 try:
363 try:
364 m = dest_repo.lookup(n)
364 m = dest_repo.lookup(n)
365 dest_repo._bookmarks[k] = m
365 dest_repo._bookmarks[k] = m
366 except error.RepoLookupError:
366 except error.RepoLookupError:
367 pass
367 pass
368 if rb:
368 if rb:
369 bookmarks.write(dest_repo)
369 bookmarks.write(dest_repo)
370 elif src_repo.local() and dest_repo.capable("pushkey"):
370 elif src_repo.local() and dest_repo.capable("pushkey"):
371 for k, n in src_repo._bookmarks.iteritems():
371 for k, n in src_repo._bookmarks.iteritems():
372 dest_repo.pushkey('bookmarks', k, '', hex(n))
372 dest_repo.pushkey('bookmarks', k, '', hex(n))
373
373
374 return src_repo, dest_repo
374 return src_repo, dest_repo
375 finally:
375 finally:
376 release(src_lock, dest_lock)
376 release(src_lock, dest_lock)
377 if dir_cleanup is not None:
377 if dir_cleanup is not None:
378 dir_cleanup.cleanup()
378 dir_cleanup.cleanup()
379
379
380 def _showstats(repo, stats):
380 def _showstats(repo, stats):
381 repo.ui.status(_("%d files updated, %d files merged, "
381 repo.ui.status(_("%d files updated, %d files merged, "
382 "%d files removed, %d files unresolved\n") % stats)
382 "%d files removed, %d files unresolved\n") % stats)
383
383
384 def update(repo, node):
384 def update(repo, node):
385 """update the working directory to node, merging linear changes"""
385 """update the working directory to node, merging linear changes"""
386 stats = mergemod.update(repo, node, False, False, None)
386 stats = mergemod.update(repo, node, False, False, None)
387 _showstats(repo, stats)
387 _showstats(repo, stats)
388 if stats[3]:
388 if stats[3]:
389 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
389 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
390 return stats[3] > 0
390 return stats[3] > 0
391
391
392 # naming conflict in clone()
392 # naming conflict in clone()
393 _update = update
393 _update = update
394
394
395 def clean(repo, node, show_stats=True):
395 def clean(repo, node, show_stats=True):
396 """forcibly switch the working directory to node, clobbering changes"""
396 """forcibly switch the working directory to node, clobbering changes"""
397 stats = mergemod.update(repo, node, False, True, None)
397 stats = mergemod.update(repo, node, False, True, None)
398 if show_stats:
398 if show_stats:
399 _showstats(repo, stats)
399 _showstats(repo, stats)
400 return stats[3] > 0
400 return stats[3] > 0
401
401
402 def merge(repo, node, force=None, remind=True):
402 def merge(repo, node, force=None, remind=True):
403 """Branch merge with node, resolving changes. Return true if any
403 """Branch merge with node, resolving changes. Return true if any
404 unresolved conflicts."""
404 unresolved conflicts."""
405 stats = mergemod.update(repo, node, True, force, False)
405 stats = mergemod.update(repo, node, True, force, False)
406 _showstats(repo, stats)
406 _showstats(repo, stats)
407 if stats[3]:
407 if stats[3]:
408 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
408 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
409 "or 'hg update -C .' to abandon\n"))
409 "or 'hg update -C .' to abandon\n"))
410 elif remind:
410 elif remind:
411 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
411 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
412 return stats[3] > 0
412 return stats[3] > 0
413
413
414 def _incoming(displaychlist, subreporecurse, ui, repo, source,
414 def _incoming(displaychlist, subreporecurse, ui, repo, source,
415 opts, buffered=False):
415 opts, buffered=False):
416 """
416 """
417 Helper for incoming / gincoming.
417 Helper for incoming / gincoming.
418 displaychlist gets called with
418 displaychlist gets called with
419 (remoterepo, incomingchangesetlist, displayer) parameters,
419 (remoterepo, incomingchangesetlist, displayer) parameters,
420 and is supposed to contain only code that can't be unified.
420 and is supposed to contain only code that can't be unified.
421 """
421 """
422 source, branches = parseurl(ui.expandpath(source), opts.get('branch'))
422 source, branches = parseurl(ui.expandpath(source), opts.get('branch'))
423 other = repository(remoteui(repo, opts), source)
423 other = repository(remoteui(repo, opts), source)
424 ui.status(_('comparing with %s\n') % url.hidepassword(source))
424 ui.status(_('comparing with %s\n') % url.hidepassword(source))
425 revs, checkout = addbranchrevs(repo, other, branches, opts.get('rev'))
425 revs, checkout = addbranchrevs(repo, other, branches, opts.get('rev'))
426
426
427 if revs:
427 if revs:
428 revs = [other.lookup(rev) for rev in revs]
428 revs = [other.lookup(rev) for rev in revs]
429 usecommon = other.capable('getbundle')
429 other, common, anyinc, bundle = bundlerepo.getremotechanges(ui, repo, other,
430 other, common, incoming, bundle = bundlerepo.getremotechanges(ui, repo, other,
430 revs, opts["bundle"], opts["force"])
431 revs, opts["bundle"], opts["force"],
431 if not anyinc:
432 usecommon=usecommon)
433 if not incoming:
434 ui.status(_("no changes found\n"))
432 ui.status(_("no changes found\n"))
435 return subreporecurse()
433 return subreporecurse()
436
434
437 try:
435 try:
438 if usecommon:
436 chlist = other.changelog.findmissing(common, revs)
439 chlist = other.changelog.findmissing(common, revs)
440 else:
441 chlist = other.changelog.nodesbetween(incoming, revs)[0]
442 displayer = cmdutil.show_changeset(ui, other, opts, buffered)
437 displayer = cmdutil.show_changeset(ui, other, opts, buffered)
443
438
444 # XXX once graphlog extension makes it into core,
439 # XXX once graphlog extension makes it into core,
445 # should be replaced by a if graph/else
440 # should be replaced by a if graph/else
446 displaychlist(other, chlist, displayer)
441 displaychlist(other, chlist, displayer)
447
442
448 displayer.close()
443 displayer.close()
449 finally:
444 finally:
450 if hasattr(other, 'close'):
445 if hasattr(other, 'close'):
451 other.close()
446 other.close()
452 if bundle:
447 if bundle:
453 os.unlink(bundle)
448 os.unlink(bundle)
454 subreporecurse()
449 subreporecurse()
455 return 0 # exit code is zero since we found incoming changes
450 return 0 # exit code is zero since we found incoming changes
456
451
457 def incoming(ui, repo, source, opts):
452 def incoming(ui, repo, source, opts):
458 def subreporecurse():
453 def subreporecurse():
459 ret = 1
454 ret = 1
460 if opts.get('subrepos'):
455 if opts.get('subrepos'):
461 ctx = repo[None]
456 ctx = repo[None]
462 for subpath in sorted(ctx.substate):
457 for subpath in sorted(ctx.substate):
463 sub = ctx.sub(subpath)
458 sub = ctx.sub(subpath)
464 ret = min(ret, sub.incoming(ui, source, opts))
459 ret = min(ret, sub.incoming(ui, source, opts))
465 return ret
460 return ret
466
461
467 def display(other, chlist, displayer):
462 def display(other, chlist, displayer):
468 limit = cmdutil.loglimit(opts)
463 limit = cmdutil.loglimit(opts)
469 if opts.get('newest_first'):
464 if opts.get('newest_first'):
470 chlist.reverse()
465 chlist.reverse()
471 count = 0
466 count = 0
472 for n in chlist:
467 for n in chlist:
473 if limit is not None and count >= limit:
468 if limit is not None and count >= limit:
474 break
469 break
475 parents = [p for p in other.changelog.parents(n) if p != nullid]
470 parents = [p for p in other.changelog.parents(n) if p != nullid]
476 if opts.get('no_merges') and len(parents) == 2:
471 if opts.get('no_merges') and len(parents) == 2:
477 continue
472 continue
478 count += 1
473 count += 1
479 displayer.show(other[n])
474 displayer.show(other[n])
480 return _incoming(display, subreporecurse, ui, repo, source, opts)
475 return _incoming(display, subreporecurse, ui, repo, source, opts)
481
476
482 def _outgoing(ui, repo, dest, opts):
477 def _outgoing(ui, repo, dest, opts):
483 dest = ui.expandpath(dest or 'default-push', dest or 'default')
478 dest = ui.expandpath(dest or 'default-push', dest or 'default')
484 dest, branches = parseurl(dest, opts.get('branch'))
479 dest, branches = parseurl(dest, opts.get('branch'))
485 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
480 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
486 revs, checkout = addbranchrevs(repo, repo, branches, opts.get('rev'))
481 revs, checkout = addbranchrevs(repo, repo, branches, opts.get('rev'))
487 if revs:
482 if revs:
488 revs = [repo.lookup(rev) for rev in revs]
483 revs = [repo.lookup(rev) for rev in revs]
489
484
490 other = repository(remoteui(repo, opts), dest)
485 other = repository(remoteui(repo, opts), dest)
491 o = discovery.findoutgoing(repo, other, force=opts.get('force'))
486 inc = discovery.findcommonincoming(repo, other, force=opts.get('force'))
487 common, _anyinc, _heads = inc
488 o = repo.changelog.findmissing(common, revs)
492 if not o:
489 if not o:
493 ui.status(_("no changes found\n"))
490 ui.status(_("no changes found\n"))
494 return None
491 return None
495
492 return o
496 return repo.changelog.nodesbetween(o, revs)[0]
497
493
498 def outgoing(ui, repo, dest, opts):
494 def outgoing(ui, repo, dest, opts):
499 def recurse():
495 def recurse():
500 ret = 1
496 ret = 1
501 if opts.get('subrepos'):
497 if opts.get('subrepos'):
502 ctx = repo[None]
498 ctx = repo[None]
503 for subpath in sorted(ctx.substate):
499 for subpath in sorted(ctx.substate):
504 sub = ctx.sub(subpath)
500 sub = ctx.sub(subpath)
505 ret = min(ret, sub.outgoing(ui, dest, opts))
501 ret = min(ret, sub.outgoing(ui, dest, opts))
506 return ret
502 return ret
507
503
508 limit = cmdutil.loglimit(opts)
504 limit = cmdutil.loglimit(opts)
509 o = _outgoing(ui, repo, dest, opts)
505 o = _outgoing(ui, repo, dest, opts)
510 if o is None:
506 if o is None:
511 return recurse()
507 return recurse()
512
508
513 if opts.get('newest_first'):
509 if opts.get('newest_first'):
514 o.reverse()
510 o.reverse()
515 displayer = cmdutil.show_changeset(ui, repo, opts)
511 displayer = cmdutil.show_changeset(ui, repo, opts)
516 count = 0
512 count = 0
517 for n in o:
513 for n in o:
518 if limit is not None and count >= limit:
514 if limit is not None and count >= limit:
519 break
515 break
520 parents = [p for p in repo.changelog.parents(n) if p != nullid]
516 parents = [p for p in repo.changelog.parents(n) if p != nullid]
521 if opts.get('no_merges') and len(parents) == 2:
517 if opts.get('no_merges') and len(parents) == 2:
522 continue
518 continue
523 count += 1
519 count += 1
524 displayer.show(repo[n])
520 displayer.show(repo[n])
525 displayer.close()
521 displayer.close()
526 recurse()
522 recurse()
527 return 0 # exit code is zero since we found outgoing changes
523 return 0 # exit code is zero since we found outgoing changes
528
524
529 def revert(repo, node, choose):
525 def revert(repo, node, choose):
530 """revert changes to revision in node without updating dirstate"""
526 """revert changes to revision in node without updating dirstate"""
531 return mergemod.update(repo, node, False, True, choose)[3] > 0
527 return mergemod.update(repo, node, False, True, choose)[3] > 0
532
528
533 def verify(repo):
529 def verify(repo):
534 """verify the consistency of a repository"""
530 """verify the consistency of a repository"""
535 return verifymod.verify(repo)
531 return verifymod.verify(repo)
536
532
537 def remoteui(src, opts):
533 def remoteui(src, opts):
538 'build a remote ui from ui or repo and opts'
534 'build a remote ui from ui or repo and opts'
539 if hasattr(src, 'baseui'): # looks like a repository
535 if hasattr(src, 'baseui'): # looks like a repository
540 dst = src.baseui.copy() # drop repo-specific config
536 dst = src.baseui.copy() # drop repo-specific config
541 src = src.ui # copy target options from repo
537 src = src.ui # copy target options from repo
542 else: # assume it's a global ui object
538 else: # assume it's a global ui object
543 dst = src.copy() # keep all global options
539 dst = src.copy() # keep all global options
544
540
545 # copy ssh-specific options
541 # copy ssh-specific options
546 for o in 'ssh', 'remotecmd':
542 for o in 'ssh', 'remotecmd':
547 v = opts.get(o) or src.config('ui', o)
543 v = opts.get(o) or src.config('ui', o)
548 if v:
544 if v:
549 dst.setconfig("ui", o, v)
545 dst.setconfig("ui", o, v)
550
546
551 # copy bundle-specific options
547 # copy bundle-specific options
552 r = src.config('bundle', 'mainreporoot')
548 r = src.config('bundle', 'mainreporoot')
553 if r:
549 if r:
554 dst.setconfig('bundle', 'mainreporoot', r)
550 dst.setconfig('bundle', 'mainreporoot', r)
555
551
556 # copy selected local settings to the remote ui
552 # copy selected local settings to the remote ui
557 for sect in ('auth', 'hostfingerprints', 'http_proxy'):
553 for sect in ('auth', 'hostfingerprints', 'http_proxy'):
558 for key, val in src.configitems(sect):
554 for key, val in src.configitems(sect):
559 dst.setconfig(sect, key, val)
555 dst.setconfig(sect, key, val)
560 v = src.config('web', 'cacerts')
556 v = src.config('web', 'cacerts')
561 if v:
557 if v:
562 dst.setconfig('web', 'cacerts', util.expandpath(v))
558 dst.setconfig('web', 'cacerts', util.expandpath(v))
563
559
564 return dst
560 return dst
@@ -1,1942 +1,1943
1 # localrepo.py - read/write repository class for mercurial
1 # localrepo.py - read/write repository 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 of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from node import bin, hex, nullid, nullrev, short
8 from node import bin, hex, nullid, nullrev, short
9 from i18n import _
9 from i18n import _
10 import repo, changegroup, subrepo, discovery, pushkey
10 import repo, changegroup, subrepo, discovery, pushkey
11 import changelog, dirstate, filelog, manifest, context, bookmarks
11 import changelog, dirstate, filelog, manifest, context, bookmarks
12 import lock, transaction, store, encoding
12 import lock, transaction, store, encoding
13 import scmutil, util, extensions, hook, error
13 import scmutil, util, extensions, hook, error
14 import match as matchmod
14 import match as matchmod
15 import merge as mergemod
15 import merge as mergemod
16 import tags as tagsmod
16 import tags as tagsmod
17 import url as urlmod
17 import url as urlmod
18 from lock import release
18 from lock import release
19 import weakref, errno, os, time, inspect
19 import weakref, errno, os, time, inspect
20 propertycache = util.propertycache
20 propertycache = util.propertycache
21
21
22 class localrepository(repo.repository):
22 class localrepository(repo.repository):
23 capabilities = set(('lookup', 'changegroupsubset', 'branchmap', 'pushkey',
23 capabilities = set(('lookup', 'changegroupsubset', 'branchmap', 'pushkey',
24 'known', 'getbundle'))
24 'known', 'getbundle'))
25 supportedformats = set(('revlogv1', 'parentdelta'))
25 supportedformats = set(('revlogv1', 'parentdelta'))
26 supported = supportedformats | set(('store', 'fncache', 'shared',
26 supported = supportedformats | set(('store', 'fncache', 'shared',
27 'dotencode'))
27 'dotencode'))
28
28
29 def __init__(self, baseui, path=None, create=0):
29 def __init__(self, baseui, path=None, create=0):
30 repo.repository.__init__(self)
30 repo.repository.__init__(self)
31 self.root = os.path.realpath(util.expandpath(path))
31 self.root = os.path.realpath(util.expandpath(path))
32 self.path = os.path.join(self.root, ".hg")
32 self.path = os.path.join(self.root, ".hg")
33 self.origroot = path
33 self.origroot = path
34 self.auditor = scmutil.path_auditor(self.root, self._checknested)
34 self.auditor = scmutil.path_auditor(self.root, self._checknested)
35 self.opener = scmutil.opener(self.path)
35 self.opener = scmutil.opener(self.path)
36 self.wopener = scmutil.opener(self.root)
36 self.wopener = scmutil.opener(self.root)
37 self.baseui = baseui
37 self.baseui = baseui
38 self.ui = baseui.copy()
38 self.ui = baseui.copy()
39
39
40 try:
40 try:
41 self.ui.readconfig(self.join("hgrc"), self.root)
41 self.ui.readconfig(self.join("hgrc"), self.root)
42 extensions.loadall(self.ui)
42 extensions.loadall(self.ui)
43 except IOError:
43 except IOError:
44 pass
44 pass
45
45
46 if not os.path.isdir(self.path):
46 if not os.path.isdir(self.path):
47 if create:
47 if create:
48 if not os.path.exists(path):
48 if not os.path.exists(path):
49 util.makedirs(path)
49 util.makedirs(path)
50 util.makedir(self.path, notindexed=True)
50 util.makedir(self.path, notindexed=True)
51 requirements = ["revlogv1"]
51 requirements = ["revlogv1"]
52 if self.ui.configbool('format', 'usestore', True):
52 if self.ui.configbool('format', 'usestore', True):
53 os.mkdir(os.path.join(self.path, "store"))
53 os.mkdir(os.path.join(self.path, "store"))
54 requirements.append("store")
54 requirements.append("store")
55 if self.ui.configbool('format', 'usefncache', True):
55 if self.ui.configbool('format', 'usefncache', True):
56 requirements.append("fncache")
56 requirements.append("fncache")
57 if self.ui.configbool('format', 'dotencode', True):
57 if self.ui.configbool('format', 'dotencode', True):
58 requirements.append('dotencode')
58 requirements.append('dotencode')
59 # create an invalid changelog
59 # create an invalid changelog
60 self.opener("00changelog.i", "a").write(
60 self.opener("00changelog.i", "a").write(
61 '\0\0\0\2' # represents revlogv2
61 '\0\0\0\2' # represents revlogv2
62 ' dummy changelog to prevent using the old repo layout'
62 ' dummy changelog to prevent using the old repo layout'
63 )
63 )
64 if self.ui.configbool('format', 'parentdelta', False):
64 if self.ui.configbool('format', 'parentdelta', False):
65 requirements.append("parentdelta")
65 requirements.append("parentdelta")
66 else:
66 else:
67 raise error.RepoError(_("repository %s not found") % path)
67 raise error.RepoError(_("repository %s not found") % path)
68 elif create:
68 elif create:
69 raise error.RepoError(_("repository %s already exists") % path)
69 raise error.RepoError(_("repository %s already exists") % path)
70 else:
70 else:
71 # find requirements
71 # find requirements
72 requirements = set()
72 requirements = set()
73 try:
73 try:
74 requirements = set(self.opener("requires").read().splitlines())
74 requirements = set(self.opener("requires").read().splitlines())
75 except IOError, inst:
75 except IOError, inst:
76 if inst.errno != errno.ENOENT:
76 if inst.errno != errno.ENOENT:
77 raise
77 raise
78 for r in requirements - self.supported:
78 for r in requirements - self.supported:
79 raise error.RequirementError(
79 raise error.RequirementError(
80 _("requirement '%s' not supported") % r)
80 _("requirement '%s' not supported") % r)
81
81
82 self.sharedpath = self.path
82 self.sharedpath = self.path
83 try:
83 try:
84 s = os.path.realpath(self.opener("sharedpath").read())
84 s = os.path.realpath(self.opener("sharedpath").read())
85 if not os.path.exists(s):
85 if not os.path.exists(s):
86 raise error.RepoError(
86 raise error.RepoError(
87 _('.hg/sharedpath points to nonexistent directory %s') % s)
87 _('.hg/sharedpath points to nonexistent directory %s') % s)
88 self.sharedpath = s
88 self.sharedpath = s
89 except IOError, inst:
89 except IOError, inst:
90 if inst.errno != errno.ENOENT:
90 if inst.errno != errno.ENOENT:
91 raise
91 raise
92
92
93 self.store = store.store(requirements, self.sharedpath, scmutil.opener)
93 self.store = store.store(requirements, self.sharedpath, scmutil.opener)
94 self.spath = self.store.path
94 self.spath = self.store.path
95 self.sopener = self.store.opener
95 self.sopener = self.store.opener
96 self.sjoin = self.store.join
96 self.sjoin = self.store.join
97 self.opener.createmode = self.store.createmode
97 self.opener.createmode = self.store.createmode
98 self._applyrequirements(requirements)
98 self._applyrequirements(requirements)
99 if create:
99 if create:
100 self._writerequirements()
100 self._writerequirements()
101
101
102 # These two define the set of tags for this repository. _tags
102 # These two define the set of tags for this repository. _tags
103 # maps tag name to node; _tagtypes maps tag name to 'global' or
103 # maps tag name to node; _tagtypes maps tag name to 'global' or
104 # 'local'. (Global tags are defined by .hgtags across all
104 # 'local'. (Global tags are defined by .hgtags across all
105 # heads, and local tags are defined in .hg/localtags.) They
105 # heads, and local tags are defined in .hg/localtags.) They
106 # constitute the in-memory cache of tags.
106 # constitute the in-memory cache of tags.
107 self._tags = None
107 self._tags = None
108 self._tagtypes = None
108 self._tagtypes = None
109
109
110 self._branchcache = None
110 self._branchcache = None
111 self._branchcachetip = None
111 self._branchcachetip = None
112 self.nodetagscache = None
112 self.nodetagscache = None
113 self.filterpats = {}
113 self.filterpats = {}
114 self._datafilters = {}
114 self._datafilters = {}
115 self._transref = self._lockref = self._wlockref = None
115 self._transref = self._lockref = self._wlockref = None
116
116
117 def _applyrequirements(self, requirements):
117 def _applyrequirements(self, requirements):
118 self.requirements = requirements
118 self.requirements = requirements
119 self.sopener.options = {}
119 self.sopener.options = {}
120 if 'parentdelta' in requirements:
120 if 'parentdelta' in requirements:
121 self.sopener.options['parentdelta'] = 1
121 self.sopener.options['parentdelta'] = 1
122
122
123 def _writerequirements(self):
123 def _writerequirements(self):
124 reqfile = self.opener("requires", "w")
124 reqfile = self.opener("requires", "w")
125 for r in self.requirements:
125 for r in self.requirements:
126 reqfile.write("%s\n" % r)
126 reqfile.write("%s\n" % r)
127 reqfile.close()
127 reqfile.close()
128
128
129 def _checknested(self, path):
129 def _checknested(self, path):
130 """Determine if path is a legal nested repository."""
130 """Determine if path is a legal nested repository."""
131 if not path.startswith(self.root):
131 if not path.startswith(self.root):
132 return False
132 return False
133 subpath = path[len(self.root) + 1:]
133 subpath = path[len(self.root) + 1:]
134
134
135 # XXX: Checking against the current working copy is wrong in
135 # XXX: Checking against the current working copy is wrong in
136 # the sense that it can reject things like
136 # the sense that it can reject things like
137 #
137 #
138 # $ hg cat -r 10 sub/x.txt
138 # $ hg cat -r 10 sub/x.txt
139 #
139 #
140 # if sub/ is no longer a subrepository in the working copy
140 # if sub/ is no longer a subrepository in the working copy
141 # parent revision.
141 # parent revision.
142 #
142 #
143 # However, it can of course also allow things that would have
143 # However, it can of course also allow things that would have
144 # been rejected before, such as the above cat command if sub/
144 # been rejected before, such as the above cat command if sub/
145 # is a subrepository now, but was a normal directory before.
145 # is a subrepository now, but was a normal directory before.
146 # The old path auditor would have rejected by mistake since it
146 # The old path auditor would have rejected by mistake since it
147 # panics when it sees sub/.hg/.
147 # panics when it sees sub/.hg/.
148 #
148 #
149 # All in all, checking against the working copy seems sensible
149 # All in all, checking against the working copy seems sensible
150 # since we want to prevent access to nested repositories on
150 # since we want to prevent access to nested repositories on
151 # the filesystem *now*.
151 # the filesystem *now*.
152 ctx = self[None]
152 ctx = self[None]
153 parts = util.splitpath(subpath)
153 parts = util.splitpath(subpath)
154 while parts:
154 while parts:
155 prefix = os.sep.join(parts)
155 prefix = os.sep.join(parts)
156 if prefix in ctx.substate:
156 if prefix in ctx.substate:
157 if prefix == subpath:
157 if prefix == subpath:
158 return True
158 return True
159 else:
159 else:
160 sub = ctx.sub(prefix)
160 sub = ctx.sub(prefix)
161 return sub.checknested(subpath[len(prefix) + 1:])
161 return sub.checknested(subpath[len(prefix) + 1:])
162 else:
162 else:
163 parts.pop()
163 parts.pop()
164 return False
164 return False
165
165
166 @util.propertycache
166 @util.propertycache
167 def _bookmarks(self):
167 def _bookmarks(self):
168 return bookmarks.read(self)
168 return bookmarks.read(self)
169
169
170 @util.propertycache
170 @util.propertycache
171 def _bookmarkcurrent(self):
171 def _bookmarkcurrent(self):
172 return bookmarks.readcurrent(self)
172 return bookmarks.readcurrent(self)
173
173
174 @propertycache
174 @propertycache
175 def changelog(self):
175 def changelog(self):
176 c = changelog.changelog(self.sopener)
176 c = changelog.changelog(self.sopener)
177 if 'HG_PENDING' in os.environ:
177 if 'HG_PENDING' in os.environ:
178 p = os.environ['HG_PENDING']
178 p = os.environ['HG_PENDING']
179 if p.startswith(self.root):
179 if p.startswith(self.root):
180 c.readpending('00changelog.i.a')
180 c.readpending('00changelog.i.a')
181 self.sopener.options['defversion'] = c.version
181 self.sopener.options['defversion'] = c.version
182 return c
182 return c
183
183
184 @propertycache
184 @propertycache
185 def manifest(self):
185 def manifest(self):
186 return manifest.manifest(self.sopener)
186 return manifest.manifest(self.sopener)
187
187
188 @propertycache
188 @propertycache
189 def dirstate(self):
189 def dirstate(self):
190 warned = [0]
190 warned = [0]
191 def validate(node):
191 def validate(node):
192 try:
192 try:
193 self.changelog.rev(node)
193 self.changelog.rev(node)
194 return node
194 return node
195 except error.LookupError:
195 except error.LookupError:
196 if not warned[0]:
196 if not warned[0]:
197 warned[0] = True
197 warned[0] = True
198 self.ui.warn(_("warning: ignoring unknown"
198 self.ui.warn(_("warning: ignoring unknown"
199 " working parent %s!\n") % short(node))
199 " working parent %s!\n") % short(node))
200 return nullid
200 return nullid
201
201
202 return dirstate.dirstate(self.opener, self.ui, self.root, validate)
202 return dirstate.dirstate(self.opener, self.ui, self.root, validate)
203
203
204 def __getitem__(self, changeid):
204 def __getitem__(self, changeid):
205 if changeid is None:
205 if changeid is None:
206 return context.workingctx(self)
206 return context.workingctx(self)
207 return context.changectx(self, changeid)
207 return context.changectx(self, changeid)
208
208
209 def __contains__(self, changeid):
209 def __contains__(self, changeid):
210 try:
210 try:
211 return bool(self.lookup(changeid))
211 return bool(self.lookup(changeid))
212 except error.RepoLookupError:
212 except error.RepoLookupError:
213 return False
213 return False
214
214
215 def __nonzero__(self):
215 def __nonzero__(self):
216 return True
216 return True
217
217
218 def __len__(self):
218 def __len__(self):
219 return len(self.changelog)
219 return len(self.changelog)
220
220
221 def __iter__(self):
221 def __iter__(self):
222 for i in xrange(len(self)):
222 for i in xrange(len(self)):
223 yield i
223 yield i
224
224
225 def url(self):
225 def url(self):
226 return 'file:' + self.root
226 return 'file:' + self.root
227
227
228 def hook(self, name, throw=False, **args):
228 def hook(self, name, throw=False, **args):
229 return hook.hook(self.ui, self, name, throw, **args)
229 return hook.hook(self.ui, self, name, throw, **args)
230
230
231 tag_disallowed = ':\r\n'
231 tag_disallowed = ':\r\n'
232
232
233 def _tag(self, names, node, message, local, user, date, extra={}):
233 def _tag(self, names, node, message, local, user, date, extra={}):
234 if isinstance(names, str):
234 if isinstance(names, str):
235 allchars = names
235 allchars = names
236 names = (names,)
236 names = (names,)
237 else:
237 else:
238 allchars = ''.join(names)
238 allchars = ''.join(names)
239 for c in self.tag_disallowed:
239 for c in self.tag_disallowed:
240 if c in allchars:
240 if c in allchars:
241 raise util.Abort(_('%r cannot be used in a tag name') % c)
241 raise util.Abort(_('%r cannot be used in a tag name') % c)
242
242
243 branches = self.branchmap()
243 branches = self.branchmap()
244 for name in names:
244 for name in names:
245 self.hook('pretag', throw=True, node=hex(node), tag=name,
245 self.hook('pretag', throw=True, node=hex(node), tag=name,
246 local=local)
246 local=local)
247 if name in branches:
247 if name in branches:
248 self.ui.warn(_("warning: tag %s conflicts with existing"
248 self.ui.warn(_("warning: tag %s conflicts with existing"
249 " branch name\n") % name)
249 " branch name\n") % name)
250
250
251 def writetags(fp, names, munge, prevtags):
251 def writetags(fp, names, munge, prevtags):
252 fp.seek(0, 2)
252 fp.seek(0, 2)
253 if prevtags and prevtags[-1] != '\n':
253 if prevtags and prevtags[-1] != '\n':
254 fp.write('\n')
254 fp.write('\n')
255 for name in names:
255 for name in names:
256 m = munge and munge(name) or name
256 m = munge and munge(name) or name
257 if self._tagtypes and name in self._tagtypes:
257 if self._tagtypes and name in self._tagtypes:
258 old = self._tags.get(name, nullid)
258 old = self._tags.get(name, nullid)
259 fp.write('%s %s\n' % (hex(old), m))
259 fp.write('%s %s\n' % (hex(old), m))
260 fp.write('%s %s\n' % (hex(node), m))
260 fp.write('%s %s\n' % (hex(node), m))
261 fp.close()
261 fp.close()
262
262
263 prevtags = ''
263 prevtags = ''
264 if local:
264 if local:
265 try:
265 try:
266 fp = self.opener('localtags', 'r+')
266 fp = self.opener('localtags', 'r+')
267 except IOError:
267 except IOError:
268 fp = self.opener('localtags', 'a')
268 fp = self.opener('localtags', 'a')
269 else:
269 else:
270 prevtags = fp.read()
270 prevtags = fp.read()
271
271
272 # local tags are stored in the current charset
272 # local tags are stored in the current charset
273 writetags(fp, names, None, prevtags)
273 writetags(fp, names, None, prevtags)
274 for name in names:
274 for name in names:
275 self.hook('tag', node=hex(node), tag=name, local=local)
275 self.hook('tag', node=hex(node), tag=name, local=local)
276 return
276 return
277
277
278 try:
278 try:
279 fp = self.wfile('.hgtags', 'rb+')
279 fp = self.wfile('.hgtags', 'rb+')
280 except IOError:
280 except IOError:
281 fp = self.wfile('.hgtags', 'ab')
281 fp = self.wfile('.hgtags', 'ab')
282 else:
282 else:
283 prevtags = fp.read()
283 prevtags = fp.read()
284
284
285 # committed tags are stored in UTF-8
285 # committed tags are stored in UTF-8
286 writetags(fp, names, encoding.fromlocal, prevtags)
286 writetags(fp, names, encoding.fromlocal, prevtags)
287
287
288 fp.close()
288 fp.close()
289
289
290 if '.hgtags' not in self.dirstate:
290 if '.hgtags' not in self.dirstate:
291 self[None].add(['.hgtags'])
291 self[None].add(['.hgtags'])
292
292
293 m = matchmod.exact(self.root, '', ['.hgtags'])
293 m = matchmod.exact(self.root, '', ['.hgtags'])
294 tagnode = self.commit(message, user, date, extra=extra, match=m)
294 tagnode = self.commit(message, user, date, extra=extra, match=m)
295
295
296 for name in names:
296 for name in names:
297 self.hook('tag', node=hex(node), tag=name, local=local)
297 self.hook('tag', node=hex(node), tag=name, local=local)
298
298
299 return tagnode
299 return tagnode
300
300
301 def tag(self, names, node, message, local, user, date):
301 def tag(self, names, node, message, local, user, date):
302 '''tag a revision with one or more symbolic names.
302 '''tag a revision with one or more symbolic names.
303
303
304 names is a list of strings or, when adding a single tag, names may be a
304 names is a list of strings or, when adding a single tag, names may be a
305 string.
305 string.
306
306
307 if local is True, the tags are stored in a per-repository file.
307 if local is True, the tags are stored in a per-repository file.
308 otherwise, they are stored in the .hgtags file, and a new
308 otherwise, they are stored in the .hgtags file, and a new
309 changeset is committed with the change.
309 changeset is committed with the change.
310
310
311 keyword arguments:
311 keyword arguments:
312
312
313 local: whether to store tags in non-version-controlled file
313 local: whether to store tags in non-version-controlled file
314 (default False)
314 (default False)
315
315
316 message: commit message to use if committing
316 message: commit message to use if committing
317
317
318 user: name of user to use if committing
318 user: name of user to use if committing
319
319
320 date: date tuple to use if committing'''
320 date: date tuple to use if committing'''
321
321
322 if not local:
322 if not local:
323 for x in self.status()[:5]:
323 for x in self.status()[:5]:
324 if '.hgtags' in x:
324 if '.hgtags' in x:
325 raise util.Abort(_('working copy of .hgtags is changed '
325 raise util.Abort(_('working copy of .hgtags is changed '
326 '(please commit .hgtags manually)'))
326 '(please commit .hgtags manually)'))
327
327
328 self.tags() # instantiate the cache
328 self.tags() # instantiate the cache
329 self._tag(names, node, message, local, user, date)
329 self._tag(names, node, message, local, user, date)
330
330
331 def tags(self):
331 def tags(self):
332 '''return a mapping of tag to node'''
332 '''return a mapping of tag to node'''
333 if self._tags is None:
333 if self._tags is None:
334 (self._tags, self._tagtypes) = self._findtags()
334 (self._tags, self._tagtypes) = self._findtags()
335
335
336 return self._tags
336 return self._tags
337
337
338 def _findtags(self):
338 def _findtags(self):
339 '''Do the hard work of finding tags. Return a pair of dicts
339 '''Do the hard work of finding tags. Return a pair of dicts
340 (tags, tagtypes) where tags maps tag name to node, and tagtypes
340 (tags, tagtypes) where tags maps tag name to node, and tagtypes
341 maps tag name to a string like \'global\' or \'local\'.
341 maps tag name to a string like \'global\' or \'local\'.
342 Subclasses or extensions are free to add their own tags, but
342 Subclasses or extensions are free to add their own tags, but
343 should be aware that the returned dicts will be retained for the
343 should be aware that the returned dicts will be retained for the
344 duration of the localrepo object.'''
344 duration of the localrepo object.'''
345
345
346 # XXX what tagtype should subclasses/extensions use? Currently
346 # XXX what tagtype should subclasses/extensions use? Currently
347 # mq and bookmarks add tags, but do not set the tagtype at all.
347 # mq and bookmarks add tags, but do not set the tagtype at all.
348 # Should each extension invent its own tag type? Should there
348 # Should each extension invent its own tag type? Should there
349 # be one tagtype for all such "virtual" tags? Or is the status
349 # be one tagtype for all such "virtual" tags? Or is the status
350 # quo fine?
350 # quo fine?
351
351
352 alltags = {} # map tag name to (node, hist)
352 alltags = {} # map tag name to (node, hist)
353 tagtypes = {}
353 tagtypes = {}
354
354
355 tagsmod.findglobaltags(self.ui, self, alltags, tagtypes)
355 tagsmod.findglobaltags(self.ui, self, alltags, tagtypes)
356 tagsmod.readlocaltags(self.ui, self, alltags, tagtypes)
356 tagsmod.readlocaltags(self.ui, self, alltags, tagtypes)
357
357
358 # Build the return dicts. Have to re-encode tag names because
358 # Build the return dicts. Have to re-encode tag names because
359 # the tags module always uses UTF-8 (in order not to lose info
359 # the tags module always uses UTF-8 (in order not to lose info
360 # writing to the cache), but the rest of Mercurial wants them in
360 # writing to the cache), but the rest of Mercurial wants them in
361 # local encoding.
361 # local encoding.
362 tags = {}
362 tags = {}
363 for (name, (node, hist)) in alltags.iteritems():
363 for (name, (node, hist)) in alltags.iteritems():
364 if node != nullid:
364 if node != nullid:
365 try:
365 try:
366 # ignore tags to unknown nodes
366 # ignore tags to unknown nodes
367 self.changelog.lookup(node)
367 self.changelog.lookup(node)
368 tags[encoding.tolocal(name)] = node
368 tags[encoding.tolocal(name)] = node
369 except error.LookupError:
369 except error.LookupError:
370 pass
370 pass
371 tags['tip'] = self.changelog.tip()
371 tags['tip'] = self.changelog.tip()
372 tagtypes = dict([(encoding.tolocal(name), value)
372 tagtypes = dict([(encoding.tolocal(name), value)
373 for (name, value) in tagtypes.iteritems()])
373 for (name, value) in tagtypes.iteritems()])
374 return (tags, tagtypes)
374 return (tags, tagtypes)
375
375
376 def tagtype(self, tagname):
376 def tagtype(self, tagname):
377 '''
377 '''
378 return the type of the given tag. result can be:
378 return the type of the given tag. result can be:
379
379
380 'local' : a local tag
380 'local' : a local tag
381 'global' : a global tag
381 'global' : a global tag
382 None : tag does not exist
382 None : tag does not exist
383 '''
383 '''
384
384
385 self.tags()
385 self.tags()
386
386
387 return self._tagtypes.get(tagname)
387 return self._tagtypes.get(tagname)
388
388
389 def tagslist(self):
389 def tagslist(self):
390 '''return a list of tags ordered by revision'''
390 '''return a list of tags ordered by revision'''
391 l = []
391 l = []
392 for t, n in self.tags().iteritems():
392 for t, n in self.tags().iteritems():
393 r = self.changelog.rev(n)
393 r = self.changelog.rev(n)
394 l.append((r, t, n))
394 l.append((r, t, n))
395 return [(t, n) for r, t, n in sorted(l)]
395 return [(t, n) for r, t, n in sorted(l)]
396
396
397 def nodetags(self, node):
397 def nodetags(self, node):
398 '''return the tags associated with a node'''
398 '''return the tags associated with a node'''
399 if not self.nodetagscache:
399 if not self.nodetagscache:
400 self.nodetagscache = {}
400 self.nodetagscache = {}
401 for t, n in self.tags().iteritems():
401 for t, n in self.tags().iteritems():
402 self.nodetagscache.setdefault(n, []).append(t)
402 self.nodetagscache.setdefault(n, []).append(t)
403 for tags in self.nodetagscache.itervalues():
403 for tags in self.nodetagscache.itervalues():
404 tags.sort()
404 tags.sort()
405 return self.nodetagscache.get(node, [])
405 return self.nodetagscache.get(node, [])
406
406
407 def nodebookmarks(self, node):
407 def nodebookmarks(self, node):
408 marks = []
408 marks = []
409 for bookmark, n in self._bookmarks.iteritems():
409 for bookmark, n in self._bookmarks.iteritems():
410 if n == node:
410 if n == node:
411 marks.append(bookmark)
411 marks.append(bookmark)
412 return sorted(marks)
412 return sorted(marks)
413
413
414 def _branchtags(self, partial, lrev):
414 def _branchtags(self, partial, lrev):
415 # TODO: rename this function?
415 # TODO: rename this function?
416 tiprev = len(self) - 1
416 tiprev = len(self) - 1
417 if lrev != tiprev:
417 if lrev != tiprev:
418 ctxgen = (self[r] for r in xrange(lrev + 1, tiprev + 1))
418 ctxgen = (self[r] for r in xrange(lrev + 1, tiprev + 1))
419 self._updatebranchcache(partial, ctxgen)
419 self._updatebranchcache(partial, ctxgen)
420 self._writebranchcache(partial, self.changelog.tip(), tiprev)
420 self._writebranchcache(partial, self.changelog.tip(), tiprev)
421
421
422 return partial
422 return partial
423
423
424 def updatebranchcache(self):
424 def updatebranchcache(self):
425 tip = self.changelog.tip()
425 tip = self.changelog.tip()
426 if self._branchcache is not None and self._branchcachetip == tip:
426 if self._branchcache is not None and self._branchcachetip == tip:
427 return self._branchcache
427 return self._branchcache
428
428
429 oldtip = self._branchcachetip
429 oldtip = self._branchcachetip
430 self._branchcachetip = tip
430 self._branchcachetip = tip
431 if oldtip is None or oldtip not in self.changelog.nodemap:
431 if oldtip is None or oldtip not in self.changelog.nodemap:
432 partial, last, lrev = self._readbranchcache()
432 partial, last, lrev = self._readbranchcache()
433 else:
433 else:
434 lrev = self.changelog.rev(oldtip)
434 lrev = self.changelog.rev(oldtip)
435 partial = self._branchcache
435 partial = self._branchcache
436
436
437 self._branchtags(partial, lrev)
437 self._branchtags(partial, lrev)
438 # this private cache holds all heads (not just tips)
438 # this private cache holds all heads (not just tips)
439 self._branchcache = partial
439 self._branchcache = partial
440
440
441 def branchmap(self):
441 def branchmap(self):
442 '''returns a dictionary {branch: [branchheads]}'''
442 '''returns a dictionary {branch: [branchheads]}'''
443 self.updatebranchcache()
443 self.updatebranchcache()
444 return self._branchcache
444 return self._branchcache
445
445
446 def branchtags(self):
446 def branchtags(self):
447 '''return a dict where branch names map to the tipmost head of
447 '''return a dict where branch names map to the tipmost head of
448 the branch, open heads come before closed'''
448 the branch, open heads come before closed'''
449 bt = {}
449 bt = {}
450 for bn, heads in self.branchmap().iteritems():
450 for bn, heads in self.branchmap().iteritems():
451 tip = heads[-1]
451 tip = heads[-1]
452 for h in reversed(heads):
452 for h in reversed(heads):
453 if 'close' not in self.changelog.read(h)[5]:
453 if 'close' not in self.changelog.read(h)[5]:
454 tip = h
454 tip = h
455 break
455 break
456 bt[bn] = tip
456 bt[bn] = tip
457 return bt
457 return bt
458
458
459 def _readbranchcache(self):
459 def _readbranchcache(self):
460 partial = {}
460 partial = {}
461 try:
461 try:
462 f = self.opener("cache/branchheads")
462 f = self.opener("cache/branchheads")
463 lines = f.read().split('\n')
463 lines = f.read().split('\n')
464 f.close()
464 f.close()
465 except (IOError, OSError):
465 except (IOError, OSError):
466 return {}, nullid, nullrev
466 return {}, nullid, nullrev
467
467
468 try:
468 try:
469 last, lrev = lines.pop(0).split(" ", 1)
469 last, lrev = lines.pop(0).split(" ", 1)
470 last, lrev = bin(last), int(lrev)
470 last, lrev = bin(last), int(lrev)
471 if lrev >= len(self) or self[lrev].node() != last:
471 if lrev >= len(self) or self[lrev].node() != last:
472 # invalidate the cache
472 # invalidate the cache
473 raise ValueError('invalidating branch cache (tip differs)')
473 raise ValueError('invalidating branch cache (tip differs)')
474 for l in lines:
474 for l in lines:
475 if not l:
475 if not l:
476 continue
476 continue
477 node, label = l.split(" ", 1)
477 node, label = l.split(" ", 1)
478 label = encoding.tolocal(label.strip())
478 label = encoding.tolocal(label.strip())
479 partial.setdefault(label, []).append(bin(node))
479 partial.setdefault(label, []).append(bin(node))
480 except KeyboardInterrupt:
480 except KeyboardInterrupt:
481 raise
481 raise
482 except Exception, inst:
482 except Exception, inst:
483 if self.ui.debugflag:
483 if self.ui.debugflag:
484 self.ui.warn(str(inst), '\n')
484 self.ui.warn(str(inst), '\n')
485 partial, last, lrev = {}, nullid, nullrev
485 partial, last, lrev = {}, nullid, nullrev
486 return partial, last, lrev
486 return partial, last, lrev
487
487
488 def _writebranchcache(self, branches, tip, tiprev):
488 def _writebranchcache(self, branches, tip, tiprev):
489 try:
489 try:
490 f = self.opener("cache/branchheads", "w", atomictemp=True)
490 f = self.opener("cache/branchheads", "w", atomictemp=True)
491 f.write("%s %s\n" % (hex(tip), tiprev))
491 f.write("%s %s\n" % (hex(tip), tiprev))
492 for label, nodes in branches.iteritems():
492 for label, nodes in branches.iteritems():
493 for node in nodes:
493 for node in nodes:
494 f.write("%s %s\n" % (hex(node), encoding.fromlocal(label)))
494 f.write("%s %s\n" % (hex(node), encoding.fromlocal(label)))
495 f.rename()
495 f.rename()
496 except (IOError, OSError):
496 except (IOError, OSError):
497 pass
497 pass
498
498
499 def _updatebranchcache(self, partial, ctxgen):
499 def _updatebranchcache(self, partial, ctxgen):
500 # collect new branch entries
500 # collect new branch entries
501 newbranches = {}
501 newbranches = {}
502 for c in ctxgen:
502 for c in ctxgen:
503 newbranches.setdefault(c.branch(), []).append(c.node())
503 newbranches.setdefault(c.branch(), []).append(c.node())
504 # if older branchheads are reachable from new ones, they aren't
504 # if older branchheads are reachable from new ones, they aren't
505 # really branchheads. Note checking parents is insufficient:
505 # really branchheads. Note checking parents is insufficient:
506 # 1 (branch a) -> 2 (branch b) -> 3 (branch a)
506 # 1 (branch a) -> 2 (branch b) -> 3 (branch a)
507 for branch, newnodes in newbranches.iteritems():
507 for branch, newnodes in newbranches.iteritems():
508 bheads = partial.setdefault(branch, [])
508 bheads = partial.setdefault(branch, [])
509 bheads.extend(newnodes)
509 bheads.extend(newnodes)
510 if len(bheads) <= 1:
510 if len(bheads) <= 1:
511 continue
511 continue
512 bheads = sorted(bheads, key=lambda x: self[x].rev())
512 bheads = sorted(bheads, key=lambda x: self[x].rev())
513 # starting from tip means fewer passes over reachable
513 # starting from tip means fewer passes over reachable
514 while newnodes:
514 while newnodes:
515 latest = newnodes.pop()
515 latest = newnodes.pop()
516 if latest not in bheads:
516 if latest not in bheads:
517 continue
517 continue
518 minbhrev = self[bheads[0]].node()
518 minbhrev = self[bheads[0]].node()
519 reachable = self.changelog.reachable(latest, minbhrev)
519 reachable = self.changelog.reachable(latest, minbhrev)
520 reachable.remove(latest)
520 reachable.remove(latest)
521 if reachable:
521 if reachable:
522 bheads = [b for b in bheads if b not in reachable]
522 bheads = [b for b in bheads if b not in reachable]
523 partial[branch] = bheads
523 partial[branch] = bheads
524
524
525 def lookup(self, key):
525 def lookup(self, key):
526 if isinstance(key, int):
526 if isinstance(key, int):
527 return self.changelog.node(key)
527 return self.changelog.node(key)
528 elif key == '.':
528 elif key == '.':
529 return self.dirstate.p1()
529 return self.dirstate.p1()
530 elif key == 'null':
530 elif key == 'null':
531 return nullid
531 return nullid
532 elif key == 'tip':
532 elif key == 'tip':
533 return self.changelog.tip()
533 return self.changelog.tip()
534 n = self.changelog._match(key)
534 n = self.changelog._match(key)
535 if n:
535 if n:
536 return n
536 return n
537 if key in self._bookmarks:
537 if key in self._bookmarks:
538 return self._bookmarks[key]
538 return self._bookmarks[key]
539 if key in self.tags():
539 if key in self.tags():
540 return self.tags()[key]
540 return self.tags()[key]
541 if key in self.branchtags():
541 if key in self.branchtags():
542 return self.branchtags()[key]
542 return self.branchtags()[key]
543 n = self.changelog._partialmatch(key)
543 n = self.changelog._partialmatch(key)
544 if n:
544 if n:
545 return n
545 return n
546
546
547 # can't find key, check if it might have come from damaged dirstate
547 # can't find key, check if it might have come from damaged dirstate
548 if key in self.dirstate.parents():
548 if key in self.dirstate.parents():
549 raise error.Abort(_("working directory has unknown parent '%s'!")
549 raise error.Abort(_("working directory has unknown parent '%s'!")
550 % short(key))
550 % short(key))
551 try:
551 try:
552 if len(key) == 20:
552 if len(key) == 20:
553 key = hex(key)
553 key = hex(key)
554 except TypeError:
554 except TypeError:
555 pass
555 pass
556 raise error.RepoLookupError(_("unknown revision '%s'") % key)
556 raise error.RepoLookupError(_("unknown revision '%s'") % key)
557
557
558 def lookupbranch(self, key, remote=None):
558 def lookupbranch(self, key, remote=None):
559 repo = remote or self
559 repo = remote or self
560 if key in repo.branchmap():
560 if key in repo.branchmap():
561 return key
561 return key
562
562
563 repo = (remote and remote.local()) and remote or self
563 repo = (remote and remote.local()) and remote or self
564 return repo[key].branch()
564 return repo[key].branch()
565
565
566 def known(self, nodes):
566 def known(self, nodes):
567 nm = self.changelog.nodemap
567 nm = self.changelog.nodemap
568 return [(n in nm) for n in nodes]
568 return [(n in nm) for n in nodes]
569
569
570 def local(self):
570 def local(self):
571 return True
571 return True
572
572
573 def join(self, f):
573 def join(self, f):
574 return os.path.join(self.path, f)
574 return os.path.join(self.path, f)
575
575
576 def wjoin(self, f):
576 def wjoin(self, f):
577 return os.path.join(self.root, f)
577 return os.path.join(self.root, f)
578
578
579 def file(self, f):
579 def file(self, f):
580 if f[0] == '/':
580 if f[0] == '/':
581 f = f[1:]
581 f = f[1:]
582 return filelog.filelog(self.sopener, f)
582 return filelog.filelog(self.sopener, f)
583
583
584 def changectx(self, changeid):
584 def changectx(self, changeid):
585 return self[changeid]
585 return self[changeid]
586
586
587 def parents(self, changeid=None):
587 def parents(self, changeid=None):
588 '''get list of changectxs for parents of changeid'''
588 '''get list of changectxs for parents of changeid'''
589 return self[changeid].parents()
589 return self[changeid].parents()
590
590
591 def filectx(self, path, changeid=None, fileid=None):
591 def filectx(self, path, changeid=None, fileid=None):
592 """changeid can be a changeset revision, node, or tag.
592 """changeid can be a changeset revision, node, or tag.
593 fileid can be a file revision or node."""
593 fileid can be a file revision or node."""
594 return context.filectx(self, path, changeid, fileid)
594 return context.filectx(self, path, changeid, fileid)
595
595
596 def getcwd(self):
596 def getcwd(self):
597 return self.dirstate.getcwd()
597 return self.dirstate.getcwd()
598
598
599 def pathto(self, f, cwd=None):
599 def pathto(self, f, cwd=None):
600 return self.dirstate.pathto(f, cwd)
600 return self.dirstate.pathto(f, cwd)
601
601
602 def wfile(self, f, mode='r'):
602 def wfile(self, f, mode='r'):
603 return self.wopener(f, mode)
603 return self.wopener(f, mode)
604
604
605 def _link(self, f):
605 def _link(self, f):
606 return os.path.islink(self.wjoin(f))
606 return os.path.islink(self.wjoin(f))
607
607
608 def _loadfilter(self, filter):
608 def _loadfilter(self, filter):
609 if filter not in self.filterpats:
609 if filter not in self.filterpats:
610 l = []
610 l = []
611 for pat, cmd in self.ui.configitems(filter):
611 for pat, cmd in self.ui.configitems(filter):
612 if cmd == '!':
612 if cmd == '!':
613 continue
613 continue
614 mf = matchmod.match(self.root, '', [pat])
614 mf = matchmod.match(self.root, '', [pat])
615 fn = None
615 fn = None
616 params = cmd
616 params = cmd
617 for name, filterfn in self._datafilters.iteritems():
617 for name, filterfn in self._datafilters.iteritems():
618 if cmd.startswith(name):
618 if cmd.startswith(name):
619 fn = filterfn
619 fn = filterfn
620 params = cmd[len(name):].lstrip()
620 params = cmd[len(name):].lstrip()
621 break
621 break
622 if not fn:
622 if not fn:
623 fn = lambda s, c, **kwargs: util.filter(s, c)
623 fn = lambda s, c, **kwargs: util.filter(s, c)
624 # Wrap old filters not supporting keyword arguments
624 # Wrap old filters not supporting keyword arguments
625 if not inspect.getargspec(fn)[2]:
625 if not inspect.getargspec(fn)[2]:
626 oldfn = fn
626 oldfn = fn
627 fn = lambda s, c, **kwargs: oldfn(s, c)
627 fn = lambda s, c, **kwargs: oldfn(s, c)
628 l.append((mf, fn, params))
628 l.append((mf, fn, params))
629 self.filterpats[filter] = l
629 self.filterpats[filter] = l
630 return self.filterpats[filter]
630 return self.filterpats[filter]
631
631
632 def _filter(self, filterpats, filename, data):
632 def _filter(self, filterpats, filename, data):
633 for mf, fn, cmd in filterpats:
633 for mf, fn, cmd in filterpats:
634 if mf(filename):
634 if mf(filename):
635 self.ui.debug("filtering %s through %s\n" % (filename, cmd))
635 self.ui.debug("filtering %s through %s\n" % (filename, cmd))
636 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
636 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
637 break
637 break
638
638
639 return data
639 return data
640
640
641 @propertycache
641 @propertycache
642 def _encodefilterpats(self):
642 def _encodefilterpats(self):
643 return self._loadfilter('encode')
643 return self._loadfilter('encode')
644
644
645 @propertycache
645 @propertycache
646 def _decodefilterpats(self):
646 def _decodefilterpats(self):
647 return self._loadfilter('decode')
647 return self._loadfilter('decode')
648
648
649 def adddatafilter(self, name, filter):
649 def adddatafilter(self, name, filter):
650 self._datafilters[name] = filter
650 self._datafilters[name] = filter
651
651
652 def wread(self, filename):
652 def wread(self, filename):
653 if self._link(filename):
653 if self._link(filename):
654 data = os.readlink(self.wjoin(filename))
654 data = os.readlink(self.wjoin(filename))
655 else:
655 else:
656 data = self.wopener(filename, 'r').read()
656 data = self.wopener(filename, 'r').read()
657 return self._filter(self._encodefilterpats, filename, data)
657 return self._filter(self._encodefilterpats, filename, data)
658
658
659 def wwrite(self, filename, data, flags):
659 def wwrite(self, filename, data, flags):
660 data = self._filter(self._decodefilterpats, filename, data)
660 data = self._filter(self._decodefilterpats, filename, data)
661 if 'l' in flags:
661 if 'l' in flags:
662 self.wopener.symlink(data, filename)
662 self.wopener.symlink(data, filename)
663 else:
663 else:
664 self.wopener(filename, 'w').write(data)
664 self.wopener(filename, 'w').write(data)
665 if 'x' in flags:
665 if 'x' in flags:
666 util.set_flags(self.wjoin(filename), False, True)
666 util.set_flags(self.wjoin(filename), False, True)
667
667
668 def wwritedata(self, filename, data):
668 def wwritedata(self, filename, data):
669 return self._filter(self._decodefilterpats, filename, data)
669 return self._filter(self._decodefilterpats, filename, data)
670
670
671 def transaction(self, desc):
671 def transaction(self, desc):
672 tr = self._transref and self._transref() or None
672 tr = self._transref and self._transref() or None
673 if tr and tr.running():
673 if tr and tr.running():
674 return tr.nest()
674 return tr.nest()
675
675
676 # abort here if the journal already exists
676 # abort here if the journal already exists
677 if os.path.exists(self.sjoin("journal")):
677 if os.path.exists(self.sjoin("journal")):
678 raise error.RepoError(
678 raise error.RepoError(
679 _("abandoned transaction found - run hg recover"))
679 _("abandoned transaction found - run hg recover"))
680
680
681 # save dirstate for rollback
681 # save dirstate for rollback
682 try:
682 try:
683 ds = self.opener("dirstate").read()
683 ds = self.opener("dirstate").read()
684 except IOError:
684 except IOError:
685 ds = ""
685 ds = ""
686 self.opener("journal.dirstate", "w").write(ds)
686 self.opener("journal.dirstate", "w").write(ds)
687 self.opener("journal.branch", "w").write(
687 self.opener("journal.branch", "w").write(
688 encoding.fromlocal(self.dirstate.branch()))
688 encoding.fromlocal(self.dirstate.branch()))
689 self.opener("journal.desc", "w").write("%d\n%s\n" % (len(self), desc))
689 self.opener("journal.desc", "w").write("%d\n%s\n" % (len(self), desc))
690
690
691 renames = [(self.sjoin("journal"), self.sjoin("undo")),
691 renames = [(self.sjoin("journal"), self.sjoin("undo")),
692 (self.join("journal.dirstate"), self.join("undo.dirstate")),
692 (self.join("journal.dirstate"), self.join("undo.dirstate")),
693 (self.join("journal.branch"), self.join("undo.branch")),
693 (self.join("journal.branch"), self.join("undo.branch")),
694 (self.join("journal.desc"), self.join("undo.desc"))]
694 (self.join("journal.desc"), self.join("undo.desc"))]
695 tr = transaction.transaction(self.ui.warn, self.sopener,
695 tr = transaction.transaction(self.ui.warn, self.sopener,
696 self.sjoin("journal"),
696 self.sjoin("journal"),
697 aftertrans(renames),
697 aftertrans(renames),
698 self.store.createmode)
698 self.store.createmode)
699 self._transref = weakref.ref(tr)
699 self._transref = weakref.ref(tr)
700 return tr
700 return tr
701
701
702 def recover(self):
702 def recover(self):
703 lock = self.lock()
703 lock = self.lock()
704 try:
704 try:
705 if os.path.exists(self.sjoin("journal")):
705 if os.path.exists(self.sjoin("journal")):
706 self.ui.status(_("rolling back interrupted transaction\n"))
706 self.ui.status(_("rolling back interrupted transaction\n"))
707 transaction.rollback(self.sopener, self.sjoin("journal"),
707 transaction.rollback(self.sopener, self.sjoin("journal"),
708 self.ui.warn)
708 self.ui.warn)
709 self.invalidate()
709 self.invalidate()
710 return True
710 return True
711 else:
711 else:
712 self.ui.warn(_("no interrupted transaction available\n"))
712 self.ui.warn(_("no interrupted transaction available\n"))
713 return False
713 return False
714 finally:
714 finally:
715 lock.release()
715 lock.release()
716
716
717 def rollback(self, dryrun=False):
717 def rollback(self, dryrun=False):
718 wlock = lock = None
718 wlock = lock = None
719 try:
719 try:
720 wlock = self.wlock()
720 wlock = self.wlock()
721 lock = self.lock()
721 lock = self.lock()
722 if os.path.exists(self.sjoin("undo")):
722 if os.path.exists(self.sjoin("undo")):
723 try:
723 try:
724 args = self.opener("undo.desc", "r").read().splitlines()
724 args = self.opener("undo.desc", "r").read().splitlines()
725 if len(args) >= 3 and self.ui.verbose:
725 if len(args) >= 3 and self.ui.verbose:
726 desc = _("repository tip rolled back to revision %s"
726 desc = _("repository tip rolled back to revision %s"
727 " (undo %s: %s)\n") % (
727 " (undo %s: %s)\n") % (
728 int(args[0]) - 1, args[1], args[2])
728 int(args[0]) - 1, args[1], args[2])
729 elif len(args) >= 2:
729 elif len(args) >= 2:
730 desc = _("repository tip rolled back to revision %s"
730 desc = _("repository tip rolled back to revision %s"
731 " (undo %s)\n") % (
731 " (undo %s)\n") % (
732 int(args[0]) - 1, args[1])
732 int(args[0]) - 1, args[1])
733 except IOError:
733 except IOError:
734 desc = _("rolling back unknown transaction\n")
734 desc = _("rolling back unknown transaction\n")
735 self.ui.status(desc)
735 self.ui.status(desc)
736 if dryrun:
736 if dryrun:
737 return
737 return
738 transaction.rollback(self.sopener, self.sjoin("undo"),
738 transaction.rollback(self.sopener, self.sjoin("undo"),
739 self.ui.warn)
739 self.ui.warn)
740 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
740 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
741 if os.path.exists(self.join('undo.bookmarks')):
741 if os.path.exists(self.join('undo.bookmarks')):
742 util.rename(self.join('undo.bookmarks'),
742 util.rename(self.join('undo.bookmarks'),
743 self.join('bookmarks'))
743 self.join('bookmarks'))
744 try:
744 try:
745 branch = self.opener("undo.branch").read()
745 branch = self.opener("undo.branch").read()
746 self.dirstate.setbranch(branch)
746 self.dirstate.setbranch(branch)
747 except IOError:
747 except IOError:
748 self.ui.warn(_("named branch could not be reset, "
748 self.ui.warn(_("named branch could not be reset, "
749 "current branch is still: %s\n")
749 "current branch is still: %s\n")
750 % self.dirstate.branch())
750 % self.dirstate.branch())
751 self.invalidate()
751 self.invalidate()
752 self.dirstate.invalidate()
752 self.dirstate.invalidate()
753 self.destroyed()
753 self.destroyed()
754 parents = tuple([p.rev() for p in self.parents()])
754 parents = tuple([p.rev() for p in self.parents()])
755 if len(parents) > 1:
755 if len(parents) > 1:
756 self.ui.status(_("working directory now based on "
756 self.ui.status(_("working directory now based on "
757 "revisions %d and %d\n") % parents)
757 "revisions %d and %d\n") % parents)
758 else:
758 else:
759 self.ui.status(_("working directory now based on "
759 self.ui.status(_("working directory now based on "
760 "revision %d\n") % parents)
760 "revision %d\n") % parents)
761 else:
761 else:
762 self.ui.warn(_("no rollback information available\n"))
762 self.ui.warn(_("no rollback information available\n"))
763 return 1
763 return 1
764 finally:
764 finally:
765 release(lock, wlock)
765 release(lock, wlock)
766
766
767 def invalidatecaches(self):
767 def invalidatecaches(self):
768 self._tags = None
768 self._tags = None
769 self._tagtypes = None
769 self._tagtypes = None
770 self.nodetagscache = None
770 self.nodetagscache = None
771 self._branchcache = None # in UTF-8
771 self._branchcache = None # in UTF-8
772 self._branchcachetip = None
772 self._branchcachetip = None
773
773
774 def invalidate(self):
774 def invalidate(self):
775 for a in ("changelog", "manifest", "_bookmarks", "_bookmarkcurrent"):
775 for a in ("changelog", "manifest", "_bookmarks", "_bookmarkcurrent"):
776 if a in self.__dict__:
776 if a in self.__dict__:
777 delattr(self, a)
777 delattr(self, a)
778 self.invalidatecaches()
778 self.invalidatecaches()
779
779
780 def _lock(self, lockname, wait, releasefn, acquirefn, desc):
780 def _lock(self, lockname, wait, releasefn, acquirefn, desc):
781 try:
781 try:
782 l = lock.lock(lockname, 0, releasefn, desc=desc)
782 l = lock.lock(lockname, 0, releasefn, desc=desc)
783 except error.LockHeld, inst:
783 except error.LockHeld, inst:
784 if not wait:
784 if not wait:
785 raise
785 raise
786 self.ui.warn(_("waiting for lock on %s held by %r\n") %
786 self.ui.warn(_("waiting for lock on %s held by %r\n") %
787 (desc, inst.locker))
787 (desc, inst.locker))
788 # default to 600 seconds timeout
788 # default to 600 seconds timeout
789 l = lock.lock(lockname, int(self.ui.config("ui", "timeout", "600")),
789 l = lock.lock(lockname, int(self.ui.config("ui", "timeout", "600")),
790 releasefn, desc=desc)
790 releasefn, desc=desc)
791 if acquirefn:
791 if acquirefn:
792 acquirefn()
792 acquirefn()
793 return l
793 return l
794
794
795 def lock(self, wait=True):
795 def lock(self, wait=True):
796 '''Lock the repository store (.hg/store) and return a weak reference
796 '''Lock the repository store (.hg/store) and return a weak reference
797 to the lock. Use this before modifying the store (e.g. committing or
797 to the lock. Use this before modifying the store (e.g. committing or
798 stripping). If you are opening a transaction, get a lock as well.)'''
798 stripping). If you are opening a transaction, get a lock as well.)'''
799 l = self._lockref and self._lockref()
799 l = self._lockref and self._lockref()
800 if l is not None and l.held:
800 if l is not None and l.held:
801 l.lock()
801 l.lock()
802 return l
802 return l
803
803
804 l = self._lock(self.sjoin("lock"), wait, self.store.write,
804 l = self._lock(self.sjoin("lock"), wait, self.store.write,
805 self.invalidate, _('repository %s') % self.origroot)
805 self.invalidate, _('repository %s') % self.origroot)
806 self._lockref = weakref.ref(l)
806 self._lockref = weakref.ref(l)
807 return l
807 return l
808
808
809 def wlock(self, wait=True):
809 def wlock(self, wait=True):
810 '''Lock the non-store parts of the repository (everything under
810 '''Lock the non-store parts of the repository (everything under
811 .hg except .hg/store) and return a weak reference to the lock.
811 .hg except .hg/store) and return a weak reference to the lock.
812 Use this before modifying files in .hg.'''
812 Use this before modifying files in .hg.'''
813 l = self._wlockref and self._wlockref()
813 l = self._wlockref and self._wlockref()
814 if l is not None and l.held:
814 if l is not None and l.held:
815 l.lock()
815 l.lock()
816 return l
816 return l
817
817
818 l = self._lock(self.join("wlock"), wait, self.dirstate.write,
818 l = self._lock(self.join("wlock"), wait, self.dirstate.write,
819 self.dirstate.invalidate, _('working directory of %s') %
819 self.dirstate.invalidate, _('working directory of %s') %
820 self.origroot)
820 self.origroot)
821 self._wlockref = weakref.ref(l)
821 self._wlockref = weakref.ref(l)
822 return l
822 return l
823
823
824 def _filecommit(self, fctx, manifest1, manifest2, linkrev, tr, changelist):
824 def _filecommit(self, fctx, manifest1, manifest2, linkrev, tr, changelist):
825 """
825 """
826 commit an individual file as part of a larger transaction
826 commit an individual file as part of a larger transaction
827 """
827 """
828
828
829 fname = fctx.path()
829 fname = fctx.path()
830 text = fctx.data()
830 text = fctx.data()
831 flog = self.file(fname)
831 flog = self.file(fname)
832 fparent1 = manifest1.get(fname, nullid)
832 fparent1 = manifest1.get(fname, nullid)
833 fparent2 = fparent2o = manifest2.get(fname, nullid)
833 fparent2 = fparent2o = manifest2.get(fname, nullid)
834
834
835 meta = {}
835 meta = {}
836 copy = fctx.renamed()
836 copy = fctx.renamed()
837 if copy and copy[0] != fname:
837 if copy and copy[0] != fname:
838 # Mark the new revision of this file as a copy of another
838 # Mark the new revision of this file as a copy of another
839 # file. This copy data will effectively act as a parent
839 # file. This copy data will effectively act as a parent
840 # of this new revision. If this is a merge, the first
840 # of this new revision. If this is a merge, the first
841 # parent will be the nullid (meaning "look up the copy data")
841 # parent will be the nullid (meaning "look up the copy data")
842 # and the second one will be the other parent. For example:
842 # and the second one will be the other parent. For example:
843 #
843 #
844 # 0 --- 1 --- 3 rev1 changes file foo
844 # 0 --- 1 --- 3 rev1 changes file foo
845 # \ / rev2 renames foo to bar and changes it
845 # \ / rev2 renames foo to bar and changes it
846 # \- 2 -/ rev3 should have bar with all changes and
846 # \- 2 -/ rev3 should have bar with all changes and
847 # should record that bar descends from
847 # should record that bar descends from
848 # bar in rev2 and foo in rev1
848 # bar in rev2 and foo in rev1
849 #
849 #
850 # this allows this merge to succeed:
850 # this allows this merge to succeed:
851 #
851 #
852 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
852 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
853 # \ / merging rev3 and rev4 should use bar@rev2
853 # \ / merging rev3 and rev4 should use bar@rev2
854 # \- 2 --- 4 as the merge base
854 # \- 2 --- 4 as the merge base
855 #
855 #
856
856
857 cfname = copy[0]
857 cfname = copy[0]
858 crev = manifest1.get(cfname)
858 crev = manifest1.get(cfname)
859 newfparent = fparent2
859 newfparent = fparent2
860
860
861 if manifest2: # branch merge
861 if manifest2: # branch merge
862 if fparent2 == nullid or crev is None: # copied on remote side
862 if fparent2 == nullid or crev is None: # copied on remote side
863 if cfname in manifest2:
863 if cfname in manifest2:
864 crev = manifest2[cfname]
864 crev = manifest2[cfname]
865 newfparent = fparent1
865 newfparent = fparent1
866
866
867 # find source in nearest ancestor if we've lost track
867 # find source in nearest ancestor if we've lost track
868 if not crev:
868 if not crev:
869 self.ui.debug(" %s: searching for copy revision for %s\n" %
869 self.ui.debug(" %s: searching for copy revision for %s\n" %
870 (fname, cfname))
870 (fname, cfname))
871 for ancestor in self[None].ancestors():
871 for ancestor in self[None].ancestors():
872 if cfname in ancestor:
872 if cfname in ancestor:
873 crev = ancestor[cfname].filenode()
873 crev = ancestor[cfname].filenode()
874 break
874 break
875
875
876 if crev:
876 if crev:
877 self.ui.debug(" %s: copy %s:%s\n" % (fname, cfname, hex(crev)))
877 self.ui.debug(" %s: copy %s:%s\n" % (fname, cfname, hex(crev)))
878 meta["copy"] = cfname
878 meta["copy"] = cfname
879 meta["copyrev"] = hex(crev)
879 meta["copyrev"] = hex(crev)
880 fparent1, fparent2 = nullid, newfparent
880 fparent1, fparent2 = nullid, newfparent
881 else:
881 else:
882 self.ui.warn(_("warning: can't find ancestor for '%s' "
882 self.ui.warn(_("warning: can't find ancestor for '%s' "
883 "copied from '%s'!\n") % (fname, cfname))
883 "copied from '%s'!\n") % (fname, cfname))
884
884
885 elif fparent2 != nullid:
885 elif fparent2 != nullid:
886 # is one parent an ancestor of the other?
886 # is one parent an ancestor of the other?
887 fparentancestor = flog.ancestor(fparent1, fparent2)
887 fparentancestor = flog.ancestor(fparent1, fparent2)
888 if fparentancestor == fparent1:
888 if fparentancestor == fparent1:
889 fparent1, fparent2 = fparent2, nullid
889 fparent1, fparent2 = fparent2, nullid
890 elif fparentancestor == fparent2:
890 elif fparentancestor == fparent2:
891 fparent2 = nullid
891 fparent2 = nullid
892
892
893 # is the file changed?
893 # is the file changed?
894 if fparent2 != nullid or flog.cmp(fparent1, text) or meta:
894 if fparent2 != nullid or flog.cmp(fparent1, text) or meta:
895 changelist.append(fname)
895 changelist.append(fname)
896 return flog.add(text, meta, tr, linkrev, fparent1, fparent2)
896 return flog.add(text, meta, tr, linkrev, fparent1, fparent2)
897
897
898 # are just the flags changed during merge?
898 # are just the flags changed during merge?
899 if fparent1 != fparent2o and manifest1.flags(fname) != fctx.flags():
899 if fparent1 != fparent2o and manifest1.flags(fname) != fctx.flags():
900 changelist.append(fname)
900 changelist.append(fname)
901
901
902 return fparent1
902 return fparent1
903
903
904 def commit(self, text="", user=None, date=None, match=None, force=False,
904 def commit(self, text="", user=None, date=None, match=None, force=False,
905 editor=False, extra={}):
905 editor=False, extra={}):
906 """Add a new revision to current repository.
906 """Add a new revision to current repository.
907
907
908 Revision information is gathered from the working directory,
908 Revision information is gathered from the working directory,
909 match can be used to filter the committed files. If editor is
909 match can be used to filter the committed files. If editor is
910 supplied, it is called to get a commit message.
910 supplied, it is called to get a commit message.
911 """
911 """
912
912
913 def fail(f, msg):
913 def fail(f, msg):
914 raise util.Abort('%s: %s' % (f, msg))
914 raise util.Abort('%s: %s' % (f, msg))
915
915
916 if not match:
916 if not match:
917 match = matchmod.always(self.root, '')
917 match = matchmod.always(self.root, '')
918
918
919 if not force:
919 if not force:
920 vdirs = []
920 vdirs = []
921 match.dir = vdirs.append
921 match.dir = vdirs.append
922 match.bad = fail
922 match.bad = fail
923
923
924 wlock = self.wlock()
924 wlock = self.wlock()
925 try:
925 try:
926 wctx = self[None]
926 wctx = self[None]
927 merge = len(wctx.parents()) > 1
927 merge = len(wctx.parents()) > 1
928
928
929 if (not force and merge and match and
929 if (not force and merge and match and
930 (match.files() or match.anypats())):
930 (match.files() or match.anypats())):
931 raise util.Abort(_('cannot partially commit a merge '
931 raise util.Abort(_('cannot partially commit a merge '
932 '(do not specify files or patterns)'))
932 '(do not specify files or patterns)'))
933
933
934 changes = self.status(match=match, clean=force)
934 changes = self.status(match=match, clean=force)
935 if force:
935 if force:
936 changes[0].extend(changes[6]) # mq may commit unchanged files
936 changes[0].extend(changes[6]) # mq may commit unchanged files
937
937
938 # check subrepos
938 # check subrepos
939 subs = []
939 subs = []
940 removedsubs = set()
940 removedsubs = set()
941 for p in wctx.parents():
941 for p in wctx.parents():
942 removedsubs.update(s for s in p.substate if match(s))
942 removedsubs.update(s for s in p.substate if match(s))
943 for s in wctx.substate:
943 for s in wctx.substate:
944 removedsubs.discard(s)
944 removedsubs.discard(s)
945 if match(s) and wctx.sub(s).dirty():
945 if match(s) and wctx.sub(s).dirty():
946 subs.append(s)
946 subs.append(s)
947 if (subs or removedsubs):
947 if (subs or removedsubs):
948 if (not match('.hgsub') and
948 if (not match('.hgsub') and
949 '.hgsub' in (wctx.modified() + wctx.added())):
949 '.hgsub' in (wctx.modified() + wctx.added())):
950 raise util.Abort(_("can't commit subrepos without .hgsub"))
950 raise util.Abort(_("can't commit subrepos without .hgsub"))
951 if '.hgsubstate' not in changes[0]:
951 if '.hgsubstate' not in changes[0]:
952 changes[0].insert(0, '.hgsubstate')
952 changes[0].insert(0, '.hgsubstate')
953
953
954 if subs and not self.ui.configbool('ui', 'commitsubrepos', True):
954 if subs and not self.ui.configbool('ui', 'commitsubrepos', True):
955 changedsubs = [s for s in subs if wctx.sub(s).dirty(True)]
955 changedsubs = [s for s in subs if wctx.sub(s).dirty(True)]
956 if changedsubs:
956 if changedsubs:
957 raise util.Abort(_("uncommitted changes in subrepo %s")
957 raise util.Abort(_("uncommitted changes in subrepo %s")
958 % changedsubs[0])
958 % changedsubs[0])
959
959
960 # make sure all explicit patterns are matched
960 # make sure all explicit patterns are matched
961 if not force and match.files():
961 if not force and match.files():
962 matched = set(changes[0] + changes[1] + changes[2])
962 matched = set(changes[0] + changes[1] + changes[2])
963
963
964 for f in match.files():
964 for f in match.files():
965 if f == '.' or f in matched or f in wctx.substate:
965 if f == '.' or f in matched or f in wctx.substate:
966 continue
966 continue
967 if f in changes[3]: # missing
967 if f in changes[3]: # missing
968 fail(f, _('file not found!'))
968 fail(f, _('file not found!'))
969 if f in vdirs: # visited directory
969 if f in vdirs: # visited directory
970 d = f + '/'
970 d = f + '/'
971 for mf in matched:
971 for mf in matched:
972 if mf.startswith(d):
972 if mf.startswith(d):
973 break
973 break
974 else:
974 else:
975 fail(f, _("no match under directory!"))
975 fail(f, _("no match under directory!"))
976 elif f not in self.dirstate:
976 elif f not in self.dirstate:
977 fail(f, _("file not tracked!"))
977 fail(f, _("file not tracked!"))
978
978
979 if (not force and not extra.get("close") and not merge
979 if (not force and not extra.get("close") and not merge
980 and not (changes[0] or changes[1] or changes[2])
980 and not (changes[0] or changes[1] or changes[2])
981 and wctx.branch() == wctx.p1().branch()):
981 and wctx.branch() == wctx.p1().branch()):
982 return None
982 return None
983
983
984 ms = mergemod.mergestate(self)
984 ms = mergemod.mergestate(self)
985 for f in changes[0]:
985 for f in changes[0]:
986 if f in ms and ms[f] == 'u':
986 if f in ms and ms[f] == 'u':
987 raise util.Abort(_("unresolved merge conflicts "
987 raise util.Abort(_("unresolved merge conflicts "
988 "(see hg help resolve)"))
988 "(see hg help resolve)"))
989
989
990 cctx = context.workingctx(self, text, user, date, extra, changes)
990 cctx = context.workingctx(self, text, user, date, extra, changes)
991 if editor:
991 if editor:
992 cctx._text = editor(self, cctx, subs)
992 cctx._text = editor(self, cctx, subs)
993 edited = (text != cctx._text)
993 edited = (text != cctx._text)
994
994
995 # commit subs
995 # commit subs
996 if subs or removedsubs:
996 if subs or removedsubs:
997 state = wctx.substate.copy()
997 state = wctx.substate.copy()
998 for s in sorted(subs):
998 for s in sorted(subs):
999 sub = wctx.sub(s)
999 sub = wctx.sub(s)
1000 self.ui.status(_('committing subrepository %s\n') %
1000 self.ui.status(_('committing subrepository %s\n') %
1001 subrepo.subrelpath(sub))
1001 subrepo.subrelpath(sub))
1002 sr = sub.commit(cctx._text, user, date)
1002 sr = sub.commit(cctx._text, user, date)
1003 state[s] = (state[s][0], sr)
1003 state[s] = (state[s][0], sr)
1004 subrepo.writestate(self, state)
1004 subrepo.writestate(self, state)
1005
1005
1006 # Save commit message in case this transaction gets rolled back
1006 # Save commit message in case this transaction gets rolled back
1007 # (e.g. by a pretxncommit hook). Leave the content alone on
1007 # (e.g. by a pretxncommit hook). Leave the content alone on
1008 # the assumption that the user will use the same editor again.
1008 # the assumption that the user will use the same editor again.
1009 msgfile = self.opener('last-message.txt', 'wb')
1009 msgfile = self.opener('last-message.txt', 'wb')
1010 msgfile.write(cctx._text)
1010 msgfile.write(cctx._text)
1011 msgfile.close()
1011 msgfile.close()
1012
1012
1013 p1, p2 = self.dirstate.parents()
1013 p1, p2 = self.dirstate.parents()
1014 hookp1, hookp2 = hex(p1), (p2 != nullid and hex(p2) or '')
1014 hookp1, hookp2 = hex(p1), (p2 != nullid and hex(p2) or '')
1015 try:
1015 try:
1016 self.hook("precommit", throw=True, parent1=hookp1, parent2=hookp2)
1016 self.hook("precommit", throw=True, parent1=hookp1, parent2=hookp2)
1017 ret = self.commitctx(cctx, True)
1017 ret = self.commitctx(cctx, True)
1018 except:
1018 except:
1019 if edited:
1019 if edited:
1020 msgfn = self.pathto(msgfile.name[len(self.root)+1:])
1020 msgfn = self.pathto(msgfile.name[len(self.root)+1:])
1021 self.ui.write(
1021 self.ui.write(
1022 _('note: commit message saved in %s\n') % msgfn)
1022 _('note: commit message saved in %s\n') % msgfn)
1023 raise
1023 raise
1024
1024
1025 # update bookmarks, dirstate and mergestate
1025 # update bookmarks, dirstate and mergestate
1026 bookmarks.update(self, p1, ret)
1026 bookmarks.update(self, p1, ret)
1027 for f in changes[0] + changes[1]:
1027 for f in changes[0] + changes[1]:
1028 self.dirstate.normal(f)
1028 self.dirstate.normal(f)
1029 for f in changes[2]:
1029 for f in changes[2]:
1030 self.dirstate.forget(f)
1030 self.dirstate.forget(f)
1031 self.dirstate.setparents(ret)
1031 self.dirstate.setparents(ret)
1032 ms.reset()
1032 ms.reset()
1033 finally:
1033 finally:
1034 wlock.release()
1034 wlock.release()
1035
1035
1036 self.hook("commit", node=hex(ret), parent1=hookp1, parent2=hookp2)
1036 self.hook("commit", node=hex(ret), parent1=hookp1, parent2=hookp2)
1037 return ret
1037 return ret
1038
1038
1039 def commitctx(self, ctx, error=False):
1039 def commitctx(self, ctx, error=False):
1040 """Add a new revision to current repository.
1040 """Add a new revision to current repository.
1041 Revision information is passed via the context argument.
1041 Revision information is passed via the context argument.
1042 """
1042 """
1043
1043
1044 tr = lock = None
1044 tr = lock = None
1045 removed = list(ctx.removed())
1045 removed = list(ctx.removed())
1046 p1, p2 = ctx.p1(), ctx.p2()
1046 p1, p2 = ctx.p1(), ctx.p2()
1047 m1 = p1.manifest().copy()
1047 m1 = p1.manifest().copy()
1048 m2 = p2.manifest()
1048 m2 = p2.manifest()
1049 user = ctx.user()
1049 user = ctx.user()
1050
1050
1051 lock = self.lock()
1051 lock = self.lock()
1052 try:
1052 try:
1053 tr = self.transaction("commit")
1053 tr = self.transaction("commit")
1054 trp = weakref.proxy(tr)
1054 trp = weakref.proxy(tr)
1055
1055
1056 # check in files
1056 # check in files
1057 new = {}
1057 new = {}
1058 changed = []
1058 changed = []
1059 linkrev = len(self)
1059 linkrev = len(self)
1060 for f in sorted(ctx.modified() + ctx.added()):
1060 for f in sorted(ctx.modified() + ctx.added()):
1061 self.ui.note(f + "\n")
1061 self.ui.note(f + "\n")
1062 try:
1062 try:
1063 fctx = ctx[f]
1063 fctx = ctx[f]
1064 new[f] = self._filecommit(fctx, m1, m2, linkrev, trp,
1064 new[f] = self._filecommit(fctx, m1, m2, linkrev, trp,
1065 changed)
1065 changed)
1066 m1.set(f, fctx.flags())
1066 m1.set(f, fctx.flags())
1067 except OSError, inst:
1067 except OSError, inst:
1068 self.ui.warn(_("trouble committing %s!\n") % f)
1068 self.ui.warn(_("trouble committing %s!\n") % f)
1069 raise
1069 raise
1070 except IOError, inst:
1070 except IOError, inst:
1071 errcode = getattr(inst, 'errno', errno.ENOENT)
1071 errcode = getattr(inst, 'errno', errno.ENOENT)
1072 if error or errcode and errcode != errno.ENOENT:
1072 if error or errcode and errcode != errno.ENOENT:
1073 self.ui.warn(_("trouble committing %s!\n") % f)
1073 self.ui.warn(_("trouble committing %s!\n") % f)
1074 raise
1074 raise
1075 else:
1075 else:
1076 removed.append(f)
1076 removed.append(f)
1077
1077
1078 # update manifest
1078 # update manifest
1079 m1.update(new)
1079 m1.update(new)
1080 removed = [f for f in sorted(removed) if f in m1 or f in m2]
1080 removed = [f for f in sorted(removed) if f in m1 or f in m2]
1081 drop = [f for f in removed if f in m1]
1081 drop = [f for f in removed if f in m1]
1082 for f in drop:
1082 for f in drop:
1083 del m1[f]
1083 del m1[f]
1084 mn = self.manifest.add(m1, trp, linkrev, p1.manifestnode(),
1084 mn = self.manifest.add(m1, trp, linkrev, p1.manifestnode(),
1085 p2.manifestnode(), (new, drop))
1085 p2.manifestnode(), (new, drop))
1086
1086
1087 # update changelog
1087 # update changelog
1088 self.changelog.delayupdate()
1088 self.changelog.delayupdate()
1089 n = self.changelog.add(mn, changed + removed, ctx.description(),
1089 n = self.changelog.add(mn, changed + removed, ctx.description(),
1090 trp, p1.node(), p2.node(),
1090 trp, p1.node(), p2.node(),
1091 user, ctx.date(), ctx.extra().copy())
1091 user, ctx.date(), ctx.extra().copy())
1092 p = lambda: self.changelog.writepending() and self.root or ""
1092 p = lambda: self.changelog.writepending() and self.root or ""
1093 xp1, xp2 = p1.hex(), p2 and p2.hex() or ''
1093 xp1, xp2 = p1.hex(), p2 and p2.hex() or ''
1094 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
1094 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
1095 parent2=xp2, pending=p)
1095 parent2=xp2, pending=p)
1096 self.changelog.finalize(trp)
1096 self.changelog.finalize(trp)
1097 tr.close()
1097 tr.close()
1098
1098
1099 if self._branchcache:
1099 if self._branchcache:
1100 self.updatebranchcache()
1100 self.updatebranchcache()
1101 return n
1101 return n
1102 finally:
1102 finally:
1103 if tr:
1103 if tr:
1104 tr.release()
1104 tr.release()
1105 lock.release()
1105 lock.release()
1106
1106
1107 def destroyed(self):
1107 def destroyed(self):
1108 '''Inform the repository that nodes have been destroyed.
1108 '''Inform the repository that nodes have been destroyed.
1109 Intended for use by strip and rollback, so there's a common
1109 Intended for use by strip and rollback, so there's a common
1110 place for anything that has to be done after destroying history.'''
1110 place for anything that has to be done after destroying history.'''
1111 # XXX it might be nice if we could take the list of destroyed
1111 # XXX it might be nice if we could take the list of destroyed
1112 # nodes, but I don't see an easy way for rollback() to do that
1112 # nodes, but I don't see an easy way for rollback() to do that
1113
1113
1114 # Ensure the persistent tag cache is updated. Doing it now
1114 # Ensure the persistent tag cache is updated. Doing it now
1115 # means that the tag cache only has to worry about destroyed
1115 # means that the tag cache only has to worry about destroyed
1116 # heads immediately after a strip/rollback. That in turn
1116 # heads immediately after a strip/rollback. That in turn
1117 # guarantees that "cachetip == currenttip" (comparing both rev
1117 # guarantees that "cachetip == currenttip" (comparing both rev
1118 # and node) always means no nodes have been added or destroyed.
1118 # and node) always means no nodes have been added or destroyed.
1119
1119
1120 # XXX this is suboptimal when qrefresh'ing: we strip the current
1120 # XXX this is suboptimal when qrefresh'ing: we strip the current
1121 # head, refresh the tag cache, then immediately add a new head.
1121 # head, refresh the tag cache, then immediately add a new head.
1122 # But I think doing it this way is necessary for the "instant
1122 # But I think doing it this way is necessary for the "instant
1123 # tag cache retrieval" case to work.
1123 # tag cache retrieval" case to work.
1124 self.invalidatecaches()
1124 self.invalidatecaches()
1125
1125
1126 def walk(self, match, node=None):
1126 def walk(self, match, node=None):
1127 '''
1127 '''
1128 walk recursively through the directory tree or a given
1128 walk recursively through the directory tree or a given
1129 changeset, finding all files matched by the match
1129 changeset, finding all files matched by the match
1130 function
1130 function
1131 '''
1131 '''
1132 return self[node].walk(match)
1132 return self[node].walk(match)
1133
1133
1134 def status(self, node1='.', node2=None, match=None,
1134 def status(self, node1='.', node2=None, match=None,
1135 ignored=False, clean=False, unknown=False,
1135 ignored=False, clean=False, unknown=False,
1136 listsubrepos=False):
1136 listsubrepos=False):
1137 """return status of files between two nodes or node and working directory
1137 """return status of files between two nodes or node and working directory
1138
1138
1139 If node1 is None, use the first dirstate parent instead.
1139 If node1 is None, use the first dirstate parent instead.
1140 If node2 is None, compare node1 with working directory.
1140 If node2 is None, compare node1 with working directory.
1141 """
1141 """
1142
1142
1143 def mfmatches(ctx):
1143 def mfmatches(ctx):
1144 mf = ctx.manifest().copy()
1144 mf = ctx.manifest().copy()
1145 for fn in mf.keys():
1145 for fn in mf.keys():
1146 if not match(fn):
1146 if not match(fn):
1147 del mf[fn]
1147 del mf[fn]
1148 return mf
1148 return mf
1149
1149
1150 if isinstance(node1, context.changectx):
1150 if isinstance(node1, context.changectx):
1151 ctx1 = node1
1151 ctx1 = node1
1152 else:
1152 else:
1153 ctx1 = self[node1]
1153 ctx1 = self[node1]
1154 if isinstance(node2, context.changectx):
1154 if isinstance(node2, context.changectx):
1155 ctx2 = node2
1155 ctx2 = node2
1156 else:
1156 else:
1157 ctx2 = self[node2]
1157 ctx2 = self[node2]
1158
1158
1159 working = ctx2.rev() is None
1159 working = ctx2.rev() is None
1160 parentworking = working and ctx1 == self['.']
1160 parentworking = working and ctx1 == self['.']
1161 match = match or matchmod.always(self.root, self.getcwd())
1161 match = match or matchmod.always(self.root, self.getcwd())
1162 listignored, listclean, listunknown = ignored, clean, unknown
1162 listignored, listclean, listunknown = ignored, clean, unknown
1163
1163
1164 # load earliest manifest first for caching reasons
1164 # load earliest manifest first for caching reasons
1165 if not working and ctx2.rev() < ctx1.rev():
1165 if not working and ctx2.rev() < ctx1.rev():
1166 ctx2.manifest()
1166 ctx2.manifest()
1167
1167
1168 if not parentworking:
1168 if not parentworking:
1169 def bad(f, msg):
1169 def bad(f, msg):
1170 if f not in ctx1:
1170 if f not in ctx1:
1171 self.ui.warn('%s: %s\n' % (self.dirstate.pathto(f), msg))
1171 self.ui.warn('%s: %s\n' % (self.dirstate.pathto(f), msg))
1172 match.bad = bad
1172 match.bad = bad
1173
1173
1174 if working: # we need to scan the working dir
1174 if working: # we need to scan the working dir
1175 subrepos = []
1175 subrepos = []
1176 if '.hgsub' in self.dirstate:
1176 if '.hgsub' in self.dirstate:
1177 subrepos = ctx1.substate.keys()
1177 subrepos = ctx1.substate.keys()
1178 s = self.dirstate.status(match, subrepos, listignored,
1178 s = self.dirstate.status(match, subrepos, listignored,
1179 listclean, listunknown)
1179 listclean, listunknown)
1180 cmp, modified, added, removed, deleted, unknown, ignored, clean = s
1180 cmp, modified, added, removed, deleted, unknown, ignored, clean = s
1181
1181
1182 # check for any possibly clean files
1182 # check for any possibly clean files
1183 if parentworking and cmp:
1183 if parentworking and cmp:
1184 fixup = []
1184 fixup = []
1185 # do a full compare of any files that might have changed
1185 # do a full compare of any files that might have changed
1186 for f in sorted(cmp):
1186 for f in sorted(cmp):
1187 if (f not in ctx1 or ctx2.flags(f) != ctx1.flags(f)
1187 if (f not in ctx1 or ctx2.flags(f) != ctx1.flags(f)
1188 or ctx1[f].cmp(ctx2[f])):
1188 or ctx1[f].cmp(ctx2[f])):
1189 modified.append(f)
1189 modified.append(f)
1190 else:
1190 else:
1191 fixup.append(f)
1191 fixup.append(f)
1192
1192
1193 # update dirstate for files that are actually clean
1193 # update dirstate for files that are actually clean
1194 if fixup:
1194 if fixup:
1195 if listclean:
1195 if listclean:
1196 clean += fixup
1196 clean += fixup
1197
1197
1198 try:
1198 try:
1199 # updating the dirstate is optional
1199 # updating the dirstate is optional
1200 # so we don't wait on the lock
1200 # so we don't wait on the lock
1201 wlock = self.wlock(False)
1201 wlock = self.wlock(False)
1202 try:
1202 try:
1203 for f in fixup:
1203 for f in fixup:
1204 self.dirstate.normal(f)
1204 self.dirstate.normal(f)
1205 finally:
1205 finally:
1206 wlock.release()
1206 wlock.release()
1207 except error.LockError:
1207 except error.LockError:
1208 pass
1208 pass
1209
1209
1210 if not parentworking:
1210 if not parentworking:
1211 mf1 = mfmatches(ctx1)
1211 mf1 = mfmatches(ctx1)
1212 if working:
1212 if working:
1213 # we are comparing working dir against non-parent
1213 # we are comparing working dir against non-parent
1214 # generate a pseudo-manifest for the working dir
1214 # generate a pseudo-manifest for the working dir
1215 mf2 = mfmatches(self['.'])
1215 mf2 = mfmatches(self['.'])
1216 for f in cmp + modified + added:
1216 for f in cmp + modified + added:
1217 mf2[f] = None
1217 mf2[f] = None
1218 mf2.set(f, ctx2.flags(f))
1218 mf2.set(f, ctx2.flags(f))
1219 for f in removed:
1219 for f in removed:
1220 if f in mf2:
1220 if f in mf2:
1221 del mf2[f]
1221 del mf2[f]
1222 else:
1222 else:
1223 # we are comparing two revisions
1223 # we are comparing two revisions
1224 deleted, unknown, ignored = [], [], []
1224 deleted, unknown, ignored = [], [], []
1225 mf2 = mfmatches(ctx2)
1225 mf2 = mfmatches(ctx2)
1226
1226
1227 modified, added, clean = [], [], []
1227 modified, added, clean = [], [], []
1228 for fn in mf2:
1228 for fn in mf2:
1229 if fn in mf1:
1229 if fn in mf1:
1230 if (fn not in deleted and
1230 if (fn not in deleted and
1231 (mf1.flags(fn) != mf2.flags(fn) or
1231 (mf1.flags(fn) != mf2.flags(fn) or
1232 (mf1[fn] != mf2[fn] and
1232 (mf1[fn] != mf2[fn] and
1233 (mf2[fn] or ctx1[fn].cmp(ctx2[fn]))))):
1233 (mf2[fn] or ctx1[fn].cmp(ctx2[fn]))))):
1234 modified.append(fn)
1234 modified.append(fn)
1235 elif listclean:
1235 elif listclean:
1236 clean.append(fn)
1236 clean.append(fn)
1237 del mf1[fn]
1237 del mf1[fn]
1238 elif fn not in deleted:
1238 elif fn not in deleted:
1239 added.append(fn)
1239 added.append(fn)
1240 removed = mf1.keys()
1240 removed = mf1.keys()
1241
1241
1242 r = modified, added, removed, deleted, unknown, ignored, clean
1242 r = modified, added, removed, deleted, unknown, ignored, clean
1243
1243
1244 if listsubrepos:
1244 if listsubrepos:
1245 for subpath, sub in subrepo.itersubrepos(ctx1, ctx2):
1245 for subpath, sub in subrepo.itersubrepos(ctx1, ctx2):
1246 if working:
1246 if working:
1247 rev2 = None
1247 rev2 = None
1248 else:
1248 else:
1249 rev2 = ctx2.substate[subpath][1]
1249 rev2 = ctx2.substate[subpath][1]
1250 try:
1250 try:
1251 submatch = matchmod.narrowmatcher(subpath, match)
1251 submatch = matchmod.narrowmatcher(subpath, match)
1252 s = sub.status(rev2, match=submatch, ignored=listignored,
1252 s = sub.status(rev2, match=submatch, ignored=listignored,
1253 clean=listclean, unknown=listunknown,
1253 clean=listclean, unknown=listunknown,
1254 listsubrepos=True)
1254 listsubrepos=True)
1255 for rfiles, sfiles in zip(r, s):
1255 for rfiles, sfiles in zip(r, s):
1256 rfiles.extend("%s/%s" % (subpath, f) for f in sfiles)
1256 rfiles.extend("%s/%s" % (subpath, f) for f in sfiles)
1257 except error.LookupError:
1257 except error.LookupError:
1258 self.ui.status(_("skipping missing subrepository: %s\n")
1258 self.ui.status(_("skipping missing subrepository: %s\n")
1259 % subpath)
1259 % subpath)
1260
1260
1261 for l in r:
1261 for l in r:
1262 l.sort()
1262 l.sort()
1263 return r
1263 return r
1264
1264
1265 def heads(self, start=None):
1265 def heads(self, start=None):
1266 heads = self.changelog.heads(start)
1266 heads = self.changelog.heads(start)
1267 # sort the output in rev descending order
1267 # sort the output in rev descending order
1268 return sorted(heads, key=self.changelog.rev, reverse=True)
1268 return sorted(heads, key=self.changelog.rev, reverse=True)
1269
1269
1270 def branchheads(self, branch=None, start=None, closed=False):
1270 def branchheads(self, branch=None, start=None, closed=False):
1271 '''return a (possibly filtered) list of heads for the given branch
1271 '''return a (possibly filtered) list of heads for the given branch
1272
1272
1273 Heads are returned in topological order, from newest to oldest.
1273 Heads are returned in topological order, from newest to oldest.
1274 If branch is None, use the dirstate branch.
1274 If branch is None, use the dirstate branch.
1275 If start is not None, return only heads reachable from start.
1275 If start is not None, return only heads reachable from start.
1276 If closed is True, return heads that are marked as closed as well.
1276 If closed is True, return heads that are marked as closed as well.
1277 '''
1277 '''
1278 if branch is None:
1278 if branch is None:
1279 branch = self[None].branch()
1279 branch = self[None].branch()
1280 branches = self.branchmap()
1280 branches = self.branchmap()
1281 if branch not in branches:
1281 if branch not in branches:
1282 return []
1282 return []
1283 # the cache returns heads ordered lowest to highest
1283 # the cache returns heads ordered lowest to highest
1284 bheads = list(reversed(branches[branch]))
1284 bheads = list(reversed(branches[branch]))
1285 if start is not None:
1285 if start is not None:
1286 # filter out the heads that cannot be reached from startrev
1286 # filter out the heads that cannot be reached from startrev
1287 fbheads = set(self.changelog.nodesbetween([start], bheads)[2])
1287 fbheads = set(self.changelog.nodesbetween([start], bheads)[2])
1288 bheads = [h for h in bheads if h in fbheads]
1288 bheads = [h for h in bheads if h in fbheads]
1289 if not closed:
1289 if not closed:
1290 bheads = [h for h in bheads if
1290 bheads = [h for h in bheads if
1291 ('close' not in self.changelog.read(h)[5])]
1291 ('close' not in self.changelog.read(h)[5])]
1292 return bheads
1292 return bheads
1293
1293
1294 def branches(self, nodes):
1294 def branches(self, nodes):
1295 if not nodes:
1295 if not nodes:
1296 nodes = [self.changelog.tip()]
1296 nodes = [self.changelog.tip()]
1297 b = []
1297 b = []
1298 for n in nodes:
1298 for n in nodes:
1299 t = n
1299 t = n
1300 while 1:
1300 while 1:
1301 p = self.changelog.parents(n)
1301 p = self.changelog.parents(n)
1302 if p[1] != nullid or p[0] == nullid:
1302 if p[1] != nullid or p[0] == nullid:
1303 b.append((t, n, p[0], p[1]))
1303 b.append((t, n, p[0], p[1]))
1304 break
1304 break
1305 n = p[0]
1305 n = p[0]
1306 return b
1306 return b
1307
1307
1308 def between(self, pairs):
1308 def between(self, pairs):
1309 r = []
1309 r = []
1310
1310
1311 for top, bottom in pairs:
1311 for top, bottom in pairs:
1312 n, l, i = top, [], 0
1312 n, l, i = top, [], 0
1313 f = 1
1313 f = 1
1314
1314
1315 while n != bottom and n != nullid:
1315 while n != bottom and n != nullid:
1316 p = self.changelog.parents(n)[0]
1316 p = self.changelog.parents(n)[0]
1317 if i == f:
1317 if i == f:
1318 l.append(n)
1318 l.append(n)
1319 f = f * 2
1319 f = f * 2
1320 n = p
1320 n = p
1321 i += 1
1321 i += 1
1322
1322
1323 r.append(l)
1323 r.append(l)
1324
1324
1325 return r
1325 return r
1326
1326
1327 def pull(self, remote, heads=None, force=False):
1327 def pull(self, remote, heads=None, force=False):
1328 lock = self.lock()
1328 lock = self.lock()
1329 try:
1329 try:
1330 usecommon = remote.capable('getbundle')
1331 tmp = discovery.findcommonincoming(self, remote, heads=heads,
1330 tmp = discovery.findcommonincoming(self, remote, heads=heads,
1332 force=force, commononly=usecommon)
1331 force=force)
1333 common, fetch, rheads = tmp
1332 common, fetch, rheads = tmp
1334 if not fetch:
1333 if not fetch:
1335 self.ui.status(_("no changes found\n"))
1334 self.ui.status(_("no changes found\n"))
1336 result = 0
1335 result = 0
1337 else:
1336 else:
1338 if heads is None and list(common) == [nullid]:
1337 if heads is None and list(common) == [nullid]:
1339 self.ui.status(_("requesting all changes\n"))
1338 self.ui.status(_("requesting all changes\n"))
1340 elif heads is None and remote.capable('changegroupsubset'):
1339 elif heads is None and remote.capable('changegroupsubset'):
1341 # issue1320, avoid a race if remote changed after discovery
1340 # issue1320, avoid a race if remote changed after discovery
1342 heads = rheads
1341 heads = rheads
1343
1342
1344 if usecommon:
1343 if remote.capable('getbundle'):
1345 cg = remote.getbundle('pull', common=common,
1344 cg = remote.getbundle('pull', common=common,
1346 heads=heads or rheads)
1345 heads=heads or rheads)
1347 elif heads is None:
1346 elif heads is None:
1348 cg = remote.changegroup(fetch, 'pull')
1347 cg = remote.changegroup(fetch, 'pull')
1349 elif not remote.capable('changegroupsubset'):
1348 elif not remote.capable('changegroupsubset'):
1350 raise util.Abort(_("partial pull cannot be done because "
1349 raise util.Abort(_("partial pull cannot be done because "
1351 "other repository doesn't support "
1350 "other repository doesn't support "
1352 "changegroupsubset."))
1351 "changegroupsubset."))
1353 else:
1352 else:
1354 cg = remote.changegroupsubset(fetch, heads, 'pull')
1353 cg = remote.changegroupsubset(fetch, heads, 'pull')
1355 result = self.addchangegroup(cg, 'pull', remote.url(),
1354 result = self.addchangegroup(cg, 'pull', remote.url(),
1356 lock=lock)
1355 lock=lock)
1357 finally:
1356 finally:
1358 lock.release()
1357 lock.release()
1359
1358
1360 return result
1359 return result
1361
1360
1362 def checkpush(self, force, revs):
1361 def checkpush(self, force, revs):
1363 """Extensions can override this function if additional checks have
1362 """Extensions can override this function if additional checks have
1364 to be performed before pushing, or call it if they override push
1363 to be performed before pushing, or call it if they override push
1365 command.
1364 command.
1366 """
1365 """
1367 pass
1366 pass
1368
1367
1369 def push(self, remote, force=False, revs=None, newbranch=False):
1368 def push(self, remote, force=False, revs=None, newbranch=False):
1370 '''Push outgoing changesets (limited by revs) from the current
1369 '''Push outgoing changesets (limited by revs) from the current
1371 repository to remote. Return an integer:
1370 repository to remote. Return an integer:
1372 - 0 means HTTP error *or* nothing to push
1371 - 0 means HTTP error *or* nothing to push
1373 - 1 means we pushed and remote head count is unchanged *or*
1372 - 1 means we pushed and remote head count is unchanged *or*
1374 we have outgoing changesets but refused to push
1373 we have outgoing changesets but refused to push
1375 - other values as described by addchangegroup()
1374 - other values as described by addchangegroup()
1376 '''
1375 '''
1377 # there are two ways to push to remote repo:
1376 # there are two ways to push to remote repo:
1378 #
1377 #
1379 # addchangegroup assumes local user can lock remote
1378 # addchangegroup assumes local user can lock remote
1380 # repo (local filesystem, old ssh servers).
1379 # repo (local filesystem, old ssh servers).
1381 #
1380 #
1382 # unbundle assumes local user cannot lock remote repo (new ssh
1381 # unbundle assumes local user cannot lock remote repo (new ssh
1383 # servers, http servers).
1382 # servers, http servers).
1384
1383
1385 self.checkpush(force, revs)
1384 self.checkpush(force, revs)
1386 lock = None
1385 lock = None
1387 unbundle = remote.capable('unbundle')
1386 unbundle = remote.capable('unbundle')
1388 if not unbundle:
1387 if not unbundle:
1389 lock = remote.lock()
1388 lock = remote.lock()
1390 try:
1389 try:
1391 cg, remote_heads = discovery.prepush(self, remote, force, revs,
1390 cg, remote_heads = discovery.prepush(self, remote, force, revs,
1392 newbranch)
1391 newbranch)
1393 ret = remote_heads
1392 ret = remote_heads
1394 if cg is not None:
1393 if cg is not None:
1395 if unbundle:
1394 if unbundle:
1396 # local repo finds heads on server, finds out what
1395 # local repo finds heads on server, finds out what
1397 # revs it must push. once revs transferred, if server
1396 # revs it must push. once revs transferred, if server
1398 # finds it has different heads (someone else won
1397 # finds it has different heads (someone else won
1399 # commit/push race), server aborts.
1398 # commit/push race), server aborts.
1400 if force:
1399 if force:
1401 remote_heads = ['force']
1400 remote_heads = ['force']
1402 # ssh: return remote's addchangegroup()
1401 # ssh: return remote's addchangegroup()
1403 # http: return remote's addchangegroup() or 0 for error
1402 # http: return remote's addchangegroup() or 0 for error
1404 ret = remote.unbundle(cg, remote_heads, 'push')
1403 ret = remote.unbundle(cg, remote_heads, 'push')
1405 else:
1404 else:
1406 # we return an integer indicating remote head count change
1405 # we return an integer indicating remote head count change
1407 ret = remote.addchangegroup(cg, 'push', self.url(),
1406 ret = remote.addchangegroup(cg, 'push', self.url(),
1408 lock=lock)
1407 lock=lock)
1409 finally:
1408 finally:
1410 if lock is not None:
1409 if lock is not None:
1411 lock.release()
1410 lock.release()
1412
1411
1413 self.ui.debug("checking for updated bookmarks\n")
1412 self.ui.debug("checking for updated bookmarks\n")
1414 rb = remote.listkeys('bookmarks')
1413 rb = remote.listkeys('bookmarks')
1415 for k in rb.keys():
1414 for k in rb.keys():
1416 if k in self._bookmarks:
1415 if k in self._bookmarks:
1417 nr, nl = rb[k], hex(self._bookmarks[k])
1416 nr, nl = rb[k], hex(self._bookmarks[k])
1418 if nr in self:
1417 if nr in self:
1419 cr = self[nr]
1418 cr = self[nr]
1420 cl = self[nl]
1419 cl = self[nl]
1421 if cl in cr.descendants():
1420 if cl in cr.descendants():
1422 r = remote.pushkey('bookmarks', k, nr, nl)
1421 r = remote.pushkey('bookmarks', k, nr, nl)
1423 if r:
1422 if r:
1424 self.ui.status(_("updating bookmark %s\n") % k)
1423 self.ui.status(_("updating bookmark %s\n") % k)
1425 else:
1424 else:
1426 self.ui.warn(_('updating bookmark %s'
1425 self.ui.warn(_('updating bookmark %s'
1427 ' failed!\n') % k)
1426 ' failed!\n') % k)
1428
1427
1429 return ret
1428 return ret
1430
1429
1431 def changegroupinfo(self, nodes, source):
1430 def changegroupinfo(self, nodes, source):
1432 if self.ui.verbose or source == 'bundle':
1431 if self.ui.verbose or source == 'bundle':
1433 self.ui.status(_("%d changesets found\n") % len(nodes))
1432 self.ui.status(_("%d changesets found\n") % len(nodes))
1434 if self.ui.debugflag:
1433 if self.ui.debugflag:
1435 self.ui.debug("list of changesets:\n")
1434 self.ui.debug("list of changesets:\n")
1436 for node in nodes:
1435 for node in nodes:
1437 self.ui.debug("%s\n" % hex(node))
1436 self.ui.debug("%s\n" % hex(node))
1438
1437
1439 def changegroupsubset(self, bases, heads, source):
1438 def changegroupsubset(self, bases, heads, source):
1440 """Compute a changegroup consisting of all the nodes that are
1439 """Compute a changegroup consisting of all the nodes that are
1441 descendents of any of the bases and ancestors of any of the heads.
1440 descendents of any of the bases and ancestors of any of the heads.
1442 Return a chunkbuffer object whose read() method will return
1441 Return a chunkbuffer object whose read() method will return
1443 successive changegroup chunks.
1442 successive changegroup chunks.
1444
1443
1445 It is fairly complex as determining which filenodes and which
1444 It is fairly complex as determining which filenodes and which
1446 manifest nodes need to be included for the changeset to be complete
1445 manifest nodes need to be included for the changeset to be complete
1447 is non-trivial.
1446 is non-trivial.
1448
1447
1449 Another wrinkle is doing the reverse, figuring out which changeset in
1448 Another wrinkle is doing the reverse, figuring out which changeset in
1450 the changegroup a particular filenode or manifestnode belongs to.
1449 the changegroup a particular filenode or manifestnode belongs to.
1451 """
1450 """
1452 cl = self.changelog
1451 cl = self.changelog
1453 if not bases:
1452 if not bases:
1454 bases = [nullid]
1453 bases = [nullid]
1455 csets, bases, heads = cl.nodesbetween(bases, heads)
1454 csets, bases, heads = cl.nodesbetween(bases, heads)
1456 # We assume that all ancestors of bases are known
1455 # We assume that all ancestors of bases are known
1457 common = set(cl.ancestors(*[cl.rev(n) for n in bases]))
1456 common = set(cl.ancestors(*[cl.rev(n) for n in bases]))
1458 return self._changegroupsubset(common, csets, heads, source)
1457 return self._changegroupsubset(common, csets, heads, source)
1459
1458
1460 def getbundle(self, source, heads=None, common=None):
1459 def getbundle(self, source, heads=None, common=None):
1461 """Like changegroupsubset, but returns the set difference between the
1460 """Like changegroupsubset, but returns the set difference between the
1462 ancestors of heads and the ancestors common.
1461 ancestors of heads and the ancestors common.
1463
1462
1464 If heads is None, use the local heads. If common is None, use [nullid].
1463 If heads is None, use the local heads. If common is None, use [nullid].
1465
1464
1466 The nodes in common might not all be known locally due to the way the
1465 The nodes in common might not all be known locally due to the way the
1467 current discovery protocol works.
1466 current discovery protocol works.
1468 """
1467 """
1469 cl = self.changelog
1468 cl = self.changelog
1470 if common:
1469 if common:
1471 nm = cl.nodemap
1470 nm = cl.nodemap
1472 common = [n for n in common if n in nm]
1471 common = [n for n in common if n in nm]
1473 else:
1472 else:
1474 common = [nullid]
1473 common = [nullid]
1475 if not heads:
1474 if not heads:
1476 heads = cl.heads()
1475 heads = cl.heads()
1477 common, missing = cl.findcommonmissing(common, heads)
1476 common, missing = cl.findcommonmissing(common, heads)
1477 if not missing:
1478 return None
1478 return self._changegroupsubset(common, missing, heads, source)
1479 return self._changegroupsubset(common, missing, heads, source)
1479
1480
1480 def _changegroupsubset(self, commonrevs, csets, heads, source):
1481 def _changegroupsubset(self, commonrevs, csets, heads, source):
1481
1482
1482 cl = self.changelog
1483 cl = self.changelog
1483 mf = self.manifest
1484 mf = self.manifest
1484 mfs = {} # needed manifests
1485 mfs = {} # needed manifests
1485 fnodes = {} # needed file nodes
1486 fnodes = {} # needed file nodes
1486 changedfiles = set()
1487 changedfiles = set()
1487 fstate = ['', {}]
1488 fstate = ['', {}]
1488 count = [0]
1489 count = [0]
1489
1490
1490 # can we go through the fast path ?
1491 # can we go through the fast path ?
1491 heads.sort()
1492 heads.sort()
1492 if heads == sorted(self.heads()):
1493 if heads == sorted(self.heads()):
1493 return self._changegroup(csets, source)
1494 return self._changegroup(csets, source)
1494
1495
1495 # slow path
1496 # slow path
1496 self.hook('preoutgoing', throw=True, source=source)
1497 self.hook('preoutgoing', throw=True, source=source)
1497 self.changegroupinfo(csets, source)
1498 self.changegroupinfo(csets, source)
1498
1499
1499 # filter any nodes that claim to be part of the known set
1500 # filter any nodes that claim to be part of the known set
1500 def prune(revlog, missing):
1501 def prune(revlog, missing):
1501 for n in missing:
1502 for n in missing:
1502 if revlog.linkrev(revlog.rev(n)) not in commonrevs:
1503 if revlog.linkrev(revlog.rev(n)) not in commonrevs:
1503 yield n
1504 yield n
1504
1505
1505 def lookup(revlog, x):
1506 def lookup(revlog, x):
1506 if revlog == cl:
1507 if revlog == cl:
1507 c = cl.read(x)
1508 c = cl.read(x)
1508 changedfiles.update(c[3])
1509 changedfiles.update(c[3])
1509 mfs.setdefault(c[0], x)
1510 mfs.setdefault(c[0], x)
1510 count[0] += 1
1511 count[0] += 1
1511 self.ui.progress(_('bundling'), count[0], unit=_('changesets'))
1512 self.ui.progress(_('bundling'), count[0], unit=_('changesets'))
1512 return x
1513 return x
1513 elif revlog == mf:
1514 elif revlog == mf:
1514 clnode = mfs[x]
1515 clnode = mfs[x]
1515 mdata = mf.readfast(x)
1516 mdata = mf.readfast(x)
1516 for f in changedfiles:
1517 for f in changedfiles:
1517 if f in mdata:
1518 if f in mdata:
1518 fnodes.setdefault(f, {}).setdefault(mdata[f], clnode)
1519 fnodes.setdefault(f, {}).setdefault(mdata[f], clnode)
1519 count[0] += 1
1520 count[0] += 1
1520 self.ui.progress(_('bundling'), count[0],
1521 self.ui.progress(_('bundling'), count[0],
1521 unit=_('manifests'), total=len(mfs))
1522 unit=_('manifests'), total=len(mfs))
1522 return mfs[x]
1523 return mfs[x]
1523 else:
1524 else:
1524 self.ui.progress(
1525 self.ui.progress(
1525 _('bundling'), count[0], item=fstate[0],
1526 _('bundling'), count[0], item=fstate[0],
1526 unit=_('files'), total=len(changedfiles))
1527 unit=_('files'), total=len(changedfiles))
1527 return fstate[1][x]
1528 return fstate[1][x]
1528
1529
1529 bundler = changegroup.bundle10(lookup)
1530 bundler = changegroup.bundle10(lookup)
1530
1531
1531 def gengroup():
1532 def gengroup():
1532 # Create a changenode group generator that will call our functions
1533 # Create a changenode group generator that will call our functions
1533 # back to lookup the owning changenode and collect information.
1534 # back to lookup the owning changenode and collect information.
1534 for chunk in cl.group(csets, bundler):
1535 for chunk in cl.group(csets, bundler):
1535 yield chunk
1536 yield chunk
1536 self.ui.progress(_('bundling'), None)
1537 self.ui.progress(_('bundling'), None)
1537
1538
1538 # Create a generator for the manifestnodes that calls our lookup
1539 # Create a generator for the manifestnodes that calls our lookup
1539 # and data collection functions back.
1540 # and data collection functions back.
1540 count[0] = 0
1541 count[0] = 0
1541 for chunk in mf.group(prune(mf, mfs), bundler):
1542 for chunk in mf.group(prune(mf, mfs), bundler):
1542 yield chunk
1543 yield chunk
1543 self.ui.progress(_('bundling'), None)
1544 self.ui.progress(_('bundling'), None)
1544
1545
1545 mfs.clear()
1546 mfs.clear()
1546
1547
1547 # Go through all our files in order sorted by name.
1548 # Go through all our files in order sorted by name.
1548 count[0] = 0
1549 count[0] = 0
1549 for fname in sorted(changedfiles):
1550 for fname in sorted(changedfiles):
1550 filerevlog = self.file(fname)
1551 filerevlog = self.file(fname)
1551 if not len(filerevlog):
1552 if not len(filerevlog):
1552 raise util.Abort(_("empty or missing revlog for %s") % fname)
1553 raise util.Abort(_("empty or missing revlog for %s") % fname)
1553 fstate[0] = fname
1554 fstate[0] = fname
1554 fstate[1] = fnodes.pop(fname, {})
1555 fstate[1] = fnodes.pop(fname, {})
1555 first = True
1556 first = True
1556
1557
1557 for chunk in filerevlog.group(prune(filerevlog, fstate[1]),
1558 for chunk in filerevlog.group(prune(filerevlog, fstate[1]),
1558 bundler):
1559 bundler):
1559 if first:
1560 if first:
1560 if chunk == bundler.close():
1561 if chunk == bundler.close():
1561 break
1562 break
1562 count[0] += 1
1563 count[0] += 1
1563 yield bundler.fileheader(fname)
1564 yield bundler.fileheader(fname)
1564 first = False
1565 first = False
1565 yield chunk
1566 yield chunk
1566 # Signal that no more groups are left.
1567 # Signal that no more groups are left.
1567 yield bundler.close()
1568 yield bundler.close()
1568 self.ui.progress(_('bundling'), None)
1569 self.ui.progress(_('bundling'), None)
1569
1570
1570 if csets:
1571 if csets:
1571 self.hook('outgoing', node=hex(csets[0]), source=source)
1572 self.hook('outgoing', node=hex(csets[0]), source=source)
1572
1573
1573 return changegroup.unbundle10(util.chunkbuffer(gengroup()), 'UN')
1574 return changegroup.unbundle10(util.chunkbuffer(gengroup()), 'UN')
1574
1575
1575 def changegroup(self, basenodes, source):
1576 def changegroup(self, basenodes, source):
1576 # to avoid a race we use changegroupsubset() (issue1320)
1577 # to avoid a race we use changegroupsubset() (issue1320)
1577 return self.changegroupsubset(basenodes, self.heads(), source)
1578 return self.changegroupsubset(basenodes, self.heads(), source)
1578
1579
1579 def _changegroup(self, nodes, source):
1580 def _changegroup(self, nodes, source):
1580 """Compute the changegroup of all nodes that we have that a recipient
1581 """Compute the changegroup of all nodes that we have that a recipient
1581 doesn't. Return a chunkbuffer object whose read() method will return
1582 doesn't. Return a chunkbuffer object whose read() method will return
1582 successive changegroup chunks.
1583 successive changegroup chunks.
1583
1584
1584 This is much easier than the previous function as we can assume that
1585 This is much easier than the previous function as we can assume that
1585 the recipient has any changenode we aren't sending them.
1586 the recipient has any changenode we aren't sending them.
1586
1587
1587 nodes is the set of nodes to send"""
1588 nodes is the set of nodes to send"""
1588
1589
1589 cl = self.changelog
1590 cl = self.changelog
1590 mf = self.manifest
1591 mf = self.manifest
1591 mfs = {}
1592 mfs = {}
1592 changedfiles = set()
1593 changedfiles = set()
1593 fstate = ['']
1594 fstate = ['']
1594 count = [0]
1595 count = [0]
1595
1596
1596 self.hook('preoutgoing', throw=True, source=source)
1597 self.hook('preoutgoing', throw=True, source=source)
1597 self.changegroupinfo(nodes, source)
1598 self.changegroupinfo(nodes, source)
1598
1599
1599 revset = set([cl.rev(n) for n in nodes])
1600 revset = set([cl.rev(n) for n in nodes])
1600
1601
1601 def gennodelst(log):
1602 def gennodelst(log):
1602 for r in log:
1603 for r in log:
1603 if log.linkrev(r) in revset:
1604 if log.linkrev(r) in revset:
1604 yield log.node(r)
1605 yield log.node(r)
1605
1606
1606 def lookup(revlog, x):
1607 def lookup(revlog, x):
1607 if revlog == cl:
1608 if revlog == cl:
1608 c = cl.read(x)
1609 c = cl.read(x)
1609 changedfiles.update(c[3])
1610 changedfiles.update(c[3])
1610 mfs.setdefault(c[0], x)
1611 mfs.setdefault(c[0], x)
1611 count[0] += 1
1612 count[0] += 1
1612 self.ui.progress(_('bundling'), count[0], unit=_('changesets'))
1613 self.ui.progress(_('bundling'), count[0], unit=_('changesets'))
1613 return x
1614 return x
1614 elif revlog == mf:
1615 elif revlog == mf:
1615 count[0] += 1
1616 count[0] += 1
1616 self.ui.progress(_('bundling'), count[0],
1617 self.ui.progress(_('bundling'), count[0],
1617 unit=_('manifests'), total=len(mfs))
1618 unit=_('manifests'), total=len(mfs))
1618 return cl.node(revlog.linkrev(revlog.rev(x)))
1619 return cl.node(revlog.linkrev(revlog.rev(x)))
1619 else:
1620 else:
1620 self.ui.progress(
1621 self.ui.progress(
1621 _('bundling'), count[0], item=fstate[0],
1622 _('bundling'), count[0], item=fstate[0],
1622 total=len(changedfiles), unit=_('files'))
1623 total=len(changedfiles), unit=_('files'))
1623 return cl.node(revlog.linkrev(revlog.rev(x)))
1624 return cl.node(revlog.linkrev(revlog.rev(x)))
1624
1625
1625 bundler = changegroup.bundle10(lookup)
1626 bundler = changegroup.bundle10(lookup)
1626
1627
1627 def gengroup():
1628 def gengroup():
1628 '''yield a sequence of changegroup chunks (strings)'''
1629 '''yield a sequence of changegroup chunks (strings)'''
1629 # construct a list of all changed files
1630 # construct a list of all changed files
1630
1631
1631 for chunk in cl.group(nodes, bundler):
1632 for chunk in cl.group(nodes, bundler):
1632 yield chunk
1633 yield chunk
1633 self.ui.progress(_('bundling'), None)
1634 self.ui.progress(_('bundling'), None)
1634
1635
1635 count[0] = 0
1636 count[0] = 0
1636 for chunk in mf.group(gennodelst(mf), bundler):
1637 for chunk in mf.group(gennodelst(mf), bundler):
1637 yield chunk
1638 yield chunk
1638 self.ui.progress(_('bundling'), None)
1639 self.ui.progress(_('bundling'), None)
1639
1640
1640 count[0] = 0
1641 count[0] = 0
1641 for fname in sorted(changedfiles):
1642 for fname in sorted(changedfiles):
1642 filerevlog = self.file(fname)
1643 filerevlog = self.file(fname)
1643 if not len(filerevlog):
1644 if not len(filerevlog):
1644 raise util.Abort(_("empty or missing revlog for %s") % fname)
1645 raise util.Abort(_("empty or missing revlog for %s") % fname)
1645 fstate[0] = fname
1646 fstate[0] = fname
1646 first = True
1647 first = True
1647 for chunk in filerevlog.group(gennodelst(filerevlog), bundler):
1648 for chunk in filerevlog.group(gennodelst(filerevlog), bundler):
1648 if first:
1649 if first:
1649 if chunk == bundler.close():
1650 if chunk == bundler.close():
1650 break
1651 break
1651 count[0] += 1
1652 count[0] += 1
1652 yield bundler.fileheader(fname)
1653 yield bundler.fileheader(fname)
1653 first = False
1654 first = False
1654 yield chunk
1655 yield chunk
1655 yield bundler.close()
1656 yield bundler.close()
1656 self.ui.progress(_('bundling'), None)
1657 self.ui.progress(_('bundling'), None)
1657
1658
1658 if nodes:
1659 if nodes:
1659 self.hook('outgoing', node=hex(nodes[0]), source=source)
1660 self.hook('outgoing', node=hex(nodes[0]), source=source)
1660
1661
1661 return changegroup.unbundle10(util.chunkbuffer(gengroup()), 'UN')
1662 return changegroup.unbundle10(util.chunkbuffer(gengroup()), 'UN')
1662
1663
1663 def addchangegroup(self, source, srctype, url, emptyok=False, lock=None):
1664 def addchangegroup(self, source, srctype, url, emptyok=False, lock=None):
1664 """Add the changegroup returned by source.read() to this repo.
1665 """Add the changegroup returned by source.read() to this repo.
1665 srctype is a string like 'push', 'pull', or 'unbundle'. url is
1666 srctype is a string like 'push', 'pull', or 'unbundle'. url is
1666 the URL of the repo where this changegroup is coming from.
1667 the URL of the repo where this changegroup is coming from.
1667 If lock is not None, the function takes ownership of the lock
1668 If lock is not None, the function takes ownership of the lock
1668 and releases it after the changegroup is added.
1669 and releases it after the changegroup is added.
1669
1670
1670 Return an integer summarizing the change to this repo:
1671 Return an integer summarizing the change to this repo:
1671 - nothing changed or no source: 0
1672 - nothing changed or no source: 0
1672 - more heads than before: 1+added heads (2..n)
1673 - more heads than before: 1+added heads (2..n)
1673 - fewer heads than before: -1-removed heads (-2..-n)
1674 - fewer heads than before: -1-removed heads (-2..-n)
1674 - number of heads stays the same: 1
1675 - number of heads stays the same: 1
1675 """
1676 """
1676 def csmap(x):
1677 def csmap(x):
1677 self.ui.debug("add changeset %s\n" % short(x))
1678 self.ui.debug("add changeset %s\n" % short(x))
1678 return len(cl)
1679 return len(cl)
1679
1680
1680 def revmap(x):
1681 def revmap(x):
1681 return cl.rev(x)
1682 return cl.rev(x)
1682
1683
1683 if not source:
1684 if not source:
1684 return 0
1685 return 0
1685
1686
1686 self.hook('prechangegroup', throw=True, source=srctype, url=url)
1687 self.hook('prechangegroup', throw=True, source=srctype, url=url)
1687
1688
1688 changesets = files = revisions = 0
1689 changesets = files = revisions = 0
1689 efiles = set()
1690 efiles = set()
1690
1691
1691 # write changelog data to temp files so concurrent readers will not see
1692 # write changelog data to temp files so concurrent readers will not see
1692 # inconsistent view
1693 # inconsistent view
1693 cl = self.changelog
1694 cl = self.changelog
1694 cl.delayupdate()
1695 cl.delayupdate()
1695 oldheads = cl.heads()
1696 oldheads = cl.heads()
1696
1697
1697 tr = self.transaction("\n".join([srctype, urlmod.hidepassword(url)]))
1698 tr = self.transaction("\n".join([srctype, urlmod.hidepassword(url)]))
1698 try:
1699 try:
1699 trp = weakref.proxy(tr)
1700 trp = weakref.proxy(tr)
1700 # pull off the changeset group
1701 # pull off the changeset group
1701 self.ui.status(_("adding changesets\n"))
1702 self.ui.status(_("adding changesets\n"))
1702 clstart = len(cl)
1703 clstart = len(cl)
1703 class prog(object):
1704 class prog(object):
1704 step = _('changesets')
1705 step = _('changesets')
1705 count = 1
1706 count = 1
1706 ui = self.ui
1707 ui = self.ui
1707 total = None
1708 total = None
1708 def __call__(self):
1709 def __call__(self):
1709 self.ui.progress(self.step, self.count, unit=_('chunks'),
1710 self.ui.progress(self.step, self.count, unit=_('chunks'),
1710 total=self.total)
1711 total=self.total)
1711 self.count += 1
1712 self.count += 1
1712 pr = prog()
1713 pr = prog()
1713 source.callback = pr
1714 source.callback = pr
1714
1715
1715 if (cl.addgroup(source, csmap, trp) is None
1716 if (cl.addgroup(source, csmap, trp) is None
1716 and not emptyok):
1717 and not emptyok):
1717 raise util.Abort(_("received changelog group is empty"))
1718 raise util.Abort(_("received changelog group is empty"))
1718 clend = len(cl)
1719 clend = len(cl)
1719 changesets = clend - clstart
1720 changesets = clend - clstart
1720 for c in xrange(clstart, clend):
1721 for c in xrange(clstart, clend):
1721 efiles.update(self[c].files())
1722 efiles.update(self[c].files())
1722 efiles = len(efiles)
1723 efiles = len(efiles)
1723 self.ui.progress(_('changesets'), None)
1724 self.ui.progress(_('changesets'), None)
1724
1725
1725 # pull off the manifest group
1726 # pull off the manifest group
1726 self.ui.status(_("adding manifests\n"))
1727 self.ui.status(_("adding manifests\n"))
1727 pr.step = _('manifests')
1728 pr.step = _('manifests')
1728 pr.count = 1
1729 pr.count = 1
1729 pr.total = changesets # manifests <= changesets
1730 pr.total = changesets # manifests <= changesets
1730 # no need to check for empty manifest group here:
1731 # no need to check for empty manifest group here:
1731 # if the result of the merge of 1 and 2 is the same in 3 and 4,
1732 # if the result of the merge of 1 and 2 is the same in 3 and 4,
1732 # no new manifest will be created and the manifest group will
1733 # no new manifest will be created and the manifest group will
1733 # be empty during the pull
1734 # be empty during the pull
1734 self.manifest.addgroup(source, revmap, trp)
1735 self.manifest.addgroup(source, revmap, trp)
1735 self.ui.progress(_('manifests'), None)
1736 self.ui.progress(_('manifests'), None)
1736
1737
1737 needfiles = {}
1738 needfiles = {}
1738 if self.ui.configbool('server', 'validate', default=False):
1739 if self.ui.configbool('server', 'validate', default=False):
1739 # validate incoming csets have their manifests
1740 # validate incoming csets have their manifests
1740 for cset in xrange(clstart, clend):
1741 for cset in xrange(clstart, clend):
1741 mfest = self.changelog.read(self.changelog.node(cset))[0]
1742 mfest = self.changelog.read(self.changelog.node(cset))[0]
1742 mfest = self.manifest.readdelta(mfest)
1743 mfest = self.manifest.readdelta(mfest)
1743 # store file nodes we must see
1744 # store file nodes we must see
1744 for f, n in mfest.iteritems():
1745 for f, n in mfest.iteritems():
1745 needfiles.setdefault(f, set()).add(n)
1746 needfiles.setdefault(f, set()).add(n)
1746
1747
1747 # process the files
1748 # process the files
1748 self.ui.status(_("adding file changes\n"))
1749 self.ui.status(_("adding file changes\n"))
1749 pr.step = 'files'
1750 pr.step = 'files'
1750 pr.count = 1
1751 pr.count = 1
1751 pr.total = efiles
1752 pr.total = efiles
1752 source.callback = None
1753 source.callback = None
1753
1754
1754 while 1:
1755 while 1:
1755 f = source.chunk()
1756 f = source.chunk()
1756 if not f:
1757 if not f:
1757 break
1758 break
1758 self.ui.debug("adding %s revisions\n" % f)
1759 self.ui.debug("adding %s revisions\n" % f)
1759 pr()
1760 pr()
1760 fl = self.file(f)
1761 fl = self.file(f)
1761 o = len(fl)
1762 o = len(fl)
1762 if fl.addgroup(source, revmap, trp) is None:
1763 if fl.addgroup(source, revmap, trp) is None:
1763 raise util.Abort(_("received file revlog group is empty"))
1764 raise util.Abort(_("received file revlog group is empty"))
1764 revisions += len(fl) - o
1765 revisions += len(fl) - o
1765 files += 1
1766 files += 1
1766 if f in needfiles:
1767 if f in needfiles:
1767 needs = needfiles[f]
1768 needs = needfiles[f]
1768 for new in xrange(o, len(fl)):
1769 for new in xrange(o, len(fl)):
1769 n = fl.node(new)
1770 n = fl.node(new)
1770 if n in needs:
1771 if n in needs:
1771 needs.remove(n)
1772 needs.remove(n)
1772 if not needs:
1773 if not needs:
1773 del needfiles[f]
1774 del needfiles[f]
1774 self.ui.progress(_('files'), None)
1775 self.ui.progress(_('files'), None)
1775
1776
1776 for f, needs in needfiles.iteritems():
1777 for f, needs in needfiles.iteritems():
1777 fl = self.file(f)
1778 fl = self.file(f)
1778 for n in needs:
1779 for n in needs:
1779 try:
1780 try:
1780 fl.rev(n)
1781 fl.rev(n)
1781 except error.LookupError:
1782 except error.LookupError:
1782 raise util.Abort(
1783 raise util.Abort(
1783 _('missing file data for %s:%s - run hg verify') %
1784 _('missing file data for %s:%s - run hg verify') %
1784 (f, hex(n)))
1785 (f, hex(n)))
1785
1786
1786 dh = 0
1787 dh = 0
1787 if oldheads:
1788 if oldheads:
1788 heads = cl.heads()
1789 heads = cl.heads()
1789 dh = len(heads) - len(oldheads)
1790 dh = len(heads) - len(oldheads)
1790 for h in heads:
1791 for h in heads:
1791 if h not in oldheads and 'close' in self[h].extra():
1792 if h not in oldheads and 'close' in self[h].extra():
1792 dh -= 1
1793 dh -= 1
1793 htext = ""
1794 htext = ""
1794 if dh:
1795 if dh:
1795 htext = _(" (%+d heads)") % dh
1796 htext = _(" (%+d heads)") % dh
1796
1797
1797 self.ui.status(_("added %d changesets"
1798 self.ui.status(_("added %d changesets"
1798 " with %d changes to %d files%s\n")
1799 " with %d changes to %d files%s\n")
1799 % (changesets, revisions, files, htext))
1800 % (changesets, revisions, files, htext))
1800
1801
1801 if changesets > 0:
1802 if changesets > 0:
1802 p = lambda: cl.writepending() and self.root or ""
1803 p = lambda: cl.writepending() and self.root or ""
1803 self.hook('pretxnchangegroup', throw=True,
1804 self.hook('pretxnchangegroup', throw=True,
1804 node=hex(cl.node(clstart)), source=srctype,
1805 node=hex(cl.node(clstart)), source=srctype,
1805 url=url, pending=p)
1806 url=url, pending=p)
1806
1807
1807 # make changelog see real files again
1808 # make changelog see real files again
1808 cl.finalize(trp)
1809 cl.finalize(trp)
1809
1810
1810 tr.close()
1811 tr.close()
1811 finally:
1812 finally:
1812 tr.release()
1813 tr.release()
1813 if lock:
1814 if lock:
1814 lock.release()
1815 lock.release()
1815
1816
1816 if changesets > 0:
1817 if changesets > 0:
1817 # forcefully update the on-disk branch cache
1818 # forcefully update the on-disk branch cache
1818 self.ui.debug("updating the branch cache\n")
1819 self.ui.debug("updating the branch cache\n")
1819 self.updatebranchcache()
1820 self.updatebranchcache()
1820 self.hook("changegroup", node=hex(cl.node(clstart)),
1821 self.hook("changegroup", node=hex(cl.node(clstart)),
1821 source=srctype, url=url)
1822 source=srctype, url=url)
1822
1823
1823 for i in xrange(clstart, clend):
1824 for i in xrange(clstart, clend):
1824 self.hook("incoming", node=hex(cl.node(i)),
1825 self.hook("incoming", node=hex(cl.node(i)),
1825 source=srctype, url=url)
1826 source=srctype, url=url)
1826
1827
1827 # never return 0 here:
1828 # never return 0 here:
1828 if dh < 0:
1829 if dh < 0:
1829 return dh - 1
1830 return dh - 1
1830 else:
1831 else:
1831 return dh + 1
1832 return dh + 1
1832
1833
1833 def stream_in(self, remote, requirements):
1834 def stream_in(self, remote, requirements):
1834 lock = self.lock()
1835 lock = self.lock()
1835 try:
1836 try:
1836 fp = remote.stream_out()
1837 fp = remote.stream_out()
1837 l = fp.readline()
1838 l = fp.readline()
1838 try:
1839 try:
1839 resp = int(l)
1840 resp = int(l)
1840 except ValueError:
1841 except ValueError:
1841 raise error.ResponseError(
1842 raise error.ResponseError(
1842 _('Unexpected response from remote server:'), l)
1843 _('Unexpected response from remote server:'), l)
1843 if resp == 1:
1844 if resp == 1:
1844 raise util.Abort(_('operation forbidden by server'))
1845 raise util.Abort(_('operation forbidden by server'))
1845 elif resp == 2:
1846 elif resp == 2:
1846 raise util.Abort(_('locking the remote repository failed'))
1847 raise util.Abort(_('locking the remote repository failed'))
1847 elif resp != 0:
1848 elif resp != 0:
1848 raise util.Abort(_('the server sent an unknown error code'))
1849 raise util.Abort(_('the server sent an unknown error code'))
1849 self.ui.status(_('streaming all changes\n'))
1850 self.ui.status(_('streaming all changes\n'))
1850 l = fp.readline()
1851 l = fp.readline()
1851 try:
1852 try:
1852 total_files, total_bytes = map(int, l.split(' ', 1))
1853 total_files, total_bytes = map(int, l.split(' ', 1))
1853 except (ValueError, TypeError):
1854 except (ValueError, TypeError):
1854 raise error.ResponseError(
1855 raise error.ResponseError(
1855 _('Unexpected response from remote server:'), l)
1856 _('Unexpected response from remote server:'), l)
1856 self.ui.status(_('%d files to transfer, %s of data\n') %
1857 self.ui.status(_('%d files to transfer, %s of data\n') %
1857 (total_files, util.bytecount(total_bytes)))
1858 (total_files, util.bytecount(total_bytes)))
1858 start = time.time()
1859 start = time.time()
1859 for i in xrange(total_files):
1860 for i in xrange(total_files):
1860 # XXX doesn't support '\n' or '\r' in filenames
1861 # XXX doesn't support '\n' or '\r' in filenames
1861 l = fp.readline()
1862 l = fp.readline()
1862 try:
1863 try:
1863 name, size = l.split('\0', 1)
1864 name, size = l.split('\0', 1)
1864 size = int(size)
1865 size = int(size)
1865 except (ValueError, TypeError):
1866 except (ValueError, TypeError):
1866 raise error.ResponseError(
1867 raise error.ResponseError(
1867 _('Unexpected response from remote server:'), l)
1868 _('Unexpected response from remote server:'), l)
1868 self.ui.debug('adding %s (%s)\n' % (name, util.bytecount(size)))
1869 self.ui.debug('adding %s (%s)\n' % (name, util.bytecount(size)))
1869 # for backwards compat, name was partially encoded
1870 # for backwards compat, name was partially encoded
1870 ofp = self.sopener(store.decodedir(name), 'w')
1871 ofp = self.sopener(store.decodedir(name), 'w')
1871 for chunk in util.filechunkiter(fp, limit=size):
1872 for chunk in util.filechunkiter(fp, limit=size):
1872 ofp.write(chunk)
1873 ofp.write(chunk)
1873 ofp.close()
1874 ofp.close()
1874 elapsed = time.time() - start
1875 elapsed = time.time() - start
1875 if elapsed <= 0:
1876 if elapsed <= 0:
1876 elapsed = 0.001
1877 elapsed = 0.001
1877 self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') %
1878 self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') %
1878 (util.bytecount(total_bytes), elapsed,
1879 (util.bytecount(total_bytes), elapsed,
1879 util.bytecount(total_bytes / elapsed)))
1880 util.bytecount(total_bytes / elapsed)))
1880
1881
1881 # new requirements = old non-format requirements + new format-related
1882 # new requirements = old non-format requirements + new format-related
1882 # requirements from the streamed-in repository
1883 # requirements from the streamed-in repository
1883 requirements.update(set(self.requirements) - self.supportedformats)
1884 requirements.update(set(self.requirements) - self.supportedformats)
1884 self._applyrequirements(requirements)
1885 self._applyrequirements(requirements)
1885 self._writerequirements()
1886 self._writerequirements()
1886
1887
1887 self.invalidate()
1888 self.invalidate()
1888 return len(self.heads()) + 1
1889 return len(self.heads()) + 1
1889 finally:
1890 finally:
1890 lock.release()
1891 lock.release()
1891
1892
1892 def clone(self, remote, heads=[], stream=False):
1893 def clone(self, remote, heads=[], stream=False):
1893 '''clone remote repository.
1894 '''clone remote repository.
1894
1895
1895 keyword arguments:
1896 keyword arguments:
1896 heads: list of revs to clone (forces use of pull)
1897 heads: list of revs to clone (forces use of pull)
1897 stream: use streaming clone if possible'''
1898 stream: use streaming clone if possible'''
1898
1899
1899 # now, all clients that can request uncompressed clones can
1900 # now, all clients that can request uncompressed clones can
1900 # read repo formats supported by all servers that can serve
1901 # read repo formats supported by all servers that can serve
1901 # them.
1902 # them.
1902
1903
1903 # if revlog format changes, client will have to check version
1904 # if revlog format changes, client will have to check version
1904 # and format flags on "stream" capability, and use
1905 # and format flags on "stream" capability, and use
1905 # uncompressed only if compatible.
1906 # uncompressed only if compatible.
1906
1907
1907 if stream and not heads:
1908 if stream and not heads:
1908 # 'stream' means remote revlog format is revlogv1 only
1909 # 'stream' means remote revlog format is revlogv1 only
1909 if remote.capable('stream'):
1910 if remote.capable('stream'):
1910 return self.stream_in(remote, set(('revlogv1',)))
1911 return self.stream_in(remote, set(('revlogv1',)))
1911 # otherwise, 'streamreqs' contains the remote revlog format
1912 # otherwise, 'streamreqs' contains the remote revlog format
1912 streamreqs = remote.capable('streamreqs')
1913 streamreqs = remote.capable('streamreqs')
1913 if streamreqs:
1914 if streamreqs:
1914 streamreqs = set(streamreqs.split(','))
1915 streamreqs = set(streamreqs.split(','))
1915 # if we support it, stream in and adjust our requirements
1916 # if we support it, stream in and adjust our requirements
1916 if not streamreqs - self.supportedformats:
1917 if not streamreqs - self.supportedformats:
1917 return self.stream_in(remote, streamreqs)
1918 return self.stream_in(remote, streamreqs)
1918 return self.pull(remote, heads)
1919 return self.pull(remote, heads)
1919
1920
1920 def pushkey(self, namespace, key, old, new):
1921 def pushkey(self, namespace, key, old, new):
1921 return pushkey.push(self, namespace, key, old, new)
1922 return pushkey.push(self, namespace, key, old, new)
1922
1923
1923 def listkeys(self, namespace):
1924 def listkeys(self, namespace):
1924 return pushkey.list(self, namespace)
1925 return pushkey.list(self, namespace)
1925
1926
1926 def debugwireargs(self, one, two, three=None, four=None, five=None):
1927 def debugwireargs(self, one, two, three=None, four=None, five=None):
1927 '''used to test argument passing over the wire'''
1928 '''used to test argument passing over the wire'''
1928 return "%s %s %s %s %s" % (one, two, three, four, five)
1929 return "%s %s %s %s %s" % (one, two, three, four, five)
1929
1930
1930 # used to avoid circular references so destructors work
1931 # used to avoid circular references so destructors work
1931 def aftertrans(files):
1932 def aftertrans(files):
1932 renamefiles = [tuple(t) for t in files]
1933 renamefiles = [tuple(t) for t in files]
1933 def a():
1934 def a():
1934 for src, dest in renamefiles:
1935 for src, dest in renamefiles:
1935 util.rename(src, dest)
1936 util.rename(src, dest)
1936 return a
1937 return a
1937
1938
1938 def instance(ui, path, create):
1939 def instance(ui, path, create):
1939 return localrepository(ui, urlmod.localpath(path), create)
1940 return localrepository(ui, urlmod.localpath(path), create)
1940
1941
1941 def islocal(path):
1942 def islocal(path):
1942 return True
1943 return True
@@ -1,909 +1,909
1 # revset.py - revision set queries for mercurial
1 # revset.py - revision set queries for mercurial
2 #
2 #
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 import re
8 import re
9 import parser, util, error, discovery, help, hbisect
9 import parser, util, error, discovery, help, hbisect
10 import bookmarks as bookmarksmod
10 import bookmarks as bookmarksmod
11 import match as matchmod
11 import match as matchmod
12 from i18n import _
12 from i18n import _
13
13
14 elements = {
14 elements = {
15 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
15 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
16 "~": (18, None, ("ancestor", 18)),
16 "~": (18, None, ("ancestor", 18)),
17 "^": (18, None, ("parent", 18), ("parentpost", 18)),
17 "^": (18, None, ("parent", 18), ("parentpost", 18)),
18 "-": (5, ("negate", 19), ("minus", 5)),
18 "-": (5, ("negate", 19), ("minus", 5)),
19 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
19 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
20 ("dagrangepost", 17)),
20 ("dagrangepost", 17)),
21 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
21 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
22 ("dagrangepost", 17)),
22 ("dagrangepost", 17)),
23 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
23 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
24 "not": (10, ("not", 10)),
24 "not": (10, ("not", 10)),
25 "!": (10, ("not", 10)),
25 "!": (10, ("not", 10)),
26 "and": (5, None, ("and", 5)),
26 "and": (5, None, ("and", 5)),
27 "&": (5, None, ("and", 5)),
27 "&": (5, None, ("and", 5)),
28 "or": (4, None, ("or", 4)),
28 "or": (4, None, ("or", 4)),
29 "|": (4, None, ("or", 4)),
29 "|": (4, None, ("or", 4)),
30 "+": (4, None, ("or", 4)),
30 "+": (4, None, ("or", 4)),
31 ",": (2, None, ("list", 2)),
31 ",": (2, None, ("list", 2)),
32 ")": (0, None, None),
32 ")": (0, None, None),
33 "symbol": (0, ("symbol",), None),
33 "symbol": (0, ("symbol",), None),
34 "string": (0, ("string",), None),
34 "string": (0, ("string",), None),
35 "end": (0, None, None),
35 "end": (0, None, None),
36 }
36 }
37
37
38 keywords = set(['and', 'or', 'not'])
38 keywords = set(['and', 'or', 'not'])
39
39
40 def tokenize(program):
40 def tokenize(program):
41 pos, l = 0, len(program)
41 pos, l = 0, len(program)
42 while pos < l:
42 while pos < l:
43 c = program[pos]
43 c = program[pos]
44 if c.isspace(): # skip inter-token whitespace
44 if c.isspace(): # skip inter-token whitespace
45 pass
45 pass
46 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
46 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
47 yield ('::', None, pos)
47 yield ('::', None, pos)
48 pos += 1 # skip ahead
48 pos += 1 # skip ahead
49 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
49 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
50 yield ('..', None, pos)
50 yield ('..', None, pos)
51 pos += 1 # skip ahead
51 pos += 1 # skip ahead
52 elif c in "():,-|&+!~^": # handle simple operators
52 elif c in "():,-|&+!~^": # handle simple operators
53 yield (c, None, pos)
53 yield (c, None, pos)
54 elif (c in '"\'' or c == 'r' and
54 elif (c in '"\'' or c == 'r' and
55 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
55 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
56 if c == 'r':
56 if c == 'r':
57 pos += 1
57 pos += 1
58 c = program[pos]
58 c = program[pos]
59 decode = lambda x: x
59 decode = lambda x: x
60 else:
60 else:
61 decode = lambda x: x.decode('string-escape')
61 decode = lambda x: x.decode('string-escape')
62 pos += 1
62 pos += 1
63 s = pos
63 s = pos
64 while pos < l: # find closing quote
64 while pos < l: # find closing quote
65 d = program[pos]
65 d = program[pos]
66 if d == '\\': # skip over escaped characters
66 if d == '\\': # skip over escaped characters
67 pos += 2
67 pos += 2
68 continue
68 continue
69 if d == c:
69 if d == c:
70 yield ('string', decode(program[s:pos]), s)
70 yield ('string', decode(program[s:pos]), s)
71 break
71 break
72 pos += 1
72 pos += 1
73 else:
73 else:
74 raise error.ParseError(_("unterminated string"), s)
74 raise error.ParseError(_("unterminated string"), s)
75 elif c.isalnum() or c in '._' or ord(c) > 127: # gather up a symbol/keyword
75 elif c.isalnum() or c in '._' or ord(c) > 127: # gather up a symbol/keyword
76 s = pos
76 s = pos
77 pos += 1
77 pos += 1
78 while pos < l: # find end of symbol
78 while pos < l: # find end of symbol
79 d = program[pos]
79 d = program[pos]
80 if not (d.isalnum() or d in "._" or ord(d) > 127):
80 if not (d.isalnum() or d in "._" or ord(d) > 127):
81 break
81 break
82 if d == '.' and program[pos - 1] == '.': # special case for ..
82 if d == '.' and program[pos - 1] == '.': # special case for ..
83 pos -= 1
83 pos -= 1
84 break
84 break
85 pos += 1
85 pos += 1
86 sym = program[s:pos]
86 sym = program[s:pos]
87 if sym in keywords: # operator keywords
87 if sym in keywords: # operator keywords
88 yield (sym, None, s)
88 yield (sym, None, s)
89 else:
89 else:
90 yield ('symbol', sym, s)
90 yield ('symbol', sym, s)
91 pos -= 1
91 pos -= 1
92 else:
92 else:
93 raise error.ParseError(_("syntax error"), pos)
93 raise error.ParseError(_("syntax error"), pos)
94 pos += 1
94 pos += 1
95 yield ('end', None, pos)
95 yield ('end', None, pos)
96
96
97 # helpers
97 # helpers
98
98
99 def getstring(x, err):
99 def getstring(x, err):
100 if x and (x[0] == 'string' or x[0] == 'symbol'):
100 if x and (x[0] == 'string' or x[0] == 'symbol'):
101 return x[1]
101 return x[1]
102 raise error.ParseError(err)
102 raise error.ParseError(err)
103
103
104 def getlist(x):
104 def getlist(x):
105 if not x:
105 if not x:
106 return []
106 return []
107 if x[0] == 'list':
107 if x[0] == 'list':
108 return getlist(x[1]) + [x[2]]
108 return getlist(x[1]) + [x[2]]
109 return [x]
109 return [x]
110
110
111 def getargs(x, min, max, err):
111 def getargs(x, min, max, err):
112 l = getlist(x)
112 l = getlist(x)
113 if len(l) < min or len(l) > max:
113 if len(l) < min or len(l) > max:
114 raise error.ParseError(err)
114 raise error.ParseError(err)
115 return l
115 return l
116
116
117 def getset(repo, subset, x):
117 def getset(repo, subset, x):
118 if not x:
118 if not x:
119 raise error.ParseError(_("missing argument"))
119 raise error.ParseError(_("missing argument"))
120 return methods[x[0]](repo, subset, *x[1:])
120 return methods[x[0]](repo, subset, *x[1:])
121
121
122 # operator methods
122 # operator methods
123
123
124 def stringset(repo, subset, x):
124 def stringset(repo, subset, x):
125 x = repo[x].rev()
125 x = repo[x].rev()
126 if x == -1 and len(subset) == len(repo):
126 if x == -1 and len(subset) == len(repo):
127 return [-1]
127 return [-1]
128 if len(subset) == len(repo) or x in subset:
128 if len(subset) == len(repo) or x in subset:
129 return [x]
129 return [x]
130 return []
130 return []
131
131
132 def symbolset(repo, subset, x):
132 def symbolset(repo, subset, x):
133 if x in symbols:
133 if x in symbols:
134 raise error.ParseError(_("can't use %s here") % x)
134 raise error.ParseError(_("can't use %s here") % x)
135 return stringset(repo, subset, x)
135 return stringset(repo, subset, x)
136
136
137 def rangeset(repo, subset, x, y):
137 def rangeset(repo, subset, x, y):
138 m = getset(repo, subset, x)
138 m = getset(repo, subset, x)
139 if not m:
139 if not m:
140 m = getset(repo, range(len(repo)), x)
140 m = getset(repo, range(len(repo)), x)
141
141
142 n = getset(repo, subset, y)
142 n = getset(repo, subset, y)
143 if not n:
143 if not n:
144 n = getset(repo, range(len(repo)), y)
144 n = getset(repo, range(len(repo)), y)
145
145
146 if not m or not n:
146 if not m or not n:
147 return []
147 return []
148 m, n = m[0], n[-1]
148 m, n = m[0], n[-1]
149
149
150 if m < n:
150 if m < n:
151 r = range(m, n + 1)
151 r = range(m, n + 1)
152 else:
152 else:
153 r = range(m, n - 1, -1)
153 r = range(m, n - 1, -1)
154 s = set(subset)
154 s = set(subset)
155 return [x for x in r if x in s]
155 return [x for x in r if x in s]
156
156
157 def andset(repo, subset, x, y):
157 def andset(repo, subset, x, y):
158 return getset(repo, getset(repo, subset, x), y)
158 return getset(repo, getset(repo, subset, x), y)
159
159
160 def orset(repo, subset, x, y):
160 def orset(repo, subset, x, y):
161 xl = getset(repo, subset, x)
161 xl = getset(repo, subset, x)
162 s = set(xl)
162 s = set(xl)
163 yl = getset(repo, [r for r in subset if r not in s], y)
163 yl = getset(repo, [r for r in subset if r not in s], y)
164 return xl + yl
164 return xl + yl
165
165
166 def notset(repo, subset, x):
166 def notset(repo, subset, x):
167 s = set(getset(repo, subset, x))
167 s = set(getset(repo, subset, x))
168 return [r for r in subset if r not in s]
168 return [r for r in subset if r not in s]
169
169
170 def listset(repo, subset, a, b):
170 def listset(repo, subset, a, b):
171 raise error.ParseError(_("can't use a list in this context"))
171 raise error.ParseError(_("can't use a list in this context"))
172
172
173 def func(repo, subset, a, b):
173 def func(repo, subset, a, b):
174 if a[0] == 'symbol' and a[1] in symbols:
174 if a[0] == 'symbol' and a[1] in symbols:
175 return symbols[a[1]](repo, subset, b)
175 return symbols[a[1]](repo, subset, b)
176 raise error.ParseError(_("not a function: %s") % a[1])
176 raise error.ParseError(_("not a function: %s") % a[1])
177
177
178 # functions
178 # functions
179
179
180 def adds(repo, subset, x):
180 def adds(repo, subset, x):
181 """``adds(pattern)``
181 """``adds(pattern)``
182 Changesets that add a file matching pattern.
182 Changesets that add a file matching pattern.
183 """
183 """
184 # i18n: "adds" is a keyword
184 # i18n: "adds" is a keyword
185 pat = getstring(x, _("adds requires a pattern"))
185 pat = getstring(x, _("adds requires a pattern"))
186 return checkstatus(repo, subset, pat, 1)
186 return checkstatus(repo, subset, pat, 1)
187
187
188 def ancestor(repo, subset, x):
188 def ancestor(repo, subset, x):
189 """``ancestor(single, single)``
189 """``ancestor(single, single)``
190 Greatest common ancestor of the two changesets.
190 Greatest common ancestor of the two changesets.
191 """
191 """
192 # i18n: "ancestor" is a keyword
192 # i18n: "ancestor" is a keyword
193 l = getargs(x, 2, 2, _("ancestor requires two arguments"))
193 l = getargs(x, 2, 2, _("ancestor requires two arguments"))
194 r = range(len(repo))
194 r = range(len(repo))
195 a = getset(repo, r, l[0])
195 a = getset(repo, r, l[0])
196 b = getset(repo, r, l[1])
196 b = getset(repo, r, l[1])
197 if len(a) != 1 or len(b) != 1:
197 if len(a) != 1 or len(b) != 1:
198 # i18n: "ancestor" is a keyword
198 # i18n: "ancestor" is a keyword
199 raise error.ParseError(_("ancestor arguments must be single revisions"))
199 raise error.ParseError(_("ancestor arguments must be single revisions"))
200 an = [repo[a[0]].ancestor(repo[b[0]]).rev()]
200 an = [repo[a[0]].ancestor(repo[b[0]]).rev()]
201
201
202 return [r for r in an if r in subset]
202 return [r for r in an if r in subset]
203
203
204 def ancestors(repo, subset, x):
204 def ancestors(repo, subset, x):
205 """``ancestors(set)``
205 """``ancestors(set)``
206 Changesets that are ancestors of a changeset in set.
206 Changesets that are ancestors of a changeset in set.
207 """
207 """
208 args = getset(repo, range(len(repo)), x)
208 args = getset(repo, range(len(repo)), x)
209 if not args:
209 if not args:
210 return []
210 return []
211 s = set(repo.changelog.ancestors(*args)) | set(args)
211 s = set(repo.changelog.ancestors(*args)) | set(args)
212 return [r for r in subset if r in s]
212 return [r for r in subset if r in s]
213
213
214 def ancestorspec(repo, subset, x, n):
214 def ancestorspec(repo, subset, x, n):
215 """``set~n``
215 """``set~n``
216 Changesets that are the Nth ancestor (first parents only) of a changeset in set.
216 Changesets that are the Nth ancestor (first parents only) of a changeset in set.
217 """
217 """
218 try:
218 try:
219 n = int(n[1])
219 n = int(n[1])
220 except ValueError:
220 except ValueError:
221 raise error.ParseError(_("~ expects a number"))
221 raise error.ParseError(_("~ expects a number"))
222 ps = set()
222 ps = set()
223 cl = repo.changelog
223 cl = repo.changelog
224 for r in getset(repo, subset, x):
224 for r in getset(repo, subset, x):
225 for i in range(n):
225 for i in range(n):
226 r = cl.parentrevs(r)[0]
226 r = cl.parentrevs(r)[0]
227 ps.add(r)
227 ps.add(r)
228 return [r for r in subset if r in ps]
228 return [r for r in subset if r in ps]
229
229
230 def author(repo, subset, x):
230 def author(repo, subset, x):
231 """``author(string)``
231 """``author(string)``
232 Alias for ``user(string)``.
232 Alias for ``user(string)``.
233 """
233 """
234 # i18n: "author" is a keyword
234 # i18n: "author" is a keyword
235 n = getstring(x, _("author requires a string")).lower()
235 n = getstring(x, _("author requires a string")).lower()
236 return [r for r in subset if n in repo[r].user().lower()]
236 return [r for r in subset if n in repo[r].user().lower()]
237
237
238 def bisected(repo, subset, x):
238 def bisected(repo, subset, x):
239 """``bisected(string)``
239 """``bisected(string)``
240 Changesets marked in the specified bisect state (good, bad, skip).
240 Changesets marked in the specified bisect state (good, bad, skip).
241 """
241 """
242 state = getstring(x, _("bisect requires a string")).lower()
242 state = getstring(x, _("bisect requires a string")).lower()
243 if state not in ('good', 'bad', 'skip', 'unknown'):
243 if state not in ('good', 'bad', 'skip', 'unknown'):
244 raise error.ParseError(_('invalid bisect state'))
244 raise error.ParseError(_('invalid bisect state'))
245 marked = set(repo.changelog.rev(n) for n in hbisect.load_state(repo)[state])
245 marked = set(repo.changelog.rev(n) for n in hbisect.load_state(repo)[state])
246 return [r for r in subset if r in marked]
246 return [r for r in subset if r in marked]
247
247
248 def bookmark(repo, subset, x):
248 def bookmark(repo, subset, x):
249 """``bookmark([name])``
249 """``bookmark([name])``
250 The named bookmark or all bookmarks.
250 The named bookmark or all bookmarks.
251 """
251 """
252 # i18n: "bookmark" is a keyword
252 # i18n: "bookmark" is a keyword
253 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
253 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
254 if args:
254 if args:
255 bm = getstring(args[0],
255 bm = getstring(args[0],
256 # i18n: "bookmark" is a keyword
256 # i18n: "bookmark" is a keyword
257 _('the argument to bookmark must be a string'))
257 _('the argument to bookmark must be a string'))
258 bmrev = bookmarksmod.listbookmarks(repo).get(bm, None)
258 bmrev = bookmarksmod.listbookmarks(repo).get(bm, None)
259 if not bmrev:
259 if not bmrev:
260 raise util.Abort(_("bookmark '%s' does not exist") % bm)
260 raise util.Abort(_("bookmark '%s' does not exist") % bm)
261 bmrev = repo[bmrev].rev()
261 bmrev = repo[bmrev].rev()
262 return [r for r in subset if r == bmrev]
262 return [r for r in subset if r == bmrev]
263 bms = set([repo[r].rev()
263 bms = set([repo[r].rev()
264 for r in bookmarksmod.listbookmarks(repo).values()])
264 for r in bookmarksmod.listbookmarks(repo).values()])
265 return [r for r in subset if r in bms]
265 return [r for r in subset if r in bms]
266
266
267 def branch(repo, subset, x):
267 def branch(repo, subset, x):
268 """``branch(string or set)``
268 """``branch(string or set)``
269 All changesets belonging to the given branch or the branches of the given
269 All changesets belonging to the given branch or the branches of the given
270 changesets.
270 changesets.
271 """
271 """
272 try:
272 try:
273 b = getstring(x, '')
273 b = getstring(x, '')
274 if b in repo.branchmap():
274 if b in repo.branchmap():
275 return [r for r in subset if repo[r].branch() == b]
275 return [r for r in subset if repo[r].branch() == b]
276 except error.ParseError:
276 except error.ParseError:
277 # not a string, but another revspec, e.g. tip()
277 # not a string, but another revspec, e.g. tip()
278 pass
278 pass
279
279
280 s = getset(repo, range(len(repo)), x)
280 s = getset(repo, range(len(repo)), x)
281 b = set()
281 b = set()
282 for r in s:
282 for r in s:
283 b.add(repo[r].branch())
283 b.add(repo[r].branch())
284 s = set(s)
284 s = set(s)
285 return [r for r in subset if r in s or repo[r].branch() in b]
285 return [r for r in subset if r in s or repo[r].branch() in b]
286
286
287 def checkstatus(repo, subset, pat, field):
287 def checkstatus(repo, subset, pat, field):
288 m = matchmod.match(repo.root, repo.getcwd(), [pat])
288 m = matchmod.match(repo.root, repo.getcwd(), [pat])
289 s = []
289 s = []
290 fast = (m.files() == [pat])
290 fast = (m.files() == [pat])
291 for r in subset:
291 for r in subset:
292 c = repo[r]
292 c = repo[r]
293 if fast:
293 if fast:
294 if pat not in c.files():
294 if pat not in c.files():
295 continue
295 continue
296 else:
296 else:
297 for f in c.files():
297 for f in c.files():
298 if m(f):
298 if m(f):
299 break
299 break
300 else:
300 else:
301 continue
301 continue
302 files = repo.status(c.p1().node(), c.node())[field]
302 files = repo.status(c.p1().node(), c.node())[field]
303 if fast:
303 if fast:
304 if pat in files:
304 if pat in files:
305 s.append(r)
305 s.append(r)
306 else:
306 else:
307 for f in files:
307 for f in files:
308 if m(f):
308 if m(f):
309 s.append(r)
309 s.append(r)
310 break
310 break
311 return s
311 return s
312
312
313 def children(repo, subset, x):
313 def children(repo, subset, x):
314 """``children(set)``
314 """``children(set)``
315 Child changesets of changesets in set.
315 Child changesets of changesets in set.
316 """
316 """
317 cs = set()
317 cs = set()
318 cl = repo.changelog
318 cl = repo.changelog
319 s = set(getset(repo, range(len(repo)), x))
319 s = set(getset(repo, range(len(repo)), x))
320 for r in xrange(0, len(repo)):
320 for r in xrange(0, len(repo)):
321 for p in cl.parentrevs(r):
321 for p in cl.parentrevs(r):
322 if p in s:
322 if p in s:
323 cs.add(r)
323 cs.add(r)
324 return [r for r in subset if r in cs]
324 return [r for r in subset if r in cs]
325
325
326 def closed(repo, subset, x):
326 def closed(repo, subset, x):
327 """``closed()``
327 """``closed()``
328 Changeset is closed.
328 Changeset is closed.
329 """
329 """
330 # i18n: "closed" is a keyword
330 # i18n: "closed" is a keyword
331 getargs(x, 0, 0, _("closed takes no arguments"))
331 getargs(x, 0, 0, _("closed takes no arguments"))
332 return [r for r in subset if repo[r].extra().get('close')]
332 return [r for r in subset if repo[r].extra().get('close')]
333
333
334 def contains(repo, subset, x):
334 def contains(repo, subset, x):
335 """``contains(pattern)``
335 """``contains(pattern)``
336 Revision contains pattern.
336 Revision contains pattern.
337 """
337 """
338 # i18n: "contains" is a keyword
338 # i18n: "contains" is a keyword
339 pat = getstring(x, _("contains requires a pattern"))
339 pat = getstring(x, _("contains requires a pattern"))
340 m = matchmod.match(repo.root, repo.getcwd(), [pat])
340 m = matchmod.match(repo.root, repo.getcwd(), [pat])
341 s = []
341 s = []
342 if m.files() == [pat]:
342 if m.files() == [pat]:
343 for r in subset:
343 for r in subset:
344 if pat in repo[r]:
344 if pat in repo[r]:
345 s.append(r)
345 s.append(r)
346 else:
346 else:
347 for r in subset:
347 for r in subset:
348 for f in repo[r].manifest():
348 for f in repo[r].manifest():
349 if m(f):
349 if m(f):
350 s.append(r)
350 s.append(r)
351 break
351 break
352 return s
352 return s
353
353
354 def date(repo, subset, x):
354 def date(repo, subset, x):
355 """``date(interval)``
355 """``date(interval)``
356 Changesets within the interval, see :hg:`help dates`.
356 Changesets within the interval, see :hg:`help dates`.
357 """
357 """
358 # i18n: "date" is a keyword
358 # i18n: "date" is a keyword
359 ds = getstring(x, _("date requires a string"))
359 ds = getstring(x, _("date requires a string"))
360 dm = util.matchdate(ds)
360 dm = util.matchdate(ds)
361 return [r for r in subset if dm(repo[r].date()[0])]
361 return [r for r in subset if dm(repo[r].date()[0])]
362
362
363 def descendants(repo, subset, x):
363 def descendants(repo, subset, x):
364 """``descendants(set)``
364 """``descendants(set)``
365 Changesets which are descendants of changesets in set.
365 Changesets which are descendants of changesets in set.
366 """
366 """
367 args = getset(repo, range(len(repo)), x)
367 args = getset(repo, range(len(repo)), x)
368 if not args:
368 if not args:
369 return []
369 return []
370 s = set(repo.changelog.descendants(*args)) | set(args)
370 s = set(repo.changelog.descendants(*args)) | set(args)
371 return [r for r in subset if r in s]
371 return [r for r in subset if r in s]
372
372
373 def follow(repo, subset, x):
373 def follow(repo, subset, x):
374 """``follow()``
374 """``follow()``
375 An alias for ``::.`` (ancestors of the working copy's first parent).
375 An alias for ``::.`` (ancestors of the working copy's first parent).
376 """
376 """
377 # i18n: "follow" is a keyword
377 # i18n: "follow" is a keyword
378 getargs(x, 0, 0, _("follow takes no arguments"))
378 getargs(x, 0, 0, _("follow takes no arguments"))
379 p = repo['.'].rev()
379 p = repo['.'].rev()
380 s = set(repo.changelog.ancestors(p)) | set([p])
380 s = set(repo.changelog.ancestors(p)) | set([p])
381 return [r for r in subset if r in s]
381 return [r for r in subset if r in s]
382
382
383 def getall(repo, subset, x):
383 def getall(repo, subset, x):
384 """``all()``
384 """``all()``
385 All changesets, the same as ``0:tip``.
385 All changesets, the same as ``0:tip``.
386 """
386 """
387 # i18n: "all" is a keyword
387 # i18n: "all" is a keyword
388 getargs(x, 0, 0, _("all takes no arguments"))
388 getargs(x, 0, 0, _("all takes no arguments"))
389 return subset
389 return subset
390
390
391 def grep(repo, subset, x):
391 def grep(repo, subset, x):
392 """``grep(regex)``
392 """``grep(regex)``
393 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
393 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
394 to ensure special escape characters are handled correctly.
394 to ensure special escape characters are handled correctly.
395 """
395 """
396 try:
396 try:
397 # i18n: "grep" is a keyword
397 # i18n: "grep" is a keyword
398 gr = re.compile(getstring(x, _("grep requires a string")))
398 gr = re.compile(getstring(x, _("grep requires a string")))
399 except re.error, e:
399 except re.error, e:
400 raise error.ParseError(_('invalid match pattern: %s') % e)
400 raise error.ParseError(_('invalid match pattern: %s') % e)
401 l = []
401 l = []
402 for r in subset:
402 for r in subset:
403 c = repo[r]
403 c = repo[r]
404 for e in c.files() + [c.user(), c.description()]:
404 for e in c.files() + [c.user(), c.description()]:
405 if gr.search(e):
405 if gr.search(e):
406 l.append(r)
406 l.append(r)
407 break
407 break
408 return l
408 return l
409
409
410 def hasfile(repo, subset, x):
410 def hasfile(repo, subset, x):
411 """``file(pattern)``
411 """``file(pattern)``
412 Changesets affecting files matched by pattern.
412 Changesets affecting files matched by pattern.
413 """
413 """
414 # i18n: "file" is a keyword
414 # i18n: "file" is a keyword
415 pat = getstring(x, _("file requires a pattern"))
415 pat = getstring(x, _("file requires a pattern"))
416 m = matchmod.match(repo.root, repo.getcwd(), [pat])
416 m = matchmod.match(repo.root, repo.getcwd(), [pat])
417 s = []
417 s = []
418 for r in subset:
418 for r in subset:
419 for f in repo[r].files():
419 for f in repo[r].files():
420 if m(f):
420 if m(f):
421 s.append(r)
421 s.append(r)
422 break
422 break
423 return s
423 return s
424
424
425 def head(repo, subset, x):
425 def head(repo, subset, x):
426 """``head()``
426 """``head()``
427 Changeset is a named branch head.
427 Changeset is a named branch head.
428 """
428 """
429 # i18n: "head" is a keyword
429 # i18n: "head" is a keyword
430 getargs(x, 0, 0, _("head takes no arguments"))
430 getargs(x, 0, 0, _("head takes no arguments"))
431 hs = set()
431 hs = set()
432 for b, ls in repo.branchmap().iteritems():
432 for b, ls in repo.branchmap().iteritems():
433 hs.update(repo[h].rev() for h in ls)
433 hs.update(repo[h].rev() for h in ls)
434 return [r for r in subset if r in hs]
434 return [r for r in subset if r in hs]
435
435
436 def heads(repo, subset, x):
436 def heads(repo, subset, x):
437 """``heads(set)``
437 """``heads(set)``
438 Members of set with no children in set.
438 Members of set with no children in set.
439 """
439 """
440 s = getset(repo, subset, x)
440 s = getset(repo, subset, x)
441 ps = set(parents(repo, subset, x))
441 ps = set(parents(repo, subset, x))
442 return [r for r in s if r not in ps]
442 return [r for r in s if r not in ps]
443
443
444 def keyword(repo, subset, x):
444 def keyword(repo, subset, x):
445 """``keyword(string)``
445 """``keyword(string)``
446 Search commit message, user name, and names of changed files for
446 Search commit message, user name, and names of changed files for
447 string.
447 string.
448 """
448 """
449 # i18n: "keyword" is a keyword
449 # i18n: "keyword" is a keyword
450 kw = getstring(x, _("keyword requires a string")).lower()
450 kw = getstring(x, _("keyword requires a string")).lower()
451 l = []
451 l = []
452 for r in subset:
452 for r in subset:
453 c = repo[r]
453 c = repo[r]
454 t = " ".join(c.files() + [c.user(), c.description()])
454 t = " ".join(c.files() + [c.user(), c.description()])
455 if kw in t.lower():
455 if kw in t.lower():
456 l.append(r)
456 l.append(r)
457 return l
457 return l
458
458
459 def limit(repo, subset, x):
459 def limit(repo, subset, x):
460 """``limit(set, n)``
460 """``limit(set, n)``
461 First n members of set.
461 First n members of set.
462 """
462 """
463 # i18n: "limit" is a keyword
463 # i18n: "limit" is a keyword
464 l = getargs(x, 2, 2, _("limit requires two arguments"))
464 l = getargs(x, 2, 2, _("limit requires two arguments"))
465 try:
465 try:
466 # i18n: "limit" is a keyword
466 # i18n: "limit" is a keyword
467 lim = int(getstring(l[1], _("limit requires a number")))
467 lim = int(getstring(l[1], _("limit requires a number")))
468 except ValueError:
468 except ValueError:
469 # i18n: "limit" is a keyword
469 # i18n: "limit" is a keyword
470 raise error.ParseError(_("limit expects a number"))
470 raise error.ParseError(_("limit expects a number"))
471 return getset(repo, subset, l[0])[:lim]
471 return getset(repo, subset, l[0])[:lim]
472
472
473 def last(repo, subset, x):
473 def last(repo, subset, x):
474 """``last(set, n)``
474 """``last(set, n)``
475 Last n members of set.
475 Last n members of set.
476 """
476 """
477 # i18n: "last" is a keyword
477 # i18n: "last" is a keyword
478 l = getargs(x, 2, 2, _("last requires two arguments"))
478 l = getargs(x, 2, 2, _("last requires two arguments"))
479 try:
479 try:
480 # i18n: "last" is a keyword
480 # i18n: "last" is a keyword
481 lim = int(getstring(l[1], _("last requires a number")))
481 lim = int(getstring(l[1], _("last requires a number")))
482 except ValueError:
482 except ValueError:
483 # i18n: "last" is a keyword
483 # i18n: "last" is a keyword
484 raise error.ParseError(_("last expects a number"))
484 raise error.ParseError(_("last expects a number"))
485 return getset(repo, subset, l[0])[-lim:]
485 return getset(repo, subset, l[0])[-lim:]
486
486
487 def maxrev(repo, subset, x):
487 def maxrev(repo, subset, x):
488 """``max(set)``
488 """``max(set)``
489 Changeset with highest revision number in set.
489 Changeset with highest revision number in set.
490 """
490 """
491 s = getset(repo, subset, x)
491 s = getset(repo, subset, x)
492 if s:
492 if s:
493 m = max(s)
493 m = max(s)
494 if m in subset:
494 if m in subset:
495 return [m]
495 return [m]
496 return []
496 return []
497
497
498 def merge(repo, subset, x):
498 def merge(repo, subset, x):
499 """``merge()``
499 """``merge()``
500 Changeset is a merge changeset.
500 Changeset is a merge changeset.
501 """
501 """
502 # i18n: "merge" is a keyword
502 # i18n: "merge" is a keyword
503 getargs(x, 0, 0, _("merge takes no arguments"))
503 getargs(x, 0, 0, _("merge takes no arguments"))
504 cl = repo.changelog
504 cl = repo.changelog
505 return [r for r in subset if cl.parentrevs(r)[1] != -1]
505 return [r for r in subset if cl.parentrevs(r)[1] != -1]
506
506
507 def minrev(repo, subset, x):
507 def minrev(repo, subset, x):
508 """``min(set)``
508 """``min(set)``
509 Changeset with lowest revision number in set.
509 Changeset with lowest revision number in set.
510 """
510 """
511 s = getset(repo, subset, x)
511 s = getset(repo, subset, x)
512 if s:
512 if s:
513 m = min(s)
513 m = min(s)
514 if m in subset:
514 if m in subset:
515 return [m]
515 return [m]
516 return []
516 return []
517
517
518 def modifies(repo, subset, x):
518 def modifies(repo, subset, x):
519 """``modifies(pattern)``
519 """``modifies(pattern)``
520 Changesets modifying files matched by pattern.
520 Changesets modifying files matched by pattern.
521 """
521 """
522 # i18n: "modifies" is a keyword
522 # i18n: "modifies" is a keyword
523 pat = getstring(x, _("modifies requires a pattern"))
523 pat = getstring(x, _("modifies requires a pattern"))
524 return checkstatus(repo, subset, pat, 0)
524 return checkstatus(repo, subset, pat, 0)
525
525
526 def node(repo, subset, x):
526 def node(repo, subset, x):
527 """``id(string)``
527 """``id(string)``
528 Revision non-ambiguously specified by the given hex string prefix.
528 Revision non-ambiguously specified by the given hex string prefix.
529 """
529 """
530 # i18n: "id" is a keyword
530 # i18n: "id" is a keyword
531 l = getargs(x, 1, 1, _("id requires one argument"))
531 l = getargs(x, 1, 1, _("id requires one argument"))
532 # i18n: "id" is a keyword
532 # i18n: "id" is a keyword
533 n = getstring(l[0], _("id requires a string"))
533 n = getstring(l[0], _("id requires a string"))
534 if len(n) == 40:
534 if len(n) == 40:
535 rn = repo[n].rev()
535 rn = repo[n].rev()
536 else:
536 else:
537 rn = repo.changelog.rev(repo.changelog._partialmatch(n))
537 rn = repo.changelog.rev(repo.changelog._partialmatch(n))
538 return [r for r in subset if r == rn]
538 return [r for r in subset if r == rn]
539
539
540 def outgoing(repo, subset, x):
540 def outgoing(repo, subset, x):
541 """``outgoing([path])``
541 """``outgoing([path])``
542 Changesets not found in the specified destination repository, or the
542 Changesets not found in the specified destination repository, or the
543 default push location.
543 default push location.
544 """
544 """
545 import hg # avoid start-up nasties
545 import hg # avoid start-up nasties
546 # i18n: "outgoing" is a keyword
546 # i18n: "outgoing" is a keyword
547 l = getargs(x, 0, 1, _("outgoing requires a repository path"))
547 l = getargs(x, 0, 1, _("outgoing requires a repository path"))
548 # i18n: "outgoing" is a keyword
548 # i18n: "outgoing" is a keyword
549 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
549 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
550 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
550 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
551 dest, branches = hg.parseurl(dest)
551 dest, branches = hg.parseurl(dest)
552 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
552 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
553 if revs:
553 if revs:
554 revs = [repo.lookup(rev) for rev in revs]
554 revs = [repo.lookup(rev) for rev in revs]
555 other = hg.repository(hg.remoteui(repo, {}), dest)
555 other = hg.repository(hg.remoteui(repo, {}), dest)
556 repo.ui.pushbuffer()
556 repo.ui.pushbuffer()
557 o = discovery.findoutgoing(repo, other)
557 common, _anyinc, _heads = discovery.findcommonincoming(repo, other)
558 repo.ui.popbuffer()
558 repo.ui.popbuffer()
559 cl = repo.changelog
559 cl = repo.changelog
560 o = set([cl.rev(r) for r in repo.changelog.nodesbetween(o, revs)[0]])
560 o = set([cl.rev(r) for r in repo.changelog.findmissing(common, revs)])
561 return [r for r in subset if r in o]
561 return [r for r in subset if r in o]
562
562
563 def p1(repo, subset, x):
563 def p1(repo, subset, x):
564 """``p1([set])``
564 """``p1([set])``
565 First parent of changesets in set, or the working directory.
565 First parent of changesets in set, or the working directory.
566 """
566 """
567 if x is None:
567 if x is None:
568 p = repo[x].p1().rev()
568 p = repo[x].p1().rev()
569 return [r for r in subset if r == p]
569 return [r for r in subset if r == p]
570
570
571 ps = set()
571 ps = set()
572 cl = repo.changelog
572 cl = repo.changelog
573 for r in getset(repo, range(len(repo)), x):
573 for r in getset(repo, range(len(repo)), x):
574 ps.add(cl.parentrevs(r)[0])
574 ps.add(cl.parentrevs(r)[0])
575 return [r for r in subset if r in ps]
575 return [r for r in subset if r in ps]
576
576
577 def p2(repo, subset, x):
577 def p2(repo, subset, x):
578 """``p2([set])``
578 """``p2([set])``
579 Second parent of changesets in set, or the working directory.
579 Second parent of changesets in set, or the working directory.
580 """
580 """
581 if x is None:
581 if x is None:
582 ps = repo[x].parents()
582 ps = repo[x].parents()
583 try:
583 try:
584 p = ps[1].rev()
584 p = ps[1].rev()
585 return [r for r in subset if r == p]
585 return [r for r in subset if r == p]
586 except IndexError:
586 except IndexError:
587 return []
587 return []
588
588
589 ps = set()
589 ps = set()
590 cl = repo.changelog
590 cl = repo.changelog
591 for r in getset(repo, range(len(repo)), x):
591 for r in getset(repo, range(len(repo)), x):
592 ps.add(cl.parentrevs(r)[1])
592 ps.add(cl.parentrevs(r)[1])
593 return [r for r in subset if r in ps]
593 return [r for r in subset if r in ps]
594
594
595 def parents(repo, subset, x):
595 def parents(repo, subset, x):
596 """``parents([set])``
596 """``parents([set])``
597 The set of all parents for all changesets in set, or the working directory.
597 The set of all parents for all changesets in set, or the working directory.
598 """
598 """
599 if x is None:
599 if x is None:
600 ps = tuple(p.rev() for p in repo[x].parents())
600 ps = tuple(p.rev() for p in repo[x].parents())
601 return [r for r in subset if r in ps]
601 return [r for r in subset if r in ps]
602
602
603 ps = set()
603 ps = set()
604 cl = repo.changelog
604 cl = repo.changelog
605 for r in getset(repo, range(len(repo)), x):
605 for r in getset(repo, range(len(repo)), x):
606 ps.update(cl.parentrevs(r))
606 ps.update(cl.parentrevs(r))
607 return [r for r in subset if r in ps]
607 return [r for r in subset if r in ps]
608
608
609 def parentspec(repo, subset, x, n):
609 def parentspec(repo, subset, x, n):
610 """``set^0``
610 """``set^0``
611 The set.
611 The set.
612 ``set^1`` (or ``set^``), ``set^2``
612 ``set^1`` (or ``set^``), ``set^2``
613 First or second parent, respectively, of all changesets in set.
613 First or second parent, respectively, of all changesets in set.
614 """
614 """
615 try:
615 try:
616 n = int(n[1])
616 n = int(n[1])
617 if n not in (0, 1, 2):
617 if n not in (0, 1, 2):
618 raise ValueError
618 raise ValueError
619 except ValueError:
619 except ValueError:
620 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
620 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
621 ps = set()
621 ps = set()
622 cl = repo.changelog
622 cl = repo.changelog
623 for r in getset(repo, subset, x):
623 for r in getset(repo, subset, x):
624 if n == 0:
624 if n == 0:
625 ps.add(r)
625 ps.add(r)
626 elif n == 1:
626 elif n == 1:
627 ps.add(cl.parentrevs(r)[0])
627 ps.add(cl.parentrevs(r)[0])
628 elif n == 2:
628 elif n == 2:
629 parents = cl.parentrevs(r)
629 parents = cl.parentrevs(r)
630 if len(parents) > 1:
630 if len(parents) > 1:
631 ps.add(parents[1])
631 ps.add(parents[1])
632 return [r for r in subset if r in ps]
632 return [r for r in subset if r in ps]
633
633
634 def present(repo, subset, x):
634 def present(repo, subset, x):
635 """``present(set)``
635 """``present(set)``
636 An empty set, if any revision in set isn't found; otherwise,
636 An empty set, if any revision in set isn't found; otherwise,
637 all revisions in set.
637 all revisions in set.
638 """
638 """
639 try:
639 try:
640 return getset(repo, subset, x)
640 return getset(repo, subset, x)
641 except error.RepoLookupError:
641 except error.RepoLookupError:
642 return []
642 return []
643
643
644 def removes(repo, subset, x):
644 def removes(repo, subset, x):
645 """``removes(pattern)``
645 """``removes(pattern)``
646 Changesets which remove files matching pattern.
646 Changesets which remove files matching pattern.
647 """
647 """
648 # i18n: "removes" is a keyword
648 # i18n: "removes" is a keyword
649 pat = getstring(x, _("removes requires a pattern"))
649 pat = getstring(x, _("removes requires a pattern"))
650 return checkstatus(repo, subset, pat, 2)
650 return checkstatus(repo, subset, pat, 2)
651
651
652 def rev(repo, subset, x):
652 def rev(repo, subset, x):
653 """``rev(number)``
653 """``rev(number)``
654 Revision with the given numeric identifier.
654 Revision with the given numeric identifier.
655 """
655 """
656 # i18n: "rev" is a keyword
656 # i18n: "rev" is a keyword
657 l = getargs(x, 1, 1, _("rev requires one argument"))
657 l = getargs(x, 1, 1, _("rev requires one argument"))
658 try:
658 try:
659 # i18n: "rev" is a keyword
659 # i18n: "rev" is a keyword
660 l = int(getstring(l[0], _("rev requires a number")))
660 l = int(getstring(l[0], _("rev requires a number")))
661 except ValueError:
661 except ValueError:
662 # i18n: "rev" is a keyword
662 # i18n: "rev" is a keyword
663 raise error.ParseError(_("rev expects a number"))
663 raise error.ParseError(_("rev expects a number"))
664 return [r for r in subset if r == l]
664 return [r for r in subset if r == l]
665
665
666 def reverse(repo, subset, x):
666 def reverse(repo, subset, x):
667 """``reverse(set)``
667 """``reverse(set)``
668 Reverse order of set.
668 Reverse order of set.
669 """
669 """
670 l = getset(repo, subset, x)
670 l = getset(repo, subset, x)
671 l.reverse()
671 l.reverse()
672 return l
672 return l
673
673
674 def roots(repo, subset, x):
674 def roots(repo, subset, x):
675 """``roots(set)``
675 """``roots(set)``
676 Changesets with no parent changeset in set.
676 Changesets with no parent changeset in set.
677 """
677 """
678 s = getset(repo, subset, x)
678 s = getset(repo, subset, x)
679 cs = set(children(repo, subset, x))
679 cs = set(children(repo, subset, x))
680 return [r for r in s if r not in cs]
680 return [r for r in s if r not in cs]
681
681
682 def sort(repo, subset, x):
682 def sort(repo, subset, x):
683 """``sort(set[, [-]key...])``
683 """``sort(set[, [-]key...])``
684 Sort set by keys. The default sort order is ascending, specify a key
684 Sort set by keys. The default sort order is ascending, specify a key
685 as ``-key`` to sort in descending order.
685 as ``-key`` to sort in descending order.
686
686
687 The keys can be:
687 The keys can be:
688
688
689 - ``rev`` for the revision number,
689 - ``rev`` for the revision number,
690 - ``branch`` for the branch name,
690 - ``branch`` for the branch name,
691 - ``desc`` for the commit message (description),
691 - ``desc`` for the commit message (description),
692 - ``user`` for user name (``author`` can be used as an alias),
692 - ``user`` for user name (``author`` can be used as an alias),
693 - ``date`` for the commit date
693 - ``date`` for the commit date
694 """
694 """
695 # i18n: "sort" is a keyword
695 # i18n: "sort" is a keyword
696 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
696 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
697 keys = "rev"
697 keys = "rev"
698 if len(l) == 2:
698 if len(l) == 2:
699 keys = getstring(l[1], _("sort spec must be a string"))
699 keys = getstring(l[1], _("sort spec must be a string"))
700
700
701 s = l[0]
701 s = l[0]
702 keys = keys.split()
702 keys = keys.split()
703 l = []
703 l = []
704 def invert(s):
704 def invert(s):
705 return "".join(chr(255 - ord(c)) for c in s)
705 return "".join(chr(255 - ord(c)) for c in s)
706 for r in getset(repo, subset, s):
706 for r in getset(repo, subset, s):
707 c = repo[r]
707 c = repo[r]
708 e = []
708 e = []
709 for k in keys:
709 for k in keys:
710 if k == 'rev':
710 if k == 'rev':
711 e.append(r)
711 e.append(r)
712 elif k == '-rev':
712 elif k == '-rev':
713 e.append(-r)
713 e.append(-r)
714 elif k == 'branch':
714 elif k == 'branch':
715 e.append(c.branch())
715 e.append(c.branch())
716 elif k == '-branch':
716 elif k == '-branch':
717 e.append(invert(c.branch()))
717 e.append(invert(c.branch()))
718 elif k == 'desc':
718 elif k == 'desc':
719 e.append(c.description())
719 e.append(c.description())
720 elif k == '-desc':
720 elif k == '-desc':
721 e.append(invert(c.description()))
721 e.append(invert(c.description()))
722 elif k in 'user author':
722 elif k in 'user author':
723 e.append(c.user())
723 e.append(c.user())
724 elif k in '-user -author':
724 elif k in '-user -author':
725 e.append(invert(c.user()))
725 e.append(invert(c.user()))
726 elif k == 'date':
726 elif k == 'date':
727 e.append(c.date()[0])
727 e.append(c.date()[0])
728 elif k == '-date':
728 elif k == '-date':
729 e.append(-c.date()[0])
729 e.append(-c.date()[0])
730 else:
730 else:
731 raise error.ParseError(_("unknown sort key %r") % k)
731 raise error.ParseError(_("unknown sort key %r") % k)
732 e.append(r)
732 e.append(r)
733 l.append(e)
733 l.append(e)
734 l.sort()
734 l.sort()
735 return [e[-1] for e in l]
735 return [e[-1] for e in l]
736
736
737 def tag(repo, subset, x):
737 def tag(repo, subset, x):
738 """``tag(name)``
738 """``tag(name)``
739 The specified tag by name, or all tagged revisions if no name is given.
739 The specified tag by name, or all tagged revisions if no name is given.
740 """
740 """
741 # i18n: "tag" is a keyword
741 # i18n: "tag" is a keyword
742 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
742 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
743 cl = repo.changelog
743 cl = repo.changelog
744 if args:
744 if args:
745 tn = getstring(args[0],
745 tn = getstring(args[0],
746 # i18n: "tag" is a keyword
746 # i18n: "tag" is a keyword
747 _('the argument to tag must be a string'))
747 _('the argument to tag must be a string'))
748 if not repo.tags().get(tn, None):
748 if not repo.tags().get(tn, None):
749 raise util.Abort(_("tag '%s' does not exist") % tn)
749 raise util.Abort(_("tag '%s' does not exist") % tn)
750 s = set([cl.rev(n) for t, n in repo.tagslist() if t == tn])
750 s = set([cl.rev(n) for t, n in repo.tagslist() if t == tn])
751 else:
751 else:
752 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
752 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
753 return [r for r in subset if r in s]
753 return [r for r in subset if r in s]
754
754
755 def tagged(repo, subset, x):
755 def tagged(repo, subset, x):
756 return tag(repo, subset, x)
756 return tag(repo, subset, x)
757
757
758 def user(repo, subset, x):
758 def user(repo, subset, x):
759 """``user(string)``
759 """``user(string)``
760 User name is string.
760 User name is string.
761 """
761 """
762 return author(repo, subset, x)
762 return author(repo, subset, x)
763
763
764 symbols = {
764 symbols = {
765 "adds": adds,
765 "adds": adds,
766 "all": getall,
766 "all": getall,
767 "ancestor": ancestor,
767 "ancestor": ancestor,
768 "ancestors": ancestors,
768 "ancestors": ancestors,
769 "author": author,
769 "author": author,
770 "bisected": bisected,
770 "bisected": bisected,
771 "bookmark": bookmark,
771 "bookmark": bookmark,
772 "branch": branch,
772 "branch": branch,
773 "children": children,
773 "children": children,
774 "closed": closed,
774 "closed": closed,
775 "contains": contains,
775 "contains": contains,
776 "date": date,
776 "date": date,
777 "descendants": descendants,
777 "descendants": descendants,
778 "file": hasfile,
778 "file": hasfile,
779 "follow": follow,
779 "follow": follow,
780 "grep": grep,
780 "grep": grep,
781 "head": head,
781 "head": head,
782 "heads": heads,
782 "heads": heads,
783 "keyword": keyword,
783 "keyword": keyword,
784 "last": last,
784 "last": last,
785 "limit": limit,
785 "limit": limit,
786 "max": maxrev,
786 "max": maxrev,
787 "min": minrev,
787 "min": minrev,
788 "merge": merge,
788 "merge": merge,
789 "modifies": modifies,
789 "modifies": modifies,
790 "id": node,
790 "id": node,
791 "outgoing": outgoing,
791 "outgoing": outgoing,
792 "p1": p1,
792 "p1": p1,
793 "p2": p2,
793 "p2": p2,
794 "parents": parents,
794 "parents": parents,
795 "present": present,
795 "present": present,
796 "removes": removes,
796 "removes": removes,
797 "reverse": reverse,
797 "reverse": reverse,
798 "rev": rev,
798 "rev": rev,
799 "roots": roots,
799 "roots": roots,
800 "sort": sort,
800 "sort": sort,
801 "tag": tag,
801 "tag": tag,
802 "tagged": tagged,
802 "tagged": tagged,
803 "user": user,
803 "user": user,
804 }
804 }
805
805
806 methods = {
806 methods = {
807 "range": rangeset,
807 "range": rangeset,
808 "string": stringset,
808 "string": stringset,
809 "symbol": symbolset,
809 "symbol": symbolset,
810 "and": andset,
810 "and": andset,
811 "or": orset,
811 "or": orset,
812 "not": notset,
812 "not": notset,
813 "list": listset,
813 "list": listset,
814 "func": func,
814 "func": func,
815 "ancestor": ancestorspec,
815 "ancestor": ancestorspec,
816 "parent": parentspec,
816 "parent": parentspec,
817 "parentpost": p1,
817 "parentpost": p1,
818 }
818 }
819
819
820 def optimize(x, small):
820 def optimize(x, small):
821 if x is None:
821 if x is None:
822 return 0, x
822 return 0, x
823
823
824 smallbonus = 1
824 smallbonus = 1
825 if small:
825 if small:
826 smallbonus = .5
826 smallbonus = .5
827
827
828 op = x[0]
828 op = x[0]
829 if op == 'minus':
829 if op == 'minus':
830 return optimize(('and', x[1], ('not', x[2])), small)
830 return optimize(('and', x[1], ('not', x[2])), small)
831 elif op == 'dagrange':
831 elif op == 'dagrange':
832 return optimize(('and', ('func', ('symbol', 'descendants'), x[1]),
832 return optimize(('and', ('func', ('symbol', 'descendants'), x[1]),
833 ('func', ('symbol', 'ancestors'), x[2])), small)
833 ('func', ('symbol', 'ancestors'), x[2])), small)
834 elif op == 'dagrangepre':
834 elif op == 'dagrangepre':
835 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
835 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
836 elif op == 'dagrangepost':
836 elif op == 'dagrangepost':
837 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
837 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
838 elif op == 'rangepre':
838 elif op == 'rangepre':
839 return optimize(('range', ('string', '0'), x[1]), small)
839 return optimize(('range', ('string', '0'), x[1]), small)
840 elif op == 'rangepost':
840 elif op == 'rangepost':
841 return optimize(('range', x[1], ('string', 'tip')), small)
841 return optimize(('range', x[1], ('string', 'tip')), small)
842 elif op == 'negate':
842 elif op == 'negate':
843 return optimize(('string',
843 return optimize(('string',
844 '-' + getstring(x[1], _("can't negate that"))), small)
844 '-' + getstring(x[1], _("can't negate that"))), small)
845 elif op in 'string symbol negate':
845 elif op in 'string symbol negate':
846 return smallbonus, x # single revisions are small
846 return smallbonus, x # single revisions are small
847 elif op == 'and' or op == 'dagrange':
847 elif op == 'and' or op == 'dagrange':
848 wa, ta = optimize(x[1], True)
848 wa, ta = optimize(x[1], True)
849 wb, tb = optimize(x[2], True)
849 wb, tb = optimize(x[2], True)
850 w = min(wa, wb)
850 w = min(wa, wb)
851 if wa > wb:
851 if wa > wb:
852 return w, (op, tb, ta)
852 return w, (op, tb, ta)
853 return w, (op, ta, tb)
853 return w, (op, ta, tb)
854 elif op == 'or':
854 elif op == 'or':
855 wa, ta = optimize(x[1], False)
855 wa, ta = optimize(x[1], False)
856 wb, tb = optimize(x[2], False)
856 wb, tb = optimize(x[2], False)
857 if wb < wa:
857 if wb < wa:
858 wb, wa = wa, wb
858 wb, wa = wa, wb
859 return max(wa, wb), (op, ta, tb)
859 return max(wa, wb), (op, ta, tb)
860 elif op == 'not':
860 elif op == 'not':
861 o = optimize(x[1], not small)
861 o = optimize(x[1], not small)
862 return o[0], (op, o[1])
862 return o[0], (op, o[1])
863 elif op == 'parentpost':
863 elif op == 'parentpost':
864 o = optimize(x[1], small)
864 o = optimize(x[1], small)
865 return o[0], (op, o[1])
865 return o[0], (op, o[1])
866 elif op == 'group':
866 elif op == 'group':
867 return optimize(x[1], small)
867 return optimize(x[1], small)
868 elif op in 'range list parent ancestorspec':
868 elif op in 'range list parent ancestorspec':
869 wa, ta = optimize(x[1], small)
869 wa, ta = optimize(x[1], small)
870 wb, tb = optimize(x[2], small)
870 wb, tb = optimize(x[2], small)
871 return wa + wb, (op, ta, tb)
871 return wa + wb, (op, ta, tb)
872 elif op == 'func':
872 elif op == 'func':
873 f = getstring(x[1], _("not a symbol"))
873 f = getstring(x[1], _("not a symbol"))
874 wa, ta = optimize(x[2], small)
874 wa, ta = optimize(x[2], small)
875 if f in "grep date user author keyword branch file outgoing closed":
875 if f in "grep date user author keyword branch file outgoing closed":
876 w = 10 # slow
876 w = 10 # slow
877 elif f in "modifies adds removes":
877 elif f in "modifies adds removes":
878 w = 30 # slower
878 w = 30 # slower
879 elif f == "contains":
879 elif f == "contains":
880 w = 100 # very slow
880 w = 100 # very slow
881 elif f == "ancestor":
881 elif f == "ancestor":
882 w = 1 * smallbonus
882 w = 1 * smallbonus
883 elif f in "reverse limit":
883 elif f in "reverse limit":
884 w = 0
884 w = 0
885 elif f in "sort":
885 elif f in "sort":
886 w = 10 # assume most sorts look at changelog
886 w = 10 # assume most sorts look at changelog
887 else:
887 else:
888 w = 1
888 w = 1
889 return w + wa, (op, x[1], ta)
889 return w + wa, (op, x[1], ta)
890 return 1, x
890 return 1, x
891
891
892 parse = parser.parser(tokenize, elements).parse
892 parse = parser.parser(tokenize, elements).parse
893
893
894 def match(spec):
894 def match(spec):
895 if not spec:
895 if not spec:
896 raise error.ParseError(_("empty query"))
896 raise error.ParseError(_("empty query"))
897 tree, pos = parse(spec)
897 tree, pos = parse(spec)
898 if (pos != len(spec)):
898 if (pos != len(spec)):
899 raise error.ParseError("invalid token", pos)
899 raise error.ParseError("invalid token", pos)
900 weight, tree = optimize(tree, True)
900 weight, tree = optimize(tree, True)
901 def mfunc(repo, subset):
901 def mfunc(repo, subset):
902 return getset(repo, subset, tree)
902 return getset(repo, subset, tree)
903 return mfunc
903 return mfunc
904
904
905 def makedoc(topic, doc):
905 def makedoc(topic, doc):
906 return help.makeitemsdoc(topic, doc, '.. predicatesmarker', symbols)
906 return help.makeitemsdoc(topic, doc, '.. predicatesmarker', symbols)
907
907
908 # tell hggettext to extract docstrings from these functions:
908 # tell hggettext to extract docstrings from these functions:
909 i18nfunctions = symbols.values()
909 i18nfunctions = symbols.values()
@@ -1,1895 +1,1870
1 > do_push()
1 > do_push()
2 > {
2 > {
3 > user=$1
3 > user=$1
4 > shift
4 > shift
5 > echo "Pushing as user $user"
5 > echo "Pushing as user $user"
6 > echo 'hgrc = """'
6 > echo 'hgrc = """'
7 > sed -e 1,2d b/.hg/hgrc | grep -v fakegroups.py
7 > sed -e 1,2d b/.hg/hgrc | grep -v fakegroups.py
8 > echo '"""'
8 > echo '"""'
9 > if test -f acl.config; then
9 > if test -f acl.config; then
10 > echo 'acl.config = """'
10 > echo 'acl.config = """'
11 > cat acl.config
11 > cat acl.config
12 > echo '"""'
12 > echo '"""'
13 > fi
13 > fi
14 > # On AIX /etc/profile sets LOGNAME read-only. So
14 > # On AIX /etc/profile sets LOGNAME read-only. So
15 > # LOGNAME=$user hg --cws a --debug push ../b
15 > # LOGNAME=$user hg --cws a --debug push ../b
16 > # fails with "This variable is read only."
16 > # fails with "This variable is read only."
17 > # Use env to work around this.
17 > # Use env to work around this.
18 > env LOGNAME=$user hg --cwd a --debug push ../b
18 > env LOGNAME=$user hg --cwd a --debug push ../b
19 > hg --cwd b rollback
19 > hg --cwd b rollback
20 > hg --cwd b --quiet tip
20 > hg --cwd b --quiet tip
21 > echo
21 > echo
22 > }
22 > }
23
23
24 > init_config()
24 > init_config()
25 > {
25 > {
26 > cat > fakegroups.py <<EOF
26 > cat > fakegroups.py <<EOF
27 > from hgext import acl
27 > from hgext import acl
28 > def fakegetusers(ui, group):
28 > def fakegetusers(ui, group):
29 > try:
29 > try:
30 > return acl._getusersorig(ui, group)
30 > return acl._getusersorig(ui, group)
31 > except:
31 > except:
32 > return ["fred", "betty"]
32 > return ["fred", "betty"]
33 > acl._getusersorig = acl._getusers
33 > acl._getusersorig = acl._getusers
34 > acl._getusers = fakegetusers
34 > acl._getusers = fakegetusers
35 > EOF
35 > EOF
36 > rm -f acl.config
36 > rm -f acl.config
37 > cat > $config <<EOF
37 > cat > $config <<EOF
38 > [hooks]
38 > [hooks]
39 > pretxnchangegroup.acl = python:hgext.acl.hook
39 > pretxnchangegroup.acl = python:hgext.acl.hook
40 > [acl]
40 > [acl]
41 > sources = push
41 > sources = push
42 > [extensions]
42 > [extensions]
43 > f=`pwd`/fakegroups.py
43 > f=`pwd`/fakegroups.py
44 > EOF
44 > EOF
45 > }
45 > }
46
46
47 $ hg init a
47 $ hg init a
48 $ cd a
48 $ cd a
49 $ mkdir foo foo/Bar quux
49 $ mkdir foo foo/Bar quux
50 $ echo 'in foo' > foo/file.txt
50 $ echo 'in foo' > foo/file.txt
51 $ echo 'in foo/Bar' > foo/Bar/file.txt
51 $ echo 'in foo/Bar' > foo/Bar/file.txt
52 $ echo 'in quux' > quux/file.py
52 $ echo 'in quux' > quux/file.py
53 $ hg add -q
53 $ hg add -q
54 $ hg ci -m 'add files' -d '1000000 0'
54 $ hg ci -m 'add files' -d '1000000 0'
55 $ echo >> foo/file.txt
55 $ echo >> foo/file.txt
56 $ hg ci -m 'change foo/file' -d '1000001 0'
56 $ hg ci -m 'change foo/file' -d '1000001 0'
57 $ echo >> foo/Bar/file.txt
57 $ echo >> foo/Bar/file.txt
58 $ hg ci -m 'change foo/Bar/file' -d '1000002 0'
58 $ hg ci -m 'change foo/Bar/file' -d '1000002 0'
59 $ echo >> quux/file.py
59 $ echo >> quux/file.py
60 $ hg ci -m 'change quux/file' -d '1000003 0'
60 $ hg ci -m 'change quux/file' -d '1000003 0'
61 $ hg tip --quiet
61 $ hg tip --quiet
62 3:911600dab2ae
62 3:911600dab2ae
63
63
64 $ cd ..
64 $ cd ..
65 $ hg clone -r 0 a b
65 $ hg clone -r 0 a b
66 adding changesets
66 adding changesets
67 adding manifests
67 adding manifests
68 adding file changes
68 adding file changes
69 added 1 changesets with 3 changes to 3 files
69 added 1 changesets with 3 changes to 3 files
70 updating to branch default
70 updating to branch default
71 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
71 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
72
72
73 $ echo '[extensions]' >> $HGRCPATH
73 $ echo '[extensions]' >> $HGRCPATH
74 $ echo 'acl =' >> $HGRCPATH
74 $ echo 'acl =' >> $HGRCPATH
75
75
76 $ config=b/.hg/hgrc
76 $ config=b/.hg/hgrc
77
77
78 Extension disabled for lack of a hook
78 Extension disabled for lack of a hook
79
79
80 $ do_push fred
80 $ do_push fred
81 Pushing as user fred
81 Pushing as user fred
82 hgrc = """
82 hgrc = """
83 """
83 """
84 pushing to ../b
84 pushing to ../b
85 searching for changes
85 searching for changes
86 common changesets up to 6675d58eff77
87 3 changesets found
86 3 changesets found
88 list of changesets:
87 list of changesets:
89 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
88 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
90 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
89 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
91 911600dab2ae7a9baff75958b84fe606851ce955
90 911600dab2ae7a9baff75958b84fe606851ce955
92 adding changesets
91 adding changesets
93 bundling: 1 changesets
92 bundling: 1 changesets
94 bundling: 2 changesets
93 bundling: 2 changesets
95 bundling: 3 changesets
94 bundling: 3 changesets
96 bundling: 1/3 manifests (33.33%)
95 bundling: 1/3 manifests (33.33%)
97 bundling: 2/3 manifests (66.67%)
96 bundling: 2/3 manifests (66.67%)
98 bundling: 3/3 manifests (100.00%)
97 bundling: 3/3 manifests (100.00%)
99 bundling: foo/Bar/file.txt 0/3 files (0.00%)
98 bundling: foo/Bar/file.txt 0/3 files (0.00%)
100 bundling: foo/file.txt 1/3 files (33.33%)
99 bundling: foo/file.txt 1/3 files (33.33%)
101 bundling: quux/file.py 2/3 files (66.67%)
100 bundling: quux/file.py 2/3 files (66.67%)
102 changesets: 1 chunks
101 changesets: 1 chunks
103 add changeset ef1ea85a6374
102 add changeset ef1ea85a6374
104 changesets: 2 chunks
103 changesets: 2 chunks
105 add changeset f9cafe1212c8
104 add changeset f9cafe1212c8
106 changesets: 3 chunks
105 changesets: 3 chunks
107 add changeset 911600dab2ae
106 add changeset 911600dab2ae
108 adding manifests
107 adding manifests
109 manifests: 1/3 chunks (33.33%)
108 manifests: 1/3 chunks (33.33%)
110 manifests: 2/3 chunks (66.67%)
109 manifests: 2/3 chunks (66.67%)
111 manifests: 3/3 chunks (100.00%)
110 manifests: 3/3 chunks (100.00%)
112 adding file changes
111 adding file changes
113 adding foo/Bar/file.txt revisions
112 adding foo/Bar/file.txt revisions
114 files: 1/3 chunks (33.33%)
113 files: 1/3 chunks (33.33%)
115 adding foo/file.txt revisions
114 adding foo/file.txt revisions
116 files: 2/3 chunks (66.67%)
115 files: 2/3 chunks (66.67%)
117 adding quux/file.py revisions
116 adding quux/file.py revisions
118 files: 3/3 chunks (100.00%)
117 files: 3/3 chunks (100.00%)
119 added 3 changesets with 3 changes to 3 files
118 added 3 changesets with 3 changes to 3 files
120 updating the branch cache
119 updating the branch cache
121 checking for updated bookmarks
120 checking for updated bookmarks
122 repository tip rolled back to revision 0 (undo push)
121 repository tip rolled back to revision 0 (undo push)
123 working directory now based on revision 0
122 working directory now based on revision 0
124 0:6675d58eff77
123 0:6675d58eff77
125
124
126
125
127 $ echo '[hooks]' >> $config
126 $ echo '[hooks]' >> $config
128 $ echo 'pretxnchangegroup.acl = python:hgext.acl.hook' >> $config
127 $ echo 'pretxnchangegroup.acl = python:hgext.acl.hook' >> $config
129
128
130 Extension disabled for lack of acl.sources
129 Extension disabled for lack of acl.sources
131
130
132 $ do_push fred
131 $ do_push fred
133 Pushing as user fred
132 Pushing as user fred
134 hgrc = """
133 hgrc = """
135 [hooks]
134 [hooks]
136 pretxnchangegroup.acl = python:hgext.acl.hook
135 pretxnchangegroup.acl = python:hgext.acl.hook
137 """
136 """
138 pushing to ../b
137 pushing to ../b
139 searching for changes
138 searching for changes
140 common changesets up to 6675d58eff77
141 invalidating branch cache (tip differs)
139 invalidating branch cache (tip differs)
142 3 changesets found
140 3 changesets found
143 list of changesets:
141 list of changesets:
144 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
142 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
145 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
143 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
146 911600dab2ae7a9baff75958b84fe606851ce955
144 911600dab2ae7a9baff75958b84fe606851ce955
147 adding changesets
145 adding changesets
148 bundling: 1 changesets
146 bundling: 1 changesets
149 bundling: 2 changesets
147 bundling: 2 changesets
150 bundling: 3 changesets
148 bundling: 3 changesets
151 bundling: 1/3 manifests (33.33%)
149 bundling: 1/3 manifests (33.33%)
152 bundling: 2/3 manifests (66.67%)
150 bundling: 2/3 manifests (66.67%)
153 bundling: 3/3 manifests (100.00%)
151 bundling: 3/3 manifests (100.00%)
154 bundling: foo/Bar/file.txt 0/3 files (0.00%)
152 bundling: foo/Bar/file.txt 0/3 files (0.00%)
155 bundling: foo/file.txt 1/3 files (33.33%)
153 bundling: foo/file.txt 1/3 files (33.33%)
156 bundling: quux/file.py 2/3 files (66.67%)
154 bundling: quux/file.py 2/3 files (66.67%)
157 changesets: 1 chunks
155 changesets: 1 chunks
158 add changeset ef1ea85a6374
156 add changeset ef1ea85a6374
159 changesets: 2 chunks
157 changesets: 2 chunks
160 add changeset f9cafe1212c8
158 add changeset f9cafe1212c8
161 changesets: 3 chunks
159 changesets: 3 chunks
162 add changeset 911600dab2ae
160 add changeset 911600dab2ae
163 adding manifests
161 adding manifests
164 manifests: 1/3 chunks (33.33%)
162 manifests: 1/3 chunks (33.33%)
165 manifests: 2/3 chunks (66.67%)
163 manifests: 2/3 chunks (66.67%)
166 manifests: 3/3 chunks (100.00%)
164 manifests: 3/3 chunks (100.00%)
167 adding file changes
165 adding file changes
168 adding foo/Bar/file.txt revisions
166 adding foo/Bar/file.txt revisions
169 files: 1/3 chunks (33.33%)
167 files: 1/3 chunks (33.33%)
170 adding foo/file.txt revisions
168 adding foo/file.txt revisions
171 files: 2/3 chunks (66.67%)
169 files: 2/3 chunks (66.67%)
172 adding quux/file.py revisions
170 adding quux/file.py revisions
173 files: 3/3 chunks (100.00%)
171 files: 3/3 chunks (100.00%)
174 added 3 changesets with 3 changes to 3 files
172 added 3 changesets with 3 changes to 3 files
175 calling hook pretxnchangegroup.acl: hgext.acl.hook
173 calling hook pretxnchangegroup.acl: hgext.acl.hook
176 acl: changes have source "push" - skipping
174 acl: changes have source "push" - skipping
177 updating the branch cache
175 updating the branch cache
178 checking for updated bookmarks
176 checking for updated bookmarks
179 repository tip rolled back to revision 0 (undo push)
177 repository tip rolled back to revision 0 (undo push)
180 working directory now based on revision 0
178 working directory now based on revision 0
181 0:6675d58eff77
179 0:6675d58eff77
182
180
183
181
184 No [acl.allow]/[acl.deny]
182 No [acl.allow]/[acl.deny]
185
183
186 $ echo '[acl]' >> $config
184 $ echo '[acl]' >> $config
187 $ echo 'sources = push' >> $config
185 $ echo 'sources = push' >> $config
188 $ do_push fred
186 $ do_push fred
189 Pushing as user fred
187 Pushing as user fred
190 hgrc = """
188 hgrc = """
191 [hooks]
189 [hooks]
192 pretxnchangegroup.acl = python:hgext.acl.hook
190 pretxnchangegroup.acl = python:hgext.acl.hook
193 [acl]
191 [acl]
194 sources = push
192 sources = push
195 """
193 """
196 pushing to ../b
194 pushing to ../b
197 searching for changes
195 searching for changes
198 common changesets up to 6675d58eff77
199 invalidating branch cache (tip differs)
196 invalidating branch cache (tip differs)
200 3 changesets found
197 3 changesets found
201 list of changesets:
198 list of changesets:
202 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
199 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
203 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
200 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
204 911600dab2ae7a9baff75958b84fe606851ce955
201 911600dab2ae7a9baff75958b84fe606851ce955
205 adding changesets
202 adding changesets
206 bundling: 1 changesets
203 bundling: 1 changesets
207 bundling: 2 changesets
204 bundling: 2 changesets
208 bundling: 3 changesets
205 bundling: 3 changesets
209 bundling: 1/3 manifests (33.33%)
206 bundling: 1/3 manifests (33.33%)
210 bundling: 2/3 manifests (66.67%)
207 bundling: 2/3 manifests (66.67%)
211 bundling: 3/3 manifests (100.00%)
208 bundling: 3/3 manifests (100.00%)
212 bundling: foo/Bar/file.txt 0/3 files (0.00%)
209 bundling: foo/Bar/file.txt 0/3 files (0.00%)
213 bundling: foo/file.txt 1/3 files (33.33%)
210 bundling: foo/file.txt 1/3 files (33.33%)
214 bundling: quux/file.py 2/3 files (66.67%)
211 bundling: quux/file.py 2/3 files (66.67%)
215 changesets: 1 chunks
212 changesets: 1 chunks
216 add changeset ef1ea85a6374
213 add changeset ef1ea85a6374
217 changesets: 2 chunks
214 changesets: 2 chunks
218 add changeset f9cafe1212c8
215 add changeset f9cafe1212c8
219 changesets: 3 chunks
216 changesets: 3 chunks
220 add changeset 911600dab2ae
217 add changeset 911600dab2ae
221 adding manifests
218 adding manifests
222 manifests: 1/3 chunks (33.33%)
219 manifests: 1/3 chunks (33.33%)
223 manifests: 2/3 chunks (66.67%)
220 manifests: 2/3 chunks (66.67%)
224 manifests: 3/3 chunks (100.00%)
221 manifests: 3/3 chunks (100.00%)
225 adding file changes
222 adding file changes
226 adding foo/Bar/file.txt revisions
223 adding foo/Bar/file.txt revisions
227 files: 1/3 chunks (33.33%)
224 files: 1/3 chunks (33.33%)
228 adding foo/file.txt revisions
225 adding foo/file.txt revisions
229 files: 2/3 chunks (66.67%)
226 files: 2/3 chunks (66.67%)
230 adding quux/file.py revisions
227 adding quux/file.py revisions
231 files: 3/3 chunks (100.00%)
228 files: 3/3 chunks (100.00%)
232 added 3 changesets with 3 changes to 3 files
229 added 3 changesets with 3 changes to 3 files
233 calling hook pretxnchangegroup.acl: hgext.acl.hook
230 calling hook pretxnchangegroup.acl: hgext.acl.hook
234 acl: acl.allow.branches not enabled
231 acl: acl.allow.branches not enabled
235 acl: acl.deny.branches not enabled
232 acl: acl.deny.branches not enabled
236 acl: acl.allow not enabled
233 acl: acl.allow not enabled
237 acl: acl.deny not enabled
234 acl: acl.deny not enabled
238 acl: branch access granted: "ef1ea85a6374" on branch "default"
235 acl: branch access granted: "ef1ea85a6374" on branch "default"
239 acl: allowing changeset ef1ea85a6374
236 acl: allowing changeset ef1ea85a6374
240 acl: branch access granted: "f9cafe1212c8" on branch "default"
237 acl: branch access granted: "f9cafe1212c8" on branch "default"
241 acl: allowing changeset f9cafe1212c8
238 acl: allowing changeset f9cafe1212c8
242 acl: branch access granted: "911600dab2ae" on branch "default"
239 acl: branch access granted: "911600dab2ae" on branch "default"
243 acl: allowing changeset 911600dab2ae
240 acl: allowing changeset 911600dab2ae
244 updating the branch cache
241 updating the branch cache
245 checking for updated bookmarks
242 checking for updated bookmarks
246 repository tip rolled back to revision 0 (undo push)
243 repository tip rolled back to revision 0 (undo push)
247 working directory now based on revision 0
244 working directory now based on revision 0
248 0:6675d58eff77
245 0:6675d58eff77
249
246
250
247
251 Empty [acl.allow]
248 Empty [acl.allow]
252
249
253 $ echo '[acl.allow]' >> $config
250 $ echo '[acl.allow]' >> $config
254 $ do_push fred
251 $ do_push fred
255 Pushing as user fred
252 Pushing as user fred
256 hgrc = """
253 hgrc = """
257 [hooks]
254 [hooks]
258 pretxnchangegroup.acl = python:hgext.acl.hook
255 pretxnchangegroup.acl = python:hgext.acl.hook
259 [acl]
256 [acl]
260 sources = push
257 sources = push
261 [acl.allow]
258 [acl.allow]
262 """
259 """
263 pushing to ../b
260 pushing to ../b
264 searching for changes
261 searching for changes
265 common changesets up to 6675d58eff77
266 invalidating branch cache (tip differs)
262 invalidating branch cache (tip differs)
267 3 changesets found
263 3 changesets found
268 list of changesets:
264 list of changesets:
269 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
265 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
270 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
266 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
271 911600dab2ae7a9baff75958b84fe606851ce955
267 911600dab2ae7a9baff75958b84fe606851ce955
272 adding changesets
268 adding changesets
273 bundling: 1 changesets
269 bundling: 1 changesets
274 bundling: 2 changesets
270 bundling: 2 changesets
275 bundling: 3 changesets
271 bundling: 3 changesets
276 bundling: 1/3 manifests (33.33%)
272 bundling: 1/3 manifests (33.33%)
277 bundling: 2/3 manifests (66.67%)
273 bundling: 2/3 manifests (66.67%)
278 bundling: 3/3 manifests (100.00%)
274 bundling: 3/3 manifests (100.00%)
279 bundling: foo/Bar/file.txt 0/3 files (0.00%)
275 bundling: foo/Bar/file.txt 0/3 files (0.00%)
280 bundling: foo/file.txt 1/3 files (33.33%)
276 bundling: foo/file.txt 1/3 files (33.33%)
281 bundling: quux/file.py 2/3 files (66.67%)
277 bundling: quux/file.py 2/3 files (66.67%)
282 changesets: 1 chunks
278 changesets: 1 chunks
283 add changeset ef1ea85a6374
279 add changeset ef1ea85a6374
284 changesets: 2 chunks
280 changesets: 2 chunks
285 add changeset f9cafe1212c8
281 add changeset f9cafe1212c8
286 changesets: 3 chunks
282 changesets: 3 chunks
287 add changeset 911600dab2ae
283 add changeset 911600dab2ae
288 adding manifests
284 adding manifests
289 manifests: 1/3 chunks (33.33%)
285 manifests: 1/3 chunks (33.33%)
290 manifests: 2/3 chunks (66.67%)
286 manifests: 2/3 chunks (66.67%)
291 manifests: 3/3 chunks (100.00%)
287 manifests: 3/3 chunks (100.00%)
292 adding file changes
288 adding file changes
293 adding foo/Bar/file.txt revisions
289 adding foo/Bar/file.txt revisions
294 files: 1/3 chunks (33.33%)
290 files: 1/3 chunks (33.33%)
295 adding foo/file.txt revisions
291 adding foo/file.txt revisions
296 files: 2/3 chunks (66.67%)
292 files: 2/3 chunks (66.67%)
297 adding quux/file.py revisions
293 adding quux/file.py revisions
298 files: 3/3 chunks (100.00%)
294 files: 3/3 chunks (100.00%)
299 added 3 changesets with 3 changes to 3 files
295 added 3 changesets with 3 changes to 3 files
300 calling hook pretxnchangegroup.acl: hgext.acl.hook
296 calling hook pretxnchangegroup.acl: hgext.acl.hook
301 acl: acl.allow.branches not enabled
297 acl: acl.allow.branches not enabled
302 acl: acl.deny.branches not enabled
298 acl: acl.deny.branches not enabled
303 acl: acl.allow enabled, 0 entries for user fred
299 acl: acl.allow enabled, 0 entries for user fred
304 acl: acl.deny not enabled
300 acl: acl.deny not enabled
305 acl: branch access granted: "ef1ea85a6374" on branch "default"
301 acl: branch access granted: "ef1ea85a6374" on branch "default"
306 acl: user fred not allowed on foo/file.txt
302 acl: user fred not allowed on foo/file.txt
307 error: pretxnchangegroup.acl hook failed: acl: access denied for changeset ef1ea85a6374
303 error: pretxnchangegroup.acl hook failed: acl: access denied for changeset ef1ea85a6374
308 transaction abort!
304 transaction abort!
309 rollback completed
305 rollback completed
310 abort: acl: access denied for changeset ef1ea85a6374
306 abort: acl: access denied for changeset ef1ea85a6374
311 no rollback information available
307 no rollback information available
312 0:6675d58eff77
308 0:6675d58eff77
313
309
314
310
315 fred is allowed inside foo/
311 fred is allowed inside foo/
316
312
317 $ echo 'foo/** = fred' >> $config
313 $ echo 'foo/** = fred' >> $config
318 $ do_push fred
314 $ do_push fred
319 Pushing as user fred
315 Pushing as user fred
320 hgrc = """
316 hgrc = """
321 [hooks]
317 [hooks]
322 pretxnchangegroup.acl = python:hgext.acl.hook
318 pretxnchangegroup.acl = python:hgext.acl.hook
323 [acl]
319 [acl]
324 sources = push
320 sources = push
325 [acl.allow]
321 [acl.allow]
326 foo/** = fred
322 foo/** = fred
327 """
323 """
328 pushing to ../b
324 pushing to ../b
329 searching for changes
325 searching for changes
330 common changesets up to 6675d58eff77
331 3 changesets found
326 3 changesets found
332 list of changesets:
327 list of changesets:
333 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
328 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
334 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
329 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
335 911600dab2ae7a9baff75958b84fe606851ce955
330 911600dab2ae7a9baff75958b84fe606851ce955
336 adding changesets
331 adding changesets
337 bundling: 1 changesets
332 bundling: 1 changesets
338 bundling: 2 changesets
333 bundling: 2 changesets
339 bundling: 3 changesets
334 bundling: 3 changesets
340 bundling: 1/3 manifests (33.33%)
335 bundling: 1/3 manifests (33.33%)
341 bundling: 2/3 manifests (66.67%)
336 bundling: 2/3 manifests (66.67%)
342 bundling: 3/3 manifests (100.00%)
337 bundling: 3/3 manifests (100.00%)
343 bundling: foo/Bar/file.txt 0/3 files (0.00%)
338 bundling: foo/Bar/file.txt 0/3 files (0.00%)
344 bundling: foo/file.txt 1/3 files (33.33%)
339 bundling: foo/file.txt 1/3 files (33.33%)
345 bundling: quux/file.py 2/3 files (66.67%)
340 bundling: quux/file.py 2/3 files (66.67%)
346 changesets: 1 chunks
341 changesets: 1 chunks
347 add changeset ef1ea85a6374
342 add changeset ef1ea85a6374
348 changesets: 2 chunks
343 changesets: 2 chunks
349 add changeset f9cafe1212c8
344 add changeset f9cafe1212c8
350 changesets: 3 chunks
345 changesets: 3 chunks
351 add changeset 911600dab2ae
346 add changeset 911600dab2ae
352 adding manifests
347 adding manifests
353 manifests: 1/3 chunks (33.33%)
348 manifests: 1/3 chunks (33.33%)
354 manifests: 2/3 chunks (66.67%)
349 manifests: 2/3 chunks (66.67%)
355 manifests: 3/3 chunks (100.00%)
350 manifests: 3/3 chunks (100.00%)
356 adding file changes
351 adding file changes
357 adding foo/Bar/file.txt revisions
352 adding foo/Bar/file.txt revisions
358 files: 1/3 chunks (33.33%)
353 files: 1/3 chunks (33.33%)
359 adding foo/file.txt revisions
354 adding foo/file.txt revisions
360 files: 2/3 chunks (66.67%)
355 files: 2/3 chunks (66.67%)
361 adding quux/file.py revisions
356 adding quux/file.py revisions
362 files: 3/3 chunks (100.00%)
357 files: 3/3 chunks (100.00%)
363 added 3 changesets with 3 changes to 3 files
358 added 3 changesets with 3 changes to 3 files
364 calling hook pretxnchangegroup.acl: hgext.acl.hook
359 calling hook pretxnchangegroup.acl: hgext.acl.hook
365 acl: acl.allow.branches not enabled
360 acl: acl.allow.branches not enabled
366 acl: acl.deny.branches not enabled
361 acl: acl.deny.branches not enabled
367 acl: acl.allow enabled, 1 entries for user fred
362 acl: acl.allow enabled, 1 entries for user fred
368 acl: acl.deny not enabled
363 acl: acl.deny not enabled
369 acl: branch access granted: "ef1ea85a6374" on branch "default"
364 acl: branch access granted: "ef1ea85a6374" on branch "default"
370 acl: allowing changeset ef1ea85a6374
365 acl: allowing changeset ef1ea85a6374
371 acl: branch access granted: "f9cafe1212c8" on branch "default"
366 acl: branch access granted: "f9cafe1212c8" on branch "default"
372 acl: allowing changeset f9cafe1212c8
367 acl: allowing changeset f9cafe1212c8
373 acl: branch access granted: "911600dab2ae" on branch "default"
368 acl: branch access granted: "911600dab2ae" on branch "default"
374 acl: user fred not allowed on quux/file.py
369 acl: user fred not allowed on quux/file.py
375 error: pretxnchangegroup.acl hook failed: acl: access denied for changeset 911600dab2ae
370 error: pretxnchangegroup.acl hook failed: acl: access denied for changeset 911600dab2ae
376 transaction abort!
371 transaction abort!
377 rollback completed
372 rollback completed
378 abort: acl: access denied for changeset 911600dab2ae
373 abort: acl: access denied for changeset 911600dab2ae
379 no rollback information available
374 no rollback information available
380 0:6675d58eff77
375 0:6675d58eff77
381
376
382
377
383 Empty [acl.deny]
378 Empty [acl.deny]
384
379
385 $ echo '[acl.deny]' >> $config
380 $ echo '[acl.deny]' >> $config
386 $ do_push barney
381 $ do_push barney
387 Pushing as user barney
382 Pushing as user barney
388 hgrc = """
383 hgrc = """
389 [hooks]
384 [hooks]
390 pretxnchangegroup.acl = python:hgext.acl.hook
385 pretxnchangegroup.acl = python:hgext.acl.hook
391 [acl]
386 [acl]
392 sources = push
387 sources = push
393 [acl.allow]
388 [acl.allow]
394 foo/** = fred
389 foo/** = fred
395 [acl.deny]
390 [acl.deny]
396 """
391 """
397 pushing to ../b
392 pushing to ../b
398 searching for changes
393 searching for changes
399 common changesets up to 6675d58eff77
400 3 changesets found
394 3 changesets found
401 list of changesets:
395 list of changesets:
402 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
396 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
403 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
397 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
404 911600dab2ae7a9baff75958b84fe606851ce955
398 911600dab2ae7a9baff75958b84fe606851ce955
405 adding changesets
399 adding changesets
406 bundling: 1 changesets
400 bundling: 1 changesets
407 bundling: 2 changesets
401 bundling: 2 changesets
408 bundling: 3 changesets
402 bundling: 3 changesets
409 bundling: 1/3 manifests (33.33%)
403 bundling: 1/3 manifests (33.33%)
410 bundling: 2/3 manifests (66.67%)
404 bundling: 2/3 manifests (66.67%)
411 bundling: 3/3 manifests (100.00%)
405 bundling: 3/3 manifests (100.00%)
412 bundling: foo/Bar/file.txt 0/3 files (0.00%)
406 bundling: foo/Bar/file.txt 0/3 files (0.00%)
413 bundling: foo/file.txt 1/3 files (33.33%)
407 bundling: foo/file.txt 1/3 files (33.33%)
414 bundling: quux/file.py 2/3 files (66.67%)
408 bundling: quux/file.py 2/3 files (66.67%)
415 changesets: 1 chunks
409 changesets: 1 chunks
416 add changeset ef1ea85a6374
410 add changeset ef1ea85a6374
417 changesets: 2 chunks
411 changesets: 2 chunks
418 add changeset f9cafe1212c8
412 add changeset f9cafe1212c8
419 changesets: 3 chunks
413 changesets: 3 chunks
420 add changeset 911600dab2ae
414 add changeset 911600dab2ae
421 adding manifests
415 adding manifests
422 manifests: 1/3 chunks (33.33%)
416 manifests: 1/3 chunks (33.33%)
423 manifests: 2/3 chunks (66.67%)
417 manifests: 2/3 chunks (66.67%)
424 manifests: 3/3 chunks (100.00%)
418 manifests: 3/3 chunks (100.00%)
425 adding file changes
419 adding file changes
426 adding foo/Bar/file.txt revisions
420 adding foo/Bar/file.txt revisions
427 files: 1/3 chunks (33.33%)
421 files: 1/3 chunks (33.33%)
428 adding foo/file.txt revisions
422 adding foo/file.txt revisions
429 files: 2/3 chunks (66.67%)
423 files: 2/3 chunks (66.67%)
430 adding quux/file.py revisions
424 adding quux/file.py revisions
431 files: 3/3 chunks (100.00%)
425 files: 3/3 chunks (100.00%)
432 added 3 changesets with 3 changes to 3 files
426 added 3 changesets with 3 changes to 3 files
433 calling hook pretxnchangegroup.acl: hgext.acl.hook
427 calling hook pretxnchangegroup.acl: hgext.acl.hook
434 acl: acl.allow.branches not enabled
428 acl: acl.allow.branches not enabled
435 acl: acl.deny.branches not enabled
429 acl: acl.deny.branches not enabled
436 acl: acl.allow enabled, 0 entries for user barney
430 acl: acl.allow enabled, 0 entries for user barney
437 acl: acl.deny enabled, 0 entries for user barney
431 acl: acl.deny enabled, 0 entries for user barney
438 acl: branch access granted: "ef1ea85a6374" on branch "default"
432 acl: branch access granted: "ef1ea85a6374" on branch "default"
439 acl: user barney not allowed on foo/file.txt
433 acl: user barney not allowed on foo/file.txt
440 error: pretxnchangegroup.acl hook failed: acl: access denied for changeset ef1ea85a6374
434 error: pretxnchangegroup.acl hook failed: acl: access denied for changeset ef1ea85a6374
441 transaction abort!
435 transaction abort!
442 rollback completed
436 rollback completed
443 abort: acl: access denied for changeset ef1ea85a6374
437 abort: acl: access denied for changeset ef1ea85a6374
444 no rollback information available
438 no rollback information available
445 0:6675d58eff77
439 0:6675d58eff77
446
440
447
441
448 fred is allowed inside foo/, but not foo/bar/ (case matters)
442 fred is allowed inside foo/, but not foo/bar/ (case matters)
449
443
450 $ echo 'foo/bar/** = fred' >> $config
444 $ echo 'foo/bar/** = fred' >> $config
451 $ do_push fred
445 $ do_push fred
452 Pushing as user fred
446 Pushing as user fred
453 hgrc = """
447 hgrc = """
454 [hooks]
448 [hooks]
455 pretxnchangegroup.acl = python:hgext.acl.hook
449 pretxnchangegroup.acl = python:hgext.acl.hook
456 [acl]
450 [acl]
457 sources = push
451 sources = push
458 [acl.allow]
452 [acl.allow]
459 foo/** = fred
453 foo/** = fred
460 [acl.deny]
454 [acl.deny]
461 foo/bar/** = fred
455 foo/bar/** = fred
462 """
456 """
463 pushing to ../b
457 pushing to ../b
464 searching for changes
458 searching for changes
465 common changesets up to 6675d58eff77
466 3 changesets found
459 3 changesets found
467 list of changesets:
460 list of changesets:
468 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
461 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
469 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
462 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
470 911600dab2ae7a9baff75958b84fe606851ce955
463 911600dab2ae7a9baff75958b84fe606851ce955
471 adding changesets
464 adding changesets
472 bundling: 1 changesets
465 bundling: 1 changesets
473 bundling: 2 changesets
466 bundling: 2 changesets
474 bundling: 3 changesets
467 bundling: 3 changesets
475 bundling: 1/3 manifests (33.33%)
468 bundling: 1/3 manifests (33.33%)
476 bundling: 2/3 manifests (66.67%)
469 bundling: 2/3 manifests (66.67%)
477 bundling: 3/3 manifests (100.00%)
470 bundling: 3/3 manifests (100.00%)
478 bundling: foo/Bar/file.txt 0/3 files (0.00%)
471 bundling: foo/Bar/file.txt 0/3 files (0.00%)
479 bundling: foo/file.txt 1/3 files (33.33%)
472 bundling: foo/file.txt 1/3 files (33.33%)
480 bundling: quux/file.py 2/3 files (66.67%)
473 bundling: quux/file.py 2/3 files (66.67%)
481 changesets: 1 chunks
474 changesets: 1 chunks
482 add changeset ef1ea85a6374
475 add changeset ef1ea85a6374
483 changesets: 2 chunks
476 changesets: 2 chunks
484 add changeset f9cafe1212c8
477 add changeset f9cafe1212c8
485 changesets: 3 chunks
478 changesets: 3 chunks
486 add changeset 911600dab2ae
479 add changeset 911600dab2ae
487 adding manifests
480 adding manifests
488 manifests: 1/3 chunks (33.33%)
481 manifests: 1/3 chunks (33.33%)
489 manifests: 2/3 chunks (66.67%)
482 manifests: 2/3 chunks (66.67%)
490 manifests: 3/3 chunks (100.00%)
483 manifests: 3/3 chunks (100.00%)
491 adding file changes
484 adding file changes
492 adding foo/Bar/file.txt revisions
485 adding foo/Bar/file.txt revisions
493 files: 1/3 chunks (33.33%)
486 files: 1/3 chunks (33.33%)
494 adding foo/file.txt revisions
487 adding foo/file.txt revisions
495 files: 2/3 chunks (66.67%)
488 files: 2/3 chunks (66.67%)
496 adding quux/file.py revisions
489 adding quux/file.py revisions
497 files: 3/3 chunks (100.00%)
490 files: 3/3 chunks (100.00%)
498 added 3 changesets with 3 changes to 3 files
491 added 3 changesets with 3 changes to 3 files
499 calling hook pretxnchangegroup.acl: hgext.acl.hook
492 calling hook pretxnchangegroup.acl: hgext.acl.hook
500 acl: acl.allow.branches not enabled
493 acl: acl.allow.branches not enabled
501 acl: acl.deny.branches not enabled
494 acl: acl.deny.branches not enabled
502 acl: acl.allow enabled, 1 entries for user fred
495 acl: acl.allow enabled, 1 entries for user fred
503 acl: acl.deny enabled, 1 entries for user fred
496 acl: acl.deny enabled, 1 entries for user fred
504 acl: branch access granted: "ef1ea85a6374" on branch "default"
497 acl: branch access granted: "ef1ea85a6374" on branch "default"
505 acl: allowing changeset ef1ea85a6374
498 acl: allowing changeset ef1ea85a6374
506 acl: branch access granted: "f9cafe1212c8" on branch "default"
499 acl: branch access granted: "f9cafe1212c8" on branch "default"
507 acl: allowing changeset f9cafe1212c8
500 acl: allowing changeset f9cafe1212c8
508 acl: branch access granted: "911600dab2ae" on branch "default"
501 acl: branch access granted: "911600dab2ae" on branch "default"
509 acl: user fred not allowed on quux/file.py
502 acl: user fred not allowed on quux/file.py
510 error: pretxnchangegroup.acl hook failed: acl: access denied for changeset 911600dab2ae
503 error: pretxnchangegroup.acl hook failed: acl: access denied for changeset 911600dab2ae
511 transaction abort!
504 transaction abort!
512 rollback completed
505 rollback completed
513 abort: acl: access denied for changeset 911600dab2ae
506 abort: acl: access denied for changeset 911600dab2ae
514 no rollback information available
507 no rollback information available
515 0:6675d58eff77
508 0:6675d58eff77
516
509
517
510
518 fred is allowed inside foo/, but not foo/Bar/
511 fred is allowed inside foo/, but not foo/Bar/
519
512
520 $ echo 'foo/Bar/** = fred' >> $config
513 $ echo 'foo/Bar/** = fred' >> $config
521 $ do_push fred
514 $ do_push fred
522 Pushing as user fred
515 Pushing as user fred
523 hgrc = """
516 hgrc = """
524 [hooks]
517 [hooks]
525 pretxnchangegroup.acl = python:hgext.acl.hook
518 pretxnchangegroup.acl = python:hgext.acl.hook
526 [acl]
519 [acl]
527 sources = push
520 sources = push
528 [acl.allow]
521 [acl.allow]
529 foo/** = fred
522 foo/** = fred
530 [acl.deny]
523 [acl.deny]
531 foo/bar/** = fred
524 foo/bar/** = fred
532 foo/Bar/** = fred
525 foo/Bar/** = fred
533 """
526 """
534 pushing to ../b
527 pushing to ../b
535 searching for changes
528 searching for changes
536 common changesets up to 6675d58eff77
537 3 changesets found
529 3 changesets found
538 list of changesets:
530 list of changesets:
539 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
531 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
540 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
532 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
541 911600dab2ae7a9baff75958b84fe606851ce955
533 911600dab2ae7a9baff75958b84fe606851ce955
542 adding changesets
534 adding changesets
543 bundling: 1 changesets
535 bundling: 1 changesets
544 bundling: 2 changesets
536 bundling: 2 changesets
545 bundling: 3 changesets
537 bundling: 3 changesets
546 bundling: 1/3 manifests (33.33%)
538 bundling: 1/3 manifests (33.33%)
547 bundling: 2/3 manifests (66.67%)
539 bundling: 2/3 manifests (66.67%)
548 bundling: 3/3 manifests (100.00%)
540 bundling: 3/3 manifests (100.00%)
549 bundling: foo/Bar/file.txt 0/3 files (0.00%)
541 bundling: foo/Bar/file.txt 0/3 files (0.00%)
550 bundling: foo/file.txt 1/3 files (33.33%)
542 bundling: foo/file.txt 1/3 files (33.33%)
551 bundling: quux/file.py 2/3 files (66.67%)
543 bundling: quux/file.py 2/3 files (66.67%)
552 changesets: 1 chunks
544 changesets: 1 chunks
553 add changeset ef1ea85a6374
545 add changeset ef1ea85a6374
554 changesets: 2 chunks
546 changesets: 2 chunks
555 add changeset f9cafe1212c8
547 add changeset f9cafe1212c8
556 changesets: 3 chunks
548 changesets: 3 chunks
557 add changeset 911600dab2ae
549 add changeset 911600dab2ae
558 adding manifests
550 adding manifests
559 manifests: 1/3 chunks (33.33%)
551 manifests: 1/3 chunks (33.33%)
560 manifests: 2/3 chunks (66.67%)
552 manifests: 2/3 chunks (66.67%)
561 manifests: 3/3 chunks (100.00%)
553 manifests: 3/3 chunks (100.00%)
562 adding file changes
554 adding file changes
563 adding foo/Bar/file.txt revisions
555 adding foo/Bar/file.txt revisions
564 files: 1/3 chunks (33.33%)
556 files: 1/3 chunks (33.33%)
565 adding foo/file.txt revisions
557 adding foo/file.txt revisions
566 files: 2/3 chunks (66.67%)
558 files: 2/3 chunks (66.67%)
567 adding quux/file.py revisions
559 adding quux/file.py revisions
568 files: 3/3 chunks (100.00%)
560 files: 3/3 chunks (100.00%)
569 added 3 changesets with 3 changes to 3 files
561 added 3 changesets with 3 changes to 3 files
570 calling hook pretxnchangegroup.acl: hgext.acl.hook
562 calling hook pretxnchangegroup.acl: hgext.acl.hook
571 acl: acl.allow.branches not enabled
563 acl: acl.allow.branches not enabled
572 acl: acl.deny.branches not enabled
564 acl: acl.deny.branches not enabled
573 acl: acl.allow enabled, 1 entries for user fred
565 acl: acl.allow enabled, 1 entries for user fred
574 acl: acl.deny enabled, 2 entries for user fred
566 acl: acl.deny enabled, 2 entries for user fred
575 acl: branch access granted: "ef1ea85a6374" on branch "default"
567 acl: branch access granted: "ef1ea85a6374" on branch "default"
576 acl: allowing changeset ef1ea85a6374
568 acl: allowing changeset ef1ea85a6374
577 acl: branch access granted: "f9cafe1212c8" on branch "default"
569 acl: branch access granted: "f9cafe1212c8" on branch "default"
578 acl: user fred denied on foo/Bar/file.txt
570 acl: user fred denied on foo/Bar/file.txt
579 error: pretxnchangegroup.acl hook failed: acl: access denied for changeset f9cafe1212c8
571 error: pretxnchangegroup.acl hook failed: acl: access denied for changeset f9cafe1212c8
580 transaction abort!
572 transaction abort!
581 rollback completed
573 rollback completed
582 abort: acl: access denied for changeset f9cafe1212c8
574 abort: acl: access denied for changeset f9cafe1212c8
583 no rollback information available
575 no rollback information available
584 0:6675d58eff77
576 0:6675d58eff77
585
577
586
578
587 $ echo 'barney is not mentioned => not allowed anywhere'
579 $ echo 'barney is not mentioned => not allowed anywhere'
588 barney is not mentioned => not allowed anywhere
580 barney is not mentioned => not allowed anywhere
589 $ do_push barney
581 $ do_push barney
590 Pushing as user barney
582 Pushing as user barney
591 hgrc = """
583 hgrc = """
592 [hooks]
584 [hooks]
593 pretxnchangegroup.acl = python:hgext.acl.hook
585 pretxnchangegroup.acl = python:hgext.acl.hook
594 [acl]
586 [acl]
595 sources = push
587 sources = push
596 [acl.allow]
588 [acl.allow]
597 foo/** = fred
589 foo/** = fred
598 [acl.deny]
590 [acl.deny]
599 foo/bar/** = fred
591 foo/bar/** = fred
600 foo/Bar/** = fred
592 foo/Bar/** = fred
601 """
593 """
602 pushing to ../b
594 pushing to ../b
603 searching for changes
595 searching for changes
604 common changesets up to 6675d58eff77
605 3 changesets found
596 3 changesets found
606 list of changesets:
597 list of changesets:
607 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
598 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
608 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
599 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
609 911600dab2ae7a9baff75958b84fe606851ce955
600 911600dab2ae7a9baff75958b84fe606851ce955
610 adding changesets
601 adding changesets
611 bundling: 1 changesets
602 bundling: 1 changesets
612 bundling: 2 changesets
603 bundling: 2 changesets
613 bundling: 3 changesets
604 bundling: 3 changesets
614 bundling: 1/3 manifests (33.33%)
605 bundling: 1/3 manifests (33.33%)
615 bundling: 2/3 manifests (66.67%)
606 bundling: 2/3 manifests (66.67%)
616 bundling: 3/3 manifests (100.00%)
607 bundling: 3/3 manifests (100.00%)
617 bundling: foo/Bar/file.txt 0/3 files (0.00%)
608 bundling: foo/Bar/file.txt 0/3 files (0.00%)
618 bundling: foo/file.txt 1/3 files (33.33%)
609 bundling: foo/file.txt 1/3 files (33.33%)
619 bundling: quux/file.py 2/3 files (66.67%)
610 bundling: quux/file.py 2/3 files (66.67%)
620 changesets: 1 chunks
611 changesets: 1 chunks
621 add changeset ef1ea85a6374
612 add changeset ef1ea85a6374
622 changesets: 2 chunks
613 changesets: 2 chunks
623 add changeset f9cafe1212c8
614 add changeset f9cafe1212c8
624 changesets: 3 chunks
615 changesets: 3 chunks
625 add changeset 911600dab2ae
616 add changeset 911600dab2ae
626 adding manifests
617 adding manifests
627 manifests: 1/3 chunks (33.33%)
618 manifests: 1/3 chunks (33.33%)
628 manifests: 2/3 chunks (66.67%)
619 manifests: 2/3 chunks (66.67%)
629 manifests: 3/3 chunks (100.00%)
620 manifests: 3/3 chunks (100.00%)
630 adding file changes
621 adding file changes
631 adding foo/Bar/file.txt revisions
622 adding foo/Bar/file.txt revisions
632 files: 1/3 chunks (33.33%)
623 files: 1/3 chunks (33.33%)
633 adding foo/file.txt revisions
624 adding foo/file.txt revisions
634 files: 2/3 chunks (66.67%)
625 files: 2/3 chunks (66.67%)
635 adding quux/file.py revisions
626 adding quux/file.py revisions
636 files: 3/3 chunks (100.00%)
627 files: 3/3 chunks (100.00%)
637 added 3 changesets with 3 changes to 3 files
628 added 3 changesets with 3 changes to 3 files
638 calling hook pretxnchangegroup.acl: hgext.acl.hook
629 calling hook pretxnchangegroup.acl: hgext.acl.hook
639 acl: acl.allow.branches not enabled
630 acl: acl.allow.branches not enabled
640 acl: acl.deny.branches not enabled
631 acl: acl.deny.branches not enabled
641 acl: acl.allow enabled, 0 entries for user barney
632 acl: acl.allow enabled, 0 entries for user barney
642 acl: acl.deny enabled, 0 entries for user barney
633 acl: acl.deny enabled, 0 entries for user barney
643 acl: branch access granted: "ef1ea85a6374" on branch "default"
634 acl: branch access granted: "ef1ea85a6374" on branch "default"
644 acl: user barney not allowed on foo/file.txt
635 acl: user barney not allowed on foo/file.txt
645 error: pretxnchangegroup.acl hook failed: acl: access denied for changeset ef1ea85a6374
636 error: pretxnchangegroup.acl hook failed: acl: access denied for changeset ef1ea85a6374
646 transaction abort!
637 transaction abort!
647 rollback completed
638 rollback completed
648 abort: acl: access denied for changeset ef1ea85a6374
639 abort: acl: access denied for changeset ef1ea85a6374
649 no rollback information available
640 no rollback information available
650 0:6675d58eff77
641 0:6675d58eff77
651
642
652
643
653 barney is allowed everywhere
644 barney is allowed everywhere
654
645
655 $ echo '[acl.allow]' >> $config
646 $ echo '[acl.allow]' >> $config
656 $ echo '** = barney' >> $config
647 $ echo '** = barney' >> $config
657 $ do_push barney
648 $ do_push barney
658 Pushing as user barney
649 Pushing as user barney
659 hgrc = """
650 hgrc = """
660 [hooks]
651 [hooks]
661 pretxnchangegroup.acl = python:hgext.acl.hook
652 pretxnchangegroup.acl = python:hgext.acl.hook
662 [acl]
653 [acl]
663 sources = push
654 sources = push
664 [acl.allow]
655 [acl.allow]
665 foo/** = fred
656 foo/** = fred
666 [acl.deny]
657 [acl.deny]
667 foo/bar/** = fred
658 foo/bar/** = fred
668 foo/Bar/** = fred
659 foo/Bar/** = fred
669 [acl.allow]
660 [acl.allow]
670 ** = barney
661 ** = barney
671 """
662 """
672 pushing to ../b
663 pushing to ../b
673 searching for changes
664 searching for changes
674 common changesets up to 6675d58eff77
675 3 changesets found
665 3 changesets found
676 list of changesets:
666 list of changesets:
677 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
667 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
678 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
668 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
679 911600dab2ae7a9baff75958b84fe606851ce955
669 911600dab2ae7a9baff75958b84fe606851ce955
680 adding changesets
670 adding changesets
681 bundling: 1 changesets
671 bundling: 1 changesets
682 bundling: 2 changesets
672 bundling: 2 changesets
683 bundling: 3 changesets
673 bundling: 3 changesets
684 bundling: 1/3 manifests (33.33%)
674 bundling: 1/3 manifests (33.33%)
685 bundling: 2/3 manifests (66.67%)
675 bundling: 2/3 manifests (66.67%)
686 bundling: 3/3 manifests (100.00%)
676 bundling: 3/3 manifests (100.00%)
687 bundling: foo/Bar/file.txt 0/3 files (0.00%)
677 bundling: foo/Bar/file.txt 0/3 files (0.00%)
688 bundling: foo/file.txt 1/3 files (33.33%)
678 bundling: foo/file.txt 1/3 files (33.33%)
689 bundling: quux/file.py 2/3 files (66.67%)
679 bundling: quux/file.py 2/3 files (66.67%)
690 changesets: 1 chunks
680 changesets: 1 chunks
691 add changeset ef1ea85a6374
681 add changeset ef1ea85a6374
692 changesets: 2 chunks
682 changesets: 2 chunks
693 add changeset f9cafe1212c8
683 add changeset f9cafe1212c8
694 changesets: 3 chunks
684 changesets: 3 chunks
695 add changeset 911600dab2ae
685 add changeset 911600dab2ae
696 adding manifests
686 adding manifests
697 manifests: 1/3 chunks (33.33%)
687 manifests: 1/3 chunks (33.33%)
698 manifests: 2/3 chunks (66.67%)
688 manifests: 2/3 chunks (66.67%)
699 manifests: 3/3 chunks (100.00%)
689 manifests: 3/3 chunks (100.00%)
700 adding file changes
690 adding file changes
701 adding foo/Bar/file.txt revisions
691 adding foo/Bar/file.txt revisions
702 files: 1/3 chunks (33.33%)
692 files: 1/3 chunks (33.33%)
703 adding foo/file.txt revisions
693 adding foo/file.txt revisions
704 files: 2/3 chunks (66.67%)
694 files: 2/3 chunks (66.67%)
705 adding quux/file.py revisions
695 adding quux/file.py revisions
706 files: 3/3 chunks (100.00%)
696 files: 3/3 chunks (100.00%)
707 added 3 changesets with 3 changes to 3 files
697 added 3 changesets with 3 changes to 3 files
708 calling hook pretxnchangegroup.acl: hgext.acl.hook
698 calling hook pretxnchangegroup.acl: hgext.acl.hook
709 acl: acl.allow.branches not enabled
699 acl: acl.allow.branches not enabled
710 acl: acl.deny.branches not enabled
700 acl: acl.deny.branches not enabled
711 acl: acl.allow enabled, 1 entries for user barney
701 acl: acl.allow enabled, 1 entries for user barney
712 acl: acl.deny enabled, 0 entries for user barney
702 acl: acl.deny enabled, 0 entries for user barney
713 acl: branch access granted: "ef1ea85a6374" on branch "default"
703 acl: branch access granted: "ef1ea85a6374" on branch "default"
714 acl: allowing changeset ef1ea85a6374
704 acl: allowing changeset ef1ea85a6374
715 acl: branch access granted: "f9cafe1212c8" on branch "default"
705 acl: branch access granted: "f9cafe1212c8" on branch "default"
716 acl: allowing changeset f9cafe1212c8
706 acl: allowing changeset f9cafe1212c8
717 acl: branch access granted: "911600dab2ae" on branch "default"
707 acl: branch access granted: "911600dab2ae" on branch "default"
718 acl: allowing changeset 911600dab2ae
708 acl: allowing changeset 911600dab2ae
719 updating the branch cache
709 updating the branch cache
720 checking for updated bookmarks
710 checking for updated bookmarks
721 repository tip rolled back to revision 0 (undo push)
711 repository tip rolled back to revision 0 (undo push)
722 working directory now based on revision 0
712 working directory now based on revision 0
723 0:6675d58eff77
713 0:6675d58eff77
724
714
725
715
726 wilma can change files with a .txt extension
716 wilma can change files with a .txt extension
727
717
728 $ echo '**/*.txt = wilma' >> $config
718 $ echo '**/*.txt = wilma' >> $config
729 $ do_push wilma
719 $ do_push wilma
730 Pushing as user wilma
720 Pushing as user wilma
731 hgrc = """
721 hgrc = """
732 [hooks]
722 [hooks]
733 pretxnchangegroup.acl = python:hgext.acl.hook
723 pretxnchangegroup.acl = python:hgext.acl.hook
734 [acl]
724 [acl]
735 sources = push
725 sources = push
736 [acl.allow]
726 [acl.allow]
737 foo/** = fred
727 foo/** = fred
738 [acl.deny]
728 [acl.deny]
739 foo/bar/** = fred
729 foo/bar/** = fred
740 foo/Bar/** = fred
730 foo/Bar/** = fred
741 [acl.allow]
731 [acl.allow]
742 ** = barney
732 ** = barney
743 **/*.txt = wilma
733 **/*.txt = wilma
744 """
734 """
745 pushing to ../b
735 pushing to ../b
746 searching for changes
736 searching for changes
747 common changesets up to 6675d58eff77
748 invalidating branch cache (tip differs)
737 invalidating branch cache (tip differs)
749 3 changesets found
738 3 changesets found
750 list of changesets:
739 list of changesets:
751 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
740 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
752 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
741 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
753 911600dab2ae7a9baff75958b84fe606851ce955
742 911600dab2ae7a9baff75958b84fe606851ce955
754 adding changesets
743 adding changesets
755 bundling: 1 changesets
744 bundling: 1 changesets
756 bundling: 2 changesets
745 bundling: 2 changesets
757 bundling: 3 changesets
746 bundling: 3 changesets
758 bundling: 1/3 manifests (33.33%)
747 bundling: 1/3 manifests (33.33%)
759 bundling: 2/3 manifests (66.67%)
748 bundling: 2/3 manifests (66.67%)
760 bundling: 3/3 manifests (100.00%)
749 bundling: 3/3 manifests (100.00%)
761 bundling: foo/Bar/file.txt 0/3 files (0.00%)
750 bundling: foo/Bar/file.txt 0/3 files (0.00%)
762 bundling: foo/file.txt 1/3 files (33.33%)
751 bundling: foo/file.txt 1/3 files (33.33%)
763 bundling: quux/file.py 2/3 files (66.67%)
752 bundling: quux/file.py 2/3 files (66.67%)
764 changesets: 1 chunks
753 changesets: 1 chunks
765 add changeset ef1ea85a6374
754 add changeset ef1ea85a6374
766 changesets: 2 chunks
755 changesets: 2 chunks
767 add changeset f9cafe1212c8
756 add changeset f9cafe1212c8
768 changesets: 3 chunks
757 changesets: 3 chunks
769 add changeset 911600dab2ae
758 add changeset 911600dab2ae
770 adding manifests
759 adding manifests
771 manifests: 1/3 chunks (33.33%)
760 manifests: 1/3 chunks (33.33%)
772 manifests: 2/3 chunks (66.67%)
761 manifests: 2/3 chunks (66.67%)
773 manifests: 3/3 chunks (100.00%)
762 manifests: 3/3 chunks (100.00%)
774 adding file changes
763 adding file changes
775 adding foo/Bar/file.txt revisions
764 adding foo/Bar/file.txt revisions
776 files: 1/3 chunks (33.33%)
765 files: 1/3 chunks (33.33%)
777 adding foo/file.txt revisions
766 adding foo/file.txt revisions
778 files: 2/3 chunks (66.67%)
767 files: 2/3 chunks (66.67%)
779 adding quux/file.py revisions
768 adding quux/file.py revisions
780 files: 3/3 chunks (100.00%)
769 files: 3/3 chunks (100.00%)
781 added 3 changesets with 3 changes to 3 files
770 added 3 changesets with 3 changes to 3 files
782 calling hook pretxnchangegroup.acl: hgext.acl.hook
771 calling hook pretxnchangegroup.acl: hgext.acl.hook
783 acl: acl.allow.branches not enabled
772 acl: acl.allow.branches not enabled
784 acl: acl.deny.branches not enabled
773 acl: acl.deny.branches not enabled
785 acl: acl.allow enabled, 1 entries for user wilma
774 acl: acl.allow enabled, 1 entries for user wilma
786 acl: acl.deny enabled, 0 entries for user wilma
775 acl: acl.deny enabled, 0 entries for user wilma
787 acl: branch access granted: "ef1ea85a6374" on branch "default"
776 acl: branch access granted: "ef1ea85a6374" on branch "default"
788 acl: allowing changeset ef1ea85a6374
777 acl: allowing changeset ef1ea85a6374
789 acl: branch access granted: "f9cafe1212c8" on branch "default"
778 acl: branch access granted: "f9cafe1212c8" on branch "default"
790 acl: allowing changeset f9cafe1212c8
779 acl: allowing changeset f9cafe1212c8
791 acl: branch access granted: "911600dab2ae" on branch "default"
780 acl: branch access granted: "911600dab2ae" on branch "default"
792 acl: user wilma not allowed on quux/file.py
781 acl: user wilma not allowed on quux/file.py
793 error: pretxnchangegroup.acl hook failed: acl: access denied for changeset 911600dab2ae
782 error: pretxnchangegroup.acl hook failed: acl: access denied for changeset 911600dab2ae
794 transaction abort!
783 transaction abort!
795 rollback completed
784 rollback completed
796 abort: acl: access denied for changeset 911600dab2ae
785 abort: acl: access denied for changeset 911600dab2ae
797 no rollback information available
786 no rollback information available
798 0:6675d58eff77
787 0:6675d58eff77
799
788
800
789
801 file specified by acl.config does not exist
790 file specified by acl.config does not exist
802
791
803 $ echo '[acl]' >> $config
792 $ echo '[acl]' >> $config
804 $ echo 'config = ../acl.config' >> $config
793 $ echo 'config = ../acl.config' >> $config
805 $ do_push barney
794 $ do_push barney
806 Pushing as user barney
795 Pushing as user barney
807 hgrc = """
796 hgrc = """
808 [hooks]
797 [hooks]
809 pretxnchangegroup.acl = python:hgext.acl.hook
798 pretxnchangegroup.acl = python:hgext.acl.hook
810 [acl]
799 [acl]
811 sources = push
800 sources = push
812 [acl.allow]
801 [acl.allow]
813 foo/** = fred
802 foo/** = fred
814 [acl.deny]
803 [acl.deny]
815 foo/bar/** = fred
804 foo/bar/** = fred
816 foo/Bar/** = fred
805 foo/Bar/** = fred
817 [acl.allow]
806 [acl.allow]
818 ** = barney
807 ** = barney
819 **/*.txt = wilma
808 **/*.txt = wilma
820 [acl]
809 [acl]
821 config = ../acl.config
810 config = ../acl.config
822 """
811 """
823 pushing to ../b
812 pushing to ../b
824 searching for changes
813 searching for changes
825 common changesets up to 6675d58eff77
826 3 changesets found
814 3 changesets found
827 list of changesets:
815 list of changesets:
828 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
816 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
829 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
817 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
830 911600dab2ae7a9baff75958b84fe606851ce955
818 911600dab2ae7a9baff75958b84fe606851ce955
831 adding changesets
819 adding changesets
832 bundling: 1 changesets
820 bundling: 1 changesets
833 bundling: 2 changesets
821 bundling: 2 changesets
834 bundling: 3 changesets
822 bundling: 3 changesets
835 bundling: 1/3 manifests (33.33%)
823 bundling: 1/3 manifests (33.33%)
836 bundling: 2/3 manifests (66.67%)
824 bundling: 2/3 manifests (66.67%)
837 bundling: 3/3 manifests (100.00%)
825 bundling: 3/3 manifests (100.00%)
838 bundling: foo/Bar/file.txt 0/3 files (0.00%)
826 bundling: foo/Bar/file.txt 0/3 files (0.00%)
839 bundling: foo/file.txt 1/3 files (33.33%)
827 bundling: foo/file.txt 1/3 files (33.33%)
840 bundling: quux/file.py 2/3 files (66.67%)
828 bundling: quux/file.py 2/3 files (66.67%)
841 changesets: 1 chunks
829 changesets: 1 chunks
842 add changeset ef1ea85a6374
830 add changeset ef1ea85a6374
843 changesets: 2 chunks
831 changesets: 2 chunks
844 add changeset f9cafe1212c8
832 add changeset f9cafe1212c8
845 changesets: 3 chunks
833 changesets: 3 chunks
846 add changeset 911600dab2ae
834 add changeset 911600dab2ae
847 adding manifests
835 adding manifests
848 manifests: 1/3 chunks (33.33%)
836 manifests: 1/3 chunks (33.33%)
849 manifests: 2/3 chunks (66.67%)
837 manifests: 2/3 chunks (66.67%)
850 manifests: 3/3 chunks (100.00%)
838 manifests: 3/3 chunks (100.00%)
851 adding file changes
839 adding file changes
852 adding foo/Bar/file.txt revisions
840 adding foo/Bar/file.txt revisions
853 files: 1/3 chunks (33.33%)
841 files: 1/3 chunks (33.33%)
854 adding foo/file.txt revisions
842 adding foo/file.txt revisions
855 files: 2/3 chunks (66.67%)
843 files: 2/3 chunks (66.67%)
856 adding quux/file.py revisions
844 adding quux/file.py revisions
857 files: 3/3 chunks (100.00%)
845 files: 3/3 chunks (100.00%)
858 added 3 changesets with 3 changes to 3 files
846 added 3 changesets with 3 changes to 3 files
859 calling hook pretxnchangegroup.acl: hgext.acl.hook
847 calling hook pretxnchangegroup.acl: hgext.acl.hook
860 error: pretxnchangegroup.acl hook raised an exception: [Errno 2] No such file or directory: '../acl.config'
848 error: pretxnchangegroup.acl hook raised an exception: [Errno 2] No such file or directory: '../acl.config'
861 transaction abort!
849 transaction abort!
862 rollback completed
850 rollback completed
863 abort: No such file or directory: ../acl.config
851 abort: No such file or directory: ../acl.config
864 no rollback information available
852 no rollback information available
865 0:6675d58eff77
853 0:6675d58eff77
866
854
867
855
868 betty is allowed inside foo/ by a acl.config file
856 betty is allowed inside foo/ by a acl.config file
869
857
870 $ echo '[acl.allow]' >> acl.config
858 $ echo '[acl.allow]' >> acl.config
871 $ echo 'foo/** = betty' >> acl.config
859 $ echo 'foo/** = betty' >> acl.config
872 $ do_push betty
860 $ do_push betty
873 Pushing as user betty
861 Pushing as user betty
874 hgrc = """
862 hgrc = """
875 [hooks]
863 [hooks]
876 pretxnchangegroup.acl = python:hgext.acl.hook
864 pretxnchangegroup.acl = python:hgext.acl.hook
877 [acl]
865 [acl]
878 sources = push
866 sources = push
879 [acl.allow]
867 [acl.allow]
880 foo/** = fred
868 foo/** = fred
881 [acl.deny]
869 [acl.deny]
882 foo/bar/** = fred
870 foo/bar/** = fred
883 foo/Bar/** = fred
871 foo/Bar/** = fred
884 [acl.allow]
872 [acl.allow]
885 ** = barney
873 ** = barney
886 **/*.txt = wilma
874 **/*.txt = wilma
887 [acl]
875 [acl]
888 config = ../acl.config
876 config = ../acl.config
889 """
877 """
890 acl.config = """
878 acl.config = """
891 [acl.allow]
879 [acl.allow]
892 foo/** = betty
880 foo/** = betty
893 """
881 """
894 pushing to ../b
882 pushing to ../b
895 searching for changes
883 searching for changes
896 common changesets up to 6675d58eff77
897 3 changesets found
884 3 changesets found
898 list of changesets:
885 list of changesets:
899 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
886 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
900 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
887 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
901 911600dab2ae7a9baff75958b84fe606851ce955
888 911600dab2ae7a9baff75958b84fe606851ce955
902 adding changesets
889 adding changesets
903 bundling: 1 changesets
890 bundling: 1 changesets
904 bundling: 2 changesets
891 bundling: 2 changesets
905 bundling: 3 changesets
892 bundling: 3 changesets
906 bundling: 1/3 manifests (33.33%)
893 bundling: 1/3 manifests (33.33%)
907 bundling: 2/3 manifests (66.67%)
894 bundling: 2/3 manifests (66.67%)
908 bundling: 3/3 manifests (100.00%)
895 bundling: 3/3 manifests (100.00%)
909 bundling: foo/Bar/file.txt 0/3 files (0.00%)
896 bundling: foo/Bar/file.txt 0/3 files (0.00%)
910 bundling: foo/file.txt 1/3 files (33.33%)
897 bundling: foo/file.txt 1/3 files (33.33%)
911 bundling: quux/file.py 2/3 files (66.67%)
898 bundling: quux/file.py 2/3 files (66.67%)
912 changesets: 1 chunks
899 changesets: 1 chunks
913 add changeset ef1ea85a6374
900 add changeset ef1ea85a6374
914 changesets: 2 chunks
901 changesets: 2 chunks
915 add changeset f9cafe1212c8
902 add changeset f9cafe1212c8
916 changesets: 3 chunks
903 changesets: 3 chunks
917 add changeset 911600dab2ae
904 add changeset 911600dab2ae
918 adding manifests
905 adding manifests
919 manifests: 1/3 chunks (33.33%)
906 manifests: 1/3 chunks (33.33%)
920 manifests: 2/3 chunks (66.67%)
907 manifests: 2/3 chunks (66.67%)
921 manifests: 3/3 chunks (100.00%)
908 manifests: 3/3 chunks (100.00%)
922 adding file changes
909 adding file changes
923 adding foo/Bar/file.txt revisions
910 adding foo/Bar/file.txt revisions
924 files: 1/3 chunks (33.33%)
911 files: 1/3 chunks (33.33%)
925 adding foo/file.txt revisions
912 adding foo/file.txt revisions
926 files: 2/3 chunks (66.67%)
913 files: 2/3 chunks (66.67%)
927 adding quux/file.py revisions
914 adding quux/file.py revisions
928 files: 3/3 chunks (100.00%)
915 files: 3/3 chunks (100.00%)
929 added 3 changesets with 3 changes to 3 files
916 added 3 changesets with 3 changes to 3 files
930 calling hook pretxnchangegroup.acl: hgext.acl.hook
917 calling hook pretxnchangegroup.acl: hgext.acl.hook
931 acl: acl.allow.branches not enabled
918 acl: acl.allow.branches not enabled
932 acl: acl.deny.branches not enabled
919 acl: acl.deny.branches not enabled
933 acl: acl.allow enabled, 1 entries for user betty
920 acl: acl.allow enabled, 1 entries for user betty
934 acl: acl.deny enabled, 0 entries for user betty
921 acl: acl.deny enabled, 0 entries for user betty
935 acl: branch access granted: "ef1ea85a6374" on branch "default"
922 acl: branch access granted: "ef1ea85a6374" on branch "default"
936 acl: allowing changeset ef1ea85a6374
923 acl: allowing changeset ef1ea85a6374
937 acl: branch access granted: "f9cafe1212c8" on branch "default"
924 acl: branch access granted: "f9cafe1212c8" on branch "default"
938 acl: allowing changeset f9cafe1212c8
925 acl: allowing changeset f9cafe1212c8
939 acl: branch access granted: "911600dab2ae" on branch "default"
926 acl: branch access granted: "911600dab2ae" on branch "default"
940 acl: user betty not allowed on quux/file.py
927 acl: user betty not allowed on quux/file.py
941 error: pretxnchangegroup.acl hook failed: acl: access denied for changeset 911600dab2ae
928 error: pretxnchangegroup.acl hook failed: acl: access denied for changeset 911600dab2ae
942 transaction abort!
929 transaction abort!
943 rollback completed
930 rollback completed
944 abort: acl: access denied for changeset 911600dab2ae
931 abort: acl: access denied for changeset 911600dab2ae
945 no rollback information available
932 no rollback information available
946 0:6675d58eff77
933 0:6675d58eff77
947
934
948
935
949 acl.config can set only [acl.allow]/[acl.deny]
936 acl.config can set only [acl.allow]/[acl.deny]
950
937
951 $ echo '[hooks]' >> acl.config
938 $ echo '[hooks]' >> acl.config
952 $ echo 'changegroup.acl = false' >> acl.config
939 $ echo 'changegroup.acl = false' >> acl.config
953 $ do_push barney
940 $ do_push barney
954 Pushing as user barney
941 Pushing as user barney
955 hgrc = """
942 hgrc = """
956 [hooks]
943 [hooks]
957 pretxnchangegroup.acl = python:hgext.acl.hook
944 pretxnchangegroup.acl = python:hgext.acl.hook
958 [acl]
945 [acl]
959 sources = push
946 sources = push
960 [acl.allow]
947 [acl.allow]
961 foo/** = fred
948 foo/** = fred
962 [acl.deny]
949 [acl.deny]
963 foo/bar/** = fred
950 foo/bar/** = fred
964 foo/Bar/** = fred
951 foo/Bar/** = fred
965 [acl.allow]
952 [acl.allow]
966 ** = barney
953 ** = barney
967 **/*.txt = wilma
954 **/*.txt = wilma
968 [acl]
955 [acl]
969 config = ../acl.config
956 config = ../acl.config
970 """
957 """
971 acl.config = """
958 acl.config = """
972 [acl.allow]
959 [acl.allow]
973 foo/** = betty
960 foo/** = betty
974 [hooks]
961 [hooks]
975 changegroup.acl = false
962 changegroup.acl = false
976 """
963 """
977 pushing to ../b
964 pushing to ../b
978 searching for changes
965 searching for changes
979 common changesets up to 6675d58eff77
980 3 changesets found
966 3 changesets found
981 list of changesets:
967 list of changesets:
982 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
968 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
983 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
969 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
984 911600dab2ae7a9baff75958b84fe606851ce955
970 911600dab2ae7a9baff75958b84fe606851ce955
985 adding changesets
971 adding changesets
986 bundling: 1 changesets
972 bundling: 1 changesets
987 bundling: 2 changesets
973 bundling: 2 changesets
988 bundling: 3 changesets
974 bundling: 3 changesets
989 bundling: 1/3 manifests (33.33%)
975 bundling: 1/3 manifests (33.33%)
990 bundling: 2/3 manifests (66.67%)
976 bundling: 2/3 manifests (66.67%)
991 bundling: 3/3 manifests (100.00%)
977 bundling: 3/3 manifests (100.00%)
992 bundling: foo/Bar/file.txt 0/3 files (0.00%)
978 bundling: foo/Bar/file.txt 0/3 files (0.00%)
993 bundling: foo/file.txt 1/3 files (33.33%)
979 bundling: foo/file.txt 1/3 files (33.33%)
994 bundling: quux/file.py 2/3 files (66.67%)
980 bundling: quux/file.py 2/3 files (66.67%)
995 changesets: 1 chunks
981 changesets: 1 chunks
996 add changeset ef1ea85a6374
982 add changeset ef1ea85a6374
997 changesets: 2 chunks
983 changesets: 2 chunks
998 add changeset f9cafe1212c8
984 add changeset f9cafe1212c8
999 changesets: 3 chunks
985 changesets: 3 chunks
1000 add changeset 911600dab2ae
986 add changeset 911600dab2ae
1001 adding manifests
987 adding manifests
1002 manifests: 1/3 chunks (33.33%)
988 manifests: 1/3 chunks (33.33%)
1003 manifests: 2/3 chunks (66.67%)
989 manifests: 2/3 chunks (66.67%)
1004 manifests: 3/3 chunks (100.00%)
990 manifests: 3/3 chunks (100.00%)
1005 adding file changes
991 adding file changes
1006 adding foo/Bar/file.txt revisions
992 adding foo/Bar/file.txt revisions
1007 files: 1/3 chunks (33.33%)
993 files: 1/3 chunks (33.33%)
1008 adding foo/file.txt revisions
994 adding foo/file.txt revisions
1009 files: 2/3 chunks (66.67%)
995 files: 2/3 chunks (66.67%)
1010 adding quux/file.py revisions
996 adding quux/file.py revisions
1011 files: 3/3 chunks (100.00%)
997 files: 3/3 chunks (100.00%)
1012 added 3 changesets with 3 changes to 3 files
998 added 3 changesets with 3 changes to 3 files
1013 calling hook pretxnchangegroup.acl: hgext.acl.hook
999 calling hook pretxnchangegroup.acl: hgext.acl.hook
1014 acl: acl.allow.branches not enabled
1000 acl: acl.allow.branches not enabled
1015 acl: acl.deny.branches not enabled
1001 acl: acl.deny.branches not enabled
1016 acl: acl.allow enabled, 1 entries for user barney
1002 acl: acl.allow enabled, 1 entries for user barney
1017 acl: acl.deny enabled, 0 entries for user barney
1003 acl: acl.deny enabled, 0 entries for user barney
1018 acl: branch access granted: "ef1ea85a6374" on branch "default"
1004 acl: branch access granted: "ef1ea85a6374" on branch "default"
1019 acl: allowing changeset ef1ea85a6374
1005 acl: allowing changeset ef1ea85a6374
1020 acl: branch access granted: "f9cafe1212c8" on branch "default"
1006 acl: branch access granted: "f9cafe1212c8" on branch "default"
1021 acl: allowing changeset f9cafe1212c8
1007 acl: allowing changeset f9cafe1212c8
1022 acl: branch access granted: "911600dab2ae" on branch "default"
1008 acl: branch access granted: "911600dab2ae" on branch "default"
1023 acl: allowing changeset 911600dab2ae
1009 acl: allowing changeset 911600dab2ae
1024 updating the branch cache
1010 updating the branch cache
1025 checking for updated bookmarks
1011 checking for updated bookmarks
1026 repository tip rolled back to revision 0 (undo push)
1012 repository tip rolled back to revision 0 (undo push)
1027 working directory now based on revision 0
1013 working directory now based on revision 0
1028 0:6675d58eff77
1014 0:6675d58eff77
1029
1015
1030
1016
1031 asterisk
1017 asterisk
1032
1018
1033 $ init_config
1019 $ init_config
1034
1020
1035 asterisk test
1021 asterisk test
1036
1022
1037 $ echo '[acl.allow]' >> $config
1023 $ echo '[acl.allow]' >> $config
1038 $ echo "** = fred" >> $config
1024 $ echo "** = fred" >> $config
1039
1025
1040 fred is always allowed
1026 fred is always allowed
1041
1027
1042 $ do_push fred
1028 $ do_push fred
1043 Pushing as user fred
1029 Pushing as user fred
1044 hgrc = """
1030 hgrc = """
1045 [acl]
1031 [acl]
1046 sources = push
1032 sources = push
1047 [extensions]
1033 [extensions]
1048 [acl.allow]
1034 [acl.allow]
1049 ** = fred
1035 ** = fred
1050 """
1036 """
1051 pushing to ../b
1037 pushing to ../b
1052 searching for changes
1038 searching for changes
1053 common changesets up to 6675d58eff77
1054 invalidating branch cache (tip differs)
1039 invalidating branch cache (tip differs)
1055 3 changesets found
1040 3 changesets found
1056 list of changesets:
1041 list of changesets:
1057 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1042 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1058 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1043 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1059 911600dab2ae7a9baff75958b84fe606851ce955
1044 911600dab2ae7a9baff75958b84fe606851ce955
1060 adding changesets
1045 adding changesets
1061 bundling: 1 changesets
1046 bundling: 1 changesets
1062 bundling: 2 changesets
1047 bundling: 2 changesets
1063 bundling: 3 changesets
1048 bundling: 3 changesets
1064 bundling: 1/3 manifests (33.33%)
1049 bundling: 1/3 manifests (33.33%)
1065 bundling: 2/3 manifests (66.67%)
1050 bundling: 2/3 manifests (66.67%)
1066 bundling: 3/3 manifests (100.00%)
1051 bundling: 3/3 manifests (100.00%)
1067 bundling: foo/Bar/file.txt 0/3 files (0.00%)
1052 bundling: foo/Bar/file.txt 0/3 files (0.00%)
1068 bundling: foo/file.txt 1/3 files (33.33%)
1053 bundling: foo/file.txt 1/3 files (33.33%)
1069 bundling: quux/file.py 2/3 files (66.67%)
1054 bundling: quux/file.py 2/3 files (66.67%)
1070 changesets: 1 chunks
1055 changesets: 1 chunks
1071 add changeset ef1ea85a6374
1056 add changeset ef1ea85a6374
1072 changesets: 2 chunks
1057 changesets: 2 chunks
1073 add changeset f9cafe1212c8
1058 add changeset f9cafe1212c8
1074 changesets: 3 chunks
1059 changesets: 3 chunks
1075 add changeset 911600dab2ae
1060 add changeset 911600dab2ae
1076 adding manifests
1061 adding manifests
1077 manifests: 1/3 chunks (33.33%)
1062 manifests: 1/3 chunks (33.33%)
1078 manifests: 2/3 chunks (66.67%)
1063 manifests: 2/3 chunks (66.67%)
1079 manifests: 3/3 chunks (100.00%)
1064 manifests: 3/3 chunks (100.00%)
1080 adding file changes
1065 adding file changes
1081 adding foo/Bar/file.txt revisions
1066 adding foo/Bar/file.txt revisions
1082 files: 1/3 chunks (33.33%)
1067 files: 1/3 chunks (33.33%)
1083 adding foo/file.txt revisions
1068 adding foo/file.txt revisions
1084 files: 2/3 chunks (66.67%)
1069 files: 2/3 chunks (66.67%)
1085 adding quux/file.py revisions
1070 adding quux/file.py revisions
1086 files: 3/3 chunks (100.00%)
1071 files: 3/3 chunks (100.00%)
1087 added 3 changesets with 3 changes to 3 files
1072 added 3 changesets with 3 changes to 3 files
1088 calling hook pretxnchangegroup.acl: hgext.acl.hook
1073 calling hook pretxnchangegroup.acl: hgext.acl.hook
1089 acl: acl.allow.branches not enabled
1074 acl: acl.allow.branches not enabled
1090 acl: acl.deny.branches not enabled
1075 acl: acl.deny.branches not enabled
1091 acl: acl.allow enabled, 1 entries for user fred
1076 acl: acl.allow enabled, 1 entries for user fred
1092 acl: acl.deny not enabled
1077 acl: acl.deny not enabled
1093 acl: branch access granted: "ef1ea85a6374" on branch "default"
1078 acl: branch access granted: "ef1ea85a6374" on branch "default"
1094 acl: allowing changeset ef1ea85a6374
1079 acl: allowing changeset ef1ea85a6374
1095 acl: branch access granted: "f9cafe1212c8" on branch "default"
1080 acl: branch access granted: "f9cafe1212c8" on branch "default"
1096 acl: allowing changeset f9cafe1212c8
1081 acl: allowing changeset f9cafe1212c8
1097 acl: branch access granted: "911600dab2ae" on branch "default"
1082 acl: branch access granted: "911600dab2ae" on branch "default"
1098 acl: allowing changeset 911600dab2ae
1083 acl: allowing changeset 911600dab2ae
1099 updating the branch cache
1084 updating the branch cache
1100 checking for updated bookmarks
1085 checking for updated bookmarks
1101 repository tip rolled back to revision 0 (undo push)
1086 repository tip rolled back to revision 0 (undo push)
1102 working directory now based on revision 0
1087 working directory now based on revision 0
1103 0:6675d58eff77
1088 0:6675d58eff77
1104
1089
1105
1090
1106 $ echo '[acl.deny]' >> $config
1091 $ echo '[acl.deny]' >> $config
1107 $ echo "foo/Bar/** = *" >> $config
1092 $ echo "foo/Bar/** = *" >> $config
1108
1093
1109 no one is allowed inside foo/Bar/
1094 no one is allowed inside foo/Bar/
1110
1095
1111 $ do_push fred
1096 $ do_push fred
1112 Pushing as user fred
1097 Pushing as user fred
1113 hgrc = """
1098 hgrc = """
1114 [acl]
1099 [acl]
1115 sources = push
1100 sources = push
1116 [extensions]
1101 [extensions]
1117 [acl.allow]
1102 [acl.allow]
1118 ** = fred
1103 ** = fred
1119 [acl.deny]
1104 [acl.deny]
1120 foo/Bar/** = *
1105 foo/Bar/** = *
1121 """
1106 """
1122 pushing to ../b
1107 pushing to ../b
1123 searching for changes
1108 searching for changes
1124 common changesets up to 6675d58eff77
1125 invalidating branch cache (tip differs)
1109 invalidating branch cache (tip differs)
1126 3 changesets found
1110 3 changesets found
1127 list of changesets:
1111 list of changesets:
1128 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1112 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1129 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1113 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1130 911600dab2ae7a9baff75958b84fe606851ce955
1114 911600dab2ae7a9baff75958b84fe606851ce955
1131 adding changesets
1115 adding changesets
1132 bundling: 1 changesets
1116 bundling: 1 changesets
1133 bundling: 2 changesets
1117 bundling: 2 changesets
1134 bundling: 3 changesets
1118 bundling: 3 changesets
1135 bundling: 1/3 manifests (33.33%)
1119 bundling: 1/3 manifests (33.33%)
1136 bundling: 2/3 manifests (66.67%)
1120 bundling: 2/3 manifests (66.67%)
1137 bundling: 3/3 manifests (100.00%)
1121 bundling: 3/3 manifests (100.00%)
1138 bundling: foo/Bar/file.txt 0/3 files (0.00%)
1122 bundling: foo/Bar/file.txt 0/3 files (0.00%)
1139 bundling: foo/file.txt 1/3 files (33.33%)
1123 bundling: foo/file.txt 1/3 files (33.33%)
1140 bundling: quux/file.py 2/3 files (66.67%)
1124 bundling: quux/file.py 2/3 files (66.67%)
1141 changesets: 1 chunks
1125 changesets: 1 chunks
1142 add changeset ef1ea85a6374
1126 add changeset ef1ea85a6374
1143 changesets: 2 chunks
1127 changesets: 2 chunks
1144 add changeset f9cafe1212c8
1128 add changeset f9cafe1212c8
1145 changesets: 3 chunks
1129 changesets: 3 chunks
1146 add changeset 911600dab2ae
1130 add changeset 911600dab2ae
1147 adding manifests
1131 adding manifests
1148 manifests: 1/3 chunks (33.33%)
1132 manifests: 1/3 chunks (33.33%)
1149 manifests: 2/3 chunks (66.67%)
1133 manifests: 2/3 chunks (66.67%)
1150 manifests: 3/3 chunks (100.00%)
1134 manifests: 3/3 chunks (100.00%)
1151 adding file changes
1135 adding file changes
1152 adding foo/Bar/file.txt revisions
1136 adding foo/Bar/file.txt revisions
1153 files: 1/3 chunks (33.33%)
1137 files: 1/3 chunks (33.33%)
1154 adding foo/file.txt revisions
1138 adding foo/file.txt revisions
1155 files: 2/3 chunks (66.67%)
1139 files: 2/3 chunks (66.67%)
1156 adding quux/file.py revisions
1140 adding quux/file.py revisions
1157 files: 3/3 chunks (100.00%)
1141 files: 3/3 chunks (100.00%)
1158 added 3 changesets with 3 changes to 3 files
1142 added 3 changesets with 3 changes to 3 files
1159 calling hook pretxnchangegroup.acl: hgext.acl.hook
1143 calling hook pretxnchangegroup.acl: hgext.acl.hook
1160 acl: acl.allow.branches not enabled
1144 acl: acl.allow.branches not enabled
1161 acl: acl.deny.branches not enabled
1145 acl: acl.deny.branches not enabled
1162 acl: acl.allow enabled, 1 entries for user fred
1146 acl: acl.allow enabled, 1 entries for user fred
1163 acl: acl.deny enabled, 1 entries for user fred
1147 acl: acl.deny enabled, 1 entries for user fred
1164 acl: branch access granted: "ef1ea85a6374" on branch "default"
1148 acl: branch access granted: "ef1ea85a6374" on branch "default"
1165 acl: allowing changeset ef1ea85a6374
1149 acl: allowing changeset ef1ea85a6374
1166 acl: branch access granted: "f9cafe1212c8" on branch "default"
1150 acl: branch access granted: "f9cafe1212c8" on branch "default"
1167 acl: user fred denied on foo/Bar/file.txt
1151 acl: user fred denied on foo/Bar/file.txt
1168 error: pretxnchangegroup.acl hook failed: acl: access denied for changeset f9cafe1212c8
1152 error: pretxnchangegroup.acl hook failed: acl: access denied for changeset f9cafe1212c8
1169 transaction abort!
1153 transaction abort!
1170 rollback completed
1154 rollback completed
1171 abort: acl: access denied for changeset f9cafe1212c8
1155 abort: acl: access denied for changeset f9cafe1212c8
1172 no rollback information available
1156 no rollback information available
1173 0:6675d58eff77
1157 0:6675d58eff77
1174
1158
1175
1159
1176 Groups
1160 Groups
1177
1161
1178 $ init_config
1162 $ init_config
1179
1163
1180 OS-level groups
1164 OS-level groups
1181
1165
1182 $ echo '[acl.allow]' >> $config
1166 $ echo '[acl.allow]' >> $config
1183 $ echo "** = @group1" >> $config
1167 $ echo "** = @group1" >> $config
1184
1168
1185 @group1 is always allowed
1169 @group1 is always allowed
1186
1170
1187 $ do_push fred
1171 $ do_push fred
1188 Pushing as user fred
1172 Pushing as user fred
1189 hgrc = """
1173 hgrc = """
1190 [acl]
1174 [acl]
1191 sources = push
1175 sources = push
1192 [extensions]
1176 [extensions]
1193 [acl.allow]
1177 [acl.allow]
1194 ** = @group1
1178 ** = @group1
1195 """
1179 """
1196 pushing to ../b
1180 pushing to ../b
1197 searching for changes
1181 searching for changes
1198 common changesets up to 6675d58eff77
1199 3 changesets found
1182 3 changesets found
1200 list of changesets:
1183 list of changesets:
1201 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1184 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1202 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1185 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1203 911600dab2ae7a9baff75958b84fe606851ce955
1186 911600dab2ae7a9baff75958b84fe606851ce955
1204 adding changesets
1187 adding changesets
1205 bundling: 1 changesets
1188 bundling: 1 changesets
1206 bundling: 2 changesets
1189 bundling: 2 changesets
1207 bundling: 3 changesets
1190 bundling: 3 changesets
1208 bundling: 1/3 manifests (33.33%)
1191 bundling: 1/3 manifests (33.33%)
1209 bundling: 2/3 manifests (66.67%)
1192 bundling: 2/3 manifests (66.67%)
1210 bundling: 3/3 manifests (100.00%)
1193 bundling: 3/3 manifests (100.00%)
1211 bundling: foo/Bar/file.txt 0/3 files (0.00%)
1194 bundling: foo/Bar/file.txt 0/3 files (0.00%)
1212 bundling: foo/file.txt 1/3 files (33.33%)
1195 bundling: foo/file.txt 1/3 files (33.33%)
1213 bundling: quux/file.py 2/3 files (66.67%)
1196 bundling: quux/file.py 2/3 files (66.67%)
1214 changesets: 1 chunks
1197 changesets: 1 chunks
1215 add changeset ef1ea85a6374
1198 add changeset ef1ea85a6374
1216 changesets: 2 chunks
1199 changesets: 2 chunks
1217 add changeset f9cafe1212c8
1200 add changeset f9cafe1212c8
1218 changesets: 3 chunks
1201 changesets: 3 chunks
1219 add changeset 911600dab2ae
1202 add changeset 911600dab2ae
1220 adding manifests
1203 adding manifests
1221 manifests: 1/3 chunks (33.33%)
1204 manifests: 1/3 chunks (33.33%)
1222 manifests: 2/3 chunks (66.67%)
1205 manifests: 2/3 chunks (66.67%)
1223 manifests: 3/3 chunks (100.00%)
1206 manifests: 3/3 chunks (100.00%)
1224 adding file changes
1207 adding file changes
1225 adding foo/Bar/file.txt revisions
1208 adding foo/Bar/file.txt revisions
1226 files: 1/3 chunks (33.33%)
1209 files: 1/3 chunks (33.33%)
1227 adding foo/file.txt revisions
1210 adding foo/file.txt revisions
1228 files: 2/3 chunks (66.67%)
1211 files: 2/3 chunks (66.67%)
1229 adding quux/file.py revisions
1212 adding quux/file.py revisions
1230 files: 3/3 chunks (100.00%)
1213 files: 3/3 chunks (100.00%)
1231 added 3 changesets with 3 changes to 3 files
1214 added 3 changesets with 3 changes to 3 files
1232 calling hook pretxnchangegroup.acl: hgext.acl.hook
1215 calling hook pretxnchangegroup.acl: hgext.acl.hook
1233 acl: acl.allow.branches not enabled
1216 acl: acl.allow.branches not enabled
1234 acl: acl.deny.branches not enabled
1217 acl: acl.deny.branches not enabled
1235 acl: "group1" not defined in [acl.groups]
1218 acl: "group1" not defined in [acl.groups]
1236 acl: acl.allow enabled, 1 entries for user fred
1219 acl: acl.allow enabled, 1 entries for user fred
1237 acl: acl.deny not enabled
1220 acl: acl.deny not enabled
1238 acl: branch access granted: "ef1ea85a6374" on branch "default"
1221 acl: branch access granted: "ef1ea85a6374" on branch "default"
1239 acl: allowing changeset ef1ea85a6374
1222 acl: allowing changeset ef1ea85a6374
1240 acl: branch access granted: "f9cafe1212c8" on branch "default"
1223 acl: branch access granted: "f9cafe1212c8" on branch "default"
1241 acl: allowing changeset f9cafe1212c8
1224 acl: allowing changeset f9cafe1212c8
1242 acl: branch access granted: "911600dab2ae" on branch "default"
1225 acl: branch access granted: "911600dab2ae" on branch "default"
1243 acl: allowing changeset 911600dab2ae
1226 acl: allowing changeset 911600dab2ae
1244 updating the branch cache
1227 updating the branch cache
1245 checking for updated bookmarks
1228 checking for updated bookmarks
1246 repository tip rolled back to revision 0 (undo push)
1229 repository tip rolled back to revision 0 (undo push)
1247 working directory now based on revision 0
1230 working directory now based on revision 0
1248 0:6675d58eff77
1231 0:6675d58eff77
1249
1232
1250
1233
1251 $ echo '[acl.deny]' >> $config
1234 $ echo '[acl.deny]' >> $config
1252 $ echo "foo/Bar/** = @group1" >> $config
1235 $ echo "foo/Bar/** = @group1" >> $config
1253
1236
1254 @group is allowed inside anything but foo/Bar/
1237 @group is allowed inside anything but foo/Bar/
1255
1238
1256 $ do_push fred
1239 $ do_push fred
1257 Pushing as user fred
1240 Pushing as user fred
1258 hgrc = """
1241 hgrc = """
1259 [acl]
1242 [acl]
1260 sources = push
1243 sources = push
1261 [extensions]
1244 [extensions]
1262 [acl.allow]
1245 [acl.allow]
1263 ** = @group1
1246 ** = @group1
1264 [acl.deny]
1247 [acl.deny]
1265 foo/Bar/** = @group1
1248 foo/Bar/** = @group1
1266 """
1249 """
1267 pushing to ../b
1250 pushing to ../b
1268 searching for changes
1251 searching for changes
1269 common changesets up to 6675d58eff77
1270 invalidating branch cache (tip differs)
1252 invalidating branch cache (tip differs)
1271 3 changesets found
1253 3 changesets found
1272 list of changesets:
1254 list of changesets:
1273 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1255 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1274 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1256 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1275 911600dab2ae7a9baff75958b84fe606851ce955
1257 911600dab2ae7a9baff75958b84fe606851ce955
1276 adding changesets
1258 adding changesets
1277 bundling: 1 changesets
1259 bundling: 1 changesets
1278 bundling: 2 changesets
1260 bundling: 2 changesets
1279 bundling: 3 changesets
1261 bundling: 3 changesets
1280 bundling: 1/3 manifests (33.33%)
1262 bundling: 1/3 manifests (33.33%)
1281 bundling: 2/3 manifests (66.67%)
1263 bundling: 2/3 manifests (66.67%)
1282 bundling: 3/3 manifests (100.00%)
1264 bundling: 3/3 manifests (100.00%)
1283 bundling: foo/Bar/file.txt 0/3 files (0.00%)
1265 bundling: foo/Bar/file.txt 0/3 files (0.00%)
1284 bundling: foo/file.txt 1/3 files (33.33%)
1266 bundling: foo/file.txt 1/3 files (33.33%)
1285 bundling: quux/file.py 2/3 files (66.67%)
1267 bundling: quux/file.py 2/3 files (66.67%)
1286 changesets: 1 chunks
1268 changesets: 1 chunks
1287 add changeset ef1ea85a6374
1269 add changeset ef1ea85a6374
1288 changesets: 2 chunks
1270 changesets: 2 chunks
1289 add changeset f9cafe1212c8
1271 add changeset f9cafe1212c8
1290 changesets: 3 chunks
1272 changesets: 3 chunks
1291 add changeset 911600dab2ae
1273 add changeset 911600dab2ae
1292 adding manifests
1274 adding manifests
1293 manifests: 1/3 chunks (33.33%)
1275 manifests: 1/3 chunks (33.33%)
1294 manifests: 2/3 chunks (66.67%)
1276 manifests: 2/3 chunks (66.67%)
1295 manifests: 3/3 chunks (100.00%)
1277 manifests: 3/3 chunks (100.00%)
1296 adding file changes
1278 adding file changes
1297 adding foo/Bar/file.txt revisions
1279 adding foo/Bar/file.txt revisions
1298 files: 1/3 chunks (33.33%)
1280 files: 1/3 chunks (33.33%)
1299 adding foo/file.txt revisions
1281 adding foo/file.txt revisions
1300 files: 2/3 chunks (66.67%)
1282 files: 2/3 chunks (66.67%)
1301 adding quux/file.py revisions
1283 adding quux/file.py revisions
1302 files: 3/3 chunks (100.00%)
1284 files: 3/3 chunks (100.00%)
1303 added 3 changesets with 3 changes to 3 files
1285 added 3 changesets with 3 changes to 3 files
1304 calling hook pretxnchangegroup.acl: hgext.acl.hook
1286 calling hook pretxnchangegroup.acl: hgext.acl.hook
1305 acl: acl.allow.branches not enabled
1287 acl: acl.allow.branches not enabled
1306 acl: acl.deny.branches not enabled
1288 acl: acl.deny.branches not enabled
1307 acl: "group1" not defined in [acl.groups]
1289 acl: "group1" not defined in [acl.groups]
1308 acl: acl.allow enabled, 1 entries for user fred
1290 acl: acl.allow enabled, 1 entries for user fred
1309 acl: "group1" not defined in [acl.groups]
1291 acl: "group1" not defined in [acl.groups]
1310 acl: acl.deny enabled, 1 entries for user fred
1292 acl: acl.deny enabled, 1 entries for user fred
1311 acl: branch access granted: "ef1ea85a6374" on branch "default"
1293 acl: branch access granted: "ef1ea85a6374" on branch "default"
1312 acl: allowing changeset ef1ea85a6374
1294 acl: allowing changeset ef1ea85a6374
1313 acl: branch access granted: "f9cafe1212c8" on branch "default"
1295 acl: branch access granted: "f9cafe1212c8" on branch "default"
1314 acl: user fred denied on foo/Bar/file.txt
1296 acl: user fred denied on foo/Bar/file.txt
1315 error: pretxnchangegroup.acl hook failed: acl: access denied for changeset f9cafe1212c8
1297 error: pretxnchangegroup.acl hook failed: acl: access denied for changeset f9cafe1212c8
1316 transaction abort!
1298 transaction abort!
1317 rollback completed
1299 rollback completed
1318 abort: acl: access denied for changeset f9cafe1212c8
1300 abort: acl: access denied for changeset f9cafe1212c8
1319 no rollback information available
1301 no rollback information available
1320 0:6675d58eff77
1302 0:6675d58eff77
1321
1303
1322
1304
1323 Invalid group
1305 Invalid group
1324
1306
1325 Disable the fakegroups trick to get real failures
1307 Disable the fakegroups trick to get real failures
1326
1308
1327 $ grep -v fakegroups $config > config.tmp
1309 $ grep -v fakegroups $config > config.tmp
1328 $ mv config.tmp $config
1310 $ mv config.tmp $config
1329 $ echo '[acl.allow]' >> $config
1311 $ echo '[acl.allow]' >> $config
1330 $ echo "** = @unlikelytoexist" >> $config
1312 $ echo "** = @unlikelytoexist" >> $config
1331 $ do_push fred 2>&1 | grep unlikelytoexist
1313 $ do_push fred 2>&1 | grep unlikelytoexist
1332 ** = @unlikelytoexist
1314 ** = @unlikelytoexist
1333 acl: "unlikelytoexist" not defined in [acl.groups]
1315 acl: "unlikelytoexist" not defined in [acl.groups]
1334 error: pretxnchangegroup.acl hook failed: group 'unlikelytoexist' is undefined
1316 error: pretxnchangegroup.acl hook failed: group 'unlikelytoexist' is undefined
1335 abort: group 'unlikelytoexist' is undefined
1317 abort: group 'unlikelytoexist' is undefined
1336
1318
1337
1319
1338 Branch acl tests setup
1320 Branch acl tests setup
1339
1321
1340 $ init_config
1322 $ init_config
1341 $ cd b
1323 $ cd b
1342 $ hg up
1324 $ hg up
1343 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1325 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1344 $ hg branch foobar
1326 $ hg branch foobar
1345 marked working directory as branch foobar
1327 marked working directory as branch foobar
1346 $ hg commit -m 'create foobar'
1328 $ hg commit -m 'create foobar'
1347 $ echo 'foo contents' > abc.txt
1329 $ echo 'foo contents' > abc.txt
1348 $ hg add abc.txt
1330 $ hg add abc.txt
1349 $ hg commit -m 'foobar contents'
1331 $ hg commit -m 'foobar contents'
1350 $ cd ..
1332 $ cd ..
1351 $ hg --cwd a pull ../b
1333 $ hg --cwd a pull ../b
1352 pulling from ../b
1334 pulling from ../b
1353 searching for changes
1335 searching for changes
1354 adding changesets
1336 adding changesets
1355 adding manifests
1337 adding manifests
1356 adding file changes
1338 adding file changes
1357 added 2 changesets with 1 changes to 1 files (+1 heads)
1339 added 2 changesets with 1 changes to 1 files (+1 heads)
1358 (run 'hg heads' to see heads)
1340 (run 'hg heads' to see heads)
1359
1341
1360 Create additional changeset on foobar branch
1342 Create additional changeset on foobar branch
1361
1343
1362 $ cd a
1344 $ cd a
1363 $ hg up -C foobar
1345 $ hg up -C foobar
1364 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
1346 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
1365 $ echo 'foo contents2' > abc.txt
1347 $ echo 'foo contents2' > abc.txt
1366 $ hg commit -m 'foobar contents2'
1348 $ hg commit -m 'foobar contents2'
1367 $ cd ..
1349 $ cd ..
1368
1350
1369
1351
1370 No branch acls specified
1352 No branch acls specified
1371
1353
1372 $ do_push astro
1354 $ do_push astro
1373 Pushing as user astro
1355 Pushing as user astro
1374 hgrc = """
1356 hgrc = """
1375 [acl]
1357 [acl]
1376 sources = push
1358 sources = push
1377 [extensions]
1359 [extensions]
1378 """
1360 """
1379 pushing to ../b
1361 pushing to ../b
1380 searching for changes
1362 searching for changes
1381 common changesets up to 07e028174695
1382 4 changesets found
1363 4 changesets found
1383 list of changesets:
1364 list of changesets:
1384 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1365 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1385 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1366 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1386 911600dab2ae7a9baff75958b84fe606851ce955
1367 911600dab2ae7a9baff75958b84fe606851ce955
1387 4ea792ff64284af438188103a0ee8aca1724fb8c
1368 4ea792ff64284af438188103a0ee8aca1724fb8c
1388 adding changesets
1369 adding changesets
1389 bundling: 1 changesets
1370 bundling: 1 changesets
1390 bundling: 2 changesets
1371 bundling: 2 changesets
1391 bundling: 3 changesets
1372 bundling: 3 changesets
1392 bundling: 4 changesets
1373 bundling: 4 changesets
1393 bundling: 1/4 manifests (25.00%)
1374 bundling: 1/4 manifests (25.00%)
1394 bundling: 2/4 manifests (50.00%)
1375 bundling: 2/4 manifests (50.00%)
1395 bundling: 3/4 manifests (75.00%)
1376 bundling: 3/4 manifests (75.00%)
1396 bundling: 4/4 manifests (100.00%)
1377 bundling: 4/4 manifests (100.00%)
1397 bundling: abc.txt 0/4 files (0.00%)
1378 bundling: abc.txt 0/4 files (0.00%)
1398 bundling: foo/Bar/file.txt 1/4 files (25.00%)
1379 bundling: foo/Bar/file.txt 1/4 files (25.00%)
1399 bundling: foo/file.txt 2/4 files (50.00%)
1380 bundling: foo/file.txt 2/4 files (50.00%)
1400 bundling: quux/file.py 3/4 files (75.00%)
1381 bundling: quux/file.py 3/4 files (75.00%)
1401 changesets: 1 chunks
1382 changesets: 1 chunks
1402 add changeset ef1ea85a6374
1383 add changeset ef1ea85a6374
1403 changesets: 2 chunks
1384 changesets: 2 chunks
1404 add changeset f9cafe1212c8
1385 add changeset f9cafe1212c8
1405 changesets: 3 chunks
1386 changesets: 3 chunks
1406 add changeset 911600dab2ae
1387 add changeset 911600dab2ae
1407 changesets: 4 chunks
1388 changesets: 4 chunks
1408 add changeset 4ea792ff6428
1389 add changeset 4ea792ff6428
1409 adding manifests
1390 adding manifests
1410 manifests: 1/4 chunks (25.00%)
1391 manifests: 1/4 chunks (25.00%)
1411 manifests: 2/4 chunks (50.00%)
1392 manifests: 2/4 chunks (50.00%)
1412 manifests: 3/4 chunks (75.00%)
1393 manifests: 3/4 chunks (75.00%)
1413 manifests: 4/4 chunks (100.00%)
1394 manifests: 4/4 chunks (100.00%)
1414 adding file changes
1395 adding file changes
1415 adding abc.txt revisions
1396 adding abc.txt revisions
1416 files: 1/4 chunks (25.00%)
1397 files: 1/4 chunks (25.00%)
1417 adding foo/Bar/file.txt revisions
1398 adding foo/Bar/file.txt revisions
1418 files: 2/4 chunks (50.00%)
1399 files: 2/4 chunks (50.00%)
1419 adding foo/file.txt revisions
1400 adding foo/file.txt revisions
1420 files: 3/4 chunks (75.00%)
1401 files: 3/4 chunks (75.00%)
1421 adding quux/file.py revisions
1402 adding quux/file.py revisions
1422 files: 4/4 chunks (100.00%)
1403 files: 4/4 chunks (100.00%)
1423 added 4 changesets with 4 changes to 4 files (+1 heads)
1404 added 4 changesets with 4 changes to 4 files (+1 heads)
1424 calling hook pretxnchangegroup.acl: hgext.acl.hook
1405 calling hook pretxnchangegroup.acl: hgext.acl.hook
1425 acl: acl.allow.branches not enabled
1406 acl: acl.allow.branches not enabled
1426 acl: acl.deny.branches not enabled
1407 acl: acl.deny.branches not enabled
1427 acl: acl.allow not enabled
1408 acl: acl.allow not enabled
1428 acl: acl.deny not enabled
1409 acl: acl.deny not enabled
1429 acl: branch access granted: "ef1ea85a6374" on branch "default"
1410 acl: branch access granted: "ef1ea85a6374" on branch "default"
1430 acl: allowing changeset ef1ea85a6374
1411 acl: allowing changeset ef1ea85a6374
1431 acl: branch access granted: "f9cafe1212c8" on branch "default"
1412 acl: branch access granted: "f9cafe1212c8" on branch "default"
1432 acl: allowing changeset f9cafe1212c8
1413 acl: allowing changeset f9cafe1212c8
1433 acl: branch access granted: "911600dab2ae" on branch "default"
1414 acl: branch access granted: "911600dab2ae" on branch "default"
1434 acl: allowing changeset 911600dab2ae
1415 acl: allowing changeset 911600dab2ae
1435 acl: branch access granted: "4ea792ff6428" on branch "foobar"
1416 acl: branch access granted: "4ea792ff6428" on branch "foobar"
1436 acl: allowing changeset 4ea792ff6428
1417 acl: allowing changeset 4ea792ff6428
1437 updating the branch cache
1418 updating the branch cache
1438 checking for updated bookmarks
1419 checking for updated bookmarks
1439 repository tip rolled back to revision 2 (undo push)
1420 repository tip rolled back to revision 2 (undo push)
1440 working directory now based on revision 2
1421 working directory now based on revision 2
1441 2:07e028174695
1422 2:07e028174695
1442
1423
1443
1424
1444 Branch acl deny test
1425 Branch acl deny test
1445
1426
1446 $ echo "[acl.deny.branches]" >> $config
1427 $ echo "[acl.deny.branches]" >> $config
1447 $ echo "foobar = *" >> $config
1428 $ echo "foobar = *" >> $config
1448 $ do_push astro
1429 $ do_push astro
1449 Pushing as user astro
1430 Pushing as user astro
1450 hgrc = """
1431 hgrc = """
1451 [acl]
1432 [acl]
1452 sources = push
1433 sources = push
1453 [extensions]
1434 [extensions]
1454 [acl.deny.branches]
1435 [acl.deny.branches]
1455 foobar = *
1436 foobar = *
1456 """
1437 """
1457 pushing to ../b
1438 pushing to ../b
1458 searching for changes
1439 searching for changes
1459 common changesets up to 07e028174695
1460 invalidating branch cache (tip differs)
1440 invalidating branch cache (tip differs)
1461 4 changesets found
1441 4 changesets found
1462 list of changesets:
1442 list of changesets:
1463 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1443 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1464 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1444 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1465 911600dab2ae7a9baff75958b84fe606851ce955
1445 911600dab2ae7a9baff75958b84fe606851ce955
1466 4ea792ff64284af438188103a0ee8aca1724fb8c
1446 4ea792ff64284af438188103a0ee8aca1724fb8c
1467 adding changesets
1447 adding changesets
1468 bundling: 1 changesets
1448 bundling: 1 changesets
1469 bundling: 2 changesets
1449 bundling: 2 changesets
1470 bundling: 3 changesets
1450 bundling: 3 changesets
1471 bundling: 4 changesets
1451 bundling: 4 changesets
1472 bundling: 1/4 manifests (25.00%)
1452 bundling: 1/4 manifests (25.00%)
1473 bundling: 2/4 manifests (50.00%)
1453 bundling: 2/4 manifests (50.00%)
1474 bundling: 3/4 manifests (75.00%)
1454 bundling: 3/4 manifests (75.00%)
1475 bundling: 4/4 manifests (100.00%)
1455 bundling: 4/4 manifests (100.00%)
1476 bundling: abc.txt 0/4 files (0.00%)
1456 bundling: abc.txt 0/4 files (0.00%)
1477 bundling: foo/Bar/file.txt 1/4 files (25.00%)
1457 bundling: foo/Bar/file.txt 1/4 files (25.00%)
1478 bundling: foo/file.txt 2/4 files (50.00%)
1458 bundling: foo/file.txt 2/4 files (50.00%)
1479 bundling: quux/file.py 3/4 files (75.00%)
1459 bundling: quux/file.py 3/4 files (75.00%)
1480 changesets: 1 chunks
1460 changesets: 1 chunks
1481 add changeset ef1ea85a6374
1461 add changeset ef1ea85a6374
1482 changesets: 2 chunks
1462 changesets: 2 chunks
1483 add changeset f9cafe1212c8
1463 add changeset f9cafe1212c8
1484 changesets: 3 chunks
1464 changesets: 3 chunks
1485 add changeset 911600dab2ae
1465 add changeset 911600dab2ae
1486 changesets: 4 chunks
1466 changesets: 4 chunks
1487 add changeset 4ea792ff6428
1467 add changeset 4ea792ff6428
1488 adding manifests
1468 adding manifests
1489 manifests: 1/4 chunks (25.00%)
1469 manifests: 1/4 chunks (25.00%)
1490 manifests: 2/4 chunks (50.00%)
1470 manifests: 2/4 chunks (50.00%)
1491 manifests: 3/4 chunks (75.00%)
1471 manifests: 3/4 chunks (75.00%)
1492 manifests: 4/4 chunks (100.00%)
1472 manifests: 4/4 chunks (100.00%)
1493 adding file changes
1473 adding file changes
1494 adding abc.txt revisions
1474 adding abc.txt revisions
1495 files: 1/4 chunks (25.00%)
1475 files: 1/4 chunks (25.00%)
1496 adding foo/Bar/file.txt revisions
1476 adding foo/Bar/file.txt revisions
1497 files: 2/4 chunks (50.00%)
1477 files: 2/4 chunks (50.00%)
1498 adding foo/file.txt revisions
1478 adding foo/file.txt revisions
1499 files: 3/4 chunks (75.00%)
1479 files: 3/4 chunks (75.00%)
1500 adding quux/file.py revisions
1480 adding quux/file.py revisions
1501 files: 4/4 chunks (100.00%)
1481 files: 4/4 chunks (100.00%)
1502 added 4 changesets with 4 changes to 4 files (+1 heads)
1482 added 4 changesets with 4 changes to 4 files (+1 heads)
1503 calling hook pretxnchangegroup.acl: hgext.acl.hook
1483 calling hook pretxnchangegroup.acl: hgext.acl.hook
1504 acl: acl.allow.branches not enabled
1484 acl: acl.allow.branches not enabled
1505 acl: acl.deny.branches enabled, 1 entries for user astro
1485 acl: acl.deny.branches enabled, 1 entries for user astro
1506 acl: acl.allow not enabled
1486 acl: acl.allow not enabled
1507 acl: acl.deny not enabled
1487 acl: acl.deny not enabled
1508 acl: branch access granted: "ef1ea85a6374" on branch "default"
1488 acl: branch access granted: "ef1ea85a6374" on branch "default"
1509 acl: allowing changeset ef1ea85a6374
1489 acl: allowing changeset ef1ea85a6374
1510 acl: branch access granted: "f9cafe1212c8" on branch "default"
1490 acl: branch access granted: "f9cafe1212c8" on branch "default"
1511 acl: allowing changeset f9cafe1212c8
1491 acl: allowing changeset f9cafe1212c8
1512 acl: branch access granted: "911600dab2ae" on branch "default"
1492 acl: branch access granted: "911600dab2ae" on branch "default"
1513 acl: allowing changeset 911600dab2ae
1493 acl: allowing changeset 911600dab2ae
1514 error: pretxnchangegroup.acl hook failed: acl: user "astro" denied on branch "foobar" (changeset "4ea792ff6428")
1494 error: pretxnchangegroup.acl hook failed: acl: user "astro" denied on branch "foobar" (changeset "4ea792ff6428")
1515 transaction abort!
1495 transaction abort!
1516 rollback completed
1496 rollback completed
1517 abort: acl: user "astro" denied on branch "foobar" (changeset "4ea792ff6428")
1497 abort: acl: user "astro" denied on branch "foobar" (changeset "4ea792ff6428")
1518 no rollback information available
1498 no rollback information available
1519 2:07e028174695
1499 2:07e028174695
1520
1500
1521
1501
1522 Branch acl empty allow test
1502 Branch acl empty allow test
1523
1503
1524 $ init_config
1504 $ init_config
1525 $ echo "[acl.allow.branches]" >> $config
1505 $ echo "[acl.allow.branches]" >> $config
1526 $ do_push astro
1506 $ do_push astro
1527 Pushing as user astro
1507 Pushing as user astro
1528 hgrc = """
1508 hgrc = """
1529 [acl]
1509 [acl]
1530 sources = push
1510 sources = push
1531 [extensions]
1511 [extensions]
1532 [acl.allow.branches]
1512 [acl.allow.branches]
1533 """
1513 """
1534 pushing to ../b
1514 pushing to ../b
1535 searching for changes
1515 searching for changes
1536 common changesets up to 07e028174695
1537 4 changesets found
1516 4 changesets found
1538 list of changesets:
1517 list of changesets:
1539 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1518 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1540 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1519 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1541 911600dab2ae7a9baff75958b84fe606851ce955
1520 911600dab2ae7a9baff75958b84fe606851ce955
1542 4ea792ff64284af438188103a0ee8aca1724fb8c
1521 4ea792ff64284af438188103a0ee8aca1724fb8c
1543 adding changesets
1522 adding changesets
1544 bundling: 1 changesets
1523 bundling: 1 changesets
1545 bundling: 2 changesets
1524 bundling: 2 changesets
1546 bundling: 3 changesets
1525 bundling: 3 changesets
1547 bundling: 4 changesets
1526 bundling: 4 changesets
1548 bundling: 1/4 manifests (25.00%)
1527 bundling: 1/4 manifests (25.00%)
1549 bundling: 2/4 manifests (50.00%)
1528 bundling: 2/4 manifests (50.00%)
1550 bundling: 3/4 manifests (75.00%)
1529 bundling: 3/4 manifests (75.00%)
1551 bundling: 4/4 manifests (100.00%)
1530 bundling: 4/4 manifests (100.00%)
1552 bundling: abc.txt 0/4 files (0.00%)
1531 bundling: abc.txt 0/4 files (0.00%)
1553 bundling: foo/Bar/file.txt 1/4 files (25.00%)
1532 bundling: foo/Bar/file.txt 1/4 files (25.00%)
1554 bundling: foo/file.txt 2/4 files (50.00%)
1533 bundling: foo/file.txt 2/4 files (50.00%)
1555 bundling: quux/file.py 3/4 files (75.00%)
1534 bundling: quux/file.py 3/4 files (75.00%)
1556 changesets: 1 chunks
1535 changesets: 1 chunks
1557 add changeset ef1ea85a6374
1536 add changeset ef1ea85a6374
1558 changesets: 2 chunks
1537 changesets: 2 chunks
1559 add changeset f9cafe1212c8
1538 add changeset f9cafe1212c8
1560 changesets: 3 chunks
1539 changesets: 3 chunks
1561 add changeset 911600dab2ae
1540 add changeset 911600dab2ae
1562 changesets: 4 chunks
1541 changesets: 4 chunks
1563 add changeset 4ea792ff6428
1542 add changeset 4ea792ff6428
1564 adding manifests
1543 adding manifests
1565 manifests: 1/4 chunks (25.00%)
1544 manifests: 1/4 chunks (25.00%)
1566 manifests: 2/4 chunks (50.00%)
1545 manifests: 2/4 chunks (50.00%)
1567 manifests: 3/4 chunks (75.00%)
1546 manifests: 3/4 chunks (75.00%)
1568 manifests: 4/4 chunks (100.00%)
1547 manifests: 4/4 chunks (100.00%)
1569 adding file changes
1548 adding file changes
1570 adding abc.txt revisions
1549 adding abc.txt revisions
1571 files: 1/4 chunks (25.00%)
1550 files: 1/4 chunks (25.00%)
1572 adding foo/Bar/file.txt revisions
1551 adding foo/Bar/file.txt revisions
1573 files: 2/4 chunks (50.00%)
1552 files: 2/4 chunks (50.00%)
1574 adding foo/file.txt revisions
1553 adding foo/file.txt revisions
1575 files: 3/4 chunks (75.00%)
1554 files: 3/4 chunks (75.00%)
1576 adding quux/file.py revisions
1555 adding quux/file.py revisions
1577 files: 4/4 chunks (100.00%)
1556 files: 4/4 chunks (100.00%)
1578 added 4 changesets with 4 changes to 4 files (+1 heads)
1557 added 4 changesets with 4 changes to 4 files (+1 heads)
1579 calling hook pretxnchangegroup.acl: hgext.acl.hook
1558 calling hook pretxnchangegroup.acl: hgext.acl.hook
1580 acl: acl.allow.branches enabled, 0 entries for user astro
1559 acl: acl.allow.branches enabled, 0 entries for user astro
1581 acl: acl.deny.branches not enabled
1560 acl: acl.deny.branches not enabled
1582 acl: acl.allow not enabled
1561 acl: acl.allow not enabled
1583 acl: acl.deny not enabled
1562 acl: acl.deny not enabled
1584 error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1563 error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1585 transaction abort!
1564 transaction abort!
1586 rollback completed
1565 rollback completed
1587 abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1566 abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1588 no rollback information available
1567 no rollback information available
1589 2:07e028174695
1568 2:07e028174695
1590
1569
1591
1570
1592 Branch acl allow other
1571 Branch acl allow other
1593
1572
1594 $ init_config
1573 $ init_config
1595 $ echo "[acl.allow.branches]" >> $config
1574 $ echo "[acl.allow.branches]" >> $config
1596 $ echo "* = george" >> $config
1575 $ echo "* = george" >> $config
1597 $ do_push astro
1576 $ do_push astro
1598 Pushing as user astro
1577 Pushing as user astro
1599 hgrc = """
1578 hgrc = """
1600 [acl]
1579 [acl]
1601 sources = push
1580 sources = push
1602 [extensions]
1581 [extensions]
1603 [acl.allow.branches]
1582 [acl.allow.branches]
1604 * = george
1583 * = george
1605 """
1584 """
1606 pushing to ../b
1585 pushing to ../b
1607 searching for changes
1586 searching for changes
1608 common changesets up to 07e028174695
1609 4 changesets found
1587 4 changesets found
1610 list of changesets:
1588 list of changesets:
1611 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1589 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1612 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1590 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1613 911600dab2ae7a9baff75958b84fe606851ce955
1591 911600dab2ae7a9baff75958b84fe606851ce955
1614 4ea792ff64284af438188103a0ee8aca1724fb8c
1592 4ea792ff64284af438188103a0ee8aca1724fb8c
1615 adding changesets
1593 adding changesets
1616 bundling: 1 changesets
1594 bundling: 1 changesets
1617 bundling: 2 changesets
1595 bundling: 2 changesets
1618 bundling: 3 changesets
1596 bundling: 3 changesets
1619 bundling: 4 changesets
1597 bundling: 4 changesets
1620 bundling: 1/4 manifests (25.00%)
1598 bundling: 1/4 manifests (25.00%)
1621 bundling: 2/4 manifests (50.00%)
1599 bundling: 2/4 manifests (50.00%)
1622 bundling: 3/4 manifests (75.00%)
1600 bundling: 3/4 manifests (75.00%)
1623 bundling: 4/4 manifests (100.00%)
1601 bundling: 4/4 manifests (100.00%)
1624 bundling: abc.txt 0/4 files (0.00%)
1602 bundling: abc.txt 0/4 files (0.00%)
1625 bundling: foo/Bar/file.txt 1/4 files (25.00%)
1603 bundling: foo/Bar/file.txt 1/4 files (25.00%)
1626 bundling: foo/file.txt 2/4 files (50.00%)
1604 bundling: foo/file.txt 2/4 files (50.00%)
1627 bundling: quux/file.py 3/4 files (75.00%)
1605 bundling: quux/file.py 3/4 files (75.00%)
1628 changesets: 1 chunks
1606 changesets: 1 chunks
1629 add changeset ef1ea85a6374
1607 add changeset ef1ea85a6374
1630 changesets: 2 chunks
1608 changesets: 2 chunks
1631 add changeset f9cafe1212c8
1609 add changeset f9cafe1212c8
1632 changesets: 3 chunks
1610 changesets: 3 chunks
1633 add changeset 911600dab2ae
1611 add changeset 911600dab2ae
1634 changesets: 4 chunks
1612 changesets: 4 chunks
1635 add changeset 4ea792ff6428
1613 add changeset 4ea792ff6428
1636 adding manifests
1614 adding manifests
1637 manifests: 1/4 chunks (25.00%)
1615 manifests: 1/4 chunks (25.00%)
1638 manifests: 2/4 chunks (50.00%)
1616 manifests: 2/4 chunks (50.00%)
1639 manifests: 3/4 chunks (75.00%)
1617 manifests: 3/4 chunks (75.00%)
1640 manifests: 4/4 chunks (100.00%)
1618 manifests: 4/4 chunks (100.00%)
1641 adding file changes
1619 adding file changes
1642 adding abc.txt revisions
1620 adding abc.txt revisions
1643 files: 1/4 chunks (25.00%)
1621 files: 1/4 chunks (25.00%)
1644 adding foo/Bar/file.txt revisions
1622 adding foo/Bar/file.txt revisions
1645 files: 2/4 chunks (50.00%)
1623 files: 2/4 chunks (50.00%)
1646 adding foo/file.txt revisions
1624 adding foo/file.txt revisions
1647 files: 3/4 chunks (75.00%)
1625 files: 3/4 chunks (75.00%)
1648 adding quux/file.py revisions
1626 adding quux/file.py revisions
1649 files: 4/4 chunks (100.00%)
1627 files: 4/4 chunks (100.00%)
1650 added 4 changesets with 4 changes to 4 files (+1 heads)
1628 added 4 changesets with 4 changes to 4 files (+1 heads)
1651 calling hook pretxnchangegroup.acl: hgext.acl.hook
1629 calling hook pretxnchangegroup.acl: hgext.acl.hook
1652 acl: acl.allow.branches enabled, 0 entries for user astro
1630 acl: acl.allow.branches enabled, 0 entries for user astro
1653 acl: acl.deny.branches not enabled
1631 acl: acl.deny.branches not enabled
1654 acl: acl.allow not enabled
1632 acl: acl.allow not enabled
1655 acl: acl.deny not enabled
1633 acl: acl.deny not enabled
1656 error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1634 error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1657 transaction abort!
1635 transaction abort!
1658 rollback completed
1636 rollback completed
1659 abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1637 abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1660 no rollback information available
1638 no rollback information available
1661 2:07e028174695
1639 2:07e028174695
1662
1640
1663 $ do_push george
1641 $ do_push george
1664 Pushing as user george
1642 Pushing as user george
1665 hgrc = """
1643 hgrc = """
1666 [acl]
1644 [acl]
1667 sources = push
1645 sources = push
1668 [extensions]
1646 [extensions]
1669 [acl.allow.branches]
1647 [acl.allow.branches]
1670 * = george
1648 * = george
1671 """
1649 """
1672 pushing to ../b
1650 pushing to ../b
1673 searching for changes
1651 searching for changes
1674 common changesets up to 07e028174695
1675 4 changesets found
1652 4 changesets found
1676 list of changesets:
1653 list of changesets:
1677 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1654 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1678 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1655 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1679 911600dab2ae7a9baff75958b84fe606851ce955
1656 911600dab2ae7a9baff75958b84fe606851ce955
1680 4ea792ff64284af438188103a0ee8aca1724fb8c
1657 4ea792ff64284af438188103a0ee8aca1724fb8c
1681 adding changesets
1658 adding changesets
1682 bundling: 1 changesets
1659 bundling: 1 changesets
1683 bundling: 2 changesets
1660 bundling: 2 changesets
1684 bundling: 3 changesets
1661 bundling: 3 changesets
1685 bundling: 4 changesets
1662 bundling: 4 changesets
1686 bundling: 1/4 manifests (25.00%)
1663 bundling: 1/4 manifests (25.00%)
1687 bundling: 2/4 manifests (50.00%)
1664 bundling: 2/4 manifests (50.00%)
1688 bundling: 3/4 manifests (75.00%)
1665 bundling: 3/4 manifests (75.00%)
1689 bundling: 4/4 manifests (100.00%)
1666 bundling: 4/4 manifests (100.00%)
1690 bundling: abc.txt 0/4 files (0.00%)
1667 bundling: abc.txt 0/4 files (0.00%)
1691 bundling: foo/Bar/file.txt 1/4 files (25.00%)
1668 bundling: foo/Bar/file.txt 1/4 files (25.00%)
1692 bundling: foo/file.txt 2/4 files (50.00%)
1669 bundling: foo/file.txt 2/4 files (50.00%)
1693 bundling: quux/file.py 3/4 files (75.00%)
1670 bundling: quux/file.py 3/4 files (75.00%)
1694 changesets: 1 chunks
1671 changesets: 1 chunks
1695 add changeset ef1ea85a6374
1672 add changeset ef1ea85a6374
1696 changesets: 2 chunks
1673 changesets: 2 chunks
1697 add changeset f9cafe1212c8
1674 add changeset f9cafe1212c8
1698 changesets: 3 chunks
1675 changesets: 3 chunks
1699 add changeset 911600dab2ae
1676 add changeset 911600dab2ae
1700 changesets: 4 chunks
1677 changesets: 4 chunks
1701 add changeset 4ea792ff6428
1678 add changeset 4ea792ff6428
1702 adding manifests
1679 adding manifests
1703 manifests: 1/4 chunks (25.00%)
1680 manifests: 1/4 chunks (25.00%)
1704 manifests: 2/4 chunks (50.00%)
1681 manifests: 2/4 chunks (50.00%)
1705 manifests: 3/4 chunks (75.00%)
1682 manifests: 3/4 chunks (75.00%)
1706 manifests: 4/4 chunks (100.00%)
1683 manifests: 4/4 chunks (100.00%)
1707 adding file changes
1684 adding file changes
1708 adding abc.txt revisions
1685 adding abc.txt revisions
1709 files: 1/4 chunks (25.00%)
1686 files: 1/4 chunks (25.00%)
1710 adding foo/Bar/file.txt revisions
1687 adding foo/Bar/file.txt revisions
1711 files: 2/4 chunks (50.00%)
1688 files: 2/4 chunks (50.00%)
1712 adding foo/file.txt revisions
1689 adding foo/file.txt revisions
1713 files: 3/4 chunks (75.00%)
1690 files: 3/4 chunks (75.00%)
1714 adding quux/file.py revisions
1691 adding quux/file.py revisions
1715 files: 4/4 chunks (100.00%)
1692 files: 4/4 chunks (100.00%)
1716 added 4 changesets with 4 changes to 4 files (+1 heads)
1693 added 4 changesets with 4 changes to 4 files (+1 heads)
1717 calling hook pretxnchangegroup.acl: hgext.acl.hook
1694 calling hook pretxnchangegroup.acl: hgext.acl.hook
1718 acl: acl.allow.branches enabled, 1 entries for user george
1695 acl: acl.allow.branches enabled, 1 entries for user george
1719 acl: acl.deny.branches not enabled
1696 acl: acl.deny.branches not enabled
1720 acl: acl.allow not enabled
1697 acl: acl.allow not enabled
1721 acl: acl.deny not enabled
1698 acl: acl.deny not enabled
1722 acl: branch access granted: "ef1ea85a6374" on branch "default"
1699 acl: branch access granted: "ef1ea85a6374" on branch "default"
1723 acl: allowing changeset ef1ea85a6374
1700 acl: allowing changeset ef1ea85a6374
1724 acl: branch access granted: "f9cafe1212c8" on branch "default"
1701 acl: branch access granted: "f9cafe1212c8" on branch "default"
1725 acl: allowing changeset f9cafe1212c8
1702 acl: allowing changeset f9cafe1212c8
1726 acl: branch access granted: "911600dab2ae" on branch "default"
1703 acl: branch access granted: "911600dab2ae" on branch "default"
1727 acl: allowing changeset 911600dab2ae
1704 acl: allowing changeset 911600dab2ae
1728 acl: branch access granted: "4ea792ff6428" on branch "foobar"
1705 acl: branch access granted: "4ea792ff6428" on branch "foobar"
1729 acl: allowing changeset 4ea792ff6428
1706 acl: allowing changeset 4ea792ff6428
1730 updating the branch cache
1707 updating the branch cache
1731 checking for updated bookmarks
1708 checking for updated bookmarks
1732 repository tip rolled back to revision 2 (undo push)
1709 repository tip rolled back to revision 2 (undo push)
1733 working directory now based on revision 2
1710 working directory now based on revision 2
1734 2:07e028174695
1711 2:07e028174695
1735
1712
1736
1713
1737 Branch acl conflicting allow
1714 Branch acl conflicting allow
1738 asterisk ends up applying to all branches and allowing george to
1715 asterisk ends up applying to all branches and allowing george to
1739 push foobar into the remote
1716 push foobar into the remote
1740
1717
1741 $ init_config
1718 $ init_config
1742 $ echo "[acl.allow.branches]" >> $config
1719 $ echo "[acl.allow.branches]" >> $config
1743 $ echo "foobar = astro" >> $config
1720 $ echo "foobar = astro" >> $config
1744 $ echo "* = george" >> $config
1721 $ echo "* = george" >> $config
1745 $ do_push george
1722 $ do_push george
1746 Pushing as user george
1723 Pushing as user george
1747 hgrc = """
1724 hgrc = """
1748 [acl]
1725 [acl]
1749 sources = push
1726 sources = push
1750 [extensions]
1727 [extensions]
1751 [acl.allow.branches]
1728 [acl.allow.branches]
1752 foobar = astro
1729 foobar = astro
1753 * = george
1730 * = george
1754 """
1731 """
1755 pushing to ../b
1732 pushing to ../b
1756 searching for changes
1733 searching for changes
1757 common changesets up to 07e028174695
1758 invalidating branch cache (tip differs)
1734 invalidating branch cache (tip differs)
1759 4 changesets found
1735 4 changesets found
1760 list of changesets:
1736 list of changesets:
1761 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1737 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1762 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1738 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1763 911600dab2ae7a9baff75958b84fe606851ce955
1739 911600dab2ae7a9baff75958b84fe606851ce955
1764 4ea792ff64284af438188103a0ee8aca1724fb8c
1740 4ea792ff64284af438188103a0ee8aca1724fb8c
1765 adding changesets
1741 adding changesets
1766 bundling: 1 changesets
1742 bundling: 1 changesets
1767 bundling: 2 changesets
1743 bundling: 2 changesets
1768 bundling: 3 changesets
1744 bundling: 3 changesets
1769 bundling: 4 changesets
1745 bundling: 4 changesets
1770 bundling: 1/4 manifests (25.00%)
1746 bundling: 1/4 manifests (25.00%)
1771 bundling: 2/4 manifests (50.00%)
1747 bundling: 2/4 manifests (50.00%)
1772 bundling: 3/4 manifests (75.00%)
1748 bundling: 3/4 manifests (75.00%)
1773 bundling: 4/4 manifests (100.00%)
1749 bundling: 4/4 manifests (100.00%)
1774 bundling: abc.txt 0/4 files (0.00%)
1750 bundling: abc.txt 0/4 files (0.00%)
1775 bundling: foo/Bar/file.txt 1/4 files (25.00%)
1751 bundling: foo/Bar/file.txt 1/4 files (25.00%)
1776 bundling: foo/file.txt 2/4 files (50.00%)
1752 bundling: foo/file.txt 2/4 files (50.00%)
1777 bundling: quux/file.py 3/4 files (75.00%)
1753 bundling: quux/file.py 3/4 files (75.00%)
1778 changesets: 1 chunks
1754 changesets: 1 chunks
1779 add changeset ef1ea85a6374
1755 add changeset ef1ea85a6374
1780 changesets: 2 chunks
1756 changesets: 2 chunks
1781 add changeset f9cafe1212c8
1757 add changeset f9cafe1212c8
1782 changesets: 3 chunks
1758 changesets: 3 chunks
1783 add changeset 911600dab2ae
1759 add changeset 911600dab2ae
1784 changesets: 4 chunks
1760 changesets: 4 chunks
1785 add changeset 4ea792ff6428
1761 add changeset 4ea792ff6428
1786 adding manifests
1762 adding manifests
1787 manifests: 1/4 chunks (25.00%)
1763 manifests: 1/4 chunks (25.00%)
1788 manifests: 2/4 chunks (50.00%)
1764 manifests: 2/4 chunks (50.00%)
1789 manifests: 3/4 chunks (75.00%)
1765 manifests: 3/4 chunks (75.00%)
1790 manifests: 4/4 chunks (100.00%)
1766 manifests: 4/4 chunks (100.00%)
1791 adding file changes
1767 adding file changes
1792 adding abc.txt revisions
1768 adding abc.txt revisions
1793 files: 1/4 chunks (25.00%)
1769 files: 1/4 chunks (25.00%)
1794 adding foo/Bar/file.txt revisions
1770 adding foo/Bar/file.txt revisions
1795 files: 2/4 chunks (50.00%)
1771 files: 2/4 chunks (50.00%)
1796 adding foo/file.txt revisions
1772 adding foo/file.txt revisions
1797 files: 3/4 chunks (75.00%)
1773 files: 3/4 chunks (75.00%)
1798 adding quux/file.py revisions
1774 adding quux/file.py revisions
1799 files: 4/4 chunks (100.00%)
1775 files: 4/4 chunks (100.00%)
1800 added 4 changesets with 4 changes to 4 files (+1 heads)
1776 added 4 changesets with 4 changes to 4 files (+1 heads)
1801 calling hook pretxnchangegroup.acl: hgext.acl.hook
1777 calling hook pretxnchangegroup.acl: hgext.acl.hook
1802 acl: acl.allow.branches enabled, 1 entries for user george
1778 acl: acl.allow.branches enabled, 1 entries for user george
1803 acl: acl.deny.branches not enabled
1779 acl: acl.deny.branches not enabled
1804 acl: acl.allow not enabled
1780 acl: acl.allow not enabled
1805 acl: acl.deny not enabled
1781 acl: acl.deny not enabled
1806 acl: branch access granted: "ef1ea85a6374" on branch "default"
1782 acl: branch access granted: "ef1ea85a6374" on branch "default"
1807 acl: allowing changeset ef1ea85a6374
1783 acl: allowing changeset ef1ea85a6374
1808 acl: branch access granted: "f9cafe1212c8" on branch "default"
1784 acl: branch access granted: "f9cafe1212c8" on branch "default"
1809 acl: allowing changeset f9cafe1212c8
1785 acl: allowing changeset f9cafe1212c8
1810 acl: branch access granted: "911600dab2ae" on branch "default"
1786 acl: branch access granted: "911600dab2ae" on branch "default"
1811 acl: allowing changeset 911600dab2ae
1787 acl: allowing changeset 911600dab2ae
1812 acl: branch access granted: "4ea792ff6428" on branch "foobar"
1788 acl: branch access granted: "4ea792ff6428" on branch "foobar"
1813 acl: allowing changeset 4ea792ff6428
1789 acl: allowing changeset 4ea792ff6428
1814 updating the branch cache
1790 updating the branch cache
1815 checking for updated bookmarks
1791 checking for updated bookmarks
1816 repository tip rolled back to revision 2 (undo push)
1792 repository tip rolled back to revision 2 (undo push)
1817 working directory now based on revision 2
1793 working directory now based on revision 2
1818 2:07e028174695
1794 2:07e028174695
1819
1795
1820 Branch acl conflicting deny
1796 Branch acl conflicting deny
1821
1797
1822 $ init_config
1798 $ init_config
1823 $ echo "[acl.deny.branches]" >> $config
1799 $ echo "[acl.deny.branches]" >> $config
1824 $ echo "foobar = astro" >> $config
1800 $ echo "foobar = astro" >> $config
1825 $ echo "default = astro" >> $config
1801 $ echo "default = astro" >> $config
1826 $ echo "* = george" >> $config
1802 $ echo "* = george" >> $config
1827 $ do_push george
1803 $ do_push george
1828 Pushing as user george
1804 Pushing as user george
1829 hgrc = """
1805 hgrc = """
1830 [acl]
1806 [acl]
1831 sources = push
1807 sources = push
1832 [extensions]
1808 [extensions]
1833 [acl.deny.branches]
1809 [acl.deny.branches]
1834 foobar = astro
1810 foobar = astro
1835 default = astro
1811 default = astro
1836 * = george
1812 * = george
1837 """
1813 """
1838 pushing to ../b
1814 pushing to ../b
1839 searching for changes
1815 searching for changes
1840 common changesets up to 07e028174695
1841 invalidating branch cache (tip differs)
1816 invalidating branch cache (tip differs)
1842 4 changesets found
1817 4 changesets found
1843 list of changesets:
1818 list of changesets:
1844 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1819 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1845 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1820 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1846 911600dab2ae7a9baff75958b84fe606851ce955
1821 911600dab2ae7a9baff75958b84fe606851ce955
1847 4ea792ff64284af438188103a0ee8aca1724fb8c
1822 4ea792ff64284af438188103a0ee8aca1724fb8c
1848 adding changesets
1823 adding changesets
1849 bundling: 1 changesets
1824 bundling: 1 changesets
1850 bundling: 2 changesets
1825 bundling: 2 changesets
1851 bundling: 3 changesets
1826 bundling: 3 changesets
1852 bundling: 4 changesets
1827 bundling: 4 changesets
1853 bundling: 1/4 manifests (25.00%)
1828 bundling: 1/4 manifests (25.00%)
1854 bundling: 2/4 manifests (50.00%)
1829 bundling: 2/4 manifests (50.00%)
1855 bundling: 3/4 manifests (75.00%)
1830 bundling: 3/4 manifests (75.00%)
1856 bundling: 4/4 manifests (100.00%)
1831 bundling: 4/4 manifests (100.00%)
1857 bundling: abc.txt 0/4 files (0.00%)
1832 bundling: abc.txt 0/4 files (0.00%)
1858 bundling: foo/Bar/file.txt 1/4 files (25.00%)
1833 bundling: foo/Bar/file.txt 1/4 files (25.00%)
1859 bundling: foo/file.txt 2/4 files (50.00%)
1834 bundling: foo/file.txt 2/4 files (50.00%)
1860 bundling: quux/file.py 3/4 files (75.00%)
1835 bundling: quux/file.py 3/4 files (75.00%)
1861 changesets: 1 chunks
1836 changesets: 1 chunks
1862 add changeset ef1ea85a6374
1837 add changeset ef1ea85a6374
1863 changesets: 2 chunks
1838 changesets: 2 chunks
1864 add changeset f9cafe1212c8
1839 add changeset f9cafe1212c8
1865 changesets: 3 chunks
1840 changesets: 3 chunks
1866 add changeset 911600dab2ae
1841 add changeset 911600dab2ae
1867 changesets: 4 chunks
1842 changesets: 4 chunks
1868 add changeset 4ea792ff6428
1843 add changeset 4ea792ff6428
1869 adding manifests
1844 adding manifests
1870 manifests: 1/4 chunks (25.00%)
1845 manifests: 1/4 chunks (25.00%)
1871 manifests: 2/4 chunks (50.00%)
1846 manifests: 2/4 chunks (50.00%)
1872 manifests: 3/4 chunks (75.00%)
1847 manifests: 3/4 chunks (75.00%)
1873 manifests: 4/4 chunks (100.00%)
1848 manifests: 4/4 chunks (100.00%)
1874 adding file changes
1849 adding file changes
1875 adding abc.txt revisions
1850 adding abc.txt revisions
1876 files: 1/4 chunks (25.00%)
1851 files: 1/4 chunks (25.00%)
1877 adding foo/Bar/file.txt revisions
1852 adding foo/Bar/file.txt revisions
1878 files: 2/4 chunks (50.00%)
1853 files: 2/4 chunks (50.00%)
1879 adding foo/file.txt revisions
1854 adding foo/file.txt revisions
1880 files: 3/4 chunks (75.00%)
1855 files: 3/4 chunks (75.00%)
1881 adding quux/file.py revisions
1856 adding quux/file.py revisions
1882 files: 4/4 chunks (100.00%)
1857 files: 4/4 chunks (100.00%)
1883 added 4 changesets with 4 changes to 4 files (+1 heads)
1858 added 4 changesets with 4 changes to 4 files (+1 heads)
1884 calling hook pretxnchangegroup.acl: hgext.acl.hook
1859 calling hook pretxnchangegroup.acl: hgext.acl.hook
1885 acl: acl.allow.branches not enabled
1860 acl: acl.allow.branches not enabled
1886 acl: acl.deny.branches enabled, 1 entries for user george
1861 acl: acl.deny.branches enabled, 1 entries for user george
1887 acl: acl.allow not enabled
1862 acl: acl.allow not enabled
1888 acl: acl.deny not enabled
1863 acl: acl.deny not enabled
1889 error: pretxnchangegroup.acl hook failed: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
1864 error: pretxnchangegroup.acl hook failed: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
1890 transaction abort!
1865 transaction abort!
1891 rollback completed
1866 rollback completed
1892 abort: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
1867 abort: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
1893 no rollback information available
1868 no rollback information available
1894 2:07e028174695
1869 2:07e028174695
1895
1870
@@ -1,376 +1,377
1 $ hg init test
1 $ hg init test
2 $ cd test
2 $ cd test
3 $ echo "0" >> afile
3 $ echo "0" >> afile
4 $ hg add afile
4 $ hg add afile
5 $ hg commit -m "0.0"
5 $ hg commit -m "0.0"
6 $ echo "1" >> afile
6 $ echo "1" >> afile
7 $ hg commit -m "0.1"
7 $ hg commit -m "0.1"
8 $ echo "2" >> afile
8 $ echo "2" >> afile
9 $ hg commit -m "0.2"
9 $ hg commit -m "0.2"
10 $ echo "3" >> afile
10 $ echo "3" >> afile
11 $ hg commit -m "0.3"
11 $ hg commit -m "0.3"
12 $ hg update -C 0
12 $ hg update -C 0
13 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
13 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
14 $ echo "1" >> afile
14 $ echo "1" >> afile
15 $ hg commit -m "1.1"
15 $ hg commit -m "1.1"
16 created new head
16 created new head
17 $ echo "2" >> afile
17 $ echo "2" >> afile
18 $ hg commit -m "1.2"
18 $ hg commit -m "1.2"
19 $ echo "a line" > fred
19 $ echo "a line" > fred
20 $ echo "3" >> afile
20 $ echo "3" >> afile
21 $ hg add fred
21 $ hg add fred
22 $ hg commit -m "1.3"
22 $ hg commit -m "1.3"
23 $ hg mv afile adifferentfile
23 $ hg mv afile adifferentfile
24 $ hg commit -m "1.3m"
24 $ hg commit -m "1.3m"
25 $ hg update -C 3
25 $ hg update -C 3
26 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
26 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
27 $ hg mv afile anotherfile
27 $ hg mv afile anotherfile
28 $ hg commit -m "0.3m"
28 $ hg commit -m "0.3m"
29 $ hg debugindex .hg/store/data/afile.i
29 $ hg debugindex .hg/store/data/afile.i
30 rev offset length base linkrev nodeid p1 p2
30 rev offset length base linkrev nodeid p1 p2
31 0 0 3 0 0 362fef284ce2 000000000000 000000000000
31 0 0 3 0 0 362fef284ce2 000000000000 000000000000
32 1 3 5 1 1 125144f7e028 362fef284ce2 000000000000
32 1 3 5 1 1 125144f7e028 362fef284ce2 000000000000
33 2 8 7 2 2 4c982badb186 125144f7e028 000000000000
33 2 8 7 2 2 4c982badb186 125144f7e028 000000000000
34 3 15 9 3 3 19b1fc555737 4c982badb186 000000000000
34 3 15 9 3 3 19b1fc555737 4c982badb186 000000000000
35 $ hg debugindex .hg/store/data/adifferentfile.i
35 $ hg debugindex .hg/store/data/adifferentfile.i
36 rev offset length base linkrev nodeid p1 p2
36 rev offset length base linkrev nodeid p1 p2
37 0 0 75 0 7 2565f3199a74 000000000000 000000000000
37 0 0 75 0 7 2565f3199a74 000000000000 000000000000
38 $ hg debugindex .hg/store/data/anotherfile.i
38 $ hg debugindex .hg/store/data/anotherfile.i
39 rev offset length base linkrev nodeid p1 p2
39 rev offset length base linkrev nodeid p1 p2
40 0 0 75 0 8 2565f3199a74 000000000000 000000000000
40 0 0 75 0 8 2565f3199a74 000000000000 000000000000
41 $ hg debugindex .hg/store/data/fred.i
41 $ hg debugindex .hg/store/data/fred.i
42 rev offset length base linkrev nodeid p1 p2
42 rev offset length base linkrev nodeid p1 p2
43 0 0 8 0 6 12ab3bcc5ea4 000000000000 000000000000
43 0 0 8 0 6 12ab3bcc5ea4 000000000000 000000000000
44 $ hg debugindex .hg/store/00manifest.i
44 $ hg debugindex .hg/store/00manifest.i
45 rev offset length base linkrev nodeid p1 p2
45 rev offset length base linkrev nodeid p1 p2
46 0 0 48 0 0 43eadb1d2d06 000000000000 000000000000
46 0 0 48 0 0 43eadb1d2d06 000000000000 000000000000
47 1 48 48 1 1 8b89697eba2c 43eadb1d2d06 000000000000
47 1 48 48 1 1 8b89697eba2c 43eadb1d2d06 000000000000
48 2 96 48 2 2 626a32663c2f 8b89697eba2c 000000000000
48 2 96 48 2 2 626a32663c2f 8b89697eba2c 000000000000
49 3 144 48 3 3 f54c32f13478 626a32663c2f 000000000000
49 3 144 48 3 3 f54c32f13478 626a32663c2f 000000000000
50 4 192 58 3 6 de68e904d169 626a32663c2f 000000000000
50 4 192 58 3 6 de68e904d169 626a32663c2f 000000000000
51 5 250 68 3 7 09bb521d218d de68e904d169 000000000000
51 5 250 68 3 7 09bb521d218d de68e904d169 000000000000
52 6 318 54 6 8 1fde233dfb0f f54c32f13478 000000000000
52 6 318 54 6 8 1fde233dfb0f f54c32f13478 000000000000
53 $ hg verify
53 $ hg verify
54 checking changesets
54 checking changesets
55 checking manifests
55 checking manifests
56 crosschecking files in changesets and manifests
56 crosschecking files in changesets and manifests
57 checking files
57 checking files
58 4 files, 9 changesets, 7 total revisions
58 4 files, 9 changesets, 7 total revisions
59 $ cd ..
59 $ cd ..
60 $ for i in 0 1 2 3 4 5 6 7 8; do
60 $ for i in 0 1 2 3 4 5 6 7 8; do
61 > mkdir test-"$i"
61 > mkdir test-"$i"
62 > hg --cwd test-"$i" init
62 > hg --cwd test-"$i" init
63 > hg -R test bundle -r "$i" test-"$i".hg test-"$i"
63 > hg -R test bundle -r "$i" test-"$i".hg test-"$i"
64 > cd test-"$i"
64 > cd test-"$i"
65 > hg unbundle ../test-"$i".hg
65 > hg unbundle ../test-"$i".hg
66 > hg verify
66 > hg verify
67 > hg tip -q
67 > hg tip -q
68 > cd ..
68 > cd ..
69 > done
69 > done
70 searching for changes
70 searching for changes
71 1 changesets found
71 1 changesets found
72 adding changesets
72 adding changesets
73 adding manifests
73 adding manifests
74 adding file changes
74 adding file changes
75 added 1 changesets with 1 changes to 1 files
75 added 1 changesets with 1 changes to 1 files
76 (run 'hg update' to get a working copy)
76 (run 'hg update' to get a working copy)
77 checking changesets
77 checking changesets
78 checking manifests
78 checking manifests
79 crosschecking files in changesets and manifests
79 crosschecking files in changesets and manifests
80 checking files
80 checking files
81 1 files, 1 changesets, 1 total revisions
81 1 files, 1 changesets, 1 total revisions
82 0:f9ee2f85a263
82 0:f9ee2f85a263
83 searching for changes
83 searching for changes
84 2 changesets found
84 2 changesets found
85 adding changesets
85 adding changesets
86 adding manifests
86 adding manifests
87 adding file changes
87 adding file changes
88 added 2 changesets with 2 changes to 1 files
88 added 2 changesets with 2 changes to 1 files
89 (run 'hg update' to get a working copy)
89 (run 'hg update' to get a working copy)
90 checking changesets
90 checking changesets
91 checking manifests
91 checking manifests
92 crosschecking files in changesets and manifests
92 crosschecking files in changesets and manifests
93 checking files
93 checking files
94 1 files, 2 changesets, 2 total revisions
94 1 files, 2 changesets, 2 total revisions
95 1:34c2bf6b0626
95 1:34c2bf6b0626
96 searching for changes
96 searching for changes
97 3 changesets found
97 3 changesets found
98 adding changesets
98 adding changesets
99 adding manifests
99 adding manifests
100 adding file changes
100 adding file changes
101 added 3 changesets with 3 changes to 1 files
101 added 3 changesets with 3 changes to 1 files
102 (run 'hg update' to get a working copy)
102 (run 'hg update' to get a working copy)
103 checking changesets
103 checking changesets
104 checking manifests
104 checking manifests
105 crosschecking files in changesets and manifests
105 crosschecking files in changesets and manifests
106 checking files
106 checking files
107 1 files, 3 changesets, 3 total revisions
107 1 files, 3 changesets, 3 total revisions
108 2:e38ba6f5b7e0
108 2:e38ba6f5b7e0
109 searching for changes
109 searching for changes
110 4 changesets found
110 4 changesets found
111 adding changesets
111 adding changesets
112 adding manifests
112 adding manifests
113 adding file changes
113 adding file changes
114 added 4 changesets with 4 changes to 1 files
114 added 4 changesets with 4 changes to 1 files
115 (run 'hg update' to get a working copy)
115 (run 'hg update' to get a working copy)
116 checking changesets
116 checking changesets
117 checking manifests
117 checking manifests
118 crosschecking files in changesets and manifests
118 crosschecking files in changesets and manifests
119 checking files
119 checking files
120 1 files, 4 changesets, 4 total revisions
120 1 files, 4 changesets, 4 total revisions
121 3:eebf5a27f8ca
121 3:eebf5a27f8ca
122 searching for changes
122 searching for changes
123 2 changesets found
123 2 changesets found
124 adding changesets
124 adding changesets
125 adding manifests
125 adding manifests
126 adding file changes
126 adding file changes
127 added 2 changesets with 2 changes to 1 files
127 added 2 changesets with 2 changes to 1 files
128 (run 'hg update' to get a working copy)
128 (run 'hg update' to get a working copy)
129 checking changesets
129 checking changesets
130 checking manifests
130 checking manifests
131 crosschecking files in changesets and manifests
131 crosschecking files in changesets and manifests
132 checking files
132 checking files
133 1 files, 2 changesets, 2 total revisions
133 1 files, 2 changesets, 2 total revisions
134 1:095197eb4973
134 1:095197eb4973
135 searching for changes
135 searching for changes
136 3 changesets found
136 3 changesets found
137 adding changesets
137 adding changesets
138 adding manifests
138 adding manifests
139 adding file changes
139 adding file changes
140 added 3 changesets with 3 changes to 1 files
140 added 3 changesets with 3 changes to 1 files
141 (run 'hg update' to get a working copy)
141 (run 'hg update' to get a working copy)
142 checking changesets
142 checking changesets
143 checking manifests
143 checking manifests
144 crosschecking files in changesets and manifests
144 crosschecking files in changesets and manifests
145 checking files
145 checking files
146 1 files, 3 changesets, 3 total revisions
146 1 files, 3 changesets, 3 total revisions
147 2:1bb50a9436a7
147 2:1bb50a9436a7
148 searching for changes
148 searching for changes
149 4 changesets found
149 4 changesets found
150 adding changesets
150 adding changesets
151 adding manifests
151 adding manifests
152 adding file changes
152 adding file changes
153 added 4 changesets with 5 changes to 2 files
153 added 4 changesets with 5 changes to 2 files
154 (run 'hg update' to get a working copy)
154 (run 'hg update' to get a working copy)
155 checking changesets
155 checking changesets
156 checking manifests
156 checking manifests
157 crosschecking files in changesets and manifests
157 crosschecking files in changesets and manifests
158 checking files
158 checking files
159 2 files, 4 changesets, 5 total revisions
159 2 files, 4 changesets, 5 total revisions
160 3:7373c1169842
160 3:7373c1169842
161 searching for changes
161 searching for changes
162 5 changesets found
162 5 changesets found
163 adding changesets
163 adding changesets
164 adding manifests
164 adding manifests
165 adding file changes
165 adding file changes
166 added 5 changesets with 6 changes to 3 files
166 added 5 changesets with 6 changes to 3 files
167 (run 'hg update' to get a working copy)
167 (run 'hg update' to get a working copy)
168 checking changesets
168 checking changesets
169 checking manifests
169 checking manifests
170 crosschecking files in changesets and manifests
170 crosschecking files in changesets and manifests
171 checking files
171 checking files
172 3 files, 5 changesets, 6 total revisions
172 3 files, 5 changesets, 6 total revisions
173 4:a6a34bfa0076
173 4:a6a34bfa0076
174 searching for changes
174 searching for changes
175 5 changesets found
175 5 changesets found
176 adding changesets
176 adding changesets
177 adding manifests
177 adding manifests
178 adding file changes
178 adding file changes
179 added 5 changesets with 5 changes to 2 files
179 added 5 changesets with 5 changes to 2 files
180 (run 'hg update' to get a working copy)
180 (run 'hg update' to get a working copy)
181 checking changesets
181 checking changesets
182 checking manifests
182 checking manifests
183 crosschecking files in changesets and manifests
183 crosschecking files in changesets and manifests
184 checking files
184 checking files
185 2 files, 5 changesets, 5 total revisions
185 2 files, 5 changesets, 5 total revisions
186 4:aa35859c02ea
186 4:aa35859c02ea
187 $ cd test-8
187 $ cd test-8
188 $ hg pull ../test-7
188 $ hg pull ../test-7
189 pulling from ../test-7
189 pulling from ../test-7
190 searching for changes
190 searching for changes
191 adding changesets
191 adding changesets
192 adding manifests
192 adding manifests
193 adding file changes
193 adding file changes
194 added 4 changesets with 2 changes to 3 files (+1 heads)
194 added 4 changesets with 2 changes to 3 files (+1 heads)
195 (run 'hg heads' to see heads, 'hg merge' to merge)
195 (run 'hg heads' to see heads, 'hg merge' to merge)
196 $ hg verify
196 $ hg verify
197 checking changesets
197 checking changesets
198 checking manifests
198 checking manifests
199 crosschecking files in changesets and manifests
199 crosschecking files in changesets and manifests
200 checking files
200 checking files
201 4 files, 9 changesets, 7 total revisions
201 4 files, 9 changesets, 7 total revisions
202 $ hg rollback
202 $ hg rollback
203 repository tip rolled back to revision 4 (undo pull)
203 repository tip rolled back to revision 4 (undo pull)
204 working directory now based on revision -1
204 working directory now based on revision -1
205 $ cd ..
205 $ cd ..
206
206
207 should fail
207 should fail
208
208
209 $ hg -R test bundle --base 2 -r tip test-bundle-branch1.hg test-3
209 $ hg -R test bundle --base 2 -r tip test-bundle-branch1.hg test-3
210 abort: --base is incompatible with specifying a destination
210 abort: --base is incompatible with specifying a destination
211 [255]
211 [255]
212 $ hg -R test bundle -r tip test-bundle-branch1.hg
212 $ hg -R test bundle -r tip test-bundle-branch1.hg
213 abort: repository default-push not found!
213 abort: repository default-push not found!
214 [255]
214 [255]
215
215
216 $ hg -R test bundle --base 2 -r tip test-bundle-branch1.hg
216 $ hg -R test bundle --base 2 -r tip test-bundle-branch1.hg
217 2 changesets found
217 2 changesets found
218 $ hg -R test bundle --base 2 -r 7 test-bundle-branch2.hg
218 $ hg -R test bundle --base 2 -r 7 test-bundle-branch2.hg
219 4 changesets found
219 4 changesets found
220 $ hg -R test bundle --base 2 test-bundle-all.hg
220 $ hg -R test bundle --base 2 test-bundle-all.hg
221 6 changesets found
221 6 changesets found
222 $ hg -R test bundle --base 3 -r tip test-bundle-should-fail.hg
222 $ hg -R test bundle --base 3 -r tip test-bundle-should-fail.hg
223 1 changesets found
223 1 changesets found
224
224
225 empty bundle
225 empty bundle
226
226
227 $ hg -R test bundle --base 7 --base 8 test-bundle-empty.hg
227 $ hg -R test bundle --base 7 --base 8 test-bundle-empty.hg
228 no changes found
228 no changes found
229 [1]
229 [1]
230
230
231 issue76 msg2163
231 issue76 msg2163
232
232
233 $ hg -R test bundle --base 3 -r 3 -r 3 test-bundle-cset-3.hg
233 $ hg -R test bundle --base 3 -r 3 -r 3 test-bundle-cset-3.hg
234 1 changesets found
234 no changes found
235 [1]
235
236
236 Issue1910: 'hg bundle --base $head' does not exclude $head from
237 Issue1910: 'hg bundle --base $head' does not exclude $head from
237 result
238 result
238
239
239 $ hg -R test bundle --base 7 test-bundle-cset-7.hg
240 $ hg -R test bundle --base 7 test-bundle-cset-7.hg
240 4 changesets found
241 4 changesets found
241
242
242 $ hg clone test-2 test-9
243 $ hg clone test-2 test-9
243 updating to branch default
244 updating to branch default
244 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
245 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
245 $ cd test-9
246 $ cd test-9
246
247
247 revision 2
248 revision 2
248
249
249 $ hg tip -q
250 $ hg tip -q
250 2:e38ba6f5b7e0
251 2:e38ba6f5b7e0
251 $ hg unbundle ../test-bundle-should-fail.hg
252 $ hg unbundle ../test-bundle-should-fail.hg
252 adding changesets
253 adding changesets
253 transaction abort!
254 transaction abort!
254 rollback completed
255 rollback completed
255 abort: 00changelog.i@eebf5a27f8ca: unknown parent!
256 abort: 00changelog.i@eebf5a27f8ca: unknown parent!
256 [255]
257 [255]
257
258
258 revision 2
259 revision 2
259
260
260 $ hg tip -q
261 $ hg tip -q
261 2:e38ba6f5b7e0
262 2:e38ba6f5b7e0
262 $ hg unbundle ../test-bundle-all.hg
263 $ hg unbundle ../test-bundle-all.hg
263 adding changesets
264 adding changesets
264 adding manifests
265 adding manifests
265 adding file changes
266 adding file changes
266 added 6 changesets with 4 changes to 4 files (+1 heads)
267 added 6 changesets with 4 changes to 4 files (+1 heads)
267 (run 'hg heads' to see heads, 'hg merge' to merge)
268 (run 'hg heads' to see heads, 'hg merge' to merge)
268
269
269 revision 8
270 revision 8
270
271
271 $ hg tip -q
272 $ hg tip -q
272 8:aa35859c02ea
273 8:aa35859c02ea
273 $ hg verify
274 $ hg verify
274 checking changesets
275 checking changesets
275 checking manifests
276 checking manifests
276 crosschecking files in changesets and manifests
277 crosschecking files in changesets and manifests
277 checking files
278 checking files
278 4 files, 9 changesets, 7 total revisions
279 4 files, 9 changesets, 7 total revisions
279 $ hg rollback
280 $ hg rollback
280 repository tip rolled back to revision 2 (undo unbundle)
281 repository tip rolled back to revision 2 (undo unbundle)
281 working directory now based on revision 2
282 working directory now based on revision 2
282
283
283 revision 2
284 revision 2
284
285
285 $ hg tip -q
286 $ hg tip -q
286 2:e38ba6f5b7e0
287 2:e38ba6f5b7e0
287 $ hg unbundle ../test-bundle-branch1.hg
288 $ hg unbundle ../test-bundle-branch1.hg
288 adding changesets
289 adding changesets
289 adding manifests
290 adding manifests
290 adding file changes
291 adding file changes
291 added 2 changesets with 2 changes to 2 files
292 added 2 changesets with 2 changes to 2 files
292 (run 'hg update' to get a working copy)
293 (run 'hg update' to get a working copy)
293
294
294 revision 4
295 revision 4
295
296
296 $ hg tip -q
297 $ hg tip -q
297 4:aa35859c02ea
298 4:aa35859c02ea
298 $ hg verify
299 $ hg verify
299 checking changesets
300 checking changesets
300 checking manifests
301 checking manifests
301 crosschecking files in changesets and manifests
302 crosschecking files in changesets and manifests
302 checking files
303 checking files
303 2 files, 5 changesets, 5 total revisions
304 2 files, 5 changesets, 5 total revisions
304 $ hg rollback
305 $ hg rollback
305 repository tip rolled back to revision 2 (undo unbundle)
306 repository tip rolled back to revision 2 (undo unbundle)
306 working directory now based on revision 2
307 working directory now based on revision 2
307 $ hg unbundle ../test-bundle-branch2.hg
308 $ hg unbundle ../test-bundle-branch2.hg
308 adding changesets
309 adding changesets
309 adding manifests
310 adding manifests
310 adding file changes
311 adding file changes
311 added 4 changesets with 3 changes to 3 files (+1 heads)
312 added 4 changesets with 3 changes to 3 files (+1 heads)
312 (run 'hg heads' to see heads, 'hg merge' to merge)
313 (run 'hg heads' to see heads, 'hg merge' to merge)
313
314
314 revision 6
315 revision 6
315
316
316 $ hg tip -q
317 $ hg tip -q
317 6:a6a34bfa0076
318 6:a6a34bfa0076
318 $ hg verify
319 $ hg verify
319 checking changesets
320 checking changesets
320 checking manifests
321 checking manifests
321 crosschecking files in changesets and manifests
322 crosschecking files in changesets and manifests
322 checking files
323 checking files
323 3 files, 7 changesets, 6 total revisions
324 3 files, 7 changesets, 6 total revisions
324 $ hg rollback
325 $ hg rollback
325 repository tip rolled back to revision 2 (undo unbundle)
326 repository tip rolled back to revision 2 (undo unbundle)
326 working directory now based on revision 2
327 working directory now based on revision 2
327 $ hg unbundle ../test-bundle-cset-7.hg
328 $ hg unbundle ../test-bundle-cset-7.hg
328 adding changesets
329 adding changesets
329 adding manifests
330 adding manifests
330 adding file changes
331 adding file changes
331 added 2 changesets with 2 changes to 2 files
332 added 2 changesets with 2 changes to 2 files
332 (run 'hg update' to get a working copy)
333 (run 'hg update' to get a working copy)
333
334
334 revision 4
335 revision 4
335
336
336 $ hg tip -q
337 $ hg tip -q
337 4:aa35859c02ea
338 4:aa35859c02ea
338 $ hg verify
339 $ hg verify
339 checking changesets
340 checking changesets
340 checking manifests
341 checking manifests
341 crosschecking files in changesets and manifests
342 crosschecking files in changesets and manifests
342 checking files
343 checking files
343 2 files, 5 changesets, 5 total revisions
344 2 files, 5 changesets, 5 total revisions
344
345
345 $ cd ../test
346 $ cd ../test
346 $ hg merge 7
347 $ hg merge 7
347 note: possible conflict - afile was renamed multiple times to:
348 note: possible conflict - afile was renamed multiple times to:
348 anotherfile
349 anotherfile
349 adifferentfile
350 adifferentfile
350 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
351 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
351 (branch merge, don't forget to commit)
352 (branch merge, don't forget to commit)
352 $ hg ci -m merge
353 $ hg ci -m merge
353 $ cd ..
354 $ cd ..
354 $ hg -R test bundle --base 2 test-bundle-head.hg
355 $ hg -R test bundle --base 2 test-bundle-head.hg
355 7 changesets found
356 7 changesets found
356 $ hg clone test-2 test-10
357 $ hg clone test-2 test-10
357 updating to branch default
358 updating to branch default
358 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
359 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
359 $ cd test-10
360 $ cd test-10
360 $ hg unbundle ../test-bundle-head.hg
361 $ hg unbundle ../test-bundle-head.hg
361 adding changesets
362 adding changesets
362 adding manifests
363 adding manifests
363 adding file changes
364 adding file changes
364 added 7 changesets with 4 changes to 4 files
365 added 7 changesets with 4 changes to 4 files
365 (run 'hg update' to get a working copy)
366 (run 'hg update' to get a working copy)
366
367
367 revision 9
368 revision 9
368
369
369 $ hg tip -q
370 $ hg tip -q
370 9:905597b0d5d4
371 9:905597b0d5d4
371 $ hg verify
372 $ hg verify
372 checking changesets
373 checking changesets
373 checking manifests
374 checking manifests
374 crosschecking files in changesets and manifests
375 crosschecking files in changesets and manifests
375 checking files
376 checking files
376 4 files, 10 changesets, 7 total revisions
377 4 files, 10 changesets, 7 total revisions
@@ -1,576 +1,575
1 Setting up test
1 Setting up test
2
2
3 $ hg init test
3 $ hg init test
4 $ cd test
4 $ cd test
5 $ echo 0 > afile
5 $ echo 0 > afile
6 $ hg add afile
6 $ hg add afile
7 $ hg commit -m "0.0"
7 $ hg commit -m "0.0"
8 $ echo 1 >> afile
8 $ echo 1 >> afile
9 $ hg commit -m "0.1"
9 $ hg commit -m "0.1"
10 $ echo 2 >> afile
10 $ echo 2 >> afile
11 $ hg commit -m "0.2"
11 $ hg commit -m "0.2"
12 $ echo 3 >> afile
12 $ echo 3 >> afile
13 $ hg commit -m "0.3"
13 $ hg commit -m "0.3"
14 $ hg update -C 0
14 $ hg update -C 0
15 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
15 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
16 $ echo 1 >> afile
16 $ echo 1 >> afile
17 $ hg commit -m "1.1"
17 $ hg commit -m "1.1"
18 created new head
18 created new head
19 $ echo 2 >> afile
19 $ echo 2 >> afile
20 $ hg commit -m "1.2"
20 $ hg commit -m "1.2"
21 $ echo "a line" > fred
21 $ echo "a line" > fred
22 $ echo 3 >> afile
22 $ echo 3 >> afile
23 $ hg add fred
23 $ hg add fred
24 $ hg commit -m "1.3"
24 $ hg commit -m "1.3"
25 $ hg mv afile adifferentfile
25 $ hg mv afile adifferentfile
26 $ hg commit -m "1.3m"
26 $ hg commit -m "1.3m"
27 $ hg update -C 3
27 $ hg update -C 3
28 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
28 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
29 $ hg mv afile anotherfile
29 $ hg mv afile anotherfile
30 $ hg commit -m "0.3m"
30 $ hg commit -m "0.3m"
31 $ hg verify
31 $ hg verify
32 checking changesets
32 checking changesets
33 checking manifests
33 checking manifests
34 crosschecking files in changesets and manifests
34 crosschecking files in changesets and manifests
35 checking files
35 checking files
36 4 files, 9 changesets, 7 total revisions
36 4 files, 9 changesets, 7 total revisions
37 $ cd ..
37 $ cd ..
38 $ hg init empty
38 $ hg init empty
39
39
40 Bundle --all
40 Bundle --all
41
41
42 $ hg -R test bundle --all all.hg
42 $ hg -R test bundle --all all.hg
43 9 changesets found
43 9 changesets found
44
44
45 Bundle test to full.hg
45 Bundle test to full.hg
46
46
47 $ hg -R test bundle full.hg empty
47 $ hg -R test bundle full.hg empty
48 searching for changes
48 searching for changes
49 9 changesets found
49 9 changesets found
50
50
51 Unbundle full.hg in test
51 Unbundle full.hg in test
52
52
53 $ hg -R test unbundle full.hg
53 $ hg -R test unbundle full.hg
54 adding changesets
54 adding changesets
55 adding manifests
55 adding manifests
56 adding file changes
56 adding file changes
57 added 0 changesets with 0 changes to 4 files
57 added 0 changesets with 0 changes to 4 files
58 (run 'hg update' to get a working copy)
58 (run 'hg update' to get a working copy)
59
59
60 Verify empty
60 Verify empty
61
61
62 $ hg -R empty heads
62 $ hg -R empty heads
63 [1]
63 [1]
64 $ hg -R empty verify
64 $ hg -R empty verify
65 checking changesets
65 checking changesets
66 checking manifests
66 checking manifests
67 crosschecking files in changesets and manifests
67 crosschecking files in changesets and manifests
68 checking files
68 checking files
69 0 files, 0 changesets, 0 total revisions
69 0 files, 0 changesets, 0 total revisions
70
70
71 Pull full.hg into test (using --cwd)
71 Pull full.hg into test (using --cwd)
72
72
73 $ hg --cwd test pull ../full.hg
73 $ hg --cwd test pull ../full.hg
74 pulling from ../full.hg
74 pulling from ../full.hg
75 searching for changes
75 searching for changes
76 no changes found
76 no changes found
77
77
78 Pull full.hg into empty (using --cwd)
78 Pull full.hg into empty (using --cwd)
79
79
80 $ hg --cwd empty pull ../full.hg
80 $ hg --cwd empty pull ../full.hg
81 pulling from ../full.hg
81 pulling from ../full.hg
82 requesting all changes
82 requesting all changes
83 adding changesets
83 adding changesets
84 adding manifests
84 adding manifests
85 adding file changes
85 adding file changes
86 added 9 changesets with 7 changes to 4 files (+1 heads)
86 added 9 changesets with 7 changes to 4 files (+1 heads)
87 (run 'hg heads' to see heads, 'hg merge' to merge)
87 (run 'hg heads' to see heads, 'hg merge' to merge)
88
88
89 Rollback empty
89 Rollback empty
90
90
91 $ hg -R empty rollback
91 $ hg -R empty rollback
92 repository tip rolled back to revision -1 (undo pull)
92 repository tip rolled back to revision -1 (undo pull)
93 working directory now based on revision -1
93 working directory now based on revision -1
94
94
95 Pull full.hg into empty again (using --cwd)
95 Pull full.hg into empty again (using --cwd)
96
96
97 $ hg --cwd empty pull ../full.hg
97 $ hg --cwd empty pull ../full.hg
98 pulling from ../full.hg
98 pulling from ../full.hg
99 requesting all changes
99 requesting all changes
100 adding changesets
100 adding changesets
101 adding manifests
101 adding manifests
102 adding file changes
102 adding file changes
103 added 9 changesets with 7 changes to 4 files (+1 heads)
103 added 9 changesets with 7 changes to 4 files (+1 heads)
104 (run 'hg heads' to see heads, 'hg merge' to merge)
104 (run 'hg heads' to see heads, 'hg merge' to merge)
105
105
106 Pull full.hg into test (using -R)
106 Pull full.hg into test (using -R)
107
107
108 $ hg -R test pull full.hg
108 $ hg -R test pull full.hg
109 pulling from full.hg
109 pulling from full.hg
110 searching for changes
110 searching for changes
111 no changes found
111 no changes found
112
112
113 Pull full.hg into empty (using -R)
113 Pull full.hg into empty (using -R)
114
114
115 $ hg -R empty pull full.hg
115 $ hg -R empty pull full.hg
116 pulling from full.hg
116 pulling from full.hg
117 searching for changes
117 searching for changes
118 no changes found
118 no changes found
119
119
120 Rollback empty
120 Rollback empty
121
121
122 $ hg -R empty rollback
122 $ hg -R empty rollback
123 repository tip rolled back to revision -1 (undo pull)
123 repository tip rolled back to revision -1 (undo pull)
124 working directory now based on revision -1
124 working directory now based on revision -1
125
125
126 Pull full.hg into empty again (using -R)
126 Pull full.hg into empty again (using -R)
127
127
128 $ hg -R empty pull full.hg
128 $ hg -R empty pull full.hg
129 pulling from full.hg
129 pulling from full.hg
130 requesting all changes
130 requesting all changes
131 adding changesets
131 adding changesets
132 adding manifests
132 adding manifests
133 adding file changes
133 adding file changes
134 added 9 changesets with 7 changes to 4 files (+1 heads)
134 added 9 changesets with 7 changes to 4 files (+1 heads)
135 (run 'hg heads' to see heads, 'hg merge' to merge)
135 (run 'hg heads' to see heads, 'hg merge' to merge)
136
136
137 Log -R full.hg in fresh empty
137 Log -R full.hg in fresh empty
138
138
139 $ rm -r empty
139 $ rm -r empty
140 $ hg init empty
140 $ hg init empty
141 $ cd empty
141 $ cd empty
142 $ hg -R bundle://../full.hg log
142 $ hg -R bundle://../full.hg log
143 changeset: 8:aa35859c02ea
143 changeset: 8:aa35859c02ea
144 tag: tip
144 tag: tip
145 parent: 3:eebf5a27f8ca
145 parent: 3:eebf5a27f8ca
146 user: test
146 user: test
147 date: Thu Jan 01 00:00:00 1970 +0000
147 date: Thu Jan 01 00:00:00 1970 +0000
148 summary: 0.3m
148 summary: 0.3m
149
149
150 changeset: 7:a6a34bfa0076
150 changeset: 7:a6a34bfa0076
151 user: test
151 user: test
152 date: Thu Jan 01 00:00:00 1970 +0000
152 date: Thu Jan 01 00:00:00 1970 +0000
153 summary: 1.3m
153 summary: 1.3m
154
154
155 changeset: 6:7373c1169842
155 changeset: 6:7373c1169842
156 user: test
156 user: test
157 date: Thu Jan 01 00:00:00 1970 +0000
157 date: Thu Jan 01 00:00:00 1970 +0000
158 summary: 1.3
158 summary: 1.3
159
159
160 changeset: 5:1bb50a9436a7
160 changeset: 5:1bb50a9436a7
161 user: test
161 user: test
162 date: Thu Jan 01 00:00:00 1970 +0000
162 date: Thu Jan 01 00:00:00 1970 +0000
163 summary: 1.2
163 summary: 1.2
164
164
165 changeset: 4:095197eb4973
165 changeset: 4:095197eb4973
166 parent: 0:f9ee2f85a263
166 parent: 0:f9ee2f85a263
167 user: test
167 user: test
168 date: Thu Jan 01 00:00:00 1970 +0000
168 date: Thu Jan 01 00:00:00 1970 +0000
169 summary: 1.1
169 summary: 1.1
170
170
171 changeset: 3:eebf5a27f8ca
171 changeset: 3:eebf5a27f8ca
172 user: test
172 user: test
173 date: Thu Jan 01 00:00:00 1970 +0000
173 date: Thu Jan 01 00:00:00 1970 +0000
174 summary: 0.3
174 summary: 0.3
175
175
176 changeset: 2:e38ba6f5b7e0
176 changeset: 2:e38ba6f5b7e0
177 user: test
177 user: test
178 date: Thu Jan 01 00:00:00 1970 +0000
178 date: Thu Jan 01 00:00:00 1970 +0000
179 summary: 0.2
179 summary: 0.2
180
180
181 changeset: 1:34c2bf6b0626
181 changeset: 1:34c2bf6b0626
182 user: test
182 user: test
183 date: Thu Jan 01 00:00:00 1970 +0000
183 date: Thu Jan 01 00:00:00 1970 +0000
184 summary: 0.1
184 summary: 0.1
185
185
186 changeset: 0:f9ee2f85a263
186 changeset: 0:f9ee2f85a263
187 user: test
187 user: test
188 date: Thu Jan 01 00:00:00 1970 +0000
188 date: Thu Jan 01 00:00:00 1970 +0000
189 summary: 0.0
189 summary: 0.0
190
190
191 Make sure bundlerepo doesn't leak tempfiles (issue2491)
191 Make sure bundlerepo doesn't leak tempfiles (issue2491)
192
192
193 $ ls .hg
193 $ ls .hg
194 00changelog.i
194 00changelog.i
195 cache
195 cache
196 requires
196 requires
197 store
197 store
198
198
199 Pull ../full.hg into empty (with hook)
199 Pull ../full.hg into empty (with hook)
200
200
201 $ echo '[hooks]' >> .hg/hgrc
201 $ echo '[hooks]' >> .hg/hgrc
202 $ echo 'changegroup = python "$TESTDIR"/printenv.py changegroup' >> .hg/hgrc
202 $ echo 'changegroup = python "$TESTDIR"/printenv.py changegroup' >> .hg/hgrc
203
203
204 doesn't work (yet ?)
204 doesn't work (yet ?)
205
205
206 hg -R bundle://../full.hg verify
206 hg -R bundle://../full.hg verify
207
207
208 $ hg pull bundle://../full.hg
208 $ hg pull bundle://../full.hg
209 pulling from bundle:../full.hg
209 pulling from bundle:../full.hg
210 requesting all changes
210 requesting all changes
211 adding changesets
211 adding changesets
212 adding manifests
212 adding manifests
213 adding file changes
213 adding file changes
214 added 9 changesets with 7 changes to 4 files (+1 heads)
214 added 9 changesets with 7 changes to 4 files (+1 heads)
215 changegroup hook: HG_NODE=f9ee2f85a263049e9ae6d37a0e67e96194ffb735 HG_SOURCE=pull HG_URL=bundle:../full.hg
215 changegroup hook: HG_NODE=f9ee2f85a263049e9ae6d37a0e67e96194ffb735 HG_SOURCE=pull HG_URL=bundle:../full.hg
216 (run 'hg heads' to see heads, 'hg merge' to merge)
216 (run 'hg heads' to see heads, 'hg merge' to merge)
217
217
218 Rollback empty
218 Rollback empty
219
219
220 $ hg rollback
220 $ hg rollback
221 repository tip rolled back to revision -1 (undo pull)
221 repository tip rolled back to revision -1 (undo pull)
222 working directory now based on revision -1
222 working directory now based on revision -1
223 $ cd ..
223 $ cd ..
224
224
225 Log -R bundle:empty+full.hg
225 Log -R bundle:empty+full.hg
226
226
227 $ hg -R bundle:empty+full.hg log --template="{rev} "; echo ""
227 $ hg -R bundle:empty+full.hg log --template="{rev} "; echo ""
228 8 7 6 5 4 3 2 1 0
228 8 7 6 5 4 3 2 1 0
229
229
230 Pull full.hg into empty again (using -R; with hook)
230 Pull full.hg into empty again (using -R; with hook)
231
231
232 $ hg -R empty pull full.hg
232 $ hg -R empty pull full.hg
233 pulling from full.hg
233 pulling from full.hg
234 requesting all changes
234 requesting all changes
235 adding changesets
235 adding changesets
236 adding manifests
236 adding manifests
237 adding file changes
237 adding file changes
238 added 9 changesets with 7 changes to 4 files (+1 heads)
238 added 9 changesets with 7 changes to 4 files (+1 heads)
239 changegroup hook: HG_NODE=f9ee2f85a263049e9ae6d37a0e67e96194ffb735 HG_SOURCE=pull HG_URL=bundle:empty+full.hg
239 changegroup hook: HG_NODE=f9ee2f85a263049e9ae6d37a0e67e96194ffb735 HG_SOURCE=pull HG_URL=bundle:empty+full.hg
240 (run 'hg heads' to see heads, 'hg merge' to merge)
240 (run 'hg heads' to see heads, 'hg merge' to merge)
241
241
242 Create partial clones
242 Create partial clones
243
243
244 $ rm -r empty
244 $ rm -r empty
245 $ hg init empty
245 $ hg init empty
246 $ hg clone -r 3 test partial
246 $ hg clone -r 3 test partial
247 adding changesets
247 adding changesets
248 adding manifests
248 adding manifests
249 adding file changes
249 adding file changes
250 added 4 changesets with 4 changes to 1 files
250 added 4 changesets with 4 changes to 1 files
251 updating to branch default
251 updating to branch default
252 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
252 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
253 $ hg clone partial partial2
253 $ hg clone partial partial2
254 updating to branch default
254 updating to branch default
255 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
255 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
256 $ cd partial
256 $ cd partial
257
257
258 Log -R full.hg in partial
258 Log -R full.hg in partial
259
259
260 $ hg -R bundle://../full.hg log
260 $ hg -R bundle://../full.hg log
261 changeset: 8:aa35859c02ea
261 changeset: 8:aa35859c02ea
262 tag: tip
262 tag: tip
263 parent: 3:eebf5a27f8ca
263 parent: 3:eebf5a27f8ca
264 user: test
264 user: test
265 date: Thu Jan 01 00:00:00 1970 +0000
265 date: Thu Jan 01 00:00:00 1970 +0000
266 summary: 0.3m
266 summary: 0.3m
267
267
268 changeset: 7:a6a34bfa0076
268 changeset: 7:a6a34bfa0076
269 user: test
269 user: test
270 date: Thu Jan 01 00:00:00 1970 +0000
270 date: Thu Jan 01 00:00:00 1970 +0000
271 summary: 1.3m
271 summary: 1.3m
272
272
273 changeset: 6:7373c1169842
273 changeset: 6:7373c1169842
274 user: test
274 user: test
275 date: Thu Jan 01 00:00:00 1970 +0000
275 date: Thu Jan 01 00:00:00 1970 +0000
276 summary: 1.3
276 summary: 1.3
277
277
278 changeset: 5:1bb50a9436a7
278 changeset: 5:1bb50a9436a7
279 user: test
279 user: test
280 date: Thu Jan 01 00:00:00 1970 +0000
280 date: Thu Jan 01 00:00:00 1970 +0000
281 summary: 1.2
281 summary: 1.2
282
282
283 changeset: 4:095197eb4973
283 changeset: 4:095197eb4973
284 parent: 0:f9ee2f85a263
284 parent: 0:f9ee2f85a263
285 user: test
285 user: test
286 date: Thu Jan 01 00:00:00 1970 +0000
286 date: Thu Jan 01 00:00:00 1970 +0000
287 summary: 1.1
287 summary: 1.1
288
288
289 changeset: 3:eebf5a27f8ca
289 changeset: 3:eebf5a27f8ca
290 user: test
290 user: test
291 date: Thu Jan 01 00:00:00 1970 +0000
291 date: Thu Jan 01 00:00:00 1970 +0000
292 summary: 0.3
292 summary: 0.3
293
293
294 changeset: 2:e38ba6f5b7e0
294 changeset: 2:e38ba6f5b7e0
295 user: test
295 user: test
296 date: Thu Jan 01 00:00:00 1970 +0000
296 date: Thu Jan 01 00:00:00 1970 +0000
297 summary: 0.2
297 summary: 0.2
298
298
299 changeset: 1:34c2bf6b0626
299 changeset: 1:34c2bf6b0626
300 user: test
300 user: test
301 date: Thu Jan 01 00:00:00 1970 +0000
301 date: Thu Jan 01 00:00:00 1970 +0000
302 summary: 0.1
302 summary: 0.1
303
303
304 changeset: 0:f9ee2f85a263
304 changeset: 0:f9ee2f85a263
305 user: test
305 user: test
306 date: Thu Jan 01 00:00:00 1970 +0000
306 date: Thu Jan 01 00:00:00 1970 +0000
307 summary: 0.0
307 summary: 0.0
308
308
309
309
310 Incoming full.hg in partial
310 Incoming full.hg in partial
311
311
312 $ hg incoming bundle://../full.hg
312 $ hg incoming bundle://../full.hg
313 comparing with bundle:../full.hg
313 comparing with bundle:../full.hg
314 searching for changes
314 searching for changes
315 changeset: 4:095197eb4973
315 changeset: 4:095197eb4973
316 parent: 0:f9ee2f85a263
316 parent: 0:f9ee2f85a263
317 user: test
317 user: test
318 date: Thu Jan 01 00:00:00 1970 +0000
318 date: Thu Jan 01 00:00:00 1970 +0000
319 summary: 1.1
319 summary: 1.1
320
320
321 changeset: 5:1bb50a9436a7
321 changeset: 5:1bb50a9436a7
322 user: test
322 user: test
323 date: Thu Jan 01 00:00:00 1970 +0000
323 date: Thu Jan 01 00:00:00 1970 +0000
324 summary: 1.2
324 summary: 1.2
325
325
326 changeset: 6:7373c1169842
326 changeset: 6:7373c1169842
327 user: test
327 user: test
328 date: Thu Jan 01 00:00:00 1970 +0000
328 date: Thu Jan 01 00:00:00 1970 +0000
329 summary: 1.3
329 summary: 1.3
330
330
331 changeset: 7:a6a34bfa0076
331 changeset: 7:a6a34bfa0076
332 user: test
332 user: test
333 date: Thu Jan 01 00:00:00 1970 +0000
333 date: Thu Jan 01 00:00:00 1970 +0000
334 summary: 1.3m
334 summary: 1.3m
335
335
336 changeset: 8:aa35859c02ea
336 changeset: 8:aa35859c02ea
337 tag: tip
337 tag: tip
338 parent: 3:eebf5a27f8ca
338 parent: 3:eebf5a27f8ca
339 user: test
339 user: test
340 date: Thu Jan 01 00:00:00 1970 +0000
340 date: Thu Jan 01 00:00:00 1970 +0000
341 summary: 0.3m
341 summary: 0.3m
342
342
343
343
344 Outgoing -R full.hg vs partial2 in partial
344 Outgoing -R full.hg vs partial2 in partial
345
345
346 $ hg -R bundle://../full.hg outgoing ../partial2
346 $ hg -R bundle://../full.hg outgoing ../partial2
347 comparing with ../partial2
347 comparing with ../partial2
348 searching for changes
348 searching for changes
349 changeset: 4:095197eb4973
349 changeset: 4:095197eb4973
350 parent: 0:f9ee2f85a263
350 parent: 0:f9ee2f85a263
351 user: test
351 user: test
352 date: Thu Jan 01 00:00:00 1970 +0000
352 date: Thu Jan 01 00:00:00 1970 +0000
353 summary: 1.1
353 summary: 1.1
354
354
355 changeset: 5:1bb50a9436a7
355 changeset: 5:1bb50a9436a7
356 user: test
356 user: test
357 date: Thu Jan 01 00:00:00 1970 +0000
357 date: Thu Jan 01 00:00:00 1970 +0000
358 summary: 1.2
358 summary: 1.2
359
359
360 changeset: 6:7373c1169842
360 changeset: 6:7373c1169842
361 user: test
361 user: test
362 date: Thu Jan 01 00:00:00 1970 +0000
362 date: Thu Jan 01 00:00:00 1970 +0000
363 summary: 1.3
363 summary: 1.3
364
364
365 changeset: 7:a6a34bfa0076
365 changeset: 7:a6a34bfa0076
366 user: test
366 user: test
367 date: Thu Jan 01 00:00:00 1970 +0000
367 date: Thu Jan 01 00:00:00 1970 +0000
368 summary: 1.3m
368 summary: 1.3m
369
369
370 changeset: 8:aa35859c02ea
370 changeset: 8:aa35859c02ea
371 tag: tip
371 tag: tip
372 parent: 3:eebf5a27f8ca
372 parent: 3:eebf5a27f8ca
373 user: test
373 user: test
374 date: Thu Jan 01 00:00:00 1970 +0000
374 date: Thu Jan 01 00:00:00 1970 +0000
375 summary: 0.3m
375 summary: 0.3m
376
376
377
377
378 Outgoing -R does-not-exist.hg vs partial2 in partial
378 Outgoing -R does-not-exist.hg vs partial2 in partial
379
379
380 $ hg -R bundle://../does-not-exist.hg outgoing ../partial2
380 $ hg -R bundle://../does-not-exist.hg outgoing ../partial2
381 abort: No such file or directory: ../does-not-exist.hg
381 abort: No such file or directory: ../does-not-exist.hg
382 [255]
382 [255]
383 $ cd ..
383 $ cd ..
384
384
385 Direct clone from bundle (all-history)
385 Direct clone from bundle (all-history)
386
386
387 $ hg clone full.hg full-clone
387 $ hg clone full.hg full-clone
388 requesting all changes
388 requesting all changes
389 adding changesets
389 adding changesets
390 adding manifests
390 adding manifests
391 adding file changes
391 adding file changes
392 added 9 changesets with 7 changes to 4 files (+1 heads)
392 added 9 changesets with 7 changes to 4 files (+1 heads)
393 updating to branch default
393 updating to branch default
394 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
394 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
395 $ hg -R full-clone heads
395 $ hg -R full-clone heads
396 changeset: 8:aa35859c02ea
396 changeset: 8:aa35859c02ea
397 tag: tip
397 tag: tip
398 parent: 3:eebf5a27f8ca
398 parent: 3:eebf5a27f8ca
399 user: test
399 user: test
400 date: Thu Jan 01 00:00:00 1970 +0000
400 date: Thu Jan 01 00:00:00 1970 +0000
401 summary: 0.3m
401 summary: 0.3m
402
402
403 changeset: 7:a6a34bfa0076
403 changeset: 7:a6a34bfa0076
404 user: test
404 user: test
405 date: Thu Jan 01 00:00:00 1970 +0000
405 date: Thu Jan 01 00:00:00 1970 +0000
406 summary: 1.3m
406 summary: 1.3m
407
407
408 $ rm -r full-clone
408 $ rm -r full-clone
409
409
410 When cloning from a non-copiable repository into '', do not
410 When cloning from a non-copiable repository into '', do not
411 recurse infinitely (issue 2528)
411 recurse infinitely (issue 2528)
412
412
413 $ hg clone full.hg ''
413 $ hg clone full.hg ''
414 abort: No such file or directory
414 abort: No such file or directory
415 [255]
415 [255]
416
416
417 test for http://mercurial.selenic.com/bts/issue216
417 test for http://mercurial.selenic.com/bts/issue216
418
418
419 Unbundle incremental bundles into fresh empty in one go
419 Unbundle incremental bundles into fresh empty in one go
420
420
421 $ rm -r empty
421 $ rm -r empty
422 $ hg init empty
422 $ hg init empty
423 $ hg -R test bundle --base null -r 0 ../0.hg
423 $ hg -R test bundle --base null -r 0 ../0.hg
424 1 changesets found
424 1 changesets found
425 $ hg -R test bundle --base 0 -r 1 ../1.hg
425 $ hg -R test bundle --base 0 -r 1 ../1.hg
426 1 changesets found
426 1 changesets found
427 $ hg -R empty unbundle -u ../0.hg ../1.hg
427 $ hg -R empty unbundle -u ../0.hg ../1.hg
428 adding changesets
428 adding changesets
429 adding manifests
429 adding manifests
430 adding file changes
430 adding file changes
431 added 1 changesets with 1 changes to 1 files
431 added 1 changesets with 1 changes to 1 files
432 adding changesets
432 adding changesets
433 adding manifests
433 adding manifests
434 adding file changes
434 adding file changes
435 added 1 changesets with 1 changes to 1 files
435 added 1 changesets with 1 changes to 1 files
436 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
436 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
437
437
438 test for 540d1059c802
438 test for 540d1059c802
439
439
440 test for 540d1059c802
440 test for 540d1059c802
441
441
442 $ hg init orig
442 $ hg init orig
443 $ cd orig
443 $ cd orig
444 $ echo foo > foo
444 $ echo foo > foo
445 $ hg add foo
445 $ hg add foo
446 $ hg ci -m 'add foo'
446 $ hg ci -m 'add foo'
447
447
448 $ hg clone . ../copy
448 $ hg clone . ../copy
449 updating to branch default
449 updating to branch default
450 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
450 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
451 $ hg tag foo
451 $ hg tag foo
452
452
453 $ cd ../copy
453 $ cd ../copy
454 $ echo >> foo
454 $ echo >> foo
455 $ hg ci -m 'change foo'
455 $ hg ci -m 'change foo'
456 $ hg bundle ../bundle.hg ../orig
456 $ hg bundle ../bundle.hg ../orig
457 searching for changes
457 searching for changes
458 1 changesets found
458 1 changesets found
459
459
460 $ cd ../orig
460 $ cd ../orig
461 $ hg incoming ../bundle.hg
461 $ hg incoming ../bundle.hg
462 comparing with ../bundle.hg
462 comparing with ../bundle.hg
463 searching for changes
463 searching for changes
464 changeset: 2:ed1b79f46b9a
464 changeset: 2:ed1b79f46b9a
465 tag: tip
465 tag: tip
466 parent: 0:bbd179dfa0a7
466 parent: 0:bbd179dfa0a7
467 user: test
467 user: test
468 date: Thu Jan 01 00:00:00 1970 +0000
468 date: Thu Jan 01 00:00:00 1970 +0000
469 summary: change foo
469 summary: change foo
470
470
471 $ cd ..
471 $ cd ..
472
472
473 test bundle with # in the filename (issue2154):
473 test bundle with # in the filename (issue2154):
474
474
475 $ cp bundle.hg 'test#bundle.hg'
475 $ cp bundle.hg 'test#bundle.hg'
476 $ cd orig
476 $ cd orig
477 $ hg incoming '../test#bundle.hg'
477 $ hg incoming '../test#bundle.hg'
478 comparing with ../test
478 comparing with ../test
479 abort: unknown revision 'bundle.hg'!
479 abort: unknown revision 'bundle.hg'!
480 [255]
480 [255]
481
481
482 note that percent encoding is not handled:
482 note that percent encoding is not handled:
483
483
484 $ hg incoming ../test%23bundle.hg
484 $ hg incoming ../test%23bundle.hg
485 abort: repository ../test%23bundle.hg not found!
485 abort: repository ../test%23bundle.hg not found!
486 [255]
486 [255]
487 $ cd ..
487 $ cd ..
488
488
489 test for http://mercurial.selenic.com/bts/issue1144
489 test for http://mercurial.selenic.com/bts/issue1144
490
490
491 test that verify bundle does not traceback
491 test that verify bundle does not traceback
492
492
493 partial history bundle, fails w/ unkown parent
493 partial history bundle, fails w/ unkown parent
494
494
495 $ hg -R bundle.hg verify
495 $ hg -R bundle.hg verify
496 abort: 00changelog.i@bbd179dfa0a7: unknown parent!
496 abort: 00changelog.i@bbd179dfa0a7: unknown parent!
497 [255]
497 [255]
498
498
499 full history bundle, refuses to verify non-local repo
499 full history bundle, refuses to verify non-local repo
500
500
501 $ hg -R all.hg verify
501 $ hg -R all.hg verify
502 abort: cannot verify bundle or remote repos
502 abort: cannot verify bundle or remote repos
503 [255]
503 [255]
504
504
505 but, regular verify must continue to work
505 but, regular verify must continue to work
506
506
507 $ hg -R orig verify
507 $ hg -R orig verify
508 checking changesets
508 checking changesets
509 checking manifests
509 checking manifests
510 crosschecking files in changesets and manifests
510 crosschecking files in changesets and manifests
511 checking files
511 checking files
512 2 files, 2 changesets, 2 total revisions
512 2 files, 2 changesets, 2 total revisions
513
513
514 diff against bundle
514 diff against bundle
515
515
516 $ hg init b
516 $ hg init b
517 $ cd b
517 $ cd b
518 $ hg -R ../all.hg diff -r tip
518 $ hg -R ../all.hg diff -r tip
519 diff -r aa35859c02ea anotherfile
519 diff -r aa35859c02ea anotherfile
520 --- a/anotherfile Thu Jan 01 00:00:00 1970 +0000
520 --- a/anotherfile Thu Jan 01 00:00:00 1970 +0000
521 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
521 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
522 @@ -1,4 +0,0 @@
522 @@ -1,4 +0,0 @@
523 -0
523 -0
524 -1
524 -1
525 -2
525 -2
526 -3
526 -3
527 $ cd ..
527 $ cd ..
528
528
529 bundle single branch
529 bundle single branch
530
530
531 $ hg init branchy
531 $ hg init branchy
532 $ cd branchy
532 $ cd branchy
533 $ echo a >a
533 $ echo a >a
534 $ hg ci -Ama
534 $ hg ci -Ama
535 adding a
535 adding a
536 $ echo b >b
536 $ echo b >b
537 $ hg ci -Amb
537 $ hg ci -Amb
538 adding b
538 adding b
539 $ echo b1 >b1
539 $ echo b1 >b1
540 $ hg ci -Amb1
540 $ hg ci -Amb1
541 adding b1
541 adding b1
542 $ hg up 0
542 $ hg up 0
543 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
543 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
544 $ echo c >c
544 $ echo c >c
545 $ hg ci -Amc
545 $ hg ci -Amc
546 adding c
546 adding c
547 created new head
547 created new head
548 $ echo c1 >c1
548 $ echo c1 >c1
549 $ hg ci -Amc1
549 $ hg ci -Amc1
550 adding c1
550 adding c1
551 $ hg clone -q .#tip part
551 $ hg clone -q .#tip part
552
552
553 == bundling via incoming
553 == bundling via incoming
554
554
555 $ hg in -R part --bundle incoming.hg --template "{node}\n" .
555 $ hg in -R part --bundle incoming.hg --template "{node}\n" .
556 comparing with .
556 comparing with .
557 searching for changes
557 searching for changes
558 d2ae7f538514cd87c17547b0de4cea71fe1af9fb
558 d2ae7f538514cd87c17547b0de4cea71fe1af9fb
559 5ece8e77363e2b5269e27c66828b72da29e4341a
559 5ece8e77363e2b5269e27c66828b72da29e4341a
560
560
561 == bundling
561 == bundling
562
562
563 $ hg bundle bundle.hg part --debug
563 $ hg bundle bundle.hg part --debug
564 searching for changes
564 searching for changes
565 common changesets up to c0025332f9ed
566 2 changesets found
565 2 changesets found
567 list of changesets:
566 list of changesets:
568 d2ae7f538514cd87c17547b0de4cea71fe1af9fb
567 d2ae7f538514cd87c17547b0de4cea71fe1af9fb
569 5ece8e77363e2b5269e27c66828b72da29e4341a
568 5ece8e77363e2b5269e27c66828b72da29e4341a
570 bundling: 1 changesets
569 bundling: 1 changesets
571 bundling: 2 changesets
570 bundling: 2 changesets
572 bundling: 1/2 manifests (50.00%)
571 bundling: 1/2 manifests (50.00%)
573 bundling: 2/2 manifests (100.00%)
572 bundling: 2/2 manifests (100.00%)
574 bundling: b 0/2 files (0.00%)
573 bundling: b 0/2 files (0.00%)
575 bundling: b1 1/2 files (50.00%)
574 bundling: b1 1/2 files (50.00%)
576
575
@@ -1,717 +1,716
1 $ echo "[extensions]" >> $HGRCPATH
1 $ echo "[extensions]" >> $HGRCPATH
2 $ echo "graphlog=" >> $HGRCPATH
2 $ echo "graphlog=" >> $HGRCPATH
3
3
4 $ hg init a
4 $ hg init a
5 $ cd a
5 $ cd a
6 $ echo foo > t1
6 $ echo foo > t1
7 $ hg add t1
7 $ hg add t1
8 $ hg commit -m "1"
8 $ hg commit -m "1"
9
9
10 $ cd ..
10 $ cd ..
11 $ hg clone a b
11 $ hg clone a b
12 updating to branch default
12 updating to branch default
13 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
13 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
14
14
15 $ cd a
15 $ cd a
16 $ echo foo > t2
16 $ echo foo > t2
17 $ hg add t2
17 $ hg add t2
18 $ hg commit -m "2"
18 $ hg commit -m "2"
19
19
20 $ cd ../b
20 $ cd ../b
21 $ echo foo > t3
21 $ echo foo > t3
22 $ hg add t3
22 $ hg add t3
23 $ hg commit -m "3"
23 $ hg commit -m "3"
24
24
25 $ hg push ../a
25 $ hg push ../a
26 pushing to ../a
26 pushing to ../a
27 searching for changes
27 searching for changes
28 abort: push creates new remote heads on branch 'default'!
28 abort: push creates new remote heads on branch 'default'!
29 (you should pull and merge or use push -f to force)
29 (you should pull and merge or use push -f to force)
30 [255]
30 [255]
31
31
32 $ hg push --debug ../a
32 $ hg push --debug ../a
33 pushing to ../a
33 pushing to ../a
34 searching for changes
34 searching for changes
35 examining 1c9246a22a0a:d8d565842d04
35 examining 1c9246a22a0a:d8d565842d04
36 found incomplete branch 1c9246a22a0a:d8d565842d04
36 found incomplete branch 1c9246a22a0a:d8d565842d04
37 searching: 1 queries
37 searching: 1 queries
38 narrowing 1:1 d8d565842d04
38 narrowing 1:1 d8d565842d04
39 found new branch changeset 1c9246a22a0a
39 found new branch changeset 1c9246a22a0a
40 found new changesets starting at 1c9246a22a0a
40 found new changesets starting at 1c9246a22a0a
41 1 total queries
41 1 total queries
42 common changesets up to d8d565842d04
43 new remote heads on branch 'default'
42 new remote heads on branch 'default'
44 new remote head 1e108cc5548c
43 new remote head 1e108cc5548c
45 abort: push creates new remote heads on branch 'default'!
44 abort: push creates new remote heads on branch 'default'!
46 (you should pull and merge or use push -f to force)
45 (you should pull and merge or use push -f to force)
47 [255]
46 [255]
48
47
49 $ hg pull ../a
48 $ hg pull ../a
50 pulling from ../a
49 pulling from ../a
51 searching for changes
50 searching for changes
52 adding changesets
51 adding changesets
53 adding manifests
52 adding manifests
54 adding file changes
53 adding file changes
55 added 1 changesets with 1 changes to 1 files (+1 heads)
54 added 1 changesets with 1 changes to 1 files (+1 heads)
56 (run 'hg heads' to see heads, 'hg merge' to merge)
55 (run 'hg heads' to see heads, 'hg merge' to merge)
57
56
58 $ hg push ../a
57 $ hg push ../a
59 pushing to ../a
58 pushing to ../a
60 searching for changes
59 searching for changes
61 abort: push creates new remote heads on branch 'default'!
60 abort: push creates new remote heads on branch 'default'!
62 (did you forget to merge? use push -f to force)
61 (did you forget to merge? use push -f to force)
63 [255]
62 [255]
64
63
65 $ hg merge
64 $ hg merge
66 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
65 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
67 (branch merge, don't forget to commit)
66 (branch merge, don't forget to commit)
68
67
69 $ hg commit -m "4"
68 $ hg commit -m "4"
70 $ hg push ../a
69 $ hg push ../a
71 pushing to ../a
70 pushing to ../a
72 searching for changes
71 searching for changes
73 adding changesets
72 adding changesets
74 adding manifests
73 adding manifests
75 adding file changes
74 adding file changes
76 added 2 changesets with 1 changes to 1 files
75 added 2 changesets with 1 changes to 1 files
77
76
78 $ cd ..
77 $ cd ..
79
78
80 $ hg init c
79 $ hg init c
81 $ cd c
80 $ cd c
82 $ for i in 0 1 2; do
81 $ for i in 0 1 2; do
83 > echo $i >> foo
82 > echo $i >> foo
84 > hg ci -Am $i
83 > hg ci -Am $i
85 > done
84 > done
86 adding foo
85 adding foo
87 $ cd ..
86 $ cd ..
88
87
89 $ hg clone c d
88 $ hg clone c d
90 updating to branch default
89 updating to branch default
91 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
90 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
92
91
93 $ cd d
92 $ cd d
94 $ for i in 0 1; do
93 $ for i in 0 1; do
95 > hg co -C $i
94 > hg co -C $i
96 > echo d-$i >> foo
95 > echo d-$i >> foo
97 > hg ci -m d-$i
96 > hg ci -m d-$i
98 > done
97 > done
99 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
98 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
100 created new head
99 created new head
101 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
100 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
102 created new head
101 created new head
103
102
104 $ HGMERGE=true hg merge 3
103 $ HGMERGE=true hg merge 3
105 merging foo
104 merging foo
106 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
105 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
107 (branch merge, don't forget to commit)
106 (branch merge, don't forget to commit)
108
107
109 $ hg ci -m c-d
108 $ hg ci -m c-d
110
109
111 $ hg push ../c
110 $ hg push ../c
112 pushing to ../c
111 pushing to ../c
113 searching for changes
112 searching for changes
114 abort: push creates new remote heads on branch 'default'!
113 abort: push creates new remote heads on branch 'default'!
115 (did you forget to merge? use push -f to force)
114 (did you forget to merge? use push -f to force)
116 [255]
115 [255]
117
116
118 $ hg push -r 2 ../c
117 $ hg push -r 2 ../c
119 pushing to ../c
118 pushing to ../c
120 searching for changes
119 searching for changes
121 no changes found
120 no changes found
122
121
123 $ hg push -r 3 ../c
122 $ hg push -r 3 ../c
124 pushing to ../c
123 pushing to ../c
125 searching for changes
124 searching for changes
126 abort: push creates new remote heads on branch 'default'!
125 abort: push creates new remote heads on branch 'default'!
127 (did you forget to merge? use push -f to force)
126 (did you forget to merge? use push -f to force)
128 [255]
127 [255]
129
128
130 $ hg push -r 3 -r 4 ../c
129 $ hg push -r 3 -r 4 ../c
131 pushing to ../c
130 pushing to ../c
132 searching for changes
131 searching for changes
133 abort: push creates new remote heads on branch 'default'!
132 abort: push creates new remote heads on branch 'default'!
134 (did you forget to merge? use push -f to force)
133 (did you forget to merge? use push -f to force)
135 [255]
134 [255]
136
135
137 $ hg push -f -r 3 -r 4 ../c
136 $ hg push -f -r 3 -r 4 ../c
138 pushing to ../c
137 pushing to ../c
139 searching for changes
138 searching for changes
140 adding changesets
139 adding changesets
141 adding manifests
140 adding manifests
142 adding file changes
141 adding file changes
143 added 2 changesets with 2 changes to 1 files (+2 heads)
142 added 2 changesets with 2 changes to 1 files (+2 heads)
144
143
145 $ hg push -r 5 ../c
144 $ hg push -r 5 ../c
146 pushing to ../c
145 pushing to ../c
147 searching for changes
146 searching for changes
148 adding changesets
147 adding changesets
149 adding manifests
148 adding manifests
150 adding file changes
149 adding file changes
151 added 1 changesets with 1 changes to 1 files (-1 heads)
150 added 1 changesets with 1 changes to 1 files (-1 heads)
152
151
153 $ hg in ../c
152 $ hg in ../c
154 comparing with ../c
153 comparing with ../c
155 searching for changes
154 searching for changes
156 no changes found
155 no changes found
157 [1]
156 [1]
158
157
159
158
160 Issue450: push -r warns about remote head creation even if no heads
159 Issue450: push -r warns about remote head creation even if no heads
161 will be created
160 will be created
162
161
163 $ hg init ../e
162 $ hg init ../e
164 $ hg push -r 0 ../e
163 $ hg push -r 0 ../e
165 pushing to ../e
164 pushing to ../e
166 searching for changes
165 searching for changes
167 adding changesets
166 adding changesets
168 adding manifests
167 adding manifests
169 adding file changes
168 adding file changes
170 added 1 changesets with 1 changes to 1 files
169 added 1 changesets with 1 changes to 1 files
171
170
172 $ hg push -r 1 ../e
171 $ hg push -r 1 ../e
173 pushing to ../e
172 pushing to ../e
174 searching for changes
173 searching for changes
175 adding changesets
174 adding changesets
176 adding manifests
175 adding manifests
177 adding file changes
176 adding file changes
178 added 1 changesets with 1 changes to 1 files
177 added 1 changesets with 1 changes to 1 files
179
178
180 $ cd ..
179 $ cd ..
181
180
182
181
183 Issue736: named branches are not considered for detection of
182 Issue736: named branches are not considered for detection of
184 unmerged heads in "hg push"
183 unmerged heads in "hg push"
185
184
186 $ hg init f
185 $ hg init f
187 $ cd f
186 $ cd f
188 $ hg -q branch a
187 $ hg -q branch a
189 $ echo 0 > foo
188 $ echo 0 > foo
190 $ hg -q ci -Am 0
189 $ hg -q ci -Am 0
191 $ echo 1 > foo
190 $ echo 1 > foo
192 $ hg -q ci -m 1
191 $ hg -q ci -m 1
193 $ hg -q up 0
192 $ hg -q up 0
194 $ echo 2 > foo
193 $ echo 2 > foo
195 $ hg -q ci -m 2
194 $ hg -q ci -m 2
196 $ hg -q up 0
195 $ hg -q up 0
197 $ hg -q branch b
196 $ hg -q branch b
198 $ echo 3 > foo
197 $ echo 3 > foo
199 $ hg -q ci -m 3
198 $ hg -q ci -m 3
200 $ cd ..
199 $ cd ..
201
200
202 $ hg -q clone f g
201 $ hg -q clone f g
203 $ cd g
202 $ cd g
204
203
205 Push on existing branch and new branch:
204 Push on existing branch and new branch:
206
205
207 $ hg -q up 1
206 $ hg -q up 1
208 $ echo 4 > foo
207 $ echo 4 > foo
209 $ hg -q ci -m 4
208 $ hg -q ci -m 4
210 $ hg -q up 0
209 $ hg -q up 0
211 $ echo 5 > foo
210 $ echo 5 > foo
212 $ hg -q branch c
211 $ hg -q branch c
213 $ hg -q ci -m 5
212 $ hg -q ci -m 5
214
213
215 $ hg push ../f
214 $ hg push ../f
216 pushing to ../f
215 pushing to ../f
217 searching for changes
216 searching for changes
218 abort: push creates new remote branches: c!
217 abort: push creates new remote branches: c!
219 (use 'hg push --new-branch' to create new remote branches)
218 (use 'hg push --new-branch' to create new remote branches)
220 [255]
219 [255]
221
220
222 $ hg push -r 4 -r 5 ../f
221 $ hg push -r 4 -r 5 ../f
223 pushing to ../f
222 pushing to ../f
224 searching for changes
223 searching for changes
225 abort: push creates new remote branches: c!
224 abort: push creates new remote branches: c!
226 (use 'hg push --new-branch' to create new remote branches)
225 (use 'hg push --new-branch' to create new remote branches)
227 [255]
226 [255]
228
227
229
228
230 Multiple new branches:
229 Multiple new branches:
231
230
232 $ hg -q branch d
231 $ hg -q branch d
233 $ echo 6 > foo
232 $ echo 6 > foo
234 $ hg -q ci -m 6
233 $ hg -q ci -m 6
235
234
236 $ hg push ../f
235 $ hg push ../f
237 pushing to ../f
236 pushing to ../f
238 searching for changes
237 searching for changes
239 abort: push creates new remote branches: c, d!
238 abort: push creates new remote branches: c, d!
240 (use 'hg push --new-branch' to create new remote branches)
239 (use 'hg push --new-branch' to create new remote branches)
241 [255]
240 [255]
242
241
243 $ hg push -r 4 -r 6 ../f
242 $ hg push -r 4 -r 6 ../f
244 pushing to ../f
243 pushing to ../f
245 searching for changes
244 searching for changes
246 abort: push creates new remote branches: c, d!
245 abort: push creates new remote branches: c, d!
247 (use 'hg push --new-branch' to create new remote branches)
246 (use 'hg push --new-branch' to create new remote branches)
248 [255]
247 [255]
249
248
250 $ cd ../g
249 $ cd ../g
251
250
252
251
253 Fail on multiple head push:
252 Fail on multiple head push:
254
253
255 $ hg -q up 1
254 $ hg -q up 1
256 $ echo 7 > foo
255 $ echo 7 > foo
257 $ hg -q ci -m 7
256 $ hg -q ci -m 7
258
257
259 $ hg push -r 4 -r 7 ../f
258 $ hg push -r 4 -r 7 ../f
260 pushing to ../f
259 pushing to ../f
261 searching for changes
260 searching for changes
262 abort: push creates new remote heads on branch 'a'!
261 abort: push creates new remote heads on branch 'a'!
263 (did you forget to merge? use push -f to force)
262 (did you forget to merge? use push -f to force)
264 [255]
263 [255]
265
264
266 Push replacement head on existing branches:
265 Push replacement head on existing branches:
267
266
268 $ hg -q up 3
267 $ hg -q up 3
269 $ echo 8 > foo
268 $ echo 8 > foo
270 $ hg -q ci -m 8
269 $ hg -q ci -m 8
271
270
272 $ hg push -r 7 -r 8 ../f
271 $ hg push -r 7 -r 8 ../f
273 pushing to ../f
272 pushing to ../f
274 searching for changes
273 searching for changes
275 adding changesets
274 adding changesets
276 adding manifests
275 adding manifests
277 adding file changes
276 adding file changes
278 added 2 changesets with 2 changes to 1 files
277 added 2 changesets with 2 changes to 1 files
279
278
280
279
281 Merge of branch a to other branch b followed by unrelated push
280 Merge of branch a to other branch b followed by unrelated push
282 on branch a:
281 on branch a:
283
282
284 $ hg -q up 7
283 $ hg -q up 7
285 $ HGMERGE=true hg -q merge 8
284 $ HGMERGE=true hg -q merge 8
286 $ hg -q ci -m 9
285 $ hg -q ci -m 9
287 $ hg -q up 8
286 $ hg -q up 8
288 $ echo 10 > foo
287 $ echo 10 > foo
289 $ hg -q ci -m 10
288 $ hg -q ci -m 10
290
289
291 $ hg push -r 9 ../f
290 $ hg push -r 9 ../f
292 pushing to ../f
291 pushing to ../f
293 searching for changes
292 searching for changes
294 adding changesets
293 adding changesets
295 adding manifests
294 adding manifests
296 adding file changes
295 adding file changes
297 added 1 changesets with 1 changes to 1 files (-1 heads)
296 added 1 changesets with 1 changes to 1 files (-1 heads)
298
297
299 $ hg push -r 10 ../f
298 $ hg push -r 10 ../f
300 pushing to ../f
299 pushing to ../f
301 searching for changes
300 searching for changes
302 adding changesets
301 adding changesets
303 adding manifests
302 adding manifests
304 adding file changes
303 adding file changes
305 added 1 changesets with 1 changes to 1 files (+1 heads)
304 added 1 changesets with 1 changes to 1 files (+1 heads)
306
305
307
306
308 Cheating the counting algorithm:
307 Cheating the counting algorithm:
309
308
310 $ hg -q up 9
309 $ hg -q up 9
311 $ HGMERGE=true hg -q merge 2
310 $ HGMERGE=true hg -q merge 2
312 $ hg -q ci -m 11
311 $ hg -q ci -m 11
313 $ hg -q up 1
312 $ hg -q up 1
314 $ echo 12 > foo
313 $ echo 12 > foo
315 $ hg -q ci -m 12
314 $ hg -q ci -m 12
316
315
317 $ hg push -r 11 -r 12 ../f
316 $ hg push -r 11 -r 12 ../f
318 pushing to ../f
317 pushing to ../f
319 searching for changes
318 searching for changes
320 adding changesets
319 adding changesets
321 adding manifests
320 adding manifests
322 adding file changes
321 adding file changes
323 added 2 changesets with 2 changes to 1 files
322 added 2 changesets with 2 changes to 1 files
324
323
325
324
326 Failed push of new named branch:
325 Failed push of new named branch:
327
326
328 $ echo 12 > foo
327 $ echo 12 > foo
329 $ hg -q ci -m 12a
328 $ hg -q ci -m 12a
330 [1]
329 [1]
331 $ hg -q up 11
330 $ hg -q up 11
332 $ echo 13 > foo
331 $ echo 13 > foo
333 $ hg -q branch e
332 $ hg -q branch e
334 $ hg -q ci -m 13d
333 $ hg -q ci -m 13d
335
334
336 $ hg push -r 12 -r 13 ../f
335 $ hg push -r 12 -r 13 ../f
337 pushing to ../f
336 pushing to ../f
338 searching for changes
337 searching for changes
339 abort: push creates new remote branches: e!
338 abort: push creates new remote branches: e!
340 (use 'hg push --new-branch' to create new remote branches)
339 (use 'hg push --new-branch' to create new remote branches)
341 [255]
340 [255]
342
341
343
342
344 Using --new-branch to push new named branch:
343 Using --new-branch to push new named branch:
345
344
346 $ hg push --new-branch -r 12 -r 13 ../f
345 $ hg push --new-branch -r 12 -r 13 ../f
347 pushing to ../f
346 pushing to ../f
348 searching for changes
347 searching for changes
349 adding changesets
348 adding changesets
350 adding manifests
349 adding manifests
351 adding file changes
350 adding file changes
352 added 1 changesets with 1 changes to 1 files
351 added 1 changesets with 1 changes to 1 files
353
352
354
353
355 Checking prepush logic does not allow silently pushing
354 Checking prepush logic does not allow silently pushing
356 multiple new heads:
355 multiple new heads:
357
356
358 $ cd ..
357 $ cd ..
359 $ hg init h
358 $ hg init h
360 $ echo init > h/init
359 $ echo init > h/init
361 $ hg -R h ci -Am init
360 $ hg -R h ci -Am init
362 adding init
361 adding init
363 $ echo a > h/a
362 $ echo a > h/a
364 $ hg -R h ci -Am a
363 $ hg -R h ci -Am a
365 adding a
364 adding a
366 $ hg clone h i
365 $ hg clone h i
367 updating to branch default
366 updating to branch default
368 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
367 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
369 $ hg -R h up 0
368 $ hg -R h up 0
370 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
369 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
371 $ echo b > h/b
370 $ echo b > h/b
372 $ hg -R h ci -Am b
371 $ hg -R h ci -Am b
373 adding b
372 adding b
374 created new head
373 created new head
375 $ hg -R i up 0
374 $ hg -R i up 0
376 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
375 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
377 $ echo c > i/c
376 $ echo c > i/c
378 $ hg -R i ci -Am c
377 $ hg -R i ci -Am c
379 adding c
378 adding c
380 created new head
379 created new head
381
380
382 $ hg -R i push h
381 $ hg -R i push h
383 pushing to h
382 pushing to h
384 searching for changes
383 searching for changes
385 abort: push creates new remote heads on branch 'default'!
384 abort: push creates new remote heads on branch 'default'!
386 (you should pull and merge or use push -f to force)
385 (you should pull and merge or use push -f to force)
387 [255]
386 [255]
388
387
389
388
390 Check prepush logic with merged branches:
389 Check prepush logic with merged branches:
391
390
392 $ hg init j
391 $ hg init j
393 $ hg -R j branch a
392 $ hg -R j branch a
394 marked working directory as branch a
393 marked working directory as branch a
395 $ echo init > j/foo
394 $ echo init > j/foo
396 $ hg -R j ci -Am init
395 $ hg -R j ci -Am init
397 adding foo
396 adding foo
398 $ hg clone j k
397 $ hg clone j k
399 updating to branch a
398 updating to branch a
400 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
399 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
401 $ echo a1 > j/foo
400 $ echo a1 > j/foo
402 $ hg -R j ci -m a1
401 $ hg -R j ci -m a1
403 $ hg -R k branch b
402 $ hg -R k branch b
404 marked working directory as branch b
403 marked working directory as branch b
405 $ echo b > k/foo
404 $ echo b > k/foo
406 $ hg -R k ci -m b
405 $ hg -R k ci -m b
407 $ hg -R k up 0
406 $ hg -R k up 0
408 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
407 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
409
408
410 $ hg -R k merge b
409 $ hg -R k merge b
411 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
410 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
412 (branch merge, don't forget to commit)
411 (branch merge, don't forget to commit)
413
412
414 $ hg -R k ci -m merge
413 $ hg -R k ci -m merge
415
414
416 $ hg -R k push -r a j
415 $ hg -R k push -r a j
417 pushing to j
416 pushing to j
418 searching for changes
417 searching for changes
419 abort: push creates new remote branches: b!
418 abort: push creates new remote branches: b!
420 (use 'hg push --new-branch' to create new remote branches)
419 (use 'hg push --new-branch' to create new remote branches)
421 [255]
420 [255]
422
421
423
422
424 Prepush -r should not allow you to sneak in new heads:
423 Prepush -r should not allow you to sneak in new heads:
425
424
426 $ hg init l
425 $ hg init l
427 $ cd l
426 $ cd l
428 $ echo a >> foo
427 $ echo a >> foo
429 $ hg -q add foo
428 $ hg -q add foo
430 $ hg -q branch a
429 $ hg -q branch a
431 $ hg -q ci -ma
430 $ hg -q ci -ma
432 $ hg -q up null
431 $ hg -q up null
433 $ echo a >> foo
432 $ echo a >> foo
434 $ hg -q add foo
433 $ hg -q add foo
435 $ hg -q branch b
434 $ hg -q branch b
436 $ hg -q ci -mb
435 $ hg -q ci -mb
437 $ cd ..
436 $ cd ..
438 $ hg -q clone l m -u a
437 $ hg -q clone l m -u a
439 $ cd m
438 $ cd m
440 $ hg -q merge b
439 $ hg -q merge b
441 $ hg -q ci -mmb
440 $ hg -q ci -mmb
442 $ hg -q up 0
441 $ hg -q up 0
443 $ echo a >> foo
442 $ echo a >> foo
444 $ hg -q ci -ma2
443 $ hg -q ci -ma2
445 $ hg -q up 2
444 $ hg -q up 2
446 $ echo a >> foo
445 $ echo a >> foo
447 $ hg -q branch -f b
446 $ hg -q branch -f b
448 $ hg -q ci -mb2
447 $ hg -q ci -mb2
449 $ hg -q merge 3
448 $ hg -q merge 3
450 $ hg -q ci -mma
449 $ hg -q ci -mma
451
450
452 $ hg push ../l -b b
451 $ hg push ../l -b b
453 pushing to ../l
452 pushing to ../l
454 searching for changes
453 searching for changes
455 abort: push creates new remote heads on branch 'a'!
454 abort: push creates new remote heads on branch 'a'!
456 (did you forget to merge? use push -f to force)
455 (did you forget to merge? use push -f to force)
457 [255]
456 [255]
458
457
459 $ cd ..
458 $ cd ..
460
459
461
460
462 Check prepush with new branch head on former topo non-head:
461 Check prepush with new branch head on former topo non-head:
463
462
464 $ hg init n
463 $ hg init n
465 $ cd n
464 $ cd n
466 $ hg branch A
465 $ hg branch A
467 marked working directory as branch A
466 marked working directory as branch A
468 $ echo a >a
467 $ echo a >a
469 $ hg ci -Ama
468 $ hg ci -Ama
470 adding a
469 adding a
471 $ hg branch B
470 $ hg branch B
472 marked working directory as branch B
471 marked working directory as branch B
473 $ echo b >b
472 $ echo b >b
474 $ hg ci -Amb
473 $ hg ci -Amb
475 adding b
474 adding b
476
475
477 b is now branch head of B, and a topological head
476 b is now branch head of B, and a topological head
478 a is now branch head of A, but not a topological head
477 a is now branch head of A, but not a topological head
479
478
480 $ hg clone . inner
479 $ hg clone . inner
481 updating to branch B
480 updating to branch B
482 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
481 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
483 $ cd inner
482 $ cd inner
484 $ hg up B
483 $ hg up B
485 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
484 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
486 $ echo b1 >b1
485 $ echo b1 >b1
487 $ hg ci -Amb1
486 $ hg ci -Amb1
488 adding b1
487 adding b1
489
488
490 in the clone b1 is now the head of B
489 in the clone b1 is now the head of B
491
490
492 $ cd ..
491 $ cd ..
493 $ hg up 0
492 $ hg up 0
494 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
493 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
495 $ echo a2 >a2
494 $ echo a2 >a2
496 $ hg ci -Ama2
495 $ hg ci -Ama2
497 adding a2
496 adding a2
498
497
499 a2 is now the new branch head of A, and a new topological head
498 a2 is now the new branch head of A, and a new topological head
500 it replaces a former inner branch head, so it should at most warn about
499 it replaces a former inner branch head, so it should at most warn about
501 A, not B
500 A, not B
502
501
503 glog of local:
502 glog of local:
504
503
505 $ hg glog --template "{rev}: {branches} {desc}\n"
504 $ hg glog --template "{rev}: {branches} {desc}\n"
506 @ 2: A a2
505 @ 2: A a2
507 |
506 |
508 | o 1: B b
507 | o 1: B b
509 |/
508 |/
510 o 0: A a
509 o 0: A a
511
510
512 glog of remote:
511 glog of remote:
513
512
514 $ hg glog -R inner --template "{rev}: {branches} {desc}\n"
513 $ hg glog -R inner --template "{rev}: {branches} {desc}\n"
515 @ 2: B b1
514 @ 2: B b1
516 |
515 |
517 o 1: B b
516 o 1: B b
518 |
517 |
519 o 0: A a
518 o 0: A a
520
519
521 outgoing:
520 outgoing:
522
521
523 $ hg out inner --template "{rev}: {branches} {desc}\n"
522 $ hg out inner --template "{rev}: {branches} {desc}\n"
524 comparing with inner
523 comparing with inner
525 searching for changes
524 searching for changes
526 2: A a2
525 2: A a2
527
526
528 $ hg push inner
527 $ hg push inner
529 pushing to inner
528 pushing to inner
530 searching for changes
529 searching for changes
531 adding changesets
530 adding changesets
532 adding manifests
531 adding manifests
533 adding file changes
532 adding file changes
534 added 1 changesets with 1 changes to 1 files (+1 heads)
533 added 1 changesets with 1 changes to 1 files (+1 heads)
535
534
536 $ cd ..
535 $ cd ..
537
536
538
537
539 Check prepush with new branch head on former topo head:
538 Check prepush with new branch head on former topo head:
540
539
541 $ hg init o
540 $ hg init o
542 $ cd o
541 $ cd o
543 $ hg branch A
542 $ hg branch A
544 marked working directory as branch A
543 marked working directory as branch A
545 $ echo a >a
544 $ echo a >a
546 $ hg ci -Ama
545 $ hg ci -Ama
547 adding a
546 adding a
548 $ hg branch B
547 $ hg branch B
549 marked working directory as branch B
548 marked working directory as branch B
550 $ echo b >b
549 $ echo b >b
551 $ hg ci -Amb
550 $ hg ci -Amb
552 adding b
551 adding b
553
552
554 b is now branch head of B, and a topological head
553 b is now branch head of B, and a topological head
555
554
556 $ hg up 0
555 $ hg up 0
557 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
556 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
558 $ echo a1 >a1
557 $ echo a1 >a1
559 $ hg ci -Ama1
558 $ hg ci -Ama1
560 adding a1
559 adding a1
561
560
562 a1 is now branch head of A, and a topological head
561 a1 is now branch head of A, and a topological head
563
562
564 $ hg clone . inner
563 $ hg clone . inner
565 updating to branch A
564 updating to branch A
566 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
565 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
567 $ cd inner
566 $ cd inner
568 $ hg up B
567 $ hg up B
569 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
568 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
570 $ echo b1 >b1
569 $ echo b1 >b1
571 $ hg ci -Amb1
570 $ hg ci -Amb1
572 adding b1
571 adding b1
573
572
574 in the clone b1 is now the head of B
573 in the clone b1 is now the head of B
575
574
576 $ cd ..
575 $ cd ..
577 $ echo a2 >a2
576 $ echo a2 >a2
578 $ hg ci -Ama2
577 $ hg ci -Ama2
579 adding a2
578 adding a2
580
579
581 a2 is now the new branch head of A, and a topological head
580 a2 is now the new branch head of A, and a topological head
582 it replaces a former topological and branch head, so this should not warn
581 it replaces a former topological and branch head, so this should not warn
583
582
584 glog of local:
583 glog of local:
585
584
586 $ hg glog --template "{rev}: {branches} {desc}\n"
585 $ hg glog --template "{rev}: {branches} {desc}\n"
587 @ 3: A a2
586 @ 3: A a2
588 |
587 |
589 o 2: A a1
588 o 2: A a1
590 |
589 |
591 | o 1: B b
590 | o 1: B b
592 |/
591 |/
593 o 0: A a
592 o 0: A a
594
593
595 glog of remote:
594 glog of remote:
596
595
597 $ hg glog -R inner --template "{rev}: {branches} {desc}\n"
596 $ hg glog -R inner --template "{rev}: {branches} {desc}\n"
598 @ 3: B b1
597 @ 3: B b1
599 |
598 |
600 | o 2: A a1
599 | o 2: A a1
601 | |
600 | |
602 o | 1: B b
601 o | 1: B b
603 |/
602 |/
604 o 0: A a
603 o 0: A a
605
604
606 outgoing:
605 outgoing:
607
606
608 $ hg out inner --template "{rev}: {branches} {desc}\n"
607 $ hg out inner --template "{rev}: {branches} {desc}\n"
609 comparing with inner
608 comparing with inner
610 searching for changes
609 searching for changes
611 3: A a2
610 3: A a2
612
611
613 $ hg push inner
612 $ hg push inner
614 pushing to inner
613 pushing to inner
615 searching for changes
614 searching for changes
616 adding changesets
615 adding changesets
617 adding manifests
616 adding manifests
618 adding file changes
617 adding file changes
619 added 1 changesets with 1 changes to 1 files
618 added 1 changesets with 1 changes to 1 files
620
619
621 $ cd ..
620 $ cd ..
622
621
623
622
624 Check prepush with new branch head and new child of former branch head
623 Check prepush with new branch head and new child of former branch head
625 but child is on different branch:
624 but child is on different branch:
626
625
627 $ hg init p
626 $ hg init p
628 $ cd p
627 $ cd p
629 $ hg branch A
628 $ hg branch A
630 marked working directory as branch A
629 marked working directory as branch A
631 $ echo a0 >a
630 $ echo a0 >a
632 $ hg ci -Ama0
631 $ hg ci -Ama0
633 adding a
632 adding a
634 $ echo a1 >a
633 $ echo a1 >a
635 $ hg ci -ma1
634 $ hg ci -ma1
636 $ hg up null
635 $ hg up null
637 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
636 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
638 $ hg branch B
637 $ hg branch B
639 marked working directory as branch B
638 marked working directory as branch B
640 $ echo b0 >b
639 $ echo b0 >b
641 $ hg ci -Amb0
640 $ hg ci -Amb0
642 adding b
641 adding b
643 $ echo b1 >b
642 $ echo b1 >b
644 $ hg ci -mb1
643 $ hg ci -mb1
645
644
646 $ hg clone . inner
645 $ hg clone . inner
647 updating to branch B
646 updating to branch B
648 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
647 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
649
648
650 $ hg up A
649 $ hg up A
651 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
650 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
652 $ hg branch -f B
651 $ hg branch -f B
653 marked working directory as branch B
652 marked working directory as branch B
654 $ echo a3 >a
653 $ echo a3 >a
655 $ hg ci -ma3
654 $ hg ci -ma3
656 created new head
655 created new head
657 $ hg up 3
656 $ hg up 3
658 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
657 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
659 $ hg branch -f A
658 $ hg branch -f A
660 marked working directory as branch A
659 marked working directory as branch A
661 $ echo b3 >b
660 $ echo b3 >b
662 $ hg ci -mb3
661 $ hg ci -mb3
663 created new head
662 created new head
664
663
665 glog of local:
664 glog of local:
666
665
667 $ hg glog --template "{rev}: {branches} {desc}\n"
666 $ hg glog --template "{rev}: {branches} {desc}\n"
668 @ 5: A b3
667 @ 5: A b3
669 |
668 |
670 | o 4: B a3
669 | o 4: B a3
671 | |
670 | |
672 o | 3: B b1
671 o | 3: B b1
673 | |
672 | |
674 o | 2: B b0
673 o | 2: B b0
675 /
674 /
676 o 1: A a1
675 o 1: A a1
677 |
676 |
678 o 0: A a0
677 o 0: A a0
679
678
680 glog of remote:
679 glog of remote:
681
680
682 $ hg glog -R inner --template "{rev}: {branches} {desc}\n"
681 $ hg glog -R inner --template "{rev}: {branches} {desc}\n"
683 @ 3: B b1
682 @ 3: B b1
684 |
683 |
685 o 2: B b0
684 o 2: B b0
686
685
687 o 1: A a1
686 o 1: A a1
688 |
687 |
689 o 0: A a0
688 o 0: A a0
690
689
691 outgoing:
690 outgoing:
692
691
693 $ hg out inner --template "{rev}: {branches} {desc}\n"
692 $ hg out inner --template "{rev}: {branches} {desc}\n"
694 comparing with inner
693 comparing with inner
695 searching for changes
694 searching for changes
696 4: B a3
695 4: B a3
697 5: A b3
696 5: A b3
698
697
699 $ hg push inner
698 $ hg push inner
700 pushing to inner
699 pushing to inner
701 searching for changes
700 searching for changes
702 abort: push creates new remote heads on branch 'A'!
701 abort: push creates new remote heads on branch 'A'!
703 (did you forget to merge? use push -f to force)
702 (did you forget to merge? use push -f to force)
704 [255]
703 [255]
705
704
706 $ hg push inner -r4 -r5
705 $ hg push inner -r4 -r5
707 pushing to inner
706 pushing to inner
708 searching for changes
707 searching for changes
709 abort: push creates new remote heads on branch 'A'!
708 abort: push creates new remote heads on branch 'A'!
710 (did you forget to merge? use push -f to force)
709 (did you forget to merge? use push -f to force)
711 [255]
710 [255]
712
711
713 $ hg in inner
712 $ hg in inner
714 comparing with inner
713 comparing with inner
715 searching for changes
714 searching for changes
716 no changes found
715 no changes found
717 [1]
716 [1]
General Comments 0
You need to be logged in to leave comments. Login now