##// END OF EJS Templates
merge with stable
Matt Mackall -
r17059:fba17a64 merge default
parent child Browse files
Show More
@@ -1,3574 +1,3578 b''
1 # mq.py - patch queues for mercurial
1 # mq.py - patch queues for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
3 # Copyright 2005, 2006 Chris Mason <mason@suse.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 '''manage a stack of patches
8 '''manage a stack of patches
9
9
10 This extension lets you work with a stack of patches in a Mercurial
10 This extension lets you work with a stack of patches in a Mercurial
11 repository. It manages two stacks of patches - all known patches, and
11 repository. It manages two stacks of patches - all known patches, and
12 applied patches (subset of known patches).
12 applied patches (subset of known patches).
13
13
14 Known patches are represented as patch files in the .hg/patches
14 Known patches are represented as patch files in the .hg/patches
15 directory. Applied patches are both patch files and changesets.
15 directory. Applied patches are both patch files and changesets.
16
16
17 Common tasks (use :hg:`help command` for more details)::
17 Common tasks (use :hg:`help command` for more details)::
18
18
19 create new patch qnew
19 create new patch qnew
20 import existing patch qimport
20 import existing patch qimport
21
21
22 print patch series qseries
22 print patch series qseries
23 print applied patches qapplied
23 print applied patches qapplied
24
24
25 add known patch to applied stack qpush
25 add known patch to applied stack qpush
26 remove patch from applied stack qpop
26 remove patch from applied stack qpop
27 refresh contents of top applied patch qrefresh
27 refresh contents of top applied patch qrefresh
28
28
29 By default, mq will automatically use git patches when required to
29 By default, mq will automatically use git patches when required to
30 avoid losing file mode changes, copy records, binary files or empty
30 avoid losing file mode changes, copy records, binary files or empty
31 files creations or deletions. This behaviour can be configured with::
31 files creations or deletions. This behaviour can be configured with::
32
32
33 [mq]
33 [mq]
34 git = auto/keep/yes/no
34 git = auto/keep/yes/no
35
35
36 If set to 'keep', mq will obey the [diff] section configuration while
36 If set to 'keep', mq will obey the [diff] section configuration while
37 preserving existing git patches upon qrefresh. If set to 'yes' or
37 preserving existing git patches upon qrefresh. If set to 'yes' or
38 'no', mq will override the [diff] section and always generate git or
38 'no', mq will override the [diff] section and always generate git or
39 regular patches, possibly losing data in the second case.
39 regular patches, possibly losing data in the second case.
40
40
41 It may be desirable for mq changesets to be kept in the secret phase (see
41 It may be desirable for mq changesets to be kept in the secret phase (see
42 :hg:`help phases`), which can be enabled with the following setting::
42 :hg:`help phases`), which can be enabled with the following setting::
43
43
44 [mq]
44 [mq]
45 secret = True
45 secret = True
46
46
47 You will by default be managing a patch queue named "patches". You can
47 You will by default be managing a patch queue named "patches". You can
48 create other, independent patch queues with the :hg:`qqueue` command.
48 create other, independent patch queues with the :hg:`qqueue` command.
49
49
50 If the working directory contains uncommitted files, qpush, qpop and
50 If the working directory contains uncommitted files, qpush, qpop and
51 qgoto abort immediately. If -f/--force is used, the changes are
51 qgoto abort immediately. If -f/--force is used, the changes are
52 discarded. Setting::
52 discarded. Setting::
53
53
54 [mq]
54 [mq]
55 keepchanges = True
55 keepchanges = True
56
56
57 make them behave as if --keep-changes were passed, and non-conflicting
57 make them behave as if --keep-changes were passed, and non-conflicting
58 local changes will be tolerated and preserved. If incompatible options
58 local changes will be tolerated and preserved. If incompatible options
59 such as -f/--force or --exact are passed, this setting is ignored.
59 such as -f/--force or --exact are passed, this setting is ignored.
60 '''
60 '''
61
61
62 from mercurial.i18n import _
62 from mercurial.i18n import _
63 from mercurial.node import bin, hex, short, nullid, nullrev
63 from mercurial.node import bin, hex, short, nullid, nullrev
64 from mercurial.lock import release
64 from mercurial.lock import release
65 from mercurial import commands, cmdutil, hg, scmutil, util, revset
65 from mercurial import commands, cmdutil, hg, scmutil, util, revset
66 from mercurial import repair, extensions, url, error, phases
66 from mercurial import repair, extensions, url, error, phases
67 from mercurial import patch as patchmod
67 from mercurial import patch as patchmod
68 import os, re, errno, shutil
68 import os, re, errno, shutil
69
69
70 commands.norepo += " qclone"
70 commands.norepo += " qclone"
71
71
72 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
72 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
73
73
74 cmdtable = {}
74 cmdtable = {}
75 command = cmdutil.command(cmdtable)
75 command = cmdutil.command(cmdtable)
76 testedwith = 'internal'
76 testedwith = 'internal'
77
77
78 # Patch names looks like unix-file names.
78 # Patch names looks like unix-file names.
79 # They must be joinable with queue directory and result in the patch path.
79 # They must be joinable with queue directory and result in the patch path.
80 normname = util.normpath
80 normname = util.normpath
81
81
82 class statusentry(object):
82 class statusentry(object):
83 def __init__(self, node, name):
83 def __init__(self, node, name):
84 self.node, self.name = node, name
84 self.node, self.name = node, name
85 def __repr__(self):
85 def __repr__(self):
86 return hex(self.node) + ':' + self.name
86 return hex(self.node) + ':' + self.name
87
87
88 class patchheader(object):
88 class patchheader(object):
89 def __init__(self, pf, plainmode=False):
89 def __init__(self, pf, plainmode=False):
90 def eatdiff(lines):
90 def eatdiff(lines):
91 while lines:
91 while lines:
92 l = lines[-1]
92 l = lines[-1]
93 if (l.startswith("diff -") or
93 if (l.startswith("diff -") or
94 l.startswith("Index:") or
94 l.startswith("Index:") or
95 l.startswith("===========")):
95 l.startswith("===========")):
96 del lines[-1]
96 del lines[-1]
97 else:
97 else:
98 break
98 break
99 def eatempty(lines):
99 def eatempty(lines):
100 while lines:
100 while lines:
101 if not lines[-1].strip():
101 if not lines[-1].strip():
102 del lines[-1]
102 del lines[-1]
103 else:
103 else:
104 break
104 break
105
105
106 message = []
106 message = []
107 comments = []
107 comments = []
108 user = None
108 user = None
109 date = None
109 date = None
110 parent = None
110 parent = None
111 format = None
111 format = None
112 subject = None
112 subject = None
113 branch = None
113 branch = None
114 nodeid = None
114 nodeid = None
115 diffstart = 0
115 diffstart = 0
116
116
117 for line in file(pf):
117 for line in file(pf):
118 line = line.rstrip()
118 line = line.rstrip()
119 if (line.startswith('diff --git')
119 if (line.startswith('diff --git')
120 or (diffstart and line.startswith('+++ '))):
120 or (diffstart and line.startswith('+++ '))):
121 diffstart = 2
121 diffstart = 2
122 break
122 break
123 diffstart = 0 # reset
123 diffstart = 0 # reset
124 if line.startswith("--- "):
124 if line.startswith("--- "):
125 diffstart = 1
125 diffstart = 1
126 continue
126 continue
127 elif format == "hgpatch":
127 elif format == "hgpatch":
128 # parse values when importing the result of an hg export
128 # parse values when importing the result of an hg export
129 if line.startswith("# User "):
129 if line.startswith("# User "):
130 user = line[7:]
130 user = line[7:]
131 elif line.startswith("# Date "):
131 elif line.startswith("# Date "):
132 date = line[7:]
132 date = line[7:]
133 elif line.startswith("# Parent "):
133 elif line.startswith("# Parent "):
134 parent = line[9:].lstrip()
134 parent = line[9:].lstrip()
135 elif line.startswith("# Branch "):
135 elif line.startswith("# Branch "):
136 branch = line[9:]
136 branch = line[9:]
137 elif line.startswith("# Node ID "):
137 elif line.startswith("# Node ID "):
138 nodeid = line[10:]
138 nodeid = line[10:]
139 elif not line.startswith("# ") and line:
139 elif not line.startswith("# ") and line:
140 message.append(line)
140 message.append(line)
141 format = None
141 format = None
142 elif line == '# HG changeset patch':
142 elif line == '# HG changeset patch':
143 message = []
143 message = []
144 format = "hgpatch"
144 format = "hgpatch"
145 elif (format != "tagdone" and (line.startswith("Subject: ") or
145 elif (format != "tagdone" and (line.startswith("Subject: ") or
146 line.startswith("subject: "))):
146 line.startswith("subject: "))):
147 subject = line[9:]
147 subject = line[9:]
148 format = "tag"
148 format = "tag"
149 elif (format != "tagdone" and (line.startswith("From: ") or
149 elif (format != "tagdone" and (line.startswith("From: ") or
150 line.startswith("from: "))):
150 line.startswith("from: "))):
151 user = line[6:]
151 user = line[6:]
152 format = "tag"
152 format = "tag"
153 elif (format != "tagdone" and (line.startswith("Date: ") or
153 elif (format != "tagdone" and (line.startswith("Date: ") or
154 line.startswith("date: "))):
154 line.startswith("date: "))):
155 date = line[6:]
155 date = line[6:]
156 format = "tag"
156 format = "tag"
157 elif format == "tag" and line == "":
157 elif format == "tag" and line == "":
158 # when looking for tags (subject: from: etc) they
158 # when looking for tags (subject: from: etc) they
159 # end once you find a blank line in the source
159 # end once you find a blank line in the source
160 format = "tagdone"
160 format = "tagdone"
161 elif message or line:
161 elif message or line:
162 message.append(line)
162 message.append(line)
163 comments.append(line)
163 comments.append(line)
164
164
165 eatdiff(message)
165 eatdiff(message)
166 eatdiff(comments)
166 eatdiff(comments)
167 # Remember the exact starting line of the patch diffs before consuming
167 # Remember the exact starting line of the patch diffs before consuming
168 # empty lines, for external use by TortoiseHg and others
168 # empty lines, for external use by TortoiseHg and others
169 self.diffstartline = len(comments)
169 self.diffstartline = len(comments)
170 eatempty(message)
170 eatempty(message)
171 eatempty(comments)
171 eatempty(comments)
172
172
173 # make sure message isn't empty
173 # make sure message isn't empty
174 if format and format.startswith("tag") and subject:
174 if format and format.startswith("tag") and subject:
175 message.insert(0, "")
175 message.insert(0, "")
176 message.insert(0, subject)
176 message.insert(0, subject)
177
177
178 self.message = message
178 self.message = message
179 self.comments = comments
179 self.comments = comments
180 self.user = user
180 self.user = user
181 self.date = date
181 self.date = date
182 self.parent = parent
182 self.parent = parent
183 # nodeid and branch are for external use by TortoiseHg and others
183 # nodeid and branch are for external use by TortoiseHg and others
184 self.nodeid = nodeid
184 self.nodeid = nodeid
185 self.branch = branch
185 self.branch = branch
186 self.haspatch = diffstart > 1
186 self.haspatch = diffstart > 1
187 self.plainmode = plainmode
187 self.plainmode = plainmode
188
188
189 def setuser(self, user):
189 def setuser(self, user):
190 if not self.updateheader(['From: ', '# User '], user):
190 if not self.updateheader(['From: ', '# User '], user):
191 try:
191 try:
192 patchheaderat = self.comments.index('# HG changeset patch')
192 patchheaderat = self.comments.index('# HG changeset patch')
193 self.comments.insert(patchheaderat + 1, '# User ' + user)
193 self.comments.insert(patchheaderat + 1, '# User ' + user)
194 except ValueError:
194 except ValueError:
195 if self.plainmode or self._hasheader(['Date: ']):
195 if self.plainmode or self._hasheader(['Date: ']):
196 self.comments = ['From: ' + user] + self.comments
196 self.comments = ['From: ' + user] + self.comments
197 else:
197 else:
198 tmp = ['# HG changeset patch', '# User ' + user, '']
198 tmp = ['# HG changeset patch', '# User ' + user, '']
199 self.comments = tmp + self.comments
199 self.comments = tmp + self.comments
200 self.user = user
200 self.user = user
201
201
202 def setdate(self, date):
202 def setdate(self, date):
203 if not self.updateheader(['Date: ', '# Date '], date):
203 if not self.updateheader(['Date: ', '# Date '], date):
204 try:
204 try:
205 patchheaderat = self.comments.index('# HG changeset patch')
205 patchheaderat = self.comments.index('# HG changeset patch')
206 self.comments.insert(patchheaderat + 1, '# Date ' + date)
206 self.comments.insert(patchheaderat + 1, '# Date ' + date)
207 except ValueError:
207 except ValueError:
208 if self.plainmode or self._hasheader(['From: ']):
208 if self.plainmode or self._hasheader(['From: ']):
209 self.comments = ['Date: ' + date] + self.comments
209 self.comments = ['Date: ' + date] + self.comments
210 else:
210 else:
211 tmp = ['# HG changeset patch', '# Date ' + date, '']
211 tmp = ['# HG changeset patch', '# Date ' + date, '']
212 self.comments = tmp + self.comments
212 self.comments = tmp + self.comments
213 self.date = date
213 self.date = date
214
214
215 def setparent(self, parent):
215 def setparent(self, parent):
216 if not self.updateheader(['# Parent '], parent):
216 if not self.updateheader(['# Parent '], parent):
217 try:
217 try:
218 patchheaderat = self.comments.index('# HG changeset patch')
218 patchheaderat = self.comments.index('# HG changeset patch')
219 self.comments.insert(patchheaderat + 1, '# Parent ' + parent)
219 self.comments.insert(patchheaderat + 1, '# Parent ' + parent)
220 except ValueError:
220 except ValueError:
221 pass
221 pass
222 self.parent = parent
222 self.parent = parent
223
223
224 def setmessage(self, message):
224 def setmessage(self, message):
225 if self.comments:
225 if self.comments:
226 self._delmsg()
226 self._delmsg()
227 self.message = [message]
227 self.message = [message]
228 self.comments += self.message
228 self.comments += self.message
229
229
230 def updateheader(self, prefixes, new):
230 def updateheader(self, prefixes, new):
231 '''Update all references to a field in the patch header.
231 '''Update all references to a field in the patch header.
232 Return whether the field is present.'''
232 Return whether the field is present.'''
233 res = False
233 res = False
234 for prefix in prefixes:
234 for prefix in prefixes:
235 for i in xrange(len(self.comments)):
235 for i in xrange(len(self.comments)):
236 if self.comments[i].startswith(prefix):
236 if self.comments[i].startswith(prefix):
237 self.comments[i] = prefix + new
237 self.comments[i] = prefix + new
238 res = True
238 res = True
239 break
239 break
240 return res
240 return res
241
241
242 def _hasheader(self, prefixes):
242 def _hasheader(self, prefixes):
243 '''Check if a header starts with any of the given prefixes.'''
243 '''Check if a header starts with any of the given prefixes.'''
244 for prefix in prefixes:
244 for prefix in prefixes:
245 for comment in self.comments:
245 for comment in self.comments:
246 if comment.startswith(prefix):
246 if comment.startswith(prefix):
247 return True
247 return True
248 return False
248 return False
249
249
250 def __str__(self):
250 def __str__(self):
251 if not self.comments:
251 if not self.comments:
252 return ''
252 return ''
253 return '\n'.join(self.comments) + '\n\n'
253 return '\n'.join(self.comments) + '\n\n'
254
254
255 def _delmsg(self):
255 def _delmsg(self):
256 '''Remove existing message, keeping the rest of the comments fields.
256 '''Remove existing message, keeping the rest of the comments fields.
257 If comments contains 'subject: ', message will prepend
257 If comments contains 'subject: ', message will prepend
258 the field and a blank line.'''
258 the field and a blank line.'''
259 if self.message:
259 if self.message:
260 subj = 'subject: ' + self.message[0].lower()
260 subj = 'subject: ' + self.message[0].lower()
261 for i in xrange(len(self.comments)):
261 for i in xrange(len(self.comments)):
262 if subj == self.comments[i].lower():
262 if subj == self.comments[i].lower():
263 del self.comments[i]
263 del self.comments[i]
264 self.message = self.message[2:]
264 self.message = self.message[2:]
265 break
265 break
266 ci = 0
266 ci = 0
267 for mi in self.message:
267 for mi in self.message:
268 while mi != self.comments[ci]:
268 while mi != self.comments[ci]:
269 ci += 1
269 ci += 1
270 del self.comments[ci]
270 del self.comments[ci]
271
271
272 def newcommit(repo, phase, *args, **kwargs):
272 def newcommit(repo, phase, *args, **kwargs):
273 """helper dedicated to ensure a commit respect mq.secret setting
273 """helper dedicated to ensure a commit respect mq.secret setting
274
274
275 It should be used instead of repo.commit inside the mq source for operation
275 It should be used instead of repo.commit inside the mq source for operation
276 creating new changeset.
276 creating new changeset.
277 """
277 """
278 if phase is None:
278 if phase is None:
279 if repo.ui.configbool('mq', 'secret', False):
279 if repo.ui.configbool('mq', 'secret', False):
280 phase = phases.secret
280 phase = phases.secret
281 if phase is not None:
281 if phase is not None:
282 backup = repo.ui.backupconfig('phases', 'new-commit')
282 backup = repo.ui.backupconfig('phases', 'new-commit')
283 # Marking the repository as committing an mq patch can be used
283 # Marking the repository as committing an mq patch can be used
284 # to optimize operations like _branchtags().
284 # to optimize operations like _branchtags().
285 repo._committingpatch = True
285 repo._committingpatch = True
286 try:
286 try:
287 if phase is not None:
287 if phase is not None:
288 repo.ui.setconfig('phases', 'new-commit', phase)
288 repo.ui.setconfig('phases', 'new-commit', phase)
289 return repo.commit(*args, **kwargs)
289 return repo.commit(*args, **kwargs)
290 finally:
290 finally:
291 repo._committingpatch = False
291 repo._committingpatch = False
292 if phase is not None:
292 if phase is not None:
293 repo.ui.restoreconfig(backup)
293 repo.ui.restoreconfig(backup)
294
294
295 class AbortNoCleanup(error.Abort):
295 class AbortNoCleanup(error.Abort):
296 pass
296 pass
297
297
298 class queue(object):
298 class queue(object):
299 def __init__(self, ui, path, patchdir=None):
299 def __init__(self, ui, path, patchdir=None):
300 self.basepath = path
300 self.basepath = path
301 try:
301 try:
302 fh = open(os.path.join(path, 'patches.queue'))
302 fh = open(os.path.join(path, 'patches.queue'))
303 cur = fh.read().rstrip()
303 cur = fh.read().rstrip()
304 fh.close()
304 fh.close()
305 if not cur:
305 if not cur:
306 curpath = os.path.join(path, 'patches')
306 curpath = os.path.join(path, 'patches')
307 else:
307 else:
308 curpath = os.path.join(path, 'patches-' + cur)
308 curpath = os.path.join(path, 'patches-' + cur)
309 except IOError:
309 except IOError:
310 curpath = os.path.join(path, 'patches')
310 curpath = os.path.join(path, 'patches')
311 self.path = patchdir or curpath
311 self.path = patchdir or curpath
312 self.opener = scmutil.opener(self.path)
312 self.opener = scmutil.opener(self.path)
313 self.ui = ui
313 self.ui = ui
314 self.applieddirty = False
314 self.applieddirty = False
315 self.seriesdirty = False
315 self.seriesdirty = False
316 self.added = []
316 self.added = []
317 self.seriespath = "series"
317 self.seriespath = "series"
318 self.statuspath = "status"
318 self.statuspath = "status"
319 self.guardspath = "guards"
319 self.guardspath = "guards"
320 self.activeguards = None
320 self.activeguards = None
321 self.guardsdirty = False
321 self.guardsdirty = False
322 # Handle mq.git as a bool with extended values
322 # Handle mq.git as a bool with extended values
323 try:
323 try:
324 gitmode = ui.configbool('mq', 'git', None)
324 gitmode = ui.configbool('mq', 'git', None)
325 if gitmode is None:
325 if gitmode is None:
326 raise error.ConfigError
326 raise error.ConfigError
327 self.gitmode = gitmode and 'yes' or 'no'
327 self.gitmode = gitmode and 'yes' or 'no'
328 except error.ConfigError:
328 except error.ConfigError:
329 self.gitmode = ui.config('mq', 'git', 'auto').lower()
329 self.gitmode = ui.config('mq', 'git', 'auto').lower()
330 self.plainmode = ui.configbool('mq', 'plain', False)
330 self.plainmode = ui.configbool('mq', 'plain', False)
331
331
332 @util.propertycache
332 @util.propertycache
333 def applied(self):
333 def applied(self):
334 def parselines(lines):
334 def parselines(lines):
335 for l in lines:
335 for l in lines:
336 entry = l.split(':', 1)
336 entry = l.split(':', 1)
337 if len(entry) > 1:
337 if len(entry) > 1:
338 n, name = entry
338 n, name = entry
339 yield statusentry(bin(n), name)
339 yield statusentry(bin(n), name)
340 elif l.strip():
340 elif l.strip():
341 self.ui.warn(_('malformated mq status line: %s\n') % entry)
341 self.ui.warn(_('malformated mq status line: %s\n') % entry)
342 # else we ignore empty lines
342 # else we ignore empty lines
343 try:
343 try:
344 lines = self.opener.read(self.statuspath).splitlines()
344 lines = self.opener.read(self.statuspath).splitlines()
345 return list(parselines(lines))
345 return list(parselines(lines))
346 except IOError, e:
346 except IOError, e:
347 if e.errno == errno.ENOENT:
347 if e.errno == errno.ENOENT:
348 return []
348 return []
349 raise
349 raise
350
350
351 @util.propertycache
351 @util.propertycache
352 def fullseries(self):
352 def fullseries(self):
353 try:
353 try:
354 return self.opener.read(self.seriespath).splitlines()
354 return self.opener.read(self.seriespath).splitlines()
355 except IOError, e:
355 except IOError, e:
356 if e.errno == errno.ENOENT:
356 if e.errno == errno.ENOENT:
357 return []
357 return []
358 raise
358 raise
359
359
360 @util.propertycache
360 @util.propertycache
361 def series(self):
361 def series(self):
362 self.parseseries()
362 self.parseseries()
363 return self.series
363 return self.series
364
364
365 @util.propertycache
365 @util.propertycache
366 def seriesguards(self):
366 def seriesguards(self):
367 self.parseseries()
367 self.parseseries()
368 return self.seriesguards
368 return self.seriesguards
369
369
370 def invalidate(self):
370 def invalidate(self):
371 for a in 'applied fullseries series seriesguards'.split():
371 for a in 'applied fullseries series seriesguards'.split():
372 if a in self.__dict__:
372 if a in self.__dict__:
373 delattr(self, a)
373 delattr(self, a)
374 self.applieddirty = False
374 self.applieddirty = False
375 self.seriesdirty = False
375 self.seriesdirty = False
376 self.guardsdirty = False
376 self.guardsdirty = False
377 self.activeguards = None
377 self.activeguards = None
378
378
379 def diffopts(self, opts={}, patchfn=None):
379 def diffopts(self, opts={}, patchfn=None):
380 diffopts = patchmod.diffopts(self.ui, opts)
380 diffopts = patchmod.diffopts(self.ui, opts)
381 if self.gitmode == 'auto':
381 if self.gitmode == 'auto':
382 diffopts.upgrade = True
382 diffopts.upgrade = True
383 elif self.gitmode == 'keep':
383 elif self.gitmode == 'keep':
384 pass
384 pass
385 elif self.gitmode in ('yes', 'no'):
385 elif self.gitmode in ('yes', 'no'):
386 diffopts.git = self.gitmode == 'yes'
386 diffopts.git = self.gitmode == 'yes'
387 else:
387 else:
388 raise util.Abort(_('mq.git option can be auto/keep/yes/no'
388 raise util.Abort(_('mq.git option can be auto/keep/yes/no'
389 ' got %s') % self.gitmode)
389 ' got %s') % self.gitmode)
390 if patchfn:
390 if patchfn:
391 diffopts = self.patchopts(diffopts, patchfn)
391 diffopts = self.patchopts(diffopts, patchfn)
392 return diffopts
392 return diffopts
393
393
394 def patchopts(self, diffopts, *patches):
394 def patchopts(self, diffopts, *patches):
395 """Return a copy of input diff options with git set to true if
395 """Return a copy of input diff options with git set to true if
396 referenced patch is a git patch and should be preserved as such.
396 referenced patch is a git patch and should be preserved as such.
397 """
397 """
398 diffopts = diffopts.copy()
398 diffopts = diffopts.copy()
399 if not diffopts.git and self.gitmode == 'keep':
399 if not diffopts.git and self.gitmode == 'keep':
400 for patchfn in patches:
400 for patchfn in patches:
401 patchf = self.opener(patchfn, 'r')
401 patchf = self.opener(patchfn, 'r')
402 # if the patch was a git patch, refresh it as a git patch
402 # if the patch was a git patch, refresh it as a git patch
403 for line in patchf:
403 for line in patchf:
404 if line.startswith('diff --git'):
404 if line.startswith('diff --git'):
405 diffopts.git = True
405 diffopts.git = True
406 break
406 break
407 patchf.close()
407 patchf.close()
408 return diffopts
408 return diffopts
409
409
410 def join(self, *p):
410 def join(self, *p):
411 return os.path.join(self.path, *p)
411 return os.path.join(self.path, *p)
412
412
413 def findseries(self, patch):
413 def findseries(self, patch):
414 def matchpatch(l):
414 def matchpatch(l):
415 l = l.split('#', 1)[0]
415 l = l.split('#', 1)[0]
416 return l.strip() == patch
416 return l.strip() == patch
417 for index, l in enumerate(self.fullseries):
417 for index, l in enumerate(self.fullseries):
418 if matchpatch(l):
418 if matchpatch(l):
419 return index
419 return index
420 return None
420 return None
421
421
422 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
422 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
423
423
424 def parseseries(self):
424 def parseseries(self):
425 self.series = []
425 self.series = []
426 self.seriesguards = []
426 self.seriesguards = []
427 for l in self.fullseries:
427 for l in self.fullseries:
428 h = l.find('#')
428 h = l.find('#')
429 if h == -1:
429 if h == -1:
430 patch = l
430 patch = l
431 comment = ''
431 comment = ''
432 elif h == 0:
432 elif h == 0:
433 continue
433 continue
434 else:
434 else:
435 patch = l[:h]
435 patch = l[:h]
436 comment = l[h:]
436 comment = l[h:]
437 patch = patch.strip()
437 patch = patch.strip()
438 if patch:
438 if patch:
439 if patch in self.series:
439 if patch in self.series:
440 raise util.Abort(_('%s appears more than once in %s') %
440 raise util.Abort(_('%s appears more than once in %s') %
441 (patch, self.join(self.seriespath)))
441 (patch, self.join(self.seriespath)))
442 self.series.append(patch)
442 self.series.append(patch)
443 self.seriesguards.append(self.guard_re.findall(comment))
443 self.seriesguards.append(self.guard_re.findall(comment))
444
444
445 def checkguard(self, guard):
445 def checkguard(self, guard):
446 if not guard:
446 if not guard:
447 return _('guard cannot be an empty string')
447 return _('guard cannot be an empty string')
448 bad_chars = '# \t\r\n\f'
448 bad_chars = '# \t\r\n\f'
449 first = guard[0]
449 first = guard[0]
450 if first in '-+':
450 if first in '-+':
451 return (_('guard %r starts with invalid character: %r') %
451 return (_('guard %r starts with invalid character: %r') %
452 (guard, first))
452 (guard, first))
453 for c in bad_chars:
453 for c in bad_chars:
454 if c in guard:
454 if c in guard:
455 return _('invalid character in guard %r: %r') % (guard, c)
455 return _('invalid character in guard %r: %r') % (guard, c)
456
456
457 def setactive(self, guards):
457 def setactive(self, guards):
458 for guard in guards:
458 for guard in guards:
459 bad = self.checkguard(guard)
459 bad = self.checkguard(guard)
460 if bad:
460 if bad:
461 raise util.Abort(bad)
461 raise util.Abort(bad)
462 guards = sorted(set(guards))
462 guards = sorted(set(guards))
463 self.ui.debug('active guards: %s\n' % ' '.join(guards))
463 self.ui.debug('active guards: %s\n' % ' '.join(guards))
464 self.activeguards = guards
464 self.activeguards = guards
465 self.guardsdirty = True
465 self.guardsdirty = True
466
466
467 def active(self):
467 def active(self):
468 if self.activeguards is None:
468 if self.activeguards is None:
469 self.activeguards = []
469 self.activeguards = []
470 try:
470 try:
471 guards = self.opener.read(self.guardspath).split()
471 guards = self.opener.read(self.guardspath).split()
472 except IOError, err:
472 except IOError, err:
473 if err.errno != errno.ENOENT:
473 if err.errno != errno.ENOENT:
474 raise
474 raise
475 guards = []
475 guards = []
476 for i, guard in enumerate(guards):
476 for i, guard in enumerate(guards):
477 bad = self.checkguard(guard)
477 bad = self.checkguard(guard)
478 if bad:
478 if bad:
479 self.ui.warn('%s:%d: %s\n' %
479 self.ui.warn('%s:%d: %s\n' %
480 (self.join(self.guardspath), i + 1, bad))
480 (self.join(self.guardspath), i + 1, bad))
481 else:
481 else:
482 self.activeguards.append(guard)
482 self.activeguards.append(guard)
483 return self.activeguards
483 return self.activeguards
484
484
485 def setguards(self, idx, guards):
485 def setguards(self, idx, guards):
486 for g in guards:
486 for g in guards:
487 if len(g) < 2:
487 if len(g) < 2:
488 raise util.Abort(_('guard %r too short') % g)
488 raise util.Abort(_('guard %r too short') % g)
489 if g[0] not in '-+':
489 if g[0] not in '-+':
490 raise util.Abort(_('guard %r starts with invalid char') % g)
490 raise util.Abort(_('guard %r starts with invalid char') % g)
491 bad = self.checkguard(g[1:])
491 bad = self.checkguard(g[1:])
492 if bad:
492 if bad:
493 raise util.Abort(bad)
493 raise util.Abort(bad)
494 drop = self.guard_re.sub('', self.fullseries[idx])
494 drop = self.guard_re.sub('', self.fullseries[idx])
495 self.fullseries[idx] = drop + ''.join([' #' + g for g in guards])
495 self.fullseries[idx] = drop + ''.join([' #' + g for g in guards])
496 self.parseseries()
496 self.parseseries()
497 self.seriesdirty = True
497 self.seriesdirty = True
498
498
499 def pushable(self, idx):
499 def pushable(self, idx):
500 if isinstance(idx, str):
500 if isinstance(idx, str):
501 idx = self.series.index(idx)
501 idx = self.series.index(idx)
502 patchguards = self.seriesguards[idx]
502 patchguards = self.seriesguards[idx]
503 if not patchguards:
503 if not patchguards:
504 return True, None
504 return True, None
505 guards = self.active()
505 guards = self.active()
506 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
506 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
507 if exactneg:
507 if exactneg:
508 return False, repr(exactneg[0])
508 return False, repr(exactneg[0])
509 pos = [g for g in patchguards if g[0] == '+']
509 pos = [g for g in patchguards if g[0] == '+']
510 exactpos = [g for g in pos if g[1:] in guards]
510 exactpos = [g for g in pos if g[1:] in guards]
511 if pos:
511 if pos:
512 if exactpos:
512 if exactpos:
513 return True, repr(exactpos[0])
513 return True, repr(exactpos[0])
514 return False, ' '.join(map(repr, pos))
514 return False, ' '.join(map(repr, pos))
515 return True, ''
515 return True, ''
516
516
517 def explainpushable(self, idx, all_patches=False):
517 def explainpushable(self, idx, all_patches=False):
518 write = all_patches and self.ui.write or self.ui.warn
518 write = all_patches and self.ui.write or self.ui.warn
519 if all_patches or self.ui.verbose:
519 if all_patches or self.ui.verbose:
520 if isinstance(idx, str):
520 if isinstance(idx, str):
521 idx = self.series.index(idx)
521 idx = self.series.index(idx)
522 pushable, why = self.pushable(idx)
522 pushable, why = self.pushable(idx)
523 if all_patches and pushable:
523 if all_patches and pushable:
524 if why is None:
524 if why is None:
525 write(_('allowing %s - no guards in effect\n') %
525 write(_('allowing %s - no guards in effect\n') %
526 self.series[idx])
526 self.series[idx])
527 else:
527 else:
528 if not why:
528 if not why:
529 write(_('allowing %s - no matching negative guards\n') %
529 write(_('allowing %s - no matching negative guards\n') %
530 self.series[idx])
530 self.series[idx])
531 else:
531 else:
532 write(_('allowing %s - guarded by %s\n') %
532 write(_('allowing %s - guarded by %s\n') %
533 (self.series[idx], why))
533 (self.series[idx], why))
534 if not pushable:
534 if not pushable:
535 if why:
535 if why:
536 write(_('skipping %s - guarded by %s\n') %
536 write(_('skipping %s - guarded by %s\n') %
537 (self.series[idx], why))
537 (self.series[idx], why))
538 else:
538 else:
539 write(_('skipping %s - no matching guards\n') %
539 write(_('skipping %s - no matching guards\n') %
540 self.series[idx])
540 self.series[idx])
541
541
542 def savedirty(self):
542 def savedirty(self):
543 def writelist(items, path):
543 def writelist(items, path):
544 fp = self.opener(path, 'w')
544 fp = self.opener(path, 'w')
545 for i in items:
545 for i in items:
546 fp.write("%s\n" % i)
546 fp.write("%s\n" % i)
547 fp.close()
547 fp.close()
548 if self.applieddirty:
548 if self.applieddirty:
549 writelist(map(str, self.applied), self.statuspath)
549 writelist(map(str, self.applied), self.statuspath)
550 self.applieddirty = False
550 self.applieddirty = False
551 if self.seriesdirty:
551 if self.seriesdirty:
552 writelist(self.fullseries, self.seriespath)
552 writelist(self.fullseries, self.seriespath)
553 self.seriesdirty = False
553 self.seriesdirty = False
554 if self.guardsdirty:
554 if self.guardsdirty:
555 writelist(self.activeguards, self.guardspath)
555 writelist(self.activeguards, self.guardspath)
556 self.guardsdirty = False
556 self.guardsdirty = False
557 if self.added:
557 if self.added:
558 qrepo = self.qrepo()
558 qrepo = self.qrepo()
559 if qrepo:
559 if qrepo:
560 qrepo[None].add(f for f in self.added if f not in qrepo[None])
560 qrepo[None].add(f for f in self.added if f not in qrepo[None])
561 self.added = []
561 self.added = []
562
562
563 def removeundo(self, repo):
563 def removeundo(self, repo):
564 undo = repo.sjoin('undo')
564 undo = repo.sjoin('undo')
565 if not os.path.exists(undo):
565 if not os.path.exists(undo):
566 return
566 return
567 try:
567 try:
568 os.unlink(undo)
568 os.unlink(undo)
569 except OSError, inst:
569 except OSError, inst:
570 self.ui.warn(_('error removing undo: %s\n') % str(inst))
570 self.ui.warn(_('error removing undo: %s\n') % str(inst))
571
571
572 def backup(self, repo, files, copy=False):
572 def backup(self, repo, files, copy=False):
573 # backup local changes in --force case
573 # backup local changes in --force case
574 for f in sorted(files):
574 for f in sorted(files):
575 absf = repo.wjoin(f)
575 absf = repo.wjoin(f)
576 if os.path.lexists(absf):
576 if os.path.lexists(absf):
577 self.ui.note(_('saving current version of %s as %s\n') %
577 self.ui.note(_('saving current version of %s as %s\n') %
578 (f, f + '.orig'))
578 (f, f + '.orig'))
579 if copy:
579 if copy:
580 util.copyfile(absf, absf + '.orig')
580 util.copyfile(absf, absf + '.orig')
581 else:
581 else:
582 util.rename(absf, absf + '.orig')
582 util.rename(absf, absf + '.orig')
583
583
584 def printdiff(self, repo, diffopts, node1, node2=None, files=None,
584 def printdiff(self, repo, diffopts, node1, node2=None, files=None,
585 fp=None, changes=None, opts={}):
585 fp=None, changes=None, opts={}):
586 stat = opts.get('stat')
586 stat = opts.get('stat')
587 m = scmutil.match(repo[node1], files, opts)
587 m = scmutil.match(repo[node1], files, opts)
588 cmdutil.diffordiffstat(self.ui, repo, diffopts, node1, node2, m,
588 cmdutil.diffordiffstat(self.ui, repo, diffopts, node1, node2, m,
589 changes, stat, fp)
589 changes, stat, fp)
590
590
591 def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
591 def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
592 # first try just applying the patch
592 # first try just applying the patch
593 (err, n) = self.apply(repo, [patch], update_status=False,
593 (err, n) = self.apply(repo, [patch], update_status=False,
594 strict=True, merge=rev)
594 strict=True, merge=rev)
595
595
596 if err == 0:
596 if err == 0:
597 return (err, n)
597 return (err, n)
598
598
599 if n is None:
599 if n is None:
600 raise util.Abort(_("apply failed for patch %s") % patch)
600 raise util.Abort(_("apply failed for patch %s") % patch)
601
601
602 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
602 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
603
603
604 # apply failed, strip away that rev and merge.
604 # apply failed, strip away that rev and merge.
605 hg.clean(repo, head)
605 hg.clean(repo, head)
606 self.strip(repo, [n], update=False, backup='strip')
606 self.strip(repo, [n], update=False, backup='strip')
607
607
608 ctx = repo[rev]
608 ctx = repo[rev]
609 ret = hg.merge(repo, rev)
609 ret = hg.merge(repo, rev)
610 if ret:
610 if ret:
611 raise util.Abort(_("update returned %d") % ret)
611 raise util.Abort(_("update returned %d") % ret)
612 n = newcommit(repo, None, ctx.description(), ctx.user(), force=True)
612 n = newcommit(repo, None, ctx.description(), ctx.user(), force=True)
613 if n is None:
613 if n is None:
614 raise util.Abort(_("repo commit failed"))
614 raise util.Abort(_("repo commit failed"))
615 try:
615 try:
616 ph = patchheader(mergeq.join(patch), self.plainmode)
616 ph = patchheader(mergeq.join(patch), self.plainmode)
617 except Exception:
617 except Exception:
618 raise util.Abort(_("unable to read %s") % patch)
618 raise util.Abort(_("unable to read %s") % patch)
619
619
620 diffopts = self.patchopts(diffopts, patch)
620 diffopts = self.patchopts(diffopts, patch)
621 patchf = self.opener(patch, "w")
621 patchf = self.opener(patch, "w")
622 comments = str(ph)
622 comments = str(ph)
623 if comments:
623 if comments:
624 patchf.write(comments)
624 patchf.write(comments)
625 self.printdiff(repo, diffopts, head, n, fp=patchf)
625 self.printdiff(repo, diffopts, head, n, fp=patchf)
626 patchf.close()
626 patchf.close()
627 self.removeundo(repo)
627 self.removeundo(repo)
628 return (0, n)
628 return (0, n)
629
629
630 def qparents(self, repo, rev=None):
630 def qparents(self, repo, rev=None):
631 if rev is None:
631 if rev is None:
632 (p1, p2) = repo.dirstate.parents()
632 (p1, p2) = repo.dirstate.parents()
633 if p2 == nullid:
633 if p2 == nullid:
634 return p1
634 return p1
635 if not self.applied:
635 if not self.applied:
636 return None
636 return None
637 return self.applied[-1].node
637 return self.applied[-1].node
638 p1, p2 = repo.changelog.parents(rev)
638 p1, p2 = repo.changelog.parents(rev)
639 if p2 != nullid and p2 in [x.node for x in self.applied]:
639 if p2 != nullid and p2 in [x.node for x in self.applied]:
640 return p2
640 return p2
641 return p1
641 return p1
642
642
643 def mergepatch(self, repo, mergeq, series, diffopts):
643 def mergepatch(self, repo, mergeq, series, diffopts):
644 if not self.applied:
644 if not self.applied:
645 # each of the patches merged in will have two parents. This
645 # each of the patches merged in will have two parents. This
646 # can confuse the qrefresh, qdiff, and strip code because it
646 # can confuse the qrefresh, qdiff, and strip code because it
647 # needs to know which parent is actually in the patch queue.
647 # needs to know which parent is actually in the patch queue.
648 # so, we insert a merge marker with only one parent. This way
648 # so, we insert a merge marker with only one parent. This way
649 # the first patch in the queue is never a merge patch
649 # the first patch in the queue is never a merge patch
650 #
650 #
651 pname = ".hg.patches.merge.marker"
651 pname = ".hg.patches.merge.marker"
652 n = newcommit(repo, None, '[mq]: merge marker', force=True)
652 n = newcommit(repo, None, '[mq]: merge marker', force=True)
653 self.removeundo(repo)
653 self.removeundo(repo)
654 self.applied.append(statusentry(n, pname))
654 self.applied.append(statusentry(n, pname))
655 self.applieddirty = True
655 self.applieddirty = True
656
656
657 head = self.qparents(repo)
657 head = self.qparents(repo)
658
658
659 for patch in series:
659 for patch in series:
660 patch = mergeq.lookup(patch, strict=True)
660 patch = mergeq.lookup(patch, strict=True)
661 if not patch:
661 if not patch:
662 self.ui.warn(_("patch %s does not exist\n") % patch)
662 self.ui.warn(_("patch %s does not exist\n") % patch)
663 return (1, None)
663 return (1, None)
664 pushable, reason = self.pushable(patch)
664 pushable, reason = self.pushable(patch)
665 if not pushable:
665 if not pushable:
666 self.explainpushable(patch, all_patches=True)
666 self.explainpushable(patch, all_patches=True)
667 continue
667 continue
668 info = mergeq.isapplied(patch)
668 info = mergeq.isapplied(patch)
669 if not info:
669 if not info:
670 self.ui.warn(_("patch %s is not applied\n") % patch)
670 self.ui.warn(_("patch %s is not applied\n") % patch)
671 return (1, None)
671 return (1, None)
672 rev = info[1]
672 rev = info[1]
673 err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
673 err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
674 if head:
674 if head:
675 self.applied.append(statusentry(head, patch))
675 self.applied.append(statusentry(head, patch))
676 self.applieddirty = True
676 self.applieddirty = True
677 if err:
677 if err:
678 return (err, head)
678 return (err, head)
679 self.savedirty()
679 self.savedirty()
680 return (0, head)
680 return (0, head)
681
681
682 def patch(self, repo, patchfile):
682 def patch(self, repo, patchfile):
683 '''Apply patchfile to the working directory.
683 '''Apply patchfile to the working directory.
684 patchfile: name of patch file'''
684 patchfile: name of patch file'''
685 files = set()
685 files = set()
686 try:
686 try:
687 fuzz = patchmod.patch(self.ui, repo, patchfile, strip=1,
687 fuzz = patchmod.patch(self.ui, repo, patchfile, strip=1,
688 files=files, eolmode=None)
688 files=files, eolmode=None)
689 return (True, list(files), fuzz)
689 return (True, list(files), fuzz)
690 except Exception, inst:
690 except Exception, inst:
691 self.ui.note(str(inst) + '\n')
691 self.ui.note(str(inst) + '\n')
692 if not self.ui.verbose:
692 if not self.ui.verbose:
693 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
693 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
694 self.ui.traceback()
694 self.ui.traceback()
695 return (False, list(files), False)
695 return (False, list(files), False)
696
696
697 def apply(self, repo, series, list=False, update_status=True,
697 def apply(self, repo, series, list=False, update_status=True,
698 strict=False, patchdir=None, merge=None, all_files=None,
698 strict=False, patchdir=None, merge=None, all_files=None,
699 tobackup=None, keepchanges=False):
699 tobackup=None, keepchanges=False):
700 wlock = lock = tr = None
700 wlock = lock = tr = None
701 try:
701 try:
702 wlock = repo.wlock()
702 wlock = repo.wlock()
703 lock = repo.lock()
703 lock = repo.lock()
704 tr = repo.transaction("qpush")
704 tr = repo.transaction("qpush")
705 try:
705 try:
706 ret = self._apply(repo, series, list, update_status,
706 ret = self._apply(repo, series, list, update_status,
707 strict, patchdir, merge, all_files=all_files,
707 strict, patchdir, merge, all_files=all_files,
708 tobackup=tobackup, keepchanges=keepchanges)
708 tobackup=tobackup, keepchanges=keepchanges)
709 tr.close()
709 tr.close()
710 self.savedirty()
710 self.savedirty()
711 return ret
711 return ret
712 except AbortNoCleanup:
712 except AbortNoCleanup:
713 tr.close()
713 tr.close()
714 self.savedirty()
714 self.savedirty()
715 return 2, repo.dirstate.p1()
715 return 2, repo.dirstate.p1()
716 except: # re-raises
716 except: # re-raises
717 try:
717 try:
718 tr.abort()
718 tr.abort()
719 finally:
719 finally:
720 repo.invalidate()
720 repo.invalidate()
721 repo.dirstate.invalidate()
721 repo.dirstate.invalidate()
722 self.invalidate()
722 self.invalidate()
723 raise
723 raise
724 finally:
724 finally:
725 release(tr, lock, wlock)
725 release(tr, lock, wlock)
726 self.removeundo(repo)
726 self.removeundo(repo)
727
727
728 def _apply(self, repo, series, list=False, update_status=True,
728 def _apply(self, repo, series, list=False, update_status=True,
729 strict=False, patchdir=None, merge=None, all_files=None,
729 strict=False, patchdir=None, merge=None, all_files=None,
730 tobackup=None, keepchanges=False):
730 tobackup=None, keepchanges=False):
731 """returns (error, hash)
731 """returns (error, hash)
732
732
733 error = 1 for unable to read, 2 for patch failed, 3 for patch
733 error = 1 for unable to read, 2 for patch failed, 3 for patch
734 fuzz. tobackup is None or a set of files to backup before they
734 fuzz. tobackup is None or a set of files to backup before they
735 are modified by a patch.
735 are modified by a patch.
736 """
736 """
737 # TODO unify with commands.py
737 # TODO unify with commands.py
738 if not patchdir:
738 if not patchdir:
739 patchdir = self.path
739 patchdir = self.path
740 err = 0
740 err = 0
741 n = None
741 n = None
742 for patchname in series:
742 for patchname in series:
743 pushable, reason = self.pushable(patchname)
743 pushable, reason = self.pushable(patchname)
744 if not pushable:
744 if not pushable:
745 self.explainpushable(patchname, all_patches=True)
745 self.explainpushable(patchname, all_patches=True)
746 continue
746 continue
747 self.ui.status(_("applying %s\n") % patchname)
747 self.ui.status(_("applying %s\n") % patchname)
748 pf = os.path.join(patchdir, patchname)
748 pf = os.path.join(patchdir, patchname)
749
749
750 try:
750 try:
751 ph = patchheader(self.join(patchname), self.plainmode)
751 ph = patchheader(self.join(patchname), self.plainmode)
752 except IOError:
752 except IOError:
753 self.ui.warn(_("unable to read %s\n") % patchname)
753 self.ui.warn(_("unable to read %s\n") % patchname)
754 err = 1
754 err = 1
755 break
755 break
756
756
757 message = ph.message
757 message = ph.message
758 if not message:
758 if not message:
759 # The commit message should not be translated
759 # The commit message should not be translated
760 message = "imported patch %s\n" % patchname
760 message = "imported patch %s\n" % patchname
761 else:
761 else:
762 if list:
762 if list:
763 # The commit message should not be translated
763 # The commit message should not be translated
764 message.append("\nimported patch %s" % patchname)
764 message.append("\nimported patch %s" % patchname)
765 message = '\n'.join(message)
765 message = '\n'.join(message)
766
766
767 if ph.haspatch:
767 if ph.haspatch:
768 if tobackup:
768 if tobackup:
769 touched = patchmod.changedfiles(self.ui, repo, pf)
769 touched = patchmod.changedfiles(self.ui, repo, pf)
770 touched = set(touched) & tobackup
770 touched = set(touched) & tobackup
771 if touched and keepchanges:
771 if touched and keepchanges:
772 raise AbortNoCleanup(
772 raise AbortNoCleanup(
773 _("local changes found, refresh first"))
773 _("local changes found, refresh first"))
774 self.backup(repo, touched, copy=True)
774 self.backup(repo, touched, copy=True)
775 tobackup = tobackup - touched
775 tobackup = tobackup - touched
776 (patcherr, files, fuzz) = self.patch(repo, pf)
776 (patcherr, files, fuzz) = self.patch(repo, pf)
777 if all_files is not None:
777 if all_files is not None:
778 all_files.update(files)
778 all_files.update(files)
779 patcherr = not patcherr
779 patcherr = not patcherr
780 else:
780 else:
781 self.ui.warn(_("patch %s is empty\n") % patchname)
781 self.ui.warn(_("patch %s is empty\n") % patchname)
782 patcherr, files, fuzz = 0, [], 0
782 patcherr, files, fuzz = 0, [], 0
783
783
784 if merge and files:
784 if merge and files:
785 # Mark as removed/merged and update dirstate parent info
785 # Mark as removed/merged and update dirstate parent info
786 removed = []
786 removed = []
787 merged = []
787 merged = []
788 for f in files:
788 for f in files:
789 if os.path.lexists(repo.wjoin(f)):
789 if os.path.lexists(repo.wjoin(f)):
790 merged.append(f)
790 merged.append(f)
791 else:
791 else:
792 removed.append(f)
792 removed.append(f)
793 for f in removed:
793 for f in removed:
794 repo.dirstate.remove(f)
794 repo.dirstate.remove(f)
795 for f in merged:
795 for f in merged:
796 repo.dirstate.merge(f)
796 repo.dirstate.merge(f)
797 p1, p2 = repo.dirstate.parents()
797 p1, p2 = repo.dirstate.parents()
798 repo.setparents(p1, merge)
798 repo.setparents(p1, merge)
799
799
800 match = scmutil.matchfiles(repo, files or [])
800 match = scmutil.matchfiles(repo, files or [])
801 oldtip = repo['tip']
801 oldtip = repo['tip']
802 n = newcommit(repo, None, message, ph.user, ph.date, match=match,
802 n = newcommit(repo, None, message, ph.user, ph.date, match=match,
803 force=True)
803 force=True)
804 if repo['tip'] == oldtip:
804 if repo['tip'] == oldtip:
805 raise util.Abort(_("qpush exactly duplicates child changeset"))
805 raise util.Abort(_("qpush exactly duplicates child changeset"))
806 if n is None:
806 if n is None:
807 raise util.Abort(_("repository commit failed"))
807 raise util.Abort(_("repository commit failed"))
808
808
809 if update_status:
809 if update_status:
810 self.applied.append(statusentry(n, patchname))
810 self.applied.append(statusentry(n, patchname))
811
811
812 if patcherr:
812 if patcherr:
813 self.ui.warn(_("patch failed, rejects left in working dir\n"))
813 self.ui.warn(_("patch failed, rejects left in working dir\n"))
814 err = 2
814 err = 2
815 break
815 break
816
816
817 if fuzz and strict:
817 if fuzz and strict:
818 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
818 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
819 err = 3
819 err = 3
820 break
820 break
821 return (err, n)
821 return (err, n)
822
822
823 def _cleanup(self, patches, numrevs, keep=False):
823 def _cleanup(self, patches, numrevs, keep=False):
824 if not keep:
824 if not keep:
825 r = self.qrepo()
825 r = self.qrepo()
826 if r:
826 if r:
827 r[None].forget(patches)
827 r[None].forget(patches)
828 for p in patches:
828 for p in patches:
829 os.unlink(self.join(p))
829 os.unlink(self.join(p))
830
830
831 qfinished = []
831 qfinished = []
832 if numrevs:
832 if numrevs:
833 qfinished = self.applied[:numrevs]
833 qfinished = self.applied[:numrevs]
834 del self.applied[:numrevs]
834 del self.applied[:numrevs]
835 self.applieddirty = True
835 self.applieddirty = True
836
836
837 unknown = []
837 unknown = []
838
838
839 for (i, p) in sorted([(self.findseries(p), p) for p in patches],
839 for (i, p) in sorted([(self.findseries(p), p) for p in patches],
840 reverse=True):
840 reverse=True):
841 if i is not None:
841 if i is not None:
842 del self.fullseries[i]
842 del self.fullseries[i]
843 else:
843 else:
844 unknown.append(p)
844 unknown.append(p)
845
845
846 if unknown:
846 if unknown:
847 if numrevs:
847 if numrevs:
848 rev = dict((entry.name, entry.node) for entry in qfinished)
848 rev = dict((entry.name, entry.node) for entry in qfinished)
849 for p in unknown:
849 for p in unknown:
850 msg = _('revision %s refers to unknown patches: %s\n')
850 msg = _('revision %s refers to unknown patches: %s\n')
851 self.ui.warn(msg % (short(rev[p]), p))
851 self.ui.warn(msg % (short(rev[p]), p))
852 else:
852 else:
853 msg = _('unknown patches: %s\n')
853 msg = _('unknown patches: %s\n')
854 raise util.Abort(''.join(msg % p for p in unknown))
854 raise util.Abort(''.join(msg % p for p in unknown))
855
855
856 self.parseseries()
856 self.parseseries()
857 self.seriesdirty = True
857 self.seriesdirty = True
858 return [entry.node for entry in qfinished]
858 return [entry.node for entry in qfinished]
859
859
860 def _revpatches(self, repo, revs):
860 def _revpatches(self, repo, revs):
861 firstrev = repo[self.applied[0].node].rev()
861 firstrev = repo[self.applied[0].node].rev()
862 patches = []
862 patches = []
863 for i, rev in enumerate(revs):
863 for i, rev in enumerate(revs):
864
864
865 if rev < firstrev:
865 if rev < firstrev:
866 raise util.Abort(_('revision %d is not managed') % rev)
866 raise util.Abort(_('revision %d is not managed') % rev)
867
867
868 ctx = repo[rev]
868 ctx = repo[rev]
869 base = self.applied[i].node
869 base = self.applied[i].node
870 if ctx.node() != base:
870 if ctx.node() != base:
871 msg = _('cannot delete revision %d above applied patches')
871 msg = _('cannot delete revision %d above applied patches')
872 raise util.Abort(msg % rev)
872 raise util.Abort(msg % rev)
873
873
874 patch = self.applied[i].name
874 patch = self.applied[i].name
875 for fmt in ('[mq]: %s', 'imported patch %s'):
875 for fmt in ('[mq]: %s', 'imported patch %s'):
876 if ctx.description() == fmt % patch:
876 if ctx.description() == fmt % patch:
877 msg = _('patch %s finalized without changeset message\n')
877 msg = _('patch %s finalized without changeset message\n')
878 repo.ui.status(msg % patch)
878 repo.ui.status(msg % patch)
879 break
879 break
880
880
881 patches.append(patch)
881 patches.append(patch)
882 return patches
882 return patches
883
883
884 def finish(self, repo, revs):
884 def finish(self, repo, revs):
885 # Manually trigger phase computation to ensure phasedefaults is
885 # Manually trigger phase computation to ensure phasedefaults is
886 # executed before we remove the patches.
886 # executed before we remove the patches.
887 repo._phasecache
887 repo._phasecache
888 patches = self._revpatches(repo, sorted(revs))
888 patches = self._revpatches(repo, sorted(revs))
889 qfinished = self._cleanup(patches, len(patches))
889 qfinished = self._cleanup(patches, len(patches))
890 if qfinished and repo.ui.configbool('mq', 'secret', False):
890 if qfinished and repo.ui.configbool('mq', 'secret', False):
891 # only use this logic when the secret option is added
891 # only use this logic when the secret option is added
892 oldqbase = repo[qfinished[0]]
892 oldqbase = repo[qfinished[0]]
893 tphase = repo.ui.config('phases', 'new-commit', phases.draft)
893 tphase = repo.ui.config('phases', 'new-commit', phases.draft)
894 if oldqbase.phase() > tphase and oldqbase.p1().phase() <= tphase:
894 if oldqbase.phase() > tphase and oldqbase.p1().phase() <= tphase:
895 phases.advanceboundary(repo, tphase, qfinished)
895 phases.advanceboundary(repo, tphase, qfinished)
896
896
897 def delete(self, repo, patches, opts):
897 def delete(self, repo, patches, opts):
898 if not patches and not opts.get('rev'):
898 if not patches and not opts.get('rev'):
899 raise util.Abort(_('qdelete requires at least one revision or '
899 raise util.Abort(_('qdelete requires at least one revision or '
900 'patch name'))
900 'patch name'))
901
901
902 realpatches = []
902 realpatches = []
903 for patch in patches:
903 for patch in patches:
904 patch = self.lookup(patch, strict=True)
904 patch = self.lookup(patch, strict=True)
905 info = self.isapplied(patch)
905 info = self.isapplied(patch)
906 if info:
906 if info:
907 raise util.Abort(_("cannot delete applied patch %s") % patch)
907 raise util.Abort(_("cannot delete applied patch %s") % patch)
908 if patch not in self.series:
908 if patch not in self.series:
909 raise util.Abort(_("patch %s not in series file") % patch)
909 raise util.Abort(_("patch %s not in series file") % patch)
910 if patch not in realpatches:
910 if patch not in realpatches:
911 realpatches.append(patch)
911 realpatches.append(patch)
912
912
913 numrevs = 0
913 numrevs = 0
914 if opts.get('rev'):
914 if opts.get('rev'):
915 if not self.applied:
915 if not self.applied:
916 raise util.Abort(_('no patches applied'))
916 raise util.Abort(_('no patches applied'))
917 revs = scmutil.revrange(repo, opts.get('rev'))
917 revs = scmutil.revrange(repo, opts.get('rev'))
918 if len(revs) > 1 and revs[0] > revs[1]:
918 if len(revs) > 1 and revs[0] > revs[1]:
919 revs.reverse()
919 revs.reverse()
920 revpatches = self._revpatches(repo, revs)
920 revpatches = self._revpatches(repo, revs)
921 realpatches += revpatches
921 realpatches += revpatches
922 numrevs = len(revpatches)
922 numrevs = len(revpatches)
923
923
924 self._cleanup(realpatches, numrevs, opts.get('keep'))
924 self._cleanup(realpatches, numrevs, opts.get('keep'))
925
925
926 def checktoppatch(self, repo):
926 def checktoppatch(self, repo):
927 if self.applied:
927 if self.applied:
928 top = self.applied[-1].node
928 top = self.applied[-1].node
929 patch = self.applied[-1].name
929 patch = self.applied[-1].name
930 pp = repo.dirstate.parents()
930 pp = repo.dirstate.parents()
931 if top not in pp:
931 if top not in pp:
932 raise util.Abort(_("working directory revision is not qtip"))
932 raise util.Abort(_("working directory revision is not qtip"))
933 return top, patch
933 return top, patch
934 return None, None
934 return None, None
935
935
936 def checksubstate(self, repo):
936 def checksubstate(self, repo):
937 '''return list of subrepos at a different revision than substate.
937 '''return list of subrepos at a different revision than substate.
938 Abort if any subrepos have uncommitted changes.'''
938 Abort if any subrepos have uncommitted changes.'''
939 inclsubs = []
939 inclsubs = []
940 wctx = repo[None]
940 wctx = repo[None]
941 for s in wctx.substate:
941 for s in wctx.substate:
942 if wctx.sub(s).dirty(True):
942 if wctx.sub(s).dirty(True):
943 raise util.Abort(
943 raise util.Abort(
944 _("uncommitted changes in subrepository %s") % s)
944 _("uncommitted changes in subrepository %s") % s)
945 elif wctx.sub(s).dirty():
945 elif wctx.sub(s).dirty():
946 inclsubs.append(s)
946 inclsubs.append(s)
947 return inclsubs
947 return inclsubs
948
948
949 def localchangesfound(self, refresh=True):
949 def localchangesfound(self, refresh=True):
950 if refresh:
950 if refresh:
951 raise util.Abort(_("local changes found, refresh first"))
951 raise util.Abort(_("local changes found, refresh first"))
952 else:
952 else:
953 raise util.Abort(_("local changes found"))
953 raise util.Abort(_("local changes found"))
954
954
955 def checklocalchanges(self, repo, force=False, refresh=True):
955 def checklocalchanges(self, repo, force=False, refresh=True):
956 m, a, r, d = repo.status()[:4]
956 m, a, r, d = repo.status()[:4]
957 if (m or a or r or d) and not force:
957 if (m or a or r or d) and not force:
958 self.localchangesfound(refresh)
958 self.localchangesfound(refresh)
959 return m, a, r, d
959 return m, a, r, d
960
960
961 _reserved = ('series', 'status', 'guards', '.', '..')
961 _reserved = ('series', 'status', 'guards', '.', '..')
962 def checkreservedname(self, name):
962 def checkreservedname(self, name):
963 if name in self._reserved:
963 if name in self._reserved:
964 raise util.Abort(_('"%s" cannot be used as the name of a patch')
964 raise util.Abort(_('"%s" cannot be used as the name of a patch')
965 % name)
965 % name)
966 for prefix in ('.hg', '.mq'):
966 for prefix in ('.hg', '.mq'):
967 if name.startswith(prefix):
967 if name.startswith(prefix):
968 raise util.Abort(_('patch name cannot begin with "%s"')
968 raise util.Abort(_('patch name cannot begin with "%s"')
969 % prefix)
969 % prefix)
970 for c in ('#', ':'):
970 for c in ('#', ':'):
971 if c in name:
971 if c in name:
972 raise util.Abort(_('"%s" cannot be used in the name of a patch')
972 raise util.Abort(_('"%s" cannot be used in the name of a patch')
973 % c)
973 % c)
974
974
975 def checkpatchname(self, name, force=False):
975 def checkpatchname(self, name, force=False):
976 self.checkreservedname(name)
976 self.checkreservedname(name)
977 if not force and os.path.exists(self.join(name)):
977 if not force and os.path.exists(self.join(name)):
978 if os.path.isdir(self.join(name)):
978 if os.path.isdir(self.join(name)):
979 raise util.Abort(_('"%s" already exists as a directory')
979 raise util.Abort(_('"%s" already exists as a directory')
980 % name)
980 % name)
981 else:
981 else:
982 raise util.Abort(_('patch "%s" already exists') % name)
982 raise util.Abort(_('patch "%s" already exists') % name)
983
983
984 def checkkeepchanges(self, keepchanges, force):
984 def checkkeepchanges(self, keepchanges, force):
985 if force and keepchanges:
985 if force and keepchanges:
986 raise util.Abort(_('cannot use both --force and --keep-changes'))
986 raise util.Abort(_('cannot use both --force and --keep-changes'))
987
987
988 def new(self, repo, patchfn, *pats, **opts):
988 def new(self, repo, patchfn, *pats, **opts):
989 """options:
989 """options:
990 msg: a string or a no-argument function returning a string
990 msg: a string or a no-argument function returning a string
991 """
991 """
992 msg = opts.get('msg')
992 msg = opts.get('msg')
993 user = opts.get('user')
993 user = opts.get('user')
994 date = opts.get('date')
994 date = opts.get('date')
995 if date:
995 if date:
996 date = util.parsedate(date)
996 date = util.parsedate(date)
997 diffopts = self.diffopts({'git': opts.get('git')})
997 diffopts = self.diffopts({'git': opts.get('git')})
998 if opts.get('checkname', True):
998 if opts.get('checkname', True):
999 self.checkpatchname(patchfn)
999 self.checkpatchname(patchfn)
1000 inclsubs = self.checksubstate(repo)
1000 inclsubs = self.checksubstate(repo)
1001 if inclsubs:
1001 if inclsubs:
1002 inclsubs.append('.hgsubstate')
1002 inclsubs.append('.hgsubstate')
1003 substatestate = repo.dirstate['.hgsubstate']
1003 substatestate = repo.dirstate['.hgsubstate']
1004 if opts.get('include') or opts.get('exclude') or pats:
1004 if opts.get('include') or opts.get('exclude') or pats:
1005 if inclsubs:
1005 if inclsubs:
1006 pats = list(pats or []) + inclsubs
1006 pats = list(pats or []) + inclsubs
1007 match = scmutil.match(repo[None], pats, opts)
1007 match = scmutil.match(repo[None], pats, opts)
1008 # detect missing files in pats
1008 # detect missing files in pats
1009 def badfn(f, msg):
1009 def badfn(f, msg):
1010 if f != '.hgsubstate': # .hgsubstate is auto-created
1010 if f != '.hgsubstate': # .hgsubstate is auto-created
1011 raise util.Abort('%s: %s' % (f, msg))
1011 raise util.Abort('%s: %s' % (f, msg))
1012 match.bad = badfn
1012 match.bad = badfn
1013 changes = repo.status(match=match)
1013 changes = repo.status(match=match)
1014 m, a, r, d = changes[:4]
1014 m, a, r, d = changes[:4]
1015 else:
1015 else:
1016 changes = self.checklocalchanges(repo, force=True)
1016 changes = self.checklocalchanges(repo, force=True)
1017 m, a, r, d = changes
1017 m, a, r, d = changes
1018 match = scmutil.matchfiles(repo, m + a + r + inclsubs)
1018 match = scmutil.matchfiles(repo, m + a + r + inclsubs)
1019 if len(repo[None].parents()) > 1:
1019 if len(repo[None].parents()) > 1:
1020 raise util.Abort(_('cannot manage merge changesets'))
1020 raise util.Abort(_('cannot manage merge changesets'))
1021 commitfiles = m + a + r
1021 commitfiles = m + a + r
1022 self.checktoppatch(repo)
1022 self.checktoppatch(repo)
1023 insert = self.fullseriesend()
1023 insert = self.fullseriesend()
1024 wlock = repo.wlock()
1024 wlock = repo.wlock()
1025 try:
1025 try:
1026 try:
1026 try:
1027 # if patch file write fails, abort early
1027 # if patch file write fails, abort early
1028 p = self.opener(patchfn, "w")
1028 p = self.opener(patchfn, "w")
1029 except IOError, e:
1029 except IOError, e:
1030 raise util.Abort(_('cannot write patch "%s": %s')
1030 raise util.Abort(_('cannot write patch "%s": %s')
1031 % (patchfn, e.strerror))
1031 % (patchfn, e.strerror))
1032 try:
1032 try:
1033 if self.plainmode:
1033 if self.plainmode:
1034 if user:
1034 if user:
1035 p.write("From: " + user + "\n")
1035 p.write("From: " + user + "\n")
1036 if not date:
1036 if not date:
1037 p.write("\n")
1037 p.write("\n")
1038 if date:
1038 if date:
1039 p.write("Date: %d %d\n\n" % date)
1039 p.write("Date: %d %d\n\n" % date)
1040 else:
1040 else:
1041 p.write("# HG changeset patch\n")
1041 p.write("# HG changeset patch\n")
1042 p.write("# Parent "
1042 p.write("# Parent "
1043 + hex(repo[None].p1().node()) + "\n")
1043 + hex(repo[None].p1().node()) + "\n")
1044 if user:
1044 if user:
1045 p.write("# User " + user + "\n")
1045 p.write("# User " + user + "\n")
1046 if date:
1046 if date:
1047 p.write("# Date %s %s\n\n" % date)
1047 p.write("# Date %s %s\n\n" % date)
1048 if util.safehasattr(msg, '__call__'):
1048 if util.safehasattr(msg, '__call__'):
1049 msg = msg()
1049 msg = msg()
1050 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
1050 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
1051 n = newcommit(repo, None, commitmsg, user, date, match=match,
1051 n = newcommit(repo, None, commitmsg, user, date, match=match,
1052 force=True)
1052 force=True)
1053 if n is None:
1053 if n is None:
1054 raise util.Abort(_("repo commit failed"))
1054 raise util.Abort(_("repo commit failed"))
1055 try:
1055 try:
1056 self.fullseries[insert:insert] = [patchfn]
1056 self.fullseries[insert:insert] = [patchfn]
1057 self.applied.append(statusentry(n, patchfn))
1057 self.applied.append(statusentry(n, patchfn))
1058 self.parseseries()
1058 self.parseseries()
1059 self.seriesdirty = True
1059 self.seriesdirty = True
1060 self.applieddirty = True
1060 self.applieddirty = True
1061 if msg:
1061 if msg:
1062 msg = msg + "\n\n"
1062 msg = msg + "\n\n"
1063 p.write(msg)
1063 p.write(msg)
1064 if commitfiles:
1064 if commitfiles:
1065 parent = self.qparents(repo, n)
1065 parent = self.qparents(repo, n)
1066 if inclsubs:
1066 if inclsubs:
1067 if substatestate in 'a?':
1067 if substatestate in 'a?':
1068 changes[1].append('.hgsubstate')
1068 changes[1].append('.hgsubstate')
1069 elif substatestate in 'r':
1069 elif substatestate in 'r':
1070 changes[2].append('.hgsubstate')
1070 changes[2].append('.hgsubstate')
1071 else: # modified
1071 else: # modified
1072 changes[0].append('.hgsubstate')
1072 changes[0].append('.hgsubstate')
1073 chunks = patchmod.diff(repo, node1=parent, node2=n,
1073 chunks = patchmod.diff(repo, node1=parent, node2=n,
1074 changes=changes, opts=diffopts)
1074 changes=changes, opts=diffopts)
1075 for chunk in chunks:
1075 for chunk in chunks:
1076 p.write(chunk)
1076 p.write(chunk)
1077 p.close()
1077 p.close()
1078 r = self.qrepo()
1078 r = self.qrepo()
1079 if r:
1079 if r:
1080 r[None].add([patchfn])
1080 r[None].add([patchfn])
1081 except: # re-raises
1081 except: # re-raises
1082 repo.rollback()
1082 repo.rollback()
1083 raise
1083 raise
1084 except Exception:
1084 except Exception:
1085 patchpath = self.join(patchfn)
1085 patchpath = self.join(patchfn)
1086 try:
1086 try:
1087 os.unlink(patchpath)
1087 os.unlink(patchpath)
1088 except OSError:
1088 except OSError:
1089 self.ui.warn(_('error unlinking %s\n') % patchpath)
1089 self.ui.warn(_('error unlinking %s\n') % patchpath)
1090 raise
1090 raise
1091 self.removeundo(repo)
1091 self.removeundo(repo)
1092 finally:
1092 finally:
1093 release(wlock)
1093 release(wlock)
1094
1094
1095 def strip(self, repo, revs, update=True, backup="all", force=None):
1095 def strip(self, repo, revs, update=True, backup="all", force=None):
1096 wlock = lock = None
1096 wlock = lock = None
1097 try:
1097 try:
1098 wlock = repo.wlock()
1098 wlock = repo.wlock()
1099 lock = repo.lock()
1099 lock = repo.lock()
1100
1100
1101 if update:
1101 if update:
1102 self.checklocalchanges(repo, force=force, refresh=False)
1102 self.checklocalchanges(repo, force=force, refresh=False)
1103 urev = self.qparents(repo, revs[0])
1103 urev = self.qparents(repo, revs[0])
1104 hg.clean(repo, urev)
1104 hg.clean(repo, urev)
1105 repo.dirstate.write()
1105 repo.dirstate.write()
1106
1106
1107 repair.strip(self.ui, repo, revs, backup)
1107 repair.strip(self.ui, repo, revs, backup)
1108 finally:
1108 finally:
1109 release(lock, wlock)
1109 release(lock, wlock)
1110
1110
1111 def isapplied(self, patch):
1111 def isapplied(self, patch):
1112 """returns (index, rev, patch)"""
1112 """returns (index, rev, patch)"""
1113 for i, a in enumerate(self.applied):
1113 for i, a in enumerate(self.applied):
1114 if a.name == patch:
1114 if a.name == patch:
1115 return (i, a.node, a.name)
1115 return (i, a.node, a.name)
1116 return None
1116 return None
1117
1117
1118 # if the exact patch name does not exist, we try a few
1118 # if the exact patch name does not exist, we try a few
1119 # variations. If strict is passed, we try only #1
1119 # variations. If strict is passed, we try only #1
1120 #
1120 #
1121 # 1) a number (as string) to indicate an offset in the series file
1121 # 1) a number (as string) to indicate an offset in the series file
1122 # 2) a unique substring of the patch name was given
1122 # 2) a unique substring of the patch name was given
1123 # 3) patchname[-+]num to indicate an offset in the series file
1123 # 3) patchname[-+]num to indicate an offset in the series file
1124 def lookup(self, patch, strict=False):
1124 def lookup(self, patch, strict=False):
1125 def partialname(s):
1125 def partialname(s):
1126 if s in self.series:
1126 if s in self.series:
1127 return s
1127 return s
1128 matches = [x for x in self.series if s in x]
1128 matches = [x for x in self.series if s in x]
1129 if len(matches) > 1:
1129 if len(matches) > 1:
1130 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
1130 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
1131 for m in matches:
1131 for m in matches:
1132 self.ui.warn(' %s\n' % m)
1132 self.ui.warn(' %s\n' % m)
1133 return None
1133 return None
1134 if matches:
1134 if matches:
1135 return matches[0]
1135 return matches[0]
1136 if self.series and self.applied:
1136 if self.series and self.applied:
1137 if s == 'qtip':
1137 if s == 'qtip':
1138 return self.series[self.seriesend(True)-1]
1138 return self.series[self.seriesend(True)-1]
1139 if s == 'qbase':
1139 if s == 'qbase':
1140 return self.series[0]
1140 return self.series[0]
1141 return None
1141 return None
1142
1142
1143 if patch in self.series:
1143 if patch in self.series:
1144 return patch
1144 return patch
1145
1145
1146 if not os.path.isfile(self.join(patch)):
1146 if not os.path.isfile(self.join(patch)):
1147 try:
1147 try:
1148 sno = int(patch)
1148 sno = int(patch)
1149 except (ValueError, OverflowError):
1149 except (ValueError, OverflowError):
1150 pass
1150 pass
1151 else:
1151 else:
1152 if -len(self.series) <= sno < len(self.series):
1152 if -len(self.series) <= sno < len(self.series):
1153 return self.series[sno]
1153 return self.series[sno]
1154
1154
1155 if not strict:
1155 if not strict:
1156 res = partialname(patch)
1156 res = partialname(patch)
1157 if res:
1157 if res:
1158 return res
1158 return res
1159 minus = patch.rfind('-')
1159 minus = patch.rfind('-')
1160 if minus >= 0:
1160 if minus >= 0:
1161 res = partialname(patch[:minus])
1161 res = partialname(patch[:minus])
1162 if res:
1162 if res:
1163 i = self.series.index(res)
1163 i = self.series.index(res)
1164 try:
1164 try:
1165 off = int(patch[minus + 1:] or 1)
1165 off = int(patch[minus + 1:] or 1)
1166 except (ValueError, OverflowError):
1166 except (ValueError, OverflowError):
1167 pass
1167 pass
1168 else:
1168 else:
1169 if i - off >= 0:
1169 if i - off >= 0:
1170 return self.series[i - off]
1170 return self.series[i - off]
1171 plus = patch.rfind('+')
1171 plus = patch.rfind('+')
1172 if plus >= 0:
1172 if plus >= 0:
1173 res = partialname(patch[:plus])
1173 res = partialname(patch[:plus])
1174 if res:
1174 if res:
1175 i = self.series.index(res)
1175 i = self.series.index(res)
1176 try:
1176 try:
1177 off = int(patch[plus + 1:] or 1)
1177 off = int(patch[plus + 1:] or 1)
1178 except (ValueError, OverflowError):
1178 except (ValueError, OverflowError):
1179 pass
1179 pass
1180 else:
1180 else:
1181 if i + off < len(self.series):
1181 if i + off < len(self.series):
1182 return self.series[i + off]
1182 return self.series[i + off]
1183 raise util.Abort(_("patch %s not in series") % patch)
1183 raise util.Abort(_("patch %s not in series") % patch)
1184
1184
1185 def push(self, repo, patch=None, force=False, list=False, mergeq=None,
1185 def push(self, repo, patch=None, force=False, list=False, mergeq=None,
1186 all=False, move=False, exact=False, nobackup=False,
1186 all=False, move=False, exact=False, nobackup=False,
1187 keepchanges=False):
1187 keepchanges=False):
1188 self.checkkeepchanges(keepchanges, force)
1188 self.checkkeepchanges(keepchanges, force)
1189 diffopts = self.diffopts()
1189 diffopts = self.diffopts()
1190 wlock = repo.wlock()
1190 wlock = repo.wlock()
1191 try:
1191 try:
1192 heads = []
1192 heads = []
1193 for b, ls in repo.branchmap().iteritems():
1193 for b, ls in repo.branchmap().iteritems():
1194 heads += ls
1194 heads += ls
1195 if not heads:
1195 if not heads:
1196 heads = [nullid]
1196 heads = [nullid]
1197 if repo.dirstate.p1() not in heads and not exact:
1197 if repo.dirstate.p1() not in heads and not exact:
1198 self.ui.status(_("(working directory not at a head)\n"))
1198 self.ui.status(_("(working directory not at a head)\n"))
1199
1199
1200 if not self.series:
1200 if not self.series:
1201 self.ui.warn(_('no patches in series\n'))
1201 self.ui.warn(_('no patches in series\n'))
1202 return 0
1202 return 0
1203
1203
1204 # Suppose our series file is: A B C and the current 'top'
1204 # Suppose our series file is: A B C and the current 'top'
1205 # patch is B. qpush C should be performed (moving forward)
1205 # patch is B. qpush C should be performed (moving forward)
1206 # qpush B is a NOP (no change) qpush A is an error (can't
1206 # qpush B is a NOP (no change) qpush A is an error (can't
1207 # go backwards with qpush)
1207 # go backwards with qpush)
1208 if patch:
1208 if patch:
1209 patch = self.lookup(patch)
1209 patch = self.lookup(patch)
1210 info = self.isapplied(patch)
1210 info = self.isapplied(patch)
1211 if info and info[0] >= len(self.applied) - 1:
1211 if info and info[0] >= len(self.applied) - 1:
1212 self.ui.warn(
1212 self.ui.warn(
1213 _('qpush: %s is already at the top\n') % patch)
1213 _('qpush: %s is already at the top\n') % patch)
1214 return 0
1214 return 0
1215
1215
1216 pushable, reason = self.pushable(patch)
1216 pushable, reason = self.pushable(patch)
1217 if pushable:
1217 if pushable:
1218 if self.series.index(patch) < self.seriesend():
1218 if self.series.index(patch) < self.seriesend():
1219 raise util.Abort(
1219 raise util.Abort(
1220 _("cannot push to a previous patch: %s") % patch)
1220 _("cannot push to a previous patch: %s") % patch)
1221 else:
1221 else:
1222 if reason:
1222 if reason:
1223 reason = _('guarded by %s') % reason
1223 reason = _('guarded by %s') % reason
1224 else:
1224 else:
1225 reason = _('no matching guards')
1225 reason = _('no matching guards')
1226 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1226 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1227 return 1
1227 return 1
1228 elif all:
1228 elif all:
1229 patch = self.series[-1]
1229 patch = self.series[-1]
1230 if self.isapplied(patch):
1230 if self.isapplied(patch):
1231 self.ui.warn(_('all patches are currently applied\n'))
1231 self.ui.warn(_('all patches are currently applied\n'))
1232 return 0
1232 return 0
1233
1233
1234 # Following the above example, starting at 'top' of B:
1234 # Following the above example, starting at 'top' of B:
1235 # qpush should be performed (pushes C), but a subsequent
1235 # qpush should be performed (pushes C), but a subsequent
1236 # qpush without an argument is an error (nothing to
1236 # qpush without an argument is an error (nothing to
1237 # apply). This allows a loop of "...while hg qpush..." to
1237 # apply). This allows a loop of "...while hg qpush..." to
1238 # work as it detects an error when done
1238 # work as it detects an error when done
1239 start = self.seriesend()
1239 start = self.seriesend()
1240 if start == len(self.series):
1240 if start == len(self.series):
1241 self.ui.warn(_('patch series already fully applied\n'))
1241 self.ui.warn(_('patch series already fully applied\n'))
1242 return 1
1242 return 1
1243 if not force and not keepchanges:
1243 if not force and not keepchanges:
1244 self.checklocalchanges(repo, refresh=self.applied)
1244 self.checklocalchanges(repo, refresh=self.applied)
1245
1245
1246 if exact:
1246 if exact:
1247 if keepchanges:
1247 if keepchanges:
1248 raise util.Abort(
1248 raise util.Abort(
1249 _("cannot use --exact and --keep-changes together"))
1249 _("cannot use --exact and --keep-changes together"))
1250 if move:
1250 if move:
1251 raise util.Abort(_('cannot use --exact and --move '
1251 raise util.Abort(_('cannot use --exact and --move '
1252 'together'))
1252 'together'))
1253 if self.applied:
1253 if self.applied:
1254 raise util.Abort(_('cannot push --exact with applied '
1254 raise util.Abort(_('cannot push --exact with applied '
1255 'patches'))
1255 'patches'))
1256 root = self.series[start]
1256 root = self.series[start]
1257 target = patchheader(self.join(root), self.plainmode).parent
1257 target = patchheader(self.join(root), self.plainmode).parent
1258 if not target:
1258 if not target:
1259 raise util.Abort(
1259 raise util.Abort(
1260 _("%s does not have a parent recorded") % root)
1260 _("%s does not have a parent recorded") % root)
1261 if not repo[target] == repo['.']:
1261 if not repo[target] == repo['.']:
1262 hg.update(repo, target)
1262 hg.update(repo, target)
1263
1263
1264 if move:
1264 if move:
1265 if not patch:
1265 if not patch:
1266 raise util.Abort(_("please specify the patch to move"))
1266 raise util.Abort(_("please specify the patch to move"))
1267 for fullstart, rpn in enumerate(self.fullseries):
1267 for fullstart, rpn in enumerate(self.fullseries):
1268 # strip markers for patch guards
1268 # strip markers for patch guards
1269 if self.guard_re.split(rpn, 1)[0] == self.series[start]:
1269 if self.guard_re.split(rpn, 1)[0] == self.series[start]:
1270 break
1270 break
1271 for i, rpn in enumerate(self.fullseries[fullstart:]):
1271 for i, rpn in enumerate(self.fullseries[fullstart:]):
1272 # strip markers for patch guards
1272 # strip markers for patch guards
1273 if self.guard_re.split(rpn, 1)[0] == patch:
1273 if self.guard_re.split(rpn, 1)[0] == patch:
1274 break
1274 break
1275 index = fullstart + i
1275 index = fullstart + i
1276 assert index < len(self.fullseries)
1276 assert index < len(self.fullseries)
1277 fullpatch = self.fullseries[index]
1277 fullpatch = self.fullseries[index]
1278 del self.fullseries[index]
1278 del self.fullseries[index]
1279 self.fullseries.insert(fullstart, fullpatch)
1279 self.fullseries.insert(fullstart, fullpatch)
1280 self.parseseries()
1280 self.parseseries()
1281 self.seriesdirty = True
1281 self.seriesdirty = True
1282
1282
1283 self.applieddirty = True
1283 self.applieddirty = True
1284 if start > 0:
1284 if start > 0:
1285 self.checktoppatch(repo)
1285 self.checktoppatch(repo)
1286 if not patch:
1286 if not patch:
1287 patch = self.series[start]
1287 patch = self.series[start]
1288 end = start + 1
1288 end = start + 1
1289 else:
1289 else:
1290 end = self.series.index(patch, start) + 1
1290 end = self.series.index(patch, start) + 1
1291
1291
1292 tobackup = set()
1292 tobackup = set()
1293 if (not nobackup and force) or keepchanges:
1293 if (not nobackup and force) or keepchanges:
1294 m, a, r, d = self.checklocalchanges(repo, force=True)
1294 m, a, r, d = self.checklocalchanges(repo, force=True)
1295 if keepchanges:
1295 if keepchanges:
1296 tobackup.update(m + a + r + d)
1296 tobackup.update(m + a + r + d)
1297 else:
1297 else:
1298 tobackup.update(m + a)
1298 tobackup.update(m + a)
1299
1299
1300 s = self.series[start:end]
1300 s = self.series[start:end]
1301 all_files = set()
1301 all_files = set()
1302 try:
1302 try:
1303 if mergeq:
1303 if mergeq:
1304 ret = self.mergepatch(repo, mergeq, s, diffopts)
1304 ret = self.mergepatch(repo, mergeq, s, diffopts)
1305 else:
1305 else:
1306 ret = self.apply(repo, s, list, all_files=all_files,
1306 ret = self.apply(repo, s, list, all_files=all_files,
1307 tobackup=tobackup, keepchanges=keepchanges)
1307 tobackup=tobackup, keepchanges=keepchanges)
1308 except: # re-raises
1308 except: # re-raises
1309 self.ui.warn(_('cleaning up working directory...'))
1309 self.ui.warn(_('cleaning up working directory...'))
1310 node = repo.dirstate.p1()
1310 node = repo.dirstate.p1()
1311 hg.revert(repo, node, None)
1311 hg.revert(repo, node, None)
1312 # only remove unknown files that we know we touched or
1312 # only remove unknown files that we know we touched or
1313 # created while patching
1313 # created while patching
1314 for f in all_files:
1314 for f in all_files:
1315 if f not in repo.dirstate:
1315 if f not in repo.dirstate:
1316 try:
1316 try:
1317 util.unlinkpath(repo.wjoin(f))
1317 util.unlinkpath(repo.wjoin(f))
1318 except OSError, inst:
1318 except OSError, inst:
1319 if inst.errno != errno.ENOENT:
1319 if inst.errno != errno.ENOENT:
1320 raise
1320 raise
1321 self.ui.warn(_('done\n'))
1321 self.ui.warn(_('done\n'))
1322 raise
1322 raise
1323
1323
1324 if not self.applied:
1324 if not self.applied:
1325 return ret[0]
1325 return ret[0]
1326 top = self.applied[-1].name
1326 top = self.applied[-1].name
1327 if ret[0] and ret[0] > 1:
1327 if ret[0] and ret[0] > 1:
1328 msg = _("errors during apply, please fix and refresh %s\n")
1328 msg = _("errors during apply, please fix and refresh %s\n")
1329 self.ui.write(msg % top)
1329 self.ui.write(msg % top)
1330 else:
1330 else:
1331 self.ui.write(_("now at: %s\n") % top)
1331 self.ui.write(_("now at: %s\n") % top)
1332 return ret[0]
1332 return ret[0]
1333
1333
1334 finally:
1334 finally:
1335 wlock.release()
1335 wlock.release()
1336
1336
1337 def pop(self, repo, patch=None, force=False, update=True, all=False,
1337 def pop(self, repo, patch=None, force=False, update=True, all=False,
1338 nobackup=False, keepchanges=False):
1338 nobackup=False, keepchanges=False):
1339 self.checkkeepchanges(keepchanges, force)
1339 self.checkkeepchanges(keepchanges, force)
1340 wlock = repo.wlock()
1340 wlock = repo.wlock()
1341 try:
1341 try:
1342 if patch:
1342 if patch:
1343 # index, rev, patch
1343 # index, rev, patch
1344 info = self.isapplied(patch)
1344 info = self.isapplied(patch)
1345 if not info:
1345 if not info:
1346 patch = self.lookup(patch)
1346 patch = self.lookup(patch)
1347 info = self.isapplied(patch)
1347 info = self.isapplied(patch)
1348 if not info:
1348 if not info:
1349 raise util.Abort(_("patch %s is not applied") % patch)
1349 raise util.Abort(_("patch %s is not applied") % patch)
1350
1350
1351 if not self.applied:
1351 if not self.applied:
1352 # Allow qpop -a to work repeatedly,
1352 # Allow qpop -a to work repeatedly,
1353 # but not qpop without an argument
1353 # but not qpop without an argument
1354 self.ui.warn(_("no patches applied\n"))
1354 self.ui.warn(_("no patches applied\n"))
1355 return not all
1355 return not all
1356
1356
1357 if all:
1357 if all:
1358 start = 0
1358 start = 0
1359 elif patch:
1359 elif patch:
1360 start = info[0] + 1
1360 start = info[0] + 1
1361 else:
1361 else:
1362 start = len(self.applied) - 1
1362 start = len(self.applied) - 1
1363
1363
1364 if start >= len(self.applied):
1364 if start >= len(self.applied):
1365 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1365 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1366 return
1366 return
1367
1367
1368 if not update:
1368 if not update:
1369 parents = repo.dirstate.parents()
1369 parents = repo.dirstate.parents()
1370 rr = [x.node for x in self.applied]
1370 rr = [x.node for x in self.applied]
1371 for p in parents:
1371 for p in parents:
1372 if p in rr:
1372 if p in rr:
1373 self.ui.warn(_("qpop: forcing dirstate update\n"))
1373 self.ui.warn(_("qpop: forcing dirstate update\n"))
1374 update = True
1374 update = True
1375 else:
1375 else:
1376 parents = [p.node() for p in repo[None].parents()]
1376 parents = [p.node() for p in repo[None].parents()]
1377 needupdate = False
1377 needupdate = False
1378 for entry in self.applied[start:]:
1378 for entry in self.applied[start:]:
1379 if entry.node in parents:
1379 if entry.node in parents:
1380 needupdate = True
1380 needupdate = True
1381 break
1381 break
1382 update = needupdate
1382 update = needupdate
1383
1383
1384 tobackup = set()
1384 tobackup = set()
1385 if update:
1385 if update:
1386 m, a, r, d = self.checklocalchanges(
1386 m, a, r, d = self.checklocalchanges(
1387 repo, force=force or keepchanges)
1387 repo, force=force or keepchanges)
1388 if force:
1388 if force:
1389 if not nobackup:
1389 if not nobackup:
1390 tobackup.update(m + a)
1390 tobackup.update(m + a)
1391 elif keepchanges:
1391 elif keepchanges:
1392 tobackup.update(m + a + r + d)
1392 tobackup.update(m + a + r + d)
1393
1393
1394 self.applieddirty = True
1394 self.applieddirty = True
1395 end = len(self.applied)
1395 end = len(self.applied)
1396 rev = self.applied[start].node
1396 rev = self.applied[start].node
1397 if update:
1397 if update:
1398 top = self.checktoppatch(repo)[0]
1398 top = self.checktoppatch(repo)[0]
1399
1399
1400 try:
1400 try:
1401 heads = repo.changelog.heads(rev)
1401 heads = repo.changelog.heads(rev)
1402 except error.LookupError:
1402 except error.LookupError:
1403 node = short(rev)
1403 node = short(rev)
1404 raise util.Abort(_('trying to pop unknown node %s') % node)
1404 raise util.Abort(_('trying to pop unknown node %s') % node)
1405
1405
1406 if heads != [self.applied[-1].node]:
1406 if heads != [self.applied[-1].node]:
1407 raise util.Abort(_("popping would remove a revision not "
1407 raise util.Abort(_("popping would remove a revision not "
1408 "managed by this patch queue"))
1408 "managed by this patch queue"))
1409 if not repo[self.applied[-1].node].mutable():
1409 if not repo[self.applied[-1].node].mutable():
1410 raise util.Abort(
1410 raise util.Abort(
1411 _("popping would remove an immutable revision"),
1411 _("popping would remove an immutable revision"),
1412 hint=_('see "hg help phases" for details'))
1412 hint=_('see "hg help phases" for details'))
1413
1413
1414 # we know there are no local changes, so we can make a simplified
1414 # we know there are no local changes, so we can make a simplified
1415 # form of hg.update.
1415 # form of hg.update.
1416 if update:
1416 if update:
1417 qp = self.qparents(repo, rev)
1417 qp = self.qparents(repo, rev)
1418 ctx = repo[qp]
1418 ctx = repo[qp]
1419 m, a, r, d = repo.status(qp, top)[:4]
1419 m, a, r, d = repo.status(qp, top)[:4]
1420 if d:
1420 if d:
1421 raise util.Abort(_("deletions found between repo revs"))
1421 raise util.Abort(_("deletions found between repo revs"))
1422
1422
1423 tobackup = set(a + m + r) & tobackup
1423 tobackup = set(a + m + r) & tobackup
1424 if keepchanges and tobackup:
1424 if keepchanges and tobackup:
1425 self.localchangesfound()
1425 self.localchangesfound()
1426 self.backup(repo, tobackup)
1426 self.backup(repo, tobackup)
1427
1427
1428 for f in a:
1428 for f in a:
1429 try:
1429 try:
1430 util.unlinkpath(repo.wjoin(f))
1430 util.unlinkpath(repo.wjoin(f))
1431 except OSError, e:
1431 except OSError, e:
1432 if e.errno != errno.ENOENT:
1432 if e.errno != errno.ENOENT:
1433 raise
1433 raise
1434 repo.dirstate.drop(f)
1434 repo.dirstate.drop(f)
1435 for f in m + r:
1435 for f in m + r:
1436 fctx = ctx[f]
1436 fctx = ctx[f]
1437 repo.wwrite(f, fctx.data(), fctx.flags())
1437 repo.wwrite(f, fctx.data(), fctx.flags())
1438 repo.dirstate.normal(f)
1438 repo.dirstate.normal(f)
1439 repo.setparents(qp, nullid)
1439 repo.setparents(qp, nullid)
1440 for patch in reversed(self.applied[start:end]):
1440 for patch in reversed(self.applied[start:end]):
1441 self.ui.status(_("popping %s\n") % patch.name)
1441 self.ui.status(_("popping %s\n") % patch.name)
1442 del self.applied[start:end]
1442 del self.applied[start:end]
1443 self.strip(repo, [rev], update=False, backup='strip')
1443 self.strip(repo, [rev], update=False, backup='strip')
1444 if self.applied:
1444 if self.applied:
1445 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1445 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1446 else:
1446 else:
1447 self.ui.write(_("patch queue now empty\n"))
1447 self.ui.write(_("patch queue now empty\n"))
1448 finally:
1448 finally:
1449 wlock.release()
1449 wlock.release()
1450
1450
1451 def diff(self, repo, pats, opts):
1451 def diff(self, repo, pats, opts):
1452 top, patch = self.checktoppatch(repo)
1452 top, patch = self.checktoppatch(repo)
1453 if not top:
1453 if not top:
1454 self.ui.write(_("no patches applied\n"))
1454 self.ui.write(_("no patches applied\n"))
1455 return
1455 return
1456 qp = self.qparents(repo, top)
1456 qp = self.qparents(repo, top)
1457 if opts.get('reverse'):
1457 if opts.get('reverse'):
1458 node1, node2 = None, qp
1458 node1, node2 = None, qp
1459 else:
1459 else:
1460 node1, node2 = qp, None
1460 node1, node2 = qp, None
1461 diffopts = self.diffopts(opts, patch)
1461 diffopts = self.diffopts(opts, patch)
1462 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1462 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1463
1463
1464 def refresh(self, repo, pats=None, **opts):
1464 def refresh(self, repo, pats=None, **opts):
1465 if not self.applied:
1465 if not self.applied:
1466 self.ui.write(_("no patches applied\n"))
1466 self.ui.write(_("no patches applied\n"))
1467 return 1
1467 return 1
1468 msg = opts.get('msg', '').rstrip()
1468 msg = opts.get('msg', '').rstrip()
1469 newuser = opts.get('user')
1469 newuser = opts.get('user')
1470 newdate = opts.get('date')
1470 newdate = opts.get('date')
1471 if newdate:
1471 if newdate:
1472 newdate = '%d %d' % util.parsedate(newdate)
1472 newdate = '%d %d' % util.parsedate(newdate)
1473 wlock = repo.wlock()
1473 wlock = repo.wlock()
1474
1474
1475 try:
1475 try:
1476 self.checktoppatch(repo)
1476 self.checktoppatch(repo)
1477 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1477 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1478 if repo.changelog.heads(top) != [top]:
1478 if repo.changelog.heads(top) != [top]:
1479 raise util.Abort(_("cannot refresh a revision with children"))
1479 raise util.Abort(_("cannot refresh a revision with children"))
1480 if not repo[top].mutable():
1480 if not repo[top].mutable():
1481 raise util.Abort(_("cannot refresh immutable revision"),
1481 raise util.Abort(_("cannot refresh immutable revision"),
1482 hint=_('see "hg help phases" for details'))
1482 hint=_('see "hg help phases" for details'))
1483
1483
1484 inclsubs = self.checksubstate(repo)
1484 inclsubs = self.checksubstate(repo)
1485
1485
1486 cparents = repo.changelog.parents(top)
1486 cparents = repo.changelog.parents(top)
1487 patchparent = self.qparents(repo, top)
1487 patchparent = self.qparents(repo, top)
1488 ph = patchheader(self.join(patchfn), self.plainmode)
1488 ph = patchheader(self.join(patchfn), self.plainmode)
1489 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1489 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1490 if msg:
1490 if msg:
1491 ph.setmessage(msg)
1491 ph.setmessage(msg)
1492 if newuser:
1492 if newuser:
1493 ph.setuser(newuser)
1493 ph.setuser(newuser)
1494 if newdate:
1494 if newdate:
1495 ph.setdate(newdate)
1495 ph.setdate(newdate)
1496 ph.setparent(hex(patchparent))
1496 ph.setparent(hex(patchparent))
1497
1497
1498 # only commit new patch when write is complete
1498 # only commit new patch when write is complete
1499 patchf = self.opener(patchfn, 'w', atomictemp=True)
1499 patchf = self.opener(patchfn, 'w', atomictemp=True)
1500
1500
1501 comments = str(ph)
1501 comments = str(ph)
1502 if comments:
1502 if comments:
1503 patchf.write(comments)
1503 patchf.write(comments)
1504
1504
1505 # update the dirstate in place, strip off the qtip commit
1505 # update the dirstate in place, strip off the qtip commit
1506 # and then commit.
1506 # and then commit.
1507 #
1507 #
1508 # this should really read:
1508 # this should really read:
1509 # mm, dd, aa = repo.status(top, patchparent)[:3]
1509 # mm, dd, aa = repo.status(top, patchparent)[:3]
1510 # but we do it backwards to take advantage of manifest/chlog
1510 # but we do it backwards to take advantage of manifest/chlog
1511 # caching against the next repo.status call
1511 # caching against the next repo.status call
1512 mm, aa, dd = repo.status(patchparent, top)[:3]
1512 mm, aa, dd = repo.status(patchparent, top)[:3]
1513 changes = repo.changelog.read(top)
1513 changes = repo.changelog.read(top)
1514 man = repo.manifest.read(changes[0])
1514 man = repo.manifest.read(changes[0])
1515 aaa = aa[:]
1515 aaa = aa[:]
1516 matchfn = scmutil.match(repo[None], pats, opts)
1516 matchfn = scmutil.match(repo[None], pats, opts)
1517 # in short mode, we only diff the files included in the
1517 # in short mode, we only diff the files included in the
1518 # patch already plus specified files
1518 # patch already plus specified files
1519 if opts.get('short'):
1519 if opts.get('short'):
1520 # if amending a patch, we start with existing
1520 # if amending a patch, we start with existing
1521 # files plus specified files - unfiltered
1521 # files plus specified files - unfiltered
1522 match = scmutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1522 match = scmutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1523 # filter with inc/exl options
1523 # filter with inc/exl options
1524 matchfn = scmutil.match(repo[None], opts=opts)
1524 matchfn = scmutil.match(repo[None], opts=opts)
1525 else:
1525 else:
1526 match = scmutil.matchall(repo)
1526 match = scmutil.matchall(repo)
1527 m, a, r, d = repo.status(match=match)[:4]
1527 m, a, r, d = repo.status(match=match)[:4]
1528 mm = set(mm)
1528 mm = set(mm)
1529 aa = set(aa)
1529 aa = set(aa)
1530 dd = set(dd)
1530 dd = set(dd)
1531
1531
1532 # we might end up with files that were added between
1532 # we might end up with files that were added between
1533 # qtip and the dirstate parent, but then changed in the
1533 # qtip and the dirstate parent, but then changed in the
1534 # local dirstate. in this case, we want them to only
1534 # local dirstate. in this case, we want them to only
1535 # show up in the added section
1535 # show up in the added section
1536 for x in m:
1536 for x in m:
1537 if x not in aa:
1537 if x not in aa:
1538 mm.add(x)
1538 mm.add(x)
1539 # we might end up with files added by the local dirstate that
1539 # we might end up with files added by the local dirstate that
1540 # were deleted by the patch. In this case, they should only
1540 # were deleted by the patch. In this case, they should only
1541 # show up in the changed section.
1541 # show up in the changed section.
1542 for x in a:
1542 for x in a:
1543 if x in dd:
1543 if x in dd:
1544 dd.remove(x)
1544 dd.remove(x)
1545 mm.add(x)
1545 mm.add(x)
1546 else:
1546 else:
1547 aa.add(x)
1547 aa.add(x)
1548 # make sure any files deleted in the local dirstate
1548 # make sure any files deleted in the local dirstate
1549 # are not in the add or change column of the patch
1549 # are not in the add or change column of the patch
1550 forget = []
1550 forget = []
1551 for x in d + r:
1551 for x in d + r:
1552 if x in aa:
1552 if x in aa:
1553 aa.remove(x)
1553 aa.remove(x)
1554 forget.append(x)
1554 forget.append(x)
1555 continue
1555 continue
1556 else:
1556 else:
1557 mm.discard(x)
1557 mm.discard(x)
1558 dd.add(x)
1558 dd.add(x)
1559
1559
1560 m = list(mm)
1560 m = list(mm)
1561 r = list(dd)
1561 r = list(dd)
1562 a = list(aa)
1562 a = list(aa)
1563 c = [filter(matchfn, l) for l in (m, a, r)]
1563 c = [filter(matchfn, l) for l in (m, a, r)]
1564 match = scmutil.matchfiles(repo, set(c[0] + c[1] + c[2] + inclsubs))
1564 match = scmutil.matchfiles(repo, set(c[0] + c[1] + c[2] + inclsubs))
1565 chunks = patchmod.diff(repo, patchparent, match=match,
1565 chunks = patchmod.diff(repo, patchparent, match=match,
1566 changes=c, opts=diffopts)
1566 changes=c, opts=diffopts)
1567 for chunk in chunks:
1567 for chunk in chunks:
1568 patchf.write(chunk)
1568 patchf.write(chunk)
1569
1569
1570 try:
1570 try:
1571 if diffopts.git or diffopts.upgrade:
1571 if diffopts.git or diffopts.upgrade:
1572 copies = {}
1572 copies = {}
1573 for dst in a:
1573 for dst in a:
1574 src = repo.dirstate.copied(dst)
1574 src = repo.dirstate.copied(dst)
1575 # during qfold, the source file for copies may
1575 # during qfold, the source file for copies may
1576 # be removed. Treat this as a simple add.
1576 # be removed. Treat this as a simple add.
1577 if src is not None and src in repo.dirstate:
1577 if src is not None and src in repo.dirstate:
1578 copies.setdefault(src, []).append(dst)
1578 copies.setdefault(src, []).append(dst)
1579 repo.dirstate.add(dst)
1579 repo.dirstate.add(dst)
1580 # remember the copies between patchparent and qtip
1580 # remember the copies between patchparent and qtip
1581 for dst in aaa:
1581 for dst in aaa:
1582 f = repo.file(dst)
1582 f = repo.file(dst)
1583 src = f.renamed(man[dst])
1583 src = f.renamed(man[dst])
1584 if src:
1584 if src:
1585 copies.setdefault(src[0], []).extend(
1585 copies.setdefault(src[0], []).extend(
1586 copies.get(dst, []))
1586 copies.get(dst, []))
1587 if dst in a:
1587 if dst in a:
1588 copies[src[0]].append(dst)
1588 copies[src[0]].append(dst)
1589 # we can't copy a file created by the patch itself
1589 # we can't copy a file created by the patch itself
1590 if dst in copies:
1590 if dst in copies:
1591 del copies[dst]
1591 del copies[dst]
1592 for src, dsts in copies.iteritems():
1592 for src, dsts in copies.iteritems():
1593 for dst in dsts:
1593 for dst in dsts:
1594 repo.dirstate.copy(src, dst)
1594 repo.dirstate.copy(src, dst)
1595 else:
1595 else:
1596 for dst in a:
1596 for dst in a:
1597 repo.dirstate.add(dst)
1597 repo.dirstate.add(dst)
1598 # Drop useless copy information
1598 # Drop useless copy information
1599 for f in list(repo.dirstate.copies()):
1599 for f in list(repo.dirstate.copies()):
1600 repo.dirstate.copy(None, f)
1600 repo.dirstate.copy(None, f)
1601 for f in r:
1601 for f in r:
1602 repo.dirstate.remove(f)
1602 repo.dirstate.remove(f)
1603 # if the patch excludes a modified file, mark that
1603 # if the patch excludes a modified file, mark that
1604 # file with mtime=0 so status can see it.
1604 # file with mtime=0 so status can see it.
1605 mm = []
1605 mm = []
1606 for i in xrange(len(m)-1, -1, -1):
1606 for i in xrange(len(m)-1, -1, -1):
1607 if not matchfn(m[i]):
1607 if not matchfn(m[i]):
1608 mm.append(m[i])
1608 mm.append(m[i])
1609 del m[i]
1609 del m[i]
1610 for f in m:
1610 for f in m:
1611 repo.dirstate.normal(f)
1611 repo.dirstate.normal(f)
1612 for f in mm:
1612 for f in mm:
1613 repo.dirstate.normallookup(f)
1613 repo.dirstate.normallookup(f)
1614 for f in forget:
1614 for f in forget:
1615 repo.dirstate.drop(f)
1615 repo.dirstate.drop(f)
1616
1616
1617 if not msg:
1617 if not msg:
1618 if not ph.message:
1618 if not ph.message:
1619 message = "[mq]: %s\n" % patchfn
1619 message = "[mq]: %s\n" % patchfn
1620 else:
1620 else:
1621 message = "\n".join(ph.message)
1621 message = "\n".join(ph.message)
1622 else:
1622 else:
1623 message = msg
1623 message = msg
1624
1624
1625 user = ph.user or changes[1]
1625 user = ph.user or changes[1]
1626
1626
1627 oldphase = repo[top].phase()
1627 oldphase = repo[top].phase()
1628
1628
1629 # assumes strip can roll itself back if interrupted
1629 # assumes strip can roll itself back if interrupted
1630 repo.setparents(*cparents)
1630 repo.setparents(*cparents)
1631 self.applied.pop()
1631 self.applied.pop()
1632 self.applieddirty = True
1632 self.applieddirty = True
1633 self.strip(repo, [top], update=False,
1633 self.strip(repo, [top], update=False,
1634 backup='strip')
1634 backup='strip')
1635 except: # re-raises
1635 except: # re-raises
1636 repo.dirstate.invalidate()
1636 repo.dirstate.invalidate()
1637 raise
1637 raise
1638
1638
1639 try:
1639 try:
1640 # might be nice to attempt to roll back strip after this
1640 # might be nice to attempt to roll back strip after this
1641
1641
1642 # Ensure we create a new changeset in the same phase than
1642 # Ensure we create a new changeset in the same phase than
1643 # the old one.
1643 # the old one.
1644 n = newcommit(repo, oldphase, message, user, ph.date,
1644 n = newcommit(repo, oldphase, message, user, ph.date,
1645 match=match, force=True)
1645 match=match, force=True)
1646 # only write patch after a successful commit
1646 # only write patch after a successful commit
1647 patchf.close()
1647 patchf.close()
1648 self.applied.append(statusentry(n, patchfn))
1648 self.applied.append(statusentry(n, patchfn))
1649 except: # re-raises
1649 except: # re-raises
1650 ctx = repo[cparents[0]]
1650 ctx = repo[cparents[0]]
1651 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1651 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1652 self.savedirty()
1652 self.savedirty()
1653 self.ui.warn(_('refresh interrupted while patch was popped! '
1653 self.ui.warn(_('refresh interrupted while patch was popped! '
1654 '(revert --all, qpush to recover)\n'))
1654 '(revert --all, qpush to recover)\n'))
1655 raise
1655 raise
1656 finally:
1656 finally:
1657 wlock.release()
1657 wlock.release()
1658 self.removeundo(repo)
1658 self.removeundo(repo)
1659
1659
1660 def init(self, repo, create=False):
1660 def init(self, repo, create=False):
1661 if not create and os.path.isdir(self.path):
1661 if not create and os.path.isdir(self.path):
1662 raise util.Abort(_("patch queue directory already exists"))
1662 raise util.Abort(_("patch queue directory already exists"))
1663 try:
1663 try:
1664 os.mkdir(self.path)
1664 os.mkdir(self.path)
1665 except OSError, inst:
1665 except OSError, inst:
1666 if inst.errno != errno.EEXIST or not create:
1666 if inst.errno != errno.EEXIST or not create:
1667 raise
1667 raise
1668 if create:
1668 if create:
1669 return self.qrepo(create=True)
1669 return self.qrepo(create=True)
1670
1670
1671 def unapplied(self, repo, patch=None):
1671 def unapplied(self, repo, patch=None):
1672 if patch and patch not in self.series:
1672 if patch and patch not in self.series:
1673 raise util.Abort(_("patch %s is not in series file") % patch)
1673 raise util.Abort(_("patch %s is not in series file") % patch)
1674 if not patch:
1674 if not patch:
1675 start = self.seriesend()
1675 start = self.seriesend()
1676 else:
1676 else:
1677 start = self.series.index(patch) + 1
1677 start = self.series.index(patch) + 1
1678 unapplied = []
1678 unapplied = []
1679 for i in xrange(start, len(self.series)):
1679 for i in xrange(start, len(self.series)):
1680 pushable, reason = self.pushable(i)
1680 pushable, reason = self.pushable(i)
1681 if pushable:
1681 if pushable:
1682 unapplied.append((i, self.series[i]))
1682 unapplied.append((i, self.series[i]))
1683 self.explainpushable(i)
1683 self.explainpushable(i)
1684 return unapplied
1684 return unapplied
1685
1685
1686 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1686 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1687 summary=False):
1687 summary=False):
1688 def displayname(pfx, patchname, state):
1688 def displayname(pfx, patchname, state):
1689 if pfx:
1689 if pfx:
1690 self.ui.write(pfx)
1690 self.ui.write(pfx)
1691 if summary:
1691 if summary:
1692 ph = patchheader(self.join(patchname), self.plainmode)
1692 ph = patchheader(self.join(patchname), self.plainmode)
1693 msg = ph.message and ph.message[0] or ''
1693 msg = ph.message and ph.message[0] or ''
1694 if self.ui.formatted():
1694 if self.ui.formatted():
1695 width = self.ui.termwidth() - len(pfx) - len(patchname) - 2
1695 width = self.ui.termwidth() - len(pfx) - len(patchname) - 2
1696 if width > 0:
1696 if width > 0:
1697 msg = util.ellipsis(msg, width)
1697 msg = util.ellipsis(msg, width)
1698 else:
1698 else:
1699 msg = ''
1699 msg = ''
1700 self.ui.write(patchname, label='qseries.' + state)
1700 self.ui.write(patchname, label='qseries.' + state)
1701 self.ui.write(': ')
1701 self.ui.write(': ')
1702 self.ui.write(msg, label='qseries.message.' + state)
1702 self.ui.write(msg, label='qseries.message.' + state)
1703 else:
1703 else:
1704 self.ui.write(patchname, label='qseries.' + state)
1704 self.ui.write(patchname, label='qseries.' + state)
1705 self.ui.write('\n')
1705 self.ui.write('\n')
1706
1706
1707 applied = set([p.name for p in self.applied])
1707 applied = set([p.name for p in self.applied])
1708 if length is None:
1708 if length is None:
1709 length = len(self.series) - start
1709 length = len(self.series) - start
1710 if not missing:
1710 if not missing:
1711 if self.ui.verbose:
1711 if self.ui.verbose:
1712 idxwidth = len(str(start + length - 1))
1712 idxwidth = len(str(start + length - 1))
1713 for i in xrange(start, start + length):
1713 for i in xrange(start, start + length):
1714 patch = self.series[i]
1714 patch = self.series[i]
1715 if patch in applied:
1715 if patch in applied:
1716 char, state = 'A', 'applied'
1716 char, state = 'A', 'applied'
1717 elif self.pushable(i)[0]:
1717 elif self.pushable(i)[0]:
1718 char, state = 'U', 'unapplied'
1718 char, state = 'U', 'unapplied'
1719 else:
1719 else:
1720 char, state = 'G', 'guarded'
1720 char, state = 'G', 'guarded'
1721 pfx = ''
1721 pfx = ''
1722 if self.ui.verbose:
1722 if self.ui.verbose:
1723 pfx = '%*d %s ' % (idxwidth, i, char)
1723 pfx = '%*d %s ' % (idxwidth, i, char)
1724 elif status and status != char:
1724 elif status and status != char:
1725 continue
1725 continue
1726 displayname(pfx, patch, state)
1726 displayname(pfx, patch, state)
1727 else:
1727 else:
1728 msng_list = []
1728 msng_list = []
1729 for root, dirs, files in os.walk(self.path):
1729 for root, dirs, files in os.walk(self.path):
1730 d = root[len(self.path) + 1:]
1730 d = root[len(self.path) + 1:]
1731 for f in files:
1731 for f in files:
1732 fl = os.path.join(d, f)
1732 fl = os.path.join(d, f)
1733 if (fl not in self.series and
1733 if (fl not in self.series and
1734 fl not in (self.statuspath, self.seriespath,
1734 fl not in (self.statuspath, self.seriespath,
1735 self.guardspath)
1735 self.guardspath)
1736 and not fl.startswith('.')):
1736 and not fl.startswith('.')):
1737 msng_list.append(fl)
1737 msng_list.append(fl)
1738 for x in sorted(msng_list):
1738 for x in sorted(msng_list):
1739 pfx = self.ui.verbose and ('D ') or ''
1739 pfx = self.ui.verbose and ('D ') or ''
1740 displayname(pfx, x, 'missing')
1740 displayname(pfx, x, 'missing')
1741
1741
1742 def issaveline(self, l):
1742 def issaveline(self, l):
1743 if l.name == '.hg.patches.save.line':
1743 if l.name == '.hg.patches.save.line':
1744 return True
1744 return True
1745
1745
1746 def qrepo(self, create=False):
1746 def qrepo(self, create=False):
1747 ui = self.ui.copy()
1747 ui = self.ui.copy()
1748 ui.setconfig('paths', 'default', '', overlay=False)
1748 ui.setconfig('paths', 'default', '', overlay=False)
1749 ui.setconfig('paths', 'default-push', '', overlay=False)
1749 ui.setconfig('paths', 'default-push', '', overlay=False)
1750 if create or os.path.isdir(self.join(".hg")):
1750 if create or os.path.isdir(self.join(".hg")):
1751 return hg.repository(ui, path=self.path, create=create)
1751 return hg.repository(ui, path=self.path, create=create)
1752
1752
1753 def restore(self, repo, rev, delete=None, qupdate=None):
1753 def restore(self, repo, rev, delete=None, qupdate=None):
1754 desc = repo[rev].description().strip()
1754 desc = repo[rev].description().strip()
1755 lines = desc.splitlines()
1755 lines = desc.splitlines()
1756 i = 0
1756 i = 0
1757 datastart = None
1757 datastart = None
1758 series = []
1758 series = []
1759 applied = []
1759 applied = []
1760 qpp = None
1760 qpp = None
1761 for i, line in enumerate(lines):
1761 for i, line in enumerate(lines):
1762 if line == 'Patch Data:':
1762 if line == 'Patch Data:':
1763 datastart = i + 1
1763 datastart = i + 1
1764 elif line.startswith('Dirstate:'):
1764 elif line.startswith('Dirstate:'):
1765 l = line.rstrip()
1765 l = line.rstrip()
1766 l = l[10:].split(' ')
1766 l = l[10:].split(' ')
1767 qpp = [bin(x) for x in l]
1767 qpp = [bin(x) for x in l]
1768 elif datastart is not None:
1768 elif datastart is not None:
1769 l = line.rstrip()
1769 l = line.rstrip()
1770 n, name = l.split(':', 1)
1770 n, name = l.split(':', 1)
1771 if n:
1771 if n:
1772 applied.append(statusentry(bin(n), name))
1772 applied.append(statusentry(bin(n), name))
1773 else:
1773 else:
1774 series.append(l)
1774 series.append(l)
1775 if datastart is None:
1775 if datastart is None:
1776 self.ui.warn(_("no saved patch data found\n"))
1776 self.ui.warn(_("no saved patch data found\n"))
1777 return 1
1777 return 1
1778 self.ui.warn(_("restoring status: %s\n") % lines[0])
1778 self.ui.warn(_("restoring status: %s\n") % lines[0])
1779 self.fullseries = series
1779 self.fullseries = series
1780 self.applied = applied
1780 self.applied = applied
1781 self.parseseries()
1781 self.parseseries()
1782 self.seriesdirty = True
1782 self.seriesdirty = True
1783 self.applieddirty = True
1783 self.applieddirty = True
1784 heads = repo.changelog.heads()
1784 heads = repo.changelog.heads()
1785 if delete:
1785 if delete:
1786 if rev not in heads:
1786 if rev not in heads:
1787 self.ui.warn(_("save entry has children, leaving it alone\n"))
1787 self.ui.warn(_("save entry has children, leaving it alone\n"))
1788 else:
1788 else:
1789 self.ui.warn(_("removing save entry %s\n") % short(rev))
1789 self.ui.warn(_("removing save entry %s\n") % short(rev))
1790 pp = repo.dirstate.parents()
1790 pp = repo.dirstate.parents()
1791 if rev in pp:
1791 if rev in pp:
1792 update = True
1792 update = True
1793 else:
1793 else:
1794 update = False
1794 update = False
1795 self.strip(repo, [rev], update=update, backup='strip')
1795 self.strip(repo, [rev], update=update, backup='strip')
1796 if qpp:
1796 if qpp:
1797 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1797 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1798 (short(qpp[0]), short(qpp[1])))
1798 (short(qpp[0]), short(qpp[1])))
1799 if qupdate:
1799 if qupdate:
1800 self.ui.status(_("updating queue directory\n"))
1800 self.ui.status(_("updating queue directory\n"))
1801 r = self.qrepo()
1801 r = self.qrepo()
1802 if not r:
1802 if not r:
1803 self.ui.warn(_("unable to load queue repository\n"))
1803 self.ui.warn(_("unable to load queue repository\n"))
1804 return 1
1804 return 1
1805 hg.clean(r, qpp[0])
1805 hg.clean(r, qpp[0])
1806
1806
1807 def save(self, repo, msg=None):
1807 def save(self, repo, msg=None):
1808 if not self.applied:
1808 if not self.applied:
1809 self.ui.warn(_("save: no patches applied, exiting\n"))
1809 self.ui.warn(_("save: no patches applied, exiting\n"))
1810 return 1
1810 return 1
1811 if self.issaveline(self.applied[-1]):
1811 if self.issaveline(self.applied[-1]):
1812 self.ui.warn(_("status is already saved\n"))
1812 self.ui.warn(_("status is already saved\n"))
1813 return 1
1813 return 1
1814
1814
1815 if not msg:
1815 if not msg:
1816 msg = _("hg patches saved state")
1816 msg = _("hg patches saved state")
1817 else:
1817 else:
1818 msg = "hg patches: " + msg.rstrip('\r\n')
1818 msg = "hg patches: " + msg.rstrip('\r\n')
1819 r = self.qrepo()
1819 r = self.qrepo()
1820 if r:
1820 if r:
1821 pp = r.dirstate.parents()
1821 pp = r.dirstate.parents()
1822 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1822 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1823 msg += "\n\nPatch Data:\n"
1823 msg += "\n\nPatch Data:\n"
1824 msg += ''.join('%s\n' % x for x in self.applied)
1824 msg += ''.join('%s\n' % x for x in self.applied)
1825 msg += ''.join(':%s\n' % x for x in self.fullseries)
1825 msg += ''.join(':%s\n' % x for x in self.fullseries)
1826 n = repo.commit(msg, force=True)
1826 n = repo.commit(msg, force=True)
1827 if not n:
1827 if not n:
1828 self.ui.warn(_("repo commit failed\n"))
1828 self.ui.warn(_("repo commit failed\n"))
1829 return 1
1829 return 1
1830 self.applied.append(statusentry(n, '.hg.patches.save.line'))
1830 self.applied.append(statusentry(n, '.hg.patches.save.line'))
1831 self.applieddirty = True
1831 self.applieddirty = True
1832 self.removeundo(repo)
1832 self.removeundo(repo)
1833
1833
1834 def fullseriesend(self):
1834 def fullseriesend(self):
1835 if self.applied:
1835 if self.applied:
1836 p = self.applied[-1].name
1836 p = self.applied[-1].name
1837 end = self.findseries(p)
1837 end = self.findseries(p)
1838 if end is None:
1838 if end is None:
1839 return len(self.fullseries)
1839 return len(self.fullseries)
1840 return end + 1
1840 return end + 1
1841 return 0
1841 return 0
1842
1842
1843 def seriesend(self, all_patches=False):
1843 def seriesend(self, all_patches=False):
1844 """If all_patches is False, return the index of the next pushable patch
1844 """If all_patches is False, return the index of the next pushable patch
1845 in the series, or the series length. If all_patches is True, return the
1845 in the series, or the series length. If all_patches is True, return the
1846 index of the first patch past the last applied one.
1846 index of the first patch past the last applied one.
1847 """
1847 """
1848 end = 0
1848 end = 0
1849 def next(start):
1849 def next(start):
1850 if all_patches or start >= len(self.series):
1850 if all_patches or start >= len(self.series):
1851 return start
1851 return start
1852 for i in xrange(start, len(self.series)):
1852 for i in xrange(start, len(self.series)):
1853 p, reason = self.pushable(i)
1853 p, reason = self.pushable(i)
1854 if p:
1854 if p:
1855 return i
1855 return i
1856 self.explainpushable(i)
1856 self.explainpushable(i)
1857 return len(self.series)
1857 return len(self.series)
1858 if self.applied:
1858 if self.applied:
1859 p = self.applied[-1].name
1859 p = self.applied[-1].name
1860 try:
1860 try:
1861 end = self.series.index(p)
1861 end = self.series.index(p)
1862 except ValueError:
1862 except ValueError:
1863 return 0
1863 return 0
1864 return next(end + 1)
1864 return next(end + 1)
1865 return next(end)
1865 return next(end)
1866
1866
1867 def appliedname(self, index):
1867 def appliedname(self, index):
1868 pname = self.applied[index].name
1868 pname = self.applied[index].name
1869 if not self.ui.verbose:
1869 if not self.ui.verbose:
1870 p = pname
1870 p = pname
1871 else:
1871 else:
1872 p = str(self.series.index(pname)) + " " + pname
1872 p = str(self.series.index(pname)) + " " + pname
1873 return p
1873 return p
1874
1874
1875 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1875 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1876 force=None, git=False):
1876 force=None, git=False):
1877 def checkseries(patchname):
1877 def checkseries(patchname):
1878 if patchname in self.series:
1878 if patchname in self.series:
1879 raise util.Abort(_('patch %s is already in the series file')
1879 raise util.Abort(_('patch %s is already in the series file')
1880 % patchname)
1880 % patchname)
1881
1881
1882 if rev:
1882 if rev:
1883 if files:
1883 if files:
1884 raise util.Abort(_('option "-r" not valid when importing '
1884 raise util.Abort(_('option "-r" not valid when importing '
1885 'files'))
1885 'files'))
1886 rev = scmutil.revrange(repo, rev)
1886 rev = scmutil.revrange(repo, rev)
1887 rev.sort(reverse=True)
1887 rev.sort(reverse=True)
1888 elif not files:
1888 elif not files:
1889 raise util.Abort(_('no files or revisions specified'))
1889 raise util.Abort(_('no files or revisions specified'))
1890 if (len(files) > 1 or len(rev) > 1) and patchname:
1890 if (len(files) > 1 or len(rev) > 1) and patchname:
1891 raise util.Abort(_('option "-n" not valid when importing multiple '
1891 raise util.Abort(_('option "-n" not valid when importing multiple '
1892 'patches'))
1892 'patches'))
1893 imported = []
1893 imported = []
1894 if rev:
1894 if rev:
1895 # If mq patches are applied, we can only import revisions
1895 # If mq patches are applied, we can only import revisions
1896 # that form a linear path to qbase.
1896 # that form a linear path to qbase.
1897 # Otherwise, they should form a linear path to a head.
1897 # Otherwise, they should form a linear path to a head.
1898 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1898 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1899 if len(heads) > 1:
1899 if len(heads) > 1:
1900 raise util.Abort(_('revision %d is the root of more than one '
1900 raise util.Abort(_('revision %d is the root of more than one '
1901 'branch') % rev[-1])
1901 'branch') % rev[-1])
1902 if self.applied:
1902 if self.applied:
1903 base = repo.changelog.node(rev[0])
1903 base = repo.changelog.node(rev[0])
1904 if base in [n.node for n in self.applied]:
1904 if base in [n.node for n in self.applied]:
1905 raise util.Abort(_('revision %d is already managed')
1905 raise util.Abort(_('revision %d is already managed')
1906 % rev[0])
1906 % rev[0])
1907 if heads != [self.applied[-1].node]:
1907 if heads != [self.applied[-1].node]:
1908 raise util.Abort(_('revision %d is not the parent of '
1908 raise util.Abort(_('revision %d is not the parent of '
1909 'the queue') % rev[0])
1909 'the queue') % rev[0])
1910 base = repo.changelog.rev(self.applied[0].node)
1910 base = repo.changelog.rev(self.applied[0].node)
1911 lastparent = repo.changelog.parentrevs(base)[0]
1911 lastparent = repo.changelog.parentrevs(base)[0]
1912 else:
1912 else:
1913 if heads != [repo.changelog.node(rev[0])]:
1913 if heads != [repo.changelog.node(rev[0])]:
1914 raise util.Abort(_('revision %d has unmanaged children')
1914 raise util.Abort(_('revision %d has unmanaged children')
1915 % rev[0])
1915 % rev[0])
1916 lastparent = None
1916 lastparent = None
1917
1917
1918 diffopts = self.diffopts({'git': git})
1918 diffopts = self.diffopts({'git': git})
1919 for r in rev:
1919 for r in rev:
1920 if not repo[r].mutable():
1920 if not repo[r].mutable():
1921 raise util.Abort(_('revision %d is not mutable') % r,
1921 raise util.Abort(_('revision %d is not mutable') % r,
1922 hint=_('see "hg help phases" for details'))
1922 hint=_('see "hg help phases" for details'))
1923 p1, p2 = repo.changelog.parentrevs(r)
1923 p1, p2 = repo.changelog.parentrevs(r)
1924 n = repo.changelog.node(r)
1924 n = repo.changelog.node(r)
1925 if p2 != nullrev:
1925 if p2 != nullrev:
1926 raise util.Abort(_('cannot import merge revision %d') % r)
1926 raise util.Abort(_('cannot import merge revision %d') % r)
1927 if lastparent and lastparent != r:
1927 if lastparent and lastparent != r:
1928 raise util.Abort(_('revision %d is not the parent of %d')
1928 raise util.Abort(_('revision %d is not the parent of %d')
1929 % (r, lastparent))
1929 % (r, lastparent))
1930 lastparent = p1
1930 lastparent = p1
1931
1931
1932 if not patchname:
1932 if not patchname:
1933 patchname = normname('%d.diff' % r)
1933 patchname = normname('%d.diff' % r)
1934 checkseries(patchname)
1934 checkseries(patchname)
1935 self.checkpatchname(patchname, force)
1935 self.checkpatchname(patchname, force)
1936 self.fullseries.insert(0, patchname)
1936 self.fullseries.insert(0, patchname)
1937
1937
1938 patchf = self.opener(patchname, "w")
1938 patchf = self.opener(patchname, "w")
1939 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
1939 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
1940 patchf.close()
1940 patchf.close()
1941
1941
1942 se = statusentry(n, patchname)
1942 se = statusentry(n, patchname)
1943 self.applied.insert(0, se)
1943 self.applied.insert(0, se)
1944
1944
1945 self.added.append(patchname)
1945 self.added.append(patchname)
1946 imported.append(patchname)
1946 imported.append(patchname)
1947 patchname = None
1947 patchname = None
1948 if rev and repo.ui.configbool('mq', 'secret', False):
1948 if rev and repo.ui.configbool('mq', 'secret', False):
1949 # if we added anything with --rev, we must move the secret root
1949 # if we added anything with --rev, we must move the secret root
1950 phases.retractboundary(repo, phases.secret, [n])
1950 phases.retractboundary(repo, phases.secret, [n])
1951 self.parseseries()
1951 self.parseseries()
1952 self.applieddirty = True
1952 self.applieddirty = True
1953 self.seriesdirty = True
1953 self.seriesdirty = True
1954
1954
1955 for i, filename in enumerate(files):
1955 for i, filename in enumerate(files):
1956 if existing:
1956 if existing:
1957 if filename == '-':
1957 if filename == '-':
1958 raise util.Abort(_('-e is incompatible with import from -'))
1958 raise util.Abort(_('-e is incompatible with import from -'))
1959 filename = normname(filename)
1959 filename = normname(filename)
1960 self.checkreservedname(filename)
1960 self.checkreservedname(filename)
1961 originpath = self.join(filename)
1961 originpath = self.join(filename)
1962 if not os.path.isfile(originpath):
1962 if not os.path.isfile(originpath):
1963 raise util.Abort(_("patch %s does not exist") % filename)
1963 raise util.Abort(_("patch %s does not exist") % filename)
1964
1964
1965 if patchname:
1965 if patchname:
1966 self.checkpatchname(patchname, force)
1966 self.checkpatchname(patchname, force)
1967
1967
1968 self.ui.write(_('renaming %s to %s\n')
1968 self.ui.write(_('renaming %s to %s\n')
1969 % (filename, patchname))
1969 % (filename, patchname))
1970 util.rename(originpath, self.join(patchname))
1970 util.rename(originpath, self.join(patchname))
1971 else:
1971 else:
1972 patchname = filename
1972 patchname = filename
1973
1973
1974 else:
1974 else:
1975 if filename == '-' and not patchname:
1975 if filename == '-' and not patchname:
1976 raise util.Abort(_('need --name to import a patch from -'))
1976 raise util.Abort(_('need --name to import a patch from -'))
1977 elif not patchname:
1977 elif not patchname:
1978 patchname = normname(os.path.basename(filename.rstrip('/')))
1978 patchname = normname(os.path.basename(filename.rstrip('/')))
1979 self.checkpatchname(patchname, force)
1979 self.checkpatchname(patchname, force)
1980 try:
1980 try:
1981 if filename == '-':
1981 if filename == '-':
1982 text = self.ui.fin.read()
1982 text = self.ui.fin.read()
1983 else:
1983 else:
1984 fp = url.open(self.ui, filename)
1984 fp = url.open(self.ui, filename)
1985 text = fp.read()
1985 text = fp.read()
1986 fp.close()
1986 fp.close()
1987 except (OSError, IOError):
1987 except (OSError, IOError):
1988 raise util.Abort(_("unable to read file %s") % filename)
1988 raise util.Abort(_("unable to read file %s") % filename)
1989 patchf = self.opener(patchname, "w")
1989 patchf = self.opener(patchname, "w")
1990 patchf.write(text)
1990 patchf.write(text)
1991 patchf.close()
1991 patchf.close()
1992 if not force:
1992 if not force:
1993 checkseries(patchname)
1993 checkseries(patchname)
1994 if patchname not in self.series:
1994 if patchname not in self.series:
1995 index = self.fullseriesend() + i
1995 index = self.fullseriesend() + i
1996 self.fullseries[index:index] = [patchname]
1996 self.fullseries[index:index] = [patchname]
1997 self.parseseries()
1997 self.parseseries()
1998 self.seriesdirty = True
1998 self.seriesdirty = True
1999 self.ui.warn(_("adding %s to series file\n") % patchname)
1999 self.ui.warn(_("adding %s to series file\n") % patchname)
2000 self.added.append(patchname)
2000 self.added.append(patchname)
2001 imported.append(patchname)
2001 imported.append(patchname)
2002 patchname = None
2002 patchname = None
2003
2003
2004 self.removeundo(repo)
2004 self.removeundo(repo)
2005 return imported
2005 return imported
2006
2006
2007 def fixkeepchangesopts(ui, opts):
2007 def fixkeepchangesopts(ui, opts):
2008 if (not ui.configbool('mq', 'keepchanges') or opts.get('force')
2008 if (not ui.configbool('mq', 'keepchanges') or opts.get('force')
2009 or opts.get('exact')):
2009 or opts.get('exact')):
2010 return opts
2010 return opts
2011 opts = dict(opts)
2011 opts = dict(opts)
2012 opts['keep_changes'] = True
2012 opts['keep_changes'] = True
2013 return opts
2013 return opts
2014
2014
2015 @command("qdelete|qremove|qrm",
2015 @command("qdelete|qremove|qrm",
2016 [('k', 'keep', None, _('keep patch file')),
2016 [('k', 'keep', None, _('keep patch file')),
2017 ('r', 'rev', [],
2017 ('r', 'rev', [],
2018 _('stop managing a revision (DEPRECATED)'), _('REV'))],
2018 _('stop managing a revision (DEPRECATED)'), _('REV'))],
2019 _('hg qdelete [-k] [PATCH]...'))
2019 _('hg qdelete [-k] [PATCH]...'))
2020 def delete(ui, repo, *patches, **opts):
2020 def delete(ui, repo, *patches, **opts):
2021 """remove patches from queue
2021 """remove patches from queue
2022
2022
2023 The patches must not be applied, and at least one patch is required. Exact
2023 The patches must not be applied, and at least one patch is required. Exact
2024 patch identifiers must be given. With -k/--keep, the patch files are
2024 patch identifiers must be given. With -k/--keep, the patch files are
2025 preserved in the patch directory.
2025 preserved in the patch directory.
2026
2026
2027 To stop managing a patch and move it into permanent history,
2027 To stop managing a patch and move it into permanent history,
2028 use the :hg:`qfinish` command."""
2028 use the :hg:`qfinish` command."""
2029 q = repo.mq
2029 q = repo.mq
2030 q.delete(repo, patches, opts)
2030 q.delete(repo, patches, opts)
2031 q.savedirty()
2031 q.savedirty()
2032 return 0
2032 return 0
2033
2033
2034 @command("qapplied",
2034 @command("qapplied",
2035 [('1', 'last', None, _('show only the preceding applied patch'))
2035 [('1', 'last', None, _('show only the preceding applied patch'))
2036 ] + seriesopts,
2036 ] + seriesopts,
2037 _('hg qapplied [-1] [-s] [PATCH]'))
2037 _('hg qapplied [-1] [-s] [PATCH]'))
2038 def applied(ui, repo, patch=None, **opts):
2038 def applied(ui, repo, patch=None, **opts):
2039 """print the patches already applied
2039 """print the patches already applied
2040
2040
2041 Returns 0 on success."""
2041 Returns 0 on success."""
2042
2042
2043 q = repo.mq
2043 q = repo.mq
2044
2044
2045 if patch:
2045 if patch:
2046 if patch not in q.series:
2046 if patch not in q.series:
2047 raise util.Abort(_("patch %s is not in series file") % patch)
2047 raise util.Abort(_("patch %s is not in series file") % patch)
2048 end = q.series.index(patch) + 1
2048 end = q.series.index(patch) + 1
2049 else:
2049 else:
2050 end = q.seriesend(True)
2050 end = q.seriesend(True)
2051
2051
2052 if opts.get('last') and not end:
2052 if opts.get('last') and not end:
2053 ui.write(_("no patches applied\n"))
2053 ui.write(_("no patches applied\n"))
2054 return 1
2054 return 1
2055 elif opts.get('last') and end == 1:
2055 elif opts.get('last') and end == 1:
2056 ui.write(_("only one patch applied\n"))
2056 ui.write(_("only one patch applied\n"))
2057 return 1
2057 return 1
2058 elif opts.get('last'):
2058 elif opts.get('last'):
2059 start = end - 2
2059 start = end - 2
2060 end = 1
2060 end = 1
2061 else:
2061 else:
2062 start = 0
2062 start = 0
2063
2063
2064 q.qseries(repo, length=end, start=start, status='A',
2064 q.qseries(repo, length=end, start=start, status='A',
2065 summary=opts.get('summary'))
2065 summary=opts.get('summary'))
2066
2066
2067
2067
2068 @command("qunapplied",
2068 @command("qunapplied",
2069 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
2069 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
2070 _('hg qunapplied [-1] [-s] [PATCH]'))
2070 _('hg qunapplied [-1] [-s] [PATCH]'))
2071 def unapplied(ui, repo, patch=None, **opts):
2071 def unapplied(ui, repo, patch=None, **opts):
2072 """print the patches not yet applied
2072 """print the patches not yet applied
2073
2073
2074 Returns 0 on success."""
2074 Returns 0 on success."""
2075
2075
2076 q = repo.mq
2076 q = repo.mq
2077 if patch:
2077 if patch:
2078 if patch not in q.series:
2078 if patch not in q.series:
2079 raise util.Abort(_("patch %s is not in series file") % patch)
2079 raise util.Abort(_("patch %s is not in series file") % patch)
2080 start = q.series.index(patch) + 1
2080 start = q.series.index(patch) + 1
2081 else:
2081 else:
2082 start = q.seriesend(True)
2082 start = q.seriesend(True)
2083
2083
2084 if start == len(q.series) and opts.get('first'):
2084 if start == len(q.series) and opts.get('first'):
2085 ui.write(_("all patches applied\n"))
2085 ui.write(_("all patches applied\n"))
2086 return 1
2086 return 1
2087
2087
2088 length = opts.get('first') and 1 or None
2088 length = opts.get('first') and 1 or None
2089 q.qseries(repo, start=start, length=length, status='U',
2089 q.qseries(repo, start=start, length=length, status='U',
2090 summary=opts.get('summary'))
2090 summary=opts.get('summary'))
2091
2091
2092 @command("qimport",
2092 @command("qimport",
2093 [('e', 'existing', None, _('import file in patch directory')),
2093 [('e', 'existing', None, _('import file in patch directory')),
2094 ('n', 'name', '',
2094 ('n', 'name', '',
2095 _('name of patch file'), _('NAME')),
2095 _('name of patch file'), _('NAME')),
2096 ('f', 'force', None, _('overwrite existing files')),
2096 ('f', 'force', None, _('overwrite existing files')),
2097 ('r', 'rev', [],
2097 ('r', 'rev', [],
2098 _('place existing revisions under mq control'), _('REV')),
2098 _('place existing revisions under mq control'), _('REV')),
2099 ('g', 'git', None, _('use git extended diff format')),
2099 ('g', 'git', None, _('use git extended diff format')),
2100 ('P', 'push', None, _('qpush after importing'))],
2100 ('P', 'push', None, _('qpush after importing'))],
2101 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... [FILE]...'))
2101 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... [FILE]...'))
2102 def qimport(ui, repo, *filename, **opts):
2102 def qimport(ui, repo, *filename, **opts):
2103 """import a patch or existing changeset
2103 """import a patch or existing changeset
2104
2104
2105 The patch is inserted into the series after the last applied
2105 The patch is inserted into the series after the last applied
2106 patch. If no patches have been applied, qimport prepends the patch
2106 patch. If no patches have been applied, qimport prepends the patch
2107 to the series.
2107 to the series.
2108
2108
2109 The patch will have the same name as its source file unless you
2109 The patch will have the same name as its source file unless you
2110 give it a new one with -n/--name.
2110 give it a new one with -n/--name.
2111
2111
2112 You can register an existing patch inside the patch directory with
2112 You can register an existing patch inside the patch directory with
2113 the -e/--existing flag.
2113 the -e/--existing flag.
2114
2114
2115 With -f/--force, an existing patch of the same name will be
2115 With -f/--force, an existing patch of the same name will be
2116 overwritten.
2116 overwritten.
2117
2117
2118 An existing changeset may be placed under mq control with -r/--rev
2118 An existing changeset may be placed under mq control with -r/--rev
2119 (e.g. qimport --rev tip -n patch will place tip under mq control).
2119 (e.g. qimport --rev tip -n patch will place tip under mq control).
2120 With -g/--git, patches imported with --rev will use the git diff
2120 With -g/--git, patches imported with --rev will use the git diff
2121 format. See the diffs help topic for information on why this is
2121 format. See the diffs help topic for information on why this is
2122 important for preserving rename/copy information and permission
2122 important for preserving rename/copy information and permission
2123 changes. Use :hg:`qfinish` to remove changesets from mq control.
2123 changes. Use :hg:`qfinish` to remove changesets from mq control.
2124
2124
2125 To import a patch from standard input, pass - as the patch file.
2125 To import a patch from standard input, pass - as the patch file.
2126 When importing from standard input, a patch name must be specified
2126 When importing from standard input, a patch name must be specified
2127 using the --name flag.
2127 using the --name flag.
2128
2128
2129 To import an existing patch while renaming it::
2129 To import an existing patch while renaming it::
2130
2130
2131 hg qimport -e existing-patch -n new-name
2131 hg qimport -e existing-patch -n new-name
2132
2132
2133 Returns 0 if import succeeded.
2133 Returns 0 if import succeeded.
2134 """
2134 """
2135 lock = repo.lock() # cause this may move phase
2135 lock = repo.lock() # cause this may move phase
2136 try:
2136 try:
2137 q = repo.mq
2137 q = repo.mq
2138 try:
2138 try:
2139 imported = q.qimport(
2139 imported = q.qimport(
2140 repo, filename, patchname=opts.get('name'),
2140 repo, filename, patchname=opts.get('name'),
2141 existing=opts.get('existing'), force=opts.get('force'),
2141 existing=opts.get('existing'), force=opts.get('force'),
2142 rev=opts.get('rev'), git=opts.get('git'))
2142 rev=opts.get('rev'), git=opts.get('git'))
2143 finally:
2143 finally:
2144 q.savedirty()
2144 q.savedirty()
2145 finally:
2145 finally:
2146 lock.release()
2146 lock.release()
2147
2147
2148 if imported and opts.get('push') and not opts.get('rev'):
2148 if imported and opts.get('push') and not opts.get('rev'):
2149 return q.push(repo, imported[-1])
2149 return q.push(repo, imported[-1])
2150 return 0
2150 return 0
2151
2151
2152 def qinit(ui, repo, create):
2152 def qinit(ui, repo, create):
2153 """initialize a new queue repository
2153 """initialize a new queue repository
2154
2154
2155 This command also creates a series file for ordering patches, and
2155 This command also creates a series file for ordering patches, and
2156 an mq-specific .hgignore file in the queue repository, to exclude
2156 an mq-specific .hgignore file in the queue repository, to exclude
2157 the status and guards files (these contain mostly transient state).
2157 the status and guards files (these contain mostly transient state).
2158
2158
2159 Returns 0 if initialization succeeded."""
2159 Returns 0 if initialization succeeded."""
2160 q = repo.mq
2160 q = repo.mq
2161 r = q.init(repo, create)
2161 r = q.init(repo, create)
2162 q.savedirty()
2162 q.savedirty()
2163 if r:
2163 if r:
2164 if not os.path.exists(r.wjoin('.hgignore')):
2164 if not os.path.exists(r.wjoin('.hgignore')):
2165 fp = r.wopener('.hgignore', 'w')
2165 fp = r.wopener('.hgignore', 'w')
2166 fp.write('^\\.hg\n')
2166 fp.write('^\\.hg\n')
2167 fp.write('^\\.mq\n')
2167 fp.write('^\\.mq\n')
2168 fp.write('syntax: glob\n')
2168 fp.write('syntax: glob\n')
2169 fp.write('status\n')
2169 fp.write('status\n')
2170 fp.write('guards\n')
2170 fp.write('guards\n')
2171 fp.close()
2171 fp.close()
2172 if not os.path.exists(r.wjoin('series')):
2172 if not os.path.exists(r.wjoin('series')):
2173 r.wopener('series', 'w').close()
2173 r.wopener('series', 'w').close()
2174 r[None].add(['.hgignore', 'series'])
2174 r[None].add(['.hgignore', 'series'])
2175 commands.add(ui, r)
2175 commands.add(ui, r)
2176 return 0
2176 return 0
2177
2177
2178 @command("^qinit",
2178 @command("^qinit",
2179 [('c', 'create-repo', None, _('create queue repository'))],
2179 [('c', 'create-repo', None, _('create queue repository'))],
2180 _('hg qinit [-c]'))
2180 _('hg qinit [-c]'))
2181 def init(ui, repo, **opts):
2181 def init(ui, repo, **opts):
2182 """init a new queue repository (DEPRECATED)
2182 """init a new queue repository (DEPRECATED)
2183
2183
2184 The queue repository is unversioned by default. If
2184 The queue repository is unversioned by default. If
2185 -c/--create-repo is specified, qinit will create a separate nested
2185 -c/--create-repo is specified, qinit will create a separate nested
2186 repository for patches (qinit -c may also be run later to convert
2186 repository for patches (qinit -c may also be run later to convert
2187 an unversioned patch repository into a versioned one). You can use
2187 an unversioned patch repository into a versioned one). You can use
2188 qcommit to commit changes to this queue repository.
2188 qcommit to commit changes to this queue repository.
2189
2189
2190 This command is deprecated. Without -c, it's implied by other relevant
2190 This command is deprecated. Without -c, it's implied by other relevant
2191 commands. With -c, use :hg:`init --mq` instead."""
2191 commands. With -c, use :hg:`init --mq` instead."""
2192 return qinit(ui, repo, create=opts.get('create_repo'))
2192 return qinit(ui, repo, create=opts.get('create_repo'))
2193
2193
2194 @command("qclone",
2194 @command("qclone",
2195 [('', 'pull', None, _('use pull protocol to copy metadata')),
2195 [('', 'pull', None, _('use pull protocol to copy metadata')),
2196 ('U', 'noupdate', None,
2196 ('U', 'noupdate', None,
2197 _('do not update the new working directories')),
2197 _('do not update the new working directories')),
2198 ('', 'uncompressed', None,
2198 ('', 'uncompressed', None,
2199 _('use uncompressed transfer (fast over LAN)')),
2199 _('use uncompressed transfer (fast over LAN)')),
2200 ('p', 'patches', '',
2200 ('p', 'patches', '',
2201 _('location of source patch repository'), _('REPO')),
2201 _('location of source patch repository'), _('REPO')),
2202 ] + commands.remoteopts,
2202 ] + commands.remoteopts,
2203 _('hg qclone [OPTION]... SOURCE [DEST]'))
2203 _('hg qclone [OPTION]... SOURCE [DEST]'))
2204 def clone(ui, source, dest=None, **opts):
2204 def clone(ui, source, dest=None, **opts):
2205 '''clone main and patch repository at same time
2205 '''clone main and patch repository at same time
2206
2206
2207 If source is local, destination will have no patches applied. If
2207 If source is local, destination will have no patches applied. If
2208 source is remote, this command can not check if patches are
2208 source is remote, this command can not check if patches are
2209 applied in source, so cannot guarantee that patches are not
2209 applied in source, so cannot guarantee that patches are not
2210 applied in destination. If you clone remote repository, be sure
2210 applied in destination. If you clone remote repository, be sure
2211 before that it has no patches applied.
2211 before that it has no patches applied.
2212
2212
2213 Source patch repository is looked for in <src>/.hg/patches by
2213 Source patch repository is looked for in <src>/.hg/patches by
2214 default. Use -p <url> to change.
2214 default. Use -p <url> to change.
2215
2215
2216 The patch directory must be a nested Mercurial repository, as
2216 The patch directory must be a nested Mercurial repository, as
2217 would be created by :hg:`init --mq`.
2217 would be created by :hg:`init --mq`.
2218
2218
2219 Return 0 on success.
2219 Return 0 on success.
2220 '''
2220 '''
2221 def patchdir(repo):
2221 def patchdir(repo):
2222 """compute a patch repo url from a repo object"""
2222 """compute a patch repo url from a repo object"""
2223 url = repo.url()
2223 url = repo.url()
2224 if url.endswith('/'):
2224 if url.endswith('/'):
2225 url = url[:-1]
2225 url = url[:-1]
2226 return url + '/.hg/patches'
2226 return url + '/.hg/patches'
2227
2227
2228 # main repo (destination and sources)
2228 # main repo (destination and sources)
2229 if dest is None:
2229 if dest is None:
2230 dest = hg.defaultdest(source)
2230 dest = hg.defaultdest(source)
2231 sr = hg.repository(hg.remoteui(ui, opts), ui.expandpath(source))
2231 sr = hg.repository(hg.remoteui(ui, opts), ui.expandpath(source))
2232
2232
2233 # patches repo (source only)
2233 # patches repo (source only)
2234 if opts.get('patches'):
2234 if opts.get('patches'):
2235 patchespath = ui.expandpath(opts.get('patches'))
2235 patchespath = ui.expandpath(opts.get('patches'))
2236 else:
2236 else:
2237 patchespath = patchdir(sr)
2237 patchespath = patchdir(sr)
2238 try:
2238 try:
2239 hg.repository(ui, patchespath)
2239 hg.repository(ui, patchespath)
2240 except error.RepoError:
2240 except error.RepoError:
2241 raise util.Abort(_('versioned patch repository not found'
2241 raise util.Abort(_('versioned patch repository not found'
2242 ' (see init --mq)'))
2242 ' (see init --mq)'))
2243 qbase, destrev = None, None
2243 qbase, destrev = None, None
2244 if sr.local():
2244 if sr.local():
2245 if sr.mq.applied and sr[qbase].phase() != phases.secret:
2245 if sr.mq.applied and sr[qbase].phase() != phases.secret:
2246 qbase = sr.mq.applied[0].node
2246 qbase = sr.mq.applied[0].node
2247 if not hg.islocal(dest):
2247 if not hg.islocal(dest):
2248 heads = set(sr.heads())
2248 heads = set(sr.heads())
2249 destrev = list(heads.difference(sr.heads(qbase)))
2249 destrev = list(heads.difference(sr.heads(qbase)))
2250 destrev.append(sr.changelog.parents(qbase)[0])
2250 destrev.append(sr.changelog.parents(qbase)[0])
2251 elif sr.capable('lookup'):
2251 elif sr.capable('lookup'):
2252 try:
2252 try:
2253 qbase = sr.lookup('qbase')
2253 qbase = sr.lookup('qbase')
2254 except error.RepoError:
2254 except error.RepoError:
2255 pass
2255 pass
2256
2256
2257 ui.note(_('cloning main repository\n'))
2257 ui.note(_('cloning main repository\n'))
2258 sr, dr = hg.clone(ui, opts, sr.url(), dest,
2258 sr, dr = hg.clone(ui, opts, sr.url(), dest,
2259 pull=opts.get('pull'),
2259 pull=opts.get('pull'),
2260 rev=destrev,
2260 rev=destrev,
2261 update=False,
2261 update=False,
2262 stream=opts.get('uncompressed'))
2262 stream=opts.get('uncompressed'))
2263
2263
2264 ui.note(_('cloning patch repository\n'))
2264 ui.note(_('cloning patch repository\n'))
2265 hg.clone(ui, opts, opts.get('patches') or patchdir(sr), patchdir(dr),
2265 hg.clone(ui, opts, opts.get('patches') or patchdir(sr), patchdir(dr),
2266 pull=opts.get('pull'), update=not opts.get('noupdate'),
2266 pull=opts.get('pull'), update=not opts.get('noupdate'),
2267 stream=opts.get('uncompressed'))
2267 stream=opts.get('uncompressed'))
2268
2268
2269 if dr.local():
2269 if dr.local():
2270 if qbase:
2270 if qbase:
2271 ui.note(_('stripping applied patches from destination '
2271 ui.note(_('stripping applied patches from destination '
2272 'repository\n'))
2272 'repository\n'))
2273 dr.mq.strip(dr, [qbase], update=False, backup=None)
2273 dr.mq.strip(dr, [qbase], update=False, backup=None)
2274 if not opts.get('noupdate'):
2274 if not opts.get('noupdate'):
2275 ui.note(_('updating destination repository\n'))
2275 ui.note(_('updating destination repository\n'))
2276 hg.update(dr, dr.changelog.tip())
2276 hg.update(dr, dr.changelog.tip())
2277
2277
2278 @command("qcommit|qci",
2278 @command("qcommit|qci",
2279 commands.table["^commit|ci"][1],
2279 commands.table["^commit|ci"][1],
2280 _('hg qcommit [OPTION]... [FILE]...'))
2280 _('hg qcommit [OPTION]... [FILE]...'))
2281 def commit(ui, repo, *pats, **opts):
2281 def commit(ui, repo, *pats, **opts):
2282 """commit changes in the queue repository (DEPRECATED)
2282 """commit changes in the queue repository (DEPRECATED)
2283
2283
2284 This command is deprecated; use :hg:`commit --mq` instead."""
2284 This command is deprecated; use :hg:`commit --mq` instead."""
2285 q = repo.mq
2285 q = repo.mq
2286 r = q.qrepo()
2286 r = q.qrepo()
2287 if not r:
2287 if not r:
2288 raise util.Abort('no queue repository')
2288 raise util.Abort('no queue repository')
2289 commands.commit(r.ui, r, *pats, **opts)
2289 commands.commit(r.ui, r, *pats, **opts)
2290
2290
2291 @command("qseries",
2291 @command("qseries",
2292 [('m', 'missing', None, _('print patches not in series')),
2292 [('m', 'missing', None, _('print patches not in series')),
2293 ] + seriesopts,
2293 ] + seriesopts,
2294 _('hg qseries [-ms]'))
2294 _('hg qseries [-ms]'))
2295 def series(ui, repo, **opts):
2295 def series(ui, repo, **opts):
2296 """print the entire series file
2296 """print the entire series file
2297
2297
2298 Returns 0 on success."""
2298 Returns 0 on success."""
2299 repo.mq.qseries(repo, missing=opts.get('missing'),
2299 repo.mq.qseries(repo, missing=opts.get('missing'),
2300 summary=opts.get('summary'))
2300 summary=opts.get('summary'))
2301 return 0
2301 return 0
2302
2302
2303 @command("qtop", seriesopts, _('hg qtop [-s]'))
2303 @command("qtop", seriesopts, _('hg qtop [-s]'))
2304 def top(ui, repo, **opts):
2304 def top(ui, repo, **opts):
2305 """print the name of the current patch
2305 """print the name of the current patch
2306
2306
2307 Returns 0 on success."""
2307 Returns 0 on success."""
2308 q = repo.mq
2308 q = repo.mq
2309 t = q.applied and q.seriesend(True) or 0
2309 t = q.applied and q.seriesend(True) or 0
2310 if t:
2310 if t:
2311 q.qseries(repo, start=t - 1, length=1, status='A',
2311 q.qseries(repo, start=t - 1, length=1, status='A',
2312 summary=opts.get('summary'))
2312 summary=opts.get('summary'))
2313 else:
2313 else:
2314 ui.write(_("no patches applied\n"))
2314 ui.write(_("no patches applied\n"))
2315 return 1
2315 return 1
2316
2316
2317 @command("qnext", seriesopts, _('hg qnext [-s]'))
2317 @command("qnext", seriesopts, _('hg qnext [-s]'))
2318 def next(ui, repo, **opts):
2318 def next(ui, repo, **opts):
2319 """print the name of the next pushable patch
2319 """print the name of the next pushable patch
2320
2320
2321 Returns 0 on success."""
2321 Returns 0 on success."""
2322 q = repo.mq
2322 q = repo.mq
2323 end = q.seriesend()
2323 end = q.seriesend()
2324 if end == len(q.series):
2324 if end == len(q.series):
2325 ui.write(_("all patches applied\n"))
2325 ui.write(_("all patches applied\n"))
2326 return 1
2326 return 1
2327 q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
2327 q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
2328
2328
2329 @command("qprev", seriesopts, _('hg qprev [-s]'))
2329 @command("qprev", seriesopts, _('hg qprev [-s]'))
2330 def prev(ui, repo, **opts):
2330 def prev(ui, repo, **opts):
2331 """print the name of the preceding applied patch
2331 """print the name of the preceding applied patch
2332
2332
2333 Returns 0 on success."""
2333 Returns 0 on success."""
2334 q = repo.mq
2334 q = repo.mq
2335 l = len(q.applied)
2335 l = len(q.applied)
2336 if l == 1:
2336 if l == 1:
2337 ui.write(_("only one patch applied\n"))
2337 ui.write(_("only one patch applied\n"))
2338 return 1
2338 return 1
2339 if not l:
2339 if not l:
2340 ui.write(_("no patches applied\n"))
2340 ui.write(_("no patches applied\n"))
2341 return 1
2341 return 1
2342 idx = q.series.index(q.applied[-2].name)
2342 idx = q.series.index(q.applied[-2].name)
2343 q.qseries(repo, start=idx, length=1, status='A',
2343 q.qseries(repo, start=idx, length=1, status='A',
2344 summary=opts.get('summary'))
2344 summary=opts.get('summary'))
2345
2345
2346 def setupheaderopts(ui, opts):
2346 def setupheaderopts(ui, opts):
2347 if not opts.get('user') and opts.get('currentuser'):
2347 if not opts.get('user') and opts.get('currentuser'):
2348 opts['user'] = ui.username()
2348 opts['user'] = ui.username()
2349 if not opts.get('date') and opts.get('currentdate'):
2349 if not opts.get('date') and opts.get('currentdate'):
2350 opts['date'] = "%d %d" % util.makedate()
2350 opts['date'] = "%d %d" % util.makedate()
2351
2351
2352 @command("^qnew",
2352 @command("^qnew",
2353 [('e', 'edit', None, _('edit commit message')),
2353 [('e', 'edit', None, _('edit commit message')),
2354 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
2354 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
2355 ('g', 'git', None, _('use git extended diff format')),
2355 ('g', 'git', None, _('use git extended diff format')),
2356 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2356 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2357 ('u', 'user', '',
2357 ('u', 'user', '',
2358 _('add "From: <USER>" to patch'), _('USER')),
2358 _('add "From: <USER>" to patch'), _('USER')),
2359 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2359 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2360 ('d', 'date', '',
2360 ('d', 'date', '',
2361 _('add "Date: <DATE>" to patch'), _('DATE'))
2361 _('add "Date: <DATE>" to patch'), _('DATE'))
2362 ] + commands.walkopts + commands.commitopts,
2362 ] + commands.walkopts + commands.commitopts,
2363 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...'))
2363 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...'))
2364 def new(ui, repo, patch, *args, **opts):
2364 def new(ui, repo, patch, *args, **opts):
2365 """create a new patch
2365 """create a new patch
2366
2366
2367 qnew creates a new patch on top of the currently-applied patch (if
2367 qnew creates a new patch on top of the currently-applied patch (if
2368 any). The patch will be initialized with any outstanding changes
2368 any). The patch will be initialized with any outstanding changes
2369 in the working directory. You may also use -I/--include,
2369 in the working directory. You may also use -I/--include,
2370 -X/--exclude, and/or a list of files after the patch name to add
2370 -X/--exclude, and/or a list of files after the patch name to add
2371 only changes to matching files to the new patch, leaving the rest
2371 only changes to matching files to the new patch, leaving the rest
2372 as uncommitted modifications.
2372 as uncommitted modifications.
2373
2373
2374 -u/--user and -d/--date can be used to set the (given) user and
2374 -u/--user and -d/--date can be used to set the (given) user and
2375 date, respectively. -U/--currentuser and -D/--currentdate set user
2375 date, respectively. -U/--currentuser and -D/--currentdate set user
2376 to current user and date to current date.
2376 to current user and date to current date.
2377
2377
2378 -e/--edit, -m/--message or -l/--logfile set the patch header as
2378 -e/--edit, -m/--message or -l/--logfile set the patch header as
2379 well as the commit message. If none is specified, the header is
2379 well as the commit message. If none is specified, the header is
2380 empty and the commit message is '[mq]: PATCH'.
2380 empty and the commit message is '[mq]: PATCH'.
2381
2381
2382 Use the -g/--git option to keep the patch in the git extended diff
2382 Use the -g/--git option to keep the patch in the git extended diff
2383 format. Read the diffs help topic for more information on why this
2383 format. Read the diffs help topic for more information on why this
2384 is important for preserving permission changes and copy/rename
2384 is important for preserving permission changes and copy/rename
2385 information.
2385 information.
2386
2386
2387 Returns 0 on successful creation of a new patch.
2387 Returns 0 on successful creation of a new patch.
2388 """
2388 """
2389 msg = cmdutil.logmessage(ui, opts)
2389 msg = cmdutil.logmessage(ui, opts)
2390 def getmsg():
2390 def getmsg():
2391 return ui.edit(msg, opts.get('user') or ui.username())
2391 return ui.edit(msg, opts.get('user') or ui.username())
2392 q = repo.mq
2392 q = repo.mq
2393 opts['msg'] = msg
2393 opts['msg'] = msg
2394 if opts.get('edit'):
2394 if opts.get('edit'):
2395 opts['msg'] = getmsg
2395 opts['msg'] = getmsg
2396 else:
2396 else:
2397 opts['msg'] = msg
2397 opts['msg'] = msg
2398 setupheaderopts(ui, opts)
2398 setupheaderopts(ui, opts)
2399 q.new(repo, patch, *args, **opts)
2399 q.new(repo, patch, *args, **opts)
2400 q.savedirty()
2400 q.savedirty()
2401 return 0
2401 return 0
2402
2402
2403 @command("^qrefresh",
2403 @command("^qrefresh",
2404 [('e', 'edit', None, _('edit commit message')),
2404 [('e', 'edit', None, _('edit commit message')),
2405 ('g', 'git', None, _('use git extended diff format')),
2405 ('g', 'git', None, _('use git extended diff format')),
2406 ('s', 'short', None,
2406 ('s', 'short', None,
2407 _('refresh only files already in the patch and specified files')),
2407 _('refresh only files already in the patch and specified files')),
2408 ('U', 'currentuser', None,
2408 ('U', 'currentuser', None,
2409 _('add/update author field in patch with current user')),
2409 _('add/update author field in patch with current user')),
2410 ('u', 'user', '',
2410 ('u', 'user', '',
2411 _('add/update author field in patch with given user'), _('USER')),
2411 _('add/update author field in patch with given user'), _('USER')),
2412 ('D', 'currentdate', None,
2412 ('D', 'currentdate', None,
2413 _('add/update date field in patch with current date')),
2413 _('add/update date field in patch with current date')),
2414 ('d', 'date', '',
2414 ('d', 'date', '',
2415 _('add/update date field in patch with given date'), _('DATE'))
2415 _('add/update date field in patch with given date'), _('DATE'))
2416 ] + commands.walkopts + commands.commitopts,
2416 ] + commands.walkopts + commands.commitopts,
2417 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...'))
2417 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...'))
2418 def refresh(ui, repo, *pats, **opts):
2418 def refresh(ui, repo, *pats, **opts):
2419 """update the current patch
2419 """update the current patch
2420
2420
2421 If any file patterns are provided, the refreshed patch will
2421 If any file patterns are provided, the refreshed patch will
2422 contain only the modifications that match those patterns; the
2422 contain only the modifications that match those patterns; the
2423 remaining modifications will remain in the working directory.
2423 remaining modifications will remain in the working directory.
2424
2424
2425 If -s/--short is specified, files currently included in the patch
2425 If -s/--short is specified, files currently included in the patch
2426 will be refreshed just like matched files and remain in the patch.
2426 will be refreshed just like matched files and remain in the patch.
2427
2427
2428 If -e/--edit is specified, Mercurial will start your configured editor for
2428 If -e/--edit is specified, Mercurial will start your configured editor for
2429 you to enter a message. In case qrefresh fails, you will find a backup of
2429 you to enter a message. In case qrefresh fails, you will find a backup of
2430 your message in ``.hg/last-message.txt``.
2430 your message in ``.hg/last-message.txt``.
2431
2431
2432 hg add/remove/copy/rename work as usual, though you might want to
2432 hg add/remove/copy/rename work as usual, though you might want to
2433 use git-style patches (-g/--git or [diff] git=1) to track copies
2433 use git-style patches (-g/--git or [diff] git=1) to track copies
2434 and renames. See the diffs help topic for more information on the
2434 and renames. See the diffs help topic for more information on the
2435 git diff format.
2435 git diff format.
2436
2436
2437 Returns 0 on success.
2437 Returns 0 on success.
2438 """
2438 """
2439 q = repo.mq
2439 q = repo.mq
2440 message = cmdutil.logmessage(ui, opts)
2440 message = cmdutil.logmessage(ui, opts)
2441 if opts.get('edit'):
2441 if opts.get('edit'):
2442 if not q.applied:
2442 if not q.applied:
2443 ui.write(_("no patches applied\n"))
2443 ui.write(_("no patches applied\n"))
2444 return 1
2444 return 1
2445 if message:
2445 if message:
2446 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2446 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2447 patch = q.applied[-1].name
2447 patch = q.applied[-1].name
2448 ph = patchheader(q.join(patch), q.plainmode)
2448 ph = patchheader(q.join(patch), q.plainmode)
2449 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
2449 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
2450 # We don't want to lose the patch message if qrefresh fails (issue2062)
2450 # We don't want to lose the patch message if qrefresh fails (issue2062)
2451 repo.savecommitmessage(message)
2451 repo.savecommitmessage(message)
2452 setupheaderopts(ui, opts)
2452 setupheaderopts(ui, opts)
2453 wlock = repo.wlock()
2453 wlock = repo.wlock()
2454 try:
2454 try:
2455 ret = q.refresh(repo, pats, msg=message, **opts)
2455 ret = q.refresh(repo, pats, msg=message, **opts)
2456 q.savedirty()
2456 q.savedirty()
2457 return ret
2457 return ret
2458 finally:
2458 finally:
2459 wlock.release()
2459 wlock.release()
2460
2460
2461 @command("^qdiff",
2461 @command("^qdiff",
2462 commands.diffopts + commands.diffopts2 + commands.walkopts,
2462 commands.diffopts + commands.diffopts2 + commands.walkopts,
2463 _('hg qdiff [OPTION]... [FILE]...'))
2463 _('hg qdiff [OPTION]... [FILE]...'))
2464 def diff(ui, repo, *pats, **opts):
2464 def diff(ui, repo, *pats, **opts):
2465 """diff of the current patch and subsequent modifications
2465 """diff of the current patch and subsequent modifications
2466
2466
2467 Shows a diff which includes the current patch as well as any
2467 Shows a diff which includes the current patch as well as any
2468 changes which have been made in the working directory since the
2468 changes which have been made in the working directory since the
2469 last refresh (thus showing what the current patch would become
2469 last refresh (thus showing what the current patch would become
2470 after a qrefresh).
2470 after a qrefresh).
2471
2471
2472 Use :hg:`diff` if you only want to see the changes made since the
2472 Use :hg:`diff` if you only want to see the changes made since the
2473 last qrefresh, or :hg:`export qtip` if you want to see changes
2473 last qrefresh, or :hg:`export qtip` if you want to see changes
2474 made by the current patch without including changes made since the
2474 made by the current patch without including changes made since the
2475 qrefresh.
2475 qrefresh.
2476
2476
2477 Returns 0 on success.
2477 Returns 0 on success.
2478 """
2478 """
2479 repo.mq.diff(repo, pats, opts)
2479 repo.mq.diff(repo, pats, opts)
2480 return 0
2480 return 0
2481
2481
2482 @command('qfold',
2482 @command('qfold',
2483 [('e', 'edit', None, _('edit patch header')),
2483 [('e', 'edit', None, _('edit patch header')),
2484 ('k', 'keep', None, _('keep folded patch files')),
2484 ('k', 'keep', None, _('keep folded patch files')),
2485 ] + commands.commitopts,
2485 ] + commands.commitopts,
2486 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...'))
2486 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...'))
2487 def fold(ui, repo, *files, **opts):
2487 def fold(ui, repo, *files, **opts):
2488 """fold the named patches into the current patch
2488 """fold the named patches into the current patch
2489
2489
2490 Patches must not yet be applied. Each patch will be successively
2490 Patches must not yet be applied. Each patch will be successively
2491 applied to the current patch in the order given. If all the
2491 applied to the current patch in the order given. If all the
2492 patches apply successfully, the current patch will be refreshed
2492 patches apply successfully, the current patch will be refreshed
2493 with the new cumulative patch, and the folded patches will be
2493 with the new cumulative patch, and the folded patches will be
2494 deleted. With -k/--keep, the folded patch files will not be
2494 deleted. With -k/--keep, the folded patch files will not be
2495 removed afterwards.
2495 removed afterwards.
2496
2496
2497 The header for each folded patch will be concatenated with the
2497 The header for each folded patch will be concatenated with the
2498 current patch header, separated by a line of ``* * *``.
2498 current patch header, separated by a line of ``* * *``.
2499
2499
2500 Returns 0 on success."""
2500 Returns 0 on success."""
2501 q = repo.mq
2501 q = repo.mq
2502 if not files:
2502 if not files:
2503 raise util.Abort(_('qfold requires at least one patch name'))
2503 raise util.Abort(_('qfold requires at least one patch name'))
2504 if not q.checktoppatch(repo)[0]:
2504 if not q.checktoppatch(repo)[0]:
2505 raise util.Abort(_('no patches applied'))
2505 raise util.Abort(_('no patches applied'))
2506 q.checklocalchanges(repo)
2506 q.checklocalchanges(repo)
2507
2507
2508 message = cmdutil.logmessage(ui, opts)
2508 message = cmdutil.logmessage(ui, opts)
2509 if opts.get('edit'):
2509 if opts.get('edit'):
2510 if message:
2510 if message:
2511 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2511 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2512
2512
2513 parent = q.lookup('qtip')
2513 parent = q.lookup('qtip')
2514 patches = []
2514 patches = []
2515 messages = []
2515 messages = []
2516 for f in files:
2516 for f in files:
2517 p = q.lookup(f)
2517 p = q.lookup(f)
2518 if p in patches or p == parent:
2518 if p in patches or p == parent:
2519 ui.warn(_('skipping already folded patch %s\n') % p)
2519 ui.warn(_('skipping already folded patch %s\n') % p)
2520 if q.isapplied(p):
2520 if q.isapplied(p):
2521 raise util.Abort(_('qfold cannot fold already applied patch %s')
2521 raise util.Abort(_('qfold cannot fold already applied patch %s')
2522 % p)
2522 % p)
2523 patches.append(p)
2523 patches.append(p)
2524
2524
2525 for p in patches:
2525 for p in patches:
2526 if not message:
2526 if not message:
2527 ph = patchheader(q.join(p), q.plainmode)
2527 ph = patchheader(q.join(p), q.plainmode)
2528 if ph.message:
2528 if ph.message:
2529 messages.append(ph.message)
2529 messages.append(ph.message)
2530 pf = q.join(p)
2530 pf = q.join(p)
2531 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2531 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2532 if not patchsuccess:
2532 if not patchsuccess:
2533 raise util.Abort(_('error folding patch %s') % p)
2533 raise util.Abort(_('error folding patch %s') % p)
2534
2534
2535 if not message:
2535 if not message:
2536 ph = patchheader(q.join(parent), q.plainmode)
2536 ph = patchheader(q.join(parent), q.plainmode)
2537 message, user = ph.message, ph.user
2537 message, user = ph.message, ph.user
2538 for msg in messages:
2538 for msg in messages:
2539 message.append('* * *')
2539 message.append('* * *')
2540 message.extend(msg)
2540 message.extend(msg)
2541 message = '\n'.join(message)
2541 message = '\n'.join(message)
2542
2542
2543 if opts.get('edit'):
2543 if opts.get('edit'):
2544 message = ui.edit(message, user or ui.username())
2544 message = ui.edit(message, user or ui.username())
2545
2545
2546 diffopts = q.patchopts(q.diffopts(), *patches)
2546 diffopts = q.patchopts(q.diffopts(), *patches)
2547 wlock = repo.wlock()
2547 wlock = repo.wlock()
2548 try:
2548 try:
2549 q.refresh(repo, msg=message, git=diffopts.git)
2549 q.refresh(repo, msg=message, git=diffopts.git)
2550 q.delete(repo, patches, opts)
2550 q.delete(repo, patches, opts)
2551 q.savedirty()
2551 q.savedirty()
2552 finally:
2552 finally:
2553 wlock.release()
2553 wlock.release()
2554
2554
2555 @command("qgoto",
2555 @command("qgoto",
2556 [('', 'keep-changes', None,
2556 [('', 'keep-changes', None,
2557 _('tolerate non-conflicting local changes')),
2557 _('tolerate non-conflicting local changes')),
2558 ('f', 'force', None, _('overwrite any local changes')),
2558 ('f', 'force', None, _('overwrite any local changes')),
2559 ('', 'no-backup', None, _('do not save backup copies of files'))],
2559 ('', 'no-backup', None, _('do not save backup copies of files'))],
2560 _('hg qgoto [OPTION]... PATCH'))
2560 _('hg qgoto [OPTION]... PATCH'))
2561 def goto(ui, repo, patch, **opts):
2561 def goto(ui, repo, patch, **opts):
2562 '''push or pop patches until named patch is at top of stack
2562 '''push or pop patches until named patch is at top of stack
2563
2563
2564 Returns 0 on success.'''
2564 Returns 0 on success.'''
2565 opts = fixkeepchangesopts(ui, opts)
2565 opts = fixkeepchangesopts(ui, opts)
2566 q = repo.mq
2566 q = repo.mq
2567 patch = q.lookup(patch)
2567 patch = q.lookup(patch)
2568 nobackup = opts.get('no_backup')
2568 nobackup = opts.get('no_backup')
2569 keepchanges = opts.get('keep_changes')
2569 keepchanges = opts.get('keep_changes')
2570 if q.isapplied(patch):
2570 if q.isapplied(patch):
2571 ret = q.pop(repo, patch, force=opts.get('force'), nobackup=nobackup,
2571 ret = q.pop(repo, patch, force=opts.get('force'), nobackup=nobackup,
2572 keepchanges=keepchanges)
2572 keepchanges=keepchanges)
2573 else:
2573 else:
2574 ret = q.push(repo, patch, force=opts.get('force'), nobackup=nobackup,
2574 ret = q.push(repo, patch, force=opts.get('force'), nobackup=nobackup,
2575 keepchanges=keepchanges)
2575 keepchanges=keepchanges)
2576 q.savedirty()
2576 q.savedirty()
2577 return ret
2577 return ret
2578
2578
2579 @command("qguard",
2579 @command("qguard",
2580 [('l', 'list', None, _('list all patches and guards')),
2580 [('l', 'list', None, _('list all patches and guards')),
2581 ('n', 'none', None, _('drop all guards'))],
2581 ('n', 'none', None, _('drop all guards'))],
2582 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]'))
2582 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]'))
2583 def guard(ui, repo, *args, **opts):
2583 def guard(ui, repo, *args, **opts):
2584 '''set or print guards for a patch
2584 '''set or print guards for a patch
2585
2585
2586 Guards control whether a patch can be pushed. A patch with no
2586 Guards control whether a patch can be pushed. A patch with no
2587 guards is always pushed. A patch with a positive guard ("+foo") is
2587 guards is always pushed. A patch with a positive guard ("+foo") is
2588 pushed only if the :hg:`qselect` command has activated it. A patch with
2588 pushed only if the :hg:`qselect` command has activated it. A patch with
2589 a negative guard ("-foo") is never pushed if the :hg:`qselect` command
2589 a negative guard ("-foo") is never pushed if the :hg:`qselect` command
2590 has activated it.
2590 has activated it.
2591
2591
2592 With no arguments, print the currently active guards.
2592 With no arguments, print the currently active guards.
2593 With arguments, set guards for the named patch.
2593 With arguments, set guards for the named patch.
2594
2594
2595 .. note::
2595 .. note::
2596 Specifying negative guards now requires '--'.
2596 Specifying negative guards now requires '--'.
2597
2597
2598 To set guards on another patch::
2598 To set guards on another patch::
2599
2599
2600 hg qguard other.patch -- +2.6.17 -stable
2600 hg qguard other.patch -- +2.6.17 -stable
2601
2601
2602 Returns 0 on success.
2602 Returns 0 on success.
2603 '''
2603 '''
2604 def status(idx):
2604 def status(idx):
2605 guards = q.seriesguards[idx] or ['unguarded']
2605 guards = q.seriesguards[idx] or ['unguarded']
2606 if q.series[idx] in applied:
2606 if q.series[idx] in applied:
2607 state = 'applied'
2607 state = 'applied'
2608 elif q.pushable(idx)[0]:
2608 elif q.pushable(idx)[0]:
2609 state = 'unapplied'
2609 state = 'unapplied'
2610 else:
2610 else:
2611 state = 'guarded'
2611 state = 'guarded'
2612 label = 'qguard.patch qguard.%s qseries.%s' % (state, state)
2612 label = 'qguard.patch qguard.%s qseries.%s' % (state, state)
2613 ui.write('%s: ' % ui.label(q.series[idx], label))
2613 ui.write('%s: ' % ui.label(q.series[idx], label))
2614
2614
2615 for i, guard in enumerate(guards):
2615 for i, guard in enumerate(guards):
2616 if guard.startswith('+'):
2616 if guard.startswith('+'):
2617 ui.write(guard, label='qguard.positive')
2617 ui.write(guard, label='qguard.positive')
2618 elif guard.startswith('-'):
2618 elif guard.startswith('-'):
2619 ui.write(guard, label='qguard.negative')
2619 ui.write(guard, label='qguard.negative')
2620 else:
2620 else:
2621 ui.write(guard, label='qguard.unguarded')
2621 ui.write(guard, label='qguard.unguarded')
2622 if i != len(guards) - 1:
2622 if i != len(guards) - 1:
2623 ui.write(' ')
2623 ui.write(' ')
2624 ui.write('\n')
2624 ui.write('\n')
2625 q = repo.mq
2625 q = repo.mq
2626 applied = set(p.name for p in q.applied)
2626 applied = set(p.name for p in q.applied)
2627 patch = None
2627 patch = None
2628 args = list(args)
2628 args = list(args)
2629 if opts.get('list'):
2629 if opts.get('list'):
2630 if args or opts.get('none'):
2630 if args or opts.get('none'):
2631 raise util.Abort(_('cannot mix -l/--list with options or '
2631 raise util.Abort(_('cannot mix -l/--list with options or '
2632 'arguments'))
2632 'arguments'))
2633 for i in xrange(len(q.series)):
2633 for i in xrange(len(q.series)):
2634 status(i)
2634 status(i)
2635 return
2635 return
2636 if not args or args[0][0:1] in '-+':
2636 if not args or args[0][0:1] in '-+':
2637 if not q.applied:
2637 if not q.applied:
2638 raise util.Abort(_('no patches applied'))
2638 raise util.Abort(_('no patches applied'))
2639 patch = q.applied[-1].name
2639 patch = q.applied[-1].name
2640 if patch is None and args[0][0:1] not in '-+':
2640 if patch is None and args[0][0:1] not in '-+':
2641 patch = args.pop(0)
2641 patch = args.pop(0)
2642 if patch is None:
2642 if patch is None:
2643 raise util.Abort(_('no patch to work with'))
2643 raise util.Abort(_('no patch to work with'))
2644 if args or opts.get('none'):
2644 if args or opts.get('none'):
2645 idx = q.findseries(patch)
2645 idx = q.findseries(patch)
2646 if idx is None:
2646 if idx is None:
2647 raise util.Abort(_('no patch named %s') % patch)
2647 raise util.Abort(_('no patch named %s') % patch)
2648 q.setguards(idx, args)
2648 q.setguards(idx, args)
2649 q.savedirty()
2649 q.savedirty()
2650 else:
2650 else:
2651 status(q.series.index(q.lookup(patch)))
2651 status(q.series.index(q.lookup(patch)))
2652
2652
2653 @command("qheader", [], _('hg qheader [PATCH]'))
2653 @command("qheader", [], _('hg qheader [PATCH]'))
2654 def header(ui, repo, patch=None):
2654 def header(ui, repo, patch=None):
2655 """print the header of the topmost or specified patch
2655 """print the header of the topmost or specified patch
2656
2656
2657 Returns 0 on success."""
2657 Returns 0 on success."""
2658 q = repo.mq
2658 q = repo.mq
2659
2659
2660 if patch:
2660 if patch:
2661 patch = q.lookup(patch)
2661 patch = q.lookup(patch)
2662 else:
2662 else:
2663 if not q.applied:
2663 if not q.applied:
2664 ui.write(_('no patches applied\n'))
2664 ui.write(_('no patches applied\n'))
2665 return 1
2665 return 1
2666 patch = q.lookup('qtip')
2666 patch = q.lookup('qtip')
2667 ph = patchheader(q.join(patch), q.plainmode)
2667 ph = patchheader(q.join(patch), q.plainmode)
2668
2668
2669 ui.write('\n'.join(ph.message) + '\n')
2669 ui.write('\n'.join(ph.message) + '\n')
2670
2670
2671 def lastsavename(path):
2671 def lastsavename(path):
2672 (directory, base) = os.path.split(path)
2672 (directory, base) = os.path.split(path)
2673 names = os.listdir(directory)
2673 names = os.listdir(directory)
2674 namere = re.compile("%s.([0-9]+)" % base)
2674 namere = re.compile("%s.([0-9]+)" % base)
2675 maxindex = None
2675 maxindex = None
2676 maxname = None
2676 maxname = None
2677 for f in names:
2677 for f in names:
2678 m = namere.match(f)
2678 m = namere.match(f)
2679 if m:
2679 if m:
2680 index = int(m.group(1))
2680 index = int(m.group(1))
2681 if maxindex is None or index > maxindex:
2681 if maxindex is None or index > maxindex:
2682 maxindex = index
2682 maxindex = index
2683 maxname = f
2683 maxname = f
2684 if maxname:
2684 if maxname:
2685 return (os.path.join(directory, maxname), maxindex)
2685 return (os.path.join(directory, maxname), maxindex)
2686 return (None, None)
2686 return (None, None)
2687
2687
2688 def savename(path):
2688 def savename(path):
2689 (last, index) = lastsavename(path)
2689 (last, index) = lastsavename(path)
2690 if last is None:
2690 if last is None:
2691 index = 0
2691 index = 0
2692 newpath = path + ".%d" % (index + 1)
2692 newpath = path + ".%d" % (index + 1)
2693 return newpath
2693 return newpath
2694
2694
2695 @command("^qpush",
2695 @command("^qpush",
2696 [('', 'keep-changes', None,
2696 [('', 'keep-changes', None,
2697 _('tolerate non-conflicting local changes')),
2697 _('tolerate non-conflicting local changes')),
2698 ('f', 'force', None, _('apply on top of local changes')),
2698 ('f', 'force', None, _('apply on top of local changes')),
2699 ('e', 'exact', None,
2699 ('e', 'exact', None,
2700 _('apply the target patch to its recorded parent')),
2700 _('apply the target patch to its recorded parent')),
2701 ('l', 'list', None, _('list patch name in commit text')),
2701 ('l', 'list', None, _('list patch name in commit text')),
2702 ('a', 'all', None, _('apply all patches')),
2702 ('a', 'all', None, _('apply all patches')),
2703 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2703 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2704 ('n', 'name', '',
2704 ('n', 'name', '',
2705 _('merge queue name (DEPRECATED)'), _('NAME')),
2705 _('merge queue name (DEPRECATED)'), _('NAME')),
2706 ('', 'move', None,
2706 ('', 'move', None,
2707 _('reorder patch series and apply only the patch')),
2707 _('reorder patch series and apply only the patch')),
2708 ('', 'no-backup', None, _('do not save backup copies of files'))],
2708 ('', 'no-backup', None, _('do not save backup copies of files'))],
2709 _('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]'))
2709 _('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]'))
2710 def push(ui, repo, patch=None, **opts):
2710 def push(ui, repo, patch=None, **opts):
2711 """push the next patch onto the stack
2711 """push the next patch onto the stack
2712
2712
2713 By default, abort if the working directory contains uncommitted
2713 By default, abort if the working directory contains uncommitted
2714 changes. With --keep-changes, abort only if the uncommitted files
2714 changes. With --keep-changes, abort only if the uncommitted files
2715 overlap with patched files. With -f/--force, backup and patch over
2715 overlap with patched files. With -f/--force, backup and patch over
2716 uncommitted changes.
2716 uncommitted changes.
2717
2717
2718 Return 0 on success.
2718 Return 0 on success.
2719 """
2719 """
2720 q = repo.mq
2720 q = repo.mq
2721 mergeq = None
2721 mergeq = None
2722
2722
2723 opts = fixkeepchangesopts(ui, opts)
2723 opts = fixkeepchangesopts(ui, opts)
2724 if opts.get('merge'):
2724 if opts.get('merge'):
2725 if opts.get('name'):
2725 if opts.get('name'):
2726 newpath = repo.join(opts.get('name'))
2726 newpath = repo.join(opts.get('name'))
2727 else:
2727 else:
2728 newpath, i = lastsavename(q.path)
2728 newpath, i = lastsavename(q.path)
2729 if not newpath:
2729 if not newpath:
2730 ui.warn(_("no saved queues found, please use -n\n"))
2730 ui.warn(_("no saved queues found, please use -n\n"))
2731 return 1
2731 return 1
2732 mergeq = queue(ui, repo.path, newpath)
2732 mergeq = queue(ui, repo.path, newpath)
2733 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2733 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2734 ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
2734 ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
2735 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
2735 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
2736 exact=opts.get('exact'), nobackup=opts.get('no_backup'),
2736 exact=opts.get('exact'), nobackup=opts.get('no_backup'),
2737 keepchanges=opts.get('keep_changes'))
2737 keepchanges=opts.get('keep_changes'))
2738 return ret
2738 return ret
2739
2739
2740 @command("^qpop",
2740 @command("^qpop",
2741 [('a', 'all', None, _('pop all patches')),
2741 [('a', 'all', None, _('pop all patches')),
2742 ('n', 'name', '',
2742 ('n', 'name', '',
2743 _('queue name to pop (DEPRECATED)'), _('NAME')),
2743 _('queue name to pop (DEPRECATED)'), _('NAME')),
2744 ('', 'keep-changes', None,
2744 ('', 'keep-changes', None,
2745 _('tolerate non-conflicting local changes')),
2745 _('tolerate non-conflicting local changes')),
2746 ('f', 'force', None, _('forget any local changes to patched files')),
2746 ('f', 'force', None, _('forget any local changes to patched files')),
2747 ('', 'no-backup', None, _('do not save backup copies of files'))],
2747 ('', 'no-backup', None, _('do not save backup copies of files'))],
2748 _('hg qpop [-a] [-f] [PATCH | INDEX]'))
2748 _('hg qpop [-a] [-f] [PATCH | INDEX]'))
2749 def pop(ui, repo, patch=None, **opts):
2749 def pop(ui, repo, patch=None, **opts):
2750 """pop the current patch off the stack
2750 """pop the current patch off the stack
2751
2751
2752 Without argument, pops off the top of the patch stack. If given a
2752 Without argument, pops off the top of the patch stack. If given a
2753 patch name, keeps popping off patches until the named patch is at
2753 patch name, keeps popping off patches until the named patch is at
2754 the top of the stack.
2754 the top of the stack.
2755
2755
2756 By default, abort if the working directory contains uncommitted
2756 By default, abort if the working directory contains uncommitted
2757 changes. With --keep-changes, abort only if the uncommitted files
2757 changes. With --keep-changes, abort only if the uncommitted files
2758 overlap with patched files. With -f/--force, backup and discard
2758 overlap with patched files. With -f/--force, backup and discard
2759 changes made to such files.
2759 changes made to such files.
2760
2760
2761 Return 0 on success.
2761 Return 0 on success.
2762 """
2762 """
2763 opts = fixkeepchangesopts(ui, opts)
2763 opts = fixkeepchangesopts(ui, opts)
2764 localupdate = True
2764 localupdate = True
2765 if opts.get('name'):
2765 if opts.get('name'):
2766 q = queue(ui, repo.path, repo.join(opts.get('name')))
2766 q = queue(ui, repo.path, repo.join(opts.get('name')))
2767 ui.warn(_('using patch queue: %s\n') % q.path)
2767 ui.warn(_('using patch queue: %s\n') % q.path)
2768 localupdate = False
2768 localupdate = False
2769 else:
2769 else:
2770 q = repo.mq
2770 q = repo.mq
2771 ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate,
2771 ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate,
2772 all=opts.get('all'), nobackup=opts.get('no_backup'),
2772 all=opts.get('all'), nobackup=opts.get('no_backup'),
2773 keepchanges=opts.get('keep_changes'))
2773 keepchanges=opts.get('keep_changes'))
2774 q.savedirty()
2774 q.savedirty()
2775 return ret
2775 return ret
2776
2776
2777 @command("qrename|qmv", [], _('hg qrename PATCH1 [PATCH2]'))
2777 @command("qrename|qmv", [], _('hg qrename PATCH1 [PATCH2]'))
2778 def rename(ui, repo, patch, name=None, **opts):
2778 def rename(ui, repo, patch, name=None, **opts):
2779 """rename a patch
2779 """rename a patch
2780
2780
2781 With one argument, renames the current patch to PATCH1.
2781 With one argument, renames the current patch to PATCH1.
2782 With two arguments, renames PATCH1 to PATCH2.
2782 With two arguments, renames PATCH1 to PATCH2.
2783
2783
2784 Returns 0 on success."""
2784 Returns 0 on success."""
2785 q = repo.mq
2785 q = repo.mq
2786 if not name:
2786 if not name:
2787 name = patch
2787 name = patch
2788 patch = None
2788 patch = None
2789
2789
2790 if patch:
2790 if patch:
2791 patch = q.lookup(patch)
2791 patch = q.lookup(patch)
2792 else:
2792 else:
2793 if not q.applied:
2793 if not q.applied:
2794 ui.write(_('no patches applied\n'))
2794 ui.write(_('no patches applied\n'))
2795 return
2795 return
2796 patch = q.lookup('qtip')
2796 patch = q.lookup('qtip')
2797 absdest = q.join(name)
2797 absdest = q.join(name)
2798 if os.path.isdir(absdest):
2798 if os.path.isdir(absdest):
2799 name = normname(os.path.join(name, os.path.basename(patch)))
2799 name = normname(os.path.join(name, os.path.basename(patch)))
2800 absdest = q.join(name)
2800 absdest = q.join(name)
2801 q.checkpatchname(name)
2801 q.checkpatchname(name)
2802
2802
2803 ui.note(_('renaming %s to %s\n') % (patch, name))
2803 ui.note(_('renaming %s to %s\n') % (patch, name))
2804 i = q.findseries(patch)
2804 i = q.findseries(patch)
2805 guards = q.guard_re.findall(q.fullseries[i])
2805 guards = q.guard_re.findall(q.fullseries[i])
2806 q.fullseries[i] = name + ''.join([' #' + g for g in guards])
2806 q.fullseries[i] = name + ''.join([' #' + g for g in guards])
2807 q.parseseries()
2807 q.parseseries()
2808 q.seriesdirty = True
2808 q.seriesdirty = True
2809
2809
2810 info = q.isapplied(patch)
2810 info = q.isapplied(patch)
2811 if info:
2811 if info:
2812 q.applied[info[0]] = statusentry(info[1], name)
2812 q.applied[info[0]] = statusentry(info[1], name)
2813 q.applieddirty = True
2813 q.applieddirty = True
2814
2814
2815 destdir = os.path.dirname(absdest)
2815 destdir = os.path.dirname(absdest)
2816 if not os.path.isdir(destdir):
2816 if not os.path.isdir(destdir):
2817 os.makedirs(destdir)
2817 os.makedirs(destdir)
2818 util.rename(q.join(patch), absdest)
2818 util.rename(q.join(patch), absdest)
2819 r = q.qrepo()
2819 r = q.qrepo()
2820 if r and patch in r.dirstate:
2820 if r and patch in r.dirstate:
2821 wctx = r[None]
2821 wctx = r[None]
2822 wlock = r.wlock()
2822 wlock = r.wlock()
2823 try:
2823 try:
2824 if r.dirstate[patch] == 'a':
2824 if r.dirstate[patch] == 'a':
2825 r.dirstate.drop(patch)
2825 r.dirstate.drop(patch)
2826 r.dirstate.add(name)
2826 r.dirstate.add(name)
2827 else:
2827 else:
2828 wctx.copy(patch, name)
2828 wctx.copy(patch, name)
2829 wctx.forget([patch])
2829 wctx.forget([patch])
2830 finally:
2830 finally:
2831 wlock.release()
2831 wlock.release()
2832
2832
2833 q.savedirty()
2833 q.savedirty()
2834
2834
2835 @command("qrestore",
2835 @command("qrestore",
2836 [('d', 'delete', None, _('delete save entry')),
2836 [('d', 'delete', None, _('delete save entry')),
2837 ('u', 'update', None, _('update queue working directory'))],
2837 ('u', 'update', None, _('update queue working directory'))],
2838 _('hg qrestore [-d] [-u] REV'))
2838 _('hg qrestore [-d] [-u] REV'))
2839 def restore(ui, repo, rev, **opts):
2839 def restore(ui, repo, rev, **opts):
2840 """restore the queue state saved by a revision (DEPRECATED)
2840 """restore the queue state saved by a revision (DEPRECATED)
2841
2841
2842 This command is deprecated, use :hg:`rebase` instead."""
2842 This command is deprecated, use :hg:`rebase` instead."""
2843 rev = repo.lookup(rev)
2843 rev = repo.lookup(rev)
2844 q = repo.mq
2844 q = repo.mq
2845 q.restore(repo, rev, delete=opts.get('delete'),
2845 q.restore(repo, rev, delete=opts.get('delete'),
2846 qupdate=opts.get('update'))
2846 qupdate=opts.get('update'))
2847 q.savedirty()
2847 q.savedirty()
2848 return 0
2848 return 0
2849
2849
2850 @command("qsave",
2850 @command("qsave",
2851 [('c', 'copy', None, _('copy patch directory')),
2851 [('c', 'copy', None, _('copy patch directory')),
2852 ('n', 'name', '',
2852 ('n', 'name', '',
2853 _('copy directory name'), _('NAME')),
2853 _('copy directory name'), _('NAME')),
2854 ('e', 'empty', None, _('clear queue status file')),
2854 ('e', 'empty', None, _('clear queue status file')),
2855 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2855 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2856 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'))
2856 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'))
2857 def save(ui, repo, **opts):
2857 def save(ui, repo, **opts):
2858 """save current queue state (DEPRECATED)
2858 """save current queue state (DEPRECATED)
2859
2859
2860 This command is deprecated, use :hg:`rebase` instead."""
2860 This command is deprecated, use :hg:`rebase` instead."""
2861 q = repo.mq
2861 q = repo.mq
2862 message = cmdutil.logmessage(ui, opts)
2862 message = cmdutil.logmessage(ui, opts)
2863 ret = q.save(repo, msg=message)
2863 ret = q.save(repo, msg=message)
2864 if ret:
2864 if ret:
2865 return ret
2865 return ret
2866 q.savedirty() # save to .hg/patches before copying
2866 q.savedirty() # save to .hg/patches before copying
2867 if opts.get('copy'):
2867 if opts.get('copy'):
2868 path = q.path
2868 path = q.path
2869 if opts.get('name'):
2869 if opts.get('name'):
2870 newpath = os.path.join(q.basepath, opts.get('name'))
2870 newpath = os.path.join(q.basepath, opts.get('name'))
2871 if os.path.exists(newpath):
2871 if os.path.exists(newpath):
2872 if not os.path.isdir(newpath):
2872 if not os.path.isdir(newpath):
2873 raise util.Abort(_('destination %s exists and is not '
2873 raise util.Abort(_('destination %s exists and is not '
2874 'a directory') % newpath)
2874 'a directory') % newpath)
2875 if not opts.get('force'):
2875 if not opts.get('force'):
2876 raise util.Abort(_('destination %s exists, '
2876 raise util.Abort(_('destination %s exists, '
2877 'use -f to force') % newpath)
2877 'use -f to force') % newpath)
2878 else:
2878 else:
2879 newpath = savename(path)
2879 newpath = savename(path)
2880 ui.warn(_("copy %s to %s\n") % (path, newpath))
2880 ui.warn(_("copy %s to %s\n") % (path, newpath))
2881 util.copyfiles(path, newpath)
2881 util.copyfiles(path, newpath)
2882 if opts.get('empty'):
2882 if opts.get('empty'):
2883 del q.applied[:]
2883 del q.applied[:]
2884 q.applieddirty = True
2884 q.applieddirty = True
2885 q.savedirty()
2885 q.savedirty()
2886 return 0
2886 return 0
2887
2887
2888 @command("strip",
2888 @command("strip",
2889 [
2889 [
2890 ('r', 'rev', [], _('strip specified revision (optional, '
2890 ('r', 'rev', [], _('strip specified revision (optional, '
2891 'can specify revisions without this '
2891 'can specify revisions without this '
2892 'option)'), _('REV')),
2892 'option)'), _('REV')),
2893 ('f', 'force', None, _('force removal of changesets, discard '
2893 ('f', 'force', None, _('force removal of changesets, discard '
2894 'uncommitted changes (no backup)')),
2894 'uncommitted changes (no backup)')),
2895 ('b', 'backup', None, _('bundle only changesets with local revision'
2895 ('b', 'backup', None, _('bundle only changesets with local revision'
2896 ' number greater than REV which are not'
2896 ' number greater than REV which are not'
2897 ' descendants of REV (DEPRECATED)')),
2897 ' descendants of REV (DEPRECATED)')),
2898 ('', 'no-backup', None, _('no backups')),
2898 ('', 'no-backup', None, _('no backups')),
2899 ('', 'nobackup', None, _('no backups (DEPRECATED)')),
2899 ('', 'nobackup', None, _('no backups (DEPRECATED)')),
2900 ('n', '', None, _('ignored (DEPRECATED)')),
2900 ('n', '', None, _('ignored (DEPRECATED)')),
2901 ('k', 'keep', None, _("do not modify working copy during strip")),
2901 ('k', 'keep', None, _("do not modify working copy during strip")),
2902 ('B', 'bookmark', '', _("remove revs only reachable from given"
2902 ('B', 'bookmark', '', _("remove revs only reachable from given"
2903 " bookmark"))],
2903 " bookmark"))],
2904 _('hg strip [-k] [-f] [-n] [-B bookmark] [-r] REV...'))
2904 _('hg strip [-k] [-f] [-n] [-B bookmark] [-r] REV...'))
2905 def strip(ui, repo, *revs, **opts):
2905 def strip(ui, repo, *revs, **opts):
2906 """strip changesets and all their descendants from the repository
2906 """strip changesets and all their descendants from the repository
2907
2907
2908 The strip command removes the specified changesets and all their
2908 The strip command removes the specified changesets and all their
2909 descendants. If the working directory has uncommitted changes, the
2909 descendants. If the working directory has uncommitted changes, the
2910 operation is aborted unless the --force flag is supplied, in which
2910 operation is aborted unless the --force flag is supplied, in which
2911 case changes will be discarded.
2911 case changes will be discarded.
2912
2912
2913 If a parent of the working directory is stripped, then the working
2913 If a parent of the working directory is stripped, then the working
2914 directory will automatically be updated to the most recent
2914 directory will automatically be updated to the most recent
2915 available ancestor of the stripped parent after the operation
2915 available ancestor of the stripped parent after the operation
2916 completes.
2916 completes.
2917
2917
2918 Any stripped changesets are stored in ``.hg/strip-backup`` as a
2918 Any stripped changesets are stored in ``.hg/strip-backup`` as a
2919 bundle (see :hg:`help bundle` and :hg:`help unbundle`). They can
2919 bundle (see :hg:`help bundle` and :hg:`help unbundle`). They can
2920 be restored by running :hg:`unbundle .hg/strip-backup/BUNDLE`,
2920 be restored by running :hg:`unbundle .hg/strip-backup/BUNDLE`,
2921 where BUNDLE is the bundle file created by the strip. Note that
2921 where BUNDLE is the bundle file created by the strip. Note that
2922 the local revision numbers will in general be different after the
2922 the local revision numbers will in general be different after the
2923 restore.
2923 restore.
2924
2924
2925 Use the --no-backup option to discard the backup bundle once the
2925 Use the --no-backup option to discard the backup bundle once the
2926 operation completes.
2926 operation completes.
2927
2927
2928 Strip is not a history-rewriting operation and can be used on
2929 changesets in the public phase. But if the stripped changesets have
2930 been pushed to a remote repository you will likely pull them again.
2931
2928 Return 0 on success.
2932 Return 0 on success.
2929 """
2933 """
2930 backup = 'all'
2934 backup = 'all'
2931 if opts.get('backup'):
2935 if opts.get('backup'):
2932 backup = 'strip'
2936 backup = 'strip'
2933 elif opts.get('no_backup') or opts.get('nobackup'):
2937 elif opts.get('no_backup') or opts.get('nobackup'):
2934 backup = 'none'
2938 backup = 'none'
2935
2939
2936 cl = repo.changelog
2940 cl = repo.changelog
2937 revs = list(revs) + opts.get('rev')
2941 revs = list(revs) + opts.get('rev')
2938 revs = set(scmutil.revrange(repo, revs))
2942 revs = set(scmutil.revrange(repo, revs))
2939
2943
2940 if opts.get('bookmark'):
2944 if opts.get('bookmark'):
2941 mark = opts.get('bookmark')
2945 mark = opts.get('bookmark')
2942 marks = repo._bookmarks
2946 marks = repo._bookmarks
2943 if mark not in marks:
2947 if mark not in marks:
2944 raise util.Abort(_("bookmark '%s' not found") % mark)
2948 raise util.Abort(_("bookmark '%s' not found") % mark)
2945
2949
2946 # If the requested bookmark is not the only one pointing to a
2950 # If the requested bookmark is not the only one pointing to a
2947 # a revision we have to only delete the bookmark and not strip
2951 # a revision we have to only delete the bookmark and not strip
2948 # anything. revsets cannot detect that case.
2952 # anything. revsets cannot detect that case.
2949 uniquebm = True
2953 uniquebm = True
2950 for m, n in marks.iteritems():
2954 for m, n in marks.iteritems():
2951 if m != mark and n == repo[mark].node():
2955 if m != mark and n == repo[mark].node():
2952 uniquebm = False
2956 uniquebm = False
2953 break
2957 break
2954 if uniquebm:
2958 if uniquebm:
2955 rsrevs = repo.revs("ancestors(bookmark(%s)) - "
2959 rsrevs = repo.revs("ancestors(bookmark(%s)) - "
2956 "ancestors(head() and not bookmark(%s)) - "
2960 "ancestors(head() and not bookmark(%s)) - "
2957 "ancestors(bookmark() and not bookmark(%s))",
2961 "ancestors(bookmark() and not bookmark(%s))",
2958 mark, mark, mark)
2962 mark, mark, mark)
2959 revs.update(set(rsrevs))
2963 revs.update(set(rsrevs))
2960 if not revs:
2964 if not revs:
2961 del marks[mark]
2965 del marks[mark]
2962 repo._writebookmarks(mark)
2966 repo._writebookmarks(mark)
2963 ui.write(_("bookmark '%s' deleted\n") % mark)
2967 ui.write(_("bookmark '%s' deleted\n") % mark)
2964
2968
2965 if not revs:
2969 if not revs:
2966 raise util.Abort(_('empty revision set'))
2970 raise util.Abort(_('empty revision set'))
2967
2971
2968 descendants = set(cl.descendants(revs))
2972 descendants = set(cl.descendants(revs))
2969 strippedrevs = revs.union(descendants)
2973 strippedrevs = revs.union(descendants)
2970 roots = revs.difference(descendants)
2974 roots = revs.difference(descendants)
2971
2975
2972 update = False
2976 update = False
2973 # if one of the wdir parent is stripped we'll need
2977 # if one of the wdir parent is stripped we'll need
2974 # to update away to an earlier revision
2978 # to update away to an earlier revision
2975 for p in repo.dirstate.parents():
2979 for p in repo.dirstate.parents():
2976 if p != nullid and cl.rev(p) in strippedrevs:
2980 if p != nullid and cl.rev(p) in strippedrevs:
2977 update = True
2981 update = True
2978 break
2982 break
2979
2983
2980 rootnodes = set(cl.node(r) for r in roots)
2984 rootnodes = set(cl.node(r) for r in roots)
2981
2985
2982 q = repo.mq
2986 q = repo.mq
2983 if q.applied:
2987 if q.applied:
2984 # refresh queue state if we're about to strip
2988 # refresh queue state if we're about to strip
2985 # applied patches
2989 # applied patches
2986 if cl.rev(repo.lookup('qtip')) in strippedrevs:
2990 if cl.rev(repo.lookup('qtip')) in strippedrevs:
2987 q.applieddirty = True
2991 q.applieddirty = True
2988 start = 0
2992 start = 0
2989 end = len(q.applied)
2993 end = len(q.applied)
2990 for i, statusentry in enumerate(q.applied):
2994 for i, statusentry in enumerate(q.applied):
2991 if statusentry.node in rootnodes:
2995 if statusentry.node in rootnodes:
2992 # if one of the stripped roots is an applied
2996 # if one of the stripped roots is an applied
2993 # patch, only part of the queue is stripped
2997 # patch, only part of the queue is stripped
2994 start = i
2998 start = i
2995 break
2999 break
2996 del q.applied[start:end]
3000 del q.applied[start:end]
2997 q.savedirty()
3001 q.savedirty()
2998
3002
2999 revs = list(rootnodes)
3003 revs = list(rootnodes)
3000 if update and opts.get('keep'):
3004 if update and opts.get('keep'):
3001 wlock = repo.wlock()
3005 wlock = repo.wlock()
3002 try:
3006 try:
3003 urev = repo.mq.qparents(repo, revs[0])
3007 urev = repo.mq.qparents(repo, revs[0])
3004 repo.dirstate.rebuild(urev, repo[urev].manifest())
3008 repo.dirstate.rebuild(urev, repo[urev].manifest())
3005 repo.dirstate.write()
3009 repo.dirstate.write()
3006 update = False
3010 update = False
3007 finally:
3011 finally:
3008 wlock.release()
3012 wlock.release()
3009
3013
3010 if opts.get('bookmark'):
3014 if opts.get('bookmark'):
3011 del marks[mark]
3015 del marks[mark]
3012 repo._writebookmarks(marks)
3016 repo._writebookmarks(marks)
3013 ui.write(_("bookmark '%s' deleted\n") % mark)
3017 ui.write(_("bookmark '%s' deleted\n") % mark)
3014
3018
3015 repo.mq.strip(repo, revs, backup=backup, update=update,
3019 repo.mq.strip(repo, revs, backup=backup, update=update,
3016 force=opts.get('force'))
3020 force=opts.get('force'))
3017
3021
3018 return 0
3022 return 0
3019
3023
3020 @command("qselect",
3024 @command("qselect",
3021 [('n', 'none', None, _('disable all guards')),
3025 [('n', 'none', None, _('disable all guards')),
3022 ('s', 'series', None, _('list all guards in series file')),
3026 ('s', 'series', None, _('list all guards in series file')),
3023 ('', 'pop', None, _('pop to before first guarded applied patch')),
3027 ('', 'pop', None, _('pop to before first guarded applied patch')),
3024 ('', 'reapply', None, _('pop, then reapply patches'))],
3028 ('', 'reapply', None, _('pop, then reapply patches'))],
3025 _('hg qselect [OPTION]... [GUARD]...'))
3029 _('hg qselect [OPTION]... [GUARD]...'))
3026 def select(ui, repo, *args, **opts):
3030 def select(ui, repo, *args, **opts):
3027 '''set or print guarded patches to push
3031 '''set or print guarded patches to push
3028
3032
3029 Use the :hg:`qguard` command to set or print guards on patch, then use
3033 Use the :hg:`qguard` command to set or print guards on patch, then use
3030 qselect to tell mq which guards to use. A patch will be pushed if
3034 qselect to tell mq which guards to use. A patch will be pushed if
3031 it has no guards or any positive guards match the currently
3035 it has no guards or any positive guards match the currently
3032 selected guard, but will not be pushed if any negative guards
3036 selected guard, but will not be pushed if any negative guards
3033 match the current guard. For example::
3037 match the current guard. For example::
3034
3038
3035 qguard foo.patch -- -stable (negative guard)
3039 qguard foo.patch -- -stable (negative guard)
3036 qguard bar.patch +stable (positive guard)
3040 qguard bar.patch +stable (positive guard)
3037 qselect stable
3041 qselect stable
3038
3042
3039 This activates the "stable" guard. mq will skip foo.patch (because
3043 This activates the "stable" guard. mq will skip foo.patch (because
3040 it has a negative match) but push bar.patch (because it has a
3044 it has a negative match) but push bar.patch (because it has a
3041 positive match).
3045 positive match).
3042
3046
3043 With no arguments, prints the currently active guards.
3047 With no arguments, prints the currently active guards.
3044 With one argument, sets the active guard.
3048 With one argument, sets the active guard.
3045
3049
3046 Use -n/--none to deactivate guards (no other arguments needed).
3050 Use -n/--none to deactivate guards (no other arguments needed).
3047 When no guards are active, patches with positive guards are
3051 When no guards are active, patches with positive guards are
3048 skipped and patches with negative guards are pushed.
3052 skipped and patches with negative guards are pushed.
3049
3053
3050 qselect can change the guards on applied patches. It does not pop
3054 qselect can change the guards on applied patches. It does not pop
3051 guarded patches by default. Use --pop to pop back to the last
3055 guarded patches by default. Use --pop to pop back to the last
3052 applied patch that is not guarded. Use --reapply (which implies
3056 applied patch that is not guarded. Use --reapply (which implies
3053 --pop) to push back to the current patch afterwards, but skip
3057 --pop) to push back to the current patch afterwards, but skip
3054 guarded patches.
3058 guarded patches.
3055
3059
3056 Use -s/--series to print a list of all guards in the series file
3060 Use -s/--series to print a list of all guards in the series file
3057 (no other arguments needed). Use -v for more information.
3061 (no other arguments needed). Use -v for more information.
3058
3062
3059 Returns 0 on success.'''
3063 Returns 0 on success.'''
3060
3064
3061 q = repo.mq
3065 q = repo.mq
3062 guards = q.active()
3066 guards = q.active()
3063 if args or opts.get('none'):
3067 if args or opts.get('none'):
3064 old_unapplied = q.unapplied(repo)
3068 old_unapplied = q.unapplied(repo)
3065 old_guarded = [i for i in xrange(len(q.applied)) if
3069 old_guarded = [i for i in xrange(len(q.applied)) if
3066 not q.pushable(i)[0]]
3070 not q.pushable(i)[0]]
3067 q.setactive(args)
3071 q.setactive(args)
3068 q.savedirty()
3072 q.savedirty()
3069 if not args:
3073 if not args:
3070 ui.status(_('guards deactivated\n'))
3074 ui.status(_('guards deactivated\n'))
3071 if not opts.get('pop') and not opts.get('reapply'):
3075 if not opts.get('pop') and not opts.get('reapply'):
3072 unapplied = q.unapplied(repo)
3076 unapplied = q.unapplied(repo)
3073 guarded = [i for i in xrange(len(q.applied))
3077 guarded = [i for i in xrange(len(q.applied))
3074 if not q.pushable(i)[0]]
3078 if not q.pushable(i)[0]]
3075 if len(unapplied) != len(old_unapplied):
3079 if len(unapplied) != len(old_unapplied):
3076 ui.status(_('number of unguarded, unapplied patches has '
3080 ui.status(_('number of unguarded, unapplied patches has '
3077 'changed from %d to %d\n') %
3081 'changed from %d to %d\n') %
3078 (len(old_unapplied), len(unapplied)))
3082 (len(old_unapplied), len(unapplied)))
3079 if len(guarded) != len(old_guarded):
3083 if len(guarded) != len(old_guarded):
3080 ui.status(_('number of guarded, applied patches has changed '
3084 ui.status(_('number of guarded, applied patches has changed '
3081 'from %d to %d\n') %
3085 'from %d to %d\n') %
3082 (len(old_guarded), len(guarded)))
3086 (len(old_guarded), len(guarded)))
3083 elif opts.get('series'):
3087 elif opts.get('series'):
3084 guards = {}
3088 guards = {}
3085 noguards = 0
3089 noguards = 0
3086 for gs in q.seriesguards:
3090 for gs in q.seriesguards:
3087 if not gs:
3091 if not gs:
3088 noguards += 1
3092 noguards += 1
3089 for g in gs:
3093 for g in gs:
3090 guards.setdefault(g, 0)
3094 guards.setdefault(g, 0)
3091 guards[g] += 1
3095 guards[g] += 1
3092 if ui.verbose:
3096 if ui.verbose:
3093 guards['NONE'] = noguards
3097 guards['NONE'] = noguards
3094 guards = guards.items()
3098 guards = guards.items()
3095 guards.sort(key=lambda x: x[0][1:])
3099 guards.sort(key=lambda x: x[0][1:])
3096 if guards:
3100 if guards:
3097 ui.note(_('guards in series file:\n'))
3101 ui.note(_('guards in series file:\n'))
3098 for guard, count in guards:
3102 for guard, count in guards:
3099 ui.note('%2d ' % count)
3103 ui.note('%2d ' % count)
3100 ui.write(guard, '\n')
3104 ui.write(guard, '\n')
3101 else:
3105 else:
3102 ui.note(_('no guards in series file\n'))
3106 ui.note(_('no guards in series file\n'))
3103 else:
3107 else:
3104 if guards:
3108 if guards:
3105 ui.note(_('active guards:\n'))
3109 ui.note(_('active guards:\n'))
3106 for g in guards:
3110 for g in guards:
3107 ui.write(g, '\n')
3111 ui.write(g, '\n')
3108 else:
3112 else:
3109 ui.write(_('no active guards\n'))
3113 ui.write(_('no active guards\n'))
3110 reapply = opts.get('reapply') and q.applied and q.appliedname(-1)
3114 reapply = opts.get('reapply') and q.applied and q.appliedname(-1)
3111 popped = False
3115 popped = False
3112 if opts.get('pop') or opts.get('reapply'):
3116 if opts.get('pop') or opts.get('reapply'):
3113 for i in xrange(len(q.applied)):
3117 for i in xrange(len(q.applied)):
3114 pushable, reason = q.pushable(i)
3118 pushable, reason = q.pushable(i)
3115 if not pushable:
3119 if not pushable:
3116 ui.status(_('popping guarded patches\n'))
3120 ui.status(_('popping guarded patches\n'))
3117 popped = True
3121 popped = True
3118 if i == 0:
3122 if i == 0:
3119 q.pop(repo, all=True)
3123 q.pop(repo, all=True)
3120 else:
3124 else:
3121 q.pop(repo, str(i - 1))
3125 q.pop(repo, str(i - 1))
3122 break
3126 break
3123 if popped:
3127 if popped:
3124 try:
3128 try:
3125 if reapply:
3129 if reapply:
3126 ui.status(_('reapplying unguarded patches\n'))
3130 ui.status(_('reapplying unguarded patches\n'))
3127 q.push(repo, reapply)
3131 q.push(repo, reapply)
3128 finally:
3132 finally:
3129 q.savedirty()
3133 q.savedirty()
3130
3134
3131 @command("qfinish",
3135 @command("qfinish",
3132 [('a', 'applied', None, _('finish all applied changesets'))],
3136 [('a', 'applied', None, _('finish all applied changesets'))],
3133 _('hg qfinish [-a] [REV]...'))
3137 _('hg qfinish [-a] [REV]...'))
3134 def finish(ui, repo, *revrange, **opts):
3138 def finish(ui, repo, *revrange, **opts):
3135 """move applied patches into repository history
3139 """move applied patches into repository history
3136
3140
3137 Finishes the specified revisions (corresponding to applied
3141 Finishes the specified revisions (corresponding to applied
3138 patches) by moving them out of mq control into regular repository
3142 patches) by moving them out of mq control into regular repository
3139 history.
3143 history.
3140
3144
3141 Accepts a revision range or the -a/--applied option. If --applied
3145 Accepts a revision range or the -a/--applied option. If --applied
3142 is specified, all applied mq revisions are removed from mq
3146 is specified, all applied mq revisions are removed from mq
3143 control. Otherwise, the given revisions must be at the base of the
3147 control. Otherwise, the given revisions must be at the base of the
3144 stack of applied patches.
3148 stack of applied patches.
3145
3149
3146 This can be especially useful if your changes have been applied to
3150 This can be especially useful if your changes have been applied to
3147 an upstream repository, or if you are about to push your changes
3151 an upstream repository, or if you are about to push your changes
3148 to upstream.
3152 to upstream.
3149
3153
3150 Returns 0 on success.
3154 Returns 0 on success.
3151 """
3155 """
3152 if not opts.get('applied') and not revrange:
3156 if not opts.get('applied') and not revrange:
3153 raise util.Abort(_('no revisions specified'))
3157 raise util.Abort(_('no revisions specified'))
3154 elif opts.get('applied'):
3158 elif opts.get('applied'):
3155 revrange = ('qbase::qtip',) + revrange
3159 revrange = ('qbase::qtip',) + revrange
3156
3160
3157 q = repo.mq
3161 q = repo.mq
3158 if not q.applied:
3162 if not q.applied:
3159 ui.status(_('no patches applied\n'))
3163 ui.status(_('no patches applied\n'))
3160 return 0
3164 return 0
3161
3165
3162 revs = scmutil.revrange(repo, revrange)
3166 revs = scmutil.revrange(repo, revrange)
3163 if repo['.'].rev() in revs and repo[None].files():
3167 if repo['.'].rev() in revs and repo[None].files():
3164 ui.warn(_('warning: uncommitted changes in the working directory\n'))
3168 ui.warn(_('warning: uncommitted changes in the working directory\n'))
3165 # queue.finish may changes phases but leave the responsability to lock the
3169 # queue.finish may changes phases but leave the responsability to lock the
3166 # repo to the caller to avoid deadlock with wlock. This command code is
3170 # repo to the caller to avoid deadlock with wlock. This command code is
3167 # responsability for this locking.
3171 # responsability for this locking.
3168 lock = repo.lock()
3172 lock = repo.lock()
3169 try:
3173 try:
3170 q.finish(repo, revs)
3174 q.finish(repo, revs)
3171 q.savedirty()
3175 q.savedirty()
3172 finally:
3176 finally:
3173 lock.release()
3177 lock.release()
3174 return 0
3178 return 0
3175
3179
3176 @command("qqueue",
3180 @command("qqueue",
3177 [('l', 'list', False, _('list all available queues')),
3181 [('l', 'list', False, _('list all available queues')),
3178 ('', 'active', False, _('print name of active queue')),
3182 ('', 'active', False, _('print name of active queue')),
3179 ('c', 'create', False, _('create new queue')),
3183 ('c', 'create', False, _('create new queue')),
3180 ('', 'rename', False, _('rename active queue')),
3184 ('', 'rename', False, _('rename active queue')),
3181 ('', 'delete', False, _('delete reference to queue')),
3185 ('', 'delete', False, _('delete reference to queue')),
3182 ('', 'purge', False, _('delete queue, and remove patch dir')),
3186 ('', 'purge', False, _('delete queue, and remove patch dir')),
3183 ],
3187 ],
3184 _('[OPTION] [QUEUE]'))
3188 _('[OPTION] [QUEUE]'))
3185 def qqueue(ui, repo, name=None, **opts):
3189 def qqueue(ui, repo, name=None, **opts):
3186 '''manage multiple patch queues
3190 '''manage multiple patch queues
3187
3191
3188 Supports switching between different patch queues, as well as creating
3192 Supports switching between different patch queues, as well as creating
3189 new patch queues and deleting existing ones.
3193 new patch queues and deleting existing ones.
3190
3194
3191 Omitting a queue name or specifying -l/--list will show you the registered
3195 Omitting a queue name or specifying -l/--list will show you the registered
3192 queues - by default the "normal" patches queue is registered. The currently
3196 queues - by default the "normal" patches queue is registered. The currently
3193 active queue will be marked with "(active)". Specifying --active will print
3197 active queue will be marked with "(active)". Specifying --active will print
3194 only the name of the active queue.
3198 only the name of the active queue.
3195
3199
3196 To create a new queue, use -c/--create. The queue is automatically made
3200 To create a new queue, use -c/--create. The queue is automatically made
3197 active, except in the case where there are applied patches from the
3201 active, except in the case where there are applied patches from the
3198 currently active queue in the repository. Then the queue will only be
3202 currently active queue in the repository. Then the queue will only be
3199 created and switching will fail.
3203 created and switching will fail.
3200
3204
3201 To delete an existing queue, use --delete. You cannot delete the currently
3205 To delete an existing queue, use --delete. You cannot delete the currently
3202 active queue.
3206 active queue.
3203
3207
3204 Returns 0 on success.
3208 Returns 0 on success.
3205 '''
3209 '''
3206 q = repo.mq
3210 q = repo.mq
3207 _defaultqueue = 'patches'
3211 _defaultqueue = 'patches'
3208 _allqueues = 'patches.queues'
3212 _allqueues = 'patches.queues'
3209 _activequeue = 'patches.queue'
3213 _activequeue = 'patches.queue'
3210
3214
3211 def _getcurrent():
3215 def _getcurrent():
3212 cur = os.path.basename(q.path)
3216 cur = os.path.basename(q.path)
3213 if cur.startswith('patches-'):
3217 if cur.startswith('patches-'):
3214 cur = cur[8:]
3218 cur = cur[8:]
3215 return cur
3219 return cur
3216
3220
3217 def _noqueues():
3221 def _noqueues():
3218 try:
3222 try:
3219 fh = repo.opener(_allqueues, 'r')
3223 fh = repo.opener(_allqueues, 'r')
3220 fh.close()
3224 fh.close()
3221 except IOError:
3225 except IOError:
3222 return True
3226 return True
3223
3227
3224 return False
3228 return False
3225
3229
3226 def _getqueues():
3230 def _getqueues():
3227 current = _getcurrent()
3231 current = _getcurrent()
3228
3232
3229 try:
3233 try:
3230 fh = repo.opener(_allqueues, 'r')
3234 fh = repo.opener(_allqueues, 'r')
3231 queues = [queue.strip() for queue in fh if queue.strip()]
3235 queues = [queue.strip() for queue in fh if queue.strip()]
3232 fh.close()
3236 fh.close()
3233 if current not in queues:
3237 if current not in queues:
3234 queues.append(current)
3238 queues.append(current)
3235 except IOError:
3239 except IOError:
3236 queues = [_defaultqueue]
3240 queues = [_defaultqueue]
3237
3241
3238 return sorted(queues)
3242 return sorted(queues)
3239
3243
3240 def _setactive(name):
3244 def _setactive(name):
3241 if q.applied:
3245 if q.applied:
3242 raise util.Abort(_('patches applied - cannot set new queue active'))
3246 raise util.Abort(_('patches applied - cannot set new queue active'))
3243 _setactivenocheck(name)
3247 _setactivenocheck(name)
3244
3248
3245 def _setactivenocheck(name):
3249 def _setactivenocheck(name):
3246 fh = repo.opener(_activequeue, 'w')
3250 fh = repo.opener(_activequeue, 'w')
3247 if name != 'patches':
3251 if name != 'patches':
3248 fh.write(name)
3252 fh.write(name)
3249 fh.close()
3253 fh.close()
3250
3254
3251 def _addqueue(name):
3255 def _addqueue(name):
3252 fh = repo.opener(_allqueues, 'a')
3256 fh = repo.opener(_allqueues, 'a')
3253 fh.write('%s\n' % (name,))
3257 fh.write('%s\n' % (name,))
3254 fh.close()
3258 fh.close()
3255
3259
3256 def _queuedir(name):
3260 def _queuedir(name):
3257 if name == 'patches':
3261 if name == 'patches':
3258 return repo.join('patches')
3262 return repo.join('patches')
3259 else:
3263 else:
3260 return repo.join('patches-' + name)
3264 return repo.join('patches-' + name)
3261
3265
3262 def _validname(name):
3266 def _validname(name):
3263 for n in name:
3267 for n in name:
3264 if n in ':\\/.':
3268 if n in ':\\/.':
3265 return False
3269 return False
3266 return True
3270 return True
3267
3271
3268 def _delete(name):
3272 def _delete(name):
3269 if name not in existing:
3273 if name not in existing:
3270 raise util.Abort(_('cannot delete queue that does not exist'))
3274 raise util.Abort(_('cannot delete queue that does not exist'))
3271
3275
3272 current = _getcurrent()
3276 current = _getcurrent()
3273
3277
3274 if name == current:
3278 if name == current:
3275 raise util.Abort(_('cannot delete currently active queue'))
3279 raise util.Abort(_('cannot delete currently active queue'))
3276
3280
3277 fh = repo.opener('patches.queues.new', 'w')
3281 fh = repo.opener('patches.queues.new', 'w')
3278 for queue in existing:
3282 for queue in existing:
3279 if queue == name:
3283 if queue == name:
3280 continue
3284 continue
3281 fh.write('%s\n' % (queue,))
3285 fh.write('%s\n' % (queue,))
3282 fh.close()
3286 fh.close()
3283 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3287 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3284
3288
3285 if not name or opts.get('list') or opts.get('active'):
3289 if not name or opts.get('list') or opts.get('active'):
3286 current = _getcurrent()
3290 current = _getcurrent()
3287 if opts.get('active'):
3291 if opts.get('active'):
3288 ui.write('%s\n' % (current,))
3292 ui.write('%s\n' % (current,))
3289 return
3293 return
3290 for queue in _getqueues():
3294 for queue in _getqueues():
3291 ui.write('%s' % (queue,))
3295 ui.write('%s' % (queue,))
3292 if queue == current and not ui.quiet:
3296 if queue == current and not ui.quiet:
3293 ui.write(_(' (active)\n'))
3297 ui.write(_(' (active)\n'))
3294 else:
3298 else:
3295 ui.write('\n')
3299 ui.write('\n')
3296 return
3300 return
3297
3301
3298 if not _validname(name):
3302 if not _validname(name):
3299 raise util.Abort(
3303 raise util.Abort(
3300 _('invalid queue name, may not contain the characters ":\\/."'))
3304 _('invalid queue name, may not contain the characters ":\\/."'))
3301
3305
3302 existing = _getqueues()
3306 existing = _getqueues()
3303
3307
3304 if opts.get('create'):
3308 if opts.get('create'):
3305 if name in existing:
3309 if name in existing:
3306 raise util.Abort(_('queue "%s" already exists') % name)
3310 raise util.Abort(_('queue "%s" already exists') % name)
3307 if _noqueues():
3311 if _noqueues():
3308 _addqueue(_defaultqueue)
3312 _addqueue(_defaultqueue)
3309 _addqueue(name)
3313 _addqueue(name)
3310 _setactive(name)
3314 _setactive(name)
3311 elif opts.get('rename'):
3315 elif opts.get('rename'):
3312 current = _getcurrent()
3316 current = _getcurrent()
3313 if name == current:
3317 if name == current:
3314 raise util.Abort(_('can\'t rename "%s" to its current name') % name)
3318 raise util.Abort(_('can\'t rename "%s" to its current name') % name)
3315 if name in existing:
3319 if name in existing:
3316 raise util.Abort(_('queue "%s" already exists') % name)
3320 raise util.Abort(_('queue "%s" already exists') % name)
3317
3321
3318 olddir = _queuedir(current)
3322 olddir = _queuedir(current)
3319 newdir = _queuedir(name)
3323 newdir = _queuedir(name)
3320
3324
3321 if os.path.exists(newdir):
3325 if os.path.exists(newdir):
3322 raise util.Abort(_('non-queue directory "%s" already exists') %
3326 raise util.Abort(_('non-queue directory "%s" already exists') %
3323 newdir)
3327 newdir)
3324
3328
3325 fh = repo.opener('patches.queues.new', 'w')
3329 fh = repo.opener('patches.queues.new', 'w')
3326 for queue in existing:
3330 for queue in existing:
3327 if queue == current:
3331 if queue == current:
3328 fh.write('%s\n' % (name,))
3332 fh.write('%s\n' % (name,))
3329 if os.path.exists(olddir):
3333 if os.path.exists(olddir):
3330 util.rename(olddir, newdir)
3334 util.rename(olddir, newdir)
3331 else:
3335 else:
3332 fh.write('%s\n' % (queue,))
3336 fh.write('%s\n' % (queue,))
3333 fh.close()
3337 fh.close()
3334 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3338 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3335 _setactivenocheck(name)
3339 _setactivenocheck(name)
3336 elif opts.get('delete'):
3340 elif opts.get('delete'):
3337 _delete(name)
3341 _delete(name)
3338 elif opts.get('purge'):
3342 elif opts.get('purge'):
3339 if name in existing:
3343 if name in existing:
3340 _delete(name)
3344 _delete(name)
3341 qdir = _queuedir(name)
3345 qdir = _queuedir(name)
3342 if os.path.exists(qdir):
3346 if os.path.exists(qdir):
3343 shutil.rmtree(qdir)
3347 shutil.rmtree(qdir)
3344 else:
3348 else:
3345 if name not in existing:
3349 if name not in existing:
3346 raise util.Abort(_('use --create to create a new queue'))
3350 raise util.Abort(_('use --create to create a new queue'))
3347 _setactive(name)
3351 _setactive(name)
3348
3352
3349 def mqphasedefaults(repo, roots):
3353 def mqphasedefaults(repo, roots):
3350 """callback used to set mq changeset as secret when no phase data exists"""
3354 """callback used to set mq changeset as secret when no phase data exists"""
3351 if repo.mq.applied:
3355 if repo.mq.applied:
3352 if repo.ui.configbool('mq', 'secret', False):
3356 if repo.ui.configbool('mq', 'secret', False):
3353 mqphase = phases.secret
3357 mqphase = phases.secret
3354 else:
3358 else:
3355 mqphase = phases.draft
3359 mqphase = phases.draft
3356 qbase = repo[repo.mq.applied[0].node]
3360 qbase = repo[repo.mq.applied[0].node]
3357 roots[mqphase].add(qbase.node())
3361 roots[mqphase].add(qbase.node())
3358 return roots
3362 return roots
3359
3363
3360 def reposetup(ui, repo):
3364 def reposetup(ui, repo):
3361 class mqrepo(repo.__class__):
3365 class mqrepo(repo.__class__):
3362 @util.propertycache
3366 @util.propertycache
3363 def mq(self):
3367 def mq(self):
3364 return queue(self.ui, self.path)
3368 return queue(self.ui, self.path)
3365
3369
3366 def abortifwdirpatched(self, errmsg, force=False):
3370 def abortifwdirpatched(self, errmsg, force=False):
3367 if self.mq.applied and not force:
3371 if self.mq.applied and not force:
3368 parents = self.dirstate.parents()
3372 parents = self.dirstate.parents()
3369 patches = [s.node for s in self.mq.applied]
3373 patches = [s.node for s in self.mq.applied]
3370 if parents[0] in patches or parents[1] in patches:
3374 if parents[0] in patches or parents[1] in patches:
3371 raise util.Abort(errmsg)
3375 raise util.Abort(errmsg)
3372
3376
3373 def commit(self, text="", user=None, date=None, match=None,
3377 def commit(self, text="", user=None, date=None, match=None,
3374 force=False, editor=False, extra={}):
3378 force=False, editor=False, extra={}):
3375 self.abortifwdirpatched(
3379 self.abortifwdirpatched(
3376 _('cannot commit over an applied mq patch'),
3380 _('cannot commit over an applied mq patch'),
3377 force)
3381 force)
3378
3382
3379 return super(mqrepo, self).commit(text, user, date, match, force,
3383 return super(mqrepo, self).commit(text, user, date, match, force,
3380 editor, extra)
3384 editor, extra)
3381
3385
3382 def checkpush(self, force, revs):
3386 def checkpush(self, force, revs):
3383 if self.mq.applied and not force:
3387 if self.mq.applied and not force:
3384 outapplied = [e.node for e in self.mq.applied]
3388 outapplied = [e.node for e in self.mq.applied]
3385 if revs:
3389 if revs:
3386 # Assume applied patches have no non-patch descendants and
3390 # Assume applied patches have no non-patch descendants and
3387 # are not on remote already. Filtering any changeset not
3391 # are not on remote already. Filtering any changeset not
3388 # pushed.
3392 # pushed.
3389 heads = set(revs)
3393 heads = set(revs)
3390 for node in reversed(outapplied):
3394 for node in reversed(outapplied):
3391 if node in heads:
3395 if node in heads:
3392 break
3396 break
3393 else:
3397 else:
3394 outapplied.pop()
3398 outapplied.pop()
3395 # looking for pushed and shared changeset
3399 # looking for pushed and shared changeset
3396 for node in outapplied:
3400 for node in outapplied:
3397 if repo[node].phase() < phases.secret:
3401 if repo[node].phase() < phases.secret:
3398 raise util.Abort(_('source has mq patches applied'))
3402 raise util.Abort(_('source has mq patches applied'))
3399 # no non-secret patches pushed
3403 # no non-secret patches pushed
3400 super(mqrepo, self).checkpush(force, revs)
3404 super(mqrepo, self).checkpush(force, revs)
3401
3405
3402 def _findtags(self):
3406 def _findtags(self):
3403 '''augment tags from base class with patch tags'''
3407 '''augment tags from base class with patch tags'''
3404 result = super(mqrepo, self)._findtags()
3408 result = super(mqrepo, self)._findtags()
3405
3409
3406 q = self.mq
3410 q = self.mq
3407 if not q.applied:
3411 if not q.applied:
3408 return result
3412 return result
3409
3413
3410 mqtags = [(patch.node, patch.name) for patch in q.applied]
3414 mqtags = [(patch.node, patch.name) for patch in q.applied]
3411
3415
3412 try:
3416 try:
3413 self.changelog.rev(mqtags[-1][0])
3417 self.changelog.rev(mqtags[-1][0])
3414 except error.LookupError:
3418 except error.LookupError:
3415 self.ui.warn(_('mq status file refers to unknown node %s\n')
3419 self.ui.warn(_('mq status file refers to unknown node %s\n')
3416 % short(mqtags[-1][0]))
3420 % short(mqtags[-1][0]))
3417 return result
3421 return result
3418
3422
3419 mqtags.append((mqtags[-1][0], 'qtip'))
3423 mqtags.append((mqtags[-1][0], 'qtip'))
3420 mqtags.append((mqtags[0][0], 'qbase'))
3424 mqtags.append((mqtags[0][0], 'qbase'))
3421 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
3425 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
3422 tags = result[0]
3426 tags = result[0]
3423 for patch in mqtags:
3427 for patch in mqtags:
3424 if patch[1] in tags:
3428 if patch[1] in tags:
3425 self.ui.warn(_('tag %s overrides mq patch of the same '
3429 self.ui.warn(_('tag %s overrides mq patch of the same '
3426 'name\n') % patch[1])
3430 'name\n') % patch[1])
3427 else:
3431 else:
3428 tags[patch[1]] = patch[0]
3432 tags[patch[1]] = patch[0]
3429
3433
3430 return result
3434 return result
3431
3435
3432 def _branchtags(self, partial, lrev):
3436 def _branchtags(self, partial, lrev):
3433 q = self.mq
3437 q = self.mq
3434 cl = self.changelog
3438 cl = self.changelog
3435 qbase = None
3439 qbase = None
3436 if not q.applied:
3440 if not q.applied:
3437 if getattr(self, '_committingpatch', False):
3441 if getattr(self, '_committingpatch', False):
3438 # Committing a new patch, must be tip
3442 # Committing a new patch, must be tip
3439 qbase = len(cl) - 1
3443 qbase = len(cl) - 1
3440 else:
3444 else:
3441 qbasenode = q.applied[0].node
3445 qbasenode = q.applied[0].node
3442 try:
3446 try:
3443 qbase = cl.rev(qbasenode)
3447 qbase = cl.rev(qbasenode)
3444 except error.LookupError:
3448 except error.LookupError:
3445 self.ui.warn(_('mq status file refers to unknown node %s\n')
3449 self.ui.warn(_('mq status file refers to unknown node %s\n')
3446 % short(qbasenode))
3450 % short(qbasenode))
3447 if qbase is None:
3451 if qbase is None:
3448 return super(mqrepo, self)._branchtags(partial, lrev)
3452 return super(mqrepo, self)._branchtags(partial, lrev)
3449
3453
3450 start = lrev + 1
3454 start = lrev + 1
3451 if start < qbase:
3455 if start < qbase:
3452 # update the cache (excluding the patches) and save it
3456 # update the cache (excluding the patches) and save it
3453 ctxgen = (self[r] for r in xrange(lrev + 1, qbase))
3457 ctxgen = (self[r] for r in xrange(lrev + 1, qbase))
3454 self._updatebranchcache(partial, ctxgen)
3458 self._updatebranchcache(partial, ctxgen)
3455 self._writebranchcache(partial, cl.node(qbase - 1), qbase - 1)
3459 self._writebranchcache(partial, cl.node(qbase - 1), qbase - 1)
3456 start = qbase
3460 start = qbase
3457 # if start = qbase, the cache is as updated as it should be.
3461 # if start = qbase, the cache is as updated as it should be.
3458 # if start > qbase, the cache includes (part of) the patches.
3462 # if start > qbase, the cache includes (part of) the patches.
3459 # we might as well use it, but we won't save it.
3463 # we might as well use it, but we won't save it.
3460
3464
3461 # update the cache up to the tip
3465 # update the cache up to the tip
3462 ctxgen = (self[r] for r in xrange(start, len(cl)))
3466 ctxgen = (self[r] for r in xrange(start, len(cl)))
3463 self._updatebranchcache(partial, ctxgen)
3467 self._updatebranchcache(partial, ctxgen)
3464
3468
3465 return partial
3469 return partial
3466
3470
3467 if repo.local():
3471 if repo.local():
3468 repo.__class__ = mqrepo
3472 repo.__class__ = mqrepo
3469
3473
3470 repo._phasedefaults.append(mqphasedefaults)
3474 repo._phasedefaults.append(mqphasedefaults)
3471
3475
3472 def mqimport(orig, ui, repo, *args, **kwargs):
3476 def mqimport(orig, ui, repo, *args, **kwargs):
3473 if (util.safehasattr(repo, 'abortifwdirpatched')
3477 if (util.safehasattr(repo, 'abortifwdirpatched')
3474 and not kwargs.get('no_commit', False)):
3478 and not kwargs.get('no_commit', False)):
3475 repo.abortifwdirpatched(_('cannot import over an applied patch'),
3479 repo.abortifwdirpatched(_('cannot import over an applied patch'),
3476 kwargs.get('force'))
3480 kwargs.get('force'))
3477 return orig(ui, repo, *args, **kwargs)
3481 return orig(ui, repo, *args, **kwargs)
3478
3482
3479 def mqinit(orig, ui, *args, **kwargs):
3483 def mqinit(orig, ui, *args, **kwargs):
3480 mq = kwargs.pop('mq', None)
3484 mq = kwargs.pop('mq', None)
3481
3485
3482 if not mq:
3486 if not mq:
3483 return orig(ui, *args, **kwargs)
3487 return orig(ui, *args, **kwargs)
3484
3488
3485 if args:
3489 if args:
3486 repopath = args[0]
3490 repopath = args[0]
3487 if not hg.islocal(repopath):
3491 if not hg.islocal(repopath):
3488 raise util.Abort(_('only a local queue repository '
3492 raise util.Abort(_('only a local queue repository '
3489 'may be initialized'))
3493 'may be initialized'))
3490 else:
3494 else:
3491 repopath = cmdutil.findrepo(os.getcwd())
3495 repopath = cmdutil.findrepo(os.getcwd())
3492 if not repopath:
3496 if not repopath:
3493 raise util.Abort(_('there is no Mercurial repository here '
3497 raise util.Abort(_('there is no Mercurial repository here '
3494 '(.hg not found)'))
3498 '(.hg not found)'))
3495 repo = hg.repository(ui, repopath)
3499 repo = hg.repository(ui, repopath)
3496 return qinit(ui, repo, True)
3500 return qinit(ui, repo, True)
3497
3501
3498 def mqcommand(orig, ui, repo, *args, **kwargs):
3502 def mqcommand(orig, ui, repo, *args, **kwargs):
3499 """Add --mq option to operate on patch repository instead of main"""
3503 """Add --mq option to operate on patch repository instead of main"""
3500
3504
3501 # some commands do not like getting unknown options
3505 # some commands do not like getting unknown options
3502 mq = kwargs.pop('mq', None)
3506 mq = kwargs.pop('mq', None)
3503
3507
3504 if not mq:
3508 if not mq:
3505 return orig(ui, repo, *args, **kwargs)
3509 return orig(ui, repo, *args, **kwargs)
3506
3510
3507 q = repo.mq
3511 q = repo.mq
3508 r = q.qrepo()
3512 r = q.qrepo()
3509 if not r:
3513 if not r:
3510 raise util.Abort(_('no queue repository'))
3514 raise util.Abort(_('no queue repository'))
3511 return orig(r.ui, r, *args, **kwargs)
3515 return orig(r.ui, r, *args, **kwargs)
3512
3516
3513 def summary(orig, ui, repo, *args, **kwargs):
3517 def summary(orig, ui, repo, *args, **kwargs):
3514 r = orig(ui, repo, *args, **kwargs)
3518 r = orig(ui, repo, *args, **kwargs)
3515 q = repo.mq
3519 q = repo.mq
3516 m = []
3520 m = []
3517 a, u = len(q.applied), len(q.unapplied(repo))
3521 a, u = len(q.applied), len(q.unapplied(repo))
3518 if a:
3522 if a:
3519 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
3523 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
3520 if u:
3524 if u:
3521 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
3525 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
3522 if m:
3526 if m:
3523 ui.write("mq: %s\n" % ', '.join(m))
3527 ui.write("mq: %s\n" % ', '.join(m))
3524 else:
3528 else:
3525 ui.note(_("mq: (empty queue)\n"))
3529 ui.note(_("mq: (empty queue)\n"))
3526 return r
3530 return r
3527
3531
3528 def revsetmq(repo, subset, x):
3532 def revsetmq(repo, subset, x):
3529 """``mq()``
3533 """``mq()``
3530 Changesets managed by MQ.
3534 Changesets managed by MQ.
3531 """
3535 """
3532 revset.getargs(x, 0, 0, _("mq takes no arguments"))
3536 revset.getargs(x, 0, 0, _("mq takes no arguments"))
3533 applied = set([repo[r.node].rev() for r in repo.mq.applied])
3537 applied = set([repo[r.node].rev() for r in repo.mq.applied])
3534 return [r for r in subset if r in applied]
3538 return [r for r in subset if r in applied]
3535
3539
3536 def extsetup(ui):
3540 def extsetup(ui):
3537 revset.symbols['mq'] = revsetmq
3541 revset.symbols['mq'] = revsetmq
3538
3542
3539 # tell hggettext to extract docstrings from these functions:
3543 # tell hggettext to extract docstrings from these functions:
3540 i18nfunctions = [revsetmq]
3544 i18nfunctions = [revsetmq]
3541
3545
3542 def uisetup(ui):
3546 def uisetup(ui):
3543 mqopt = [('', 'mq', None, _("operate on patch repository"))]
3547 mqopt = [('', 'mq', None, _("operate on patch repository"))]
3544
3548
3545 extensions.wrapcommand(commands.table, 'import', mqimport)
3549 extensions.wrapcommand(commands.table, 'import', mqimport)
3546 extensions.wrapcommand(commands.table, 'summary', summary)
3550 extensions.wrapcommand(commands.table, 'summary', summary)
3547
3551
3548 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
3552 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
3549 entry[1].extend(mqopt)
3553 entry[1].extend(mqopt)
3550
3554
3551 nowrap = set(commands.norepo.split(" "))
3555 nowrap = set(commands.norepo.split(" "))
3552
3556
3553 def dotable(cmdtable):
3557 def dotable(cmdtable):
3554 for cmd in cmdtable.keys():
3558 for cmd in cmdtable.keys():
3555 cmd = cmdutil.parsealiases(cmd)[0]
3559 cmd = cmdutil.parsealiases(cmd)[0]
3556 if cmd in nowrap:
3560 if cmd in nowrap:
3557 continue
3561 continue
3558 entry = extensions.wrapcommand(cmdtable, cmd, mqcommand)
3562 entry = extensions.wrapcommand(cmdtable, cmd, mqcommand)
3559 entry[1].extend(mqopt)
3563 entry[1].extend(mqopt)
3560
3564
3561 dotable(commands.table)
3565 dotable(commands.table)
3562
3566
3563 for extname, extmodule in extensions.extensions():
3567 for extname, extmodule in extensions.extensions():
3564 if extmodule.__file__ != __file__:
3568 if extmodule.__file__ != __file__:
3565 dotable(getattr(extmodule, 'cmdtable', {}))
3569 dotable(getattr(extmodule, 'cmdtable', {}))
3566
3570
3567
3571
3568 colortable = {'qguard.negative': 'red',
3572 colortable = {'qguard.negative': 'red',
3569 'qguard.positive': 'yellow',
3573 'qguard.positive': 'yellow',
3570 'qguard.unguarded': 'green',
3574 'qguard.unguarded': 'green',
3571 'qseries.applied': 'blue bold underline',
3575 'qseries.applied': 'blue bold underline',
3572 'qseries.guarded': 'black bold',
3576 'qseries.guarded': 'black bold',
3573 'qseries.missing': 'red bold',
3577 'qseries.missing': 'red bold',
3574 'qseries.unapplied': 'black bold'}
3578 'qseries.unapplied': 'black bold'}
@@ -1,703 +1,709 b''
1 # rebase.py - rebasing feature for mercurial
1 # rebase.py - rebasing feature for mercurial
2 #
2 #
3 # Copyright 2008 Stefano Tortarolo <stefano.tortarolo at gmail dot com>
3 # Copyright 2008 Stefano Tortarolo <stefano.tortarolo at gmail dot 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 move sets of revisions to a different ancestor
8 '''command to move sets of revisions to a different ancestor
9
9
10 This extension lets you rebase changesets in an existing Mercurial
10 This extension lets you rebase changesets in an existing Mercurial
11 repository.
11 repository.
12
12
13 For more information:
13 For more information:
14 http://mercurial.selenic.com/wiki/RebaseExtension
14 http://mercurial.selenic.com/wiki/RebaseExtension
15 '''
15 '''
16
16
17 from mercurial import hg, util, repair, merge, cmdutil, commands, bookmarks
17 from mercurial import hg, util, repair, merge, cmdutil, commands, bookmarks
18 from mercurial import extensions, patch, scmutil, phases
18 from mercurial import extensions, patch, scmutil, phases
19 from mercurial.commands import templateopts
19 from mercurial.commands import templateopts
20 from mercurial.node import nullrev
20 from mercurial.node import nullrev
21 from mercurial.lock import release
21 from mercurial.lock import release
22 from mercurial.i18n import _
22 from mercurial.i18n import _
23 import os, errno
23 import os, errno
24
24
25 nullmerge = -2
25 nullmerge = -2
26
26
27 cmdtable = {}
27 cmdtable = {}
28 command = cmdutil.command(cmdtable)
28 command = cmdutil.command(cmdtable)
29 testedwith = 'internal'
29 testedwith = 'internal'
30
30
31 @command('rebase',
31 @command('rebase',
32 [('s', 'source', '',
32 [('s', 'source', '',
33 _('rebase from the specified changeset'), _('REV')),
33 _('rebase from the specified changeset'), _('REV')),
34 ('b', 'base', '',
34 ('b', 'base', '',
35 _('rebase from the base of the specified changeset '
35 _('rebase from the base of the specified changeset '
36 '(up to greatest common ancestor of base and dest)'),
36 '(up to greatest common ancestor of base and dest)'),
37 _('REV')),
37 _('REV')),
38 ('r', 'rev', [],
38 ('r', 'rev', [],
39 _('rebase these revisions'),
39 _('rebase these revisions'),
40 _('REV')),
40 _('REV')),
41 ('d', 'dest', '',
41 ('d', 'dest', '',
42 _('rebase onto the specified changeset'), _('REV')),
42 _('rebase onto the specified changeset'), _('REV')),
43 ('', 'collapse', False, _('collapse the rebased changesets')),
43 ('', 'collapse', False, _('collapse the rebased changesets')),
44 ('m', 'message', '',
44 ('m', 'message', '',
45 _('use text as collapse commit message'), _('TEXT')),
45 _('use text as collapse commit message'), _('TEXT')),
46 ('e', 'edit', False, _('invoke editor on commit messages')),
46 ('e', 'edit', False, _('invoke editor on commit messages')),
47 ('l', 'logfile', '',
47 ('l', 'logfile', '',
48 _('read collapse commit message from file'), _('FILE')),
48 _('read collapse commit message from file'), _('FILE')),
49 ('', 'keep', False, _('keep original changesets')),
49 ('', 'keep', False, _('keep original changesets')),
50 ('', 'keepbranches', False, _('keep original branch names')),
50 ('', 'keepbranches', False, _('keep original branch names')),
51 ('D', 'detach', False, _('(DEPRECATED)')),
51 ('D', 'detach', False, _('(DEPRECATED)')),
52 ('t', 'tool', '', _('specify merge tool')),
52 ('t', 'tool', '', _('specify merge tool')),
53 ('c', 'continue', False, _('continue an interrupted rebase')),
53 ('c', 'continue', False, _('continue an interrupted rebase')),
54 ('a', 'abort', False, _('abort an interrupted rebase'))] +
54 ('a', 'abort', False, _('abort an interrupted rebase'))] +
55 templateopts,
55 templateopts,
56 _('hg rebase [-s REV | -b REV] [-d REV] [options]\n'
56 _('hg rebase [-s REV | -b REV] [-d REV] [options]\n'
57 'hg rebase {-a|-c}'))
57 'hg rebase {-a|-c}'))
58 def rebase(ui, repo, **opts):
58 def rebase(ui, repo, **opts):
59 """move changeset (and descendants) to a different branch
59 """move changeset (and descendants) to a different branch
60
60
61 Rebase uses repeated merging to graft changesets from one part of
61 Rebase uses repeated merging to graft changesets from one part of
62 history (the source) onto another (the destination). This can be
62 history (the source) onto another (the destination). This can be
63 useful for linearizing *local* changes relative to a master
63 useful for linearizing *local* changes relative to a master
64 development tree.
64 development tree.
65
65
66 You should not rebase changesets that have already been shared
66 You should not rebase changesets that have already been shared
67 with others. Doing so will force everybody else to perform the
67 with others. Doing so will force everybody else to perform the
68 same rebase or they will end up with duplicated changesets after
68 same rebase or they will end up with duplicated changesets after
69 pulling in your rebased changesets.
69 pulling in your rebased changesets.
70
70
71 If you don't specify a destination changeset (``-d/--dest``),
71 If you don't specify a destination changeset (``-d/--dest``),
72 rebase uses the tipmost head of the current named branch as the
72 rebase uses the tipmost head of the current named branch as the
73 destination. (The destination changeset is not modified by
73 destination. (The destination changeset is not modified by
74 rebasing, but new changesets are added as its descendants.)
74 rebasing, but new changesets are added as its descendants.)
75
75
76 You can specify which changesets to rebase in two ways: as a
76 You can specify which changesets to rebase in two ways: as a
77 "source" changeset or as a "base" changeset. Both are shorthand
77 "source" changeset or as a "base" changeset. Both are shorthand
78 for a topologically related set of changesets (the "source
78 for a topologically related set of changesets (the "source
79 branch"). If you specify source (``-s/--source``), rebase will
79 branch"). If you specify source (``-s/--source``), rebase will
80 rebase that changeset and all of its descendants onto dest. If you
80 rebase that changeset and all of its descendants onto dest. If you
81 specify base (``-b/--base``), rebase will select ancestors of base
81 specify base (``-b/--base``), rebase will select ancestors of base
82 back to but not including the common ancestor with dest. Thus,
82 back to but not including the common ancestor with dest. Thus,
83 ``-b`` is less precise but more convenient than ``-s``: you can
83 ``-b`` is less precise but more convenient than ``-s``: you can
84 specify any changeset in the source branch, and rebase will select
84 specify any changeset in the source branch, and rebase will select
85 the whole branch. If you specify neither ``-s`` nor ``-b``, rebase
85 the whole branch. If you specify neither ``-s`` nor ``-b``, rebase
86 uses the parent of the working directory as the base.
86 uses the parent of the working directory as the base.
87
87
88 By default, rebase recreates the changesets in the source branch
88 By default, rebase recreates the changesets in the source branch
89 as descendants of dest and then destroys the originals. Use
89 as descendants of dest and then destroys the originals. Use
90 ``--keep`` to preserve the original source changesets. Some
90 ``--keep`` to preserve the original source changesets. Some
91 changesets in the source branch (e.g. merges from the destination
91 changesets in the source branch (e.g. merges from the destination
92 branch) may be dropped if they no longer contribute any change.
92 branch) may be dropped if they no longer contribute any change.
93
93
94 One result of the rules for selecting the destination changeset
94 One result of the rules for selecting the destination changeset
95 and source branch is that, unlike ``merge``, rebase will do
95 and source branch is that, unlike ``merge``, rebase will do
96 nothing if you are at the latest (tipmost) head of a named branch
96 nothing if you are at the latest (tipmost) head of a named branch
97 with two heads. You need to explicitly specify source and/or
97 with two heads. You need to explicitly specify source and/or
98 destination (or ``update`` to the other head, if it's the head of
98 destination (or ``update`` to the other head, if it's the head of
99 the intended source branch).
99 the intended source branch).
100
100
101 If a rebase is interrupted to manually resolve a merge, it can be
101 If a rebase is interrupted to manually resolve a merge, it can be
102 continued with --continue/-c or aborted with --abort/-a.
102 continued with --continue/-c or aborted with --abort/-a.
103
103
104 Returns 0 on success, 1 if nothing to rebase.
104 Returns 0 on success, 1 if nothing to rebase.
105 """
105 """
106 originalwd = target = None
106 originalwd = target = None
107 external = nullrev
107 external = nullrev
108 state = {}
108 state = {}
109 skipped = set()
109 skipped = set()
110 targetancestors = set()
110 targetancestors = set()
111
111
112 editor = None
112 editor = None
113 if opts.get('edit'):
113 if opts.get('edit'):
114 editor = cmdutil.commitforceeditor
114 editor = cmdutil.commitforceeditor
115
115
116 lock = wlock = None
116 lock = wlock = None
117 try:
117 try:
118 wlock = repo.wlock()
118 wlock = repo.wlock()
119 lock = repo.lock()
119 lock = repo.lock()
120
120
121 # Validate input and define rebasing points
121 # Validate input and define rebasing points
122 destf = opts.get('dest', None)
122 destf = opts.get('dest', None)
123 srcf = opts.get('source', None)
123 srcf = opts.get('source', None)
124 basef = opts.get('base', None)
124 basef = opts.get('base', None)
125 revf = opts.get('rev', [])
125 revf = opts.get('rev', [])
126 contf = opts.get('continue')
126 contf = opts.get('continue')
127 abortf = opts.get('abort')
127 abortf = opts.get('abort')
128 collapsef = opts.get('collapse', False)
128 collapsef = opts.get('collapse', False)
129 collapsemsg = cmdutil.logmessage(ui, opts)
129 collapsemsg = cmdutil.logmessage(ui, opts)
130 extrafn = opts.get('extrafn') # internal, used by e.g. hgsubversion
130 extrafn = opts.get('extrafn') # internal, used by e.g. hgsubversion
131 keepf = opts.get('keep', False)
131 keepf = opts.get('keep', False)
132 keepbranchesf = opts.get('keepbranches', False)
132 keepbranchesf = opts.get('keepbranches', False)
133 # keepopen is not meant for use on the command line, but by
133 # keepopen is not meant for use on the command line, but by
134 # other extensions
134 # other extensions
135 keepopen = opts.get('keepopen', False)
135 keepopen = opts.get('keepopen', False)
136
136
137 if collapsemsg and not collapsef:
137 if collapsemsg and not collapsef:
138 raise util.Abort(
138 raise util.Abort(
139 _('message can only be specified with collapse'))
139 _('message can only be specified with collapse'))
140
140
141 if contf or abortf:
141 if contf or abortf:
142 if contf and abortf:
142 if contf and abortf:
143 raise util.Abort(_('cannot use both abort and continue'))
143 raise util.Abort(_('cannot use both abort and continue'))
144 if collapsef:
144 if collapsef:
145 raise util.Abort(
145 raise util.Abort(
146 _('cannot use collapse with continue or abort'))
146 _('cannot use collapse with continue or abort'))
147 if srcf or basef or destf:
147 if srcf or basef or destf:
148 raise util.Abort(
148 raise util.Abort(
149 _('abort and continue do not allow specifying revisions'))
149 _('abort and continue do not allow specifying revisions'))
150 if opts.get('tool', False):
150 if opts.get('tool', False):
151 ui.warn(_('tool option will be ignored\n'))
151 ui.warn(_('tool option will be ignored\n'))
152
152
153 (originalwd, target, state, skipped, collapsef, keepf,
153 (originalwd, target, state, skipped, collapsef, keepf,
154 keepbranchesf, external) = restorestatus(repo)
154 keepbranchesf, external) = restorestatus(repo)
155 if abortf:
155 if abortf:
156 return abort(repo, originalwd, target, state)
156 return abort(repo, originalwd, target, state)
157 else:
157 else:
158 if srcf and basef:
158 if srcf and basef:
159 raise util.Abort(_('cannot specify both a '
159 raise util.Abort(_('cannot specify both a '
160 'source and a base'))
160 'source and a base'))
161 if revf and basef:
161 if revf and basef:
162 raise util.Abort(_('cannot specify both a '
162 raise util.Abort(_('cannot specify both a '
163 'revision and a base'))
163 'revision and a base'))
164 if revf and srcf:
164 if revf and srcf:
165 raise util.Abort(_('cannot specify both a '
165 raise util.Abort(_('cannot specify both a '
166 'revision and a source'))
166 'revision and a source'))
167
167
168 cmdutil.bailifchanged(repo)
168 cmdutil.bailifchanged(repo)
169
169
170 if not destf:
170 if not destf:
171 # Destination defaults to the latest revision in the
171 # Destination defaults to the latest revision in the
172 # current branch
172 # current branch
173 branch = repo[None].branch()
173 branch = repo[None].branch()
174 dest = repo[branch]
174 dest = repo[branch]
175 else:
175 else:
176 dest = scmutil.revsingle(repo, destf)
176 dest = scmutil.revsingle(repo, destf)
177
177
178 if revf:
178 if revf:
179 rebaseset = repo.revs('%lr', revf)
179 rebaseset = repo.revs('%lr', revf)
180 elif srcf:
180 elif srcf:
181 src = scmutil.revrange(repo, [srcf])
181 src = scmutil.revrange(repo, [srcf])
182 rebaseset = repo.revs('(%ld)::', src)
182 rebaseset = repo.revs('(%ld)::', src)
183 else:
183 else:
184 base = scmutil.revrange(repo, [basef or '.'])
184 base = scmutil.revrange(repo, [basef or '.'])
185 rebaseset = repo.revs(
185 rebaseset = repo.revs(
186 '(children(ancestor(%ld, %d)) and ::(%ld))::',
186 '(children(ancestor(%ld, %d)) and ::(%ld))::',
187 base, dest, base)
187 base, dest, base)
188
188
189 if rebaseset:
189 if rebaseset:
190 root = min(rebaseset)
190 root = min(rebaseset)
191 else:
191 else:
192 root = None
192 root = None
193
193
194 if not rebaseset:
194 if not rebaseset:
195 repo.ui.debug('base is ancestor of destination\n')
195 repo.ui.debug('base is ancestor of destination\n')
196 result = None
196 result = None
197 elif not keepf and list(repo.revs('first(children(%ld) - %ld)',
197 elif not keepf and list(repo.revs('first(children(%ld) - %ld)',
198 rebaseset, rebaseset)):
198 rebaseset, rebaseset)):
199 raise util.Abort(
199 raise util.Abort(
200 _("can't remove original changesets with"
200 _("can't remove original changesets with"
201 " unrebased descendants"),
201 " unrebased descendants"),
202 hint=_('use --keep to keep original changesets'))
202 hint=_('use --keep to keep original changesets'))
203 elif not keepf and not repo[root].mutable():
203 elif not keepf and not repo[root].mutable():
204 raise util.Abort(_("can't rebase immutable changeset %s")
204 raise util.Abort(_("can't rebase immutable changeset %s")
205 % repo[root],
205 % repo[root],
206 hint=_('see hg help phases for details'))
206 hint=_('see hg help phases for details'))
207 else:
207 else:
208 result = buildstate(repo, dest, rebaseset, collapsef)
208 result = buildstate(repo, dest, rebaseset, collapsef)
209
209
210 if not result:
210 if not result:
211 # Empty state built, nothing to rebase
211 # Empty state built, nothing to rebase
212 ui.status(_('nothing to rebase\n'))
212 ui.status(_('nothing to rebase\n'))
213 return 1
213 return 1
214 else:
214 else:
215 originalwd, target, state = result
215 originalwd, target, state = result
216 if collapsef:
216 if collapsef:
217 targetancestors = set(repo.changelog.ancestors([target]))
217 targetancestors = set(repo.changelog.ancestors([target]))
218 targetancestors.add(target)
218 targetancestors.add(target)
219 external = checkexternal(repo, state, targetancestors)
219 external = checkexternal(repo, state, targetancestors)
220
220
221 if keepbranchesf:
221 if keepbranchesf:
222 assert not extrafn, 'cannot use both keepbranches and extrafn'
222 assert not extrafn, 'cannot use both keepbranches and extrafn'
223 def extrafn(ctx, extra):
223 def extrafn(ctx, extra):
224 extra['branch'] = ctx.branch()
224 extra['branch'] = ctx.branch()
225 if collapsef:
225 if collapsef:
226 branches = set()
226 branches = set()
227 for rev in state:
227 for rev in state:
228 branches.add(repo[rev].branch())
228 branches.add(repo[rev].branch())
229 if len(branches) > 1:
229 if len(branches) > 1:
230 raise util.Abort(_('cannot collapse multiple named '
230 raise util.Abort(_('cannot collapse multiple named '
231 'branches'))
231 'branches'))
232
232
233
233
234 # Rebase
234 # Rebase
235 if not targetancestors:
235 if not targetancestors:
236 targetancestors = set(repo.changelog.ancestors([target]))
236 targetancestors = set(repo.changelog.ancestors([target]))
237 targetancestors.add(target)
237 targetancestors.add(target)
238
238
239 # Keep track of the current bookmarks in order to reset them later
239 # Keep track of the current bookmarks in order to reset them later
240 currentbookmarks = repo._bookmarks.copy()
240 currentbookmarks = repo._bookmarks.copy()
241 activebookmark = repo._bookmarkcurrent
242 if activebookmark:
243 bookmarks.unsetcurrent(repo)
241
244
242 sortedstate = sorted(state)
245 sortedstate = sorted(state)
243 total = len(sortedstate)
246 total = len(sortedstate)
244 pos = 0
247 pos = 0
245 for rev in sortedstate:
248 for rev in sortedstate:
246 pos += 1
249 pos += 1
247 if state[rev] == -1:
250 if state[rev] == -1:
248 ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, repo[rev])),
251 ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, repo[rev])),
249 _('changesets'), total)
252 _('changesets'), total)
250 storestatus(repo, originalwd, target, state, collapsef, keepf,
253 storestatus(repo, originalwd, target, state, collapsef, keepf,
251 keepbranchesf, external)
254 keepbranchesf, external)
252 p1, p2 = defineparents(repo, rev, target, state,
255 p1, p2 = defineparents(repo, rev, target, state,
253 targetancestors)
256 targetancestors)
254 if len(repo.parents()) == 2:
257 if len(repo.parents()) == 2:
255 repo.ui.debug('resuming interrupted rebase\n')
258 repo.ui.debug('resuming interrupted rebase\n')
256 else:
259 else:
257 try:
260 try:
258 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
261 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
259 stats = rebasenode(repo, rev, p1, state, collapsef)
262 stats = rebasenode(repo, rev, p1, state, collapsef)
260 if stats and stats[3] > 0:
263 if stats and stats[3] > 0:
261 raise util.Abort(_('unresolved conflicts (see hg '
264 raise util.Abort(_('unresolved conflicts (see hg '
262 'resolve, then hg rebase --continue)'))
265 'resolve, then hg rebase --continue)'))
263 finally:
266 finally:
264 ui.setconfig('ui', 'forcemerge', '')
267 ui.setconfig('ui', 'forcemerge', '')
265 cmdutil.duplicatecopies(repo, rev, target)
268 cmdutil.duplicatecopies(repo, rev, target)
266 if not collapsef:
269 if not collapsef:
267 newrev = concludenode(repo, rev, p1, p2, extrafn=extrafn,
270 newrev = concludenode(repo, rev, p1, p2, extrafn=extrafn,
268 editor=editor)
271 editor=editor)
269 else:
272 else:
270 # Skip commit if we are collapsing
273 # Skip commit if we are collapsing
271 repo.setparents(repo[p1].node())
274 repo.setparents(repo[p1].node())
272 newrev = None
275 newrev = None
273 # Update the state
276 # Update the state
274 if newrev is not None:
277 if newrev is not None:
275 state[rev] = repo[newrev].rev()
278 state[rev] = repo[newrev].rev()
276 else:
279 else:
277 if not collapsef:
280 if not collapsef:
278 ui.note(_('no changes, revision %d skipped\n') % rev)
281 ui.note(_('no changes, revision %d skipped\n') % rev)
279 ui.debug('next revision set to %s\n' % p1)
282 ui.debug('next revision set to %s\n' % p1)
280 skipped.add(rev)
283 skipped.add(rev)
281 state[rev] = p1
284 state[rev] = p1
282
285
283 ui.progress(_('rebasing'), None)
286 ui.progress(_('rebasing'), None)
284 ui.note(_('rebase merging completed\n'))
287 ui.note(_('rebase merging completed\n'))
285
288
286 if collapsef and not keepopen:
289 if collapsef and not keepopen:
287 p1, p2 = defineparents(repo, min(state), target,
290 p1, p2 = defineparents(repo, min(state), target,
288 state, targetancestors)
291 state, targetancestors)
289 if collapsemsg:
292 if collapsemsg:
290 commitmsg = collapsemsg
293 commitmsg = collapsemsg
291 else:
294 else:
292 commitmsg = 'Collapsed revision'
295 commitmsg = 'Collapsed revision'
293 for rebased in state:
296 for rebased in state:
294 if rebased not in skipped and state[rebased] != nullmerge:
297 if rebased not in skipped and state[rebased] != nullmerge:
295 commitmsg += '\n* %s' % repo[rebased].description()
298 commitmsg += '\n* %s' % repo[rebased].description()
296 commitmsg = ui.edit(commitmsg, repo.ui.username())
299 commitmsg = ui.edit(commitmsg, repo.ui.username())
297 newrev = concludenode(repo, rev, p1, external, commitmsg=commitmsg,
300 newrev = concludenode(repo, rev, p1, external, commitmsg=commitmsg,
298 extrafn=extrafn, editor=editor)
301 extrafn=extrafn, editor=editor)
299
302
300 if 'qtip' in repo.tags():
303 if 'qtip' in repo.tags():
301 updatemq(repo, state, skipped, **opts)
304 updatemq(repo, state, skipped, **opts)
302
305
303 if currentbookmarks:
306 if currentbookmarks:
304 # Nodeids are needed to reset bookmarks
307 # Nodeids are needed to reset bookmarks
305 nstate = {}
308 nstate = {}
306 for k, v in state.iteritems():
309 for k, v in state.iteritems():
307 if v != nullmerge:
310 if v != nullmerge:
308 nstate[repo[k].node()] = repo[v].node()
311 nstate[repo[k].node()] = repo[v].node()
309
312
310 if not keepf:
313 if not keepf:
311 # Remove no more useful revisions
314 # Remove no more useful revisions
312 rebased = [rev for rev in state if state[rev] != nullmerge]
315 rebased = [rev for rev in state if state[rev] != nullmerge]
313 if rebased:
316 if rebased:
314 if set(repo.changelog.descendants([min(rebased)])) - set(state):
317 if set(repo.changelog.descendants([min(rebased)])) - set(state):
315 ui.warn(_("warning: new changesets detected "
318 ui.warn(_("warning: new changesets detected "
316 "on source branch, not stripping\n"))
319 "on source branch, not stripping\n"))
317 else:
320 else:
318 # backup the old csets by default
321 # backup the old csets by default
319 repair.strip(ui, repo, repo[min(rebased)].node(), "all")
322 repair.strip(ui, repo, repo[min(rebased)].node(), "all")
320
323
321 if currentbookmarks:
324 if currentbookmarks:
322 updatebookmarks(repo, nstate, currentbookmarks, **opts)
325 updatebookmarks(repo, nstate, currentbookmarks, **opts)
323
326
324 clearstatus(repo)
327 clearstatus(repo)
325 ui.note(_("rebase completed\n"))
328 ui.note(_("rebase completed\n"))
326 if os.path.exists(repo.sjoin('undo')):
329 if os.path.exists(repo.sjoin('undo')):
327 util.unlinkpath(repo.sjoin('undo'))
330 util.unlinkpath(repo.sjoin('undo'))
328 if skipped:
331 if skipped:
329 ui.note(_("%d revisions have been skipped\n") % len(skipped))
332 ui.note(_("%d revisions have been skipped\n") % len(skipped))
333
334 if (activebookmark and
335 repo['tip'].node() == repo._bookmarks[activebookmark]):
336 bookmarks.setcurrent(repo, activebookmark)
337
330 finally:
338 finally:
331 release(lock, wlock)
339 release(lock, wlock)
332
340
333 def checkexternal(repo, state, targetancestors):
341 def checkexternal(repo, state, targetancestors):
334 """Check whether one or more external revisions need to be taken in
342 """Check whether one or more external revisions need to be taken in
335 consideration. In the latter case, abort.
343 consideration. In the latter case, abort.
336 """
344 """
337 external = nullrev
345 external = nullrev
338 source = min(state)
346 source = min(state)
339 for rev in state:
347 for rev in state:
340 if rev == source:
348 if rev == source:
341 continue
349 continue
342 # Check externals and fail if there are more than one
350 # Check externals and fail if there are more than one
343 for p in repo[rev].parents():
351 for p in repo[rev].parents():
344 if (p.rev() not in state
352 if (p.rev() not in state
345 and p.rev() not in targetancestors):
353 and p.rev() not in targetancestors):
346 if external != nullrev:
354 if external != nullrev:
347 raise util.Abort(_('unable to collapse, there is more '
355 raise util.Abort(_('unable to collapse, there is more '
348 'than one external parent'))
356 'than one external parent'))
349 external = p.rev()
357 external = p.rev()
350 return external
358 return external
351
359
352 def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None):
360 def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None):
353 'Commit the changes and store useful information in extra'
361 'Commit the changes and store useful information in extra'
354 try:
362 try:
355 repo.setparents(repo[p1].node(), repo[p2].node())
363 repo.setparents(repo[p1].node(), repo[p2].node())
356 ctx = repo[rev]
364 ctx = repo[rev]
357 if commitmsg is None:
365 if commitmsg is None:
358 commitmsg = ctx.description()
366 commitmsg = ctx.description()
359 extra = {'rebase_source': ctx.hex()}
367 extra = {'rebase_source': ctx.hex()}
360 if extrafn:
368 if extrafn:
361 extrafn(ctx, extra)
369 extrafn(ctx, extra)
362 # Commit might fail if unresolved files exist
370 # Commit might fail if unresolved files exist
363 newrev = repo.commit(text=commitmsg, user=ctx.user(),
371 newrev = repo.commit(text=commitmsg, user=ctx.user(),
364 date=ctx.date(), extra=extra, editor=editor)
372 date=ctx.date(), extra=extra, editor=editor)
365 repo.dirstate.setbranch(repo[newrev].branch())
373 repo.dirstate.setbranch(repo[newrev].branch())
366 targetphase = max(ctx.phase(), phases.draft)
374 targetphase = max(ctx.phase(), phases.draft)
367 # retractboundary doesn't overwrite upper phase inherited from parent
375 # retractboundary doesn't overwrite upper phase inherited from parent
368 newnode = repo[newrev].node()
376 newnode = repo[newrev].node()
369 if newnode:
377 if newnode:
370 phases.retractboundary(repo, targetphase, [newnode])
378 phases.retractboundary(repo, targetphase, [newnode])
371 return newrev
379 return newrev
372 except util.Abort:
380 except util.Abort:
373 # Invalidate the previous setparents
381 # Invalidate the previous setparents
374 repo.dirstate.invalidate()
382 repo.dirstate.invalidate()
375 raise
383 raise
376
384
377 def rebasenode(repo, rev, p1, state, collapse):
385 def rebasenode(repo, rev, p1, state, collapse):
378 'Rebase a single revision'
386 'Rebase a single revision'
379 # Merge phase
387 # Merge phase
380 # Update to target and merge it with local
388 # Update to target and merge it with local
381 if repo['.'].rev() != repo[p1].rev():
389 if repo['.'].rev() != repo[p1].rev():
382 repo.ui.debug(" update to %d:%s\n" % (repo[p1].rev(), repo[p1]))
390 repo.ui.debug(" update to %d:%s\n" % (repo[p1].rev(), repo[p1]))
383 merge.update(repo, p1, False, True, False)
391 merge.update(repo, p1, False, True, False)
384 else:
392 else:
385 repo.ui.debug(" already in target\n")
393 repo.ui.debug(" already in target\n")
386 repo.dirstate.write()
394 repo.dirstate.write()
387 repo.ui.debug(" merge against %d:%s\n" % (repo[rev].rev(), repo[rev]))
395 repo.ui.debug(" merge against %d:%s\n" % (repo[rev].rev(), repo[rev]))
388 base = None
396 base = None
389 if repo[rev].rev() != repo[min(state)].rev():
397 if repo[rev].rev() != repo[min(state)].rev():
390 base = repo[rev].p1().node()
398 base = repo[rev].p1().node()
391 # When collapsing in-place, the parent is the common ancestor, we
399 # When collapsing in-place, the parent is the common ancestor, we
392 # have to allow merging with it.
400 # have to allow merging with it.
393 return merge.update(repo, rev, True, True, False, base, collapse)
401 return merge.update(repo, rev, True, True, False, base, collapse)
394
402
395 def defineparents(repo, rev, target, state, targetancestors):
403 def defineparents(repo, rev, target, state, targetancestors):
396 'Return the new parent relationship of the revision that will be rebased'
404 'Return the new parent relationship of the revision that will be rebased'
397 parents = repo[rev].parents()
405 parents = repo[rev].parents()
398 p1 = p2 = nullrev
406 p1 = p2 = nullrev
399
407
400 P1n = parents[0].rev()
408 P1n = parents[0].rev()
401 if P1n in targetancestors:
409 if P1n in targetancestors:
402 p1 = target
410 p1 = target
403 elif P1n in state:
411 elif P1n in state:
404 if state[P1n] == nullmerge:
412 if state[P1n] == nullmerge:
405 p1 = target
413 p1 = target
406 else:
414 else:
407 p1 = state[P1n]
415 p1 = state[P1n]
408 else: # P1n external
416 else: # P1n external
409 p1 = target
417 p1 = target
410 p2 = P1n
418 p2 = P1n
411
419
412 if len(parents) == 2 and parents[1].rev() not in targetancestors:
420 if len(parents) == 2 and parents[1].rev() not in targetancestors:
413 P2n = parents[1].rev()
421 P2n = parents[1].rev()
414 # interesting second parent
422 # interesting second parent
415 if P2n in state:
423 if P2n in state:
416 if p1 == target: # P1n in targetancestors or external
424 if p1 == target: # P1n in targetancestors or external
417 p1 = state[P2n]
425 p1 = state[P2n]
418 else:
426 else:
419 p2 = state[P2n]
427 p2 = state[P2n]
420 else: # P2n external
428 else: # P2n external
421 if p2 != nullrev: # P1n external too => rev is a merged revision
429 if p2 != nullrev: # P1n external too => rev is a merged revision
422 raise util.Abort(_('cannot use revision %d as base, result '
430 raise util.Abort(_('cannot use revision %d as base, result '
423 'would have 3 parents') % rev)
431 'would have 3 parents') % rev)
424 p2 = P2n
432 p2 = P2n
425 repo.ui.debug(" future parents are %d and %d\n" %
433 repo.ui.debug(" future parents are %d and %d\n" %
426 (repo[p1].rev(), repo[p2].rev()))
434 (repo[p1].rev(), repo[p2].rev()))
427 return p1, p2
435 return p1, p2
428
436
429 def isagitpatch(repo, patchname):
437 def isagitpatch(repo, patchname):
430 'Return true if the given patch is in git format'
438 'Return true if the given patch is in git format'
431 mqpatch = os.path.join(repo.mq.path, patchname)
439 mqpatch = os.path.join(repo.mq.path, patchname)
432 for line in patch.linereader(file(mqpatch, 'rb')):
440 for line in patch.linereader(file(mqpatch, 'rb')):
433 if line.startswith('diff --git'):
441 if line.startswith('diff --git'):
434 return True
442 return True
435 return False
443 return False
436
444
437 def updatemq(repo, state, skipped, **opts):
445 def updatemq(repo, state, skipped, **opts):
438 'Update rebased mq patches - finalize and then import them'
446 'Update rebased mq patches - finalize and then import them'
439 mqrebase = {}
447 mqrebase = {}
440 mq = repo.mq
448 mq = repo.mq
441 original_series = mq.fullseries[:]
449 original_series = mq.fullseries[:]
442 skippedpatches = set()
450 skippedpatches = set()
443
451
444 for p in mq.applied:
452 for p in mq.applied:
445 rev = repo[p.node].rev()
453 rev = repo[p.node].rev()
446 if rev in state:
454 if rev in state:
447 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
455 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
448 (rev, p.name))
456 (rev, p.name))
449 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
457 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
450 else:
458 else:
451 # Applied but not rebased, not sure this should happen
459 # Applied but not rebased, not sure this should happen
452 skippedpatches.add(p.name)
460 skippedpatches.add(p.name)
453
461
454 if mqrebase:
462 if mqrebase:
455 mq.finish(repo, mqrebase.keys())
463 mq.finish(repo, mqrebase.keys())
456
464
457 # We must start import from the newest revision
465 # We must start import from the newest revision
458 for rev in sorted(mqrebase, reverse=True):
466 for rev in sorted(mqrebase, reverse=True):
459 if rev not in skipped:
467 if rev not in skipped:
460 name, isgit = mqrebase[rev]
468 name, isgit = mqrebase[rev]
461 repo.ui.debug('import mq patch %d (%s)\n' % (state[rev], name))
469 repo.ui.debug('import mq patch %d (%s)\n' % (state[rev], name))
462 mq.qimport(repo, (), patchname=name, git=isgit,
470 mq.qimport(repo, (), patchname=name, git=isgit,
463 rev=[str(state[rev])])
471 rev=[str(state[rev])])
464 else:
472 else:
465 # Rebased and skipped
473 # Rebased and skipped
466 skippedpatches.add(mqrebase[rev][0])
474 skippedpatches.add(mqrebase[rev][0])
467
475
468 # Patches were either applied and rebased and imported in
476 # Patches were either applied and rebased and imported in
469 # order, applied and removed or unapplied. Discard the removed
477 # order, applied and removed or unapplied. Discard the removed
470 # ones while preserving the original series order and guards.
478 # ones while preserving the original series order and guards.
471 newseries = [s for s in original_series
479 newseries = [s for s in original_series
472 if mq.guard_re.split(s, 1)[0] not in skippedpatches]
480 if mq.guard_re.split(s, 1)[0] not in skippedpatches]
473 mq.fullseries[:] = newseries
481 mq.fullseries[:] = newseries
474 mq.seriesdirty = True
482 mq.seriesdirty = True
475 mq.savedirty()
483 mq.savedirty()
476
484
477 def updatebookmarks(repo, nstate, originalbookmarks, **opts):
485 def updatebookmarks(repo, nstate, originalbookmarks, **opts):
478 'Move bookmarks to their correct changesets'
486 'Move bookmarks to their correct changesets'
479 current = repo._bookmarkcurrent
480 for k, v in originalbookmarks.iteritems():
487 for k, v in originalbookmarks.iteritems():
481 if v in nstate:
488 if v in nstate:
482 if nstate[v] != nullmerge:
489 if nstate[v] != nullmerge:
483 # reset the pointer if the bookmark was moved incorrectly
490 # update the bookmarks for revs that have moved
484 if k != current:
491 repo._bookmarks[k] = nstate[v]
485 repo._bookmarks[k] = nstate[v]
486
492
487 bookmarks.write(repo)
493 bookmarks.write(repo)
488
494
489 def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
495 def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
490 external):
496 external):
491 'Store the current status to allow recovery'
497 'Store the current status to allow recovery'
492 f = repo.opener("rebasestate", "w")
498 f = repo.opener("rebasestate", "w")
493 f.write(repo[originalwd].hex() + '\n')
499 f.write(repo[originalwd].hex() + '\n')
494 f.write(repo[target].hex() + '\n')
500 f.write(repo[target].hex() + '\n')
495 f.write(repo[external].hex() + '\n')
501 f.write(repo[external].hex() + '\n')
496 f.write('%d\n' % int(collapse))
502 f.write('%d\n' % int(collapse))
497 f.write('%d\n' % int(keep))
503 f.write('%d\n' % int(keep))
498 f.write('%d\n' % int(keepbranches))
504 f.write('%d\n' % int(keepbranches))
499 for d, v in state.iteritems():
505 for d, v in state.iteritems():
500 oldrev = repo[d].hex()
506 oldrev = repo[d].hex()
501 if v != nullmerge:
507 if v != nullmerge:
502 newrev = repo[v].hex()
508 newrev = repo[v].hex()
503 else:
509 else:
504 newrev = v
510 newrev = v
505 f.write("%s:%s\n" % (oldrev, newrev))
511 f.write("%s:%s\n" % (oldrev, newrev))
506 f.close()
512 f.close()
507 repo.ui.debug('rebase status stored\n')
513 repo.ui.debug('rebase status stored\n')
508
514
509 def clearstatus(repo):
515 def clearstatus(repo):
510 'Remove the status files'
516 'Remove the status files'
511 if os.path.exists(repo.join("rebasestate")):
517 if os.path.exists(repo.join("rebasestate")):
512 util.unlinkpath(repo.join("rebasestate"))
518 util.unlinkpath(repo.join("rebasestate"))
513
519
514 def restorestatus(repo):
520 def restorestatus(repo):
515 'Restore a previously stored status'
521 'Restore a previously stored status'
516 try:
522 try:
517 target = None
523 target = None
518 collapse = False
524 collapse = False
519 external = nullrev
525 external = nullrev
520 state = {}
526 state = {}
521 f = repo.opener("rebasestate")
527 f = repo.opener("rebasestate")
522 for i, l in enumerate(f.read().splitlines()):
528 for i, l in enumerate(f.read().splitlines()):
523 if i == 0:
529 if i == 0:
524 originalwd = repo[l].rev()
530 originalwd = repo[l].rev()
525 elif i == 1:
531 elif i == 1:
526 target = repo[l].rev()
532 target = repo[l].rev()
527 elif i == 2:
533 elif i == 2:
528 external = repo[l].rev()
534 external = repo[l].rev()
529 elif i == 3:
535 elif i == 3:
530 collapse = bool(int(l))
536 collapse = bool(int(l))
531 elif i == 4:
537 elif i == 4:
532 keep = bool(int(l))
538 keep = bool(int(l))
533 elif i == 5:
539 elif i == 5:
534 keepbranches = bool(int(l))
540 keepbranches = bool(int(l))
535 else:
541 else:
536 oldrev, newrev = l.split(':')
542 oldrev, newrev = l.split(':')
537 if newrev != str(nullmerge):
543 if newrev != str(nullmerge):
538 state[repo[oldrev].rev()] = repo[newrev].rev()
544 state[repo[oldrev].rev()] = repo[newrev].rev()
539 else:
545 else:
540 state[repo[oldrev].rev()] = int(newrev)
546 state[repo[oldrev].rev()] = int(newrev)
541 skipped = set()
547 skipped = set()
542 # recompute the set of skipped revs
548 # recompute the set of skipped revs
543 if not collapse:
549 if not collapse:
544 seen = set([target])
550 seen = set([target])
545 for old, new in sorted(state.items()):
551 for old, new in sorted(state.items()):
546 if new != nullrev and new in seen:
552 if new != nullrev and new in seen:
547 skipped.add(old)
553 skipped.add(old)
548 seen.add(new)
554 seen.add(new)
549 repo.ui.debug('computed skipped revs: %s\n' % skipped)
555 repo.ui.debug('computed skipped revs: %s\n' % skipped)
550 repo.ui.debug('rebase status resumed\n')
556 repo.ui.debug('rebase status resumed\n')
551 return (originalwd, target, state, skipped,
557 return (originalwd, target, state, skipped,
552 collapse, keep, keepbranches, external)
558 collapse, keep, keepbranches, external)
553 except IOError, err:
559 except IOError, err:
554 if err.errno != errno.ENOENT:
560 if err.errno != errno.ENOENT:
555 raise
561 raise
556 raise util.Abort(_('no rebase in progress'))
562 raise util.Abort(_('no rebase in progress'))
557
563
558 def abort(repo, originalwd, target, state):
564 def abort(repo, originalwd, target, state):
559 'Restore the repository to its original state'
565 'Restore the repository to its original state'
560 dstates = [s for s in state.values() if s != nullrev]
566 dstates = [s for s in state.values() if s != nullrev]
561 immutable = [d for d in dstates if not repo[d].mutable()]
567 immutable = [d for d in dstates if not repo[d].mutable()]
562 if immutable:
568 if immutable:
563 raise util.Abort(_("can't abort rebase due to immutable changesets %s")
569 raise util.Abort(_("can't abort rebase due to immutable changesets %s")
564 % ', '.join(str(repo[r]) for r in immutable),
570 % ', '.join(str(repo[r]) for r in immutable),
565 hint=_('see hg help phases for details'))
571 hint=_('see hg help phases for details'))
566
572
567 descendants = set()
573 descendants = set()
568 if dstates:
574 if dstates:
569 descendants = set(repo.changelog.descendants(dstates))
575 descendants = set(repo.changelog.descendants(dstates))
570 if descendants - set(dstates):
576 if descendants - set(dstates):
571 repo.ui.warn(_("warning: new changesets detected on target branch, "
577 repo.ui.warn(_("warning: new changesets detected on target branch, "
572 "can't abort\n"))
578 "can't abort\n"))
573 return -1
579 return -1
574 else:
580 else:
575 # Strip from the first rebased revision
581 # Strip from the first rebased revision
576 merge.update(repo, repo[originalwd].rev(), False, True, False)
582 merge.update(repo, repo[originalwd].rev(), False, True, False)
577 rebased = filter(lambda x: x > -1 and x != target, state.values())
583 rebased = filter(lambda x: x > -1 and x != target, state.values())
578 if rebased:
584 if rebased:
579 strippoint = min(rebased)
585 strippoint = min(rebased)
580 # no backup of rebased cset versions needed
586 # no backup of rebased cset versions needed
581 repair.strip(repo.ui, repo, repo[strippoint].node())
587 repair.strip(repo.ui, repo, repo[strippoint].node())
582 clearstatus(repo)
588 clearstatus(repo)
583 repo.ui.warn(_('rebase aborted\n'))
589 repo.ui.warn(_('rebase aborted\n'))
584 return 0
590 return 0
585
591
586 def buildstate(repo, dest, rebaseset, collapse):
592 def buildstate(repo, dest, rebaseset, collapse):
587 '''Define which revisions are going to be rebased and where
593 '''Define which revisions are going to be rebased and where
588
594
589 repo: repo
595 repo: repo
590 dest: context
596 dest: context
591 rebaseset: set of rev
597 rebaseset: set of rev
592 '''
598 '''
593
599
594 # This check isn't strictly necessary, since mq detects commits over an
600 # This check isn't strictly necessary, since mq detects commits over an
595 # applied patch. But it prevents messing up the working directory when
601 # applied patch. But it prevents messing up the working directory when
596 # a partially completed rebase is blocked by mq.
602 # a partially completed rebase is blocked by mq.
597 if 'qtip' in repo.tags() and (dest.node() in
603 if 'qtip' in repo.tags() and (dest.node() in
598 [s.node for s in repo.mq.applied]):
604 [s.node for s in repo.mq.applied]):
599 raise util.Abort(_('cannot rebase onto an applied mq patch'))
605 raise util.Abort(_('cannot rebase onto an applied mq patch'))
600
606
601 roots = list(repo.set('roots(%ld)', rebaseset))
607 roots = list(repo.set('roots(%ld)', rebaseset))
602 if not roots:
608 if not roots:
603 raise util.Abort(_('no matching revisions'))
609 raise util.Abort(_('no matching revisions'))
604 if len(roots) > 1:
610 if len(roots) > 1:
605 raise util.Abort(_("can't rebase multiple roots"))
611 raise util.Abort(_("can't rebase multiple roots"))
606 root = roots[0]
612 root = roots[0]
607
613
608 commonbase = root.ancestor(dest)
614 commonbase = root.ancestor(dest)
609 if commonbase == root:
615 if commonbase == root:
610 raise util.Abort(_('source is ancestor of destination'))
616 raise util.Abort(_('source is ancestor of destination'))
611 if commonbase == dest:
617 if commonbase == dest:
612 samebranch = root.branch() == dest.branch()
618 samebranch = root.branch() == dest.branch()
613 if not collapse and samebranch and root in dest.children():
619 if not collapse and samebranch and root in dest.children():
614 repo.ui.debug('source is a child of destination\n')
620 repo.ui.debug('source is a child of destination\n')
615 return None
621 return None
616
622
617 repo.ui.debug('rebase onto %d starting from %d\n' % (dest, root))
623 repo.ui.debug('rebase onto %d starting from %d\n' % (dest, root))
618 state = dict.fromkeys(rebaseset, nullrev)
624 state = dict.fromkeys(rebaseset, nullrev)
619 # Rebase tries to turn <dest> into a parent of <root> while
625 # Rebase tries to turn <dest> into a parent of <root> while
620 # preserving the number of parents of rebased changesets:
626 # preserving the number of parents of rebased changesets:
621 #
627 #
622 # - A changeset with a single parent will always be rebased as a
628 # - A changeset with a single parent will always be rebased as a
623 # changeset with a single parent.
629 # changeset with a single parent.
624 #
630 #
625 # - A merge will be rebased as merge unless its parents are both
631 # - A merge will be rebased as merge unless its parents are both
626 # ancestors of <dest> or are themselves in the rebased set and
632 # ancestors of <dest> or are themselves in the rebased set and
627 # pruned while rebased.
633 # pruned while rebased.
628 #
634 #
629 # If one parent of <root> is an ancestor of <dest>, the rebased
635 # If one parent of <root> is an ancestor of <dest>, the rebased
630 # version of this parent will be <dest>. This is always true with
636 # version of this parent will be <dest>. This is always true with
631 # --base option.
637 # --base option.
632 #
638 #
633 # Otherwise, we need to *replace* the original parents with
639 # Otherwise, we need to *replace* the original parents with
634 # <dest>. This "detaches" the rebased set from its former location
640 # <dest>. This "detaches" the rebased set from its former location
635 # and rebases it onto <dest>. Changes introduced by ancestors of
641 # and rebases it onto <dest>. Changes introduced by ancestors of
636 # <root> not common with <dest> (the detachset, marked as
642 # <root> not common with <dest> (the detachset, marked as
637 # nullmerge) are "removed" from the rebased changesets.
643 # nullmerge) are "removed" from the rebased changesets.
638 #
644 #
639 # - If <root> has a single parent, set it to <dest>.
645 # - If <root> has a single parent, set it to <dest>.
640 #
646 #
641 # - If <root> is a merge, we cannot decide which parent to
647 # - If <root> is a merge, we cannot decide which parent to
642 # replace, the rebase operation is not clearly defined.
648 # replace, the rebase operation is not clearly defined.
643 #
649 #
644 # The table below sums up this behavior:
650 # The table below sums up this behavior:
645 #
651 #
646 # +--------------------+----------------------+-------------------------+
652 # +--------------------+----------------------+-------------------------+
647 # | | one parent | merge |
653 # | | one parent | merge |
648 # +--------------------+----------------------+-------------------------+
654 # +--------------------+----------------------+-------------------------+
649 # | parent in ::<dest> | new parent is <dest> | parents in ::<dest> are |
655 # | parent in ::<dest> | new parent is <dest> | parents in ::<dest> are |
650 # | | | remapped to <dest> |
656 # | | | remapped to <dest> |
651 # +--------------------+----------------------+-------------------------+
657 # +--------------------+----------------------+-------------------------+
652 # | unrelated source | new parent is <dest> | ambiguous, abort |
658 # | unrelated source | new parent is <dest> | ambiguous, abort |
653 # +--------------------+----------------------+-------------------------+
659 # +--------------------+----------------------+-------------------------+
654 #
660 #
655 # The actual abort is handled by `defineparents`
661 # The actual abort is handled by `defineparents`
656 if len(root.parents()) <= 1:
662 if len(root.parents()) <= 1:
657 # (strict) ancestors of <root> not ancestors of <dest>
663 # (strict) ancestors of <root> not ancestors of <dest>
658 detachset = repo.revs('::%d - ::%d - %d', root, commonbase, root)
664 detachset = repo.revs('::%d - ::%d - %d', root, commonbase, root)
659 state.update(dict.fromkeys(detachset, nullmerge))
665 state.update(dict.fromkeys(detachset, nullmerge))
660 return repo['.'].rev(), dest.rev(), state
666 return repo['.'].rev(), dest.rev(), state
661
667
662 def pullrebase(orig, ui, repo, *args, **opts):
668 def pullrebase(orig, ui, repo, *args, **opts):
663 'Call rebase after pull if the latter has been invoked with --rebase'
669 'Call rebase after pull if the latter has been invoked with --rebase'
664 if opts.get('rebase'):
670 if opts.get('rebase'):
665 if opts.get('update'):
671 if opts.get('update'):
666 del opts['update']
672 del opts['update']
667 ui.debug('--update and --rebase are not compatible, ignoring '
673 ui.debug('--update and --rebase are not compatible, ignoring '
668 'the update flag\n')
674 'the update flag\n')
669
675
670 movemarkfrom = repo['.'].node()
676 movemarkfrom = repo['.'].node()
671 cmdutil.bailifchanged(repo)
677 cmdutil.bailifchanged(repo)
672 revsprepull = len(repo)
678 revsprepull = len(repo)
673 origpostincoming = commands.postincoming
679 origpostincoming = commands.postincoming
674 def _dummy(*args, **kwargs):
680 def _dummy(*args, **kwargs):
675 pass
681 pass
676 commands.postincoming = _dummy
682 commands.postincoming = _dummy
677 try:
683 try:
678 orig(ui, repo, *args, **opts)
684 orig(ui, repo, *args, **opts)
679 finally:
685 finally:
680 commands.postincoming = origpostincoming
686 commands.postincoming = origpostincoming
681 revspostpull = len(repo)
687 revspostpull = len(repo)
682 if revspostpull > revsprepull:
688 if revspostpull > revsprepull:
683 rebase(ui, repo, **opts)
689 rebase(ui, repo, **opts)
684 branch = repo[None].branch()
690 branch = repo[None].branch()
685 dest = repo[branch].rev()
691 dest = repo[branch].rev()
686 if dest != repo['.'].rev():
692 if dest != repo['.'].rev():
687 # there was nothing to rebase we force an update
693 # there was nothing to rebase we force an update
688 hg.update(repo, dest)
694 hg.update(repo, dest)
689 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
695 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
690 ui.status(_("updating bookmark %s\n")
696 ui.status(_("updating bookmark %s\n")
691 % repo._bookmarkcurrent)
697 % repo._bookmarkcurrent)
692 else:
698 else:
693 if opts.get('tool'):
699 if opts.get('tool'):
694 raise util.Abort(_('--tool can only be used with --rebase'))
700 raise util.Abort(_('--tool can only be used with --rebase'))
695 orig(ui, repo, *args, **opts)
701 orig(ui, repo, *args, **opts)
696
702
697 def uisetup(ui):
703 def uisetup(ui):
698 'Replace pull with a decorator to provide --rebase option'
704 'Replace pull with a decorator to provide --rebase option'
699 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
705 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
700 entry[1].append(('', 'rebase', None,
706 entry[1].append(('', 'rebase', None,
701 _("rebase working directory to branch head")))
707 _("rebase working directory to branch head")))
702 entry[1].append(('t', 'tool', '',
708 entry[1].append(('t', 'tool', '',
703 _("specify merge tool for rebase")))
709 _("specify merge tool for rebase")))
@@ -1,1656 +1,1660 b''
1 # cmdutil.py - help for command processing in mercurial
1 # cmdutil.py - help for command processing in mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from node import hex, nullid, nullrev, short
8 from node import hex, nullid, nullrev, short
9 from i18n import _
9 from i18n import _
10 import os, sys, errno, re, tempfile
10 import os, sys, errno, re, tempfile
11 import util, scmutil, templater, patch, error, templatekw, revlog, copies
11 import util, scmutil, templater, patch, error, templatekw, revlog, copies
12 import match as matchmod
12 import match as matchmod
13 import subrepo, context, repair, bookmarks
13 import subrepo, context, repair, bookmarks
14
14
15 def parsealiases(cmd):
15 def parsealiases(cmd):
16 return cmd.lstrip("^").split("|")
16 return cmd.lstrip("^").split("|")
17
17
18 def findpossible(cmd, table, strict=False):
18 def findpossible(cmd, table, strict=False):
19 """
19 """
20 Return cmd -> (aliases, command table entry)
20 Return cmd -> (aliases, command table entry)
21 for each matching command.
21 for each matching command.
22 Return debug commands (or their aliases) only if no normal command matches.
22 Return debug commands (or their aliases) only if no normal command matches.
23 """
23 """
24 choice = {}
24 choice = {}
25 debugchoice = {}
25 debugchoice = {}
26
26
27 if cmd in table:
27 if cmd in table:
28 # short-circuit exact matches, "log" alias beats "^log|history"
28 # short-circuit exact matches, "log" alias beats "^log|history"
29 keys = [cmd]
29 keys = [cmd]
30 else:
30 else:
31 keys = table.keys()
31 keys = table.keys()
32
32
33 for e in keys:
33 for e in keys:
34 aliases = parsealiases(e)
34 aliases = parsealiases(e)
35 found = None
35 found = None
36 if cmd in aliases:
36 if cmd in aliases:
37 found = cmd
37 found = cmd
38 elif not strict:
38 elif not strict:
39 for a in aliases:
39 for a in aliases:
40 if a.startswith(cmd):
40 if a.startswith(cmd):
41 found = a
41 found = a
42 break
42 break
43 if found is not None:
43 if found is not None:
44 if aliases[0].startswith("debug") or found.startswith("debug"):
44 if aliases[0].startswith("debug") or found.startswith("debug"):
45 debugchoice[found] = (aliases, table[e])
45 debugchoice[found] = (aliases, table[e])
46 else:
46 else:
47 choice[found] = (aliases, table[e])
47 choice[found] = (aliases, table[e])
48
48
49 if not choice and debugchoice:
49 if not choice and debugchoice:
50 choice = debugchoice
50 choice = debugchoice
51
51
52 return choice
52 return choice
53
53
54 def findcmd(cmd, table, strict=True):
54 def findcmd(cmd, table, strict=True):
55 """Return (aliases, command table entry) for command string."""
55 """Return (aliases, command table entry) for command string."""
56 choice = findpossible(cmd, table, strict)
56 choice = findpossible(cmd, table, strict)
57
57
58 if cmd in choice:
58 if cmd in choice:
59 return choice[cmd]
59 return choice[cmd]
60
60
61 if len(choice) > 1:
61 if len(choice) > 1:
62 clist = choice.keys()
62 clist = choice.keys()
63 clist.sort()
63 clist.sort()
64 raise error.AmbiguousCommand(cmd, clist)
64 raise error.AmbiguousCommand(cmd, clist)
65
65
66 if choice:
66 if choice:
67 return choice.values()[0]
67 return choice.values()[0]
68
68
69 raise error.UnknownCommand(cmd)
69 raise error.UnknownCommand(cmd)
70
70
71 def findrepo(p):
71 def findrepo(p):
72 while not os.path.isdir(os.path.join(p, ".hg")):
72 while not os.path.isdir(os.path.join(p, ".hg")):
73 oldp, p = p, os.path.dirname(p)
73 oldp, p = p, os.path.dirname(p)
74 if p == oldp:
74 if p == oldp:
75 return None
75 return None
76
76
77 return p
77 return p
78
78
79 def bailifchanged(repo):
79 def bailifchanged(repo):
80 if repo.dirstate.p2() != nullid:
80 if repo.dirstate.p2() != nullid:
81 raise util.Abort(_('outstanding uncommitted merge'))
81 raise util.Abort(_('outstanding uncommitted merge'))
82 modified, added, removed, deleted = repo.status()[:4]
82 modified, added, removed, deleted = repo.status()[:4]
83 if modified or added or removed or deleted:
83 if modified or added or removed or deleted:
84 raise util.Abort(_("outstanding uncommitted changes"))
84 raise util.Abort(_("outstanding uncommitted changes"))
85 ctx = repo[None]
85 ctx = repo[None]
86 for s in ctx.substate:
86 for s in ctx.substate:
87 if ctx.sub(s).dirty():
87 if ctx.sub(s).dirty():
88 raise util.Abort(_("uncommitted changes in subrepo %s") % s)
88 raise util.Abort(_("uncommitted changes in subrepo %s") % s)
89
89
90 def logmessage(ui, opts):
90 def logmessage(ui, opts):
91 """ get the log message according to -m and -l option """
91 """ get the log message according to -m and -l option """
92 message = opts.get('message')
92 message = opts.get('message')
93 logfile = opts.get('logfile')
93 logfile = opts.get('logfile')
94
94
95 if message and logfile:
95 if message and logfile:
96 raise util.Abort(_('options --message and --logfile are mutually '
96 raise util.Abort(_('options --message and --logfile are mutually '
97 'exclusive'))
97 'exclusive'))
98 if not message and logfile:
98 if not message and logfile:
99 try:
99 try:
100 if logfile == '-':
100 if logfile == '-':
101 message = ui.fin.read()
101 message = ui.fin.read()
102 else:
102 else:
103 message = '\n'.join(util.readfile(logfile).splitlines())
103 message = '\n'.join(util.readfile(logfile).splitlines())
104 except IOError, inst:
104 except IOError, inst:
105 raise util.Abort(_("can't read commit message '%s': %s") %
105 raise util.Abort(_("can't read commit message '%s': %s") %
106 (logfile, inst.strerror))
106 (logfile, inst.strerror))
107 return message
107 return message
108
108
109 def loglimit(opts):
109 def loglimit(opts):
110 """get the log limit according to option -l/--limit"""
110 """get the log limit according to option -l/--limit"""
111 limit = opts.get('limit')
111 limit = opts.get('limit')
112 if limit:
112 if limit:
113 try:
113 try:
114 limit = int(limit)
114 limit = int(limit)
115 except ValueError:
115 except ValueError:
116 raise util.Abort(_('limit must be a positive integer'))
116 raise util.Abort(_('limit must be a positive integer'))
117 if limit <= 0:
117 if limit <= 0:
118 raise util.Abort(_('limit must be positive'))
118 raise util.Abort(_('limit must be positive'))
119 else:
119 else:
120 limit = None
120 limit = None
121 return limit
121 return limit
122
122
123 def makefilename(repo, pat, node, desc=None,
123 def makefilename(repo, pat, node, desc=None,
124 total=None, seqno=None, revwidth=None, pathname=None):
124 total=None, seqno=None, revwidth=None, pathname=None):
125 node_expander = {
125 node_expander = {
126 'H': lambda: hex(node),
126 'H': lambda: hex(node),
127 'R': lambda: str(repo.changelog.rev(node)),
127 'R': lambda: str(repo.changelog.rev(node)),
128 'h': lambda: short(node),
128 'h': lambda: short(node),
129 'm': lambda: re.sub('[^\w]', '_', str(desc))
129 'm': lambda: re.sub('[^\w]', '_', str(desc))
130 }
130 }
131 expander = {
131 expander = {
132 '%': lambda: '%',
132 '%': lambda: '%',
133 'b': lambda: os.path.basename(repo.root),
133 'b': lambda: os.path.basename(repo.root),
134 }
134 }
135
135
136 try:
136 try:
137 if node:
137 if node:
138 expander.update(node_expander)
138 expander.update(node_expander)
139 if node:
139 if node:
140 expander['r'] = (lambda:
140 expander['r'] = (lambda:
141 str(repo.changelog.rev(node)).zfill(revwidth or 0))
141 str(repo.changelog.rev(node)).zfill(revwidth or 0))
142 if total is not None:
142 if total is not None:
143 expander['N'] = lambda: str(total)
143 expander['N'] = lambda: str(total)
144 if seqno is not None:
144 if seqno is not None:
145 expander['n'] = lambda: str(seqno)
145 expander['n'] = lambda: str(seqno)
146 if total is not None and seqno is not None:
146 if total is not None and seqno is not None:
147 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
147 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
148 if pathname is not None:
148 if pathname is not None:
149 expander['s'] = lambda: os.path.basename(pathname)
149 expander['s'] = lambda: os.path.basename(pathname)
150 expander['d'] = lambda: os.path.dirname(pathname) or '.'
150 expander['d'] = lambda: os.path.dirname(pathname) or '.'
151 expander['p'] = lambda: pathname
151 expander['p'] = lambda: pathname
152
152
153 newname = []
153 newname = []
154 patlen = len(pat)
154 patlen = len(pat)
155 i = 0
155 i = 0
156 while i < patlen:
156 while i < patlen:
157 c = pat[i]
157 c = pat[i]
158 if c == '%':
158 if c == '%':
159 i += 1
159 i += 1
160 c = pat[i]
160 c = pat[i]
161 c = expander[c]()
161 c = expander[c]()
162 newname.append(c)
162 newname.append(c)
163 i += 1
163 i += 1
164 return ''.join(newname)
164 return ''.join(newname)
165 except KeyError, inst:
165 except KeyError, inst:
166 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
166 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
167 inst.args[0])
167 inst.args[0])
168
168
169 def makefileobj(repo, pat, node=None, desc=None, total=None,
169 def makefileobj(repo, pat, node=None, desc=None, total=None,
170 seqno=None, revwidth=None, mode='wb', pathname=None):
170 seqno=None, revwidth=None, mode='wb', pathname=None):
171
171
172 writable = mode not in ('r', 'rb')
172 writable = mode not in ('r', 'rb')
173
173
174 if not pat or pat == '-':
174 if not pat or pat == '-':
175 fp = writable and repo.ui.fout or repo.ui.fin
175 fp = writable and repo.ui.fout or repo.ui.fin
176 if util.safehasattr(fp, 'fileno'):
176 if util.safehasattr(fp, 'fileno'):
177 return os.fdopen(os.dup(fp.fileno()), mode)
177 return os.fdopen(os.dup(fp.fileno()), mode)
178 else:
178 else:
179 # if this fp can't be duped properly, return
179 # if this fp can't be duped properly, return
180 # a dummy object that can be closed
180 # a dummy object that can be closed
181 class wrappedfileobj(object):
181 class wrappedfileobj(object):
182 noop = lambda x: None
182 noop = lambda x: None
183 def __init__(self, f):
183 def __init__(self, f):
184 self.f = f
184 self.f = f
185 def __getattr__(self, attr):
185 def __getattr__(self, attr):
186 if attr == 'close':
186 if attr == 'close':
187 return self.noop
187 return self.noop
188 else:
188 else:
189 return getattr(self.f, attr)
189 return getattr(self.f, attr)
190
190
191 return wrappedfileobj(fp)
191 return wrappedfileobj(fp)
192 if util.safehasattr(pat, 'write') and writable:
192 if util.safehasattr(pat, 'write') and writable:
193 return pat
193 return pat
194 if util.safehasattr(pat, 'read') and 'r' in mode:
194 if util.safehasattr(pat, 'read') and 'r' in mode:
195 return pat
195 return pat
196 return open(makefilename(repo, pat, node, desc, total, seqno, revwidth,
196 return open(makefilename(repo, pat, node, desc, total, seqno, revwidth,
197 pathname),
197 pathname),
198 mode)
198 mode)
199
199
200 def openrevlog(repo, cmd, file_, opts):
200 def openrevlog(repo, cmd, file_, opts):
201 """opens the changelog, manifest, a filelog or a given revlog"""
201 """opens the changelog, manifest, a filelog or a given revlog"""
202 cl = opts['changelog']
202 cl = opts['changelog']
203 mf = opts['manifest']
203 mf = opts['manifest']
204 msg = None
204 msg = None
205 if cl and mf:
205 if cl and mf:
206 msg = _('cannot specify --changelog and --manifest at the same time')
206 msg = _('cannot specify --changelog and --manifest at the same time')
207 elif cl or mf:
207 elif cl or mf:
208 if file_:
208 if file_:
209 msg = _('cannot specify filename with --changelog or --manifest')
209 msg = _('cannot specify filename with --changelog or --manifest')
210 elif not repo:
210 elif not repo:
211 msg = _('cannot specify --changelog or --manifest '
211 msg = _('cannot specify --changelog or --manifest '
212 'without a repository')
212 'without a repository')
213 if msg:
213 if msg:
214 raise util.Abort(msg)
214 raise util.Abort(msg)
215
215
216 r = None
216 r = None
217 if repo:
217 if repo:
218 if cl:
218 if cl:
219 r = repo.changelog
219 r = repo.changelog
220 elif mf:
220 elif mf:
221 r = repo.manifest
221 r = repo.manifest
222 elif file_:
222 elif file_:
223 filelog = repo.file(file_)
223 filelog = repo.file(file_)
224 if len(filelog):
224 if len(filelog):
225 r = filelog
225 r = filelog
226 if not r:
226 if not r:
227 if not file_:
227 if not file_:
228 raise error.CommandError(cmd, _('invalid arguments'))
228 raise error.CommandError(cmd, _('invalid arguments'))
229 if not os.path.isfile(file_):
229 if not os.path.isfile(file_):
230 raise util.Abort(_("revlog '%s' not found") % file_)
230 raise util.Abort(_("revlog '%s' not found") % file_)
231 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
231 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
232 file_[:-2] + ".i")
232 file_[:-2] + ".i")
233 return r
233 return r
234
234
235 def copy(ui, repo, pats, opts, rename=False):
235 def copy(ui, repo, pats, opts, rename=False):
236 # called with the repo lock held
236 # called with the repo lock held
237 #
237 #
238 # hgsep => pathname that uses "/" to separate directories
238 # hgsep => pathname that uses "/" to separate directories
239 # ossep => pathname that uses os.sep to separate directories
239 # ossep => pathname that uses os.sep to separate directories
240 cwd = repo.getcwd()
240 cwd = repo.getcwd()
241 targets = {}
241 targets = {}
242 after = opts.get("after")
242 after = opts.get("after")
243 dryrun = opts.get("dry_run")
243 dryrun = opts.get("dry_run")
244 wctx = repo[None]
244 wctx = repo[None]
245
245
246 def walkpat(pat):
246 def walkpat(pat):
247 srcs = []
247 srcs = []
248 badstates = after and '?' or '?r'
248 badstates = after and '?' or '?r'
249 m = scmutil.match(repo[None], [pat], opts, globbed=True)
249 m = scmutil.match(repo[None], [pat], opts, globbed=True)
250 for abs in repo.walk(m):
250 for abs in repo.walk(m):
251 state = repo.dirstate[abs]
251 state = repo.dirstate[abs]
252 rel = m.rel(abs)
252 rel = m.rel(abs)
253 exact = m.exact(abs)
253 exact = m.exact(abs)
254 if state in badstates:
254 if state in badstates:
255 if exact and state == '?':
255 if exact and state == '?':
256 ui.warn(_('%s: not copying - file is not managed\n') % rel)
256 ui.warn(_('%s: not copying - file is not managed\n') % rel)
257 if exact and state == 'r':
257 if exact and state == 'r':
258 ui.warn(_('%s: not copying - file has been marked for'
258 ui.warn(_('%s: not copying - file has been marked for'
259 ' remove\n') % rel)
259 ' remove\n') % rel)
260 continue
260 continue
261 # abs: hgsep
261 # abs: hgsep
262 # rel: ossep
262 # rel: ossep
263 srcs.append((abs, rel, exact))
263 srcs.append((abs, rel, exact))
264 return srcs
264 return srcs
265
265
266 # abssrc: hgsep
266 # abssrc: hgsep
267 # relsrc: ossep
267 # relsrc: ossep
268 # otarget: ossep
268 # otarget: ossep
269 def copyfile(abssrc, relsrc, otarget, exact):
269 def copyfile(abssrc, relsrc, otarget, exact):
270 abstarget = scmutil.canonpath(repo.root, cwd, otarget)
270 abstarget = scmutil.canonpath(repo.root, cwd, otarget)
271 if '/' in abstarget:
271 if '/' in abstarget:
272 # We cannot normalize abstarget itself, this would prevent
272 # We cannot normalize abstarget itself, this would prevent
273 # case only renames, like a => A.
273 # case only renames, like a => A.
274 abspath, absname = abstarget.rsplit('/', 1)
274 abspath, absname = abstarget.rsplit('/', 1)
275 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
275 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
276 reltarget = repo.pathto(abstarget, cwd)
276 reltarget = repo.pathto(abstarget, cwd)
277 target = repo.wjoin(abstarget)
277 target = repo.wjoin(abstarget)
278 src = repo.wjoin(abssrc)
278 src = repo.wjoin(abssrc)
279 state = repo.dirstate[abstarget]
279 state = repo.dirstate[abstarget]
280
280
281 scmutil.checkportable(ui, abstarget)
281 scmutil.checkportable(ui, abstarget)
282
282
283 # check for collisions
283 # check for collisions
284 prevsrc = targets.get(abstarget)
284 prevsrc = targets.get(abstarget)
285 if prevsrc is not None:
285 if prevsrc is not None:
286 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
286 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
287 (reltarget, repo.pathto(abssrc, cwd),
287 (reltarget, repo.pathto(abssrc, cwd),
288 repo.pathto(prevsrc, cwd)))
288 repo.pathto(prevsrc, cwd)))
289 return
289 return
290
290
291 # check for overwrites
291 # check for overwrites
292 exists = os.path.lexists(target)
292 exists = os.path.lexists(target)
293 samefile = False
293 samefile = False
294 if exists and abssrc != abstarget:
294 if exists and abssrc != abstarget:
295 if (repo.dirstate.normalize(abssrc) ==
295 if (repo.dirstate.normalize(abssrc) ==
296 repo.dirstate.normalize(abstarget)):
296 repo.dirstate.normalize(abstarget)):
297 if not rename:
297 if not rename:
298 ui.warn(_("%s: can't copy - same file\n") % reltarget)
298 ui.warn(_("%s: can't copy - same file\n") % reltarget)
299 return
299 return
300 exists = False
300 exists = False
301 samefile = True
301 samefile = True
302
302
303 if not after and exists or after and state in 'mn':
303 if not after and exists or after and state in 'mn':
304 if not opts['force']:
304 if not opts['force']:
305 ui.warn(_('%s: not overwriting - file exists\n') %
305 ui.warn(_('%s: not overwriting - file exists\n') %
306 reltarget)
306 reltarget)
307 return
307 return
308
308
309 if after:
309 if after:
310 if not exists:
310 if not exists:
311 if rename:
311 if rename:
312 ui.warn(_('%s: not recording move - %s does not exist\n') %
312 ui.warn(_('%s: not recording move - %s does not exist\n') %
313 (relsrc, reltarget))
313 (relsrc, reltarget))
314 else:
314 else:
315 ui.warn(_('%s: not recording copy - %s does not exist\n') %
315 ui.warn(_('%s: not recording copy - %s does not exist\n') %
316 (relsrc, reltarget))
316 (relsrc, reltarget))
317 return
317 return
318 elif not dryrun:
318 elif not dryrun:
319 try:
319 try:
320 if exists:
320 if exists:
321 os.unlink(target)
321 os.unlink(target)
322 targetdir = os.path.dirname(target) or '.'
322 targetdir = os.path.dirname(target) or '.'
323 if not os.path.isdir(targetdir):
323 if not os.path.isdir(targetdir):
324 os.makedirs(targetdir)
324 os.makedirs(targetdir)
325 if samefile:
325 if samefile:
326 tmp = target + "~hgrename"
326 tmp = target + "~hgrename"
327 os.rename(src, tmp)
327 os.rename(src, tmp)
328 os.rename(tmp, target)
328 os.rename(tmp, target)
329 else:
329 else:
330 util.copyfile(src, target)
330 util.copyfile(src, target)
331 srcexists = True
331 srcexists = True
332 except IOError, inst:
332 except IOError, inst:
333 if inst.errno == errno.ENOENT:
333 if inst.errno == errno.ENOENT:
334 ui.warn(_('%s: deleted in working copy\n') % relsrc)
334 ui.warn(_('%s: deleted in working copy\n') % relsrc)
335 srcexists = False
335 srcexists = False
336 else:
336 else:
337 ui.warn(_('%s: cannot copy - %s\n') %
337 ui.warn(_('%s: cannot copy - %s\n') %
338 (relsrc, inst.strerror))
338 (relsrc, inst.strerror))
339 return True # report a failure
339 return True # report a failure
340
340
341 if ui.verbose or not exact:
341 if ui.verbose or not exact:
342 if rename:
342 if rename:
343 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
343 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
344 else:
344 else:
345 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
345 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
346
346
347 targets[abstarget] = abssrc
347 targets[abstarget] = abssrc
348
348
349 # fix up dirstate
349 # fix up dirstate
350 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
350 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
351 dryrun=dryrun, cwd=cwd)
351 dryrun=dryrun, cwd=cwd)
352 if rename and not dryrun:
352 if rename and not dryrun:
353 if not after and srcexists and not samefile:
353 if not after and srcexists and not samefile:
354 util.unlinkpath(repo.wjoin(abssrc))
354 util.unlinkpath(repo.wjoin(abssrc))
355 wctx.forget([abssrc])
355 wctx.forget([abssrc])
356
356
357 # pat: ossep
357 # pat: ossep
358 # dest ossep
358 # dest ossep
359 # srcs: list of (hgsep, hgsep, ossep, bool)
359 # srcs: list of (hgsep, hgsep, ossep, bool)
360 # return: function that takes hgsep and returns ossep
360 # return: function that takes hgsep and returns ossep
361 def targetpathfn(pat, dest, srcs):
361 def targetpathfn(pat, dest, srcs):
362 if os.path.isdir(pat):
362 if os.path.isdir(pat):
363 abspfx = scmutil.canonpath(repo.root, cwd, pat)
363 abspfx = scmutil.canonpath(repo.root, cwd, pat)
364 abspfx = util.localpath(abspfx)
364 abspfx = util.localpath(abspfx)
365 if destdirexists:
365 if destdirexists:
366 striplen = len(os.path.split(abspfx)[0])
366 striplen = len(os.path.split(abspfx)[0])
367 else:
367 else:
368 striplen = len(abspfx)
368 striplen = len(abspfx)
369 if striplen:
369 if striplen:
370 striplen += len(os.sep)
370 striplen += len(os.sep)
371 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
371 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
372 elif destdirexists:
372 elif destdirexists:
373 res = lambda p: os.path.join(dest,
373 res = lambda p: os.path.join(dest,
374 os.path.basename(util.localpath(p)))
374 os.path.basename(util.localpath(p)))
375 else:
375 else:
376 res = lambda p: dest
376 res = lambda p: dest
377 return res
377 return res
378
378
379 # pat: ossep
379 # pat: ossep
380 # dest ossep
380 # dest ossep
381 # srcs: list of (hgsep, hgsep, ossep, bool)
381 # srcs: list of (hgsep, hgsep, ossep, bool)
382 # return: function that takes hgsep and returns ossep
382 # return: function that takes hgsep and returns ossep
383 def targetpathafterfn(pat, dest, srcs):
383 def targetpathafterfn(pat, dest, srcs):
384 if matchmod.patkind(pat):
384 if matchmod.patkind(pat):
385 # a mercurial pattern
385 # a mercurial pattern
386 res = lambda p: os.path.join(dest,
386 res = lambda p: os.path.join(dest,
387 os.path.basename(util.localpath(p)))
387 os.path.basename(util.localpath(p)))
388 else:
388 else:
389 abspfx = scmutil.canonpath(repo.root, cwd, pat)
389 abspfx = scmutil.canonpath(repo.root, cwd, pat)
390 if len(abspfx) < len(srcs[0][0]):
390 if len(abspfx) < len(srcs[0][0]):
391 # A directory. Either the target path contains the last
391 # A directory. Either the target path contains the last
392 # component of the source path or it does not.
392 # component of the source path or it does not.
393 def evalpath(striplen):
393 def evalpath(striplen):
394 score = 0
394 score = 0
395 for s in srcs:
395 for s in srcs:
396 t = os.path.join(dest, util.localpath(s[0])[striplen:])
396 t = os.path.join(dest, util.localpath(s[0])[striplen:])
397 if os.path.lexists(t):
397 if os.path.lexists(t):
398 score += 1
398 score += 1
399 return score
399 return score
400
400
401 abspfx = util.localpath(abspfx)
401 abspfx = util.localpath(abspfx)
402 striplen = len(abspfx)
402 striplen = len(abspfx)
403 if striplen:
403 if striplen:
404 striplen += len(os.sep)
404 striplen += len(os.sep)
405 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
405 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
406 score = evalpath(striplen)
406 score = evalpath(striplen)
407 striplen1 = len(os.path.split(abspfx)[0])
407 striplen1 = len(os.path.split(abspfx)[0])
408 if striplen1:
408 if striplen1:
409 striplen1 += len(os.sep)
409 striplen1 += len(os.sep)
410 if evalpath(striplen1) > score:
410 if evalpath(striplen1) > score:
411 striplen = striplen1
411 striplen = striplen1
412 res = lambda p: os.path.join(dest,
412 res = lambda p: os.path.join(dest,
413 util.localpath(p)[striplen:])
413 util.localpath(p)[striplen:])
414 else:
414 else:
415 # a file
415 # a file
416 if destdirexists:
416 if destdirexists:
417 res = lambda p: os.path.join(dest,
417 res = lambda p: os.path.join(dest,
418 os.path.basename(util.localpath(p)))
418 os.path.basename(util.localpath(p)))
419 else:
419 else:
420 res = lambda p: dest
420 res = lambda p: dest
421 return res
421 return res
422
422
423
423
424 pats = scmutil.expandpats(pats)
424 pats = scmutil.expandpats(pats)
425 if not pats:
425 if not pats:
426 raise util.Abort(_('no source or destination specified'))
426 raise util.Abort(_('no source or destination specified'))
427 if len(pats) == 1:
427 if len(pats) == 1:
428 raise util.Abort(_('no destination specified'))
428 raise util.Abort(_('no destination specified'))
429 dest = pats.pop()
429 dest = pats.pop()
430 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
430 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
431 if not destdirexists:
431 if not destdirexists:
432 if len(pats) > 1 or matchmod.patkind(pats[0]):
432 if len(pats) > 1 or matchmod.patkind(pats[0]):
433 raise util.Abort(_('with multiple sources, destination must be an '
433 raise util.Abort(_('with multiple sources, destination must be an '
434 'existing directory'))
434 'existing directory'))
435 if util.endswithsep(dest):
435 if util.endswithsep(dest):
436 raise util.Abort(_('destination %s is not a directory') % dest)
436 raise util.Abort(_('destination %s is not a directory') % dest)
437
437
438 tfn = targetpathfn
438 tfn = targetpathfn
439 if after:
439 if after:
440 tfn = targetpathafterfn
440 tfn = targetpathafterfn
441 copylist = []
441 copylist = []
442 for pat in pats:
442 for pat in pats:
443 srcs = walkpat(pat)
443 srcs = walkpat(pat)
444 if not srcs:
444 if not srcs:
445 continue
445 continue
446 copylist.append((tfn(pat, dest, srcs), srcs))
446 copylist.append((tfn(pat, dest, srcs), srcs))
447 if not copylist:
447 if not copylist:
448 raise util.Abort(_('no files to copy'))
448 raise util.Abort(_('no files to copy'))
449
449
450 errors = 0
450 errors = 0
451 for targetpath, srcs in copylist:
451 for targetpath, srcs in copylist:
452 for abssrc, relsrc, exact in srcs:
452 for abssrc, relsrc, exact in srcs:
453 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
453 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
454 errors += 1
454 errors += 1
455
455
456 if errors:
456 if errors:
457 ui.warn(_('(consider using --after)\n'))
457 ui.warn(_('(consider using --after)\n'))
458
458
459 return errors != 0
459 return errors != 0
460
460
461 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
461 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
462 runargs=None, appendpid=False):
462 runargs=None, appendpid=False):
463 '''Run a command as a service.'''
463 '''Run a command as a service.'''
464
464
465 if opts['daemon'] and not opts['daemon_pipefds']:
465 if opts['daemon'] and not opts['daemon_pipefds']:
466 # Signal child process startup with file removal
466 # Signal child process startup with file removal
467 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
467 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
468 os.close(lockfd)
468 os.close(lockfd)
469 try:
469 try:
470 if not runargs:
470 if not runargs:
471 runargs = util.hgcmd() + sys.argv[1:]
471 runargs = util.hgcmd() + sys.argv[1:]
472 runargs.append('--daemon-pipefds=%s' % lockpath)
472 runargs.append('--daemon-pipefds=%s' % lockpath)
473 # Don't pass --cwd to the child process, because we've already
473 # Don't pass --cwd to the child process, because we've already
474 # changed directory.
474 # changed directory.
475 for i in xrange(1, len(runargs)):
475 for i in xrange(1, len(runargs)):
476 if runargs[i].startswith('--cwd='):
476 if runargs[i].startswith('--cwd='):
477 del runargs[i]
477 del runargs[i]
478 break
478 break
479 elif runargs[i].startswith('--cwd'):
479 elif runargs[i].startswith('--cwd'):
480 del runargs[i:i + 2]
480 del runargs[i:i + 2]
481 break
481 break
482 def condfn():
482 def condfn():
483 return not os.path.exists(lockpath)
483 return not os.path.exists(lockpath)
484 pid = util.rundetached(runargs, condfn)
484 pid = util.rundetached(runargs, condfn)
485 if pid < 0:
485 if pid < 0:
486 raise util.Abort(_('child process failed to start'))
486 raise util.Abort(_('child process failed to start'))
487 finally:
487 finally:
488 try:
488 try:
489 os.unlink(lockpath)
489 os.unlink(lockpath)
490 except OSError, e:
490 except OSError, e:
491 if e.errno != errno.ENOENT:
491 if e.errno != errno.ENOENT:
492 raise
492 raise
493 if parentfn:
493 if parentfn:
494 return parentfn(pid)
494 return parentfn(pid)
495 else:
495 else:
496 return
496 return
497
497
498 if initfn:
498 if initfn:
499 initfn()
499 initfn()
500
500
501 if opts['pid_file']:
501 if opts['pid_file']:
502 mode = appendpid and 'a' or 'w'
502 mode = appendpid and 'a' or 'w'
503 fp = open(opts['pid_file'], mode)
503 fp = open(opts['pid_file'], mode)
504 fp.write(str(os.getpid()) + '\n')
504 fp.write(str(os.getpid()) + '\n')
505 fp.close()
505 fp.close()
506
506
507 if opts['daemon_pipefds']:
507 if opts['daemon_pipefds']:
508 lockpath = opts['daemon_pipefds']
508 lockpath = opts['daemon_pipefds']
509 try:
509 try:
510 os.setsid()
510 os.setsid()
511 except AttributeError:
511 except AttributeError:
512 pass
512 pass
513 os.unlink(lockpath)
513 os.unlink(lockpath)
514 util.hidewindow()
514 util.hidewindow()
515 sys.stdout.flush()
515 sys.stdout.flush()
516 sys.stderr.flush()
516 sys.stderr.flush()
517
517
518 nullfd = os.open(util.nulldev, os.O_RDWR)
518 nullfd = os.open(util.nulldev, os.O_RDWR)
519 logfilefd = nullfd
519 logfilefd = nullfd
520 if logfile:
520 if logfile:
521 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
521 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
522 os.dup2(nullfd, 0)
522 os.dup2(nullfd, 0)
523 os.dup2(logfilefd, 1)
523 os.dup2(logfilefd, 1)
524 os.dup2(logfilefd, 2)
524 os.dup2(logfilefd, 2)
525 if nullfd not in (0, 1, 2):
525 if nullfd not in (0, 1, 2):
526 os.close(nullfd)
526 os.close(nullfd)
527 if logfile and logfilefd not in (0, 1, 2):
527 if logfile and logfilefd not in (0, 1, 2):
528 os.close(logfilefd)
528 os.close(logfilefd)
529
529
530 if runfn:
530 if runfn:
531 return runfn()
531 return runfn()
532
532
533 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
533 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
534 opts=None):
534 opts=None):
535 '''export changesets as hg patches.'''
535 '''export changesets as hg patches.'''
536
536
537 total = len(revs)
537 total = len(revs)
538 revwidth = max([len(str(rev)) for rev in revs])
538 revwidth = max([len(str(rev)) for rev in revs])
539
539
540 def single(rev, seqno, fp):
540 def single(rev, seqno, fp):
541 ctx = repo[rev]
541 ctx = repo[rev]
542 node = ctx.node()
542 node = ctx.node()
543 parents = [p.node() for p in ctx.parents() if p]
543 parents = [p.node() for p in ctx.parents() if p]
544 branch = ctx.branch()
544 branch = ctx.branch()
545 if switch_parent:
545 if switch_parent:
546 parents.reverse()
546 parents.reverse()
547 prev = (parents and parents[0]) or nullid
547 prev = (parents and parents[0]) or nullid
548
548
549 shouldclose = False
549 shouldclose = False
550 if not fp:
550 if not fp:
551 desc_lines = ctx.description().rstrip().split('\n')
551 desc_lines = ctx.description().rstrip().split('\n')
552 desc = desc_lines[0] #Commit always has a first line.
552 desc = desc_lines[0] #Commit always has a first line.
553 fp = makefileobj(repo, template, node, desc=desc, total=total,
553 fp = makefileobj(repo, template, node, desc=desc, total=total,
554 seqno=seqno, revwidth=revwidth, mode='ab')
554 seqno=seqno, revwidth=revwidth, mode='ab')
555 if fp != template:
555 if fp != template:
556 shouldclose = True
556 shouldclose = True
557 if fp != sys.stdout and util.safehasattr(fp, 'name'):
557 if fp != sys.stdout and util.safehasattr(fp, 'name'):
558 repo.ui.note("%s\n" % fp.name)
558 repo.ui.note("%s\n" % fp.name)
559
559
560 fp.write("# HG changeset patch\n")
560 fp.write("# HG changeset patch\n")
561 fp.write("# User %s\n" % ctx.user())
561 fp.write("# User %s\n" % ctx.user())
562 fp.write("# Date %d %d\n" % ctx.date())
562 fp.write("# Date %d %d\n" % ctx.date())
563 if branch and branch != 'default':
563 if branch and branch != 'default':
564 fp.write("# Branch %s\n" % branch)
564 fp.write("# Branch %s\n" % branch)
565 fp.write("# Node ID %s\n" % hex(node))
565 fp.write("# Node ID %s\n" % hex(node))
566 fp.write("# Parent %s\n" % hex(prev))
566 fp.write("# Parent %s\n" % hex(prev))
567 if len(parents) > 1:
567 if len(parents) > 1:
568 fp.write("# Parent %s\n" % hex(parents[1]))
568 fp.write("# Parent %s\n" % hex(parents[1]))
569 fp.write(ctx.description().rstrip())
569 fp.write(ctx.description().rstrip())
570 fp.write("\n\n")
570 fp.write("\n\n")
571
571
572 for chunk in patch.diff(repo, prev, node, opts=opts):
572 for chunk in patch.diff(repo, prev, node, opts=opts):
573 fp.write(chunk)
573 fp.write(chunk)
574
574
575 if shouldclose:
575 if shouldclose:
576 fp.close()
576 fp.close()
577
577
578 for seqno, rev in enumerate(revs):
578 for seqno, rev in enumerate(revs):
579 single(rev, seqno + 1, fp)
579 single(rev, seqno + 1, fp)
580
580
581 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
581 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
582 changes=None, stat=False, fp=None, prefix='',
582 changes=None, stat=False, fp=None, prefix='',
583 listsubrepos=False):
583 listsubrepos=False):
584 '''show diff or diffstat.'''
584 '''show diff or diffstat.'''
585 if fp is None:
585 if fp is None:
586 write = ui.write
586 write = ui.write
587 else:
587 else:
588 def write(s, **kw):
588 def write(s, **kw):
589 fp.write(s)
589 fp.write(s)
590
590
591 if stat:
591 if stat:
592 diffopts = diffopts.copy(context=0)
592 diffopts = diffopts.copy(context=0)
593 width = 80
593 width = 80
594 if not ui.plain():
594 if not ui.plain():
595 width = ui.termwidth()
595 width = ui.termwidth()
596 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
596 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
597 prefix=prefix)
597 prefix=prefix)
598 for chunk, label in patch.diffstatui(util.iterlines(chunks),
598 for chunk, label in patch.diffstatui(util.iterlines(chunks),
599 width=width,
599 width=width,
600 git=diffopts.git):
600 git=diffopts.git):
601 write(chunk, label=label)
601 write(chunk, label=label)
602 else:
602 else:
603 for chunk, label in patch.diffui(repo, node1, node2, match,
603 for chunk, label in patch.diffui(repo, node1, node2, match,
604 changes, diffopts, prefix=prefix):
604 changes, diffopts, prefix=prefix):
605 write(chunk, label=label)
605 write(chunk, label=label)
606
606
607 if listsubrepos:
607 if listsubrepos:
608 ctx1 = repo[node1]
608 ctx1 = repo[node1]
609 ctx2 = repo[node2]
609 ctx2 = repo[node2]
610 for subpath, sub in subrepo.itersubrepos(ctx1, ctx2):
610 for subpath, sub in subrepo.itersubrepos(ctx1, ctx2):
611 tempnode2 = node2
611 tempnode2 = node2
612 try:
612 try:
613 if node2 is not None:
613 if node2 is not None:
614 tempnode2 = ctx2.substate[subpath][1]
614 tempnode2 = ctx2.substate[subpath][1]
615 except KeyError:
615 except KeyError:
616 # A subrepo that existed in node1 was deleted between node1 and
616 # A subrepo that existed in node1 was deleted between node1 and
617 # node2 (inclusive). Thus, ctx2's substate won't contain that
617 # node2 (inclusive). Thus, ctx2's substate won't contain that
618 # subpath. The best we can do is to ignore it.
618 # subpath. The best we can do is to ignore it.
619 tempnode2 = None
619 tempnode2 = None
620 submatch = matchmod.narrowmatcher(subpath, match)
620 submatch = matchmod.narrowmatcher(subpath, match)
621 sub.diff(diffopts, tempnode2, submatch, changes=changes,
621 sub.diff(diffopts, tempnode2, submatch, changes=changes,
622 stat=stat, fp=fp, prefix=prefix)
622 stat=stat, fp=fp, prefix=prefix)
623
623
624 class changeset_printer(object):
624 class changeset_printer(object):
625 '''show changeset information when templating not requested.'''
625 '''show changeset information when templating not requested.'''
626
626
627 def __init__(self, ui, repo, patch, diffopts, buffered):
627 def __init__(self, ui, repo, patch, diffopts, buffered):
628 self.ui = ui
628 self.ui = ui
629 self.repo = repo
629 self.repo = repo
630 self.buffered = buffered
630 self.buffered = buffered
631 self.patch = patch
631 self.patch = patch
632 self.diffopts = diffopts
632 self.diffopts = diffopts
633 self.header = {}
633 self.header = {}
634 self.hunk = {}
634 self.hunk = {}
635 self.lastheader = None
635 self.lastheader = None
636 self.footer = None
636 self.footer = None
637
637
638 def flush(self, rev):
638 def flush(self, rev):
639 if rev in self.header:
639 if rev in self.header:
640 h = self.header[rev]
640 h = self.header[rev]
641 if h != self.lastheader:
641 if h != self.lastheader:
642 self.lastheader = h
642 self.lastheader = h
643 self.ui.write(h)
643 self.ui.write(h)
644 del self.header[rev]
644 del self.header[rev]
645 if rev in self.hunk:
645 if rev in self.hunk:
646 self.ui.write(self.hunk[rev])
646 self.ui.write(self.hunk[rev])
647 del self.hunk[rev]
647 del self.hunk[rev]
648 return 1
648 return 1
649 return 0
649 return 0
650
650
651 def close(self):
651 def close(self):
652 if self.footer:
652 if self.footer:
653 self.ui.write(self.footer)
653 self.ui.write(self.footer)
654
654
655 def show(self, ctx, copies=None, matchfn=None, **props):
655 def show(self, ctx, copies=None, matchfn=None, **props):
656 if self.buffered:
656 if self.buffered:
657 self.ui.pushbuffer()
657 self.ui.pushbuffer()
658 self._show(ctx, copies, matchfn, props)
658 self._show(ctx, copies, matchfn, props)
659 self.hunk[ctx.rev()] = self.ui.popbuffer(labeled=True)
659 self.hunk[ctx.rev()] = self.ui.popbuffer(labeled=True)
660 else:
660 else:
661 self._show(ctx, copies, matchfn, props)
661 self._show(ctx, copies, matchfn, props)
662
662
663 def _show(self, ctx, copies, matchfn, props):
663 def _show(self, ctx, copies, matchfn, props):
664 '''show a single changeset or file revision'''
664 '''show a single changeset or file revision'''
665 changenode = ctx.node()
665 changenode = ctx.node()
666 rev = ctx.rev()
666 rev = ctx.rev()
667
667
668 if self.ui.quiet:
668 if self.ui.quiet:
669 self.ui.write("%d:%s\n" % (rev, short(changenode)),
669 self.ui.write("%d:%s\n" % (rev, short(changenode)),
670 label='log.node')
670 label='log.node')
671 return
671 return
672
672
673 log = self.repo.changelog
673 log = self.repo.changelog
674 date = util.datestr(ctx.date())
674 date = util.datestr(ctx.date())
675
675
676 hexfunc = self.ui.debugflag and hex or short
676 hexfunc = self.ui.debugflag and hex or short
677
677
678 parents = [(p, hexfunc(log.node(p)))
678 parents = [(p, hexfunc(log.node(p)))
679 for p in self._meaningful_parentrevs(log, rev)]
679 for p in self._meaningful_parentrevs(log, rev)]
680
680
681 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)),
681 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)),
682 label='log.changeset')
682 label='log.changeset')
683
683
684 branch = ctx.branch()
684 branch = ctx.branch()
685 # don't show the default branch name
685 # don't show the default branch name
686 if branch != 'default':
686 if branch != 'default':
687 self.ui.write(_("branch: %s\n") % branch,
687 self.ui.write(_("branch: %s\n") % branch,
688 label='log.branch')
688 label='log.branch')
689 for bookmark in self.repo.nodebookmarks(changenode):
689 for bookmark in self.repo.nodebookmarks(changenode):
690 self.ui.write(_("bookmark: %s\n") % bookmark,
690 self.ui.write(_("bookmark: %s\n") % bookmark,
691 label='log.bookmark')
691 label='log.bookmark')
692 for tag in self.repo.nodetags(changenode):
692 for tag in self.repo.nodetags(changenode):
693 self.ui.write(_("tag: %s\n") % tag,
693 self.ui.write(_("tag: %s\n") % tag,
694 label='log.tag')
694 label='log.tag')
695 if self.ui.debugflag and ctx.phase():
695 if self.ui.debugflag and ctx.phase():
696 self.ui.write(_("phase: %s\n") % _(ctx.phasestr()),
696 self.ui.write(_("phase: %s\n") % _(ctx.phasestr()),
697 label='log.phase')
697 label='log.phase')
698 for parent in parents:
698 for parent in parents:
699 self.ui.write(_("parent: %d:%s\n") % parent,
699 self.ui.write(_("parent: %d:%s\n") % parent,
700 label='log.parent')
700 label='log.parent')
701
701
702 if self.ui.debugflag:
702 if self.ui.debugflag:
703 mnode = ctx.manifestnode()
703 mnode = ctx.manifestnode()
704 self.ui.write(_("manifest: %d:%s\n") %
704 self.ui.write(_("manifest: %d:%s\n") %
705 (self.repo.manifest.rev(mnode), hex(mnode)),
705 (self.repo.manifest.rev(mnode), hex(mnode)),
706 label='ui.debug log.manifest')
706 label='ui.debug log.manifest')
707 self.ui.write(_("user: %s\n") % ctx.user(),
707 self.ui.write(_("user: %s\n") % ctx.user(),
708 label='log.user')
708 label='log.user')
709 self.ui.write(_("date: %s\n") % date,
709 self.ui.write(_("date: %s\n") % date,
710 label='log.date')
710 label='log.date')
711
711
712 if self.ui.debugflag:
712 if self.ui.debugflag:
713 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
713 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
714 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
714 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
715 files):
715 files):
716 if value:
716 if value:
717 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
717 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
718 label='ui.debug log.files')
718 label='ui.debug log.files')
719 elif ctx.files() and self.ui.verbose:
719 elif ctx.files() and self.ui.verbose:
720 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
720 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
721 label='ui.note log.files')
721 label='ui.note log.files')
722 if copies and self.ui.verbose:
722 if copies and self.ui.verbose:
723 copies = ['%s (%s)' % c for c in copies]
723 copies = ['%s (%s)' % c for c in copies]
724 self.ui.write(_("copies: %s\n") % ' '.join(copies),
724 self.ui.write(_("copies: %s\n") % ' '.join(copies),
725 label='ui.note log.copies')
725 label='ui.note log.copies')
726
726
727 extra = ctx.extra()
727 extra = ctx.extra()
728 if extra and self.ui.debugflag:
728 if extra and self.ui.debugflag:
729 for key, value in sorted(extra.items()):
729 for key, value in sorted(extra.items()):
730 self.ui.write(_("extra: %s=%s\n")
730 self.ui.write(_("extra: %s=%s\n")
731 % (key, value.encode('string_escape')),
731 % (key, value.encode('string_escape')),
732 label='ui.debug log.extra')
732 label='ui.debug log.extra')
733
733
734 description = ctx.description().strip()
734 description = ctx.description().strip()
735 if description:
735 if description:
736 if self.ui.verbose:
736 if self.ui.verbose:
737 self.ui.write(_("description:\n"),
737 self.ui.write(_("description:\n"),
738 label='ui.note log.description')
738 label='ui.note log.description')
739 self.ui.write(description,
739 self.ui.write(description,
740 label='ui.note log.description')
740 label='ui.note log.description')
741 self.ui.write("\n\n")
741 self.ui.write("\n\n")
742 else:
742 else:
743 self.ui.write(_("summary: %s\n") %
743 self.ui.write(_("summary: %s\n") %
744 description.splitlines()[0],
744 description.splitlines()[0],
745 label='log.summary')
745 label='log.summary')
746 self.ui.write("\n")
746 self.ui.write("\n")
747
747
748 self.showpatch(changenode, matchfn)
748 self.showpatch(changenode, matchfn)
749
749
750 def showpatch(self, node, matchfn):
750 def showpatch(self, node, matchfn):
751 if not matchfn:
751 if not matchfn:
752 matchfn = self.patch
752 matchfn = self.patch
753 if matchfn:
753 if matchfn:
754 stat = self.diffopts.get('stat')
754 stat = self.diffopts.get('stat')
755 diff = self.diffopts.get('patch')
755 diff = self.diffopts.get('patch')
756 diffopts = patch.diffopts(self.ui, self.diffopts)
756 diffopts = patch.diffopts(self.ui, self.diffopts)
757 prev = self.repo.changelog.parents(node)[0]
757 prev = self.repo.changelog.parents(node)[0]
758 if stat:
758 if stat:
759 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
759 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
760 match=matchfn, stat=True)
760 match=matchfn, stat=True)
761 if diff:
761 if diff:
762 if stat:
762 if stat:
763 self.ui.write("\n")
763 self.ui.write("\n")
764 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
764 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
765 match=matchfn, stat=False)
765 match=matchfn, stat=False)
766 self.ui.write("\n")
766 self.ui.write("\n")
767
767
768 def _meaningful_parentrevs(self, log, rev):
768 def _meaningful_parentrevs(self, log, rev):
769 """Return list of meaningful (or all if debug) parentrevs for rev.
769 """Return list of meaningful (or all if debug) parentrevs for rev.
770
770
771 For merges (two non-nullrev revisions) both parents are meaningful.
771 For merges (two non-nullrev revisions) both parents are meaningful.
772 Otherwise the first parent revision is considered meaningful if it
772 Otherwise the first parent revision is considered meaningful if it
773 is not the preceding revision.
773 is not the preceding revision.
774 """
774 """
775 parents = log.parentrevs(rev)
775 parents = log.parentrevs(rev)
776 if not self.ui.debugflag and parents[1] == nullrev:
776 if not self.ui.debugflag and parents[1] == nullrev:
777 if parents[0] >= rev - 1:
777 if parents[0] >= rev - 1:
778 parents = []
778 parents = []
779 else:
779 else:
780 parents = [parents[0]]
780 parents = [parents[0]]
781 return parents
781 return parents
782
782
783
783
784 class changeset_templater(changeset_printer):
784 class changeset_templater(changeset_printer):
785 '''format changeset information.'''
785 '''format changeset information.'''
786
786
787 def __init__(self, ui, repo, patch, diffopts, mapfile, buffered):
787 def __init__(self, ui, repo, patch, diffopts, mapfile, buffered):
788 changeset_printer.__init__(self, ui, repo, patch, diffopts, buffered)
788 changeset_printer.__init__(self, ui, repo, patch, diffopts, buffered)
789 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
789 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
790 defaulttempl = {
790 defaulttempl = {
791 'parent': '{rev}:{node|formatnode} ',
791 'parent': '{rev}:{node|formatnode} ',
792 'manifest': '{rev}:{node|formatnode}',
792 'manifest': '{rev}:{node|formatnode}',
793 'file_copy': '{name} ({source})',
793 'file_copy': '{name} ({source})',
794 'extra': '{key}={value|stringescape}'
794 'extra': '{key}={value|stringescape}'
795 }
795 }
796 # filecopy is preserved for compatibility reasons
796 # filecopy is preserved for compatibility reasons
797 defaulttempl['filecopy'] = defaulttempl['file_copy']
797 defaulttempl['filecopy'] = defaulttempl['file_copy']
798 self.t = templater.templater(mapfile, {'formatnode': formatnode},
798 self.t = templater.templater(mapfile, {'formatnode': formatnode},
799 cache=defaulttempl)
799 cache=defaulttempl)
800 self.cache = {}
800 self.cache = {}
801
801
802 def use_template(self, t):
802 def use_template(self, t):
803 '''set template string to use'''
803 '''set template string to use'''
804 self.t.cache['changeset'] = t
804 self.t.cache['changeset'] = t
805
805
806 def _meaningful_parentrevs(self, ctx):
806 def _meaningful_parentrevs(self, ctx):
807 """Return list of meaningful (or all if debug) parentrevs for rev.
807 """Return list of meaningful (or all if debug) parentrevs for rev.
808 """
808 """
809 parents = ctx.parents()
809 parents = ctx.parents()
810 if len(parents) > 1:
810 if len(parents) > 1:
811 return parents
811 return parents
812 if self.ui.debugflag:
812 if self.ui.debugflag:
813 return [parents[0], self.repo['null']]
813 return [parents[0], self.repo['null']]
814 if parents[0].rev() >= ctx.rev() - 1:
814 if parents[0].rev() >= ctx.rev() - 1:
815 return []
815 return []
816 return parents
816 return parents
817
817
818 def _show(self, ctx, copies, matchfn, props):
818 def _show(self, ctx, copies, matchfn, props):
819 '''show a single changeset or file revision'''
819 '''show a single changeset or file revision'''
820
820
821 showlist = templatekw.showlist
821 showlist = templatekw.showlist
822
822
823 # showparents() behaviour depends on ui trace level which
823 # showparents() behaviour depends on ui trace level which
824 # causes unexpected behaviours at templating level and makes
824 # causes unexpected behaviours at templating level and makes
825 # it harder to extract it in a standalone function. Its
825 # it harder to extract it in a standalone function. Its
826 # behaviour cannot be changed so leave it here for now.
826 # behaviour cannot be changed so leave it here for now.
827 def showparents(**args):
827 def showparents(**args):
828 ctx = args['ctx']
828 ctx = args['ctx']
829 parents = [[('rev', p.rev()), ('node', p.hex())]
829 parents = [[('rev', p.rev()), ('node', p.hex())]
830 for p in self._meaningful_parentrevs(ctx)]
830 for p in self._meaningful_parentrevs(ctx)]
831 return showlist('parent', parents, **args)
831 return showlist('parent', parents, **args)
832
832
833 props = props.copy()
833 props = props.copy()
834 props.update(templatekw.keywords)
834 props.update(templatekw.keywords)
835 props['parents'] = showparents
835 props['parents'] = showparents
836 props['templ'] = self.t
836 props['templ'] = self.t
837 props['ctx'] = ctx
837 props['ctx'] = ctx
838 props['repo'] = self.repo
838 props['repo'] = self.repo
839 props['revcache'] = {'copies': copies}
839 props['revcache'] = {'copies': copies}
840 props['cache'] = self.cache
840 props['cache'] = self.cache
841
841
842 # find correct templates for current mode
842 # find correct templates for current mode
843
843
844 tmplmodes = [
844 tmplmodes = [
845 (True, None),
845 (True, None),
846 (self.ui.verbose, 'verbose'),
846 (self.ui.verbose, 'verbose'),
847 (self.ui.quiet, 'quiet'),
847 (self.ui.quiet, 'quiet'),
848 (self.ui.debugflag, 'debug'),
848 (self.ui.debugflag, 'debug'),
849 ]
849 ]
850
850
851 types = {'header': '', 'footer':'', 'changeset': 'changeset'}
851 types = {'header': '', 'footer':'', 'changeset': 'changeset'}
852 for mode, postfix in tmplmodes:
852 for mode, postfix in tmplmodes:
853 for type in types:
853 for type in types:
854 cur = postfix and ('%s_%s' % (type, postfix)) or type
854 cur = postfix and ('%s_%s' % (type, postfix)) or type
855 if mode and cur in self.t:
855 if mode and cur in self.t:
856 types[type] = cur
856 types[type] = cur
857
857
858 try:
858 try:
859
859
860 # write header
860 # write header
861 if types['header']:
861 if types['header']:
862 h = templater.stringify(self.t(types['header'], **props))
862 h = templater.stringify(self.t(types['header'], **props))
863 if self.buffered:
863 if self.buffered:
864 self.header[ctx.rev()] = h
864 self.header[ctx.rev()] = h
865 else:
865 else:
866 if self.lastheader != h:
866 if self.lastheader != h:
867 self.lastheader = h
867 self.lastheader = h
868 self.ui.write(h)
868 self.ui.write(h)
869
869
870 # write changeset metadata, then patch if requested
870 # write changeset metadata, then patch if requested
871 key = types['changeset']
871 key = types['changeset']
872 self.ui.write(templater.stringify(self.t(key, **props)))
872 self.ui.write(templater.stringify(self.t(key, **props)))
873 self.showpatch(ctx.node(), matchfn)
873 self.showpatch(ctx.node(), matchfn)
874
874
875 if types['footer']:
875 if types['footer']:
876 if not self.footer:
876 if not self.footer:
877 self.footer = templater.stringify(self.t(types['footer'],
877 self.footer = templater.stringify(self.t(types['footer'],
878 **props))
878 **props))
879
879
880 except KeyError, inst:
880 except KeyError, inst:
881 msg = _("%s: no key named '%s'")
881 msg = _("%s: no key named '%s'")
882 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
882 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
883 except SyntaxError, inst:
883 except SyntaxError, inst:
884 raise util.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
884 raise util.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
885
885
886 def show_changeset(ui, repo, opts, buffered=False):
886 def show_changeset(ui, repo, opts, buffered=False):
887 """show one changeset using template or regular display.
887 """show one changeset using template or regular display.
888
888
889 Display format will be the first non-empty hit of:
889 Display format will be the first non-empty hit of:
890 1. option 'template'
890 1. option 'template'
891 2. option 'style'
891 2. option 'style'
892 3. [ui] setting 'logtemplate'
892 3. [ui] setting 'logtemplate'
893 4. [ui] setting 'style'
893 4. [ui] setting 'style'
894 If all of these values are either the unset or the empty string,
894 If all of these values are either the unset or the empty string,
895 regular display via changeset_printer() is done.
895 regular display via changeset_printer() is done.
896 """
896 """
897 # options
897 # options
898 patch = False
898 patch = False
899 if opts.get('patch') or opts.get('stat'):
899 if opts.get('patch') or opts.get('stat'):
900 patch = scmutil.matchall(repo)
900 patch = scmutil.matchall(repo)
901
901
902 tmpl = opts.get('template')
902 tmpl = opts.get('template')
903 style = None
903 style = None
904 if tmpl:
904 if tmpl:
905 tmpl = templater.parsestring(tmpl, quoted=False)
905 tmpl = templater.parsestring(tmpl, quoted=False)
906 else:
906 else:
907 style = opts.get('style')
907 style = opts.get('style')
908
908
909 # ui settings
909 # ui settings
910 if not (tmpl or style):
910 if not (tmpl or style):
911 tmpl = ui.config('ui', 'logtemplate')
911 tmpl = ui.config('ui', 'logtemplate')
912 if tmpl:
912 if tmpl:
913 try:
913 try:
914 tmpl = templater.parsestring(tmpl)
914 tmpl = templater.parsestring(tmpl)
915 except SyntaxError:
915 except SyntaxError:
916 tmpl = templater.parsestring(tmpl, quoted=False)
916 tmpl = templater.parsestring(tmpl, quoted=False)
917 else:
917 else:
918 style = util.expandpath(ui.config('ui', 'style', ''))
918 style = util.expandpath(ui.config('ui', 'style', ''))
919
919
920 if not (tmpl or style):
920 if not (tmpl or style):
921 return changeset_printer(ui, repo, patch, opts, buffered)
921 return changeset_printer(ui, repo, patch, opts, buffered)
922
922
923 mapfile = None
923 mapfile = None
924 if style and not tmpl:
924 if style and not tmpl:
925 mapfile = style
925 mapfile = style
926 if not os.path.split(mapfile)[0]:
926 if not os.path.split(mapfile)[0]:
927 mapname = (templater.templatepath('map-cmdline.' + mapfile)
927 mapname = (templater.templatepath('map-cmdline.' + mapfile)
928 or templater.templatepath(mapfile))
928 or templater.templatepath(mapfile))
929 if mapname:
929 if mapname:
930 mapfile = mapname
930 mapfile = mapname
931
931
932 try:
932 try:
933 t = changeset_templater(ui, repo, patch, opts, mapfile, buffered)
933 t = changeset_templater(ui, repo, patch, opts, mapfile, buffered)
934 except SyntaxError, inst:
934 except SyntaxError, inst:
935 raise util.Abort(inst.args[0])
935 raise util.Abort(inst.args[0])
936 if tmpl:
936 if tmpl:
937 t.use_template(tmpl)
937 t.use_template(tmpl)
938 return t
938 return t
939
939
940 def finddate(ui, repo, date):
940 def finddate(ui, repo, date):
941 """Find the tipmost changeset that matches the given date spec"""
941 """Find the tipmost changeset that matches the given date spec"""
942
942
943 df = util.matchdate(date)
943 df = util.matchdate(date)
944 m = scmutil.matchall(repo)
944 m = scmutil.matchall(repo)
945 results = {}
945 results = {}
946
946
947 def prep(ctx, fns):
947 def prep(ctx, fns):
948 d = ctx.date()
948 d = ctx.date()
949 if df(d[0]):
949 if df(d[0]):
950 results[ctx.rev()] = d
950 results[ctx.rev()] = d
951
951
952 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
952 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
953 rev = ctx.rev()
953 rev = ctx.rev()
954 if rev in results:
954 if rev in results:
955 ui.status(_("found revision %s from %s\n") %
955 ui.status(_("found revision %s from %s\n") %
956 (rev, util.datestr(results[rev])))
956 (rev, util.datestr(results[rev])))
957 return str(rev)
957 return str(rev)
958
958
959 raise util.Abort(_("revision matching date not found"))
959 raise util.Abort(_("revision matching date not found"))
960
960
961 def increasingwindows(start, end, windowsize=8, sizelimit=512):
961 def increasingwindows(start, end, windowsize=8, sizelimit=512):
962 if start < end:
962 if start < end:
963 while start < end:
963 while start < end:
964 yield start, min(windowsize, end - start)
964 yield start, min(windowsize, end - start)
965 start += windowsize
965 start += windowsize
966 if windowsize < sizelimit:
966 if windowsize < sizelimit:
967 windowsize *= 2
967 windowsize *= 2
968 else:
968 else:
969 while start > end:
969 while start > end:
970 yield start, min(windowsize, start - end - 1)
970 yield start, min(windowsize, start - end - 1)
971 start -= windowsize
971 start -= windowsize
972 if windowsize < sizelimit:
972 if windowsize < sizelimit:
973 windowsize *= 2
973 windowsize *= 2
974
974
975 def walkchangerevs(repo, match, opts, prepare):
975 def walkchangerevs(repo, match, opts, prepare):
976 '''Iterate over files and the revs in which they changed.
976 '''Iterate over files and the revs in which they changed.
977
977
978 Callers most commonly need to iterate backwards over the history
978 Callers most commonly need to iterate backwards over the history
979 in which they are interested. Doing so has awful (quadratic-looking)
979 in which they are interested. Doing so has awful (quadratic-looking)
980 performance, so we use iterators in a "windowed" way.
980 performance, so we use iterators in a "windowed" way.
981
981
982 We walk a window of revisions in the desired order. Within the
982 We walk a window of revisions in the desired order. Within the
983 window, we first walk forwards to gather data, then in the desired
983 window, we first walk forwards to gather data, then in the desired
984 order (usually backwards) to display it.
984 order (usually backwards) to display it.
985
985
986 This function returns an iterator yielding contexts. Before
986 This function returns an iterator yielding contexts. Before
987 yielding each context, the iterator will first call the prepare
987 yielding each context, the iterator will first call the prepare
988 function on each context in the window in forward order.'''
988 function on each context in the window in forward order.'''
989
989
990 follow = opts.get('follow') or opts.get('follow_first')
990 follow = opts.get('follow') or opts.get('follow_first')
991
991
992 if not len(repo):
992 if not len(repo):
993 return []
993 return []
994
994
995 if follow:
995 if follow:
996 defrange = '%s:0' % repo['.'].rev()
996 defrange = '%s:0' % repo['.'].rev()
997 else:
997 else:
998 defrange = '-1:0'
998 defrange = '-1:0'
999 revs = scmutil.revrange(repo, opts['rev'] or [defrange])
999 revs = scmutil.revrange(repo, opts['rev'] or [defrange])
1000 if not revs:
1000 if not revs:
1001 return []
1001 return []
1002 wanted = set()
1002 wanted = set()
1003 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1003 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1004 fncache = {}
1004 fncache = {}
1005 change = repo.changectx
1005 change = repo.changectx
1006
1006
1007 # First step is to fill wanted, the set of revisions that we want to yield.
1007 # First step is to fill wanted, the set of revisions that we want to yield.
1008 # When it does not induce extra cost, we also fill fncache for revisions in
1008 # When it does not induce extra cost, we also fill fncache for revisions in
1009 # wanted: a cache of filenames that were changed (ctx.files()) and that
1009 # wanted: a cache of filenames that were changed (ctx.files()) and that
1010 # match the file filtering conditions.
1010 # match the file filtering conditions.
1011
1011
1012 if not slowpath and not match.files():
1012 if not slowpath and not match.files():
1013 # No files, no patterns. Display all revs.
1013 # No files, no patterns. Display all revs.
1014 wanted = set(revs)
1014 wanted = set(revs)
1015 copies = []
1015 copies = []
1016
1016
1017 if not slowpath and match.files():
1017 if not slowpath and match.files():
1018 # We only have to read through the filelog to find wanted revisions
1018 # We only have to read through the filelog to find wanted revisions
1019
1019
1020 minrev, maxrev = min(revs), max(revs)
1020 minrev, maxrev = min(revs), max(revs)
1021 def filerevgen(filelog, last):
1021 def filerevgen(filelog, last):
1022 """
1022 """
1023 Only files, no patterns. Check the history of each file.
1023 Only files, no patterns. Check the history of each file.
1024
1024
1025 Examines filelog entries within minrev, maxrev linkrev range
1025 Examines filelog entries within minrev, maxrev linkrev range
1026 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1026 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1027 tuples in backwards order
1027 tuples in backwards order
1028 """
1028 """
1029 cl_count = len(repo)
1029 cl_count = len(repo)
1030 revs = []
1030 revs = []
1031 for j in xrange(0, last + 1):
1031 for j in xrange(0, last + 1):
1032 linkrev = filelog.linkrev(j)
1032 linkrev = filelog.linkrev(j)
1033 if linkrev < minrev:
1033 if linkrev < minrev:
1034 continue
1034 continue
1035 # only yield rev for which we have the changelog, it can
1035 # only yield rev for which we have the changelog, it can
1036 # happen while doing "hg log" during a pull or commit
1036 # happen while doing "hg log" during a pull or commit
1037 if linkrev >= cl_count:
1037 if linkrev >= cl_count:
1038 break
1038 break
1039
1039
1040 parentlinkrevs = []
1040 parentlinkrevs = []
1041 for p in filelog.parentrevs(j):
1041 for p in filelog.parentrevs(j):
1042 if p != nullrev:
1042 if p != nullrev:
1043 parentlinkrevs.append(filelog.linkrev(p))
1043 parentlinkrevs.append(filelog.linkrev(p))
1044 n = filelog.node(j)
1044 n = filelog.node(j)
1045 revs.append((linkrev, parentlinkrevs,
1045 revs.append((linkrev, parentlinkrevs,
1046 follow and filelog.renamed(n)))
1046 follow and filelog.renamed(n)))
1047
1047
1048 return reversed(revs)
1048 return reversed(revs)
1049 def iterfiles():
1049 def iterfiles():
1050 pctx = repo['.']
1050 pctx = repo['.']
1051 for filename in match.files():
1051 for filename in match.files():
1052 if follow:
1052 if follow:
1053 if filename not in pctx:
1053 if filename not in pctx:
1054 raise util.Abort(_('cannot follow file not in parent '
1054 raise util.Abort(_('cannot follow file not in parent '
1055 'revision: "%s"') % filename)
1055 'revision: "%s"') % filename)
1056 yield filename, pctx[filename].filenode()
1056 yield filename, pctx[filename].filenode()
1057 else:
1057 else:
1058 yield filename, None
1058 yield filename, None
1059 for filename_node in copies:
1059 for filename_node in copies:
1060 yield filename_node
1060 yield filename_node
1061 for file_, node in iterfiles():
1061 for file_, node in iterfiles():
1062 filelog = repo.file(file_)
1062 filelog = repo.file(file_)
1063 if not len(filelog):
1063 if not len(filelog):
1064 if node is None:
1064 if node is None:
1065 # A zero count may be a directory or deleted file, so
1065 # A zero count may be a directory or deleted file, so
1066 # try to find matching entries on the slow path.
1066 # try to find matching entries on the slow path.
1067 if follow:
1067 if follow:
1068 raise util.Abort(
1068 raise util.Abort(
1069 _('cannot follow nonexistent file: "%s"') % file_)
1069 _('cannot follow nonexistent file: "%s"') % file_)
1070 slowpath = True
1070 slowpath = True
1071 break
1071 break
1072 else:
1072 else:
1073 continue
1073 continue
1074
1074
1075 if node is None:
1075 if node is None:
1076 last = len(filelog) - 1
1076 last = len(filelog) - 1
1077 else:
1077 else:
1078 last = filelog.rev(node)
1078 last = filelog.rev(node)
1079
1079
1080
1080
1081 # keep track of all ancestors of the file
1081 # keep track of all ancestors of the file
1082 ancestors = set([filelog.linkrev(last)])
1082 ancestors = set([filelog.linkrev(last)])
1083
1083
1084 # iterate from latest to oldest revision
1084 # iterate from latest to oldest revision
1085 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1085 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1086 if not follow:
1086 if not follow:
1087 if rev > maxrev:
1087 if rev > maxrev:
1088 continue
1088 continue
1089 else:
1089 else:
1090 # Note that last might not be the first interesting
1090 # Note that last might not be the first interesting
1091 # rev to us:
1091 # rev to us:
1092 # if the file has been changed after maxrev, we'll
1092 # if the file has been changed after maxrev, we'll
1093 # have linkrev(last) > maxrev, and we still need
1093 # have linkrev(last) > maxrev, and we still need
1094 # to explore the file graph
1094 # to explore the file graph
1095 if rev not in ancestors:
1095 if rev not in ancestors:
1096 continue
1096 continue
1097 # XXX insert 1327 fix here
1097 # XXX insert 1327 fix here
1098 if flparentlinkrevs:
1098 if flparentlinkrevs:
1099 ancestors.update(flparentlinkrevs)
1099 ancestors.update(flparentlinkrevs)
1100
1100
1101 fncache.setdefault(rev, []).append(file_)
1101 fncache.setdefault(rev, []).append(file_)
1102 wanted.add(rev)
1102 wanted.add(rev)
1103 if copied:
1103 if copied:
1104 copies.append(copied)
1104 copies.append(copied)
1105 if slowpath:
1105 if slowpath:
1106 # We have to read the changelog to match filenames against
1106 # We have to read the changelog to match filenames against
1107 # changed files
1107 # changed files
1108
1108
1109 if follow:
1109 if follow:
1110 raise util.Abort(_('can only follow copies/renames for explicit '
1110 raise util.Abort(_('can only follow copies/renames for explicit '
1111 'filenames'))
1111 'filenames'))
1112
1112
1113 # The slow path checks files modified in every changeset.
1113 # The slow path checks files modified in every changeset.
1114 for i in sorted(revs):
1114 for i in sorted(revs):
1115 ctx = change(i)
1115 ctx = change(i)
1116 matches = filter(match, ctx.files())
1116 matches = filter(match, ctx.files())
1117 if matches:
1117 if matches:
1118 fncache[i] = matches
1118 fncache[i] = matches
1119 wanted.add(i)
1119 wanted.add(i)
1120
1120
1121 class followfilter(object):
1121 class followfilter(object):
1122 def __init__(self, onlyfirst=False):
1122 def __init__(self, onlyfirst=False):
1123 self.startrev = nullrev
1123 self.startrev = nullrev
1124 self.roots = set()
1124 self.roots = set()
1125 self.onlyfirst = onlyfirst
1125 self.onlyfirst = onlyfirst
1126
1126
1127 def match(self, rev):
1127 def match(self, rev):
1128 def realparents(rev):
1128 def realparents(rev):
1129 if self.onlyfirst:
1129 if self.onlyfirst:
1130 return repo.changelog.parentrevs(rev)[0:1]
1130 return repo.changelog.parentrevs(rev)[0:1]
1131 else:
1131 else:
1132 return filter(lambda x: x != nullrev,
1132 return filter(lambda x: x != nullrev,
1133 repo.changelog.parentrevs(rev))
1133 repo.changelog.parentrevs(rev))
1134
1134
1135 if self.startrev == nullrev:
1135 if self.startrev == nullrev:
1136 self.startrev = rev
1136 self.startrev = rev
1137 return True
1137 return True
1138
1138
1139 if rev > self.startrev:
1139 if rev > self.startrev:
1140 # forward: all descendants
1140 # forward: all descendants
1141 if not self.roots:
1141 if not self.roots:
1142 self.roots.add(self.startrev)
1142 self.roots.add(self.startrev)
1143 for parent in realparents(rev):
1143 for parent in realparents(rev):
1144 if parent in self.roots:
1144 if parent in self.roots:
1145 self.roots.add(rev)
1145 self.roots.add(rev)
1146 return True
1146 return True
1147 else:
1147 else:
1148 # backwards: all parents
1148 # backwards: all parents
1149 if not self.roots:
1149 if not self.roots:
1150 self.roots.update(realparents(self.startrev))
1150 self.roots.update(realparents(self.startrev))
1151 if rev in self.roots:
1151 if rev in self.roots:
1152 self.roots.remove(rev)
1152 self.roots.remove(rev)
1153 self.roots.update(realparents(rev))
1153 self.roots.update(realparents(rev))
1154 return True
1154 return True
1155
1155
1156 return False
1156 return False
1157
1157
1158 # it might be worthwhile to do this in the iterator if the rev range
1158 # it might be worthwhile to do this in the iterator if the rev range
1159 # is descending and the prune args are all within that range
1159 # is descending and the prune args are all within that range
1160 for rev in opts.get('prune', ()):
1160 for rev in opts.get('prune', ()):
1161 rev = repo[rev].rev()
1161 rev = repo[rev].rev()
1162 ff = followfilter()
1162 ff = followfilter()
1163 stop = min(revs[0], revs[-1])
1163 stop = min(revs[0], revs[-1])
1164 for x in xrange(rev, stop - 1, -1):
1164 for x in xrange(rev, stop - 1, -1):
1165 if ff.match(x):
1165 if ff.match(x):
1166 wanted.discard(x)
1166 wanted.discard(x)
1167
1167
1168 # Now that wanted is correctly initialized, we can iterate over the
1168 # Now that wanted is correctly initialized, we can iterate over the
1169 # revision range, yielding only revisions in wanted.
1169 # revision range, yielding only revisions in wanted.
1170 def iterate():
1170 def iterate():
1171 if follow and not match.files():
1171 if follow and not match.files():
1172 ff = followfilter(onlyfirst=opts.get('follow_first'))
1172 ff = followfilter(onlyfirst=opts.get('follow_first'))
1173 def want(rev):
1173 def want(rev):
1174 return ff.match(rev) and rev in wanted
1174 return ff.match(rev) and rev in wanted
1175 else:
1175 else:
1176 def want(rev):
1176 def want(rev):
1177 return rev in wanted
1177 return rev in wanted
1178
1178
1179 for i, window in increasingwindows(0, len(revs)):
1179 for i, window in increasingwindows(0, len(revs)):
1180 nrevs = [rev for rev in revs[i:i + window] if want(rev)]
1180 nrevs = [rev for rev in revs[i:i + window] if want(rev)]
1181 for rev in sorted(nrevs):
1181 for rev in sorted(nrevs):
1182 fns = fncache.get(rev)
1182 fns = fncache.get(rev)
1183 ctx = change(rev)
1183 ctx = change(rev)
1184 if not fns:
1184 if not fns:
1185 def fns_generator():
1185 def fns_generator():
1186 for f in ctx.files():
1186 for f in ctx.files():
1187 if match(f):
1187 if match(f):
1188 yield f
1188 yield f
1189 fns = fns_generator()
1189 fns = fns_generator()
1190 prepare(ctx, fns)
1190 prepare(ctx, fns)
1191 for rev in nrevs:
1191 for rev in nrevs:
1192 yield change(rev)
1192 yield change(rev)
1193 return iterate()
1193 return iterate()
1194
1194
1195 def add(ui, repo, match, dryrun, listsubrepos, prefix, explicitonly):
1195 def add(ui, repo, match, dryrun, listsubrepos, prefix, explicitonly):
1196 join = lambda f: os.path.join(prefix, f)
1196 join = lambda f: os.path.join(prefix, f)
1197 bad = []
1197 bad = []
1198 oldbad = match.bad
1198 oldbad = match.bad
1199 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
1199 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
1200 names = []
1200 names = []
1201 wctx = repo[None]
1201 wctx = repo[None]
1202 cca = None
1202 cca = None
1203 abort, warn = scmutil.checkportabilityalert(ui)
1203 abort, warn = scmutil.checkportabilityalert(ui)
1204 if abort or warn:
1204 if abort or warn:
1205 cca = scmutil.casecollisionauditor(ui, abort, wctx)
1205 cca = scmutil.casecollisionauditor(ui, abort, wctx)
1206 for f in repo.walk(match):
1206 for f in repo.walk(match):
1207 exact = match.exact(f)
1207 exact = match.exact(f)
1208 if exact or not explicitonly and f not in repo.dirstate:
1208 if exact or not explicitonly and f not in repo.dirstate:
1209 if cca:
1209 if cca:
1210 cca(f)
1210 cca(f)
1211 names.append(f)
1211 names.append(f)
1212 if ui.verbose or not exact:
1212 if ui.verbose or not exact:
1213 ui.status(_('adding %s\n') % match.rel(join(f)))
1213 ui.status(_('adding %s\n') % match.rel(join(f)))
1214
1214
1215 for subpath in wctx.substate:
1215 for subpath in wctx.substate:
1216 sub = wctx.sub(subpath)
1216 sub = wctx.sub(subpath)
1217 try:
1217 try:
1218 submatch = matchmod.narrowmatcher(subpath, match)
1218 submatch = matchmod.narrowmatcher(subpath, match)
1219 if listsubrepos:
1219 if listsubrepos:
1220 bad.extend(sub.add(ui, submatch, dryrun, listsubrepos, prefix,
1220 bad.extend(sub.add(ui, submatch, dryrun, listsubrepos, prefix,
1221 False))
1221 False))
1222 else:
1222 else:
1223 bad.extend(sub.add(ui, submatch, dryrun, listsubrepos, prefix,
1223 bad.extend(sub.add(ui, submatch, dryrun, listsubrepos, prefix,
1224 True))
1224 True))
1225 except error.LookupError:
1225 except error.LookupError:
1226 ui.status(_("skipping missing subrepository: %s\n")
1226 ui.status(_("skipping missing subrepository: %s\n")
1227 % join(subpath))
1227 % join(subpath))
1228
1228
1229 if not dryrun:
1229 if not dryrun:
1230 rejected = wctx.add(names, prefix)
1230 rejected = wctx.add(names, prefix)
1231 bad.extend(f for f in rejected if f in match.files())
1231 bad.extend(f for f in rejected if f in match.files())
1232 return bad
1232 return bad
1233
1233
1234 def forget(ui, repo, match, prefix, explicitonly):
1234 def forget(ui, repo, match, prefix, explicitonly):
1235 join = lambda f: os.path.join(prefix, f)
1235 join = lambda f: os.path.join(prefix, f)
1236 bad = []
1236 bad = []
1237 oldbad = match.bad
1237 oldbad = match.bad
1238 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
1238 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
1239 wctx = repo[None]
1239 wctx = repo[None]
1240 forgot = []
1240 forgot = []
1241 s = repo.status(match=match, clean=True)
1241 s = repo.status(match=match, clean=True)
1242 forget = sorted(s[0] + s[1] + s[3] + s[6])
1242 forget = sorted(s[0] + s[1] + s[3] + s[6])
1243 if explicitonly:
1243 if explicitonly:
1244 forget = [f for f in forget if match.exact(f)]
1244 forget = [f for f in forget if match.exact(f)]
1245
1245
1246 for subpath in wctx.substate:
1246 for subpath in wctx.substate:
1247 sub = wctx.sub(subpath)
1247 sub = wctx.sub(subpath)
1248 try:
1248 try:
1249 submatch = matchmod.narrowmatcher(subpath, match)
1249 submatch = matchmod.narrowmatcher(subpath, match)
1250 subbad, subforgot = sub.forget(ui, submatch, prefix)
1250 subbad, subforgot = sub.forget(ui, submatch, prefix)
1251 bad.extend([subpath + '/' + f for f in subbad])
1251 bad.extend([subpath + '/' + f for f in subbad])
1252 forgot.extend([subpath + '/' + f for f in subforgot])
1252 forgot.extend([subpath + '/' + f for f in subforgot])
1253 except error.LookupError:
1253 except error.LookupError:
1254 ui.status(_("skipping missing subrepository: %s\n")
1254 ui.status(_("skipping missing subrepository: %s\n")
1255 % join(subpath))
1255 % join(subpath))
1256
1256
1257 if not explicitonly:
1257 if not explicitonly:
1258 for f in match.files():
1258 for f in match.files():
1259 if f not in repo.dirstate and not os.path.isdir(match.rel(join(f))):
1259 if f not in repo.dirstate and not os.path.isdir(match.rel(join(f))):
1260 if f not in forgot:
1260 if f not in forgot:
1261 if os.path.exists(match.rel(join(f))):
1261 if os.path.exists(match.rel(join(f))):
1262 ui.warn(_('not removing %s: '
1262 ui.warn(_('not removing %s: '
1263 'file is already untracked\n')
1263 'file is already untracked\n')
1264 % match.rel(join(f)))
1264 % match.rel(join(f)))
1265 bad.append(f)
1265 bad.append(f)
1266
1266
1267 for f in forget:
1267 for f in forget:
1268 if ui.verbose or not match.exact(f):
1268 if ui.verbose or not match.exact(f):
1269 ui.status(_('removing %s\n') % match.rel(join(f)))
1269 ui.status(_('removing %s\n') % match.rel(join(f)))
1270
1270
1271 rejected = wctx.forget(forget, prefix)
1271 rejected = wctx.forget(forget, prefix)
1272 bad.extend(f for f in rejected if f in match.files())
1272 bad.extend(f for f in rejected if f in match.files())
1273 forgot.extend(forget)
1273 forgot.extend(forget)
1274 return bad, forgot
1274 return bad, forgot
1275
1275
1276 def duplicatecopies(repo, rev, p1):
1276 def duplicatecopies(repo, rev, p1):
1277 "Reproduce copies found in the source revision in the dirstate for grafts"
1277 "Reproduce copies found in the source revision in the dirstate for grafts"
1278 for dst, src in copies.pathcopies(repo[p1], repo[rev]).iteritems():
1278 for dst, src in copies.pathcopies(repo[p1], repo[rev]).iteritems():
1279 repo.dirstate.copy(src, dst)
1279 repo.dirstate.copy(src, dst)
1280
1280
1281 def commit(ui, repo, commitfunc, pats, opts):
1281 def commit(ui, repo, commitfunc, pats, opts):
1282 '''commit the specified files or all outstanding changes'''
1282 '''commit the specified files or all outstanding changes'''
1283 date = opts.get('date')
1283 date = opts.get('date')
1284 if date:
1284 if date:
1285 opts['date'] = util.parsedate(date)
1285 opts['date'] = util.parsedate(date)
1286 message = logmessage(ui, opts)
1286 message = logmessage(ui, opts)
1287
1287
1288 # extract addremove carefully -- this function can be called from a command
1288 # extract addremove carefully -- this function can be called from a command
1289 # that doesn't support addremove
1289 # that doesn't support addremove
1290 if opts.get('addremove'):
1290 if opts.get('addremove'):
1291 scmutil.addremove(repo, pats, opts)
1291 scmutil.addremove(repo, pats, opts)
1292
1292
1293 return commitfunc(ui, repo, message,
1293 return commitfunc(ui, repo, message,
1294 scmutil.match(repo[None], pats, opts), opts)
1294 scmutil.match(repo[None], pats, opts), opts)
1295
1295
1296 def amend(ui, repo, commitfunc, old, extra, pats, opts):
1296 def amend(ui, repo, commitfunc, old, extra, pats, opts):
1297 ui.note(_('amending changeset %s\n') % old)
1297 ui.note(_('amending changeset %s\n') % old)
1298 base = old.p1()
1298 base = old.p1()
1299
1299
1300 wlock = repo.wlock()
1300 wlock = repo.wlock()
1301 try:
1301 try:
1302 # First, do a regular commit to record all changes in the working
1302 # First, do a regular commit to record all changes in the working
1303 # directory (if there are any)
1303 # directory (if there are any)
1304 node = commit(ui, repo, commitfunc, pats, opts)
1304 ui.callhooks = False
1305 try:
1306 node = commit(ui, repo, commitfunc, pats, opts)
1307 finally:
1308 ui.callhooks = True
1305 ctx = repo[node]
1309 ctx = repo[node]
1306
1310
1307 # Participating changesets:
1311 # Participating changesets:
1308 #
1312 #
1309 # node/ctx o - new (intermediate) commit that contains changes from
1313 # node/ctx o - new (intermediate) commit that contains changes from
1310 # | working dir to go into amending commit (or a workingctx
1314 # | working dir to go into amending commit (or a workingctx
1311 # | if there were no changes)
1315 # | if there were no changes)
1312 # |
1316 # |
1313 # old o - changeset to amend
1317 # old o - changeset to amend
1314 # |
1318 # |
1315 # base o - parent of amending changeset
1319 # base o - parent of amending changeset
1316
1320
1317 # Update extra dict from amended commit (e.g. to preserve graft source)
1321 # Update extra dict from amended commit (e.g. to preserve graft source)
1318 extra.update(old.extra())
1322 extra.update(old.extra())
1319
1323
1320 # Also update it from the intermediate commit or from the wctx
1324 # Also update it from the intermediate commit or from the wctx
1321 extra.update(ctx.extra())
1325 extra.update(ctx.extra())
1322
1326
1323 files = set(old.files())
1327 files = set(old.files())
1324
1328
1325 # Second, we use either the commit we just did, or if there were no
1329 # Second, we use either the commit we just did, or if there were no
1326 # changes the parent of the working directory as the version of the
1330 # changes the parent of the working directory as the version of the
1327 # files in the final amend commit
1331 # files in the final amend commit
1328 if node:
1332 if node:
1329 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
1333 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
1330
1334
1331 user = ctx.user()
1335 user = ctx.user()
1332 date = ctx.date()
1336 date = ctx.date()
1333 message = ctx.description()
1337 message = ctx.description()
1334 # Recompute copies (avoid recording a -> b -> a)
1338 # Recompute copies (avoid recording a -> b -> a)
1335 copied = copies.pathcopies(base, ctx)
1339 copied = copies.pathcopies(base, ctx)
1336
1340
1337 # Prune files which were reverted by the updates: if old introduced
1341 # Prune files which were reverted by the updates: if old introduced
1338 # file X and our intermediate commit, node, renamed that file, then
1342 # file X and our intermediate commit, node, renamed that file, then
1339 # those two files are the same and we can discard X from our list
1343 # those two files are the same and we can discard X from our list
1340 # of files. Likewise if X was deleted, it's no longer relevant
1344 # of files. Likewise if X was deleted, it's no longer relevant
1341 files.update(ctx.files())
1345 files.update(ctx.files())
1342
1346
1343 def samefile(f):
1347 def samefile(f):
1344 if f in ctx.manifest():
1348 if f in ctx.manifest():
1345 a = ctx.filectx(f)
1349 a = ctx.filectx(f)
1346 if f in base.manifest():
1350 if f in base.manifest():
1347 b = base.filectx(f)
1351 b = base.filectx(f)
1348 return (not a.cmp(b)
1352 return (not a.cmp(b)
1349 and a.flags() == b.flags())
1353 and a.flags() == b.flags())
1350 else:
1354 else:
1351 return False
1355 return False
1352 else:
1356 else:
1353 return f not in base.manifest()
1357 return f not in base.manifest()
1354 files = [f for f in files if not samefile(f)]
1358 files = [f for f in files if not samefile(f)]
1355
1359
1356 def filectxfn(repo, ctx_, path):
1360 def filectxfn(repo, ctx_, path):
1357 try:
1361 try:
1358 fctx = ctx[path]
1362 fctx = ctx[path]
1359 flags = fctx.flags()
1363 flags = fctx.flags()
1360 mctx = context.memfilectx(fctx.path(), fctx.data(),
1364 mctx = context.memfilectx(fctx.path(), fctx.data(),
1361 islink='l' in flags,
1365 islink='l' in flags,
1362 isexec='x' in flags,
1366 isexec='x' in flags,
1363 copied=copied.get(path))
1367 copied=copied.get(path))
1364 return mctx
1368 return mctx
1365 except KeyError:
1369 except KeyError:
1366 raise IOError
1370 raise IOError
1367 else:
1371 else:
1368 ui.note(_('copying changeset %s to %s\n') % (old, base))
1372 ui.note(_('copying changeset %s to %s\n') % (old, base))
1369
1373
1370 # Use version of files as in the old cset
1374 # Use version of files as in the old cset
1371 def filectxfn(repo, ctx_, path):
1375 def filectxfn(repo, ctx_, path):
1372 try:
1376 try:
1373 return old.filectx(path)
1377 return old.filectx(path)
1374 except KeyError:
1378 except KeyError:
1375 raise IOError
1379 raise IOError
1376
1380
1377 # See if we got a message from -m or -l, if not, open the editor
1381 # See if we got a message from -m or -l, if not, open the editor
1378 # with the message of the changeset to amend
1382 # with the message of the changeset to amend
1379 user = opts.get('user') or old.user()
1383 user = opts.get('user') or old.user()
1380 date = opts.get('date') or old.date()
1384 date = opts.get('date') or old.date()
1381 message = logmessage(ui, opts)
1385 message = logmessage(ui, opts)
1382 if not message:
1386 if not message:
1383 cctx = context.workingctx(repo, old.description(), user, date,
1387 cctx = context.workingctx(repo, old.description(), user, date,
1384 extra,
1388 extra,
1385 repo.status(base.node(), old.node()))
1389 repo.status(base.node(), old.node()))
1386 message = commitforceeditor(repo, cctx, [])
1390 message = commitforceeditor(repo, cctx, [])
1387
1391
1388 new = context.memctx(repo,
1392 new = context.memctx(repo,
1389 parents=[base.node(), nullid],
1393 parents=[base.node(), nullid],
1390 text=message,
1394 text=message,
1391 files=files,
1395 files=files,
1392 filectxfn=filectxfn,
1396 filectxfn=filectxfn,
1393 user=user,
1397 user=user,
1394 date=date,
1398 date=date,
1395 extra=extra)
1399 extra=extra)
1396 newid = repo.commitctx(new)
1400 newid = repo.commitctx(new)
1397 if newid != old.node():
1401 if newid != old.node():
1398 # Reroute the working copy parent to the new changeset
1402 # Reroute the working copy parent to the new changeset
1399 repo.setparents(newid, nullid)
1403 repo.setparents(newid, nullid)
1400
1404
1401 # Move bookmarks from old parent to amend commit
1405 # Move bookmarks from old parent to amend commit
1402 bms = repo.nodebookmarks(old.node())
1406 bms = repo.nodebookmarks(old.node())
1403 if bms:
1407 if bms:
1404 for bm in bms:
1408 for bm in bms:
1405 repo._bookmarks[bm] = newid
1409 repo._bookmarks[bm] = newid
1406 bookmarks.write(repo)
1410 bookmarks.write(repo)
1407
1411
1408 # Strip the intermediate commit (if there was one) and the amended
1412 # Strip the intermediate commit (if there was one) and the amended
1409 # commit
1413 # commit
1410 lock = repo.lock()
1414 lock = repo.lock()
1411 try:
1415 try:
1412 if node:
1416 if node:
1413 ui.note(_('stripping intermediate changeset %s\n') % ctx)
1417 ui.note(_('stripping intermediate changeset %s\n') % ctx)
1414 ui.note(_('stripping amended changeset %s\n') % old)
1418 ui.note(_('stripping amended changeset %s\n') % old)
1415 repair.strip(ui, repo, old.node(), topic='amend-backup')
1419 repair.strip(ui, repo, old.node(), topic='amend-backup')
1416 finally:
1420 finally:
1417 lock.release()
1421 lock.release()
1418 finally:
1422 finally:
1419 wlock.release()
1423 wlock.release()
1420 return newid
1424 return newid
1421
1425
1422 def commiteditor(repo, ctx, subs):
1426 def commiteditor(repo, ctx, subs):
1423 if ctx.description():
1427 if ctx.description():
1424 return ctx.description()
1428 return ctx.description()
1425 return commitforceeditor(repo, ctx, subs)
1429 return commitforceeditor(repo, ctx, subs)
1426
1430
1427 def commitforceeditor(repo, ctx, subs):
1431 def commitforceeditor(repo, ctx, subs):
1428 edittext = []
1432 edittext = []
1429 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
1433 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
1430 if ctx.description():
1434 if ctx.description():
1431 edittext.append(ctx.description())
1435 edittext.append(ctx.description())
1432 edittext.append("")
1436 edittext.append("")
1433 edittext.append("") # Empty line between message and comments.
1437 edittext.append("") # Empty line between message and comments.
1434 edittext.append(_("HG: Enter commit message."
1438 edittext.append(_("HG: Enter commit message."
1435 " Lines beginning with 'HG:' are removed."))
1439 " Lines beginning with 'HG:' are removed."))
1436 edittext.append(_("HG: Leave message empty to abort commit."))
1440 edittext.append(_("HG: Leave message empty to abort commit."))
1437 edittext.append("HG: --")
1441 edittext.append("HG: --")
1438 edittext.append(_("HG: user: %s") % ctx.user())
1442 edittext.append(_("HG: user: %s") % ctx.user())
1439 if ctx.p2():
1443 if ctx.p2():
1440 edittext.append(_("HG: branch merge"))
1444 edittext.append(_("HG: branch merge"))
1441 if ctx.branch():
1445 if ctx.branch():
1442 edittext.append(_("HG: branch '%s'") % ctx.branch())
1446 edittext.append(_("HG: branch '%s'") % ctx.branch())
1443 edittext.extend([_("HG: subrepo %s") % s for s in subs])
1447 edittext.extend([_("HG: subrepo %s") % s for s in subs])
1444 edittext.extend([_("HG: added %s") % f for f in added])
1448 edittext.extend([_("HG: added %s") % f for f in added])
1445 edittext.extend([_("HG: changed %s") % f for f in modified])
1449 edittext.extend([_("HG: changed %s") % f for f in modified])
1446 edittext.extend([_("HG: removed %s") % f for f in removed])
1450 edittext.extend([_("HG: removed %s") % f for f in removed])
1447 if not added and not modified and not removed:
1451 if not added and not modified and not removed:
1448 edittext.append(_("HG: no files changed"))
1452 edittext.append(_("HG: no files changed"))
1449 edittext.append("")
1453 edittext.append("")
1450 # run editor in the repository root
1454 # run editor in the repository root
1451 olddir = os.getcwd()
1455 olddir = os.getcwd()
1452 os.chdir(repo.root)
1456 os.chdir(repo.root)
1453 text = repo.ui.edit("\n".join(edittext), ctx.user())
1457 text = repo.ui.edit("\n".join(edittext), ctx.user())
1454 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
1458 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
1455 os.chdir(olddir)
1459 os.chdir(olddir)
1456
1460
1457 if not text.strip():
1461 if not text.strip():
1458 raise util.Abort(_("empty commit message"))
1462 raise util.Abort(_("empty commit message"))
1459
1463
1460 return text
1464 return text
1461
1465
1462 def revert(ui, repo, ctx, parents, *pats, **opts):
1466 def revert(ui, repo, ctx, parents, *pats, **opts):
1463 parent, p2 = parents
1467 parent, p2 = parents
1464 node = ctx.node()
1468 node = ctx.node()
1465
1469
1466 mf = ctx.manifest()
1470 mf = ctx.manifest()
1467 if node == parent:
1471 if node == parent:
1468 pmf = mf
1472 pmf = mf
1469 else:
1473 else:
1470 pmf = None
1474 pmf = None
1471
1475
1472 # need all matching names in dirstate and manifest of target rev,
1476 # need all matching names in dirstate and manifest of target rev,
1473 # so have to walk both. do not print errors if files exist in one
1477 # so have to walk both. do not print errors if files exist in one
1474 # but not other.
1478 # but not other.
1475
1479
1476 names = {}
1480 names = {}
1477
1481
1478 wlock = repo.wlock()
1482 wlock = repo.wlock()
1479 try:
1483 try:
1480 # walk dirstate.
1484 # walk dirstate.
1481
1485
1482 m = scmutil.match(repo[None], pats, opts)
1486 m = scmutil.match(repo[None], pats, opts)
1483 m.bad = lambda x, y: False
1487 m.bad = lambda x, y: False
1484 for abs in repo.walk(m):
1488 for abs in repo.walk(m):
1485 names[abs] = m.rel(abs), m.exact(abs)
1489 names[abs] = m.rel(abs), m.exact(abs)
1486
1490
1487 # walk target manifest.
1491 # walk target manifest.
1488
1492
1489 def badfn(path, msg):
1493 def badfn(path, msg):
1490 if path in names:
1494 if path in names:
1491 return
1495 return
1492 if path in ctx.substate:
1496 if path in ctx.substate:
1493 return
1497 return
1494 path_ = path + '/'
1498 path_ = path + '/'
1495 for f in names:
1499 for f in names:
1496 if f.startswith(path_):
1500 if f.startswith(path_):
1497 return
1501 return
1498 ui.warn("%s: %s\n" % (m.rel(path), msg))
1502 ui.warn("%s: %s\n" % (m.rel(path), msg))
1499
1503
1500 m = scmutil.match(ctx, pats, opts)
1504 m = scmutil.match(ctx, pats, opts)
1501 m.bad = badfn
1505 m.bad = badfn
1502 for abs in ctx.walk(m):
1506 for abs in ctx.walk(m):
1503 if abs not in names:
1507 if abs not in names:
1504 names[abs] = m.rel(abs), m.exact(abs)
1508 names[abs] = m.rel(abs), m.exact(abs)
1505
1509
1506 # get the list of subrepos that must be reverted
1510 # get the list of subrepos that must be reverted
1507 targetsubs = [s for s in ctx.substate if m(s)]
1511 targetsubs = [s for s in ctx.substate if m(s)]
1508 m = scmutil.matchfiles(repo, names)
1512 m = scmutil.matchfiles(repo, names)
1509 changes = repo.status(match=m)[:4]
1513 changes = repo.status(match=m)[:4]
1510 modified, added, removed, deleted = map(set, changes)
1514 modified, added, removed, deleted = map(set, changes)
1511
1515
1512 # if f is a rename, also revert the source
1516 # if f is a rename, also revert the source
1513 cwd = repo.getcwd()
1517 cwd = repo.getcwd()
1514 for f in added:
1518 for f in added:
1515 src = repo.dirstate.copied(f)
1519 src = repo.dirstate.copied(f)
1516 if src and src not in names and repo.dirstate[src] == 'r':
1520 if src and src not in names and repo.dirstate[src] == 'r':
1517 removed.add(src)
1521 removed.add(src)
1518 names[src] = (repo.pathto(src, cwd), True)
1522 names[src] = (repo.pathto(src, cwd), True)
1519
1523
1520 def removeforget(abs):
1524 def removeforget(abs):
1521 if repo.dirstate[abs] == 'a':
1525 if repo.dirstate[abs] == 'a':
1522 return _('forgetting %s\n')
1526 return _('forgetting %s\n')
1523 return _('removing %s\n')
1527 return _('removing %s\n')
1524
1528
1525 revert = ([], _('reverting %s\n'))
1529 revert = ([], _('reverting %s\n'))
1526 add = ([], _('adding %s\n'))
1530 add = ([], _('adding %s\n'))
1527 remove = ([], removeforget)
1531 remove = ([], removeforget)
1528 undelete = ([], _('undeleting %s\n'))
1532 undelete = ([], _('undeleting %s\n'))
1529
1533
1530 disptable = (
1534 disptable = (
1531 # dispatch table:
1535 # dispatch table:
1532 # file state
1536 # file state
1533 # action if in target manifest
1537 # action if in target manifest
1534 # action if not in target manifest
1538 # action if not in target manifest
1535 # make backup if in target manifest
1539 # make backup if in target manifest
1536 # make backup if not in target manifest
1540 # make backup if not in target manifest
1537 (modified, revert, remove, True, True),
1541 (modified, revert, remove, True, True),
1538 (added, revert, remove, True, False),
1542 (added, revert, remove, True, False),
1539 (removed, undelete, None, False, False),
1543 (removed, undelete, None, False, False),
1540 (deleted, revert, remove, False, False),
1544 (deleted, revert, remove, False, False),
1541 )
1545 )
1542
1546
1543 for abs, (rel, exact) in sorted(names.items()):
1547 for abs, (rel, exact) in sorted(names.items()):
1544 mfentry = mf.get(abs)
1548 mfentry = mf.get(abs)
1545 target = repo.wjoin(abs)
1549 target = repo.wjoin(abs)
1546 def handle(xlist, dobackup):
1550 def handle(xlist, dobackup):
1547 xlist[0].append(abs)
1551 xlist[0].append(abs)
1548 if (dobackup and not opts.get('no_backup') and
1552 if (dobackup and not opts.get('no_backup') and
1549 os.path.lexists(target)):
1553 os.path.lexists(target)):
1550 bakname = "%s.orig" % rel
1554 bakname = "%s.orig" % rel
1551 ui.note(_('saving current version of %s as %s\n') %
1555 ui.note(_('saving current version of %s as %s\n') %
1552 (rel, bakname))
1556 (rel, bakname))
1553 if not opts.get('dry_run'):
1557 if not opts.get('dry_run'):
1554 util.rename(target, bakname)
1558 util.rename(target, bakname)
1555 if ui.verbose or not exact:
1559 if ui.verbose or not exact:
1556 msg = xlist[1]
1560 msg = xlist[1]
1557 if not isinstance(msg, basestring):
1561 if not isinstance(msg, basestring):
1558 msg = msg(abs)
1562 msg = msg(abs)
1559 ui.status(msg % rel)
1563 ui.status(msg % rel)
1560 for table, hitlist, misslist, backuphit, backupmiss in disptable:
1564 for table, hitlist, misslist, backuphit, backupmiss in disptable:
1561 if abs not in table:
1565 if abs not in table:
1562 continue
1566 continue
1563 # file has changed in dirstate
1567 # file has changed in dirstate
1564 if mfentry:
1568 if mfentry:
1565 handle(hitlist, backuphit)
1569 handle(hitlist, backuphit)
1566 elif misslist is not None:
1570 elif misslist is not None:
1567 handle(misslist, backupmiss)
1571 handle(misslist, backupmiss)
1568 break
1572 break
1569 else:
1573 else:
1570 if abs not in repo.dirstate:
1574 if abs not in repo.dirstate:
1571 if mfentry:
1575 if mfentry:
1572 handle(add, True)
1576 handle(add, True)
1573 elif exact:
1577 elif exact:
1574 ui.warn(_('file not managed: %s\n') % rel)
1578 ui.warn(_('file not managed: %s\n') % rel)
1575 continue
1579 continue
1576 # file has not changed in dirstate
1580 # file has not changed in dirstate
1577 if node == parent:
1581 if node == parent:
1578 if exact:
1582 if exact:
1579 ui.warn(_('no changes needed to %s\n') % rel)
1583 ui.warn(_('no changes needed to %s\n') % rel)
1580 continue
1584 continue
1581 if pmf is None:
1585 if pmf is None:
1582 # only need parent manifest in this unlikely case,
1586 # only need parent manifest in this unlikely case,
1583 # so do not read by default
1587 # so do not read by default
1584 pmf = repo[parent].manifest()
1588 pmf = repo[parent].manifest()
1585 if abs in pmf and mfentry:
1589 if abs in pmf and mfentry:
1586 # if version of file is same in parent and target
1590 # if version of file is same in parent and target
1587 # manifests, do nothing
1591 # manifests, do nothing
1588 if (pmf[abs] != mfentry or
1592 if (pmf[abs] != mfentry or
1589 pmf.flags(abs) != mf.flags(abs)):
1593 pmf.flags(abs) != mf.flags(abs)):
1590 handle(revert, False)
1594 handle(revert, False)
1591 else:
1595 else:
1592 handle(remove, False)
1596 handle(remove, False)
1593
1597
1594 if not opts.get('dry_run'):
1598 if not opts.get('dry_run'):
1595 def checkout(f):
1599 def checkout(f):
1596 fc = ctx[f]
1600 fc = ctx[f]
1597 repo.wwrite(f, fc.data(), fc.flags())
1601 repo.wwrite(f, fc.data(), fc.flags())
1598
1602
1599 audit_path = scmutil.pathauditor(repo.root)
1603 audit_path = scmutil.pathauditor(repo.root)
1600 for f in remove[0]:
1604 for f in remove[0]:
1601 if repo.dirstate[f] == 'a':
1605 if repo.dirstate[f] == 'a':
1602 repo.dirstate.drop(f)
1606 repo.dirstate.drop(f)
1603 continue
1607 continue
1604 audit_path(f)
1608 audit_path(f)
1605 try:
1609 try:
1606 util.unlinkpath(repo.wjoin(f))
1610 util.unlinkpath(repo.wjoin(f))
1607 except OSError:
1611 except OSError:
1608 pass
1612 pass
1609 repo.dirstate.remove(f)
1613 repo.dirstate.remove(f)
1610
1614
1611 normal = None
1615 normal = None
1612 if node == parent:
1616 if node == parent:
1613 # We're reverting to our parent. If possible, we'd like status
1617 # We're reverting to our parent. If possible, we'd like status
1614 # to report the file as clean. We have to use normallookup for
1618 # to report the file as clean. We have to use normallookup for
1615 # merges to avoid losing information about merged/dirty files.
1619 # merges to avoid losing information about merged/dirty files.
1616 if p2 != nullid:
1620 if p2 != nullid:
1617 normal = repo.dirstate.normallookup
1621 normal = repo.dirstate.normallookup
1618 else:
1622 else:
1619 normal = repo.dirstate.normal
1623 normal = repo.dirstate.normal
1620 for f in revert[0]:
1624 for f in revert[0]:
1621 checkout(f)
1625 checkout(f)
1622 if normal:
1626 if normal:
1623 normal(f)
1627 normal(f)
1624
1628
1625 for f in add[0]:
1629 for f in add[0]:
1626 checkout(f)
1630 checkout(f)
1627 repo.dirstate.add(f)
1631 repo.dirstate.add(f)
1628
1632
1629 normal = repo.dirstate.normallookup
1633 normal = repo.dirstate.normallookup
1630 if node == parent and p2 == nullid:
1634 if node == parent and p2 == nullid:
1631 normal = repo.dirstate.normal
1635 normal = repo.dirstate.normal
1632 for f in undelete[0]:
1636 for f in undelete[0]:
1633 checkout(f)
1637 checkout(f)
1634 normal(f)
1638 normal(f)
1635
1639
1636 if targetsubs:
1640 if targetsubs:
1637 # Revert the subrepos on the revert list
1641 # Revert the subrepos on the revert list
1638 for sub in targetsubs:
1642 for sub in targetsubs:
1639 ctx.sub(sub).revert(ui, ctx.substate[sub], *pats, **opts)
1643 ctx.sub(sub).revert(ui, ctx.substate[sub], *pats, **opts)
1640 finally:
1644 finally:
1641 wlock.release()
1645 wlock.release()
1642
1646
1643 def command(table):
1647 def command(table):
1644 '''returns a function object bound to table which can be used as
1648 '''returns a function object bound to table which can be used as
1645 a decorator for populating table as a command table'''
1649 a decorator for populating table as a command table'''
1646
1650
1647 def cmd(name, options, synopsis=None):
1651 def cmd(name, options, synopsis=None):
1648 def decorator(func):
1652 def decorator(func):
1649 if synopsis:
1653 if synopsis:
1650 table[name] = func, options[:], synopsis
1654 table[name] = func, options[:], synopsis
1651 else:
1655 else:
1652 table[name] = func, options[:]
1656 table[name] = func, options[:]
1653 return func
1657 return func
1654 return decorator
1658 return decorator
1655
1659
1656 return cmd
1660 return cmd
@@ -1,5782 +1,5783 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from node import hex, 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, difflib, time, tempfile, errno
11 import os, re, difflib, time, tempfile, errno
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, hbisect
14 import archival, changegroup, cmdutil, hbisect
15 import sshserver, hgweb, hgweb.server, commandserver
15 import sshserver, hgweb, hgweb.server, commandserver
16 import merge as mergemod
16 import merge as mergemod
17 import minirst, revset, fileset
17 import minirst, revset, fileset
18 import dagparser, context, simplemerge
18 import dagparser, context, simplemerge
19 import random, setdiscovery, treediscovery, dagutil, pvec
19 import random, setdiscovery, treediscovery, dagutil, pvec
20 import phases
20 import phases
21
21
22 table = {}
22 table = {}
23
23
24 command = cmdutil.command(table)
24 command = cmdutil.command(table)
25
25
26 # common command options
26 # common command options
27
27
28 globalopts = [
28 globalopts = [
29 ('R', 'repository', '',
29 ('R', 'repository', '',
30 _('repository root directory or name of overlay bundle file'),
30 _('repository root directory or name of overlay bundle file'),
31 _('REPO')),
31 _('REPO')),
32 ('', 'cwd', '',
32 ('', 'cwd', '',
33 _('change working directory'), _('DIR')),
33 _('change working directory'), _('DIR')),
34 ('y', 'noninteractive', None,
34 ('y', 'noninteractive', None,
35 _('do not prompt, automatically pick the first choice for all prompts')),
35 _('do not prompt, automatically pick the first choice for all prompts')),
36 ('q', 'quiet', None, _('suppress output')),
36 ('q', 'quiet', None, _('suppress output')),
37 ('v', 'verbose', None, _('enable additional output')),
37 ('v', 'verbose', None, _('enable additional output')),
38 ('', 'config', [],
38 ('', 'config', [],
39 _('set/override config option (use \'section.name=value\')'),
39 _('set/override config option (use \'section.name=value\')'),
40 _('CONFIG')),
40 _('CONFIG')),
41 ('', 'debug', None, _('enable debugging output')),
41 ('', 'debug', None, _('enable debugging output')),
42 ('', 'debugger', None, _('start debugger')),
42 ('', 'debugger', None, _('start debugger')),
43 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
43 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
44 _('ENCODE')),
44 _('ENCODE')),
45 ('', 'encodingmode', encoding.encodingmode,
45 ('', 'encodingmode', encoding.encodingmode,
46 _('set the charset encoding mode'), _('MODE')),
46 _('set the charset encoding mode'), _('MODE')),
47 ('', 'traceback', None, _('always print a traceback on exception')),
47 ('', 'traceback', None, _('always print a traceback on exception')),
48 ('', 'time', None, _('time how long the command takes')),
48 ('', 'time', None, _('time how long the command takes')),
49 ('', 'profile', None, _('print command execution profile')),
49 ('', 'profile', None, _('print command execution profile')),
50 ('', 'version', None, _('output version information and exit')),
50 ('', 'version', None, _('output version information and exit')),
51 ('h', 'help', None, _('display help and exit')),
51 ('h', 'help', None, _('display help and exit')),
52 ]
52 ]
53
53
54 dryrunopts = [('n', 'dry-run', None,
54 dryrunopts = [('n', 'dry-run', None,
55 _('do not perform actions, just print output'))]
55 _('do not perform actions, just print output'))]
56
56
57 remoteopts = [
57 remoteopts = [
58 ('e', 'ssh', '',
58 ('e', 'ssh', '',
59 _('specify ssh command to use'), _('CMD')),
59 _('specify ssh command to use'), _('CMD')),
60 ('', 'remotecmd', '',
60 ('', 'remotecmd', '',
61 _('specify hg command to run on the remote side'), _('CMD')),
61 _('specify hg command to run on the remote side'), _('CMD')),
62 ('', 'insecure', None,
62 ('', 'insecure', None,
63 _('do not verify server certificate (ignoring web.cacerts config)')),
63 _('do not verify server certificate (ignoring web.cacerts config)')),
64 ]
64 ]
65
65
66 walkopts = [
66 walkopts = [
67 ('I', 'include', [],
67 ('I', 'include', [],
68 _('include names matching the given patterns'), _('PATTERN')),
68 _('include names matching the given patterns'), _('PATTERN')),
69 ('X', 'exclude', [],
69 ('X', 'exclude', [],
70 _('exclude names matching the given patterns'), _('PATTERN')),
70 _('exclude names matching the given patterns'), _('PATTERN')),
71 ]
71 ]
72
72
73 commitopts = [
73 commitopts = [
74 ('m', 'message', '',
74 ('m', 'message', '',
75 _('use text as commit message'), _('TEXT')),
75 _('use text as commit message'), _('TEXT')),
76 ('l', 'logfile', '',
76 ('l', 'logfile', '',
77 _('read commit message from file'), _('FILE')),
77 _('read commit message from file'), _('FILE')),
78 ]
78 ]
79
79
80 commitopts2 = [
80 commitopts2 = [
81 ('d', 'date', '',
81 ('d', 'date', '',
82 _('record the specified date as commit date'), _('DATE')),
82 _('record the specified date as commit date'), _('DATE')),
83 ('u', 'user', '',
83 ('u', 'user', '',
84 _('record the specified user as committer'), _('USER')),
84 _('record the specified user as committer'), _('USER')),
85 ]
85 ]
86
86
87 templateopts = [
87 templateopts = [
88 ('', 'style', '',
88 ('', 'style', '',
89 _('display using template map file'), _('STYLE')),
89 _('display using template map file'), _('STYLE')),
90 ('', 'template', '',
90 ('', 'template', '',
91 _('display with template'), _('TEMPLATE')),
91 _('display with template'), _('TEMPLATE')),
92 ]
92 ]
93
93
94 logopts = [
94 logopts = [
95 ('p', 'patch', None, _('show patch')),
95 ('p', 'patch', None, _('show patch')),
96 ('g', 'git', None, _('use git extended diff format')),
96 ('g', 'git', None, _('use git extended diff format')),
97 ('l', 'limit', '',
97 ('l', 'limit', '',
98 _('limit number of changes displayed'), _('NUM')),
98 _('limit number of changes displayed'), _('NUM')),
99 ('M', 'no-merges', None, _('do not show merges')),
99 ('M', 'no-merges', None, _('do not show merges')),
100 ('', 'stat', None, _('output diffstat-style summary of changes')),
100 ('', 'stat', None, _('output diffstat-style summary of changes')),
101 ] + templateopts
101 ] + templateopts
102
102
103 diffopts = [
103 diffopts = [
104 ('a', 'text', None, _('treat all files as text')),
104 ('a', 'text', None, _('treat all files as text')),
105 ('g', 'git', None, _('use git extended diff format')),
105 ('g', 'git', None, _('use git extended diff format')),
106 ('', 'nodates', None, _('omit dates from diff headers'))
106 ('', 'nodates', None, _('omit dates from diff headers'))
107 ]
107 ]
108
108
109 diffwsopts = [
109 diffwsopts = [
110 ('w', 'ignore-all-space', None,
110 ('w', 'ignore-all-space', None,
111 _('ignore white space when comparing lines')),
111 _('ignore white space when comparing lines')),
112 ('b', 'ignore-space-change', None,
112 ('b', 'ignore-space-change', None,
113 _('ignore changes in the amount of white space')),
113 _('ignore changes in the amount of white space')),
114 ('B', 'ignore-blank-lines', None,
114 ('B', 'ignore-blank-lines', None,
115 _('ignore changes whose lines are all blank')),
115 _('ignore changes whose lines are all blank')),
116 ]
116 ]
117
117
118 diffopts2 = [
118 diffopts2 = [
119 ('p', 'show-function', None, _('show which function each change is in')),
119 ('p', 'show-function', None, _('show which function each change is in')),
120 ('', 'reverse', None, _('produce a diff that undoes the changes')),
120 ('', 'reverse', None, _('produce a diff that undoes the changes')),
121 ] + diffwsopts + [
121 ] + diffwsopts + [
122 ('U', 'unified', '',
122 ('U', 'unified', '',
123 _('number of lines of context to show'), _('NUM')),
123 _('number of lines of context to show'), _('NUM')),
124 ('', 'stat', None, _('output diffstat-style summary of changes')),
124 ('', 'stat', None, _('output diffstat-style summary of changes')),
125 ]
125 ]
126
126
127 mergetoolopts = [
127 mergetoolopts = [
128 ('t', 'tool', '', _('specify merge tool')),
128 ('t', 'tool', '', _('specify merge tool')),
129 ]
129 ]
130
130
131 similarityopts = [
131 similarityopts = [
132 ('s', 'similarity', '',
132 ('s', 'similarity', '',
133 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
133 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
134 ]
134 ]
135
135
136 subrepoopts = [
136 subrepoopts = [
137 ('S', 'subrepos', None,
137 ('S', 'subrepos', None,
138 _('recurse into subrepositories'))
138 _('recurse into subrepositories'))
139 ]
139 ]
140
140
141 # Commands start here, listed alphabetically
141 # Commands start here, listed alphabetically
142
142
143 @command('^add',
143 @command('^add',
144 walkopts + subrepoopts + dryrunopts,
144 walkopts + subrepoopts + dryrunopts,
145 _('[OPTION]... [FILE]...'))
145 _('[OPTION]... [FILE]...'))
146 def add(ui, repo, *pats, **opts):
146 def add(ui, repo, *pats, **opts):
147 """add the specified files on the next commit
147 """add the specified files on the next commit
148
148
149 Schedule files to be version controlled and added to the
149 Schedule files to be version controlled and added to the
150 repository.
150 repository.
151
151
152 The files will be added to the repository at the next commit. To
152 The files will be added to the repository at the next commit. To
153 undo an add before that, see :hg:`forget`.
153 undo an add before that, see :hg:`forget`.
154
154
155 If no names are given, add all files to the repository.
155 If no names are given, add all files to the repository.
156
156
157 .. container:: verbose
157 .. container:: verbose
158
158
159 An example showing how new (unknown) files are added
159 An example showing how new (unknown) files are added
160 automatically by :hg:`add`::
160 automatically by :hg:`add`::
161
161
162 $ ls
162 $ ls
163 foo.c
163 foo.c
164 $ hg status
164 $ hg status
165 ? foo.c
165 ? foo.c
166 $ hg add
166 $ hg add
167 adding foo.c
167 adding foo.c
168 $ hg status
168 $ hg status
169 A foo.c
169 A foo.c
170
170
171 Returns 0 if all files are successfully added.
171 Returns 0 if all files are successfully added.
172 """
172 """
173
173
174 m = scmutil.match(repo[None], pats, opts)
174 m = scmutil.match(repo[None], pats, opts)
175 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
175 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
176 opts.get('subrepos'), prefix="", explicitonly=False)
176 opts.get('subrepos'), prefix="", explicitonly=False)
177 return rejected and 1 or 0
177 return rejected and 1 or 0
178
178
179 @command('addremove',
179 @command('addremove',
180 similarityopts + walkopts + dryrunopts,
180 similarityopts + walkopts + dryrunopts,
181 _('[OPTION]... [FILE]...'))
181 _('[OPTION]... [FILE]...'))
182 def addremove(ui, repo, *pats, **opts):
182 def addremove(ui, repo, *pats, **opts):
183 """add all new files, delete all missing files
183 """add all new files, delete all missing files
184
184
185 Add all new files and remove all missing files from the
185 Add all new files and remove all missing files from the
186 repository.
186 repository.
187
187
188 New files are ignored if they match any of the patterns in
188 New files are ignored if they match any of the patterns in
189 ``.hgignore``. As with add, these changes take effect at the next
189 ``.hgignore``. As with add, these changes take effect at the next
190 commit.
190 commit.
191
191
192 Use the -s/--similarity option to detect renamed files. With a
192 Use the -s/--similarity option to detect renamed files. With a
193 parameter greater than 0, this compares every removed file with
193 parameter greater than 0, this compares every removed file with
194 every added file and records those similar enough as renames. This
194 every added file and records those similar enough as renames. This
195 option takes a percentage between 0 (disabled) and 100 (files must
195 option takes a percentage between 0 (disabled) and 100 (files must
196 be identical) as its parameter. Detecting renamed files this way
196 be identical) as its parameter. Detecting renamed files this way
197 can be expensive. After using this option, :hg:`status -C` can be
197 can be expensive. After using this option, :hg:`status -C` can be
198 used to check which files were identified as moved or renamed.
198 used to check which files were identified as moved or renamed.
199 If this option is not specified, only renames of identical files
199 If this option is not specified, only renames of identical files
200 are detected.
200 are detected.
201
201
202 Returns 0 if all files are successfully added.
202 Returns 0 if all files are successfully added.
203 """
203 """
204 try:
204 try:
205 sim = float(opts.get('similarity') or 100)
205 sim = float(opts.get('similarity') or 100)
206 except ValueError:
206 except ValueError:
207 raise util.Abort(_('similarity must be a number'))
207 raise util.Abort(_('similarity must be a number'))
208 if sim < 0 or sim > 100:
208 if sim < 0 or sim > 100:
209 raise util.Abort(_('similarity must be between 0 and 100'))
209 raise util.Abort(_('similarity must be between 0 and 100'))
210 return scmutil.addremove(repo, pats, opts, similarity=sim / 100.0)
210 return scmutil.addremove(repo, pats, opts, similarity=sim / 100.0)
211
211
212 @command('^annotate|blame',
212 @command('^annotate|blame',
213 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
213 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
214 ('', 'follow', None,
214 ('', 'follow', None,
215 _('follow copies/renames and list the filename (DEPRECATED)')),
215 _('follow copies/renames and list the filename (DEPRECATED)')),
216 ('', 'no-follow', None, _("don't follow copies and renames")),
216 ('', 'no-follow', None, _("don't follow copies and renames")),
217 ('a', 'text', None, _('treat all files as text')),
217 ('a', 'text', None, _('treat all files as text')),
218 ('u', 'user', None, _('list the author (long with -v)')),
218 ('u', 'user', None, _('list the author (long with -v)')),
219 ('f', 'file', None, _('list the filename')),
219 ('f', 'file', None, _('list the filename')),
220 ('d', 'date', None, _('list the date (short with -q)')),
220 ('d', 'date', None, _('list the date (short with -q)')),
221 ('n', 'number', None, _('list the revision number (default)')),
221 ('n', 'number', None, _('list the revision number (default)')),
222 ('c', 'changeset', None, _('list the changeset')),
222 ('c', 'changeset', None, _('list the changeset')),
223 ('l', 'line-number', None, _('show line number at the first appearance'))
223 ('l', 'line-number', None, _('show line number at the first appearance'))
224 ] + diffwsopts + walkopts,
224 ] + diffwsopts + walkopts,
225 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'))
225 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'))
226 def annotate(ui, repo, *pats, **opts):
226 def annotate(ui, repo, *pats, **opts):
227 """show changeset information by line for each file
227 """show changeset information by line for each file
228
228
229 List changes in files, showing the revision id responsible for
229 List changes in files, showing the revision id responsible for
230 each line
230 each line
231
231
232 This command is useful for discovering when a change was made and
232 This command is useful for discovering when a change was made and
233 by whom.
233 by whom.
234
234
235 Without the -a/--text option, annotate will avoid processing files
235 Without the -a/--text option, annotate will avoid processing files
236 it detects as binary. With -a, annotate will annotate the file
236 it detects as binary. With -a, annotate will annotate the file
237 anyway, although the results will probably be neither useful
237 anyway, although the results will probably be neither useful
238 nor desirable.
238 nor desirable.
239
239
240 Returns 0 on success.
240 Returns 0 on success.
241 """
241 """
242 if opts.get('follow'):
242 if opts.get('follow'):
243 # --follow is deprecated and now just an alias for -f/--file
243 # --follow is deprecated and now just an alias for -f/--file
244 # to mimic the behavior of Mercurial before version 1.5
244 # to mimic the behavior of Mercurial before version 1.5
245 opts['file'] = True
245 opts['file'] = True
246
246
247 datefunc = ui.quiet and util.shortdate or util.datestr
247 datefunc = ui.quiet and util.shortdate or util.datestr
248 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
248 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
249
249
250 if not pats:
250 if not pats:
251 raise util.Abort(_('at least one filename or pattern is required'))
251 raise util.Abort(_('at least one filename or pattern is required'))
252
252
253 hexfn = ui.debugflag and hex or short
253 hexfn = ui.debugflag and hex or short
254
254
255 opmap = [('user', ' ', lambda x: ui.shortuser(x[0].user())),
255 opmap = [('user', ' ', lambda x: ui.shortuser(x[0].user())),
256 ('number', ' ', lambda x: str(x[0].rev())),
256 ('number', ' ', lambda x: str(x[0].rev())),
257 ('changeset', ' ', lambda x: hexfn(x[0].node())),
257 ('changeset', ' ', lambda x: hexfn(x[0].node())),
258 ('date', ' ', getdate),
258 ('date', ' ', getdate),
259 ('file', ' ', lambda x: x[0].path()),
259 ('file', ' ', lambda x: x[0].path()),
260 ('line_number', ':', lambda x: str(x[1])),
260 ('line_number', ':', lambda x: str(x[1])),
261 ]
261 ]
262
262
263 if (not opts.get('user') and not opts.get('changeset')
263 if (not opts.get('user') and not opts.get('changeset')
264 and not opts.get('date') and not opts.get('file')):
264 and not opts.get('date') and not opts.get('file')):
265 opts['number'] = True
265 opts['number'] = True
266
266
267 linenumber = opts.get('line_number') is not None
267 linenumber = opts.get('line_number') is not None
268 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
268 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
269 raise util.Abort(_('at least one of -n/-c is required for -l'))
269 raise util.Abort(_('at least one of -n/-c is required for -l'))
270
270
271 funcmap = [(func, sep) for op, sep, func in opmap if opts.get(op)]
271 funcmap = [(func, sep) for op, sep, func in opmap if opts.get(op)]
272 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
272 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
273
273
274 def bad(x, y):
274 def bad(x, y):
275 raise util.Abort("%s: %s" % (x, y))
275 raise util.Abort("%s: %s" % (x, y))
276
276
277 ctx = scmutil.revsingle(repo, opts.get('rev'))
277 ctx = scmutil.revsingle(repo, opts.get('rev'))
278 m = scmutil.match(ctx, pats, opts)
278 m = scmutil.match(ctx, pats, opts)
279 m.bad = bad
279 m.bad = bad
280 follow = not opts.get('no_follow')
280 follow = not opts.get('no_follow')
281 diffopts = patch.diffopts(ui, opts, section='annotate')
281 diffopts = patch.diffopts(ui, opts, section='annotate')
282 for abs in ctx.walk(m):
282 for abs in ctx.walk(m):
283 fctx = ctx[abs]
283 fctx = ctx[abs]
284 if not opts.get('text') and util.binary(fctx.data()):
284 if not opts.get('text') and util.binary(fctx.data()):
285 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
285 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
286 continue
286 continue
287
287
288 lines = fctx.annotate(follow=follow, linenumber=linenumber,
288 lines = fctx.annotate(follow=follow, linenumber=linenumber,
289 diffopts=diffopts)
289 diffopts=diffopts)
290 pieces = []
290 pieces = []
291
291
292 for f, sep in funcmap:
292 for f, sep in funcmap:
293 l = [f(n) for n, dummy in lines]
293 l = [f(n) for n, dummy in lines]
294 if l:
294 if l:
295 sized = [(x, encoding.colwidth(x)) for x in l]
295 sized = [(x, encoding.colwidth(x)) for x in l]
296 ml = max([w for x, w in sized])
296 ml = max([w for x, w in sized])
297 pieces.append(["%s%s%s" % (sep, ' ' * (ml - w), x)
297 pieces.append(["%s%s%s" % (sep, ' ' * (ml - w), x)
298 for x, w in sized])
298 for x, w in sized])
299
299
300 if pieces:
300 if pieces:
301 for p, l in zip(zip(*pieces), lines):
301 for p, l in zip(zip(*pieces), lines):
302 ui.write("%s: %s" % ("".join(p), l[1]))
302 ui.write("%s: %s" % ("".join(p), l[1]))
303
303
304 if lines and not lines[-1][1].endswith('\n'):
304 if lines and not lines[-1][1].endswith('\n'):
305 ui.write('\n')
305 ui.write('\n')
306
306
307 @command('archive',
307 @command('archive',
308 [('', 'no-decode', None, _('do not pass files through decoders')),
308 [('', 'no-decode', None, _('do not pass files through decoders')),
309 ('p', 'prefix', '', _('directory prefix for files in archive'),
309 ('p', 'prefix', '', _('directory prefix for files in archive'),
310 _('PREFIX')),
310 _('PREFIX')),
311 ('r', 'rev', '', _('revision to distribute'), _('REV')),
311 ('r', 'rev', '', _('revision to distribute'), _('REV')),
312 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
312 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
313 ] + subrepoopts + walkopts,
313 ] + subrepoopts + walkopts,
314 _('[OPTION]... DEST'))
314 _('[OPTION]... DEST'))
315 def archive(ui, repo, dest, **opts):
315 def archive(ui, repo, dest, **opts):
316 '''create an unversioned archive of a repository revision
316 '''create an unversioned archive of a repository revision
317
317
318 By default, the revision used is the parent of the working
318 By default, the revision used is the parent of the working
319 directory; use -r/--rev to specify a different revision.
319 directory; use -r/--rev to specify a different revision.
320
320
321 The archive type is automatically detected based on file
321 The archive type is automatically detected based on file
322 extension (or override using -t/--type).
322 extension (or override using -t/--type).
323
323
324 .. container:: verbose
324 .. container:: verbose
325
325
326 Examples:
326 Examples:
327
327
328 - create a zip file containing the 1.0 release::
328 - create a zip file containing the 1.0 release::
329
329
330 hg archive -r 1.0 project-1.0.zip
330 hg archive -r 1.0 project-1.0.zip
331
331
332 - create a tarball excluding .hg files::
332 - create a tarball excluding .hg files::
333
333
334 hg archive project.tar.gz -X ".hg*"
334 hg archive project.tar.gz -X ".hg*"
335
335
336 Valid types are:
336 Valid types are:
337
337
338 :``files``: a directory full of files (default)
338 :``files``: a directory full of files (default)
339 :``tar``: tar archive, uncompressed
339 :``tar``: tar archive, uncompressed
340 :``tbz2``: tar archive, compressed using bzip2
340 :``tbz2``: tar archive, compressed using bzip2
341 :``tgz``: tar archive, compressed using gzip
341 :``tgz``: tar archive, compressed using gzip
342 :``uzip``: zip archive, uncompressed
342 :``uzip``: zip archive, uncompressed
343 :``zip``: zip archive, compressed using deflate
343 :``zip``: zip archive, compressed using deflate
344
344
345 The exact name of the destination archive or directory is given
345 The exact name of the destination archive or directory is given
346 using a format string; see :hg:`help export` for details.
346 using a format string; see :hg:`help export` for details.
347
347
348 Each member added to an archive file has a directory prefix
348 Each member added to an archive file has a directory prefix
349 prepended. Use -p/--prefix to specify a format string for the
349 prepended. Use -p/--prefix to specify a format string for the
350 prefix. The default is the basename of the archive, with suffixes
350 prefix. The default is the basename of the archive, with suffixes
351 removed.
351 removed.
352
352
353 Returns 0 on success.
353 Returns 0 on success.
354 '''
354 '''
355
355
356 ctx = scmutil.revsingle(repo, opts.get('rev'))
356 ctx = scmutil.revsingle(repo, opts.get('rev'))
357 if not ctx:
357 if not ctx:
358 raise util.Abort(_('no working directory: please specify a revision'))
358 raise util.Abort(_('no working directory: please specify a revision'))
359 node = ctx.node()
359 node = ctx.node()
360 dest = cmdutil.makefilename(repo, dest, node)
360 dest = cmdutil.makefilename(repo, dest, node)
361 if os.path.realpath(dest) == repo.root:
361 if os.path.realpath(dest) == repo.root:
362 raise util.Abort(_('repository root cannot be destination'))
362 raise util.Abort(_('repository root cannot be destination'))
363
363
364 kind = opts.get('type') or archival.guesskind(dest) or 'files'
364 kind = opts.get('type') or archival.guesskind(dest) or 'files'
365 prefix = opts.get('prefix')
365 prefix = opts.get('prefix')
366
366
367 if dest == '-':
367 if dest == '-':
368 if kind == 'files':
368 if kind == 'files':
369 raise util.Abort(_('cannot archive plain files to stdout'))
369 raise util.Abort(_('cannot archive plain files to stdout'))
370 dest = cmdutil.makefileobj(repo, dest)
370 dest = cmdutil.makefileobj(repo, dest)
371 if not prefix:
371 if not prefix:
372 prefix = os.path.basename(repo.root) + '-%h'
372 prefix = os.path.basename(repo.root) + '-%h'
373
373
374 prefix = cmdutil.makefilename(repo, prefix, node)
374 prefix = cmdutil.makefilename(repo, prefix, node)
375 matchfn = scmutil.match(ctx, [], opts)
375 matchfn = scmutil.match(ctx, [], opts)
376 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
376 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
377 matchfn, prefix, subrepos=opts.get('subrepos'))
377 matchfn, prefix, subrepos=opts.get('subrepos'))
378
378
379 @command('backout',
379 @command('backout',
380 [('', 'merge', None, _('merge with old dirstate parent after backout')),
380 [('', 'merge', None, _('merge with old dirstate parent after backout')),
381 ('', 'parent', '',
381 ('', 'parent', '',
382 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
382 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
383 ('r', 'rev', '', _('revision to backout'), _('REV')),
383 ('r', 'rev', '', _('revision to backout'), _('REV')),
384 ] + mergetoolopts + walkopts + commitopts + commitopts2,
384 ] + mergetoolopts + walkopts + commitopts + commitopts2,
385 _('[OPTION]... [-r] REV'))
385 _('[OPTION]... [-r] REV'))
386 def backout(ui, repo, node=None, rev=None, **opts):
386 def backout(ui, repo, node=None, rev=None, **opts):
387 '''reverse effect of earlier changeset
387 '''reverse effect of earlier changeset
388
388
389 Prepare a new changeset with the effect of REV undone in the
389 Prepare a new changeset with the effect of REV undone in the
390 current working directory.
390 current working directory.
391
391
392 If REV is the parent of the working directory, then this new changeset
392 If REV is the parent of the working directory, then this new changeset
393 is committed automatically. Otherwise, hg needs to merge the
393 is committed automatically. Otherwise, hg needs to merge the
394 changes and the merged result is left uncommitted.
394 changes and the merged result is left uncommitted.
395
395
396 .. note::
396 .. note::
397 backout cannot be used to fix either an unwanted or
397 backout cannot be used to fix either an unwanted or
398 incorrect merge.
398 incorrect merge.
399
399
400 .. container:: verbose
400 .. container:: verbose
401
401
402 By default, the pending changeset will have one parent,
402 By default, the pending changeset will have one parent,
403 maintaining a linear history. With --merge, the pending
403 maintaining a linear history. With --merge, the pending
404 changeset will instead have two parents: the old parent of the
404 changeset will instead have two parents: the old parent of the
405 working directory and a new child of REV that simply undoes REV.
405 working directory and a new child of REV that simply undoes REV.
406
406
407 Before version 1.7, the behavior without --merge was equivalent
407 Before version 1.7, the behavior without --merge was equivalent
408 to specifying --merge followed by :hg:`update --clean .` to
408 to specifying --merge followed by :hg:`update --clean .` to
409 cancel the merge and leave the child of REV as a head to be
409 cancel the merge and leave the child of REV as a head to be
410 merged separately.
410 merged separately.
411
411
412 See :hg:`help dates` for a list of formats valid for -d/--date.
412 See :hg:`help dates` for a list of formats valid for -d/--date.
413
413
414 Returns 0 on success.
414 Returns 0 on success.
415 '''
415 '''
416 if rev and node:
416 if rev and node:
417 raise util.Abort(_("please specify just one revision"))
417 raise util.Abort(_("please specify just one revision"))
418
418
419 if not rev:
419 if not rev:
420 rev = node
420 rev = node
421
421
422 if not rev:
422 if not rev:
423 raise util.Abort(_("please specify a revision to backout"))
423 raise util.Abort(_("please specify a revision to backout"))
424
424
425 date = opts.get('date')
425 date = opts.get('date')
426 if date:
426 if date:
427 opts['date'] = util.parsedate(date)
427 opts['date'] = util.parsedate(date)
428
428
429 cmdutil.bailifchanged(repo)
429 cmdutil.bailifchanged(repo)
430 node = scmutil.revsingle(repo, rev).node()
430 node = scmutil.revsingle(repo, rev).node()
431
431
432 op1, op2 = repo.dirstate.parents()
432 op1, op2 = repo.dirstate.parents()
433 a = repo.changelog.ancestor(op1, node)
433 a = repo.changelog.ancestor(op1, node)
434 if a != node:
434 if a != node:
435 raise util.Abort(_('cannot backout change on a different branch'))
435 raise util.Abort(_('cannot backout change on a different branch'))
436
436
437 p1, p2 = repo.changelog.parents(node)
437 p1, p2 = repo.changelog.parents(node)
438 if p1 == nullid:
438 if p1 == nullid:
439 raise util.Abort(_('cannot backout a change with no parents'))
439 raise util.Abort(_('cannot backout a change with no parents'))
440 if p2 != nullid:
440 if p2 != nullid:
441 if not opts.get('parent'):
441 if not opts.get('parent'):
442 raise util.Abort(_('cannot backout a merge changeset'))
442 raise util.Abort(_('cannot backout a merge changeset'))
443 p = repo.lookup(opts['parent'])
443 p = repo.lookup(opts['parent'])
444 if p not in (p1, p2):
444 if p not in (p1, p2):
445 raise util.Abort(_('%s is not a parent of %s') %
445 raise util.Abort(_('%s is not a parent of %s') %
446 (short(p), short(node)))
446 (short(p), short(node)))
447 parent = p
447 parent = p
448 else:
448 else:
449 if opts.get('parent'):
449 if opts.get('parent'):
450 raise util.Abort(_('cannot use --parent on non-merge changeset'))
450 raise util.Abort(_('cannot use --parent on non-merge changeset'))
451 parent = p1
451 parent = p1
452
452
453 # the backout should appear on the same branch
453 # the backout should appear on the same branch
454 wlock = repo.wlock()
454 wlock = repo.wlock()
455 try:
455 try:
456 branch = repo.dirstate.branch()
456 branch = repo.dirstate.branch()
457 hg.clean(repo, node, show_stats=False)
457 hg.clean(repo, node, show_stats=False)
458 repo.dirstate.setbranch(branch)
458 repo.dirstate.setbranch(branch)
459 revert_opts = opts.copy()
459 revert_opts = opts.copy()
460 revert_opts['date'] = None
460 revert_opts['date'] = None
461 revert_opts['all'] = True
461 revert_opts['all'] = True
462 revert_opts['rev'] = hex(parent)
462 revert_opts['rev'] = hex(parent)
463 revert_opts['no_backup'] = None
463 revert_opts['no_backup'] = None
464 revert(ui, repo, **revert_opts)
464 revert(ui, repo, **revert_opts)
465 if not opts.get('merge') and op1 != node:
465 if not opts.get('merge') and op1 != node:
466 try:
466 try:
467 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
467 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
468 return hg.update(repo, op1)
468 return hg.update(repo, op1)
469 finally:
469 finally:
470 ui.setconfig('ui', 'forcemerge', '')
470 ui.setconfig('ui', 'forcemerge', '')
471
471
472 commit_opts = opts.copy()
472 commit_opts = opts.copy()
473 commit_opts['addremove'] = False
473 commit_opts['addremove'] = False
474 if not commit_opts['message'] and not commit_opts['logfile']:
474 if not commit_opts['message'] and not commit_opts['logfile']:
475 # we don't translate commit messages
475 # we don't translate commit messages
476 commit_opts['message'] = "Backed out changeset %s" % short(node)
476 commit_opts['message'] = "Backed out changeset %s" % short(node)
477 commit_opts['force_editor'] = True
477 commit_opts['force_editor'] = True
478 commit(ui, repo, **commit_opts)
478 commit(ui, repo, **commit_opts)
479 def nice(node):
479 def nice(node):
480 return '%d:%s' % (repo.changelog.rev(node), short(node))
480 return '%d:%s' % (repo.changelog.rev(node), short(node))
481 ui.status(_('changeset %s backs out changeset %s\n') %
481 ui.status(_('changeset %s backs out changeset %s\n') %
482 (nice(repo.changelog.tip()), nice(node)))
482 (nice(repo.changelog.tip()), nice(node)))
483 if opts.get('merge') and op1 != node:
483 if opts.get('merge') and op1 != node:
484 hg.clean(repo, op1, show_stats=False)
484 hg.clean(repo, op1, show_stats=False)
485 ui.status(_('merging with changeset %s\n')
485 ui.status(_('merging with changeset %s\n')
486 % nice(repo.changelog.tip()))
486 % nice(repo.changelog.tip()))
487 try:
487 try:
488 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
488 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
489 return hg.merge(repo, hex(repo.changelog.tip()))
489 return hg.merge(repo, hex(repo.changelog.tip()))
490 finally:
490 finally:
491 ui.setconfig('ui', 'forcemerge', '')
491 ui.setconfig('ui', 'forcemerge', '')
492 finally:
492 finally:
493 wlock.release()
493 wlock.release()
494 return 0
494 return 0
495
495
496 @command('bisect',
496 @command('bisect',
497 [('r', 'reset', False, _('reset bisect state')),
497 [('r', 'reset', False, _('reset bisect state')),
498 ('g', 'good', False, _('mark changeset good')),
498 ('g', 'good', False, _('mark changeset good')),
499 ('b', 'bad', False, _('mark changeset bad')),
499 ('b', 'bad', False, _('mark changeset bad')),
500 ('s', 'skip', False, _('skip testing changeset')),
500 ('s', 'skip', False, _('skip testing changeset')),
501 ('e', 'extend', False, _('extend the bisect range')),
501 ('e', 'extend', False, _('extend the bisect range')),
502 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
502 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
503 ('U', 'noupdate', False, _('do not update to target'))],
503 ('U', 'noupdate', False, _('do not update to target'))],
504 _("[-gbsr] [-U] [-c CMD] [REV]"))
504 _("[-gbsr] [-U] [-c CMD] [REV]"))
505 def bisect(ui, repo, rev=None, extra=None, command=None,
505 def bisect(ui, repo, rev=None, extra=None, command=None,
506 reset=None, good=None, bad=None, skip=None, extend=None,
506 reset=None, good=None, bad=None, skip=None, extend=None,
507 noupdate=None):
507 noupdate=None):
508 """subdivision search of changesets
508 """subdivision search of changesets
509
509
510 This command helps to find changesets which introduce problems. To
510 This command helps to find changesets which introduce problems. To
511 use, mark the earliest changeset you know exhibits the problem as
511 use, mark the earliest changeset you know exhibits the problem as
512 bad, then mark the latest changeset which is free from the problem
512 bad, then mark the latest changeset which is free from the problem
513 as good. Bisect will update your working directory to a revision
513 as good. Bisect will update your working directory to a revision
514 for testing (unless the -U/--noupdate option is specified). Once
514 for testing (unless the -U/--noupdate option is specified). Once
515 you have performed tests, mark the working directory as good or
515 you have performed tests, mark the working directory as good or
516 bad, and bisect will either update to another candidate changeset
516 bad, and bisect will either update to another candidate changeset
517 or announce that it has found the bad revision.
517 or announce that it has found the bad revision.
518
518
519 As a shortcut, you can also use the revision argument to mark a
519 As a shortcut, you can also use the revision argument to mark a
520 revision as good or bad without checking it out first.
520 revision as good or bad without checking it out first.
521
521
522 If you supply a command, it will be used for automatic bisection.
522 If you supply a command, it will be used for automatic bisection.
523 The environment variable HG_NODE will contain the ID of the
523 The environment variable HG_NODE will contain the ID of the
524 changeset being tested. The exit status of the command will be
524 changeset being tested. The exit status of the command will be
525 used to mark revisions as good or bad: status 0 means good, 125
525 used to mark revisions as good or bad: status 0 means good, 125
526 means to skip the revision, 127 (command not found) will abort the
526 means to skip the revision, 127 (command not found) will abort the
527 bisection, and any other non-zero exit status means the revision
527 bisection, and any other non-zero exit status means the revision
528 is bad.
528 is bad.
529
529
530 .. container:: verbose
530 .. container:: verbose
531
531
532 Some examples:
532 Some examples:
533
533
534 - start a bisection with known bad revision 12, and good revision 34::
534 - start a bisection with known bad revision 12, and good revision 34::
535
535
536 hg bisect --bad 34
536 hg bisect --bad 34
537 hg bisect --good 12
537 hg bisect --good 12
538
538
539 - advance the current bisection by marking current revision as good or
539 - advance the current bisection by marking current revision as good or
540 bad::
540 bad::
541
541
542 hg bisect --good
542 hg bisect --good
543 hg bisect --bad
543 hg bisect --bad
544
544
545 - mark the current revision, or a known revision, to be skipped (eg. if
545 - mark the current revision, or a known revision, to be skipped (eg. if
546 that revision is not usable because of another issue)::
546 that revision is not usable because of another issue)::
547
547
548 hg bisect --skip
548 hg bisect --skip
549 hg bisect --skip 23
549 hg bisect --skip 23
550
550
551 - forget the current bisection::
551 - forget the current bisection::
552
552
553 hg bisect --reset
553 hg bisect --reset
554
554
555 - use 'make && make tests' to automatically find the first broken
555 - use 'make && make tests' to automatically find the first broken
556 revision::
556 revision::
557
557
558 hg bisect --reset
558 hg bisect --reset
559 hg bisect --bad 34
559 hg bisect --bad 34
560 hg bisect --good 12
560 hg bisect --good 12
561 hg bisect --command 'make && make tests'
561 hg bisect --command 'make && make tests'
562
562
563 - see all changesets whose states are already known in the current
563 - see all changesets whose states are already known in the current
564 bisection::
564 bisection::
565
565
566 hg log -r "bisect(pruned)"
566 hg log -r "bisect(pruned)"
567
567
568 - see the changeset currently being bisected (especially useful
568 - see the changeset currently being bisected (especially useful
569 if running with -U/--noupdate)::
569 if running with -U/--noupdate)::
570
570
571 hg log -r "bisect(current)"
571 hg log -r "bisect(current)"
572
572
573 - see all changesets that took part in the current bisection::
573 - see all changesets that took part in the current bisection::
574
574
575 hg log -r "bisect(range)"
575 hg log -r "bisect(range)"
576
576
577 - with the graphlog extension, you can even get a nice graph::
577 - with the graphlog extension, you can even get a nice graph::
578
578
579 hg log --graph -r "bisect(range)"
579 hg log --graph -r "bisect(range)"
580
580
581 See :hg:`help revsets` for more about the `bisect()` keyword.
581 See :hg:`help revsets` for more about the `bisect()` keyword.
582
582
583 Returns 0 on success.
583 Returns 0 on success.
584 """
584 """
585 def extendbisectrange(nodes, good):
585 def extendbisectrange(nodes, good):
586 # bisect is incomplete when it ends on a merge node and
586 # bisect is incomplete when it ends on a merge node and
587 # one of the parent was not checked.
587 # one of the parent was not checked.
588 parents = repo[nodes[0]].parents()
588 parents = repo[nodes[0]].parents()
589 if len(parents) > 1:
589 if len(parents) > 1:
590 side = good and state['bad'] or state['good']
590 side = good and state['bad'] or state['good']
591 num = len(set(i.node() for i in parents) & set(side))
591 num = len(set(i.node() for i in parents) & set(side))
592 if num == 1:
592 if num == 1:
593 return parents[0].ancestor(parents[1])
593 return parents[0].ancestor(parents[1])
594 return None
594 return None
595
595
596 def print_result(nodes, good):
596 def print_result(nodes, good):
597 displayer = cmdutil.show_changeset(ui, repo, {})
597 displayer = cmdutil.show_changeset(ui, repo, {})
598 if len(nodes) == 1:
598 if len(nodes) == 1:
599 # narrowed it down to a single revision
599 # narrowed it down to a single revision
600 if good:
600 if good:
601 ui.write(_("The first good revision is:\n"))
601 ui.write(_("The first good revision is:\n"))
602 else:
602 else:
603 ui.write(_("The first bad revision is:\n"))
603 ui.write(_("The first bad revision is:\n"))
604 displayer.show(repo[nodes[0]])
604 displayer.show(repo[nodes[0]])
605 extendnode = extendbisectrange(nodes, good)
605 extendnode = extendbisectrange(nodes, good)
606 if extendnode is not None:
606 if extendnode is not None:
607 ui.write(_('Not all ancestors of this changeset have been'
607 ui.write(_('Not all ancestors of this changeset have been'
608 ' checked.\nUse bisect --extend to continue the '
608 ' checked.\nUse bisect --extend to continue the '
609 'bisection from\nthe common ancestor, %s.\n')
609 'bisection from\nthe common ancestor, %s.\n')
610 % extendnode)
610 % extendnode)
611 else:
611 else:
612 # multiple possible revisions
612 # multiple possible revisions
613 if good:
613 if good:
614 ui.write(_("Due to skipped revisions, the first "
614 ui.write(_("Due to skipped revisions, the first "
615 "good revision could be any of:\n"))
615 "good revision could be any of:\n"))
616 else:
616 else:
617 ui.write(_("Due to skipped revisions, the first "
617 ui.write(_("Due to skipped revisions, the first "
618 "bad revision could be any of:\n"))
618 "bad revision could be any of:\n"))
619 for n in nodes:
619 for n in nodes:
620 displayer.show(repo[n])
620 displayer.show(repo[n])
621 displayer.close()
621 displayer.close()
622
622
623 def check_state(state, interactive=True):
623 def check_state(state, interactive=True):
624 if not state['good'] or not state['bad']:
624 if not state['good'] or not state['bad']:
625 if (good or bad or skip or reset) and interactive:
625 if (good or bad or skip or reset) and interactive:
626 return
626 return
627 if not state['good']:
627 if not state['good']:
628 raise util.Abort(_('cannot bisect (no known good revisions)'))
628 raise util.Abort(_('cannot bisect (no known good revisions)'))
629 else:
629 else:
630 raise util.Abort(_('cannot bisect (no known bad revisions)'))
630 raise util.Abort(_('cannot bisect (no known bad revisions)'))
631 return True
631 return True
632
632
633 # backward compatibility
633 # backward compatibility
634 if rev in "good bad reset init".split():
634 if rev in "good bad reset init".split():
635 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
635 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
636 cmd, rev, extra = rev, extra, None
636 cmd, rev, extra = rev, extra, None
637 if cmd == "good":
637 if cmd == "good":
638 good = True
638 good = True
639 elif cmd == "bad":
639 elif cmd == "bad":
640 bad = True
640 bad = True
641 else:
641 else:
642 reset = True
642 reset = True
643 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
643 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
644 raise util.Abort(_('incompatible arguments'))
644 raise util.Abort(_('incompatible arguments'))
645
645
646 if reset:
646 if reset:
647 p = repo.join("bisect.state")
647 p = repo.join("bisect.state")
648 if os.path.exists(p):
648 if os.path.exists(p):
649 os.unlink(p)
649 os.unlink(p)
650 return
650 return
651
651
652 state = hbisect.load_state(repo)
652 state = hbisect.load_state(repo)
653
653
654 if command:
654 if command:
655 changesets = 1
655 changesets = 1
656 try:
656 try:
657 node = state['current'][0]
657 node = state['current'][0]
658 except LookupError:
658 except LookupError:
659 if noupdate:
659 if noupdate:
660 raise util.Abort(_('current bisect revision is unknown - '
660 raise util.Abort(_('current bisect revision is unknown - '
661 'start a new bisect to fix'))
661 'start a new bisect to fix'))
662 node, p2 = repo.dirstate.parents()
662 node, p2 = repo.dirstate.parents()
663 if p2 != nullid:
663 if p2 != nullid:
664 raise util.Abort(_('current bisect revision is a merge'))
664 raise util.Abort(_('current bisect revision is a merge'))
665 try:
665 try:
666 while changesets:
666 while changesets:
667 # update state
667 # update state
668 state['current'] = [node]
668 state['current'] = [node]
669 hbisect.save_state(repo, state)
669 hbisect.save_state(repo, state)
670 status = util.system(command,
670 status = util.system(command,
671 environ={'HG_NODE': hex(node)},
671 environ={'HG_NODE': hex(node)},
672 out=ui.fout)
672 out=ui.fout)
673 if status == 125:
673 if status == 125:
674 transition = "skip"
674 transition = "skip"
675 elif status == 0:
675 elif status == 0:
676 transition = "good"
676 transition = "good"
677 # status < 0 means process was killed
677 # status < 0 means process was killed
678 elif status == 127:
678 elif status == 127:
679 raise util.Abort(_("failed to execute %s") % command)
679 raise util.Abort(_("failed to execute %s") % command)
680 elif status < 0:
680 elif status < 0:
681 raise util.Abort(_("%s killed") % command)
681 raise util.Abort(_("%s killed") % command)
682 else:
682 else:
683 transition = "bad"
683 transition = "bad"
684 ctx = scmutil.revsingle(repo, rev, node)
684 ctx = scmutil.revsingle(repo, rev, node)
685 rev = None # clear for future iterations
685 rev = None # clear for future iterations
686 state[transition].append(ctx.node())
686 state[transition].append(ctx.node())
687 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
687 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
688 check_state(state, interactive=False)
688 check_state(state, interactive=False)
689 # bisect
689 # bisect
690 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
690 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
691 # update to next check
691 # update to next check
692 node = nodes[0]
692 node = nodes[0]
693 if not noupdate:
693 if not noupdate:
694 cmdutil.bailifchanged(repo)
694 cmdutil.bailifchanged(repo)
695 hg.clean(repo, node, show_stats=False)
695 hg.clean(repo, node, show_stats=False)
696 finally:
696 finally:
697 state['current'] = [node]
697 state['current'] = [node]
698 hbisect.save_state(repo, state)
698 hbisect.save_state(repo, state)
699 print_result(nodes, good)
699 print_result(nodes, good)
700 return
700 return
701
701
702 # update state
702 # update state
703
703
704 if rev:
704 if rev:
705 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
705 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
706 else:
706 else:
707 nodes = [repo.lookup('.')]
707 nodes = [repo.lookup('.')]
708
708
709 if good or bad or skip:
709 if good or bad or skip:
710 if good:
710 if good:
711 state['good'] += nodes
711 state['good'] += nodes
712 elif bad:
712 elif bad:
713 state['bad'] += nodes
713 state['bad'] += nodes
714 elif skip:
714 elif skip:
715 state['skip'] += nodes
715 state['skip'] += nodes
716 hbisect.save_state(repo, state)
716 hbisect.save_state(repo, state)
717
717
718 if not check_state(state):
718 if not check_state(state):
719 return
719 return
720
720
721 # actually bisect
721 # actually bisect
722 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
722 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
723 if extend:
723 if extend:
724 if not changesets:
724 if not changesets:
725 extendnode = extendbisectrange(nodes, good)
725 extendnode = extendbisectrange(nodes, good)
726 if extendnode is not None:
726 if extendnode is not None:
727 ui.write(_("Extending search to changeset %d:%s\n"
727 ui.write(_("Extending search to changeset %d:%s\n"
728 % (extendnode.rev(), extendnode)))
728 % (extendnode.rev(), extendnode)))
729 state['current'] = [extendnode.node()]
729 state['current'] = [extendnode.node()]
730 hbisect.save_state(repo, state)
730 hbisect.save_state(repo, state)
731 if noupdate:
731 if noupdate:
732 return
732 return
733 cmdutil.bailifchanged(repo)
733 cmdutil.bailifchanged(repo)
734 return hg.clean(repo, extendnode.node())
734 return hg.clean(repo, extendnode.node())
735 raise util.Abort(_("nothing to extend"))
735 raise util.Abort(_("nothing to extend"))
736
736
737 if changesets == 0:
737 if changesets == 0:
738 print_result(nodes, good)
738 print_result(nodes, good)
739 else:
739 else:
740 assert len(nodes) == 1 # only a single node can be tested next
740 assert len(nodes) == 1 # only a single node can be tested next
741 node = nodes[0]
741 node = nodes[0]
742 # compute the approximate number of remaining tests
742 # compute the approximate number of remaining tests
743 tests, size = 0, 2
743 tests, size = 0, 2
744 while size <= changesets:
744 while size <= changesets:
745 tests, size = tests + 1, size * 2
745 tests, size = tests + 1, size * 2
746 rev = repo.changelog.rev(node)
746 rev = repo.changelog.rev(node)
747 ui.write(_("Testing changeset %d:%s "
747 ui.write(_("Testing changeset %d:%s "
748 "(%d changesets remaining, ~%d tests)\n")
748 "(%d changesets remaining, ~%d tests)\n")
749 % (rev, short(node), changesets, tests))
749 % (rev, short(node), changesets, tests))
750 state['current'] = [node]
750 state['current'] = [node]
751 hbisect.save_state(repo, state)
751 hbisect.save_state(repo, state)
752 if not noupdate:
752 if not noupdate:
753 cmdutil.bailifchanged(repo)
753 cmdutil.bailifchanged(repo)
754 return hg.clean(repo, node)
754 return hg.clean(repo, node)
755
755
756 @command('bookmarks',
756 @command('bookmarks',
757 [('f', 'force', False, _('force')),
757 [('f', 'force', False, _('force')),
758 ('r', 'rev', '', _('revision'), _('REV')),
758 ('r', 'rev', '', _('revision'), _('REV')),
759 ('d', 'delete', False, _('delete a given bookmark')),
759 ('d', 'delete', False, _('delete a given bookmark')),
760 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
760 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
761 ('i', 'inactive', False, _('mark a bookmark inactive'))],
761 ('i', 'inactive', False, _('mark a bookmark inactive'))],
762 _('hg bookmarks [-f] [-d] [-i] [-m NAME] [-r REV] [NAME]'))
762 _('hg bookmarks [-f] [-d] [-i] [-m NAME] [-r REV] [NAME]'))
763 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False,
763 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False,
764 rename=None, inactive=False):
764 rename=None, inactive=False):
765 '''track a line of development with movable markers
765 '''track a line of development with movable markers
766
766
767 Bookmarks are pointers to certain commits that move when committing.
767 Bookmarks are pointers to certain commits that move when committing.
768 Bookmarks are local. They can be renamed, copied and deleted. It is
768 Bookmarks are local. They can be renamed, copied and deleted. It is
769 possible to use :hg:`merge NAME` to merge from a given bookmark, and
769 possible to use :hg:`merge NAME` to merge from a given bookmark, and
770 :hg:`update NAME` to update to a given bookmark.
770 :hg:`update NAME` to update to a given bookmark.
771
771
772 You can use :hg:`bookmark NAME` to set a bookmark on the working
772 You can use :hg:`bookmark NAME` to set a bookmark on the working
773 directory's parent revision with the given name. If you specify
773 directory's parent revision with the given name. If you specify
774 a revision using -r REV (where REV may be an existing bookmark),
774 a revision using -r REV (where REV may be an existing bookmark),
775 the bookmark is assigned to that revision.
775 the bookmark is assigned to that revision.
776
776
777 Bookmarks can be pushed and pulled between repositories (see :hg:`help
777 Bookmarks can be pushed and pulled between repositories (see :hg:`help
778 push` and :hg:`help pull`). This requires both the local and remote
778 push` and :hg:`help pull`). This requires both the local and remote
779 repositories to support bookmarks. For versions prior to 1.8, this means
779 repositories to support bookmarks. For versions prior to 1.8, this means
780 the bookmarks extension must be enabled.
780 the bookmarks extension must be enabled.
781
781
782 With -i/--inactive, the new bookmark will not be made the active
782 With -i/--inactive, the new bookmark will not be made the active
783 bookmark. If -r/--rev is given, the new bookmark will not be made
783 bookmark. If -r/--rev is given, the new bookmark will not be made
784 active even if -i/--inactive is not given. If no NAME is given, the
784 active even if -i/--inactive is not given. If no NAME is given, the
785 current active bookmark will be marked inactive.
785 current active bookmark will be marked inactive.
786 '''
786 '''
787 hexfn = ui.debugflag and hex or short
787 hexfn = ui.debugflag and hex or short
788 marks = repo._bookmarks
788 marks = repo._bookmarks
789 cur = repo.changectx('.').node()
789 cur = repo.changectx('.').node()
790
790
791 if delete:
791 if delete:
792 if mark is None:
792 if mark is None:
793 raise util.Abort(_("bookmark name required"))
793 raise util.Abort(_("bookmark name required"))
794 if mark not in marks:
794 if mark not in marks:
795 raise util.Abort(_("bookmark '%s' does not exist") % mark)
795 raise util.Abort(_("bookmark '%s' does not exist") % mark)
796 if mark == repo._bookmarkcurrent:
796 if mark == repo._bookmarkcurrent:
797 bookmarks.setcurrent(repo, None)
797 bookmarks.setcurrent(repo, None)
798 del marks[mark]
798 del marks[mark]
799 bookmarks.write(repo)
799 bookmarks.write(repo)
800 return
800 return
801
801
802 if rename:
802 if rename:
803 if rename not in marks:
803 if rename not in marks:
804 raise util.Abort(_("bookmark '%s' does not exist") % rename)
804 raise util.Abort(_("bookmark '%s' does not exist") % rename)
805 if mark in marks and not force:
805 if mark in marks and not force:
806 raise util.Abort(_("bookmark '%s' already exists "
806 raise util.Abort(_("bookmark '%s' already exists "
807 "(use -f to force)") % mark)
807 "(use -f to force)") % mark)
808 if mark is None:
808 if mark is None:
809 raise util.Abort(_("new bookmark name required"))
809 raise util.Abort(_("new bookmark name required"))
810 marks[mark] = marks[rename]
810 marks[mark] = marks[rename]
811 if repo._bookmarkcurrent == rename and not inactive:
811 if repo._bookmarkcurrent == rename and not inactive:
812 bookmarks.setcurrent(repo, mark)
812 bookmarks.setcurrent(repo, mark)
813 del marks[rename]
813 del marks[rename]
814 bookmarks.write(repo)
814 bookmarks.write(repo)
815 return
815 return
816
816
817 if mark is not None:
817 if mark is not None:
818 if "\n" in mark:
818 if "\n" in mark:
819 raise util.Abort(_("bookmark name cannot contain newlines"))
819 raise util.Abort(_("bookmark name cannot contain newlines"))
820 mark = mark.strip()
820 mark = mark.strip()
821 if not mark:
821 if not mark:
822 raise util.Abort(_("bookmark names cannot consist entirely of "
822 raise util.Abort(_("bookmark names cannot consist entirely of "
823 "whitespace"))
823 "whitespace"))
824 if inactive and mark == repo._bookmarkcurrent:
824 if inactive and mark == repo._bookmarkcurrent:
825 bookmarks.setcurrent(repo, None)
825 bookmarks.setcurrent(repo, None)
826 return
826 return
827 if mark in marks and not force:
827 if mark in marks and not force:
828 raise util.Abort(_("bookmark '%s' already exists "
828 raise util.Abort(_("bookmark '%s' already exists "
829 "(use -f to force)") % mark)
829 "(use -f to force)") % mark)
830 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
830 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
831 and not force):
831 and not force):
832 raise util.Abort(
832 raise util.Abort(
833 _("a bookmark cannot have the name of an existing branch"))
833 _("a bookmark cannot have the name of an existing branch"))
834 if rev:
834 if rev:
835 marks[mark] = repo.lookup(rev)
835 marks[mark] = repo.lookup(rev)
836 else:
836 else:
837 marks[mark] = cur
837 marks[mark] = cur
838 if not inactive and cur == marks[mark]:
838 if not inactive and cur == marks[mark]:
839 bookmarks.setcurrent(repo, mark)
839 bookmarks.setcurrent(repo, mark)
840 bookmarks.write(repo)
840 bookmarks.write(repo)
841 return
841 return
842
842
843 if mark is None:
843 if mark is None:
844 if rev:
844 if rev:
845 raise util.Abort(_("bookmark name required"))
845 raise util.Abort(_("bookmark name required"))
846 if len(marks) == 0:
846 if len(marks) == 0:
847 ui.status(_("no bookmarks set\n"))
847 ui.status(_("no bookmarks set\n"))
848 else:
848 else:
849 for bmark, n in sorted(marks.iteritems()):
849 for bmark, n in sorted(marks.iteritems()):
850 current = repo._bookmarkcurrent
850 current = repo._bookmarkcurrent
851 if bmark == current and n == cur:
851 if bmark == current and n == cur:
852 prefix, label = '*', 'bookmarks.current'
852 prefix, label = '*', 'bookmarks.current'
853 else:
853 else:
854 prefix, label = ' ', ''
854 prefix, label = ' ', ''
855
855
856 if ui.quiet:
856 if ui.quiet:
857 ui.write("%s\n" % bmark, label=label)
857 ui.write("%s\n" % bmark, label=label)
858 else:
858 else:
859 ui.write(" %s %-25s %d:%s\n" % (
859 ui.write(" %s %-25s %d:%s\n" % (
860 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
860 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
861 label=label)
861 label=label)
862 return
862 return
863
863
864 @command('branch',
864 @command('branch',
865 [('f', 'force', None,
865 [('f', 'force', None,
866 _('set branch name even if it shadows an existing branch')),
866 _('set branch name even if it shadows an existing branch')),
867 ('C', 'clean', None, _('reset branch name to parent branch name'))],
867 ('C', 'clean', None, _('reset branch name to parent branch name'))],
868 _('[-fC] [NAME]'))
868 _('[-fC] [NAME]'))
869 def branch(ui, repo, label=None, **opts):
869 def branch(ui, repo, label=None, **opts):
870 """set or show the current branch name
870 """set or show the current branch name
871
871
872 .. note::
872 .. note::
873 Branch names are permanent and global. Use :hg:`bookmark` to create a
873 Branch names are permanent and global. Use :hg:`bookmark` to create a
874 light-weight bookmark instead. See :hg:`help glossary` for more
874 light-weight bookmark instead. See :hg:`help glossary` for more
875 information about named branches and bookmarks.
875 information about named branches and bookmarks.
876
876
877 With no argument, show the current branch name. With one argument,
877 With no argument, show the current branch name. With one argument,
878 set the working directory branch name (the branch will not exist
878 set the working directory branch name (the branch will not exist
879 in the repository until the next commit). Standard practice
879 in the repository until the next commit). Standard practice
880 recommends that primary development take place on the 'default'
880 recommends that primary development take place on the 'default'
881 branch.
881 branch.
882
882
883 Unless -f/--force is specified, branch will not let you set a
883 Unless -f/--force is specified, branch will not let you set a
884 branch name that already exists, even if it's inactive.
884 branch name that already exists, even if it's inactive.
885
885
886 Use -C/--clean to reset the working directory branch to that of
886 Use -C/--clean to reset the working directory branch to that of
887 the parent of the working directory, negating a previous branch
887 the parent of the working directory, negating a previous branch
888 change.
888 change.
889
889
890 Use the command :hg:`update` to switch to an existing branch. Use
890 Use the command :hg:`update` to switch to an existing branch. Use
891 :hg:`commit --close-branch` to mark this branch as closed.
891 :hg:`commit --close-branch` to mark this branch as closed.
892
892
893 Returns 0 on success.
893 Returns 0 on success.
894 """
894 """
895 if not opts.get('clean') and not label:
895 if not opts.get('clean') and not label:
896 ui.write("%s\n" % repo.dirstate.branch())
896 ui.write("%s\n" % repo.dirstate.branch())
897 return
897 return
898
898
899 wlock = repo.wlock()
899 wlock = repo.wlock()
900 try:
900 try:
901 if opts.get('clean'):
901 if opts.get('clean'):
902 label = repo[None].p1().branch()
902 label = repo[None].p1().branch()
903 repo.dirstate.setbranch(label)
903 repo.dirstate.setbranch(label)
904 ui.status(_('reset working directory to branch %s\n') % label)
904 ui.status(_('reset working directory to branch %s\n') % label)
905 elif label:
905 elif label:
906 if not opts.get('force') and label in repo.branchmap():
906 if not opts.get('force') and label in repo.branchmap():
907 if label not in [p.branch() for p in repo.parents()]:
907 if label not in [p.branch() for p in repo.parents()]:
908 raise util.Abort(_('a branch of the same name already'
908 raise util.Abort(_('a branch of the same name already'
909 ' exists'),
909 ' exists'),
910 # i18n: "it" refers to an existing branch
910 # i18n: "it" refers to an existing branch
911 hint=_("use 'hg update' to switch to it"))
911 hint=_("use 'hg update' to switch to it"))
912 repo.dirstate.setbranch(label)
912 repo.dirstate.setbranch(label)
913 ui.status(_('marked working directory as branch %s\n') % label)
913 ui.status(_('marked working directory as branch %s\n') % label)
914 ui.status(_('(branches are permanent and global, '
914 ui.status(_('(branches are permanent and global, '
915 'did you want a bookmark?)\n'))
915 'did you want a bookmark?)\n'))
916 finally:
916 finally:
917 wlock.release()
917 wlock.release()
918
918
919 @command('branches',
919 @command('branches',
920 [('a', 'active', False, _('show only branches that have unmerged heads')),
920 [('a', 'active', False, _('show only branches that have unmerged heads')),
921 ('c', 'closed', False, _('show normal and closed branches'))],
921 ('c', 'closed', False, _('show normal and closed branches'))],
922 _('[-ac]'))
922 _('[-ac]'))
923 def branches(ui, repo, active=False, closed=False):
923 def branches(ui, repo, active=False, closed=False):
924 """list repository named branches
924 """list repository named branches
925
925
926 List the repository's named branches, indicating which ones are
926 List the repository's named branches, indicating which ones are
927 inactive. If -c/--closed is specified, also list branches which have
927 inactive. If -c/--closed is specified, also list branches which have
928 been marked closed (see :hg:`commit --close-branch`).
928 been marked closed (see :hg:`commit --close-branch`).
929
929
930 If -a/--active is specified, only show active branches. A branch
930 If -a/--active is specified, only show active branches. A branch
931 is considered active if it contains repository heads.
931 is considered active if it contains repository heads.
932
932
933 Use the command :hg:`update` to switch to an existing branch.
933 Use the command :hg:`update` to switch to an existing branch.
934
934
935 Returns 0.
935 Returns 0.
936 """
936 """
937
937
938 hexfunc = ui.debugflag and hex or short
938 hexfunc = ui.debugflag and hex or short
939
939
940 activebranches = set([repo[n].branch() for n in repo.heads()])
940 activebranches = set([repo[n].branch() for n in repo.heads()])
941 branches = []
941 branches = []
942 for tag, heads in repo.branchmap().iteritems():
942 for tag, heads in repo.branchmap().iteritems():
943 for h in reversed(heads):
943 for h in reversed(heads):
944 ctx = repo[h]
944 ctx = repo[h]
945 isopen = not ctx.closesbranch()
945 isopen = not ctx.closesbranch()
946 if isopen:
946 if isopen:
947 tip = ctx
947 tip = ctx
948 break
948 break
949 else:
949 else:
950 tip = repo[heads[-1]]
950 tip = repo[heads[-1]]
951 isactive = tag in activebranches and isopen
951 isactive = tag in activebranches and isopen
952 branches.append((tip, isactive, isopen))
952 branches.append((tip, isactive, isopen))
953 branches.sort(key=lambda i: (i[1], i[0].rev(), i[0].branch(), i[2]),
953 branches.sort(key=lambda i: (i[1], i[0].rev(), i[0].branch(), i[2]),
954 reverse=True)
954 reverse=True)
955
955
956 for ctx, isactive, isopen in branches:
956 for ctx, isactive, isopen in branches:
957 if (not active) or isactive:
957 if (not active) or isactive:
958 if isactive:
958 if isactive:
959 label = 'branches.active'
959 label = 'branches.active'
960 notice = ''
960 notice = ''
961 elif not isopen:
961 elif not isopen:
962 if not closed:
962 if not closed:
963 continue
963 continue
964 label = 'branches.closed'
964 label = 'branches.closed'
965 notice = _(' (closed)')
965 notice = _(' (closed)')
966 else:
966 else:
967 label = 'branches.inactive'
967 label = 'branches.inactive'
968 notice = _(' (inactive)')
968 notice = _(' (inactive)')
969 if ctx.branch() == repo.dirstate.branch():
969 if ctx.branch() == repo.dirstate.branch():
970 label = 'branches.current'
970 label = 'branches.current'
971 rev = str(ctx.rev()).rjust(31 - encoding.colwidth(ctx.branch()))
971 rev = str(ctx.rev()).rjust(31 - encoding.colwidth(ctx.branch()))
972 rev = ui.label('%s:%s' % (rev, hexfunc(ctx.node())),
972 rev = ui.label('%s:%s' % (rev, hexfunc(ctx.node())),
973 'log.changeset')
973 'log.changeset')
974 tag = ui.label(ctx.branch(), label)
974 tag = ui.label(ctx.branch(), label)
975 if ui.quiet:
975 if ui.quiet:
976 ui.write("%s\n" % tag)
976 ui.write("%s\n" % tag)
977 else:
977 else:
978 ui.write("%s %s%s\n" % (tag, rev, notice))
978 ui.write("%s %s%s\n" % (tag, rev, notice))
979
979
980 @command('bundle',
980 @command('bundle',
981 [('f', 'force', None, _('run even when the destination is unrelated')),
981 [('f', 'force', None, _('run even when the destination is unrelated')),
982 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
982 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
983 _('REV')),
983 _('REV')),
984 ('b', 'branch', [], _('a specific branch you would like to bundle'),
984 ('b', 'branch', [], _('a specific branch you would like to bundle'),
985 _('BRANCH')),
985 _('BRANCH')),
986 ('', 'base', [],
986 ('', 'base', [],
987 _('a base changeset assumed to be available at the destination'),
987 _('a base changeset assumed to be available at the destination'),
988 _('REV')),
988 _('REV')),
989 ('a', 'all', None, _('bundle all changesets in the repository')),
989 ('a', 'all', None, _('bundle all changesets in the repository')),
990 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
990 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
991 ] + remoteopts,
991 ] + remoteopts,
992 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
992 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
993 def bundle(ui, repo, fname, dest=None, **opts):
993 def bundle(ui, repo, fname, dest=None, **opts):
994 """create a changegroup file
994 """create a changegroup file
995
995
996 Generate a compressed changegroup file collecting changesets not
996 Generate a compressed changegroup file collecting changesets not
997 known to be in another repository.
997 known to be in another repository.
998
998
999 If you omit the destination repository, then hg assumes the
999 If you omit the destination repository, then hg assumes the
1000 destination will have all the nodes you specify with --base
1000 destination will have all the nodes you specify with --base
1001 parameters. To create a bundle containing all changesets, use
1001 parameters. To create a bundle containing all changesets, use
1002 -a/--all (or --base null).
1002 -a/--all (or --base null).
1003
1003
1004 You can change compression method with the -t/--type option.
1004 You can change compression method with the -t/--type option.
1005 The available compression methods are: none, bzip2, and
1005 The available compression methods are: none, bzip2, and
1006 gzip (by default, bundles are compressed using bzip2).
1006 gzip (by default, bundles are compressed using bzip2).
1007
1007
1008 The bundle file can then be transferred using conventional means
1008 The bundle file can then be transferred using conventional means
1009 and applied to another repository with the unbundle or pull
1009 and applied to another repository with the unbundle or pull
1010 command. This is useful when direct push and pull are not
1010 command. This is useful when direct push and pull are not
1011 available or when exporting an entire repository is undesirable.
1011 available or when exporting an entire repository is undesirable.
1012
1012
1013 Applying bundles preserves all changeset contents including
1013 Applying bundles preserves all changeset contents including
1014 permissions, copy/rename information, and revision history.
1014 permissions, copy/rename information, and revision history.
1015
1015
1016 Returns 0 on success, 1 if no changes found.
1016 Returns 0 on success, 1 if no changes found.
1017 """
1017 """
1018 revs = None
1018 revs = None
1019 if 'rev' in opts:
1019 if 'rev' in opts:
1020 revs = scmutil.revrange(repo, opts['rev'])
1020 revs = scmutil.revrange(repo, opts['rev'])
1021
1021
1022 bundletype = opts.get('type', 'bzip2').lower()
1022 bundletype = opts.get('type', 'bzip2').lower()
1023 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1023 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1024 bundletype = btypes.get(bundletype)
1024 bundletype = btypes.get(bundletype)
1025 if bundletype not in changegroup.bundletypes:
1025 if bundletype not in changegroup.bundletypes:
1026 raise util.Abort(_('unknown bundle type specified with --type'))
1026 raise util.Abort(_('unknown bundle type specified with --type'))
1027
1027
1028 if opts.get('all'):
1028 if opts.get('all'):
1029 base = ['null']
1029 base = ['null']
1030 else:
1030 else:
1031 base = scmutil.revrange(repo, opts.get('base'))
1031 base = scmutil.revrange(repo, opts.get('base'))
1032 if base:
1032 if base:
1033 if dest:
1033 if dest:
1034 raise util.Abort(_("--base is incompatible with specifying "
1034 raise util.Abort(_("--base is incompatible with specifying "
1035 "a destination"))
1035 "a destination"))
1036 common = [repo.lookup(rev) for rev in base]
1036 common = [repo.lookup(rev) for rev in base]
1037 heads = revs and map(repo.lookup, revs) or revs
1037 heads = revs and map(repo.lookup, revs) or revs
1038 cg = repo.getbundle('bundle', heads=heads, common=common)
1038 cg = repo.getbundle('bundle', heads=heads, common=common)
1039 outgoing = None
1039 outgoing = None
1040 else:
1040 else:
1041 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1041 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1042 dest, branches = hg.parseurl(dest, opts.get('branch'))
1042 dest, branches = hg.parseurl(dest, opts.get('branch'))
1043 other = hg.peer(repo, opts, dest)
1043 other = hg.peer(repo, opts, dest)
1044 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
1044 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
1045 heads = revs and map(repo.lookup, revs) or revs
1045 heads = revs and map(repo.lookup, revs) or revs
1046 outgoing = discovery.findcommonoutgoing(repo, other,
1046 outgoing = discovery.findcommonoutgoing(repo, other,
1047 onlyheads=heads,
1047 onlyheads=heads,
1048 force=opts.get('force'),
1048 force=opts.get('force'),
1049 portable=True)
1049 portable=True)
1050 cg = repo.getlocalbundle('bundle', outgoing)
1050 cg = repo.getlocalbundle('bundle', outgoing)
1051 if not cg:
1051 if not cg:
1052 scmutil.nochangesfound(ui, outgoing and outgoing.excluded)
1052 scmutil.nochangesfound(ui, outgoing and outgoing.excluded)
1053 return 1
1053 return 1
1054
1054
1055 changegroup.writebundle(cg, fname, bundletype)
1055 changegroup.writebundle(cg, fname, bundletype)
1056
1056
1057 @command('cat',
1057 @command('cat',
1058 [('o', 'output', '',
1058 [('o', 'output', '',
1059 _('print output to file with formatted name'), _('FORMAT')),
1059 _('print output to file with formatted name'), _('FORMAT')),
1060 ('r', 'rev', '', _('print the given revision'), _('REV')),
1060 ('r', 'rev', '', _('print the given revision'), _('REV')),
1061 ('', 'decode', None, _('apply any matching decode filter')),
1061 ('', 'decode', None, _('apply any matching decode filter')),
1062 ] + walkopts,
1062 ] + walkopts,
1063 _('[OPTION]... FILE...'))
1063 _('[OPTION]... FILE...'))
1064 def cat(ui, repo, file1, *pats, **opts):
1064 def cat(ui, repo, file1, *pats, **opts):
1065 """output the current or given revision of files
1065 """output the current or given revision of files
1066
1066
1067 Print the specified files as they were at the given revision. If
1067 Print the specified files as they were at the given revision. If
1068 no revision is given, the parent of the working directory is used,
1068 no revision is given, the parent of the working directory is used,
1069 or tip if no revision is checked out.
1069 or tip if no revision is checked out.
1070
1070
1071 Output may be to a file, in which case the name of the file is
1071 Output may be to a file, in which case the name of the file is
1072 given using a format string. The formatting rules are the same as
1072 given using a format string. The formatting rules are the same as
1073 for the export command, with the following additions:
1073 for the export command, with the following additions:
1074
1074
1075 :``%s``: basename of file being printed
1075 :``%s``: basename of file being printed
1076 :``%d``: dirname of file being printed, or '.' if in repository root
1076 :``%d``: dirname of file being printed, or '.' if in repository root
1077 :``%p``: root-relative path name of file being printed
1077 :``%p``: root-relative path name of file being printed
1078
1078
1079 Returns 0 on success.
1079 Returns 0 on success.
1080 """
1080 """
1081 ctx = scmutil.revsingle(repo, opts.get('rev'))
1081 ctx = scmutil.revsingle(repo, opts.get('rev'))
1082 err = 1
1082 err = 1
1083 m = scmutil.match(ctx, (file1,) + pats, opts)
1083 m = scmutil.match(ctx, (file1,) + pats, opts)
1084 for abs in ctx.walk(m):
1084 for abs in ctx.walk(m):
1085 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1085 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1086 pathname=abs)
1086 pathname=abs)
1087 data = ctx[abs].data()
1087 data = ctx[abs].data()
1088 if opts.get('decode'):
1088 if opts.get('decode'):
1089 data = repo.wwritedata(abs, data)
1089 data = repo.wwritedata(abs, data)
1090 fp.write(data)
1090 fp.write(data)
1091 fp.close()
1091 fp.close()
1092 err = 0
1092 err = 0
1093 return err
1093 return err
1094
1094
1095 @command('^clone',
1095 @command('^clone',
1096 [('U', 'noupdate', None,
1096 [('U', 'noupdate', None,
1097 _('the clone will include an empty working copy (only a repository)')),
1097 _('the clone will include an empty working copy (only a repository)')),
1098 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
1098 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
1099 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1099 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1100 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1100 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1101 ('', 'pull', None, _('use pull protocol to copy metadata')),
1101 ('', 'pull', None, _('use pull protocol to copy metadata')),
1102 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1102 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1103 ] + remoteopts,
1103 ] + remoteopts,
1104 _('[OPTION]... SOURCE [DEST]'))
1104 _('[OPTION]... SOURCE [DEST]'))
1105 def clone(ui, source, dest=None, **opts):
1105 def clone(ui, source, dest=None, **opts):
1106 """make a copy of an existing repository
1106 """make a copy of an existing repository
1107
1107
1108 Create a copy of an existing repository in a new directory.
1108 Create a copy of an existing repository in a new directory.
1109
1109
1110 If no destination directory name is specified, it defaults to the
1110 If no destination directory name is specified, it defaults to the
1111 basename of the source.
1111 basename of the source.
1112
1112
1113 The location of the source is added to the new repository's
1113 The location of the source is added to the new repository's
1114 ``.hg/hgrc`` file, as the default to be used for future pulls.
1114 ``.hg/hgrc`` file, as the default to be used for future pulls.
1115
1115
1116 Only local paths and ``ssh://`` URLs are supported as
1116 Only local paths and ``ssh://`` URLs are supported as
1117 destinations. For ``ssh://`` destinations, no working directory or
1117 destinations. For ``ssh://`` destinations, no working directory or
1118 ``.hg/hgrc`` will be created on the remote side.
1118 ``.hg/hgrc`` will be created on the remote side.
1119
1119
1120 To pull only a subset of changesets, specify one or more revisions
1120 To pull only a subset of changesets, specify one or more revisions
1121 identifiers with -r/--rev or branches with -b/--branch. The
1121 identifiers with -r/--rev or branches with -b/--branch. The
1122 resulting clone will contain only the specified changesets and
1122 resulting clone will contain only the specified changesets and
1123 their ancestors. These options (or 'clone src#rev dest') imply
1123 their ancestors. These options (or 'clone src#rev dest') imply
1124 --pull, even for local source repositories. Note that specifying a
1124 --pull, even for local source repositories. Note that specifying a
1125 tag will include the tagged changeset but not the changeset
1125 tag will include the tagged changeset but not the changeset
1126 containing the tag.
1126 containing the tag.
1127
1127
1128 To check out a particular version, use -u/--update, or
1128 To check out a particular version, use -u/--update, or
1129 -U/--noupdate to create a clone with no working directory.
1129 -U/--noupdate to create a clone with no working directory.
1130
1130
1131 .. container:: verbose
1131 .. container:: verbose
1132
1132
1133 For efficiency, hardlinks are used for cloning whenever the
1133 For efficiency, hardlinks are used for cloning whenever the
1134 source and destination are on the same filesystem (note this
1134 source and destination are on the same filesystem (note this
1135 applies only to the repository data, not to the working
1135 applies only to the repository data, not to the working
1136 directory). Some filesystems, such as AFS, implement hardlinking
1136 directory). Some filesystems, such as AFS, implement hardlinking
1137 incorrectly, but do not report errors. In these cases, use the
1137 incorrectly, but do not report errors. In these cases, use the
1138 --pull option to avoid hardlinking.
1138 --pull option to avoid hardlinking.
1139
1139
1140 In some cases, you can clone repositories and the working
1140 In some cases, you can clone repositories and the working
1141 directory using full hardlinks with ::
1141 directory using full hardlinks with ::
1142
1142
1143 $ cp -al REPO REPOCLONE
1143 $ cp -al REPO REPOCLONE
1144
1144
1145 This is the fastest way to clone, but it is not always safe. The
1145 This is the fastest way to clone, but it is not always safe. The
1146 operation is not atomic (making sure REPO is not modified during
1146 operation is not atomic (making sure REPO is not modified during
1147 the operation is up to you) and you have to make sure your
1147 the operation is up to you) and you have to make sure your
1148 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1148 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1149 so). Also, this is not compatible with certain extensions that
1149 so). Also, this is not compatible with certain extensions that
1150 place their metadata under the .hg directory, such as mq.
1150 place their metadata under the .hg directory, such as mq.
1151
1151
1152 Mercurial will update the working directory to the first applicable
1152 Mercurial will update the working directory to the first applicable
1153 revision from this list:
1153 revision from this list:
1154
1154
1155 a) null if -U or the source repository has no changesets
1155 a) null if -U or the source repository has no changesets
1156 b) if -u . and the source repository is local, the first parent of
1156 b) if -u . and the source repository is local, the first parent of
1157 the source repository's working directory
1157 the source repository's working directory
1158 c) the changeset specified with -u (if a branch name, this means the
1158 c) the changeset specified with -u (if a branch name, this means the
1159 latest head of that branch)
1159 latest head of that branch)
1160 d) the changeset specified with -r
1160 d) the changeset specified with -r
1161 e) the tipmost head specified with -b
1161 e) the tipmost head specified with -b
1162 f) the tipmost head specified with the url#branch source syntax
1162 f) the tipmost head specified with the url#branch source syntax
1163 g) the tipmost head of the default branch
1163 g) the tipmost head of the default branch
1164 h) tip
1164 h) tip
1165
1165
1166 Examples:
1166 Examples:
1167
1167
1168 - clone a remote repository to a new directory named hg/::
1168 - clone a remote repository to a new directory named hg/::
1169
1169
1170 hg clone http://selenic.com/hg
1170 hg clone http://selenic.com/hg
1171
1171
1172 - create a lightweight local clone::
1172 - create a lightweight local clone::
1173
1173
1174 hg clone project/ project-feature/
1174 hg clone project/ project-feature/
1175
1175
1176 - clone from an absolute path on an ssh server (note double-slash)::
1176 - clone from an absolute path on an ssh server (note double-slash)::
1177
1177
1178 hg clone ssh://user@server//home/projects/alpha/
1178 hg clone ssh://user@server//home/projects/alpha/
1179
1179
1180 - do a high-speed clone over a LAN while checking out a
1180 - do a high-speed clone over a LAN while checking out a
1181 specified version::
1181 specified version::
1182
1182
1183 hg clone --uncompressed http://server/repo -u 1.5
1183 hg clone --uncompressed http://server/repo -u 1.5
1184
1184
1185 - create a repository without changesets after a particular revision::
1185 - create a repository without changesets after a particular revision::
1186
1186
1187 hg clone -r 04e544 experimental/ good/
1187 hg clone -r 04e544 experimental/ good/
1188
1188
1189 - clone (and track) a particular named branch::
1189 - clone (and track) a particular named branch::
1190
1190
1191 hg clone http://selenic.com/hg#stable
1191 hg clone http://selenic.com/hg#stable
1192
1192
1193 See :hg:`help urls` for details on specifying URLs.
1193 See :hg:`help urls` for details on specifying URLs.
1194
1194
1195 Returns 0 on success.
1195 Returns 0 on success.
1196 """
1196 """
1197 if opts.get('noupdate') and opts.get('updaterev'):
1197 if opts.get('noupdate') and opts.get('updaterev'):
1198 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1198 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1199
1199
1200 r = hg.clone(ui, opts, source, dest,
1200 r = hg.clone(ui, opts, source, dest,
1201 pull=opts.get('pull'),
1201 pull=opts.get('pull'),
1202 stream=opts.get('uncompressed'),
1202 stream=opts.get('uncompressed'),
1203 rev=opts.get('rev'),
1203 rev=opts.get('rev'),
1204 update=opts.get('updaterev') or not opts.get('noupdate'),
1204 update=opts.get('updaterev') or not opts.get('noupdate'),
1205 branch=opts.get('branch'))
1205 branch=opts.get('branch'))
1206
1206
1207 return r is None
1207 return r is None
1208
1208
1209 @command('^commit|ci',
1209 @command('^commit|ci',
1210 [('A', 'addremove', None,
1210 [('A', 'addremove', None,
1211 _('mark new/missing files as added/removed before committing')),
1211 _('mark new/missing files as added/removed before committing')),
1212 ('', 'close-branch', None,
1212 ('', 'close-branch', None,
1213 _('mark a branch as closed, hiding it from the branch list')),
1213 _('mark a branch as closed, hiding it from the branch list')),
1214 ('', 'amend', None, _('amend the parent of the working dir')),
1214 ('', 'amend', None, _('amend the parent of the working dir')),
1215 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1215 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1216 _('[OPTION]... [FILE]...'))
1216 _('[OPTION]... [FILE]...'))
1217 def commit(ui, repo, *pats, **opts):
1217 def commit(ui, repo, *pats, **opts):
1218 """commit the specified files or all outstanding changes
1218 """commit the specified files or all outstanding changes
1219
1219
1220 Commit changes to the given files into the repository. Unlike a
1220 Commit changes to the given files into the repository. Unlike a
1221 centralized SCM, this operation is a local operation. See
1221 centralized SCM, this operation is a local operation. See
1222 :hg:`push` for a way to actively distribute your changes.
1222 :hg:`push` for a way to actively distribute your changes.
1223
1223
1224 If a list of files is omitted, all changes reported by :hg:`status`
1224 If a list of files is omitted, all changes reported by :hg:`status`
1225 will be committed.
1225 will be committed.
1226
1226
1227 If you are committing the result of a merge, do not provide any
1227 If you are committing the result of a merge, do not provide any
1228 filenames or -I/-X filters.
1228 filenames or -I/-X filters.
1229
1229
1230 If no commit message is specified, Mercurial starts your
1230 If no commit message is specified, Mercurial starts your
1231 configured editor where you can enter a message. In case your
1231 configured editor where you can enter a message. In case your
1232 commit fails, you will find a backup of your message in
1232 commit fails, you will find a backup of your message in
1233 ``.hg/last-message.txt``.
1233 ``.hg/last-message.txt``.
1234
1234
1235 The --amend flag can be used to amend the parent of the
1235 The --amend flag can be used to amend the parent of the
1236 working directory with a new commit that contains the changes
1236 working directory with a new commit that contains the changes
1237 in the parent in addition to those currently reported by :hg:`status`,
1237 in the parent in addition to those currently reported by :hg:`status`,
1238 if there are any. The old commit is stored in a backup bundle in
1238 if there are any. The old commit is stored in a backup bundle in
1239 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1239 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1240 on how to restore it).
1240 on how to restore it).
1241
1241
1242 Message, user and date are taken from the amended commit unless
1242 Message, user and date are taken from the amended commit unless
1243 specified. When a message isn't specified on the command line,
1243 specified. When a message isn't specified on the command line,
1244 the editor will open with the message of the amended commit.
1244 the editor will open with the message of the amended commit.
1245
1245
1246 It is not possible to amend public changesets (see :hg:`help phases`)
1246 It is not possible to amend public changesets (see :hg:`help phases`)
1247 or changesets that have children.
1247 or changesets that have children.
1248
1248
1249 See :hg:`help dates` for a list of formats valid for -d/--date.
1249 See :hg:`help dates` for a list of formats valid for -d/--date.
1250
1250
1251 Returns 0 on success, 1 if nothing changed.
1251 Returns 0 on success, 1 if nothing changed.
1252 """
1252 """
1253 if opts.get('subrepos'):
1253 if opts.get('subrepos'):
1254 # Let --subrepos on the command line overide config setting.
1254 # Let --subrepos on the command line overide config setting.
1255 ui.setconfig('ui', 'commitsubrepos', True)
1255 ui.setconfig('ui', 'commitsubrepos', True)
1256
1256
1257 extra = {}
1257 extra = {}
1258 if opts.get('close_branch'):
1258 if opts.get('close_branch'):
1259 if repo['.'].node() not in repo.branchheads():
1259 if repo['.'].node() not in repo.branchheads():
1260 # The topo heads set is included in the branch heads set of the
1260 # The topo heads set is included in the branch heads set of the
1261 # current branch, so it's sufficient to test branchheads
1261 # current branch, so it's sufficient to test branchheads
1262 raise util.Abort(_('can only close branch heads'))
1262 raise util.Abort(_('can only close branch heads'))
1263 extra['close'] = 1
1263 extra['close'] = 1
1264
1264
1265 branch = repo[None].branch()
1265 branch = repo[None].branch()
1266 bheads = repo.branchheads(branch)
1266 bheads = repo.branchheads(branch)
1267
1267
1268 if opts.get('amend'):
1268 if opts.get('amend'):
1269 if ui.configbool('ui', 'commitsubrepos'):
1269 if ui.configbool('ui', 'commitsubrepos'):
1270 raise util.Abort(_('cannot amend recursively'))
1270 raise util.Abort(_('cannot amend recursively'))
1271
1271
1272 old = repo['.']
1272 old = repo['.']
1273 if old.phase() == phases.public:
1273 if old.phase() == phases.public:
1274 raise util.Abort(_('cannot amend public changesets'))
1274 raise util.Abort(_('cannot amend public changesets'))
1275 if len(old.parents()) > 1:
1275 if len(old.parents()) > 1:
1276 raise util.Abort(_('cannot amend merge changesets'))
1276 raise util.Abort(_('cannot amend merge changesets'))
1277 if len(repo[None].parents()) > 1:
1277 if len(repo[None].parents()) > 1:
1278 raise util.Abort(_('cannot amend while merging'))
1278 raise util.Abort(_('cannot amend while merging'))
1279 if old.children():
1279 if old.children():
1280 raise util.Abort(_('cannot amend changeset with children'))
1280 raise util.Abort(_('cannot amend changeset with children'))
1281
1281
1282 e = cmdutil.commiteditor
1282 e = cmdutil.commiteditor
1283 if opts.get('force_editor'):
1283 if opts.get('force_editor'):
1284 e = cmdutil.commitforceeditor
1284 e = cmdutil.commitforceeditor
1285
1285
1286 def commitfunc(ui, repo, message, match, opts):
1286 def commitfunc(ui, repo, message, match, opts):
1287 editor = e
1287 editor = e
1288 # message contains text from -m or -l, if it's empty,
1288 # message contains text from -m or -l, if it's empty,
1289 # open the editor with the old message
1289 # open the editor with the old message
1290 if not message:
1290 if not message:
1291 message = old.description()
1291 message = old.description()
1292 editor = cmdutil.commitforceeditor
1292 editor = cmdutil.commitforceeditor
1293 return repo.commit(message,
1293 return repo.commit(message,
1294 opts.get('user') or old.user(),
1294 opts.get('user') or old.user(),
1295 opts.get('date') or old.date(),
1295 opts.get('date') or old.date(),
1296 match,
1296 match,
1297 editor=editor,
1297 editor=editor,
1298 extra=extra)
1298 extra=extra)
1299
1299
1300 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1300 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1301 if node == old.node():
1301 if node == old.node():
1302 ui.status(_("nothing changed\n"))
1302 ui.status(_("nothing changed\n"))
1303 return 1
1303 return 1
1304 else:
1304 else:
1305 e = cmdutil.commiteditor
1305 e = cmdutil.commiteditor
1306 if opts.get('force_editor'):
1306 if opts.get('force_editor'):
1307 e = cmdutil.commitforceeditor
1307 e = cmdutil.commitforceeditor
1308
1308
1309 def commitfunc(ui, repo, message, match, opts):
1309 def commitfunc(ui, repo, message, match, opts):
1310 return repo.commit(message, opts.get('user'), opts.get('date'),
1310 return repo.commit(message, opts.get('user'), opts.get('date'),
1311 match, editor=e, extra=extra)
1311 match, editor=e, extra=extra)
1312
1312
1313 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1313 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1314
1314
1315 if not node:
1315 if not node:
1316 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1316 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1317 if stat[3]:
1317 if stat[3]:
1318 ui.status(_("nothing changed (%d missing files, see "
1318 ui.status(_("nothing changed (%d missing files, see "
1319 "'hg status')\n") % len(stat[3]))
1319 "'hg status')\n") % len(stat[3]))
1320 else:
1320 else:
1321 ui.status(_("nothing changed\n"))
1321 ui.status(_("nothing changed\n"))
1322 return 1
1322 return 1
1323
1323
1324 ctx = repo[node]
1324 ctx = repo[node]
1325 parents = ctx.parents()
1325 parents = ctx.parents()
1326
1326
1327 if (not opts.get('amend') and bheads and node not in bheads and not
1327 if (not opts.get('amend') and bheads and node not in bheads and not
1328 [x for x in parents if x.node() in bheads and x.branch() == branch]):
1328 [x for x in parents if x.node() in bheads and x.branch() == branch]):
1329 ui.status(_('created new head\n'))
1329 ui.status(_('created new head\n'))
1330 # The message is not printed for initial roots. For the other
1330 # The message is not printed for initial roots. For the other
1331 # changesets, it is printed in the following situations:
1331 # changesets, it is printed in the following situations:
1332 #
1332 #
1333 # Par column: for the 2 parents with ...
1333 # Par column: for the 2 parents with ...
1334 # N: null or no parent
1334 # N: null or no parent
1335 # B: parent is on another named branch
1335 # B: parent is on another named branch
1336 # C: parent is a regular non head changeset
1336 # C: parent is a regular non head changeset
1337 # H: parent was a branch head of the current branch
1337 # H: parent was a branch head of the current branch
1338 # Msg column: whether we print "created new head" message
1338 # Msg column: whether we print "created new head" message
1339 # In the following, it is assumed that there already exists some
1339 # In the following, it is assumed that there already exists some
1340 # initial branch heads of the current branch, otherwise nothing is
1340 # initial branch heads of the current branch, otherwise nothing is
1341 # printed anyway.
1341 # printed anyway.
1342 #
1342 #
1343 # Par Msg Comment
1343 # Par Msg Comment
1344 # NN y additional topo root
1344 # NN y additional topo root
1345 #
1345 #
1346 # BN y additional branch root
1346 # BN y additional branch root
1347 # CN y additional topo head
1347 # CN y additional topo head
1348 # HN n usual case
1348 # HN n usual case
1349 #
1349 #
1350 # BB y weird additional branch root
1350 # BB y weird additional branch root
1351 # CB y branch merge
1351 # CB y branch merge
1352 # HB n merge with named branch
1352 # HB n merge with named branch
1353 #
1353 #
1354 # CC y additional head from merge
1354 # CC y additional head from merge
1355 # CH n merge with a head
1355 # CH n merge with a head
1356 #
1356 #
1357 # HH n head merge: head count decreases
1357 # HH n head merge: head count decreases
1358
1358
1359 if not opts.get('close_branch'):
1359 if not opts.get('close_branch'):
1360 for r in parents:
1360 for r in parents:
1361 if r.closesbranch() and r.branch() == branch:
1361 if r.closesbranch() and r.branch() == branch:
1362 ui.status(_('reopening closed branch head %d\n') % r)
1362 ui.status(_('reopening closed branch head %d\n') % r)
1363
1363
1364 if ui.debugflag:
1364 if ui.debugflag:
1365 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
1365 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
1366 elif ui.verbose:
1366 elif ui.verbose:
1367 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
1367 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
1368
1368
1369 @command('copy|cp',
1369 @command('copy|cp',
1370 [('A', 'after', None, _('record a copy that has already occurred')),
1370 [('A', 'after', None, _('record a copy that has already occurred')),
1371 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1371 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1372 ] + walkopts + dryrunopts,
1372 ] + walkopts + dryrunopts,
1373 _('[OPTION]... [SOURCE]... DEST'))
1373 _('[OPTION]... [SOURCE]... DEST'))
1374 def copy(ui, repo, *pats, **opts):
1374 def copy(ui, repo, *pats, **opts):
1375 """mark files as copied for the next commit
1375 """mark files as copied for the next commit
1376
1376
1377 Mark dest as having copies of source files. If dest is a
1377 Mark dest as having copies of source files. If dest is a
1378 directory, copies are put in that directory. If dest is a file,
1378 directory, copies are put in that directory. If dest is a file,
1379 the source must be a single file.
1379 the source must be a single file.
1380
1380
1381 By default, this command copies the contents of files as they
1381 By default, this command copies the contents of files as they
1382 exist in the working directory. If invoked with -A/--after, the
1382 exist in the working directory. If invoked with -A/--after, the
1383 operation is recorded, but no copying is performed.
1383 operation is recorded, but no copying is performed.
1384
1384
1385 This command takes effect with the next commit. To undo a copy
1385 This command takes effect with the next commit. To undo a copy
1386 before that, see :hg:`revert`.
1386 before that, see :hg:`revert`.
1387
1387
1388 Returns 0 on success, 1 if errors are encountered.
1388 Returns 0 on success, 1 if errors are encountered.
1389 """
1389 """
1390 wlock = repo.wlock(False)
1390 wlock = repo.wlock(False)
1391 try:
1391 try:
1392 return cmdutil.copy(ui, repo, pats, opts)
1392 return cmdutil.copy(ui, repo, pats, opts)
1393 finally:
1393 finally:
1394 wlock.release()
1394 wlock.release()
1395
1395
1396 @command('debugancestor', [], _('[INDEX] REV1 REV2'))
1396 @command('debugancestor', [], _('[INDEX] REV1 REV2'))
1397 def debugancestor(ui, repo, *args):
1397 def debugancestor(ui, repo, *args):
1398 """find the ancestor revision of two revisions in a given index"""
1398 """find the ancestor revision of two revisions in a given index"""
1399 if len(args) == 3:
1399 if len(args) == 3:
1400 index, rev1, rev2 = args
1400 index, rev1, rev2 = args
1401 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1401 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1402 lookup = r.lookup
1402 lookup = r.lookup
1403 elif len(args) == 2:
1403 elif len(args) == 2:
1404 if not repo:
1404 if not repo:
1405 raise util.Abort(_("there is no Mercurial repository here "
1405 raise util.Abort(_("there is no Mercurial repository here "
1406 "(.hg not found)"))
1406 "(.hg not found)"))
1407 rev1, rev2 = args
1407 rev1, rev2 = args
1408 r = repo.changelog
1408 r = repo.changelog
1409 lookup = repo.lookup
1409 lookup = repo.lookup
1410 else:
1410 else:
1411 raise util.Abort(_('either two or three arguments required'))
1411 raise util.Abort(_('either two or three arguments required'))
1412 a = r.ancestor(lookup(rev1), lookup(rev2))
1412 a = r.ancestor(lookup(rev1), lookup(rev2))
1413 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1413 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1414
1414
1415 @command('debugbuilddag',
1415 @command('debugbuilddag',
1416 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1416 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1417 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1417 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1418 ('n', 'new-file', None, _('add new file at each rev'))],
1418 ('n', 'new-file', None, _('add new file at each rev'))],
1419 _('[OPTION]... [TEXT]'))
1419 _('[OPTION]... [TEXT]'))
1420 def debugbuilddag(ui, repo, text=None,
1420 def debugbuilddag(ui, repo, text=None,
1421 mergeable_file=False,
1421 mergeable_file=False,
1422 overwritten_file=False,
1422 overwritten_file=False,
1423 new_file=False):
1423 new_file=False):
1424 """builds a repo with a given DAG from scratch in the current empty repo
1424 """builds a repo with a given DAG from scratch in the current empty repo
1425
1425
1426 The description of the DAG is read from stdin if not given on the
1426 The description of the DAG is read from stdin if not given on the
1427 command line.
1427 command line.
1428
1428
1429 Elements:
1429 Elements:
1430
1430
1431 - "+n" is a linear run of n nodes based on the current default parent
1431 - "+n" is a linear run of n nodes based on the current default parent
1432 - "." is a single node based on the current default parent
1432 - "." is a single node based on the current default parent
1433 - "$" resets the default parent to null (implied at the start);
1433 - "$" resets the default parent to null (implied at the start);
1434 otherwise the default parent is always the last node created
1434 otherwise the default parent is always the last node created
1435 - "<p" sets the default parent to the backref p
1435 - "<p" sets the default parent to the backref p
1436 - "*p" is a fork at parent p, which is a backref
1436 - "*p" is a fork at parent p, which is a backref
1437 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1437 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1438 - "/p2" is a merge of the preceding node and p2
1438 - "/p2" is a merge of the preceding node and p2
1439 - ":tag" defines a local tag for the preceding node
1439 - ":tag" defines a local tag for the preceding node
1440 - "@branch" sets the named branch for subsequent nodes
1440 - "@branch" sets the named branch for subsequent nodes
1441 - "#...\\n" is a comment up to the end of the line
1441 - "#...\\n" is a comment up to the end of the line
1442
1442
1443 Whitespace between the above elements is ignored.
1443 Whitespace between the above elements is ignored.
1444
1444
1445 A backref is either
1445 A backref is either
1446
1446
1447 - a number n, which references the node curr-n, where curr is the current
1447 - a number n, which references the node curr-n, where curr is the current
1448 node, or
1448 node, or
1449 - the name of a local tag you placed earlier using ":tag", or
1449 - the name of a local tag you placed earlier using ":tag", or
1450 - empty to denote the default parent.
1450 - empty to denote the default parent.
1451
1451
1452 All string valued-elements are either strictly alphanumeric, or must
1452 All string valued-elements are either strictly alphanumeric, or must
1453 be enclosed in double quotes ("..."), with "\\" as escape character.
1453 be enclosed in double quotes ("..."), with "\\" as escape character.
1454 """
1454 """
1455
1455
1456 if text is None:
1456 if text is None:
1457 ui.status(_("reading DAG from stdin\n"))
1457 ui.status(_("reading DAG from stdin\n"))
1458 text = ui.fin.read()
1458 text = ui.fin.read()
1459
1459
1460 cl = repo.changelog
1460 cl = repo.changelog
1461 if len(cl) > 0:
1461 if len(cl) > 0:
1462 raise util.Abort(_('repository is not empty'))
1462 raise util.Abort(_('repository is not empty'))
1463
1463
1464 # determine number of revs in DAG
1464 # determine number of revs in DAG
1465 total = 0
1465 total = 0
1466 for type, data in dagparser.parsedag(text):
1466 for type, data in dagparser.parsedag(text):
1467 if type == 'n':
1467 if type == 'n':
1468 total += 1
1468 total += 1
1469
1469
1470 if mergeable_file:
1470 if mergeable_file:
1471 linesperrev = 2
1471 linesperrev = 2
1472 # make a file with k lines per rev
1472 # make a file with k lines per rev
1473 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1473 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1474 initialmergedlines.append("")
1474 initialmergedlines.append("")
1475
1475
1476 tags = []
1476 tags = []
1477
1477
1478 lock = tr = None
1478 lock = tr = None
1479 try:
1479 try:
1480 lock = repo.lock()
1480 lock = repo.lock()
1481 tr = repo.transaction("builddag")
1481 tr = repo.transaction("builddag")
1482
1482
1483 at = -1
1483 at = -1
1484 atbranch = 'default'
1484 atbranch = 'default'
1485 nodeids = []
1485 nodeids = []
1486 id = 0
1486 id = 0
1487 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1487 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1488 for type, data in dagparser.parsedag(text):
1488 for type, data in dagparser.parsedag(text):
1489 if type == 'n':
1489 if type == 'n':
1490 ui.note('node %s\n' % str(data))
1490 ui.note('node %s\n' % str(data))
1491 id, ps = data
1491 id, ps = data
1492
1492
1493 files = []
1493 files = []
1494 fctxs = {}
1494 fctxs = {}
1495
1495
1496 p2 = None
1496 p2 = None
1497 if mergeable_file:
1497 if mergeable_file:
1498 fn = "mf"
1498 fn = "mf"
1499 p1 = repo[ps[0]]
1499 p1 = repo[ps[0]]
1500 if len(ps) > 1:
1500 if len(ps) > 1:
1501 p2 = repo[ps[1]]
1501 p2 = repo[ps[1]]
1502 pa = p1.ancestor(p2)
1502 pa = p1.ancestor(p2)
1503 base, local, other = [x[fn].data() for x in pa, p1, p2]
1503 base, local, other = [x[fn].data() for x in pa, p1, p2]
1504 m3 = simplemerge.Merge3Text(base, local, other)
1504 m3 = simplemerge.Merge3Text(base, local, other)
1505 ml = [l.strip() for l in m3.merge_lines()]
1505 ml = [l.strip() for l in m3.merge_lines()]
1506 ml.append("")
1506 ml.append("")
1507 elif at > 0:
1507 elif at > 0:
1508 ml = p1[fn].data().split("\n")
1508 ml = p1[fn].data().split("\n")
1509 else:
1509 else:
1510 ml = initialmergedlines
1510 ml = initialmergedlines
1511 ml[id * linesperrev] += " r%i" % id
1511 ml[id * linesperrev] += " r%i" % id
1512 mergedtext = "\n".join(ml)
1512 mergedtext = "\n".join(ml)
1513 files.append(fn)
1513 files.append(fn)
1514 fctxs[fn] = context.memfilectx(fn, mergedtext)
1514 fctxs[fn] = context.memfilectx(fn, mergedtext)
1515
1515
1516 if overwritten_file:
1516 if overwritten_file:
1517 fn = "of"
1517 fn = "of"
1518 files.append(fn)
1518 files.append(fn)
1519 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1519 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1520
1520
1521 if new_file:
1521 if new_file:
1522 fn = "nf%i" % id
1522 fn = "nf%i" % id
1523 files.append(fn)
1523 files.append(fn)
1524 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1524 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1525 if len(ps) > 1:
1525 if len(ps) > 1:
1526 if not p2:
1526 if not p2:
1527 p2 = repo[ps[1]]
1527 p2 = repo[ps[1]]
1528 for fn in p2:
1528 for fn in p2:
1529 if fn.startswith("nf"):
1529 if fn.startswith("nf"):
1530 files.append(fn)
1530 files.append(fn)
1531 fctxs[fn] = p2[fn]
1531 fctxs[fn] = p2[fn]
1532
1532
1533 def fctxfn(repo, cx, path):
1533 def fctxfn(repo, cx, path):
1534 return fctxs.get(path)
1534 return fctxs.get(path)
1535
1535
1536 if len(ps) == 0 or ps[0] < 0:
1536 if len(ps) == 0 or ps[0] < 0:
1537 pars = [None, None]
1537 pars = [None, None]
1538 elif len(ps) == 1:
1538 elif len(ps) == 1:
1539 pars = [nodeids[ps[0]], None]
1539 pars = [nodeids[ps[0]], None]
1540 else:
1540 else:
1541 pars = [nodeids[p] for p in ps]
1541 pars = [nodeids[p] for p in ps]
1542 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1542 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1543 date=(id, 0),
1543 date=(id, 0),
1544 user="debugbuilddag",
1544 user="debugbuilddag",
1545 extra={'branch': atbranch})
1545 extra={'branch': atbranch})
1546 nodeid = repo.commitctx(cx)
1546 nodeid = repo.commitctx(cx)
1547 nodeids.append(nodeid)
1547 nodeids.append(nodeid)
1548 at = id
1548 at = id
1549 elif type == 'l':
1549 elif type == 'l':
1550 id, name = data
1550 id, name = data
1551 ui.note('tag %s\n' % name)
1551 ui.note('tag %s\n' % name)
1552 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1552 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1553 elif type == 'a':
1553 elif type == 'a':
1554 ui.note('branch %s\n' % data)
1554 ui.note('branch %s\n' % data)
1555 atbranch = data
1555 atbranch = data
1556 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1556 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1557 tr.close()
1557 tr.close()
1558
1558
1559 if tags:
1559 if tags:
1560 repo.opener.write("localtags", "".join(tags))
1560 repo.opener.write("localtags", "".join(tags))
1561 finally:
1561 finally:
1562 ui.progress(_('building'), None)
1562 ui.progress(_('building'), None)
1563 release(tr, lock)
1563 release(tr, lock)
1564
1564
1565 @command('debugbundle', [('a', 'all', None, _('show all details'))], _('FILE'))
1565 @command('debugbundle', [('a', 'all', None, _('show all details'))], _('FILE'))
1566 def debugbundle(ui, bundlepath, all=None, **opts):
1566 def debugbundle(ui, bundlepath, all=None, **opts):
1567 """lists the contents of a bundle"""
1567 """lists the contents of a bundle"""
1568 f = url.open(ui, bundlepath)
1568 f = url.open(ui, bundlepath)
1569 try:
1569 try:
1570 gen = changegroup.readbundle(f, bundlepath)
1570 gen = changegroup.readbundle(f, bundlepath)
1571 if all:
1571 if all:
1572 ui.write("format: id, p1, p2, cset, delta base, len(delta)\n")
1572 ui.write("format: id, p1, p2, cset, delta base, len(delta)\n")
1573
1573
1574 def showchunks(named):
1574 def showchunks(named):
1575 ui.write("\n%s\n" % named)
1575 ui.write("\n%s\n" % named)
1576 chain = None
1576 chain = None
1577 while True:
1577 while True:
1578 chunkdata = gen.deltachunk(chain)
1578 chunkdata = gen.deltachunk(chain)
1579 if not chunkdata:
1579 if not chunkdata:
1580 break
1580 break
1581 node = chunkdata['node']
1581 node = chunkdata['node']
1582 p1 = chunkdata['p1']
1582 p1 = chunkdata['p1']
1583 p2 = chunkdata['p2']
1583 p2 = chunkdata['p2']
1584 cs = chunkdata['cs']
1584 cs = chunkdata['cs']
1585 deltabase = chunkdata['deltabase']
1585 deltabase = chunkdata['deltabase']
1586 delta = chunkdata['delta']
1586 delta = chunkdata['delta']
1587 ui.write("%s %s %s %s %s %s\n" %
1587 ui.write("%s %s %s %s %s %s\n" %
1588 (hex(node), hex(p1), hex(p2),
1588 (hex(node), hex(p1), hex(p2),
1589 hex(cs), hex(deltabase), len(delta)))
1589 hex(cs), hex(deltabase), len(delta)))
1590 chain = node
1590 chain = node
1591
1591
1592 chunkdata = gen.changelogheader()
1592 chunkdata = gen.changelogheader()
1593 showchunks("changelog")
1593 showchunks("changelog")
1594 chunkdata = gen.manifestheader()
1594 chunkdata = gen.manifestheader()
1595 showchunks("manifest")
1595 showchunks("manifest")
1596 while True:
1596 while True:
1597 chunkdata = gen.filelogheader()
1597 chunkdata = gen.filelogheader()
1598 if not chunkdata:
1598 if not chunkdata:
1599 break
1599 break
1600 fname = chunkdata['filename']
1600 fname = chunkdata['filename']
1601 showchunks(fname)
1601 showchunks(fname)
1602 else:
1602 else:
1603 chunkdata = gen.changelogheader()
1603 chunkdata = gen.changelogheader()
1604 chain = None
1604 chain = None
1605 while True:
1605 while True:
1606 chunkdata = gen.deltachunk(chain)
1606 chunkdata = gen.deltachunk(chain)
1607 if not chunkdata:
1607 if not chunkdata:
1608 break
1608 break
1609 node = chunkdata['node']
1609 node = chunkdata['node']
1610 ui.write("%s\n" % hex(node))
1610 ui.write("%s\n" % hex(node))
1611 chain = node
1611 chain = node
1612 finally:
1612 finally:
1613 f.close()
1613 f.close()
1614
1614
1615 @command('debugcheckstate', [], '')
1615 @command('debugcheckstate', [], '')
1616 def debugcheckstate(ui, repo):
1616 def debugcheckstate(ui, repo):
1617 """validate the correctness of the current dirstate"""
1617 """validate the correctness of the current dirstate"""
1618 parent1, parent2 = repo.dirstate.parents()
1618 parent1, parent2 = repo.dirstate.parents()
1619 m1 = repo[parent1].manifest()
1619 m1 = repo[parent1].manifest()
1620 m2 = repo[parent2].manifest()
1620 m2 = repo[parent2].manifest()
1621 errors = 0
1621 errors = 0
1622 for f in repo.dirstate:
1622 for f in repo.dirstate:
1623 state = repo.dirstate[f]
1623 state = repo.dirstate[f]
1624 if state in "nr" and f not in m1:
1624 if state in "nr" and f not in m1:
1625 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1625 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1626 errors += 1
1626 errors += 1
1627 if state in "a" and f in m1:
1627 if state in "a" and f in m1:
1628 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1628 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1629 errors += 1
1629 errors += 1
1630 if state in "m" and f not in m1 and f not in m2:
1630 if state in "m" and f not in m1 and f not in m2:
1631 ui.warn(_("%s in state %s, but not in either manifest\n") %
1631 ui.warn(_("%s in state %s, but not in either manifest\n") %
1632 (f, state))
1632 (f, state))
1633 errors += 1
1633 errors += 1
1634 for f in m1:
1634 for f in m1:
1635 state = repo.dirstate[f]
1635 state = repo.dirstate[f]
1636 if state not in "nrm":
1636 if state not in "nrm":
1637 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1637 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1638 errors += 1
1638 errors += 1
1639 if errors:
1639 if errors:
1640 error = _(".hg/dirstate inconsistent with current parent's manifest")
1640 error = _(".hg/dirstate inconsistent with current parent's manifest")
1641 raise util.Abort(error)
1641 raise util.Abort(error)
1642
1642
1643 @command('debugcommands', [], _('[COMMAND]'))
1643 @command('debugcommands', [], _('[COMMAND]'))
1644 def debugcommands(ui, cmd='', *args):
1644 def debugcommands(ui, cmd='', *args):
1645 """list all available commands and options"""
1645 """list all available commands and options"""
1646 for cmd, vals in sorted(table.iteritems()):
1646 for cmd, vals in sorted(table.iteritems()):
1647 cmd = cmd.split('|')[0].strip('^')
1647 cmd = cmd.split('|')[0].strip('^')
1648 opts = ', '.join([i[1] for i in vals[1]])
1648 opts = ', '.join([i[1] for i in vals[1]])
1649 ui.write('%s: %s\n' % (cmd, opts))
1649 ui.write('%s: %s\n' % (cmd, opts))
1650
1650
1651 @command('debugcomplete',
1651 @command('debugcomplete',
1652 [('o', 'options', None, _('show the command options'))],
1652 [('o', 'options', None, _('show the command options'))],
1653 _('[-o] CMD'))
1653 _('[-o] CMD'))
1654 def debugcomplete(ui, cmd='', **opts):
1654 def debugcomplete(ui, cmd='', **opts):
1655 """returns the completion list associated with the given command"""
1655 """returns the completion list associated with the given command"""
1656
1656
1657 if opts.get('options'):
1657 if opts.get('options'):
1658 options = []
1658 options = []
1659 otables = [globalopts]
1659 otables = [globalopts]
1660 if cmd:
1660 if cmd:
1661 aliases, entry = cmdutil.findcmd(cmd, table, False)
1661 aliases, entry = cmdutil.findcmd(cmd, table, False)
1662 otables.append(entry[1])
1662 otables.append(entry[1])
1663 for t in otables:
1663 for t in otables:
1664 for o in t:
1664 for o in t:
1665 if "(DEPRECATED)" in o[3]:
1665 if "(DEPRECATED)" in o[3]:
1666 continue
1666 continue
1667 if o[0]:
1667 if o[0]:
1668 options.append('-%s' % o[0])
1668 options.append('-%s' % o[0])
1669 options.append('--%s' % o[1])
1669 options.append('--%s' % o[1])
1670 ui.write("%s\n" % "\n".join(options))
1670 ui.write("%s\n" % "\n".join(options))
1671 return
1671 return
1672
1672
1673 cmdlist = cmdutil.findpossible(cmd, table)
1673 cmdlist = cmdutil.findpossible(cmd, table)
1674 if ui.verbose:
1674 if ui.verbose:
1675 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1675 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1676 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1676 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1677
1677
1678 @command('debugdag',
1678 @command('debugdag',
1679 [('t', 'tags', None, _('use tags as labels')),
1679 [('t', 'tags', None, _('use tags as labels')),
1680 ('b', 'branches', None, _('annotate with branch names')),
1680 ('b', 'branches', None, _('annotate with branch names')),
1681 ('', 'dots', None, _('use dots for runs')),
1681 ('', 'dots', None, _('use dots for runs')),
1682 ('s', 'spaces', None, _('separate elements by spaces'))],
1682 ('s', 'spaces', None, _('separate elements by spaces'))],
1683 _('[OPTION]... [FILE [REV]...]'))
1683 _('[OPTION]... [FILE [REV]...]'))
1684 def debugdag(ui, repo, file_=None, *revs, **opts):
1684 def debugdag(ui, repo, file_=None, *revs, **opts):
1685 """format the changelog or an index DAG as a concise textual description
1685 """format the changelog or an index DAG as a concise textual description
1686
1686
1687 If you pass a revlog index, the revlog's DAG is emitted. If you list
1687 If you pass a revlog index, the revlog's DAG is emitted. If you list
1688 revision numbers, they get labelled in the output as rN.
1688 revision numbers, they get labelled in the output as rN.
1689
1689
1690 Otherwise, the changelog DAG of the current repo is emitted.
1690 Otherwise, the changelog DAG of the current repo is emitted.
1691 """
1691 """
1692 spaces = opts.get('spaces')
1692 spaces = opts.get('spaces')
1693 dots = opts.get('dots')
1693 dots = opts.get('dots')
1694 if file_:
1694 if file_:
1695 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1695 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1696 revs = set((int(r) for r in revs))
1696 revs = set((int(r) for r in revs))
1697 def events():
1697 def events():
1698 for r in rlog:
1698 for r in rlog:
1699 yield 'n', (r, list(set(p for p in rlog.parentrevs(r)
1699 yield 'n', (r, list(set(p for p in rlog.parentrevs(r)
1700 if p != -1)))
1700 if p != -1)))
1701 if r in revs:
1701 if r in revs:
1702 yield 'l', (r, "r%i" % r)
1702 yield 'l', (r, "r%i" % r)
1703 elif repo:
1703 elif repo:
1704 cl = repo.changelog
1704 cl = repo.changelog
1705 tags = opts.get('tags')
1705 tags = opts.get('tags')
1706 branches = opts.get('branches')
1706 branches = opts.get('branches')
1707 if tags:
1707 if tags:
1708 labels = {}
1708 labels = {}
1709 for l, n in repo.tags().items():
1709 for l, n in repo.tags().items():
1710 labels.setdefault(cl.rev(n), []).append(l)
1710 labels.setdefault(cl.rev(n), []).append(l)
1711 def events():
1711 def events():
1712 b = "default"
1712 b = "default"
1713 for r in cl:
1713 for r in cl:
1714 if branches:
1714 if branches:
1715 newb = cl.read(cl.node(r))[5]['branch']
1715 newb = cl.read(cl.node(r))[5]['branch']
1716 if newb != b:
1716 if newb != b:
1717 yield 'a', newb
1717 yield 'a', newb
1718 b = newb
1718 b = newb
1719 yield 'n', (r, list(set(p for p in cl.parentrevs(r)
1719 yield 'n', (r, list(set(p for p in cl.parentrevs(r)
1720 if p != -1)))
1720 if p != -1)))
1721 if tags:
1721 if tags:
1722 ls = labels.get(r)
1722 ls = labels.get(r)
1723 if ls:
1723 if ls:
1724 for l in ls:
1724 for l in ls:
1725 yield 'l', (r, l)
1725 yield 'l', (r, l)
1726 else:
1726 else:
1727 raise util.Abort(_('need repo for changelog dag'))
1727 raise util.Abort(_('need repo for changelog dag'))
1728
1728
1729 for line in dagparser.dagtextlines(events(),
1729 for line in dagparser.dagtextlines(events(),
1730 addspaces=spaces,
1730 addspaces=spaces,
1731 wraplabels=True,
1731 wraplabels=True,
1732 wrapannotations=True,
1732 wrapannotations=True,
1733 wrapnonlinear=dots,
1733 wrapnonlinear=dots,
1734 usedots=dots,
1734 usedots=dots,
1735 maxlinewidth=70):
1735 maxlinewidth=70):
1736 ui.write(line)
1736 ui.write(line)
1737 ui.write("\n")
1737 ui.write("\n")
1738
1738
1739 @command('debugdata',
1739 @command('debugdata',
1740 [('c', 'changelog', False, _('open changelog')),
1740 [('c', 'changelog', False, _('open changelog')),
1741 ('m', 'manifest', False, _('open manifest'))],
1741 ('m', 'manifest', False, _('open manifest'))],
1742 _('-c|-m|FILE REV'))
1742 _('-c|-m|FILE REV'))
1743 def debugdata(ui, repo, file_, rev = None, **opts):
1743 def debugdata(ui, repo, file_, rev = None, **opts):
1744 """dump the contents of a data file revision"""
1744 """dump the contents of a data file revision"""
1745 if opts.get('changelog') or opts.get('manifest'):
1745 if opts.get('changelog') or opts.get('manifest'):
1746 file_, rev = None, file_
1746 file_, rev = None, file_
1747 elif rev is None:
1747 elif rev is None:
1748 raise error.CommandError('debugdata', _('invalid arguments'))
1748 raise error.CommandError('debugdata', _('invalid arguments'))
1749 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
1749 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
1750 try:
1750 try:
1751 ui.write(r.revision(r.lookup(rev)))
1751 ui.write(r.revision(r.lookup(rev)))
1752 except KeyError:
1752 except KeyError:
1753 raise util.Abort(_('invalid revision identifier %s') % rev)
1753 raise util.Abort(_('invalid revision identifier %s') % rev)
1754
1754
1755 @command('debugdate',
1755 @command('debugdate',
1756 [('e', 'extended', None, _('try extended date formats'))],
1756 [('e', 'extended', None, _('try extended date formats'))],
1757 _('[-e] DATE [RANGE]'))
1757 _('[-e] DATE [RANGE]'))
1758 def debugdate(ui, date, range=None, **opts):
1758 def debugdate(ui, date, range=None, **opts):
1759 """parse and display a date"""
1759 """parse and display a date"""
1760 if opts["extended"]:
1760 if opts["extended"]:
1761 d = util.parsedate(date, util.extendeddateformats)
1761 d = util.parsedate(date, util.extendeddateformats)
1762 else:
1762 else:
1763 d = util.parsedate(date)
1763 d = util.parsedate(date)
1764 ui.write("internal: %s %s\n" % d)
1764 ui.write("internal: %s %s\n" % d)
1765 ui.write("standard: %s\n" % util.datestr(d))
1765 ui.write("standard: %s\n" % util.datestr(d))
1766 if range:
1766 if range:
1767 m = util.matchdate(range)
1767 m = util.matchdate(range)
1768 ui.write("match: %s\n" % m(d[0]))
1768 ui.write("match: %s\n" % m(d[0]))
1769
1769
1770 @command('debugdiscovery',
1770 @command('debugdiscovery',
1771 [('', 'old', None, _('use old-style discovery')),
1771 [('', 'old', None, _('use old-style discovery')),
1772 ('', 'nonheads', None,
1772 ('', 'nonheads', None,
1773 _('use old-style discovery with non-heads included')),
1773 _('use old-style discovery with non-heads included')),
1774 ] + remoteopts,
1774 ] + remoteopts,
1775 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
1775 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
1776 def debugdiscovery(ui, repo, remoteurl="default", **opts):
1776 def debugdiscovery(ui, repo, remoteurl="default", **opts):
1777 """runs the changeset discovery protocol in isolation"""
1777 """runs the changeset discovery protocol in isolation"""
1778 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl),
1778 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl),
1779 opts.get('branch'))
1779 opts.get('branch'))
1780 remote = hg.peer(repo, opts, remoteurl)
1780 remote = hg.peer(repo, opts, remoteurl)
1781 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
1781 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
1782
1782
1783 # make sure tests are repeatable
1783 # make sure tests are repeatable
1784 random.seed(12323)
1784 random.seed(12323)
1785
1785
1786 def doit(localheads, remoteheads):
1786 def doit(localheads, remoteheads):
1787 if opts.get('old'):
1787 if opts.get('old'):
1788 if localheads:
1788 if localheads:
1789 raise util.Abort('cannot use localheads with old style '
1789 raise util.Abort('cannot use localheads with old style '
1790 'discovery')
1790 'discovery')
1791 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
1791 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
1792 force=True)
1792 force=True)
1793 common = set(common)
1793 common = set(common)
1794 if not opts.get('nonheads'):
1794 if not opts.get('nonheads'):
1795 ui.write("unpruned common: %s\n" % " ".join([short(n)
1795 ui.write("unpruned common: %s\n" % " ".join([short(n)
1796 for n in common]))
1796 for n in common]))
1797 dag = dagutil.revlogdag(repo.changelog)
1797 dag = dagutil.revlogdag(repo.changelog)
1798 all = dag.ancestorset(dag.internalizeall(common))
1798 all = dag.ancestorset(dag.internalizeall(common))
1799 common = dag.externalizeall(dag.headsetofconnecteds(all))
1799 common = dag.externalizeall(dag.headsetofconnecteds(all))
1800 else:
1800 else:
1801 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
1801 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
1802 common = set(common)
1802 common = set(common)
1803 rheads = set(hds)
1803 rheads = set(hds)
1804 lheads = set(repo.heads())
1804 lheads = set(repo.heads())
1805 ui.write("common heads: %s\n" % " ".join([short(n) for n in common]))
1805 ui.write("common heads: %s\n" % " ".join([short(n) for n in common]))
1806 if lheads <= common:
1806 if lheads <= common:
1807 ui.write("local is subset\n")
1807 ui.write("local is subset\n")
1808 elif rheads <= common:
1808 elif rheads <= common:
1809 ui.write("remote is subset\n")
1809 ui.write("remote is subset\n")
1810
1810
1811 serverlogs = opts.get('serverlog')
1811 serverlogs = opts.get('serverlog')
1812 if serverlogs:
1812 if serverlogs:
1813 for filename in serverlogs:
1813 for filename in serverlogs:
1814 logfile = open(filename, 'r')
1814 logfile = open(filename, 'r')
1815 try:
1815 try:
1816 line = logfile.readline()
1816 line = logfile.readline()
1817 while line:
1817 while line:
1818 parts = line.strip().split(';')
1818 parts = line.strip().split(';')
1819 op = parts[1]
1819 op = parts[1]
1820 if op == 'cg':
1820 if op == 'cg':
1821 pass
1821 pass
1822 elif op == 'cgss':
1822 elif op == 'cgss':
1823 doit(parts[2].split(' '), parts[3].split(' '))
1823 doit(parts[2].split(' '), parts[3].split(' '))
1824 elif op == 'unb':
1824 elif op == 'unb':
1825 doit(parts[3].split(' '), parts[2].split(' '))
1825 doit(parts[3].split(' '), parts[2].split(' '))
1826 line = logfile.readline()
1826 line = logfile.readline()
1827 finally:
1827 finally:
1828 logfile.close()
1828 logfile.close()
1829
1829
1830 else:
1830 else:
1831 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
1831 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
1832 opts.get('remote_head'))
1832 opts.get('remote_head'))
1833 localrevs = opts.get('local_head')
1833 localrevs = opts.get('local_head')
1834 doit(localrevs, remoterevs)
1834 doit(localrevs, remoterevs)
1835
1835
1836 @command('debugfileset', [], ('REVSPEC'))
1836 @command('debugfileset', [], ('REVSPEC'))
1837 def debugfileset(ui, repo, expr):
1837 def debugfileset(ui, repo, expr):
1838 '''parse and apply a fileset specification'''
1838 '''parse and apply a fileset specification'''
1839 if ui.verbose:
1839 if ui.verbose:
1840 tree = fileset.parse(expr)[0]
1840 tree = fileset.parse(expr)[0]
1841 ui.note(tree, "\n")
1841 ui.note(tree, "\n")
1842
1842
1843 for f in fileset.getfileset(repo[None], expr):
1843 for f in fileset.getfileset(repo[None], expr):
1844 ui.write("%s\n" % f)
1844 ui.write("%s\n" % f)
1845
1845
1846 @command('debugfsinfo', [], _('[PATH]'))
1846 @command('debugfsinfo', [], _('[PATH]'))
1847 def debugfsinfo(ui, path = "."):
1847 def debugfsinfo(ui, path = "."):
1848 """show information detected about current filesystem"""
1848 """show information detected about current filesystem"""
1849 util.writefile('.debugfsinfo', '')
1849 util.writefile('.debugfsinfo', '')
1850 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1850 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1851 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1851 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1852 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1852 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1853 and 'yes' or 'no'))
1853 and 'yes' or 'no'))
1854 os.unlink('.debugfsinfo')
1854 os.unlink('.debugfsinfo')
1855
1855
1856 @command('debuggetbundle',
1856 @command('debuggetbundle',
1857 [('H', 'head', [], _('id of head node'), _('ID')),
1857 [('H', 'head', [], _('id of head node'), _('ID')),
1858 ('C', 'common', [], _('id of common node'), _('ID')),
1858 ('C', 'common', [], _('id of common node'), _('ID')),
1859 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
1859 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
1860 _('REPO FILE [-H|-C ID]...'))
1860 _('REPO FILE [-H|-C ID]...'))
1861 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
1861 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
1862 """retrieves a bundle from a repo
1862 """retrieves a bundle from a repo
1863
1863
1864 Every ID must be a full-length hex node id string. Saves the bundle to the
1864 Every ID must be a full-length hex node id string. Saves the bundle to the
1865 given file.
1865 given file.
1866 """
1866 """
1867 repo = hg.peer(ui, opts, repopath)
1867 repo = hg.peer(ui, opts, repopath)
1868 if not repo.capable('getbundle'):
1868 if not repo.capable('getbundle'):
1869 raise util.Abort("getbundle() not supported by target repository")
1869 raise util.Abort("getbundle() not supported by target repository")
1870 args = {}
1870 args = {}
1871 if common:
1871 if common:
1872 args['common'] = [bin(s) for s in common]
1872 args['common'] = [bin(s) for s in common]
1873 if head:
1873 if head:
1874 args['heads'] = [bin(s) for s in head]
1874 args['heads'] = [bin(s) for s in head]
1875 bundle = repo.getbundle('debug', **args)
1875 bundle = repo.getbundle('debug', **args)
1876
1876
1877 bundletype = opts.get('type', 'bzip2').lower()
1877 bundletype = opts.get('type', 'bzip2').lower()
1878 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1878 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1879 bundletype = btypes.get(bundletype)
1879 bundletype = btypes.get(bundletype)
1880 if bundletype not in changegroup.bundletypes:
1880 if bundletype not in changegroup.bundletypes:
1881 raise util.Abort(_('unknown bundle type specified with --type'))
1881 raise util.Abort(_('unknown bundle type specified with --type'))
1882 changegroup.writebundle(bundle, bundlepath, bundletype)
1882 changegroup.writebundle(bundle, bundlepath, bundletype)
1883
1883
1884 @command('debugignore', [], '')
1884 @command('debugignore', [], '')
1885 def debugignore(ui, repo, *values, **opts):
1885 def debugignore(ui, repo, *values, **opts):
1886 """display the combined ignore pattern"""
1886 """display the combined ignore pattern"""
1887 ignore = repo.dirstate._ignore
1887 ignore = repo.dirstate._ignore
1888 includepat = getattr(ignore, 'includepat', None)
1888 includepat = getattr(ignore, 'includepat', None)
1889 if includepat is not None:
1889 if includepat is not None:
1890 ui.write("%s\n" % includepat)
1890 ui.write("%s\n" % includepat)
1891 else:
1891 else:
1892 raise util.Abort(_("no ignore patterns found"))
1892 raise util.Abort(_("no ignore patterns found"))
1893
1893
1894 @command('debugindex',
1894 @command('debugindex',
1895 [('c', 'changelog', False, _('open changelog')),
1895 [('c', 'changelog', False, _('open changelog')),
1896 ('m', 'manifest', False, _('open manifest')),
1896 ('m', 'manifest', False, _('open manifest')),
1897 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
1897 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
1898 _('[-f FORMAT] -c|-m|FILE'))
1898 _('[-f FORMAT] -c|-m|FILE'))
1899 def debugindex(ui, repo, file_ = None, **opts):
1899 def debugindex(ui, repo, file_ = None, **opts):
1900 """dump the contents of an index file"""
1900 """dump the contents of an index file"""
1901 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
1901 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
1902 format = opts.get('format', 0)
1902 format = opts.get('format', 0)
1903 if format not in (0, 1):
1903 if format not in (0, 1):
1904 raise util.Abort(_("unknown format %d") % format)
1904 raise util.Abort(_("unknown format %d") % format)
1905
1905
1906 generaldelta = r.version & revlog.REVLOGGENERALDELTA
1906 generaldelta = r.version & revlog.REVLOGGENERALDELTA
1907 if generaldelta:
1907 if generaldelta:
1908 basehdr = ' delta'
1908 basehdr = ' delta'
1909 else:
1909 else:
1910 basehdr = ' base'
1910 basehdr = ' base'
1911
1911
1912 if format == 0:
1912 if format == 0:
1913 ui.write(" rev offset length " + basehdr + " linkrev"
1913 ui.write(" rev offset length " + basehdr + " linkrev"
1914 " nodeid p1 p2\n")
1914 " nodeid p1 p2\n")
1915 elif format == 1:
1915 elif format == 1:
1916 ui.write(" rev flag offset length"
1916 ui.write(" rev flag offset length"
1917 " size " + basehdr + " link p1 p2"
1917 " size " + basehdr + " link p1 p2"
1918 " nodeid\n")
1918 " nodeid\n")
1919
1919
1920 for i in r:
1920 for i in r:
1921 node = r.node(i)
1921 node = r.node(i)
1922 if generaldelta:
1922 if generaldelta:
1923 base = r.deltaparent(i)
1923 base = r.deltaparent(i)
1924 else:
1924 else:
1925 base = r.chainbase(i)
1925 base = r.chainbase(i)
1926 if format == 0:
1926 if format == 0:
1927 try:
1927 try:
1928 pp = r.parents(node)
1928 pp = r.parents(node)
1929 except Exception:
1929 except Exception:
1930 pp = [nullid, nullid]
1930 pp = [nullid, nullid]
1931 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1931 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1932 i, r.start(i), r.length(i), base, r.linkrev(i),
1932 i, r.start(i), r.length(i), base, r.linkrev(i),
1933 short(node), short(pp[0]), short(pp[1])))
1933 short(node), short(pp[0]), short(pp[1])))
1934 elif format == 1:
1934 elif format == 1:
1935 pr = r.parentrevs(i)
1935 pr = r.parentrevs(i)
1936 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1936 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1937 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1937 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1938 base, r.linkrev(i), pr[0], pr[1], short(node)))
1938 base, r.linkrev(i), pr[0], pr[1], short(node)))
1939
1939
1940 @command('debugindexdot', [], _('FILE'))
1940 @command('debugindexdot', [], _('FILE'))
1941 def debugindexdot(ui, repo, file_):
1941 def debugindexdot(ui, repo, file_):
1942 """dump an index DAG as a graphviz dot file"""
1942 """dump an index DAG as a graphviz dot file"""
1943 r = None
1943 r = None
1944 if repo:
1944 if repo:
1945 filelog = repo.file(file_)
1945 filelog = repo.file(file_)
1946 if len(filelog):
1946 if len(filelog):
1947 r = filelog
1947 r = filelog
1948 if not r:
1948 if not r:
1949 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1949 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1950 ui.write("digraph G {\n")
1950 ui.write("digraph G {\n")
1951 for i in r:
1951 for i in r:
1952 node = r.node(i)
1952 node = r.node(i)
1953 pp = r.parents(node)
1953 pp = r.parents(node)
1954 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1954 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1955 if pp[1] != nullid:
1955 if pp[1] != nullid:
1956 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1956 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1957 ui.write("}\n")
1957 ui.write("}\n")
1958
1958
1959 @command('debuginstall', [], '')
1959 @command('debuginstall', [], '')
1960 def debuginstall(ui):
1960 def debuginstall(ui):
1961 '''test Mercurial installation
1961 '''test Mercurial installation
1962
1962
1963 Returns 0 on success.
1963 Returns 0 on success.
1964 '''
1964 '''
1965
1965
1966 def writetemp(contents):
1966 def writetemp(contents):
1967 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1967 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1968 f = os.fdopen(fd, "wb")
1968 f = os.fdopen(fd, "wb")
1969 f.write(contents)
1969 f.write(contents)
1970 f.close()
1970 f.close()
1971 return name
1971 return name
1972
1972
1973 problems = 0
1973 problems = 0
1974
1974
1975 # encoding
1975 # encoding
1976 ui.status(_("checking encoding (%s)...\n") % encoding.encoding)
1976 ui.status(_("checking encoding (%s)...\n") % encoding.encoding)
1977 try:
1977 try:
1978 encoding.fromlocal("test")
1978 encoding.fromlocal("test")
1979 except util.Abort, inst:
1979 except util.Abort, inst:
1980 ui.write(" %s\n" % inst)
1980 ui.write(" %s\n" % inst)
1981 ui.write(_(" (check that your locale is properly set)\n"))
1981 ui.write(_(" (check that your locale is properly set)\n"))
1982 problems += 1
1982 problems += 1
1983
1983
1984 # compiled modules
1984 # compiled modules
1985 ui.status(_("checking installed modules (%s)...\n")
1985 ui.status(_("checking installed modules (%s)...\n")
1986 % os.path.dirname(__file__))
1986 % os.path.dirname(__file__))
1987 try:
1987 try:
1988 import bdiff, mpatch, base85, osutil
1988 import bdiff, mpatch, base85, osutil
1989 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
1989 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
1990 except Exception, inst:
1990 except Exception, inst:
1991 ui.write(" %s\n" % inst)
1991 ui.write(" %s\n" % inst)
1992 ui.write(_(" One or more extensions could not be found"))
1992 ui.write(_(" One or more extensions could not be found"))
1993 ui.write(_(" (check that you compiled the extensions)\n"))
1993 ui.write(_(" (check that you compiled the extensions)\n"))
1994 problems += 1
1994 problems += 1
1995
1995
1996 # templates
1996 # templates
1997 import templater
1997 import templater
1998 p = templater.templatepath()
1998 p = templater.templatepath()
1999 ui.status(_("checking templates (%s)...\n") % ' '.join(p))
1999 ui.status(_("checking templates (%s)...\n") % ' '.join(p))
2000 try:
2000 try:
2001 templater.templater(templater.templatepath("map-cmdline.default"))
2001 templater.templater(templater.templatepath("map-cmdline.default"))
2002 except Exception, inst:
2002 except Exception, inst:
2003 ui.write(" %s\n" % inst)
2003 ui.write(" %s\n" % inst)
2004 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
2004 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
2005 problems += 1
2005 problems += 1
2006
2006
2007 # editor
2007 # editor
2008 ui.status(_("checking commit editor...\n"))
2008 ui.status(_("checking commit editor...\n"))
2009 editor = ui.geteditor()
2009 editor = ui.geteditor()
2010 cmdpath = util.findexe(editor) or util.findexe(editor.split()[0])
2010 cmdpath = util.findexe(editor) or util.findexe(editor.split()[0])
2011 if not cmdpath:
2011 if not cmdpath:
2012 if editor == 'vi':
2012 if editor == 'vi':
2013 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
2013 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
2014 ui.write(_(" (specify a commit editor in your configuration"
2014 ui.write(_(" (specify a commit editor in your configuration"
2015 " file)\n"))
2015 " file)\n"))
2016 else:
2016 else:
2017 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
2017 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
2018 ui.write(_(" (specify a commit editor in your configuration"
2018 ui.write(_(" (specify a commit editor in your configuration"
2019 " file)\n"))
2019 " file)\n"))
2020 problems += 1
2020 problems += 1
2021
2021
2022 # check username
2022 # check username
2023 ui.status(_("checking username...\n"))
2023 ui.status(_("checking username...\n"))
2024 try:
2024 try:
2025 ui.username()
2025 ui.username()
2026 except util.Abort, e:
2026 except util.Abort, e:
2027 ui.write(" %s\n" % e)
2027 ui.write(" %s\n" % e)
2028 ui.write(_(" (specify a username in your configuration file)\n"))
2028 ui.write(_(" (specify a username in your configuration file)\n"))
2029 problems += 1
2029 problems += 1
2030
2030
2031 if not problems:
2031 if not problems:
2032 ui.status(_("no problems detected\n"))
2032 ui.status(_("no problems detected\n"))
2033 else:
2033 else:
2034 ui.write(_("%s problems detected,"
2034 ui.write(_("%s problems detected,"
2035 " please check your install!\n") % problems)
2035 " please check your install!\n") % problems)
2036
2036
2037 return problems
2037 return problems
2038
2038
2039 @command('debugknown', [], _('REPO ID...'))
2039 @command('debugknown', [], _('REPO ID...'))
2040 def debugknown(ui, repopath, *ids, **opts):
2040 def debugknown(ui, repopath, *ids, **opts):
2041 """test whether node ids are known to a repo
2041 """test whether node ids are known to a repo
2042
2042
2043 Every ID must be a full-length hex node id string. Returns a list of 0s
2043 Every ID must be a full-length hex node id string. Returns a list of 0s
2044 and 1s indicating unknown/known.
2044 and 1s indicating unknown/known.
2045 """
2045 """
2046 repo = hg.peer(ui, opts, repopath)
2046 repo = hg.peer(ui, opts, repopath)
2047 if not repo.capable('known'):
2047 if not repo.capable('known'):
2048 raise util.Abort("known() not supported by target repository")
2048 raise util.Abort("known() not supported by target repository")
2049 flags = repo.known([bin(s) for s in ids])
2049 flags = repo.known([bin(s) for s in ids])
2050 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
2050 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
2051
2051
2052 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'))
2052 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'))
2053 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
2053 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
2054 '''access the pushkey key/value protocol
2054 '''access the pushkey key/value protocol
2055
2055
2056 With two args, list the keys in the given namespace.
2056 With two args, list the keys in the given namespace.
2057
2057
2058 With five args, set a key to new if it currently is set to old.
2058 With five args, set a key to new if it currently is set to old.
2059 Reports success or failure.
2059 Reports success or failure.
2060 '''
2060 '''
2061
2061
2062 target = hg.peer(ui, {}, repopath)
2062 target = hg.peer(ui, {}, repopath)
2063 if keyinfo:
2063 if keyinfo:
2064 key, old, new = keyinfo
2064 key, old, new = keyinfo
2065 r = target.pushkey(namespace, key, old, new)
2065 r = target.pushkey(namespace, key, old, new)
2066 ui.status(str(r) + '\n')
2066 ui.status(str(r) + '\n')
2067 return not r
2067 return not r
2068 else:
2068 else:
2069 for k, v in target.listkeys(namespace).iteritems():
2069 for k, v in target.listkeys(namespace).iteritems():
2070 ui.write("%s\t%s\n" % (k.encode('string-escape'),
2070 ui.write("%s\t%s\n" % (k.encode('string-escape'),
2071 v.encode('string-escape')))
2071 v.encode('string-escape')))
2072
2072
2073 @command('debugpvec', [], _('A B'))
2073 @command('debugpvec', [], _('A B'))
2074 def debugpvec(ui, repo, a, b=None):
2074 def debugpvec(ui, repo, a, b=None):
2075 ca = scmutil.revsingle(repo, a)
2075 ca = scmutil.revsingle(repo, a)
2076 cb = scmutil.revsingle(repo, b)
2076 cb = scmutil.revsingle(repo, b)
2077 pa = pvec.ctxpvec(ca)
2077 pa = pvec.ctxpvec(ca)
2078 pb = pvec.ctxpvec(cb)
2078 pb = pvec.ctxpvec(cb)
2079 if pa == pb:
2079 if pa == pb:
2080 rel = "="
2080 rel = "="
2081 elif pa > pb:
2081 elif pa > pb:
2082 rel = ">"
2082 rel = ">"
2083 elif pa < pb:
2083 elif pa < pb:
2084 rel = "<"
2084 rel = "<"
2085 elif pa | pb:
2085 elif pa | pb:
2086 rel = "|"
2086 rel = "|"
2087 ui.write(_("a: %s\n") % pa)
2087 ui.write(_("a: %s\n") % pa)
2088 ui.write(_("b: %s\n") % pb)
2088 ui.write(_("b: %s\n") % pb)
2089 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
2089 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
2090 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
2090 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
2091 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
2091 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
2092 pa.distance(pb), rel))
2092 pa.distance(pb), rel))
2093
2093
2094 @command('debugrebuildstate',
2094 @command('debugrebuildstate',
2095 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
2095 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
2096 _('[-r REV] [REV]'))
2096 _('[-r REV] [REV]'))
2097 def debugrebuildstate(ui, repo, rev="tip"):
2097 def debugrebuildstate(ui, repo, rev="tip"):
2098 """rebuild the dirstate as it would look like for the given revision"""
2098 """rebuild the dirstate as it would look like for the given revision"""
2099 ctx = scmutil.revsingle(repo, rev)
2099 ctx = scmutil.revsingle(repo, rev)
2100 wlock = repo.wlock()
2100 wlock = repo.wlock()
2101 try:
2101 try:
2102 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
2102 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
2103 finally:
2103 finally:
2104 wlock.release()
2104 wlock.release()
2105
2105
2106 @command('debugrename',
2106 @command('debugrename',
2107 [('r', 'rev', '', _('revision to debug'), _('REV'))],
2107 [('r', 'rev', '', _('revision to debug'), _('REV'))],
2108 _('[-r REV] FILE'))
2108 _('[-r REV] FILE'))
2109 def debugrename(ui, repo, file1, *pats, **opts):
2109 def debugrename(ui, repo, file1, *pats, **opts):
2110 """dump rename information"""
2110 """dump rename information"""
2111
2111
2112 ctx = scmutil.revsingle(repo, opts.get('rev'))
2112 ctx = scmutil.revsingle(repo, opts.get('rev'))
2113 m = scmutil.match(ctx, (file1,) + pats, opts)
2113 m = scmutil.match(ctx, (file1,) + pats, opts)
2114 for abs in ctx.walk(m):
2114 for abs in ctx.walk(m):
2115 fctx = ctx[abs]
2115 fctx = ctx[abs]
2116 o = fctx.filelog().renamed(fctx.filenode())
2116 o = fctx.filelog().renamed(fctx.filenode())
2117 rel = m.rel(abs)
2117 rel = m.rel(abs)
2118 if o:
2118 if o:
2119 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2119 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2120 else:
2120 else:
2121 ui.write(_("%s not renamed\n") % rel)
2121 ui.write(_("%s not renamed\n") % rel)
2122
2122
2123 @command('debugrevlog',
2123 @command('debugrevlog',
2124 [('c', 'changelog', False, _('open changelog')),
2124 [('c', 'changelog', False, _('open changelog')),
2125 ('m', 'manifest', False, _('open manifest')),
2125 ('m', 'manifest', False, _('open manifest')),
2126 ('d', 'dump', False, _('dump index data'))],
2126 ('d', 'dump', False, _('dump index data'))],
2127 _('-c|-m|FILE'))
2127 _('-c|-m|FILE'))
2128 def debugrevlog(ui, repo, file_ = None, **opts):
2128 def debugrevlog(ui, repo, file_ = None, **opts):
2129 """show data and statistics about a revlog"""
2129 """show data and statistics about a revlog"""
2130 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
2130 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
2131
2131
2132 if opts.get("dump"):
2132 if opts.get("dump"):
2133 numrevs = len(r)
2133 numrevs = len(r)
2134 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
2134 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
2135 " rawsize totalsize compression heads\n")
2135 " rawsize totalsize compression heads\n")
2136 ts = 0
2136 ts = 0
2137 heads = set()
2137 heads = set()
2138 for rev in xrange(numrevs):
2138 for rev in xrange(numrevs):
2139 dbase = r.deltaparent(rev)
2139 dbase = r.deltaparent(rev)
2140 if dbase == -1:
2140 if dbase == -1:
2141 dbase = rev
2141 dbase = rev
2142 cbase = r.chainbase(rev)
2142 cbase = r.chainbase(rev)
2143 p1, p2 = r.parentrevs(rev)
2143 p1, p2 = r.parentrevs(rev)
2144 rs = r.rawsize(rev)
2144 rs = r.rawsize(rev)
2145 ts = ts + rs
2145 ts = ts + rs
2146 heads -= set(r.parentrevs(rev))
2146 heads -= set(r.parentrevs(rev))
2147 heads.add(rev)
2147 heads.add(rev)
2148 ui.write("%d %d %d %d %d %d %d %d %d %d %d %d %d\n" %
2148 ui.write("%d %d %d %d %d %d %d %d %d %d %d %d %d\n" %
2149 (rev, p1, p2, r.start(rev), r.end(rev),
2149 (rev, p1, p2, r.start(rev), r.end(rev),
2150 r.start(dbase), r.start(cbase),
2150 r.start(dbase), r.start(cbase),
2151 r.start(p1), r.start(p2),
2151 r.start(p1), r.start(p2),
2152 rs, ts, ts / r.end(rev), len(heads)))
2152 rs, ts, ts / r.end(rev), len(heads)))
2153 return 0
2153 return 0
2154
2154
2155 v = r.version
2155 v = r.version
2156 format = v & 0xFFFF
2156 format = v & 0xFFFF
2157 flags = []
2157 flags = []
2158 gdelta = False
2158 gdelta = False
2159 if v & revlog.REVLOGNGINLINEDATA:
2159 if v & revlog.REVLOGNGINLINEDATA:
2160 flags.append('inline')
2160 flags.append('inline')
2161 if v & revlog.REVLOGGENERALDELTA:
2161 if v & revlog.REVLOGGENERALDELTA:
2162 gdelta = True
2162 gdelta = True
2163 flags.append('generaldelta')
2163 flags.append('generaldelta')
2164 if not flags:
2164 if not flags:
2165 flags = ['(none)']
2165 flags = ['(none)']
2166
2166
2167 nummerges = 0
2167 nummerges = 0
2168 numfull = 0
2168 numfull = 0
2169 numprev = 0
2169 numprev = 0
2170 nump1 = 0
2170 nump1 = 0
2171 nump2 = 0
2171 nump2 = 0
2172 numother = 0
2172 numother = 0
2173 nump1prev = 0
2173 nump1prev = 0
2174 nump2prev = 0
2174 nump2prev = 0
2175 chainlengths = []
2175 chainlengths = []
2176
2176
2177 datasize = [None, 0, 0L]
2177 datasize = [None, 0, 0L]
2178 fullsize = [None, 0, 0L]
2178 fullsize = [None, 0, 0L]
2179 deltasize = [None, 0, 0L]
2179 deltasize = [None, 0, 0L]
2180
2180
2181 def addsize(size, l):
2181 def addsize(size, l):
2182 if l[0] is None or size < l[0]:
2182 if l[0] is None or size < l[0]:
2183 l[0] = size
2183 l[0] = size
2184 if size > l[1]:
2184 if size > l[1]:
2185 l[1] = size
2185 l[1] = size
2186 l[2] += size
2186 l[2] += size
2187
2187
2188 numrevs = len(r)
2188 numrevs = len(r)
2189 for rev in xrange(numrevs):
2189 for rev in xrange(numrevs):
2190 p1, p2 = r.parentrevs(rev)
2190 p1, p2 = r.parentrevs(rev)
2191 delta = r.deltaparent(rev)
2191 delta = r.deltaparent(rev)
2192 if format > 0:
2192 if format > 0:
2193 addsize(r.rawsize(rev), datasize)
2193 addsize(r.rawsize(rev), datasize)
2194 if p2 != nullrev:
2194 if p2 != nullrev:
2195 nummerges += 1
2195 nummerges += 1
2196 size = r.length(rev)
2196 size = r.length(rev)
2197 if delta == nullrev:
2197 if delta == nullrev:
2198 chainlengths.append(0)
2198 chainlengths.append(0)
2199 numfull += 1
2199 numfull += 1
2200 addsize(size, fullsize)
2200 addsize(size, fullsize)
2201 else:
2201 else:
2202 chainlengths.append(chainlengths[delta] + 1)
2202 chainlengths.append(chainlengths[delta] + 1)
2203 addsize(size, deltasize)
2203 addsize(size, deltasize)
2204 if delta == rev - 1:
2204 if delta == rev - 1:
2205 numprev += 1
2205 numprev += 1
2206 if delta == p1:
2206 if delta == p1:
2207 nump1prev += 1
2207 nump1prev += 1
2208 elif delta == p2:
2208 elif delta == p2:
2209 nump2prev += 1
2209 nump2prev += 1
2210 elif delta == p1:
2210 elif delta == p1:
2211 nump1 += 1
2211 nump1 += 1
2212 elif delta == p2:
2212 elif delta == p2:
2213 nump2 += 1
2213 nump2 += 1
2214 elif delta != nullrev:
2214 elif delta != nullrev:
2215 numother += 1
2215 numother += 1
2216
2216
2217 numdeltas = numrevs - numfull
2217 numdeltas = numrevs - numfull
2218 numoprev = numprev - nump1prev - nump2prev
2218 numoprev = numprev - nump1prev - nump2prev
2219 totalrawsize = datasize[2]
2219 totalrawsize = datasize[2]
2220 datasize[2] /= numrevs
2220 datasize[2] /= numrevs
2221 fulltotal = fullsize[2]
2221 fulltotal = fullsize[2]
2222 fullsize[2] /= numfull
2222 fullsize[2] /= numfull
2223 deltatotal = deltasize[2]
2223 deltatotal = deltasize[2]
2224 deltasize[2] /= numrevs - numfull
2224 deltasize[2] /= numrevs - numfull
2225 totalsize = fulltotal + deltatotal
2225 totalsize = fulltotal + deltatotal
2226 avgchainlen = sum(chainlengths) / numrevs
2226 avgchainlen = sum(chainlengths) / numrevs
2227 compratio = totalrawsize / totalsize
2227 compratio = totalrawsize / totalsize
2228
2228
2229 basedfmtstr = '%%%dd\n'
2229 basedfmtstr = '%%%dd\n'
2230 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2230 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2231
2231
2232 def dfmtstr(max):
2232 def dfmtstr(max):
2233 return basedfmtstr % len(str(max))
2233 return basedfmtstr % len(str(max))
2234 def pcfmtstr(max, padding=0):
2234 def pcfmtstr(max, padding=0):
2235 return basepcfmtstr % (len(str(max)), ' ' * padding)
2235 return basepcfmtstr % (len(str(max)), ' ' * padding)
2236
2236
2237 def pcfmt(value, total):
2237 def pcfmt(value, total):
2238 return (value, 100 * float(value) / total)
2238 return (value, 100 * float(value) / total)
2239
2239
2240 ui.write('format : %d\n' % format)
2240 ui.write('format : %d\n' % format)
2241 ui.write('flags : %s\n' % ', '.join(flags))
2241 ui.write('flags : %s\n' % ', '.join(flags))
2242
2242
2243 ui.write('\n')
2243 ui.write('\n')
2244 fmt = pcfmtstr(totalsize)
2244 fmt = pcfmtstr(totalsize)
2245 fmt2 = dfmtstr(totalsize)
2245 fmt2 = dfmtstr(totalsize)
2246 ui.write('revisions : ' + fmt2 % numrevs)
2246 ui.write('revisions : ' + fmt2 % numrevs)
2247 ui.write(' merges : ' + fmt % pcfmt(nummerges, numrevs))
2247 ui.write(' merges : ' + fmt % pcfmt(nummerges, numrevs))
2248 ui.write(' normal : ' + fmt % pcfmt(numrevs - nummerges, numrevs))
2248 ui.write(' normal : ' + fmt % pcfmt(numrevs - nummerges, numrevs))
2249 ui.write('revisions : ' + fmt2 % numrevs)
2249 ui.write('revisions : ' + fmt2 % numrevs)
2250 ui.write(' full : ' + fmt % pcfmt(numfull, numrevs))
2250 ui.write(' full : ' + fmt % pcfmt(numfull, numrevs))
2251 ui.write(' deltas : ' + fmt % pcfmt(numdeltas, numrevs))
2251 ui.write(' deltas : ' + fmt % pcfmt(numdeltas, numrevs))
2252 ui.write('revision size : ' + fmt2 % totalsize)
2252 ui.write('revision size : ' + fmt2 % totalsize)
2253 ui.write(' full : ' + fmt % pcfmt(fulltotal, totalsize))
2253 ui.write(' full : ' + fmt % pcfmt(fulltotal, totalsize))
2254 ui.write(' deltas : ' + fmt % pcfmt(deltatotal, totalsize))
2254 ui.write(' deltas : ' + fmt % pcfmt(deltatotal, totalsize))
2255
2255
2256 ui.write('\n')
2256 ui.write('\n')
2257 fmt = dfmtstr(max(avgchainlen, compratio))
2257 fmt = dfmtstr(max(avgchainlen, compratio))
2258 ui.write('avg chain length : ' + fmt % avgchainlen)
2258 ui.write('avg chain length : ' + fmt % avgchainlen)
2259 ui.write('compression ratio : ' + fmt % compratio)
2259 ui.write('compression ratio : ' + fmt % compratio)
2260
2260
2261 if format > 0:
2261 if format > 0:
2262 ui.write('\n')
2262 ui.write('\n')
2263 ui.write('uncompressed data size (min/max/avg) : %d / %d / %d\n'
2263 ui.write('uncompressed data size (min/max/avg) : %d / %d / %d\n'
2264 % tuple(datasize))
2264 % tuple(datasize))
2265 ui.write('full revision size (min/max/avg) : %d / %d / %d\n'
2265 ui.write('full revision size (min/max/avg) : %d / %d / %d\n'
2266 % tuple(fullsize))
2266 % tuple(fullsize))
2267 ui.write('delta size (min/max/avg) : %d / %d / %d\n'
2267 ui.write('delta size (min/max/avg) : %d / %d / %d\n'
2268 % tuple(deltasize))
2268 % tuple(deltasize))
2269
2269
2270 if numdeltas > 0:
2270 if numdeltas > 0:
2271 ui.write('\n')
2271 ui.write('\n')
2272 fmt = pcfmtstr(numdeltas)
2272 fmt = pcfmtstr(numdeltas)
2273 fmt2 = pcfmtstr(numdeltas, 4)
2273 fmt2 = pcfmtstr(numdeltas, 4)
2274 ui.write('deltas against prev : ' + fmt % pcfmt(numprev, numdeltas))
2274 ui.write('deltas against prev : ' + fmt % pcfmt(numprev, numdeltas))
2275 if numprev > 0:
2275 if numprev > 0:
2276 ui.write(' where prev = p1 : ' + fmt2 % pcfmt(nump1prev,
2276 ui.write(' where prev = p1 : ' + fmt2 % pcfmt(nump1prev,
2277 numprev))
2277 numprev))
2278 ui.write(' where prev = p2 : ' + fmt2 % pcfmt(nump2prev,
2278 ui.write(' where prev = p2 : ' + fmt2 % pcfmt(nump2prev,
2279 numprev))
2279 numprev))
2280 ui.write(' other : ' + fmt2 % pcfmt(numoprev,
2280 ui.write(' other : ' + fmt2 % pcfmt(numoprev,
2281 numprev))
2281 numprev))
2282 if gdelta:
2282 if gdelta:
2283 ui.write('deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas))
2283 ui.write('deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas))
2284 ui.write('deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas))
2284 ui.write('deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas))
2285 ui.write('deltas against other : ' + fmt % pcfmt(numother,
2285 ui.write('deltas against other : ' + fmt % pcfmt(numother,
2286 numdeltas))
2286 numdeltas))
2287
2287
2288 @command('debugrevspec', [], ('REVSPEC'))
2288 @command('debugrevspec', [], ('REVSPEC'))
2289 def debugrevspec(ui, repo, expr):
2289 def debugrevspec(ui, repo, expr):
2290 """parse and apply a revision specification
2290 """parse and apply a revision specification
2291
2291
2292 Use --verbose to print the parsed tree before and after aliases
2292 Use --verbose to print the parsed tree before and after aliases
2293 expansion.
2293 expansion.
2294 """
2294 """
2295 if ui.verbose:
2295 if ui.verbose:
2296 tree = revset.parse(expr)[0]
2296 tree = revset.parse(expr)[0]
2297 ui.note(revset.prettyformat(tree), "\n")
2297 ui.note(revset.prettyformat(tree), "\n")
2298 newtree = revset.findaliases(ui, tree)
2298 newtree = revset.findaliases(ui, tree)
2299 if newtree != tree:
2299 if newtree != tree:
2300 ui.note(revset.prettyformat(newtree), "\n")
2300 ui.note(revset.prettyformat(newtree), "\n")
2301 func = revset.match(ui, expr)
2301 func = revset.match(ui, expr)
2302 for c in func(repo, range(len(repo))):
2302 for c in func(repo, range(len(repo))):
2303 ui.write("%s\n" % c)
2303 ui.write("%s\n" % c)
2304
2304
2305 @command('debugsetparents', [], _('REV1 [REV2]'))
2305 @command('debugsetparents', [], _('REV1 [REV2]'))
2306 def debugsetparents(ui, repo, rev1, rev2=None):
2306 def debugsetparents(ui, repo, rev1, rev2=None):
2307 """manually set the parents of the current working directory
2307 """manually set the parents of the current working directory
2308
2308
2309 This is useful for writing repository conversion tools, but should
2309 This is useful for writing repository conversion tools, but should
2310 be used with care.
2310 be used with care.
2311
2311
2312 Returns 0 on success.
2312 Returns 0 on success.
2313 """
2313 """
2314
2314
2315 r1 = scmutil.revsingle(repo, rev1).node()
2315 r1 = scmutil.revsingle(repo, rev1).node()
2316 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2316 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2317
2317
2318 wlock = repo.wlock()
2318 wlock = repo.wlock()
2319 try:
2319 try:
2320 repo.setparents(r1, r2)
2320 repo.setparents(r1, r2)
2321 finally:
2321 finally:
2322 wlock.release()
2322 wlock.release()
2323
2323
2324 @command('debugstate',
2324 @command('debugstate',
2325 [('', 'nodates', None, _('do not display the saved mtime')),
2325 [('', 'nodates', None, _('do not display the saved mtime')),
2326 ('', 'datesort', None, _('sort by saved mtime'))],
2326 ('', 'datesort', None, _('sort by saved mtime'))],
2327 _('[OPTION]...'))
2327 _('[OPTION]...'))
2328 def debugstate(ui, repo, nodates=None, datesort=None):
2328 def debugstate(ui, repo, nodates=None, datesort=None):
2329 """show the contents of the current dirstate"""
2329 """show the contents of the current dirstate"""
2330 timestr = ""
2330 timestr = ""
2331 showdate = not nodates
2331 showdate = not nodates
2332 if datesort:
2332 if datesort:
2333 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2333 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2334 else:
2334 else:
2335 keyfunc = None # sort by filename
2335 keyfunc = None # sort by filename
2336 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2336 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2337 if showdate:
2337 if showdate:
2338 if ent[3] == -1:
2338 if ent[3] == -1:
2339 # Pad or slice to locale representation
2339 # Pad or slice to locale representation
2340 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
2340 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
2341 time.localtime(0)))
2341 time.localtime(0)))
2342 timestr = 'unset'
2342 timestr = 'unset'
2343 timestr = (timestr[:locale_len] +
2343 timestr = (timestr[:locale_len] +
2344 ' ' * (locale_len - len(timestr)))
2344 ' ' * (locale_len - len(timestr)))
2345 else:
2345 else:
2346 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2346 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2347 time.localtime(ent[3]))
2347 time.localtime(ent[3]))
2348 if ent[1] & 020000:
2348 if ent[1] & 020000:
2349 mode = 'lnk'
2349 mode = 'lnk'
2350 else:
2350 else:
2351 mode = '%3o' % (ent[1] & 0777 & ~util.umask)
2351 mode = '%3o' % (ent[1] & 0777 & ~util.umask)
2352 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2352 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2353 for f in repo.dirstate.copies():
2353 for f in repo.dirstate.copies():
2354 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2354 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2355
2355
2356 @command('debugsub',
2356 @command('debugsub',
2357 [('r', 'rev', '',
2357 [('r', 'rev', '',
2358 _('revision to check'), _('REV'))],
2358 _('revision to check'), _('REV'))],
2359 _('[-r REV] [REV]'))
2359 _('[-r REV] [REV]'))
2360 def debugsub(ui, repo, rev=None):
2360 def debugsub(ui, repo, rev=None):
2361 ctx = scmutil.revsingle(repo, rev, None)
2361 ctx = scmutil.revsingle(repo, rev, None)
2362 for k, v in sorted(ctx.substate.items()):
2362 for k, v in sorted(ctx.substate.items()):
2363 ui.write('path %s\n' % k)
2363 ui.write('path %s\n' % k)
2364 ui.write(' source %s\n' % v[0])
2364 ui.write(' source %s\n' % v[0])
2365 ui.write(' revision %s\n' % v[1])
2365 ui.write(' revision %s\n' % v[1])
2366
2366
2367 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'))
2367 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'))
2368 def debugwalk(ui, repo, *pats, **opts):
2368 def debugwalk(ui, repo, *pats, **opts):
2369 """show how files match on given patterns"""
2369 """show how files match on given patterns"""
2370 m = scmutil.match(repo[None], pats, opts)
2370 m = scmutil.match(repo[None], pats, opts)
2371 items = list(repo.walk(m))
2371 items = list(repo.walk(m))
2372 if not items:
2372 if not items:
2373 return
2373 return
2374 f = lambda fn: fn
2374 f = lambda fn: fn
2375 if ui.configbool('ui', 'slash') and os.sep != '/':
2375 if ui.configbool('ui', 'slash') and os.sep != '/':
2376 f = lambda fn: util.normpath(fn)
2376 f = lambda fn: util.normpath(fn)
2377 fmt = 'f %%-%ds %%-%ds %%s' % (
2377 fmt = 'f %%-%ds %%-%ds %%s' % (
2378 max([len(abs) for abs in items]),
2378 max([len(abs) for abs in items]),
2379 max([len(m.rel(abs)) for abs in items]))
2379 max([len(m.rel(abs)) for abs in items]))
2380 for abs in items:
2380 for abs in items:
2381 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
2381 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
2382 ui.write("%s\n" % line.rstrip())
2382 ui.write("%s\n" % line.rstrip())
2383
2383
2384 @command('debugwireargs',
2384 @command('debugwireargs',
2385 [('', 'three', '', 'three'),
2385 [('', 'three', '', 'three'),
2386 ('', 'four', '', 'four'),
2386 ('', 'four', '', 'four'),
2387 ('', 'five', '', 'five'),
2387 ('', 'five', '', 'five'),
2388 ] + remoteopts,
2388 ] + remoteopts,
2389 _('REPO [OPTIONS]... [ONE [TWO]]'))
2389 _('REPO [OPTIONS]... [ONE [TWO]]'))
2390 def debugwireargs(ui, repopath, *vals, **opts):
2390 def debugwireargs(ui, repopath, *vals, **opts):
2391 repo = hg.peer(ui, opts, repopath)
2391 repo = hg.peer(ui, opts, repopath)
2392 for opt in remoteopts:
2392 for opt in remoteopts:
2393 del opts[opt[1]]
2393 del opts[opt[1]]
2394 args = {}
2394 args = {}
2395 for k, v in opts.iteritems():
2395 for k, v in opts.iteritems():
2396 if v:
2396 if v:
2397 args[k] = v
2397 args[k] = v
2398 # run twice to check that we don't mess up the stream for the next command
2398 # run twice to check that we don't mess up the stream for the next command
2399 res1 = repo.debugwireargs(*vals, **args)
2399 res1 = repo.debugwireargs(*vals, **args)
2400 res2 = repo.debugwireargs(*vals, **args)
2400 res2 = repo.debugwireargs(*vals, **args)
2401 ui.write("%s\n" % res1)
2401 ui.write("%s\n" % res1)
2402 if res1 != res2:
2402 if res1 != res2:
2403 ui.warn("%s\n" % res2)
2403 ui.warn("%s\n" % res2)
2404
2404
2405 @command('^diff',
2405 @command('^diff',
2406 [('r', 'rev', [], _('revision'), _('REV')),
2406 [('r', 'rev', [], _('revision'), _('REV')),
2407 ('c', 'change', '', _('change made by revision'), _('REV'))
2407 ('c', 'change', '', _('change made by revision'), _('REV'))
2408 ] + diffopts + diffopts2 + walkopts + subrepoopts,
2408 ] + diffopts + diffopts2 + walkopts + subrepoopts,
2409 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'))
2409 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'))
2410 def diff(ui, repo, *pats, **opts):
2410 def diff(ui, repo, *pats, **opts):
2411 """diff repository (or selected files)
2411 """diff repository (or selected files)
2412
2412
2413 Show differences between revisions for the specified files.
2413 Show differences between revisions for the specified files.
2414
2414
2415 Differences between files are shown using the unified diff format.
2415 Differences between files are shown using the unified diff format.
2416
2416
2417 .. note::
2417 .. note::
2418 diff may generate unexpected results for merges, as it will
2418 diff may generate unexpected results for merges, as it will
2419 default to comparing against the working directory's first
2419 default to comparing against the working directory's first
2420 parent changeset if no revisions are specified.
2420 parent changeset if no revisions are specified.
2421
2421
2422 When two revision arguments are given, then changes are shown
2422 When two revision arguments are given, then changes are shown
2423 between those revisions. If only one revision is specified then
2423 between those revisions. If only one revision is specified then
2424 that revision is compared to the working directory, and, when no
2424 that revision is compared to the working directory, and, when no
2425 revisions are specified, the working directory files are compared
2425 revisions are specified, the working directory files are compared
2426 to its parent.
2426 to its parent.
2427
2427
2428 Alternatively you can specify -c/--change with a revision to see
2428 Alternatively you can specify -c/--change with a revision to see
2429 the changes in that changeset relative to its first parent.
2429 the changes in that changeset relative to its first parent.
2430
2430
2431 Without the -a/--text option, diff will avoid generating diffs of
2431 Without the -a/--text option, diff will avoid generating diffs of
2432 files it detects as binary. With -a, diff will generate a diff
2432 files it detects as binary. With -a, diff will generate a diff
2433 anyway, probably with undesirable results.
2433 anyway, probably with undesirable results.
2434
2434
2435 Use the -g/--git option to generate diffs in the git extended diff
2435 Use the -g/--git option to generate diffs in the git extended diff
2436 format. For more information, read :hg:`help diffs`.
2436 format. For more information, read :hg:`help diffs`.
2437
2437
2438 .. container:: verbose
2438 .. container:: verbose
2439
2439
2440 Examples:
2440 Examples:
2441
2441
2442 - compare a file in the current working directory to its parent::
2442 - compare a file in the current working directory to its parent::
2443
2443
2444 hg diff foo.c
2444 hg diff foo.c
2445
2445
2446 - compare two historical versions of a directory, with rename info::
2446 - compare two historical versions of a directory, with rename info::
2447
2447
2448 hg diff --git -r 1.0:1.2 lib/
2448 hg diff --git -r 1.0:1.2 lib/
2449
2449
2450 - get change stats relative to the last change on some date::
2450 - get change stats relative to the last change on some date::
2451
2451
2452 hg diff --stat -r "date('may 2')"
2452 hg diff --stat -r "date('may 2')"
2453
2453
2454 - diff all newly-added files that contain a keyword::
2454 - diff all newly-added files that contain a keyword::
2455
2455
2456 hg diff "set:added() and grep(GNU)"
2456 hg diff "set:added() and grep(GNU)"
2457
2457
2458 - compare a revision and its parents::
2458 - compare a revision and its parents::
2459
2459
2460 hg diff -c 9353 # compare against first parent
2460 hg diff -c 9353 # compare against first parent
2461 hg diff -r 9353^:9353 # same using revset syntax
2461 hg diff -r 9353^:9353 # same using revset syntax
2462 hg diff -r 9353^2:9353 # compare against the second parent
2462 hg diff -r 9353^2:9353 # compare against the second parent
2463
2463
2464 Returns 0 on success.
2464 Returns 0 on success.
2465 """
2465 """
2466
2466
2467 revs = opts.get('rev')
2467 revs = opts.get('rev')
2468 change = opts.get('change')
2468 change = opts.get('change')
2469 stat = opts.get('stat')
2469 stat = opts.get('stat')
2470 reverse = opts.get('reverse')
2470 reverse = opts.get('reverse')
2471
2471
2472 if revs and change:
2472 if revs and change:
2473 msg = _('cannot specify --rev and --change at the same time')
2473 msg = _('cannot specify --rev and --change at the same time')
2474 raise util.Abort(msg)
2474 raise util.Abort(msg)
2475 elif change:
2475 elif change:
2476 node2 = scmutil.revsingle(repo, change, None).node()
2476 node2 = scmutil.revsingle(repo, change, None).node()
2477 node1 = repo[node2].p1().node()
2477 node1 = repo[node2].p1().node()
2478 else:
2478 else:
2479 node1, node2 = scmutil.revpair(repo, revs)
2479 node1, node2 = scmutil.revpair(repo, revs)
2480
2480
2481 if reverse:
2481 if reverse:
2482 node1, node2 = node2, node1
2482 node1, node2 = node2, node1
2483
2483
2484 diffopts = patch.diffopts(ui, opts)
2484 diffopts = patch.diffopts(ui, opts)
2485 m = scmutil.match(repo[node2], pats, opts)
2485 m = scmutil.match(repo[node2], pats, opts)
2486 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2486 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2487 listsubrepos=opts.get('subrepos'))
2487 listsubrepos=opts.get('subrepos'))
2488
2488
2489 @command('^export',
2489 @command('^export',
2490 [('o', 'output', '',
2490 [('o', 'output', '',
2491 _('print output to file with formatted name'), _('FORMAT')),
2491 _('print output to file with formatted name'), _('FORMAT')),
2492 ('', 'switch-parent', None, _('diff against the second parent')),
2492 ('', 'switch-parent', None, _('diff against the second parent')),
2493 ('r', 'rev', [], _('revisions to export'), _('REV')),
2493 ('r', 'rev', [], _('revisions to export'), _('REV')),
2494 ] + diffopts,
2494 ] + diffopts,
2495 _('[OPTION]... [-o OUTFILESPEC] [-r] REV...'))
2495 _('[OPTION]... [-o OUTFILESPEC] [-r] REV...'))
2496 def export(ui, repo, *changesets, **opts):
2496 def export(ui, repo, *changesets, **opts):
2497 """dump the header and diffs for one or more changesets
2497 """dump the header and diffs for one or more changesets
2498
2498
2499 Print the changeset header and diffs for one or more revisions.
2499 Print the changeset header and diffs for one or more revisions.
2500
2500
2501 The information shown in the changeset header is: author, date,
2501 The information shown in the changeset header is: author, date,
2502 branch name (if non-default), changeset hash, parent(s) and commit
2502 branch name (if non-default), changeset hash, parent(s) and commit
2503 comment.
2503 comment.
2504
2504
2505 .. note::
2505 .. note::
2506 export may generate unexpected diff output for merge
2506 export may generate unexpected diff output for merge
2507 changesets, as it will compare the merge changeset against its
2507 changesets, as it will compare the merge changeset against its
2508 first parent only.
2508 first parent only.
2509
2509
2510 Output may be to a file, in which case the name of the file is
2510 Output may be to a file, in which case the name of the file is
2511 given using a format string. The formatting rules are as follows:
2511 given using a format string. The formatting rules are as follows:
2512
2512
2513 :``%%``: literal "%" character
2513 :``%%``: literal "%" character
2514 :``%H``: changeset hash (40 hexadecimal digits)
2514 :``%H``: changeset hash (40 hexadecimal digits)
2515 :``%N``: number of patches being generated
2515 :``%N``: number of patches being generated
2516 :``%R``: changeset revision number
2516 :``%R``: changeset revision number
2517 :``%b``: basename of the exporting repository
2517 :``%b``: basename of the exporting repository
2518 :``%h``: short-form changeset hash (12 hexadecimal digits)
2518 :``%h``: short-form changeset hash (12 hexadecimal digits)
2519 :``%m``: first line of the commit message (only alphanumeric characters)
2519 :``%m``: first line of the commit message (only alphanumeric characters)
2520 :``%n``: zero-padded sequence number, starting at 1
2520 :``%n``: zero-padded sequence number, starting at 1
2521 :``%r``: zero-padded changeset revision number
2521 :``%r``: zero-padded changeset revision number
2522
2522
2523 Without the -a/--text option, export will avoid generating diffs
2523 Without the -a/--text option, export will avoid generating diffs
2524 of files it detects as binary. With -a, export will generate a
2524 of files it detects as binary. With -a, export will generate a
2525 diff anyway, probably with undesirable results.
2525 diff anyway, probably with undesirable results.
2526
2526
2527 Use the -g/--git option to generate diffs in the git extended diff
2527 Use the -g/--git option to generate diffs in the git extended diff
2528 format. See :hg:`help diffs` for more information.
2528 format. See :hg:`help diffs` for more information.
2529
2529
2530 With the --switch-parent option, the diff will be against the
2530 With the --switch-parent option, the diff will be against the
2531 second parent. It can be useful to review a merge.
2531 second parent. It can be useful to review a merge.
2532
2532
2533 .. container:: verbose
2533 .. container:: verbose
2534
2534
2535 Examples:
2535 Examples:
2536
2536
2537 - use export and import to transplant a bugfix to the current
2537 - use export and import to transplant a bugfix to the current
2538 branch::
2538 branch::
2539
2539
2540 hg export -r 9353 | hg import -
2540 hg export -r 9353 | hg import -
2541
2541
2542 - export all the changesets between two revisions to a file with
2542 - export all the changesets between two revisions to a file with
2543 rename information::
2543 rename information::
2544
2544
2545 hg export --git -r 123:150 > changes.txt
2545 hg export --git -r 123:150 > changes.txt
2546
2546
2547 - split outgoing changes into a series of patches with
2547 - split outgoing changes into a series of patches with
2548 descriptive names::
2548 descriptive names::
2549
2549
2550 hg export -r "outgoing()" -o "%n-%m.patch"
2550 hg export -r "outgoing()" -o "%n-%m.patch"
2551
2551
2552 Returns 0 on success.
2552 Returns 0 on success.
2553 """
2553 """
2554 changesets += tuple(opts.get('rev', []))
2554 changesets += tuple(opts.get('rev', []))
2555 revs = scmutil.revrange(repo, changesets)
2555 revs = scmutil.revrange(repo, changesets)
2556 if not revs:
2556 if not revs:
2557 raise util.Abort(_("export requires at least one changeset"))
2557 raise util.Abort(_("export requires at least one changeset"))
2558 if len(revs) > 1:
2558 if len(revs) > 1:
2559 ui.note(_('exporting patches:\n'))
2559 ui.note(_('exporting patches:\n'))
2560 else:
2560 else:
2561 ui.note(_('exporting patch:\n'))
2561 ui.note(_('exporting patch:\n'))
2562 cmdutil.export(repo, revs, template=opts.get('output'),
2562 cmdutil.export(repo, revs, template=opts.get('output'),
2563 switch_parent=opts.get('switch_parent'),
2563 switch_parent=opts.get('switch_parent'),
2564 opts=patch.diffopts(ui, opts))
2564 opts=patch.diffopts(ui, opts))
2565
2565
2566 @command('^forget', walkopts, _('[OPTION]... FILE...'))
2566 @command('^forget', walkopts, _('[OPTION]... FILE...'))
2567 def forget(ui, repo, *pats, **opts):
2567 def forget(ui, repo, *pats, **opts):
2568 """forget the specified files on the next commit
2568 """forget the specified files on the next commit
2569
2569
2570 Mark the specified files so they will no longer be tracked
2570 Mark the specified files so they will no longer be tracked
2571 after the next commit.
2571 after the next commit.
2572
2572
2573 This only removes files from the current branch, not from the
2573 This only removes files from the current branch, not from the
2574 entire project history, and it does not delete them from the
2574 entire project history, and it does not delete them from the
2575 working directory.
2575 working directory.
2576
2576
2577 To undo a forget before the next commit, see :hg:`add`.
2577 To undo a forget before the next commit, see :hg:`add`.
2578
2578
2579 .. container:: verbose
2579 .. container:: verbose
2580
2580
2581 Examples:
2581 Examples:
2582
2582
2583 - forget newly-added binary files::
2583 - forget newly-added binary files::
2584
2584
2585 hg forget "set:added() and binary()"
2585 hg forget "set:added() and binary()"
2586
2586
2587 - forget files that would be excluded by .hgignore::
2587 - forget files that would be excluded by .hgignore::
2588
2588
2589 hg forget "set:hgignore()"
2589 hg forget "set:hgignore()"
2590
2590
2591 Returns 0 on success.
2591 Returns 0 on success.
2592 """
2592 """
2593
2593
2594 if not pats:
2594 if not pats:
2595 raise util.Abort(_('no files specified'))
2595 raise util.Abort(_('no files specified'))
2596
2596
2597 m = scmutil.match(repo[None], pats, opts)
2597 m = scmutil.match(repo[None], pats, opts)
2598 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
2598 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
2599 return rejected and 1 or 0
2599 return rejected and 1 or 0
2600
2600
2601 @command(
2601 @command(
2602 'graft',
2602 'graft',
2603 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2603 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2604 ('c', 'continue', False, _('resume interrupted graft')),
2604 ('c', 'continue', False, _('resume interrupted graft')),
2605 ('e', 'edit', False, _('invoke editor on commit messages')),
2605 ('e', 'edit', False, _('invoke editor on commit messages')),
2606 ('', 'log', None, _('append graft info to log message')),
2606 ('', 'log', None, _('append graft info to log message')),
2607 ('D', 'currentdate', False,
2607 ('D', 'currentdate', False,
2608 _('record the current date as commit date')),
2608 _('record the current date as commit date')),
2609 ('U', 'currentuser', False,
2609 ('U', 'currentuser', False,
2610 _('record the current user as committer'), _('DATE'))]
2610 _('record the current user as committer'), _('DATE'))]
2611 + commitopts2 + mergetoolopts + dryrunopts,
2611 + commitopts2 + mergetoolopts + dryrunopts,
2612 _('[OPTION]... [-r] REV...'))
2612 _('[OPTION]... [-r] REV...'))
2613 def graft(ui, repo, *revs, **opts):
2613 def graft(ui, repo, *revs, **opts):
2614 '''copy changes from other branches onto the current branch
2614 '''copy changes from other branches onto the current branch
2615
2615
2616 This command uses Mercurial's merge logic to copy individual
2616 This command uses Mercurial's merge logic to copy individual
2617 changes from other branches without merging branches in the
2617 changes from other branches without merging branches in the
2618 history graph. This is sometimes known as 'backporting' or
2618 history graph. This is sometimes known as 'backporting' or
2619 'cherry-picking'. By default, graft will copy user, date, and
2619 'cherry-picking'. By default, graft will copy user, date, and
2620 description from the source changesets.
2620 description from the source changesets.
2621
2621
2622 Changesets that are ancestors of the current revision, that have
2622 Changesets that are ancestors of the current revision, that have
2623 already been grafted, or that are merges will be skipped.
2623 already been grafted, or that are merges will be skipped.
2624
2624
2625 If --log is specified, log messages will have a comment appended
2625 If --log is specified, log messages will have a comment appended
2626 of the form::
2626 of the form::
2627
2627
2628 (grafted from CHANGESETHASH)
2628 (grafted from CHANGESETHASH)
2629
2629
2630 If a graft merge results in conflicts, the graft process is
2630 If a graft merge results in conflicts, the graft process is
2631 interrupted so that the current merge can be manually resolved.
2631 interrupted so that the current merge can be manually resolved.
2632 Once all conflicts are addressed, the graft process can be
2632 Once all conflicts are addressed, the graft process can be
2633 continued with the -c/--continue option.
2633 continued with the -c/--continue option.
2634
2634
2635 .. note::
2635 .. note::
2636 The -c/--continue option does not reapply earlier options.
2636 The -c/--continue option does not reapply earlier options.
2637
2637
2638 .. container:: verbose
2638 .. container:: verbose
2639
2639
2640 Examples:
2640 Examples:
2641
2641
2642 - copy a single change to the stable branch and edit its description::
2642 - copy a single change to the stable branch and edit its description::
2643
2643
2644 hg update stable
2644 hg update stable
2645 hg graft --edit 9393
2645 hg graft --edit 9393
2646
2646
2647 - graft a range of changesets with one exception, updating dates::
2647 - graft a range of changesets with one exception, updating dates::
2648
2648
2649 hg graft -D "2085::2093 and not 2091"
2649 hg graft -D "2085::2093 and not 2091"
2650
2650
2651 - continue a graft after resolving conflicts::
2651 - continue a graft after resolving conflicts::
2652
2652
2653 hg graft -c
2653 hg graft -c
2654
2654
2655 - show the source of a grafted changeset::
2655 - show the source of a grafted changeset::
2656
2656
2657 hg log --debug -r tip
2657 hg log --debug -r tip
2658
2658
2659 Returns 0 on successful completion.
2659 Returns 0 on successful completion.
2660 '''
2660 '''
2661
2661
2662 revs = list(revs)
2662 revs = list(revs)
2663 revs.extend(opts['rev'])
2663 revs.extend(opts['rev'])
2664
2664
2665 if not opts.get('user') and opts.get('currentuser'):
2665 if not opts.get('user') and opts.get('currentuser'):
2666 opts['user'] = ui.username()
2666 opts['user'] = ui.username()
2667 if not opts.get('date') and opts.get('currentdate'):
2667 if not opts.get('date') and opts.get('currentdate'):
2668 opts['date'] = "%d %d" % util.makedate()
2668 opts['date'] = "%d %d" % util.makedate()
2669
2669
2670 editor = None
2670 editor = None
2671 if opts.get('edit'):
2671 if opts.get('edit'):
2672 editor = cmdutil.commitforceeditor
2672 editor = cmdutil.commitforceeditor
2673
2673
2674 cont = False
2674 cont = False
2675 if opts['continue']:
2675 if opts['continue']:
2676 cont = True
2676 cont = True
2677 if revs:
2677 if revs:
2678 raise util.Abort(_("can't specify --continue and revisions"))
2678 raise util.Abort(_("can't specify --continue and revisions"))
2679 # read in unfinished revisions
2679 # read in unfinished revisions
2680 try:
2680 try:
2681 nodes = repo.opener.read('graftstate').splitlines()
2681 nodes = repo.opener.read('graftstate').splitlines()
2682 revs = [repo[node].rev() for node in nodes]
2682 revs = [repo[node].rev() for node in nodes]
2683 except IOError, inst:
2683 except IOError, inst:
2684 if inst.errno != errno.ENOENT:
2684 if inst.errno != errno.ENOENT:
2685 raise
2685 raise
2686 raise util.Abort(_("no graft state found, can't continue"))
2686 raise util.Abort(_("no graft state found, can't continue"))
2687 else:
2687 else:
2688 cmdutil.bailifchanged(repo)
2688 cmdutil.bailifchanged(repo)
2689 if not revs:
2689 if not revs:
2690 raise util.Abort(_('no revisions specified'))
2690 raise util.Abort(_('no revisions specified'))
2691 revs = scmutil.revrange(repo, revs)
2691 revs = scmutil.revrange(repo, revs)
2692
2692
2693 # check for merges
2693 # check for merges
2694 for rev in repo.revs('%ld and merge()', revs):
2694 for rev in repo.revs('%ld and merge()', revs):
2695 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
2695 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
2696 revs.remove(rev)
2696 revs.remove(rev)
2697 if not revs:
2697 if not revs:
2698 return -1
2698 return -1
2699
2699
2700 # check for ancestors of dest branch
2700 # check for ancestors of dest branch
2701 for rev in repo.revs('::. and %ld', revs):
2701 for rev in repo.revs('::. and %ld', revs):
2702 ui.warn(_('skipping ancestor revision %s\n') % rev)
2702 ui.warn(_('skipping ancestor revision %s\n') % rev)
2703 revs.remove(rev)
2703 revs.remove(rev)
2704 if not revs:
2704 if not revs:
2705 return -1
2705 return -1
2706
2706
2707 # analyze revs for earlier grafts
2707 # analyze revs for earlier grafts
2708 ids = {}
2708 ids = {}
2709 for ctx in repo.set("%ld", revs):
2709 for ctx in repo.set("%ld", revs):
2710 ids[ctx.hex()] = ctx.rev()
2710 ids[ctx.hex()] = ctx.rev()
2711 n = ctx.extra().get('source')
2711 n = ctx.extra().get('source')
2712 if n:
2712 if n:
2713 ids[n] = ctx.rev()
2713 ids[n] = ctx.rev()
2714
2714
2715 # check ancestors for earlier grafts
2715 # check ancestors for earlier grafts
2716 ui.debug('scanning for duplicate grafts\n')
2716 ui.debug('scanning for duplicate grafts\n')
2717 for ctx in repo.set("::. - ::%ld", revs):
2717 for ctx in repo.set("::. - ::%ld", revs):
2718 n = ctx.extra().get('source')
2718 n = ctx.extra().get('source')
2719 if n in ids:
2719 if n in ids:
2720 r = repo[n].rev()
2720 r = repo[n].rev()
2721 if r in revs:
2721 if r in revs:
2722 ui.warn(_('skipping already grafted revision %s\n') % r)
2722 ui.warn(_('skipping already grafted revision %s\n') % r)
2723 revs.remove(r)
2723 revs.remove(r)
2724 elif ids[n] in revs:
2724 elif ids[n] in revs:
2725 ui.warn(_('skipping already grafted revision %s '
2725 ui.warn(_('skipping already grafted revision %s '
2726 '(same origin %d)\n') % (ids[n], r))
2726 '(same origin %d)\n') % (ids[n], r))
2727 revs.remove(ids[n])
2727 revs.remove(ids[n])
2728 elif ctx.hex() in ids:
2728 elif ctx.hex() in ids:
2729 r = ids[ctx.hex()]
2729 r = ids[ctx.hex()]
2730 ui.warn(_('skipping already grafted revision %s '
2730 ui.warn(_('skipping already grafted revision %s '
2731 '(was grafted from %d)\n') % (r, ctx.rev()))
2731 '(was grafted from %d)\n') % (r, ctx.rev()))
2732 revs.remove(r)
2732 revs.remove(r)
2733 if not revs:
2733 if not revs:
2734 return -1
2734 return -1
2735
2735
2736 wlock = repo.wlock()
2736 wlock = repo.wlock()
2737 try:
2737 try:
2738 for pos, ctx in enumerate(repo.set("%ld", revs)):
2738 for pos, ctx in enumerate(repo.set("%ld", revs)):
2739 current = repo['.']
2739 current = repo['.']
2740
2740
2741 ui.status(_('grafting revision %s\n') % ctx.rev())
2741 ui.status(_('grafting revision %s\n') % ctx.rev())
2742 if opts.get('dry_run'):
2742 if opts.get('dry_run'):
2743 continue
2743 continue
2744
2744
2745 # we don't merge the first commit when continuing
2745 # we don't merge the first commit when continuing
2746 if not cont:
2746 if not cont:
2747 # perform the graft merge with p1(rev) as 'ancestor'
2747 # perform the graft merge with p1(rev) as 'ancestor'
2748 try:
2748 try:
2749 # ui.forcemerge is an internal variable, do not document
2749 # ui.forcemerge is an internal variable, do not document
2750 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
2750 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
2751 stats = mergemod.update(repo, ctx.node(), True, True, False,
2751 stats = mergemod.update(repo, ctx.node(), True, True, False,
2752 ctx.p1().node())
2752 ctx.p1().node())
2753 finally:
2753 finally:
2754 repo.ui.setconfig('ui', 'forcemerge', '')
2754 repo.ui.setconfig('ui', 'forcemerge', '')
2755 # drop the second merge parent
2756 repo.setparents(current.node(), nullid)
2757 repo.dirstate.write()
2758 # fix up dirstate for copies and renames
2759 cmdutil.duplicatecopies(repo, ctx.rev(), ctx.p1().rev())
2760 # report any conflicts
2755 # report any conflicts
2761 if stats and stats[3] > 0:
2756 if stats and stats[3] > 0:
2762 # write out state for --continue
2757 # write out state for --continue
2763 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
2758 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
2764 repo.opener.write('graftstate', ''.join(nodelines))
2759 repo.opener.write('graftstate', ''.join(nodelines))
2765 raise util.Abort(
2760 raise util.Abort(
2766 _("unresolved conflicts, can't continue"),
2761 _("unresolved conflicts, can't continue"),
2767 hint=_('use hg resolve and hg graft --continue'))
2762 hint=_('use hg resolve and hg graft --continue'))
2768 else:
2763 else:
2769 cont = False
2764 cont = False
2770
2765
2766 # drop the second merge parent
2767 repo.setparents(current.node(), nullid)
2768 repo.dirstate.write()
2769 # fix up dirstate for copies and renames
2770 cmdutil.duplicatecopies(repo, ctx.rev(), ctx.p1().rev())
2771
2771 # commit
2772 # commit
2772 source = ctx.extra().get('source')
2773 source = ctx.extra().get('source')
2773 if not source:
2774 if not source:
2774 source = ctx.hex()
2775 source = ctx.hex()
2775 extra = {'source': source}
2776 extra = {'source': source}
2776 user = ctx.user()
2777 user = ctx.user()
2777 if opts.get('user'):
2778 if opts.get('user'):
2778 user = opts['user']
2779 user = opts['user']
2779 date = ctx.date()
2780 date = ctx.date()
2780 if opts.get('date'):
2781 if opts.get('date'):
2781 date = opts['date']
2782 date = opts['date']
2782 message = ctx.description()
2783 message = ctx.description()
2783 if opts.get('log'):
2784 if opts.get('log'):
2784 message += '\n(grafted from %s)' % ctx.hex()
2785 message += '\n(grafted from %s)' % ctx.hex()
2785 node = repo.commit(text=message, user=user,
2786 node = repo.commit(text=message, user=user,
2786 date=date, extra=extra, editor=editor)
2787 date=date, extra=extra, editor=editor)
2787 if node is None:
2788 if node is None:
2788 ui.status(_('graft for revision %s is empty\n') % ctx.rev())
2789 ui.status(_('graft for revision %s is empty\n') % ctx.rev())
2789 finally:
2790 finally:
2790 wlock.release()
2791 wlock.release()
2791
2792
2792 # remove state when we complete successfully
2793 # remove state when we complete successfully
2793 if not opts.get('dry_run') and os.path.exists(repo.join('graftstate')):
2794 if not opts.get('dry_run') and os.path.exists(repo.join('graftstate')):
2794 util.unlinkpath(repo.join('graftstate'))
2795 util.unlinkpath(repo.join('graftstate'))
2795
2796
2796 return 0
2797 return 0
2797
2798
2798 @command('grep',
2799 @command('grep',
2799 [('0', 'print0', None, _('end fields with NUL')),
2800 [('0', 'print0', None, _('end fields with NUL')),
2800 ('', 'all', None, _('print all revisions that match')),
2801 ('', 'all', None, _('print all revisions that match')),
2801 ('a', 'text', None, _('treat all files as text')),
2802 ('a', 'text', None, _('treat all files as text')),
2802 ('f', 'follow', None,
2803 ('f', 'follow', None,
2803 _('follow changeset history,'
2804 _('follow changeset history,'
2804 ' or file history across copies and renames')),
2805 ' or file history across copies and renames')),
2805 ('i', 'ignore-case', None, _('ignore case when matching')),
2806 ('i', 'ignore-case', None, _('ignore case when matching')),
2806 ('l', 'files-with-matches', None,
2807 ('l', 'files-with-matches', None,
2807 _('print only filenames and revisions that match')),
2808 _('print only filenames and revisions that match')),
2808 ('n', 'line-number', None, _('print matching line numbers')),
2809 ('n', 'line-number', None, _('print matching line numbers')),
2809 ('r', 'rev', [],
2810 ('r', 'rev', [],
2810 _('only search files changed within revision range'), _('REV')),
2811 _('only search files changed within revision range'), _('REV')),
2811 ('u', 'user', None, _('list the author (long with -v)')),
2812 ('u', 'user', None, _('list the author (long with -v)')),
2812 ('d', 'date', None, _('list the date (short with -q)')),
2813 ('d', 'date', None, _('list the date (short with -q)')),
2813 ] + walkopts,
2814 ] + walkopts,
2814 _('[OPTION]... PATTERN [FILE]...'))
2815 _('[OPTION]... PATTERN [FILE]...'))
2815 def grep(ui, repo, pattern, *pats, **opts):
2816 def grep(ui, repo, pattern, *pats, **opts):
2816 """search for a pattern in specified files and revisions
2817 """search for a pattern in specified files and revisions
2817
2818
2818 Search revisions of files for a regular expression.
2819 Search revisions of files for a regular expression.
2819
2820
2820 This command behaves differently than Unix grep. It only accepts
2821 This command behaves differently than Unix grep. It only accepts
2821 Python/Perl regexps. It searches repository history, not the
2822 Python/Perl regexps. It searches repository history, not the
2822 working directory. It always prints the revision number in which a
2823 working directory. It always prints the revision number in which a
2823 match appears.
2824 match appears.
2824
2825
2825 By default, grep only prints output for the first revision of a
2826 By default, grep only prints output for the first revision of a
2826 file in which it finds a match. To get it to print every revision
2827 file in which it finds a match. To get it to print every revision
2827 that contains a change in match status ("-" for a match that
2828 that contains a change in match status ("-" for a match that
2828 becomes a non-match, or "+" for a non-match that becomes a match),
2829 becomes a non-match, or "+" for a non-match that becomes a match),
2829 use the --all flag.
2830 use the --all flag.
2830
2831
2831 Returns 0 if a match is found, 1 otherwise.
2832 Returns 0 if a match is found, 1 otherwise.
2832 """
2833 """
2833 reflags = re.M
2834 reflags = re.M
2834 if opts.get('ignore_case'):
2835 if opts.get('ignore_case'):
2835 reflags |= re.I
2836 reflags |= re.I
2836 try:
2837 try:
2837 regexp = re.compile(pattern, reflags)
2838 regexp = re.compile(pattern, reflags)
2838 except re.error, inst:
2839 except re.error, inst:
2839 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2840 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2840 return 1
2841 return 1
2841 sep, eol = ':', '\n'
2842 sep, eol = ':', '\n'
2842 if opts.get('print0'):
2843 if opts.get('print0'):
2843 sep = eol = '\0'
2844 sep = eol = '\0'
2844
2845
2845 getfile = util.lrucachefunc(repo.file)
2846 getfile = util.lrucachefunc(repo.file)
2846
2847
2847 def matchlines(body):
2848 def matchlines(body):
2848 begin = 0
2849 begin = 0
2849 linenum = 0
2850 linenum = 0
2850 while True:
2851 while True:
2851 match = regexp.search(body, begin)
2852 match = regexp.search(body, begin)
2852 if not match:
2853 if not match:
2853 break
2854 break
2854 mstart, mend = match.span()
2855 mstart, mend = match.span()
2855 linenum += body.count('\n', begin, mstart) + 1
2856 linenum += body.count('\n', begin, mstart) + 1
2856 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2857 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2857 begin = body.find('\n', mend) + 1 or len(body) + 1
2858 begin = body.find('\n', mend) + 1 or len(body) + 1
2858 lend = begin - 1
2859 lend = begin - 1
2859 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2860 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2860
2861
2861 class linestate(object):
2862 class linestate(object):
2862 def __init__(self, line, linenum, colstart, colend):
2863 def __init__(self, line, linenum, colstart, colend):
2863 self.line = line
2864 self.line = line
2864 self.linenum = linenum
2865 self.linenum = linenum
2865 self.colstart = colstart
2866 self.colstart = colstart
2866 self.colend = colend
2867 self.colend = colend
2867
2868
2868 def __hash__(self):
2869 def __hash__(self):
2869 return hash((self.linenum, self.line))
2870 return hash((self.linenum, self.line))
2870
2871
2871 def __eq__(self, other):
2872 def __eq__(self, other):
2872 return self.line == other.line
2873 return self.line == other.line
2873
2874
2874 matches = {}
2875 matches = {}
2875 copies = {}
2876 copies = {}
2876 def grepbody(fn, rev, body):
2877 def grepbody(fn, rev, body):
2877 matches[rev].setdefault(fn, [])
2878 matches[rev].setdefault(fn, [])
2878 m = matches[rev][fn]
2879 m = matches[rev][fn]
2879 for lnum, cstart, cend, line in matchlines(body):
2880 for lnum, cstart, cend, line in matchlines(body):
2880 s = linestate(line, lnum, cstart, cend)
2881 s = linestate(line, lnum, cstart, cend)
2881 m.append(s)
2882 m.append(s)
2882
2883
2883 def difflinestates(a, b):
2884 def difflinestates(a, b):
2884 sm = difflib.SequenceMatcher(None, a, b)
2885 sm = difflib.SequenceMatcher(None, a, b)
2885 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2886 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2886 if tag == 'insert':
2887 if tag == 'insert':
2887 for i in xrange(blo, bhi):
2888 for i in xrange(blo, bhi):
2888 yield ('+', b[i])
2889 yield ('+', b[i])
2889 elif tag == 'delete':
2890 elif tag == 'delete':
2890 for i in xrange(alo, ahi):
2891 for i in xrange(alo, ahi):
2891 yield ('-', a[i])
2892 yield ('-', a[i])
2892 elif tag == 'replace':
2893 elif tag == 'replace':
2893 for i in xrange(alo, ahi):
2894 for i in xrange(alo, ahi):
2894 yield ('-', a[i])
2895 yield ('-', a[i])
2895 for i in xrange(blo, bhi):
2896 for i in xrange(blo, bhi):
2896 yield ('+', b[i])
2897 yield ('+', b[i])
2897
2898
2898 def display(fn, ctx, pstates, states):
2899 def display(fn, ctx, pstates, states):
2899 rev = ctx.rev()
2900 rev = ctx.rev()
2900 datefunc = ui.quiet and util.shortdate or util.datestr
2901 datefunc = ui.quiet and util.shortdate or util.datestr
2901 found = False
2902 found = False
2902 filerevmatches = {}
2903 filerevmatches = {}
2903 def binary():
2904 def binary():
2904 flog = getfile(fn)
2905 flog = getfile(fn)
2905 return util.binary(flog.read(ctx.filenode(fn)))
2906 return util.binary(flog.read(ctx.filenode(fn)))
2906
2907
2907 if opts.get('all'):
2908 if opts.get('all'):
2908 iter = difflinestates(pstates, states)
2909 iter = difflinestates(pstates, states)
2909 else:
2910 else:
2910 iter = [('', l) for l in states]
2911 iter = [('', l) for l in states]
2911 for change, l in iter:
2912 for change, l in iter:
2912 cols = [fn, str(rev)]
2913 cols = [fn, str(rev)]
2913 before, match, after = None, None, None
2914 before, match, after = None, None, None
2914 if opts.get('line_number'):
2915 if opts.get('line_number'):
2915 cols.append(str(l.linenum))
2916 cols.append(str(l.linenum))
2916 if opts.get('all'):
2917 if opts.get('all'):
2917 cols.append(change)
2918 cols.append(change)
2918 if opts.get('user'):
2919 if opts.get('user'):
2919 cols.append(ui.shortuser(ctx.user()))
2920 cols.append(ui.shortuser(ctx.user()))
2920 if opts.get('date'):
2921 if opts.get('date'):
2921 cols.append(datefunc(ctx.date()))
2922 cols.append(datefunc(ctx.date()))
2922 if opts.get('files_with_matches'):
2923 if opts.get('files_with_matches'):
2923 c = (fn, rev)
2924 c = (fn, rev)
2924 if c in filerevmatches:
2925 if c in filerevmatches:
2925 continue
2926 continue
2926 filerevmatches[c] = 1
2927 filerevmatches[c] = 1
2927 else:
2928 else:
2928 before = l.line[:l.colstart]
2929 before = l.line[:l.colstart]
2929 match = l.line[l.colstart:l.colend]
2930 match = l.line[l.colstart:l.colend]
2930 after = l.line[l.colend:]
2931 after = l.line[l.colend:]
2931 ui.write(sep.join(cols))
2932 ui.write(sep.join(cols))
2932 if before is not None:
2933 if before is not None:
2933 if not opts.get('text') and binary():
2934 if not opts.get('text') and binary():
2934 ui.write(sep + " Binary file matches")
2935 ui.write(sep + " Binary file matches")
2935 else:
2936 else:
2936 ui.write(sep + before)
2937 ui.write(sep + before)
2937 ui.write(match, label='grep.match')
2938 ui.write(match, label='grep.match')
2938 ui.write(after)
2939 ui.write(after)
2939 ui.write(eol)
2940 ui.write(eol)
2940 found = True
2941 found = True
2941 return found
2942 return found
2942
2943
2943 skip = {}
2944 skip = {}
2944 revfiles = {}
2945 revfiles = {}
2945 matchfn = scmutil.match(repo[None], pats, opts)
2946 matchfn = scmutil.match(repo[None], pats, opts)
2946 found = False
2947 found = False
2947 follow = opts.get('follow')
2948 follow = opts.get('follow')
2948
2949
2949 def prep(ctx, fns):
2950 def prep(ctx, fns):
2950 rev = ctx.rev()
2951 rev = ctx.rev()
2951 pctx = ctx.p1()
2952 pctx = ctx.p1()
2952 parent = pctx.rev()
2953 parent = pctx.rev()
2953 matches.setdefault(rev, {})
2954 matches.setdefault(rev, {})
2954 matches.setdefault(parent, {})
2955 matches.setdefault(parent, {})
2955 files = revfiles.setdefault(rev, [])
2956 files = revfiles.setdefault(rev, [])
2956 for fn in fns:
2957 for fn in fns:
2957 flog = getfile(fn)
2958 flog = getfile(fn)
2958 try:
2959 try:
2959 fnode = ctx.filenode(fn)
2960 fnode = ctx.filenode(fn)
2960 except error.LookupError:
2961 except error.LookupError:
2961 continue
2962 continue
2962
2963
2963 copied = flog.renamed(fnode)
2964 copied = flog.renamed(fnode)
2964 copy = follow and copied and copied[0]
2965 copy = follow and copied and copied[0]
2965 if copy:
2966 if copy:
2966 copies.setdefault(rev, {})[fn] = copy
2967 copies.setdefault(rev, {})[fn] = copy
2967 if fn in skip:
2968 if fn in skip:
2968 if copy:
2969 if copy:
2969 skip[copy] = True
2970 skip[copy] = True
2970 continue
2971 continue
2971 files.append(fn)
2972 files.append(fn)
2972
2973
2973 if fn not in matches[rev]:
2974 if fn not in matches[rev]:
2974 grepbody(fn, rev, flog.read(fnode))
2975 grepbody(fn, rev, flog.read(fnode))
2975
2976
2976 pfn = copy or fn
2977 pfn = copy or fn
2977 if pfn not in matches[parent]:
2978 if pfn not in matches[parent]:
2978 try:
2979 try:
2979 fnode = pctx.filenode(pfn)
2980 fnode = pctx.filenode(pfn)
2980 grepbody(pfn, parent, flog.read(fnode))
2981 grepbody(pfn, parent, flog.read(fnode))
2981 except error.LookupError:
2982 except error.LookupError:
2982 pass
2983 pass
2983
2984
2984 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2985 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2985 rev = ctx.rev()
2986 rev = ctx.rev()
2986 parent = ctx.p1().rev()
2987 parent = ctx.p1().rev()
2987 for fn in sorted(revfiles.get(rev, [])):
2988 for fn in sorted(revfiles.get(rev, [])):
2988 states = matches[rev][fn]
2989 states = matches[rev][fn]
2989 copy = copies.get(rev, {}).get(fn)
2990 copy = copies.get(rev, {}).get(fn)
2990 if fn in skip:
2991 if fn in skip:
2991 if copy:
2992 if copy:
2992 skip[copy] = True
2993 skip[copy] = True
2993 continue
2994 continue
2994 pstates = matches.get(parent, {}).get(copy or fn, [])
2995 pstates = matches.get(parent, {}).get(copy or fn, [])
2995 if pstates or states:
2996 if pstates or states:
2996 r = display(fn, ctx, pstates, states)
2997 r = display(fn, ctx, pstates, states)
2997 found = found or r
2998 found = found or r
2998 if r and not opts.get('all'):
2999 if r and not opts.get('all'):
2999 skip[fn] = True
3000 skip[fn] = True
3000 if copy:
3001 if copy:
3001 skip[copy] = True
3002 skip[copy] = True
3002 del matches[rev]
3003 del matches[rev]
3003 del revfiles[rev]
3004 del revfiles[rev]
3004
3005
3005 return not found
3006 return not found
3006
3007
3007 @command('heads',
3008 @command('heads',
3008 [('r', 'rev', '',
3009 [('r', 'rev', '',
3009 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3010 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3010 ('t', 'topo', False, _('show topological heads only')),
3011 ('t', 'topo', False, _('show topological heads only')),
3011 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3012 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3012 ('c', 'closed', False, _('show normal and closed branch heads')),
3013 ('c', 'closed', False, _('show normal and closed branch heads')),
3013 ] + templateopts,
3014 ] + templateopts,
3014 _('[-ct] [-r STARTREV] [REV]...'))
3015 _('[-ct] [-r STARTREV] [REV]...'))
3015 def heads(ui, repo, *branchrevs, **opts):
3016 def heads(ui, repo, *branchrevs, **opts):
3016 """show current repository heads or show branch heads
3017 """show current repository heads or show branch heads
3017
3018
3018 With no arguments, show all repository branch heads.
3019 With no arguments, show all repository branch heads.
3019
3020
3020 Repository "heads" are changesets with no child changesets. They are
3021 Repository "heads" are changesets with no child changesets. They are
3021 where development generally takes place and are the usual targets
3022 where development generally takes place and are the usual targets
3022 for update and merge operations. Branch heads are changesets that have
3023 for update and merge operations. Branch heads are changesets that have
3023 no child changeset on the same branch.
3024 no child changeset on the same branch.
3024
3025
3025 If one or more REVs are given, only branch heads on the branches
3026 If one or more REVs are given, only branch heads on the branches
3026 associated with the specified changesets are shown. This means
3027 associated with the specified changesets are shown. This means
3027 that you can use :hg:`heads foo` to see the heads on a branch
3028 that you can use :hg:`heads foo` to see the heads on a branch
3028 named ``foo``.
3029 named ``foo``.
3029
3030
3030 If -c/--closed is specified, also show branch heads marked closed
3031 If -c/--closed is specified, also show branch heads marked closed
3031 (see :hg:`commit --close-branch`).
3032 (see :hg:`commit --close-branch`).
3032
3033
3033 If STARTREV is specified, only those heads that are descendants of
3034 If STARTREV is specified, only those heads that are descendants of
3034 STARTREV will be displayed.
3035 STARTREV will be displayed.
3035
3036
3036 If -t/--topo is specified, named branch mechanics will be ignored and only
3037 If -t/--topo is specified, named branch mechanics will be ignored and only
3037 changesets without children will be shown.
3038 changesets without children will be shown.
3038
3039
3039 Returns 0 if matching heads are found, 1 if not.
3040 Returns 0 if matching heads are found, 1 if not.
3040 """
3041 """
3041
3042
3042 start = None
3043 start = None
3043 if 'rev' in opts:
3044 if 'rev' in opts:
3044 start = scmutil.revsingle(repo, opts['rev'], None).node()
3045 start = scmutil.revsingle(repo, opts['rev'], None).node()
3045
3046
3046 if opts.get('topo'):
3047 if opts.get('topo'):
3047 heads = [repo[h] for h in repo.heads(start)]
3048 heads = [repo[h] for h in repo.heads(start)]
3048 else:
3049 else:
3049 heads = []
3050 heads = []
3050 for branch in repo.branchmap():
3051 for branch in repo.branchmap():
3051 heads += repo.branchheads(branch, start, opts.get('closed'))
3052 heads += repo.branchheads(branch, start, opts.get('closed'))
3052 heads = [repo[h] for h in heads]
3053 heads = [repo[h] for h in heads]
3053
3054
3054 if branchrevs:
3055 if branchrevs:
3055 branches = set(repo[br].branch() for br in branchrevs)
3056 branches = set(repo[br].branch() for br in branchrevs)
3056 heads = [h for h in heads if h.branch() in branches]
3057 heads = [h for h in heads if h.branch() in branches]
3057
3058
3058 if opts.get('active') and branchrevs:
3059 if opts.get('active') and branchrevs:
3059 dagheads = repo.heads(start)
3060 dagheads = repo.heads(start)
3060 heads = [h for h in heads if h.node() in dagheads]
3061 heads = [h for h in heads if h.node() in dagheads]
3061
3062
3062 if branchrevs:
3063 if branchrevs:
3063 haveheads = set(h.branch() for h in heads)
3064 haveheads = set(h.branch() for h in heads)
3064 if branches - haveheads:
3065 if branches - haveheads:
3065 headless = ', '.join(b for b in branches - haveheads)
3066 headless = ', '.join(b for b in branches - haveheads)
3066 msg = _('no open branch heads found on branches %s')
3067 msg = _('no open branch heads found on branches %s')
3067 if opts.get('rev'):
3068 if opts.get('rev'):
3068 msg += _(' (started at %s)') % opts['rev']
3069 msg += _(' (started at %s)') % opts['rev']
3069 ui.warn((msg + '\n') % headless)
3070 ui.warn((msg + '\n') % headless)
3070
3071
3071 if not heads:
3072 if not heads:
3072 return 1
3073 return 1
3073
3074
3074 heads = sorted(heads, key=lambda x: -x.rev())
3075 heads = sorted(heads, key=lambda x: -x.rev())
3075 displayer = cmdutil.show_changeset(ui, repo, opts)
3076 displayer = cmdutil.show_changeset(ui, repo, opts)
3076 for ctx in heads:
3077 for ctx in heads:
3077 displayer.show(ctx)
3078 displayer.show(ctx)
3078 displayer.close()
3079 displayer.close()
3079
3080
3080 @command('help',
3081 @command('help',
3081 [('e', 'extension', None, _('show only help for extensions')),
3082 [('e', 'extension', None, _('show only help for extensions')),
3082 ('c', 'command', None, _('show only help for commands')),
3083 ('c', 'command', None, _('show only help for commands')),
3083 ('k', 'keyword', '', _('show topics matching keyword')),
3084 ('k', 'keyword', '', _('show topics matching keyword')),
3084 ],
3085 ],
3085 _('[-ec] [TOPIC]'))
3086 _('[-ec] [TOPIC]'))
3086 def help_(ui, name=None, unknowncmd=False, full=True, **opts):
3087 def help_(ui, name=None, unknowncmd=False, full=True, **opts):
3087 """show help for a given topic or a help overview
3088 """show help for a given topic or a help overview
3088
3089
3089 With no arguments, print a list of commands with short help messages.
3090 With no arguments, print a list of commands with short help messages.
3090
3091
3091 Given a topic, extension, or command name, print help for that
3092 Given a topic, extension, or command name, print help for that
3092 topic.
3093 topic.
3093
3094
3094 Returns 0 if successful.
3095 Returns 0 if successful.
3095 """
3096 """
3096
3097
3097 textwidth = min(ui.termwidth(), 80) - 2
3098 textwidth = min(ui.termwidth(), 80) - 2
3098
3099
3099 def helpcmd(name):
3100 def helpcmd(name):
3100 try:
3101 try:
3101 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
3102 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
3102 except error.AmbiguousCommand, inst:
3103 except error.AmbiguousCommand, inst:
3103 # py3k fix: except vars can't be used outside the scope of the
3104 # py3k fix: except vars can't be used outside the scope of the
3104 # except block, nor can be used inside a lambda. python issue4617
3105 # except block, nor can be used inside a lambda. python issue4617
3105 prefix = inst.args[0]
3106 prefix = inst.args[0]
3106 select = lambda c: c.lstrip('^').startswith(prefix)
3107 select = lambda c: c.lstrip('^').startswith(prefix)
3107 rst = helplist(select)
3108 rst = helplist(select)
3108 return rst
3109 return rst
3109
3110
3110 rst = []
3111 rst = []
3111
3112
3112 # check if it's an invalid alias and display its error if it is
3113 # check if it's an invalid alias and display its error if it is
3113 if getattr(entry[0], 'badalias', False):
3114 if getattr(entry[0], 'badalias', False):
3114 if not unknowncmd:
3115 if not unknowncmd:
3115 ui.pushbuffer()
3116 ui.pushbuffer()
3116 entry[0](ui)
3117 entry[0](ui)
3117 rst.append(ui.popbuffer())
3118 rst.append(ui.popbuffer())
3118 return rst
3119 return rst
3119
3120
3120 # synopsis
3121 # synopsis
3121 if len(entry) > 2:
3122 if len(entry) > 2:
3122 if entry[2].startswith('hg'):
3123 if entry[2].startswith('hg'):
3123 rst.append("%s\n" % entry[2])
3124 rst.append("%s\n" % entry[2])
3124 else:
3125 else:
3125 rst.append('hg %s %s\n' % (aliases[0], entry[2]))
3126 rst.append('hg %s %s\n' % (aliases[0], entry[2]))
3126 else:
3127 else:
3127 rst.append('hg %s\n' % aliases[0])
3128 rst.append('hg %s\n' % aliases[0])
3128 # aliases
3129 # aliases
3129 if full and not ui.quiet and len(aliases) > 1:
3130 if full and not ui.quiet and len(aliases) > 1:
3130 rst.append(_("\naliases: %s\n") % ', '.join(aliases[1:]))
3131 rst.append(_("\naliases: %s\n") % ', '.join(aliases[1:]))
3131 rst.append('\n')
3132 rst.append('\n')
3132
3133
3133 # description
3134 # description
3134 doc = gettext(entry[0].__doc__)
3135 doc = gettext(entry[0].__doc__)
3135 if not doc:
3136 if not doc:
3136 doc = _("(no help text available)")
3137 doc = _("(no help text available)")
3137 if util.safehasattr(entry[0], 'definition'): # aliased command
3138 if util.safehasattr(entry[0], 'definition'): # aliased command
3138 if entry[0].definition.startswith('!'): # shell alias
3139 if entry[0].definition.startswith('!'): # shell alias
3139 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
3140 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
3140 else:
3141 else:
3141 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
3142 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
3142 doc = doc.splitlines(True)
3143 doc = doc.splitlines(True)
3143 if ui.quiet or not full:
3144 if ui.quiet or not full:
3144 rst.append(doc[0])
3145 rst.append(doc[0])
3145 else:
3146 else:
3146 rst.extend(doc)
3147 rst.extend(doc)
3147 rst.append('\n')
3148 rst.append('\n')
3148
3149
3149 # check if this command shadows a non-trivial (multi-line)
3150 # check if this command shadows a non-trivial (multi-line)
3150 # extension help text
3151 # extension help text
3151 try:
3152 try:
3152 mod = extensions.find(name)
3153 mod = extensions.find(name)
3153 doc = gettext(mod.__doc__) or ''
3154 doc = gettext(mod.__doc__) or ''
3154 if '\n' in doc.strip():
3155 if '\n' in doc.strip():
3155 msg = _('use "hg help -e %s" to show help for '
3156 msg = _('use "hg help -e %s" to show help for '
3156 'the %s extension') % (name, name)
3157 'the %s extension') % (name, name)
3157 rst.append('\n%s\n' % msg)
3158 rst.append('\n%s\n' % msg)
3158 except KeyError:
3159 except KeyError:
3159 pass
3160 pass
3160
3161
3161 # options
3162 # options
3162 if not ui.quiet and entry[1]:
3163 if not ui.quiet and entry[1]:
3163 rst.append('\n%s\n\n' % _("options:"))
3164 rst.append('\n%s\n\n' % _("options:"))
3164 rst.append(help.optrst(entry[1], ui.verbose))
3165 rst.append(help.optrst(entry[1], ui.verbose))
3165
3166
3166 if ui.verbose:
3167 if ui.verbose:
3167 rst.append('\n%s\n\n' % _("global options:"))
3168 rst.append('\n%s\n\n' % _("global options:"))
3168 rst.append(help.optrst(globalopts, ui.verbose))
3169 rst.append(help.optrst(globalopts, ui.verbose))
3169
3170
3170 if not ui.verbose:
3171 if not ui.verbose:
3171 if not full:
3172 if not full:
3172 rst.append(_('\nuse "hg help %s" to show the full help text\n')
3173 rst.append(_('\nuse "hg help %s" to show the full help text\n')
3173 % name)
3174 % name)
3174 elif not ui.quiet:
3175 elif not ui.quiet:
3175 rst.append(_('\nuse "hg -v help %s" to show more info\n')
3176 rst.append(_('\nuse "hg -v help %s" to show more info\n')
3176 % name)
3177 % name)
3177 return rst
3178 return rst
3178
3179
3179
3180
3180 def helplist(select=None):
3181 def helplist(select=None):
3181 # list of commands
3182 # list of commands
3182 if name == "shortlist":
3183 if name == "shortlist":
3183 header = _('basic commands:\n\n')
3184 header = _('basic commands:\n\n')
3184 else:
3185 else:
3185 header = _('list of commands:\n\n')
3186 header = _('list of commands:\n\n')
3186
3187
3187 h = {}
3188 h = {}
3188 cmds = {}
3189 cmds = {}
3189 for c, e in table.iteritems():
3190 for c, e in table.iteritems():
3190 f = c.split("|", 1)[0]
3191 f = c.split("|", 1)[0]
3191 if select and not select(f):
3192 if select and not select(f):
3192 continue
3193 continue
3193 if (not select and name != 'shortlist' and
3194 if (not select and name != 'shortlist' and
3194 e[0].__module__ != __name__):
3195 e[0].__module__ != __name__):
3195 continue
3196 continue
3196 if name == "shortlist" and not f.startswith("^"):
3197 if name == "shortlist" and not f.startswith("^"):
3197 continue
3198 continue
3198 f = f.lstrip("^")
3199 f = f.lstrip("^")
3199 if not ui.debugflag and f.startswith("debug"):
3200 if not ui.debugflag and f.startswith("debug"):
3200 continue
3201 continue
3201 doc = e[0].__doc__
3202 doc = e[0].__doc__
3202 if doc and 'DEPRECATED' in doc and not ui.verbose:
3203 if doc and 'DEPRECATED' in doc and not ui.verbose:
3203 continue
3204 continue
3204 doc = gettext(doc)
3205 doc = gettext(doc)
3205 if not doc:
3206 if not doc:
3206 doc = _("(no help text available)")
3207 doc = _("(no help text available)")
3207 h[f] = doc.splitlines()[0].rstrip()
3208 h[f] = doc.splitlines()[0].rstrip()
3208 cmds[f] = c.lstrip("^")
3209 cmds[f] = c.lstrip("^")
3209
3210
3210 rst = []
3211 rst = []
3211 if not h:
3212 if not h:
3212 if not ui.quiet:
3213 if not ui.quiet:
3213 rst.append(_('no commands defined\n'))
3214 rst.append(_('no commands defined\n'))
3214 return rst
3215 return rst
3215
3216
3216 if not ui.quiet:
3217 if not ui.quiet:
3217 rst.append(header)
3218 rst.append(header)
3218 fns = sorted(h)
3219 fns = sorted(h)
3219 for f in fns:
3220 for f in fns:
3220 if ui.verbose:
3221 if ui.verbose:
3221 commands = cmds[f].replace("|",", ")
3222 commands = cmds[f].replace("|",", ")
3222 rst.append(" :%s: %s\n" % (commands, h[f]))
3223 rst.append(" :%s: %s\n" % (commands, h[f]))
3223 else:
3224 else:
3224 rst.append(' :%s: %s\n' % (f, h[f]))
3225 rst.append(' :%s: %s\n' % (f, h[f]))
3225
3226
3226 if not name:
3227 if not name:
3227 exts = help.listexts(_('enabled extensions:'), extensions.enabled())
3228 exts = help.listexts(_('enabled extensions:'), extensions.enabled())
3228 if exts:
3229 if exts:
3229 rst.append('\n')
3230 rst.append('\n')
3230 rst.extend(exts)
3231 rst.extend(exts)
3231
3232
3232 rst.append(_("\nadditional help topics:\n\n"))
3233 rst.append(_("\nadditional help topics:\n\n"))
3233 topics = []
3234 topics = []
3234 for names, header, doc in help.helptable:
3235 for names, header, doc in help.helptable:
3235 topics.append((sorted(names, key=len, reverse=True)[0], header))
3236 topics.append((sorted(names, key=len, reverse=True)[0], header))
3236 for t, desc in topics:
3237 for t, desc in topics:
3237 rst.append(" :%s: %s\n" % (t, desc))
3238 rst.append(" :%s: %s\n" % (t, desc))
3238
3239
3239 optlist = []
3240 optlist = []
3240 if not ui.quiet:
3241 if not ui.quiet:
3241 if ui.verbose:
3242 if ui.verbose:
3242 optlist.append((_("global options:"), globalopts))
3243 optlist.append((_("global options:"), globalopts))
3243 if name == 'shortlist':
3244 if name == 'shortlist':
3244 optlist.append((_('use "hg help" for the full list '
3245 optlist.append((_('use "hg help" for the full list '
3245 'of commands'), ()))
3246 'of commands'), ()))
3246 else:
3247 else:
3247 if name == 'shortlist':
3248 if name == 'shortlist':
3248 msg = _('use "hg help" for the full list of commands '
3249 msg = _('use "hg help" for the full list of commands '
3249 'or "hg -v" for details')
3250 'or "hg -v" for details')
3250 elif name and not full:
3251 elif name and not full:
3251 msg = _('use "hg help %s" to show the full help '
3252 msg = _('use "hg help %s" to show the full help '
3252 'text') % name
3253 'text') % name
3253 else:
3254 else:
3254 msg = _('use "hg -v help%s" to show builtin aliases and '
3255 msg = _('use "hg -v help%s" to show builtin aliases and '
3255 'global options') % (name and " " + name or "")
3256 'global options') % (name and " " + name or "")
3256 optlist.append((msg, ()))
3257 optlist.append((msg, ()))
3257
3258
3258 if optlist:
3259 if optlist:
3259 for title, options in optlist:
3260 for title, options in optlist:
3260 rst.append('\n%s\n' % title)
3261 rst.append('\n%s\n' % title)
3261 if options:
3262 if options:
3262 rst.append('\n%s\n' % help.optrst(options, ui.verbose))
3263 rst.append('\n%s\n' % help.optrst(options, ui.verbose))
3263 return rst
3264 return rst
3264
3265
3265 def helptopic(name):
3266 def helptopic(name):
3266 for names, header, doc in help.helptable:
3267 for names, header, doc in help.helptable:
3267 if name in names:
3268 if name in names:
3268 break
3269 break
3269 else:
3270 else:
3270 raise error.UnknownCommand(name)
3271 raise error.UnknownCommand(name)
3271
3272
3272 rst = ["%s\n\n" % header]
3273 rst = ["%s\n\n" % header]
3273 # description
3274 # description
3274 if not doc:
3275 if not doc:
3275 rst.append(" %s\n" % _("(no help text available)"))
3276 rst.append(" %s\n" % _("(no help text available)"))
3276 if util.safehasattr(doc, '__call__'):
3277 if util.safehasattr(doc, '__call__'):
3277 rst += [" %s\n" % l for l in doc().splitlines()]
3278 rst += [" %s\n" % l for l in doc().splitlines()]
3278
3279
3279 try:
3280 try:
3280 cmdutil.findcmd(name, table)
3281 cmdutil.findcmd(name, table)
3281 rst.append(_('\nuse "hg help -c %s" to see help for '
3282 rst.append(_('\nuse "hg help -c %s" to see help for '
3282 'the %s command\n') % (name, name))
3283 'the %s command\n') % (name, name))
3283 except error.UnknownCommand:
3284 except error.UnknownCommand:
3284 pass
3285 pass
3285 return rst
3286 return rst
3286
3287
3287 def helpext(name):
3288 def helpext(name):
3288 try:
3289 try:
3289 mod = extensions.find(name)
3290 mod = extensions.find(name)
3290 doc = gettext(mod.__doc__) or _('no help text available')
3291 doc = gettext(mod.__doc__) or _('no help text available')
3291 except KeyError:
3292 except KeyError:
3292 mod = None
3293 mod = None
3293 doc = extensions.disabledext(name)
3294 doc = extensions.disabledext(name)
3294 if not doc:
3295 if not doc:
3295 raise error.UnknownCommand(name)
3296 raise error.UnknownCommand(name)
3296
3297
3297 if '\n' not in doc:
3298 if '\n' not in doc:
3298 head, tail = doc, ""
3299 head, tail = doc, ""
3299 else:
3300 else:
3300 head, tail = doc.split('\n', 1)
3301 head, tail = doc.split('\n', 1)
3301 rst = [_('%s extension - %s\n\n') % (name.split('.')[-1], head)]
3302 rst = [_('%s extension - %s\n\n') % (name.split('.')[-1], head)]
3302 if tail:
3303 if tail:
3303 rst.extend(tail.splitlines(True))
3304 rst.extend(tail.splitlines(True))
3304 rst.append('\n')
3305 rst.append('\n')
3305
3306
3306 if mod:
3307 if mod:
3307 try:
3308 try:
3308 ct = mod.cmdtable
3309 ct = mod.cmdtable
3309 except AttributeError:
3310 except AttributeError:
3310 ct = {}
3311 ct = {}
3311 modcmds = set([c.split('|', 1)[0] for c in ct])
3312 modcmds = set([c.split('|', 1)[0] for c in ct])
3312 rst.extend(helplist(modcmds.__contains__))
3313 rst.extend(helplist(modcmds.__contains__))
3313 else:
3314 else:
3314 rst.append(_('use "hg help extensions" for information on enabling '
3315 rst.append(_('use "hg help extensions" for information on enabling '
3315 'extensions\n'))
3316 'extensions\n'))
3316 return rst
3317 return rst
3317
3318
3318 def helpextcmd(name):
3319 def helpextcmd(name):
3319 cmd, ext, mod = extensions.disabledcmd(ui, name,
3320 cmd, ext, mod = extensions.disabledcmd(ui, name,
3320 ui.configbool('ui', 'strict'))
3321 ui.configbool('ui', 'strict'))
3321 doc = gettext(mod.__doc__).splitlines()[0]
3322 doc = gettext(mod.__doc__).splitlines()[0]
3322
3323
3323 rst = help.listexts(_("'%s' is provided by the following "
3324 rst = help.listexts(_("'%s' is provided by the following "
3324 "extension:") % cmd, {ext: doc}, indent=4)
3325 "extension:") % cmd, {ext: doc}, indent=4)
3325 rst.append('\n')
3326 rst.append('\n')
3326 rst.append(_('use "hg help extensions" for information on enabling '
3327 rst.append(_('use "hg help extensions" for information on enabling '
3327 'extensions\n'))
3328 'extensions\n'))
3328 return rst
3329 return rst
3329
3330
3330
3331
3331 rst = []
3332 rst = []
3332 kw = opts.get('keyword')
3333 kw = opts.get('keyword')
3333 if kw:
3334 if kw:
3334 matches = help.topicmatch(kw)
3335 matches = help.topicmatch(kw)
3335 for t, title in (('topics', _('Topics')),
3336 for t, title in (('topics', _('Topics')),
3336 ('commands', _('Commands')),
3337 ('commands', _('Commands')),
3337 ('extensions', _('Extensions')),
3338 ('extensions', _('Extensions')),
3338 ('extensioncommands', _('Extension Commands'))):
3339 ('extensioncommands', _('Extension Commands'))):
3339 if matches[t]:
3340 if matches[t]:
3340 rst.append('%s:\n\n' % title)
3341 rst.append('%s:\n\n' % title)
3341 rst.extend(minirst.maketable(sorted(matches[t]), 1))
3342 rst.extend(minirst.maketable(sorted(matches[t]), 1))
3342 rst.append('\n')
3343 rst.append('\n')
3343 elif name and name != 'shortlist':
3344 elif name and name != 'shortlist':
3344 i = None
3345 i = None
3345 if unknowncmd:
3346 if unknowncmd:
3346 queries = (helpextcmd,)
3347 queries = (helpextcmd,)
3347 elif opts.get('extension'):
3348 elif opts.get('extension'):
3348 queries = (helpext,)
3349 queries = (helpext,)
3349 elif opts.get('command'):
3350 elif opts.get('command'):
3350 queries = (helpcmd,)
3351 queries = (helpcmd,)
3351 else:
3352 else:
3352 queries = (helptopic, helpcmd, helpext, helpextcmd)
3353 queries = (helptopic, helpcmd, helpext, helpextcmd)
3353 for f in queries:
3354 for f in queries:
3354 try:
3355 try:
3355 rst = f(name)
3356 rst = f(name)
3356 i = None
3357 i = None
3357 break
3358 break
3358 except error.UnknownCommand, inst:
3359 except error.UnknownCommand, inst:
3359 i = inst
3360 i = inst
3360 if i:
3361 if i:
3361 raise i
3362 raise i
3362 else:
3363 else:
3363 # program name
3364 # program name
3364 if not ui.quiet:
3365 if not ui.quiet:
3365 rst = [_("Mercurial Distributed SCM\n"), '\n']
3366 rst = [_("Mercurial Distributed SCM\n"), '\n']
3366 rst.extend(helplist())
3367 rst.extend(helplist())
3367
3368
3368 keep = ui.verbose and ['verbose'] or []
3369 keep = ui.verbose and ['verbose'] or []
3369 formatted, pruned = minirst.format(''.join(rst), textwidth, keep=keep)
3370 formatted, pruned = minirst.format(''.join(rst), textwidth, keep=keep)
3370 ui.write(formatted)
3371 ui.write(formatted)
3371
3372
3372
3373
3373 @command('identify|id',
3374 @command('identify|id',
3374 [('r', 'rev', '',
3375 [('r', 'rev', '',
3375 _('identify the specified revision'), _('REV')),
3376 _('identify the specified revision'), _('REV')),
3376 ('n', 'num', None, _('show local revision number')),
3377 ('n', 'num', None, _('show local revision number')),
3377 ('i', 'id', None, _('show global revision id')),
3378 ('i', 'id', None, _('show global revision id')),
3378 ('b', 'branch', None, _('show branch')),
3379 ('b', 'branch', None, _('show branch')),
3379 ('t', 'tags', None, _('show tags')),
3380 ('t', 'tags', None, _('show tags')),
3380 ('B', 'bookmarks', None, _('show bookmarks')),
3381 ('B', 'bookmarks', None, _('show bookmarks')),
3381 ] + remoteopts,
3382 ] + remoteopts,
3382 _('[-nibtB] [-r REV] [SOURCE]'))
3383 _('[-nibtB] [-r REV] [SOURCE]'))
3383 def identify(ui, repo, source=None, rev=None,
3384 def identify(ui, repo, source=None, rev=None,
3384 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3385 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3385 """identify the working copy or specified revision
3386 """identify the working copy or specified revision
3386
3387
3387 Print a summary identifying the repository state at REV using one or
3388 Print a summary identifying the repository state at REV using one or
3388 two parent hash identifiers, followed by a "+" if the working
3389 two parent hash identifiers, followed by a "+" if the working
3389 directory has uncommitted changes, the branch name (if not default),
3390 directory has uncommitted changes, the branch name (if not default),
3390 a list of tags, and a list of bookmarks.
3391 a list of tags, and a list of bookmarks.
3391
3392
3392 When REV is not given, print a summary of the current state of the
3393 When REV is not given, print a summary of the current state of the
3393 repository.
3394 repository.
3394
3395
3395 Specifying a path to a repository root or Mercurial bundle will
3396 Specifying a path to a repository root or Mercurial bundle will
3396 cause lookup to operate on that repository/bundle.
3397 cause lookup to operate on that repository/bundle.
3397
3398
3398 .. container:: verbose
3399 .. container:: verbose
3399
3400
3400 Examples:
3401 Examples:
3401
3402
3402 - generate a build identifier for the working directory::
3403 - generate a build identifier for the working directory::
3403
3404
3404 hg id --id > build-id.dat
3405 hg id --id > build-id.dat
3405
3406
3406 - find the revision corresponding to a tag::
3407 - find the revision corresponding to a tag::
3407
3408
3408 hg id -n -r 1.3
3409 hg id -n -r 1.3
3409
3410
3410 - check the most recent revision of a remote repository::
3411 - check the most recent revision of a remote repository::
3411
3412
3412 hg id -r tip http://selenic.com/hg/
3413 hg id -r tip http://selenic.com/hg/
3413
3414
3414 Returns 0 if successful.
3415 Returns 0 if successful.
3415 """
3416 """
3416
3417
3417 if not repo and not source:
3418 if not repo and not source:
3418 raise util.Abort(_("there is no Mercurial repository here "
3419 raise util.Abort(_("there is no Mercurial repository here "
3419 "(.hg not found)"))
3420 "(.hg not found)"))
3420
3421
3421 hexfunc = ui.debugflag and hex or short
3422 hexfunc = ui.debugflag and hex or short
3422 default = not (num or id or branch or tags or bookmarks)
3423 default = not (num or id or branch or tags or bookmarks)
3423 output = []
3424 output = []
3424 revs = []
3425 revs = []
3425
3426
3426 if source:
3427 if source:
3427 source, branches = hg.parseurl(ui.expandpath(source))
3428 source, branches = hg.parseurl(ui.expandpath(source))
3428 repo = hg.peer(ui, opts, source)
3429 repo = hg.peer(ui, opts, source)
3429 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3430 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3430
3431
3431 if not repo.local():
3432 if not repo.local():
3432 if num or branch or tags:
3433 if num or branch or tags:
3433 raise util.Abort(
3434 raise util.Abort(
3434 _("can't query remote revision number, branch, or tags"))
3435 _("can't query remote revision number, branch, or tags"))
3435 if not rev and revs:
3436 if not rev and revs:
3436 rev = revs[0]
3437 rev = revs[0]
3437 if not rev:
3438 if not rev:
3438 rev = "tip"
3439 rev = "tip"
3439
3440
3440 remoterev = repo.lookup(rev)
3441 remoterev = repo.lookup(rev)
3441 if default or id:
3442 if default or id:
3442 output = [hexfunc(remoterev)]
3443 output = [hexfunc(remoterev)]
3443
3444
3444 def getbms():
3445 def getbms():
3445 bms = []
3446 bms = []
3446
3447
3447 if 'bookmarks' in repo.listkeys('namespaces'):
3448 if 'bookmarks' in repo.listkeys('namespaces'):
3448 hexremoterev = hex(remoterev)
3449 hexremoterev = hex(remoterev)
3449 bms = [bm for bm, bmr in repo.listkeys('bookmarks').iteritems()
3450 bms = [bm for bm, bmr in repo.listkeys('bookmarks').iteritems()
3450 if bmr == hexremoterev]
3451 if bmr == hexremoterev]
3451
3452
3452 return bms
3453 return bms
3453
3454
3454 if bookmarks:
3455 if bookmarks:
3455 output.extend(getbms())
3456 output.extend(getbms())
3456 elif default and not ui.quiet:
3457 elif default and not ui.quiet:
3457 # multiple bookmarks for a single parent separated by '/'
3458 # multiple bookmarks for a single parent separated by '/'
3458 bm = '/'.join(getbms())
3459 bm = '/'.join(getbms())
3459 if bm:
3460 if bm:
3460 output.append(bm)
3461 output.append(bm)
3461 else:
3462 else:
3462 if not rev:
3463 if not rev:
3463 ctx = repo[None]
3464 ctx = repo[None]
3464 parents = ctx.parents()
3465 parents = ctx.parents()
3465 changed = ""
3466 changed = ""
3466 if default or id or num:
3467 if default or id or num:
3467 changed = util.any(repo.status()) and "+" or ""
3468 changed = util.any(repo.status()) and "+" or ""
3468 if default or id:
3469 if default or id:
3469 output = ["%s%s" %
3470 output = ["%s%s" %
3470 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
3471 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
3471 if num:
3472 if num:
3472 output.append("%s%s" %
3473 output.append("%s%s" %
3473 ('+'.join([str(p.rev()) for p in parents]), changed))
3474 ('+'.join([str(p.rev()) for p in parents]), changed))
3474 else:
3475 else:
3475 ctx = scmutil.revsingle(repo, rev)
3476 ctx = scmutil.revsingle(repo, rev)
3476 if default or id:
3477 if default or id:
3477 output = [hexfunc(ctx.node())]
3478 output = [hexfunc(ctx.node())]
3478 if num:
3479 if num:
3479 output.append(str(ctx.rev()))
3480 output.append(str(ctx.rev()))
3480
3481
3481 if default and not ui.quiet:
3482 if default and not ui.quiet:
3482 b = ctx.branch()
3483 b = ctx.branch()
3483 if b != 'default':
3484 if b != 'default':
3484 output.append("(%s)" % b)
3485 output.append("(%s)" % b)
3485
3486
3486 # multiple tags for a single parent separated by '/'
3487 # multiple tags for a single parent separated by '/'
3487 t = '/'.join(ctx.tags())
3488 t = '/'.join(ctx.tags())
3488 if t:
3489 if t:
3489 output.append(t)
3490 output.append(t)
3490
3491
3491 # multiple bookmarks for a single parent separated by '/'
3492 # multiple bookmarks for a single parent separated by '/'
3492 bm = '/'.join(ctx.bookmarks())
3493 bm = '/'.join(ctx.bookmarks())
3493 if bm:
3494 if bm:
3494 output.append(bm)
3495 output.append(bm)
3495 else:
3496 else:
3496 if branch:
3497 if branch:
3497 output.append(ctx.branch())
3498 output.append(ctx.branch())
3498
3499
3499 if tags:
3500 if tags:
3500 output.extend(ctx.tags())
3501 output.extend(ctx.tags())
3501
3502
3502 if bookmarks:
3503 if bookmarks:
3503 output.extend(ctx.bookmarks())
3504 output.extend(ctx.bookmarks())
3504
3505
3505 ui.write("%s\n" % ' '.join(output))
3506 ui.write("%s\n" % ' '.join(output))
3506
3507
3507 @command('import|patch',
3508 @command('import|patch',
3508 [('p', 'strip', 1,
3509 [('p', 'strip', 1,
3509 _('directory strip option for patch. This has the same '
3510 _('directory strip option for patch. This has the same '
3510 'meaning as the corresponding patch option'), _('NUM')),
3511 'meaning as the corresponding patch option'), _('NUM')),
3511 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3512 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3512 ('e', 'edit', False, _('invoke editor on commit messages')),
3513 ('e', 'edit', False, _('invoke editor on commit messages')),
3513 ('f', 'force', None, _('skip check for outstanding uncommitted changes')),
3514 ('f', 'force', None, _('skip check for outstanding uncommitted changes')),
3514 ('', 'no-commit', None,
3515 ('', 'no-commit', None,
3515 _("don't commit, just update the working directory")),
3516 _("don't commit, just update the working directory")),
3516 ('', 'bypass', None,
3517 ('', 'bypass', None,
3517 _("apply patch without touching the working directory")),
3518 _("apply patch without touching the working directory")),
3518 ('', 'exact', None,
3519 ('', 'exact', None,
3519 _('apply patch to the nodes from which it was generated')),
3520 _('apply patch to the nodes from which it was generated')),
3520 ('', 'import-branch', None,
3521 ('', 'import-branch', None,
3521 _('use any branch information in patch (implied by --exact)'))] +
3522 _('use any branch information in patch (implied by --exact)'))] +
3522 commitopts + commitopts2 + similarityopts,
3523 commitopts + commitopts2 + similarityopts,
3523 _('[OPTION]... PATCH...'))
3524 _('[OPTION]... PATCH...'))
3524 def import_(ui, repo, patch1=None, *patches, **opts):
3525 def import_(ui, repo, patch1=None, *patches, **opts):
3525 """import an ordered set of patches
3526 """import an ordered set of patches
3526
3527
3527 Import a list of patches and commit them individually (unless
3528 Import a list of patches and commit them individually (unless
3528 --no-commit is specified).
3529 --no-commit is specified).
3529
3530
3530 If there are outstanding changes in the working directory, import
3531 If there are outstanding changes in the working directory, import
3531 will abort unless given the -f/--force flag.
3532 will abort unless given the -f/--force flag.
3532
3533
3533 You can import a patch straight from a mail message. Even patches
3534 You can import a patch straight from a mail message. Even patches
3534 as attachments work (to use the body part, it must have type
3535 as attachments work (to use the body part, it must have type
3535 text/plain or text/x-patch). From and Subject headers of email
3536 text/plain or text/x-patch). From and Subject headers of email
3536 message are used as default committer and commit message. All
3537 message are used as default committer and commit message. All
3537 text/plain body parts before first diff are added to commit
3538 text/plain body parts before first diff are added to commit
3538 message.
3539 message.
3539
3540
3540 If the imported patch was generated by :hg:`export`, user and
3541 If the imported patch was generated by :hg:`export`, user and
3541 description from patch override values from message headers and
3542 description from patch override values from message headers and
3542 body. Values given on command line with -m/--message and -u/--user
3543 body. Values given on command line with -m/--message and -u/--user
3543 override these.
3544 override these.
3544
3545
3545 If --exact is specified, import will set the working directory to
3546 If --exact is specified, import will set the working directory to
3546 the parent of each patch before applying it, and will abort if the
3547 the parent of each patch before applying it, and will abort if the
3547 resulting changeset has a different ID than the one recorded in
3548 resulting changeset has a different ID than the one recorded in
3548 the patch. This may happen due to character set problems or other
3549 the patch. This may happen due to character set problems or other
3549 deficiencies in the text patch format.
3550 deficiencies in the text patch format.
3550
3551
3551 Use --bypass to apply and commit patches directly to the
3552 Use --bypass to apply and commit patches directly to the
3552 repository, not touching the working directory. Without --exact,
3553 repository, not touching the working directory. Without --exact,
3553 patches will be applied on top of the working directory parent
3554 patches will be applied on top of the working directory parent
3554 revision.
3555 revision.
3555
3556
3556 With -s/--similarity, hg will attempt to discover renames and
3557 With -s/--similarity, hg will attempt to discover renames and
3557 copies in the patch in the same way as :hg:`addremove`.
3558 copies in the patch in the same way as :hg:`addremove`.
3558
3559
3559 To read a patch from standard input, use "-" as the patch name. If
3560 To read a patch from standard input, use "-" as the patch name. If
3560 a URL is specified, the patch will be downloaded from it.
3561 a URL is specified, the patch will be downloaded from it.
3561 See :hg:`help dates` for a list of formats valid for -d/--date.
3562 See :hg:`help dates` for a list of formats valid for -d/--date.
3562
3563
3563 .. container:: verbose
3564 .. container:: verbose
3564
3565
3565 Examples:
3566 Examples:
3566
3567
3567 - import a traditional patch from a website and detect renames::
3568 - import a traditional patch from a website and detect renames::
3568
3569
3569 hg import -s 80 http://example.com/bugfix.patch
3570 hg import -s 80 http://example.com/bugfix.patch
3570
3571
3571 - import a changeset from an hgweb server::
3572 - import a changeset from an hgweb server::
3572
3573
3573 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
3574 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
3574
3575
3575 - import all the patches in an Unix-style mbox::
3576 - import all the patches in an Unix-style mbox::
3576
3577
3577 hg import incoming-patches.mbox
3578 hg import incoming-patches.mbox
3578
3579
3579 - attempt to exactly restore an exported changeset (not always
3580 - attempt to exactly restore an exported changeset (not always
3580 possible)::
3581 possible)::
3581
3582
3582 hg import --exact proposed-fix.patch
3583 hg import --exact proposed-fix.patch
3583
3584
3584 Returns 0 on success.
3585 Returns 0 on success.
3585 """
3586 """
3586
3587
3587 if not patch1:
3588 if not patch1:
3588 raise util.Abort(_('need at least one patch to import'))
3589 raise util.Abort(_('need at least one patch to import'))
3589
3590
3590 patches = (patch1,) + patches
3591 patches = (patch1,) + patches
3591
3592
3592 date = opts.get('date')
3593 date = opts.get('date')
3593 if date:
3594 if date:
3594 opts['date'] = util.parsedate(date)
3595 opts['date'] = util.parsedate(date)
3595
3596
3596 editor = cmdutil.commiteditor
3597 editor = cmdutil.commiteditor
3597 if opts.get('edit'):
3598 if opts.get('edit'):
3598 editor = cmdutil.commitforceeditor
3599 editor = cmdutil.commitforceeditor
3599
3600
3600 update = not opts.get('bypass')
3601 update = not opts.get('bypass')
3601 if not update and opts.get('no_commit'):
3602 if not update and opts.get('no_commit'):
3602 raise util.Abort(_('cannot use --no-commit with --bypass'))
3603 raise util.Abort(_('cannot use --no-commit with --bypass'))
3603 try:
3604 try:
3604 sim = float(opts.get('similarity') or 0)
3605 sim = float(opts.get('similarity') or 0)
3605 except ValueError:
3606 except ValueError:
3606 raise util.Abort(_('similarity must be a number'))
3607 raise util.Abort(_('similarity must be a number'))
3607 if sim < 0 or sim > 100:
3608 if sim < 0 or sim > 100:
3608 raise util.Abort(_('similarity must be between 0 and 100'))
3609 raise util.Abort(_('similarity must be between 0 and 100'))
3609 if sim and not update:
3610 if sim and not update:
3610 raise util.Abort(_('cannot use --similarity with --bypass'))
3611 raise util.Abort(_('cannot use --similarity with --bypass'))
3611
3612
3612 if (opts.get('exact') or not opts.get('force')) and update:
3613 if (opts.get('exact') or not opts.get('force')) and update:
3613 cmdutil.bailifchanged(repo)
3614 cmdutil.bailifchanged(repo)
3614
3615
3615 base = opts["base"]
3616 base = opts["base"]
3616 strip = opts["strip"]
3617 strip = opts["strip"]
3617 wlock = lock = tr = None
3618 wlock = lock = tr = None
3618 msgs = []
3619 msgs = []
3619
3620
3620 def checkexact(repo, n, nodeid):
3621 def checkexact(repo, n, nodeid):
3621 if opts.get('exact') and hex(n) != nodeid:
3622 if opts.get('exact') and hex(n) != nodeid:
3622 repo.rollback()
3623 repo.rollback()
3623 raise util.Abort(_('patch is damaged or loses information'))
3624 raise util.Abort(_('patch is damaged or loses information'))
3624
3625
3625 def tryone(ui, hunk, parents):
3626 def tryone(ui, hunk, parents):
3626 tmpname, message, user, date, branch, nodeid, p1, p2 = \
3627 tmpname, message, user, date, branch, nodeid, p1, p2 = \
3627 patch.extract(ui, hunk)
3628 patch.extract(ui, hunk)
3628
3629
3629 if not tmpname:
3630 if not tmpname:
3630 return (None, None)
3631 return (None, None)
3631 msg = _('applied to working directory')
3632 msg = _('applied to working directory')
3632
3633
3633 try:
3634 try:
3634 cmdline_message = cmdutil.logmessage(ui, opts)
3635 cmdline_message = cmdutil.logmessage(ui, opts)
3635 if cmdline_message:
3636 if cmdline_message:
3636 # pickup the cmdline msg
3637 # pickup the cmdline msg
3637 message = cmdline_message
3638 message = cmdline_message
3638 elif message:
3639 elif message:
3639 # pickup the patch msg
3640 # pickup the patch msg
3640 message = message.strip()
3641 message = message.strip()
3641 else:
3642 else:
3642 # launch the editor
3643 # launch the editor
3643 message = None
3644 message = None
3644 ui.debug('message:\n%s\n' % message)
3645 ui.debug('message:\n%s\n' % message)
3645
3646
3646 if len(parents) == 1:
3647 if len(parents) == 1:
3647 parents.append(repo[nullid])
3648 parents.append(repo[nullid])
3648 if opts.get('exact'):
3649 if opts.get('exact'):
3649 if not nodeid or not p1:
3650 if not nodeid or not p1:
3650 raise util.Abort(_('not a Mercurial patch'))
3651 raise util.Abort(_('not a Mercurial patch'))
3651 p1 = repo[p1]
3652 p1 = repo[p1]
3652 p2 = repo[p2 or nullid]
3653 p2 = repo[p2 or nullid]
3653 elif p2:
3654 elif p2:
3654 try:
3655 try:
3655 p1 = repo[p1]
3656 p1 = repo[p1]
3656 p2 = repo[p2]
3657 p2 = repo[p2]
3657 # Without any options, consider p2 only if the
3658 # Without any options, consider p2 only if the
3658 # patch is being applied on top of the recorded
3659 # patch is being applied on top of the recorded
3659 # first parent.
3660 # first parent.
3660 if p1 != parents[0]:
3661 if p1 != parents[0]:
3661 p1 = parents[0]
3662 p1 = parents[0]
3662 p2 = repo[nullid]
3663 p2 = repo[nullid]
3663 except error.RepoError:
3664 except error.RepoError:
3664 p1, p2 = parents
3665 p1, p2 = parents
3665 else:
3666 else:
3666 p1, p2 = parents
3667 p1, p2 = parents
3667
3668
3668 n = None
3669 n = None
3669 if update:
3670 if update:
3670 if p1 != parents[0]:
3671 if p1 != parents[0]:
3671 hg.clean(repo, p1.node())
3672 hg.clean(repo, p1.node())
3672 if p2 != parents[1]:
3673 if p2 != parents[1]:
3673 repo.setparents(p1.node(), p2.node())
3674 repo.setparents(p1.node(), p2.node())
3674
3675
3675 if opts.get('exact') or opts.get('import_branch'):
3676 if opts.get('exact') or opts.get('import_branch'):
3676 repo.dirstate.setbranch(branch or 'default')
3677 repo.dirstate.setbranch(branch or 'default')
3677
3678
3678 files = set()
3679 files = set()
3679 patch.patch(ui, repo, tmpname, strip=strip, files=files,
3680 patch.patch(ui, repo, tmpname, strip=strip, files=files,
3680 eolmode=None, similarity=sim / 100.0)
3681 eolmode=None, similarity=sim / 100.0)
3681 files = list(files)
3682 files = list(files)
3682 if opts.get('no_commit'):
3683 if opts.get('no_commit'):
3683 if message:
3684 if message:
3684 msgs.append(message)
3685 msgs.append(message)
3685 else:
3686 else:
3686 if opts.get('exact') or p2:
3687 if opts.get('exact') or p2:
3687 # If you got here, you either use --force and know what
3688 # If you got here, you either use --force and know what
3688 # you are doing or used --exact or a merge patch while
3689 # you are doing or used --exact or a merge patch while
3689 # being updated to its first parent.
3690 # being updated to its first parent.
3690 m = None
3691 m = None
3691 else:
3692 else:
3692 m = scmutil.matchfiles(repo, files or [])
3693 m = scmutil.matchfiles(repo, files or [])
3693 n = repo.commit(message, opts.get('user') or user,
3694 n = repo.commit(message, opts.get('user') or user,
3694 opts.get('date') or date, match=m,
3695 opts.get('date') or date, match=m,
3695 editor=editor)
3696 editor=editor)
3696 checkexact(repo, n, nodeid)
3697 checkexact(repo, n, nodeid)
3697 else:
3698 else:
3698 if opts.get('exact') or opts.get('import_branch'):
3699 if opts.get('exact') or opts.get('import_branch'):
3699 branch = branch or 'default'
3700 branch = branch or 'default'
3700 else:
3701 else:
3701 branch = p1.branch()
3702 branch = p1.branch()
3702 store = patch.filestore()
3703 store = patch.filestore()
3703 try:
3704 try:
3704 files = set()
3705 files = set()
3705 try:
3706 try:
3706 patch.patchrepo(ui, repo, p1, store, tmpname, strip,
3707 patch.patchrepo(ui, repo, p1, store, tmpname, strip,
3707 files, eolmode=None)
3708 files, eolmode=None)
3708 except patch.PatchError, e:
3709 except patch.PatchError, e:
3709 raise util.Abort(str(e))
3710 raise util.Abort(str(e))
3710 memctx = patch.makememctx(repo, (p1.node(), p2.node()),
3711 memctx = patch.makememctx(repo, (p1.node(), p2.node()),
3711 message,
3712 message,
3712 opts.get('user') or user,
3713 opts.get('user') or user,
3713 opts.get('date') or date,
3714 opts.get('date') or date,
3714 branch, files, store,
3715 branch, files, store,
3715 editor=cmdutil.commiteditor)
3716 editor=cmdutil.commiteditor)
3716 repo.savecommitmessage(memctx.description())
3717 repo.savecommitmessage(memctx.description())
3717 n = memctx.commit()
3718 n = memctx.commit()
3718 checkexact(repo, n, nodeid)
3719 checkexact(repo, n, nodeid)
3719 finally:
3720 finally:
3720 store.close()
3721 store.close()
3721 if n:
3722 if n:
3722 # i18n: refers to a short changeset id
3723 # i18n: refers to a short changeset id
3723 msg = _('created %s') % short(n)
3724 msg = _('created %s') % short(n)
3724 return (msg, n)
3725 return (msg, n)
3725 finally:
3726 finally:
3726 os.unlink(tmpname)
3727 os.unlink(tmpname)
3727
3728
3728 try:
3729 try:
3729 try:
3730 try:
3730 wlock = repo.wlock()
3731 wlock = repo.wlock()
3731 if not opts.get('no_commit'):
3732 if not opts.get('no_commit'):
3732 lock = repo.lock()
3733 lock = repo.lock()
3733 tr = repo.transaction('import')
3734 tr = repo.transaction('import')
3734 parents = repo.parents()
3735 parents = repo.parents()
3735 for patchurl in patches:
3736 for patchurl in patches:
3736 if patchurl == '-':
3737 if patchurl == '-':
3737 ui.status(_('applying patch from stdin\n'))
3738 ui.status(_('applying patch from stdin\n'))
3738 patchfile = ui.fin
3739 patchfile = ui.fin
3739 patchurl = 'stdin' # for error message
3740 patchurl = 'stdin' # for error message
3740 else:
3741 else:
3741 patchurl = os.path.join(base, patchurl)
3742 patchurl = os.path.join(base, patchurl)
3742 ui.status(_('applying %s\n') % patchurl)
3743 ui.status(_('applying %s\n') % patchurl)
3743 patchfile = url.open(ui, patchurl)
3744 patchfile = url.open(ui, patchurl)
3744
3745
3745 haspatch = False
3746 haspatch = False
3746 for hunk in patch.split(patchfile):
3747 for hunk in patch.split(patchfile):
3747 (msg, node) = tryone(ui, hunk, parents)
3748 (msg, node) = tryone(ui, hunk, parents)
3748 if msg:
3749 if msg:
3749 haspatch = True
3750 haspatch = True
3750 ui.note(msg + '\n')
3751 ui.note(msg + '\n')
3751 if update or opts.get('exact'):
3752 if update or opts.get('exact'):
3752 parents = repo.parents()
3753 parents = repo.parents()
3753 else:
3754 else:
3754 parents = [repo[node]]
3755 parents = [repo[node]]
3755
3756
3756 if not haspatch:
3757 if not haspatch:
3757 raise util.Abort(_('%s: no diffs found') % patchurl)
3758 raise util.Abort(_('%s: no diffs found') % patchurl)
3758
3759
3759 if tr:
3760 if tr:
3760 tr.close()
3761 tr.close()
3761 if msgs:
3762 if msgs:
3762 repo.savecommitmessage('\n* * *\n'.join(msgs))
3763 repo.savecommitmessage('\n* * *\n'.join(msgs))
3763 except: # re-raises
3764 except: # re-raises
3764 # wlock.release() indirectly calls dirstate.write(): since
3765 # wlock.release() indirectly calls dirstate.write(): since
3765 # we're crashing, we do not want to change the working dir
3766 # we're crashing, we do not want to change the working dir
3766 # parent after all, so make sure it writes nothing
3767 # parent after all, so make sure it writes nothing
3767 repo.dirstate.invalidate()
3768 repo.dirstate.invalidate()
3768 raise
3769 raise
3769 finally:
3770 finally:
3770 if tr:
3771 if tr:
3771 tr.release()
3772 tr.release()
3772 release(lock, wlock)
3773 release(lock, wlock)
3773
3774
3774 @command('incoming|in',
3775 @command('incoming|in',
3775 [('f', 'force', None,
3776 [('f', 'force', None,
3776 _('run even if remote repository is unrelated')),
3777 _('run even if remote repository is unrelated')),
3777 ('n', 'newest-first', None, _('show newest record first')),
3778 ('n', 'newest-first', None, _('show newest record first')),
3778 ('', 'bundle', '',
3779 ('', 'bundle', '',
3779 _('file to store the bundles into'), _('FILE')),
3780 _('file to store the bundles into'), _('FILE')),
3780 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3781 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3781 ('B', 'bookmarks', False, _("compare bookmarks")),
3782 ('B', 'bookmarks', False, _("compare bookmarks")),
3782 ('b', 'branch', [],
3783 ('b', 'branch', [],
3783 _('a specific branch you would like to pull'), _('BRANCH')),
3784 _('a specific branch you would like to pull'), _('BRANCH')),
3784 ] + logopts + remoteopts + subrepoopts,
3785 ] + logopts + remoteopts + subrepoopts,
3785 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3786 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3786 def incoming(ui, repo, source="default", **opts):
3787 def incoming(ui, repo, source="default", **opts):
3787 """show new changesets found in source
3788 """show new changesets found in source
3788
3789
3789 Show new changesets found in the specified path/URL or the default
3790 Show new changesets found in the specified path/URL or the default
3790 pull location. These are the changesets that would have been pulled
3791 pull location. These are the changesets that would have been pulled
3791 if a pull at the time you issued this command.
3792 if a pull at the time you issued this command.
3792
3793
3793 For remote repository, using --bundle avoids downloading the
3794 For remote repository, using --bundle avoids downloading the
3794 changesets twice if the incoming is followed by a pull.
3795 changesets twice if the incoming is followed by a pull.
3795
3796
3796 See pull for valid source format details.
3797 See pull for valid source format details.
3797
3798
3798 Returns 0 if there are incoming changes, 1 otherwise.
3799 Returns 0 if there are incoming changes, 1 otherwise.
3799 """
3800 """
3800 if opts.get('bundle') and opts.get('subrepos'):
3801 if opts.get('bundle') and opts.get('subrepos'):
3801 raise util.Abort(_('cannot combine --bundle and --subrepos'))
3802 raise util.Abort(_('cannot combine --bundle and --subrepos'))
3802
3803
3803 if opts.get('bookmarks'):
3804 if opts.get('bookmarks'):
3804 source, branches = hg.parseurl(ui.expandpath(source),
3805 source, branches = hg.parseurl(ui.expandpath(source),
3805 opts.get('branch'))
3806 opts.get('branch'))
3806 other = hg.peer(repo, opts, source)
3807 other = hg.peer(repo, opts, source)
3807 if 'bookmarks' not in other.listkeys('namespaces'):
3808 if 'bookmarks' not in other.listkeys('namespaces'):
3808 ui.warn(_("remote doesn't support bookmarks\n"))
3809 ui.warn(_("remote doesn't support bookmarks\n"))
3809 return 0
3810 return 0
3810 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3811 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3811 return bookmarks.diff(ui, repo, other)
3812 return bookmarks.diff(ui, repo, other)
3812
3813
3813 repo._subtoppath = ui.expandpath(source)
3814 repo._subtoppath = ui.expandpath(source)
3814 try:
3815 try:
3815 return hg.incoming(ui, repo, source, opts)
3816 return hg.incoming(ui, repo, source, opts)
3816 finally:
3817 finally:
3817 del repo._subtoppath
3818 del repo._subtoppath
3818
3819
3819
3820
3820 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'))
3821 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'))
3821 def init(ui, dest=".", **opts):
3822 def init(ui, dest=".", **opts):
3822 """create a new repository in the given directory
3823 """create a new repository in the given directory
3823
3824
3824 Initialize a new repository in the given directory. If the given
3825 Initialize a new repository in the given directory. If the given
3825 directory does not exist, it will be created.
3826 directory does not exist, it will be created.
3826
3827
3827 If no directory is given, the current directory is used.
3828 If no directory is given, the current directory is used.
3828
3829
3829 It is possible to specify an ``ssh://`` URL as the destination.
3830 It is possible to specify an ``ssh://`` URL as the destination.
3830 See :hg:`help urls` for more information.
3831 See :hg:`help urls` for more information.
3831
3832
3832 Returns 0 on success.
3833 Returns 0 on success.
3833 """
3834 """
3834 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3835 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3835
3836
3836 @command('locate',
3837 @command('locate',
3837 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3838 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3838 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3839 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3839 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3840 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3840 ] + walkopts,
3841 ] + walkopts,
3841 _('[OPTION]... [PATTERN]...'))
3842 _('[OPTION]... [PATTERN]...'))
3842 def locate(ui, repo, *pats, **opts):
3843 def locate(ui, repo, *pats, **opts):
3843 """locate files matching specific patterns
3844 """locate files matching specific patterns
3844
3845
3845 Print files under Mercurial control in the working directory whose
3846 Print files under Mercurial control in the working directory whose
3846 names match the given patterns.
3847 names match the given patterns.
3847
3848
3848 By default, this command searches all directories in the working
3849 By default, this command searches all directories in the working
3849 directory. To search just the current directory and its
3850 directory. To search just the current directory and its
3850 subdirectories, use "--include .".
3851 subdirectories, use "--include .".
3851
3852
3852 If no patterns are given to match, this command prints the names
3853 If no patterns are given to match, this command prints the names
3853 of all files under Mercurial control in the working directory.
3854 of all files under Mercurial control in the working directory.
3854
3855
3855 If you want to feed the output of this command into the "xargs"
3856 If you want to feed the output of this command into the "xargs"
3856 command, use the -0 option to both this command and "xargs". This
3857 command, use the -0 option to both this command and "xargs". This
3857 will avoid the problem of "xargs" treating single filenames that
3858 will avoid the problem of "xargs" treating single filenames that
3858 contain whitespace as multiple filenames.
3859 contain whitespace as multiple filenames.
3859
3860
3860 Returns 0 if a match is found, 1 otherwise.
3861 Returns 0 if a match is found, 1 otherwise.
3861 """
3862 """
3862 end = opts.get('print0') and '\0' or '\n'
3863 end = opts.get('print0') and '\0' or '\n'
3863 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3864 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3864
3865
3865 ret = 1
3866 ret = 1
3866 m = scmutil.match(repo[rev], pats, opts, default='relglob')
3867 m = scmutil.match(repo[rev], pats, opts, default='relglob')
3867 m.bad = lambda x, y: False
3868 m.bad = lambda x, y: False
3868 for abs in repo[rev].walk(m):
3869 for abs in repo[rev].walk(m):
3869 if not rev and abs not in repo.dirstate:
3870 if not rev and abs not in repo.dirstate:
3870 continue
3871 continue
3871 if opts.get('fullpath'):
3872 if opts.get('fullpath'):
3872 ui.write(repo.wjoin(abs), end)
3873 ui.write(repo.wjoin(abs), end)
3873 else:
3874 else:
3874 ui.write(((pats and m.rel(abs)) or abs), end)
3875 ui.write(((pats and m.rel(abs)) or abs), end)
3875 ret = 0
3876 ret = 0
3876
3877
3877 return ret
3878 return ret
3878
3879
3879 @command('^log|history',
3880 @command('^log|history',
3880 [('f', 'follow', None,
3881 [('f', 'follow', None,
3881 _('follow changeset history, or file history across copies and renames')),
3882 _('follow changeset history, or file history across copies and renames')),
3882 ('', 'follow-first', None,
3883 ('', 'follow-first', None,
3883 _('only follow the first parent of merge changesets (DEPRECATED)')),
3884 _('only follow the first parent of merge changesets (DEPRECATED)')),
3884 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3885 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3885 ('C', 'copies', None, _('show copied files')),
3886 ('C', 'copies', None, _('show copied files')),
3886 ('k', 'keyword', [],
3887 ('k', 'keyword', [],
3887 _('do case-insensitive search for a given text'), _('TEXT')),
3888 _('do case-insensitive search for a given text'), _('TEXT')),
3888 ('r', 'rev', [], _('show the specified revision or range'), _('REV')),
3889 ('r', 'rev', [], _('show the specified revision or range'), _('REV')),
3889 ('', 'removed', None, _('include revisions where files were removed')),
3890 ('', 'removed', None, _('include revisions where files were removed')),
3890 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3891 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3891 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3892 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3892 ('', 'only-branch', [],
3893 ('', 'only-branch', [],
3893 _('show only changesets within the given named branch (DEPRECATED)'),
3894 _('show only changesets within the given named branch (DEPRECATED)'),
3894 _('BRANCH')),
3895 _('BRANCH')),
3895 ('b', 'branch', [],
3896 ('b', 'branch', [],
3896 _('show changesets within the given named branch'), _('BRANCH')),
3897 _('show changesets within the given named branch'), _('BRANCH')),
3897 ('P', 'prune', [],
3898 ('P', 'prune', [],
3898 _('do not display revision or any of its ancestors'), _('REV')),
3899 _('do not display revision or any of its ancestors'), _('REV')),
3899 ('', 'hidden', False, _('show hidden changesets (DEPRECATED)')),
3900 ('', 'hidden', False, _('show hidden changesets (DEPRECATED)')),
3900 ] + logopts + walkopts,
3901 ] + logopts + walkopts,
3901 _('[OPTION]... [FILE]'))
3902 _('[OPTION]... [FILE]'))
3902 def log(ui, repo, *pats, **opts):
3903 def log(ui, repo, *pats, **opts):
3903 """show revision history of entire repository or files
3904 """show revision history of entire repository or files
3904
3905
3905 Print the revision history of the specified files or the entire
3906 Print the revision history of the specified files or the entire
3906 project.
3907 project.
3907
3908
3908 If no revision range is specified, the default is ``tip:0`` unless
3909 If no revision range is specified, the default is ``tip:0`` unless
3909 --follow is set, in which case the working directory parent is
3910 --follow is set, in which case the working directory parent is
3910 used as the starting revision.
3911 used as the starting revision.
3911
3912
3912 File history is shown without following rename or copy history of
3913 File history is shown without following rename or copy history of
3913 files. Use -f/--follow with a filename to follow history across
3914 files. Use -f/--follow with a filename to follow history across
3914 renames and copies. --follow without a filename will only show
3915 renames and copies. --follow without a filename will only show
3915 ancestors or descendants of the starting revision.
3916 ancestors or descendants of the starting revision.
3916
3917
3917 By default this command prints revision number and changeset id,
3918 By default this command prints revision number and changeset id,
3918 tags, non-trivial parents, user, date and time, and a summary for
3919 tags, non-trivial parents, user, date and time, and a summary for
3919 each commit. When the -v/--verbose switch is used, the list of
3920 each commit. When the -v/--verbose switch is used, the list of
3920 changed files and full commit message are shown.
3921 changed files and full commit message are shown.
3921
3922
3922 .. note::
3923 .. note::
3923 log -p/--patch may generate unexpected diff output for merge
3924 log -p/--patch may generate unexpected diff output for merge
3924 changesets, as it will only compare the merge changeset against
3925 changesets, as it will only compare the merge changeset against
3925 its first parent. Also, only files different from BOTH parents
3926 its first parent. Also, only files different from BOTH parents
3926 will appear in files:.
3927 will appear in files:.
3927
3928
3928 .. note::
3929 .. note::
3929 for performance reasons, log FILE may omit duplicate changes
3930 for performance reasons, log FILE may omit duplicate changes
3930 made on branches and will not show deletions. To see all
3931 made on branches and will not show deletions. To see all
3931 changes including duplicates and deletions, use the --removed
3932 changes including duplicates and deletions, use the --removed
3932 switch.
3933 switch.
3933
3934
3934 .. container:: verbose
3935 .. container:: verbose
3935
3936
3936 Some examples:
3937 Some examples:
3937
3938
3938 - changesets with full descriptions and file lists::
3939 - changesets with full descriptions and file lists::
3939
3940
3940 hg log -v
3941 hg log -v
3941
3942
3942 - changesets ancestral to the working directory::
3943 - changesets ancestral to the working directory::
3943
3944
3944 hg log -f
3945 hg log -f
3945
3946
3946 - last 10 commits on the current branch::
3947 - last 10 commits on the current branch::
3947
3948
3948 hg log -l 10 -b .
3949 hg log -l 10 -b .
3949
3950
3950 - changesets showing all modifications of a file, including removals::
3951 - changesets showing all modifications of a file, including removals::
3951
3952
3952 hg log --removed file.c
3953 hg log --removed file.c
3953
3954
3954 - all changesets that touch a directory, with diffs, excluding merges::
3955 - all changesets that touch a directory, with diffs, excluding merges::
3955
3956
3956 hg log -Mp lib/
3957 hg log -Mp lib/
3957
3958
3958 - all revision numbers that match a keyword::
3959 - all revision numbers that match a keyword::
3959
3960
3960 hg log -k bug --template "{rev}\\n"
3961 hg log -k bug --template "{rev}\\n"
3961
3962
3962 - check if a given changeset is included is a tagged release::
3963 - check if a given changeset is included is a tagged release::
3963
3964
3964 hg log -r "a21ccf and ancestor(1.9)"
3965 hg log -r "a21ccf and ancestor(1.9)"
3965
3966
3966 - find all changesets by some user in a date range::
3967 - find all changesets by some user in a date range::
3967
3968
3968 hg log -k alice -d "may 2008 to jul 2008"
3969 hg log -k alice -d "may 2008 to jul 2008"
3969
3970
3970 - summary of all changesets after the last tag::
3971 - summary of all changesets after the last tag::
3971
3972
3972 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3973 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3973
3974
3974 See :hg:`help dates` for a list of formats valid for -d/--date.
3975 See :hg:`help dates` for a list of formats valid for -d/--date.
3975
3976
3976 See :hg:`help revisions` and :hg:`help revsets` for more about
3977 See :hg:`help revisions` and :hg:`help revsets` for more about
3977 specifying revisions.
3978 specifying revisions.
3978
3979
3979 See :hg:`help templates` for more about pre-packaged styles and
3980 See :hg:`help templates` for more about pre-packaged styles and
3980 specifying custom templates.
3981 specifying custom templates.
3981
3982
3982 Returns 0 on success.
3983 Returns 0 on success.
3983 """
3984 """
3984
3985
3985 matchfn = scmutil.match(repo[None], pats, opts)
3986 matchfn = scmutil.match(repo[None], pats, opts)
3986 limit = cmdutil.loglimit(opts)
3987 limit = cmdutil.loglimit(opts)
3987 count = 0
3988 count = 0
3988
3989
3989 getrenamed, endrev = None, None
3990 getrenamed, endrev = None, None
3990 if opts.get('copies'):
3991 if opts.get('copies'):
3991 if opts.get('rev'):
3992 if opts.get('rev'):
3992 endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1
3993 endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1
3993 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3994 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3994
3995
3995 df = False
3996 df = False
3996 if opts["date"]:
3997 if opts["date"]:
3997 df = util.matchdate(opts["date"])
3998 df = util.matchdate(opts["date"])
3998
3999
3999 branches = opts.get('branch', []) + opts.get('only_branch', [])
4000 branches = opts.get('branch', []) + opts.get('only_branch', [])
4000 opts['branch'] = [repo.lookupbranch(b) for b in branches]
4001 opts['branch'] = [repo.lookupbranch(b) for b in branches]
4001
4002
4002 displayer = cmdutil.show_changeset(ui, repo, opts, True)
4003 displayer = cmdutil.show_changeset(ui, repo, opts, True)
4003 def prep(ctx, fns):
4004 def prep(ctx, fns):
4004 rev = ctx.rev()
4005 rev = ctx.rev()
4005 parents = [p for p in repo.changelog.parentrevs(rev)
4006 parents = [p for p in repo.changelog.parentrevs(rev)
4006 if p != nullrev]
4007 if p != nullrev]
4007 if opts.get('no_merges') and len(parents) == 2:
4008 if opts.get('no_merges') and len(parents) == 2:
4008 return
4009 return
4009 if opts.get('only_merges') and len(parents) != 2:
4010 if opts.get('only_merges') and len(parents) != 2:
4010 return
4011 return
4011 if opts.get('branch') and ctx.branch() not in opts['branch']:
4012 if opts.get('branch') and ctx.branch() not in opts['branch']:
4012 return
4013 return
4013 if not opts.get('hidden') and ctx.hidden():
4014 if not opts.get('hidden') and ctx.hidden():
4014 return
4015 return
4015 if df and not df(ctx.date()[0]):
4016 if df and not df(ctx.date()[0]):
4016 return
4017 return
4017
4018
4018 lower = encoding.lower
4019 lower = encoding.lower
4019 if opts.get('user'):
4020 if opts.get('user'):
4020 luser = lower(ctx.user())
4021 luser = lower(ctx.user())
4021 for k in [lower(x) for x in opts['user']]:
4022 for k in [lower(x) for x in opts['user']]:
4022 if (k in luser):
4023 if (k in luser):
4023 break
4024 break
4024 else:
4025 else:
4025 return
4026 return
4026 if opts.get('keyword'):
4027 if opts.get('keyword'):
4027 luser = lower(ctx.user())
4028 luser = lower(ctx.user())
4028 ldesc = lower(ctx.description())
4029 ldesc = lower(ctx.description())
4029 lfiles = lower(" ".join(ctx.files()))
4030 lfiles = lower(" ".join(ctx.files()))
4030 for k in [lower(x) for x in opts['keyword']]:
4031 for k in [lower(x) for x in opts['keyword']]:
4031 if (k in luser or k in ldesc or k in lfiles):
4032 if (k in luser or k in ldesc or k in lfiles):
4032 break
4033 break
4033 else:
4034 else:
4034 return
4035 return
4035
4036
4036 copies = None
4037 copies = None
4037 if getrenamed is not None and rev:
4038 if getrenamed is not None and rev:
4038 copies = []
4039 copies = []
4039 for fn in ctx.files():
4040 for fn in ctx.files():
4040 rename = getrenamed(fn, rev)
4041 rename = getrenamed(fn, rev)
4041 if rename:
4042 if rename:
4042 copies.append((fn, rename[0]))
4043 copies.append((fn, rename[0]))
4043
4044
4044 revmatchfn = None
4045 revmatchfn = None
4045 if opts.get('patch') or opts.get('stat'):
4046 if opts.get('patch') or opts.get('stat'):
4046 if opts.get('follow') or opts.get('follow_first'):
4047 if opts.get('follow') or opts.get('follow_first'):
4047 # note: this might be wrong when following through merges
4048 # note: this might be wrong when following through merges
4048 revmatchfn = scmutil.match(repo[None], fns, default='path')
4049 revmatchfn = scmutil.match(repo[None], fns, default='path')
4049 else:
4050 else:
4050 revmatchfn = matchfn
4051 revmatchfn = matchfn
4051
4052
4052 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
4053 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
4053
4054
4054 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
4055 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
4055 if count == limit:
4056 if count == limit:
4056 break
4057 break
4057 if displayer.flush(ctx.rev()):
4058 if displayer.flush(ctx.rev()):
4058 count += 1
4059 count += 1
4059 displayer.close()
4060 displayer.close()
4060
4061
4061 @command('manifest',
4062 @command('manifest',
4062 [('r', 'rev', '', _('revision to display'), _('REV')),
4063 [('r', 'rev', '', _('revision to display'), _('REV')),
4063 ('', 'all', False, _("list files from all revisions"))],
4064 ('', 'all', False, _("list files from all revisions"))],
4064 _('[-r REV]'))
4065 _('[-r REV]'))
4065 def manifest(ui, repo, node=None, rev=None, **opts):
4066 def manifest(ui, repo, node=None, rev=None, **opts):
4066 """output the current or given revision of the project manifest
4067 """output the current or given revision of the project manifest
4067
4068
4068 Print a list of version controlled files for the given revision.
4069 Print a list of version controlled files for the given revision.
4069 If no revision is given, the first parent of the working directory
4070 If no revision is given, the first parent of the working directory
4070 is used, or the null revision if no revision is checked out.
4071 is used, or the null revision if no revision is checked out.
4071
4072
4072 With -v, print file permissions, symlink and executable bits.
4073 With -v, print file permissions, symlink and executable bits.
4073 With --debug, print file revision hashes.
4074 With --debug, print file revision hashes.
4074
4075
4075 If option --all is specified, the list of all files from all revisions
4076 If option --all is specified, the list of all files from all revisions
4076 is printed. This includes deleted and renamed files.
4077 is printed. This includes deleted and renamed files.
4077
4078
4078 Returns 0 on success.
4079 Returns 0 on success.
4079 """
4080 """
4080 if opts.get('all'):
4081 if opts.get('all'):
4081 if rev or node:
4082 if rev or node:
4082 raise util.Abort(_("can't specify a revision with --all"))
4083 raise util.Abort(_("can't specify a revision with --all"))
4083
4084
4084 res = []
4085 res = []
4085 prefix = "data/"
4086 prefix = "data/"
4086 suffix = ".i"
4087 suffix = ".i"
4087 plen = len(prefix)
4088 plen = len(prefix)
4088 slen = len(suffix)
4089 slen = len(suffix)
4089 lock = repo.lock()
4090 lock = repo.lock()
4090 try:
4091 try:
4091 for fn, b, size in repo.store.datafiles():
4092 for fn, b, size in repo.store.datafiles():
4092 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
4093 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
4093 res.append(fn[plen:-slen])
4094 res.append(fn[plen:-slen])
4094 finally:
4095 finally:
4095 lock.release()
4096 lock.release()
4096 for f in sorted(res):
4097 for f in sorted(res):
4097 ui.write("%s\n" % f)
4098 ui.write("%s\n" % f)
4098 return
4099 return
4099
4100
4100 if rev and node:
4101 if rev and node:
4101 raise util.Abort(_("please specify just one revision"))
4102 raise util.Abort(_("please specify just one revision"))
4102
4103
4103 if not node:
4104 if not node:
4104 node = rev
4105 node = rev
4105
4106
4106 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
4107 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
4107 ctx = scmutil.revsingle(repo, node)
4108 ctx = scmutil.revsingle(repo, node)
4108 for f in ctx:
4109 for f in ctx:
4109 if ui.debugflag:
4110 if ui.debugflag:
4110 ui.write("%40s " % hex(ctx.manifest()[f]))
4111 ui.write("%40s " % hex(ctx.manifest()[f]))
4111 if ui.verbose:
4112 if ui.verbose:
4112 ui.write(decor[ctx.flags(f)])
4113 ui.write(decor[ctx.flags(f)])
4113 ui.write("%s\n" % f)
4114 ui.write("%s\n" % f)
4114
4115
4115 @command('^merge',
4116 @command('^merge',
4116 [('f', 'force', None, _('force a merge with outstanding changes')),
4117 [('f', 'force', None, _('force a merge with outstanding changes')),
4117 ('r', 'rev', '', _('revision to merge'), _('REV')),
4118 ('r', 'rev', '', _('revision to merge'), _('REV')),
4118 ('P', 'preview', None,
4119 ('P', 'preview', None,
4119 _('review revisions to merge (no merge is performed)'))
4120 _('review revisions to merge (no merge is performed)'))
4120 ] + mergetoolopts,
4121 ] + mergetoolopts,
4121 _('[-P] [-f] [[-r] REV]'))
4122 _('[-P] [-f] [[-r] REV]'))
4122 def merge(ui, repo, node=None, **opts):
4123 def merge(ui, repo, node=None, **opts):
4123 """merge working directory with another revision
4124 """merge working directory with another revision
4124
4125
4125 The current working directory is updated with all changes made in
4126 The current working directory is updated with all changes made in
4126 the requested revision since the last common predecessor revision.
4127 the requested revision since the last common predecessor revision.
4127
4128
4128 Files that changed between either parent are marked as changed for
4129 Files that changed between either parent are marked as changed for
4129 the next commit and a commit must be performed before any further
4130 the next commit and a commit must be performed before any further
4130 updates to the repository are allowed. The next commit will have
4131 updates to the repository are allowed. The next commit will have
4131 two parents.
4132 two parents.
4132
4133
4133 ``--tool`` can be used to specify the merge tool used for file
4134 ``--tool`` can be used to specify the merge tool used for file
4134 merges. It overrides the HGMERGE environment variable and your
4135 merges. It overrides the HGMERGE environment variable and your
4135 configuration files. See :hg:`help merge-tools` for options.
4136 configuration files. See :hg:`help merge-tools` for options.
4136
4137
4137 If no revision is specified, the working directory's parent is a
4138 If no revision is specified, the working directory's parent is a
4138 head revision, and the current branch contains exactly one other
4139 head revision, and the current branch contains exactly one other
4139 head, the other head is merged with by default. Otherwise, an
4140 head, the other head is merged with by default. Otherwise, an
4140 explicit revision with which to merge with must be provided.
4141 explicit revision with which to merge with must be provided.
4141
4142
4142 :hg:`resolve` must be used to resolve unresolved files.
4143 :hg:`resolve` must be used to resolve unresolved files.
4143
4144
4144 To undo an uncommitted merge, use :hg:`update --clean .` which
4145 To undo an uncommitted merge, use :hg:`update --clean .` which
4145 will check out a clean copy of the original merge parent, losing
4146 will check out a clean copy of the original merge parent, losing
4146 all changes.
4147 all changes.
4147
4148
4148 Returns 0 on success, 1 if there are unresolved files.
4149 Returns 0 on success, 1 if there are unresolved files.
4149 """
4150 """
4150
4151
4151 if opts.get('rev') and node:
4152 if opts.get('rev') and node:
4152 raise util.Abort(_("please specify just one revision"))
4153 raise util.Abort(_("please specify just one revision"))
4153 if not node:
4154 if not node:
4154 node = opts.get('rev')
4155 node = opts.get('rev')
4155
4156
4156 if node:
4157 if node:
4157 node = scmutil.revsingle(repo, node).node()
4158 node = scmutil.revsingle(repo, node).node()
4158
4159
4159 if not node and repo._bookmarkcurrent:
4160 if not node and repo._bookmarkcurrent:
4160 bmheads = repo.bookmarkheads(repo._bookmarkcurrent)
4161 bmheads = repo.bookmarkheads(repo._bookmarkcurrent)
4161 curhead = repo[repo._bookmarkcurrent]
4162 curhead = repo[repo._bookmarkcurrent]
4162 if len(bmheads) == 2:
4163 if len(bmheads) == 2:
4163 if curhead == bmheads[0]:
4164 if curhead == bmheads[0]:
4164 node = bmheads[1]
4165 node = bmheads[1]
4165 else:
4166 else:
4166 node = bmheads[0]
4167 node = bmheads[0]
4167 elif len(bmheads) > 2:
4168 elif len(bmheads) > 2:
4168 raise util.Abort(_("multiple matching bookmarks to merge - "
4169 raise util.Abort(_("multiple matching bookmarks to merge - "
4169 "please merge with an explicit rev or bookmark"),
4170 "please merge with an explicit rev or bookmark"),
4170 hint=_("run 'hg heads' to see all heads"))
4171 hint=_("run 'hg heads' to see all heads"))
4171 elif len(bmheads) <= 1:
4172 elif len(bmheads) <= 1:
4172 raise util.Abort(_("no matching bookmark to merge - "
4173 raise util.Abort(_("no matching bookmark to merge - "
4173 "please merge with an explicit rev or bookmark"),
4174 "please merge with an explicit rev or bookmark"),
4174 hint=_("run 'hg heads' to see all heads"))
4175 hint=_("run 'hg heads' to see all heads"))
4175
4176
4176 if not node and not repo._bookmarkcurrent:
4177 if not node and not repo._bookmarkcurrent:
4177 branch = repo[None].branch()
4178 branch = repo[None].branch()
4178 bheads = repo.branchheads(branch)
4179 bheads = repo.branchheads(branch)
4179 nbhs = [bh for bh in bheads if not repo[bh].bookmarks()]
4180 nbhs = [bh for bh in bheads if not repo[bh].bookmarks()]
4180
4181
4181 if len(nbhs) > 2:
4182 if len(nbhs) > 2:
4182 raise util.Abort(_("branch '%s' has %d heads - "
4183 raise util.Abort(_("branch '%s' has %d heads - "
4183 "please merge with an explicit rev")
4184 "please merge with an explicit rev")
4184 % (branch, len(bheads)),
4185 % (branch, len(bheads)),
4185 hint=_("run 'hg heads .' to see heads"))
4186 hint=_("run 'hg heads .' to see heads"))
4186
4187
4187 parent = repo.dirstate.p1()
4188 parent = repo.dirstate.p1()
4188 if len(nbhs) == 1:
4189 if len(nbhs) == 1:
4189 if len(bheads) > 1:
4190 if len(bheads) > 1:
4190 raise util.Abort(_("heads are bookmarked - "
4191 raise util.Abort(_("heads are bookmarked - "
4191 "please merge with an explicit rev"),
4192 "please merge with an explicit rev"),
4192 hint=_("run 'hg heads' to see all heads"))
4193 hint=_("run 'hg heads' to see all heads"))
4193 if len(repo.heads()) > 1:
4194 if len(repo.heads()) > 1:
4194 raise util.Abort(_("branch '%s' has one head - "
4195 raise util.Abort(_("branch '%s' has one head - "
4195 "please merge with an explicit rev")
4196 "please merge with an explicit rev")
4196 % branch,
4197 % branch,
4197 hint=_("run 'hg heads' to see all heads"))
4198 hint=_("run 'hg heads' to see all heads"))
4198 msg, hint = _('nothing to merge'), None
4199 msg, hint = _('nothing to merge'), None
4199 if parent != repo.lookup(branch):
4200 if parent != repo.lookup(branch):
4200 hint = _("use 'hg update' instead")
4201 hint = _("use 'hg update' instead")
4201 raise util.Abort(msg, hint=hint)
4202 raise util.Abort(msg, hint=hint)
4202
4203
4203 if parent not in bheads:
4204 if parent not in bheads:
4204 raise util.Abort(_('working directory not at a head revision'),
4205 raise util.Abort(_('working directory not at a head revision'),
4205 hint=_("use 'hg update' or merge with an "
4206 hint=_("use 'hg update' or merge with an "
4206 "explicit revision"))
4207 "explicit revision"))
4207 if parent == nbhs[0]:
4208 if parent == nbhs[0]:
4208 node = nbhs[-1]
4209 node = nbhs[-1]
4209 else:
4210 else:
4210 node = nbhs[0]
4211 node = nbhs[0]
4211
4212
4212 if opts.get('preview'):
4213 if opts.get('preview'):
4213 # find nodes that are ancestors of p2 but not of p1
4214 # find nodes that are ancestors of p2 but not of p1
4214 p1 = repo.lookup('.')
4215 p1 = repo.lookup('.')
4215 p2 = repo.lookup(node)
4216 p2 = repo.lookup(node)
4216 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4217 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4217
4218
4218 displayer = cmdutil.show_changeset(ui, repo, opts)
4219 displayer = cmdutil.show_changeset(ui, repo, opts)
4219 for node in nodes:
4220 for node in nodes:
4220 displayer.show(repo[node])
4221 displayer.show(repo[node])
4221 displayer.close()
4222 displayer.close()
4222 return 0
4223 return 0
4223
4224
4224 try:
4225 try:
4225 # ui.forcemerge is an internal variable, do not document
4226 # ui.forcemerge is an internal variable, do not document
4226 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4227 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4227 return hg.merge(repo, node, force=opts.get('force'))
4228 return hg.merge(repo, node, force=opts.get('force'))
4228 finally:
4229 finally:
4229 ui.setconfig('ui', 'forcemerge', '')
4230 ui.setconfig('ui', 'forcemerge', '')
4230
4231
4231 @command('outgoing|out',
4232 @command('outgoing|out',
4232 [('f', 'force', None, _('run even when the destination is unrelated')),
4233 [('f', 'force', None, _('run even when the destination is unrelated')),
4233 ('r', 'rev', [],
4234 ('r', 'rev', [],
4234 _('a changeset intended to be included in the destination'), _('REV')),
4235 _('a changeset intended to be included in the destination'), _('REV')),
4235 ('n', 'newest-first', None, _('show newest record first')),
4236 ('n', 'newest-first', None, _('show newest record first')),
4236 ('B', 'bookmarks', False, _('compare bookmarks')),
4237 ('B', 'bookmarks', False, _('compare bookmarks')),
4237 ('b', 'branch', [], _('a specific branch you would like to push'),
4238 ('b', 'branch', [], _('a specific branch you would like to push'),
4238 _('BRANCH')),
4239 _('BRANCH')),
4239 ] + logopts + remoteopts + subrepoopts,
4240 ] + logopts + remoteopts + subrepoopts,
4240 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4241 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4241 def outgoing(ui, repo, dest=None, **opts):
4242 def outgoing(ui, repo, dest=None, **opts):
4242 """show changesets not found in the destination
4243 """show changesets not found in the destination
4243
4244
4244 Show changesets not found in the specified destination repository
4245 Show changesets not found in the specified destination repository
4245 or the default push location. These are the changesets that would
4246 or the default push location. These are the changesets that would
4246 be pushed if a push was requested.
4247 be pushed if a push was requested.
4247
4248
4248 See pull for details of valid destination formats.
4249 See pull for details of valid destination formats.
4249
4250
4250 Returns 0 if there are outgoing changes, 1 otherwise.
4251 Returns 0 if there are outgoing changes, 1 otherwise.
4251 """
4252 """
4252
4253
4253 if opts.get('bookmarks'):
4254 if opts.get('bookmarks'):
4254 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4255 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4255 dest, branches = hg.parseurl(dest, opts.get('branch'))
4256 dest, branches = hg.parseurl(dest, opts.get('branch'))
4256 other = hg.peer(repo, opts, dest)
4257 other = hg.peer(repo, opts, dest)
4257 if 'bookmarks' not in other.listkeys('namespaces'):
4258 if 'bookmarks' not in other.listkeys('namespaces'):
4258 ui.warn(_("remote doesn't support bookmarks\n"))
4259 ui.warn(_("remote doesn't support bookmarks\n"))
4259 return 0
4260 return 0
4260 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4261 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4261 return bookmarks.diff(ui, other, repo)
4262 return bookmarks.diff(ui, other, repo)
4262
4263
4263 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4264 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4264 try:
4265 try:
4265 return hg.outgoing(ui, repo, dest, opts)
4266 return hg.outgoing(ui, repo, dest, opts)
4266 finally:
4267 finally:
4267 del repo._subtoppath
4268 del repo._subtoppath
4268
4269
4269 @command('parents',
4270 @command('parents',
4270 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4271 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4271 ] + templateopts,
4272 ] + templateopts,
4272 _('[-r REV] [FILE]'))
4273 _('[-r REV] [FILE]'))
4273 def parents(ui, repo, file_=None, **opts):
4274 def parents(ui, repo, file_=None, **opts):
4274 """show the parents of the working directory or revision
4275 """show the parents of the working directory or revision
4275
4276
4276 Print the working directory's parent revisions. If a revision is
4277 Print the working directory's parent revisions. If a revision is
4277 given via -r/--rev, the parent of that revision will be printed.
4278 given via -r/--rev, the parent of that revision will be printed.
4278 If a file argument is given, the revision in which the file was
4279 If a file argument is given, the revision in which the file was
4279 last changed (before the working directory revision or the
4280 last changed (before the working directory revision or the
4280 argument to --rev if given) is printed.
4281 argument to --rev if given) is printed.
4281
4282
4282 Returns 0 on success.
4283 Returns 0 on success.
4283 """
4284 """
4284
4285
4285 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4286 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4286
4287
4287 if file_:
4288 if file_:
4288 m = scmutil.match(ctx, (file_,), opts)
4289 m = scmutil.match(ctx, (file_,), opts)
4289 if m.anypats() or len(m.files()) != 1:
4290 if m.anypats() or len(m.files()) != 1:
4290 raise util.Abort(_('can only specify an explicit filename'))
4291 raise util.Abort(_('can only specify an explicit filename'))
4291 file_ = m.files()[0]
4292 file_ = m.files()[0]
4292 filenodes = []
4293 filenodes = []
4293 for cp in ctx.parents():
4294 for cp in ctx.parents():
4294 if not cp:
4295 if not cp:
4295 continue
4296 continue
4296 try:
4297 try:
4297 filenodes.append(cp.filenode(file_))
4298 filenodes.append(cp.filenode(file_))
4298 except error.LookupError:
4299 except error.LookupError:
4299 pass
4300 pass
4300 if not filenodes:
4301 if not filenodes:
4301 raise util.Abort(_("'%s' not found in manifest!") % file_)
4302 raise util.Abort(_("'%s' not found in manifest!") % file_)
4302 fl = repo.file(file_)
4303 fl = repo.file(file_)
4303 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
4304 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
4304 else:
4305 else:
4305 p = [cp.node() for cp in ctx.parents()]
4306 p = [cp.node() for cp in ctx.parents()]
4306
4307
4307 displayer = cmdutil.show_changeset(ui, repo, opts)
4308 displayer = cmdutil.show_changeset(ui, repo, opts)
4308 for n in p:
4309 for n in p:
4309 if n != nullid:
4310 if n != nullid:
4310 displayer.show(repo[n])
4311 displayer.show(repo[n])
4311 displayer.close()
4312 displayer.close()
4312
4313
4313 @command('paths', [], _('[NAME]'))
4314 @command('paths', [], _('[NAME]'))
4314 def paths(ui, repo, search=None):
4315 def paths(ui, repo, search=None):
4315 """show aliases for remote repositories
4316 """show aliases for remote repositories
4316
4317
4317 Show definition of symbolic path name NAME. If no name is given,
4318 Show definition of symbolic path name NAME. If no name is given,
4318 show definition of all available names.
4319 show definition of all available names.
4319
4320
4320 Option -q/--quiet suppresses all output when searching for NAME
4321 Option -q/--quiet suppresses all output when searching for NAME
4321 and shows only the path names when listing all definitions.
4322 and shows only the path names when listing all definitions.
4322
4323
4323 Path names are defined in the [paths] section of your
4324 Path names are defined in the [paths] section of your
4324 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4325 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4325 repository, ``.hg/hgrc`` is used, too.
4326 repository, ``.hg/hgrc`` is used, too.
4326
4327
4327 The path names ``default`` and ``default-push`` have a special
4328 The path names ``default`` and ``default-push`` have a special
4328 meaning. When performing a push or pull operation, they are used
4329 meaning. When performing a push or pull operation, they are used
4329 as fallbacks if no location is specified on the command-line.
4330 as fallbacks if no location is specified on the command-line.
4330 When ``default-push`` is set, it will be used for push and
4331 When ``default-push`` is set, it will be used for push and
4331 ``default`` will be used for pull; otherwise ``default`` is used
4332 ``default`` will be used for pull; otherwise ``default`` is used
4332 as the fallback for both. When cloning a repository, the clone
4333 as the fallback for both. When cloning a repository, the clone
4333 source is written as ``default`` in ``.hg/hgrc``. Note that
4334 source is written as ``default`` in ``.hg/hgrc``. Note that
4334 ``default`` and ``default-push`` apply to all inbound (e.g.
4335 ``default`` and ``default-push`` apply to all inbound (e.g.
4335 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
4336 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
4336 :hg:`bundle`) operations.
4337 :hg:`bundle`) operations.
4337
4338
4338 See :hg:`help urls` for more information.
4339 See :hg:`help urls` for more information.
4339
4340
4340 Returns 0 on success.
4341 Returns 0 on success.
4341 """
4342 """
4342 if search:
4343 if search:
4343 for name, path in ui.configitems("paths"):
4344 for name, path in ui.configitems("paths"):
4344 if name == search:
4345 if name == search:
4345 ui.status("%s\n" % util.hidepassword(path))
4346 ui.status("%s\n" % util.hidepassword(path))
4346 return
4347 return
4347 if not ui.quiet:
4348 if not ui.quiet:
4348 ui.warn(_("not found!\n"))
4349 ui.warn(_("not found!\n"))
4349 return 1
4350 return 1
4350 else:
4351 else:
4351 for name, path in ui.configitems("paths"):
4352 for name, path in ui.configitems("paths"):
4352 if ui.quiet:
4353 if ui.quiet:
4353 ui.write("%s\n" % name)
4354 ui.write("%s\n" % name)
4354 else:
4355 else:
4355 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
4356 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
4356
4357
4357 @command('^phase',
4358 @command('^phase',
4358 [('p', 'public', False, _('set changeset phase to public')),
4359 [('p', 'public', False, _('set changeset phase to public')),
4359 ('d', 'draft', False, _('set changeset phase to draft')),
4360 ('d', 'draft', False, _('set changeset phase to draft')),
4360 ('s', 'secret', False, _('set changeset phase to secret')),
4361 ('s', 'secret', False, _('set changeset phase to secret')),
4361 ('f', 'force', False, _('allow to move boundary backward')),
4362 ('f', 'force', False, _('allow to move boundary backward')),
4362 ('r', 'rev', [], _('target revision'), _('REV')),
4363 ('r', 'rev', [], _('target revision'), _('REV')),
4363 ],
4364 ],
4364 _('[-p|-d|-s] [-f] [-r] REV...'))
4365 _('[-p|-d|-s] [-f] [-r] REV...'))
4365 def phase(ui, repo, *revs, **opts):
4366 def phase(ui, repo, *revs, **opts):
4366 """set or show the current phase name
4367 """set or show the current phase name
4367
4368
4368 With no argument, show the phase name of specified revisions.
4369 With no argument, show the phase name of specified revisions.
4369
4370
4370 With one of -p/--public, -d/--draft or -s/--secret, change the
4371 With one of -p/--public, -d/--draft or -s/--secret, change the
4371 phase value of the specified revisions.
4372 phase value of the specified revisions.
4372
4373
4373 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
4374 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
4374 lower phase to an higher phase. Phases are ordered as follows::
4375 lower phase to an higher phase. Phases are ordered as follows::
4375
4376
4376 public < draft < secret
4377 public < draft < secret
4377
4378
4378 Return 0 on success, 1 if no phases were changed or some could not
4379 Return 0 on success, 1 if no phases were changed or some could not
4379 be changed.
4380 be changed.
4380 """
4381 """
4381 # search for a unique phase argument
4382 # search for a unique phase argument
4382 targetphase = None
4383 targetphase = None
4383 for idx, name in enumerate(phases.phasenames):
4384 for idx, name in enumerate(phases.phasenames):
4384 if opts[name]:
4385 if opts[name]:
4385 if targetphase is not None:
4386 if targetphase is not None:
4386 raise util.Abort(_('only one phase can be specified'))
4387 raise util.Abort(_('only one phase can be specified'))
4387 targetphase = idx
4388 targetphase = idx
4388
4389
4389 # look for specified revision
4390 # look for specified revision
4390 revs = list(revs)
4391 revs = list(revs)
4391 revs.extend(opts['rev'])
4392 revs.extend(opts['rev'])
4392 if not revs:
4393 if not revs:
4393 raise util.Abort(_('no revisions specified'))
4394 raise util.Abort(_('no revisions specified'))
4394
4395
4395 revs = scmutil.revrange(repo, revs)
4396 revs = scmutil.revrange(repo, revs)
4396
4397
4397 lock = None
4398 lock = None
4398 ret = 0
4399 ret = 0
4399 if targetphase is None:
4400 if targetphase is None:
4400 # display
4401 # display
4401 for r in revs:
4402 for r in revs:
4402 ctx = repo[r]
4403 ctx = repo[r]
4403 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4404 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4404 else:
4405 else:
4405 lock = repo.lock()
4406 lock = repo.lock()
4406 try:
4407 try:
4407 # set phase
4408 # set phase
4408 if not revs:
4409 if not revs:
4409 raise util.Abort(_('empty revision set'))
4410 raise util.Abort(_('empty revision set'))
4410 nodes = [repo[r].node() for r in revs]
4411 nodes = [repo[r].node() for r in revs]
4411 olddata = repo._phasecache.getphaserevs(repo)[:]
4412 olddata = repo._phasecache.getphaserevs(repo)[:]
4412 phases.advanceboundary(repo, targetphase, nodes)
4413 phases.advanceboundary(repo, targetphase, nodes)
4413 if opts['force']:
4414 if opts['force']:
4414 phases.retractboundary(repo, targetphase, nodes)
4415 phases.retractboundary(repo, targetphase, nodes)
4415 finally:
4416 finally:
4416 lock.release()
4417 lock.release()
4417 newdata = repo._phasecache.getphaserevs(repo)
4418 newdata = repo._phasecache.getphaserevs(repo)
4418 changes = sum(o != newdata[i] for i, o in enumerate(olddata))
4419 changes = sum(o != newdata[i] for i, o in enumerate(olddata))
4419 rejected = [n for n in nodes
4420 rejected = [n for n in nodes
4420 if newdata[repo[n].rev()] < targetphase]
4421 if newdata[repo[n].rev()] < targetphase]
4421 if rejected:
4422 if rejected:
4422 ui.warn(_('cannot move %i changesets to a more permissive '
4423 ui.warn(_('cannot move %i changesets to a more permissive '
4423 'phase, use --force\n') % len(rejected))
4424 'phase, use --force\n') % len(rejected))
4424 ret = 1
4425 ret = 1
4425 if changes:
4426 if changes:
4426 msg = _('phase changed for %i changesets\n') % changes
4427 msg = _('phase changed for %i changesets\n') % changes
4427 if ret:
4428 if ret:
4428 ui.status(msg)
4429 ui.status(msg)
4429 else:
4430 else:
4430 ui.note(msg)
4431 ui.note(msg)
4431 else:
4432 else:
4432 ui.warn(_('no phases changed\n'))
4433 ui.warn(_('no phases changed\n'))
4433 ret = 1
4434 ret = 1
4434 return ret
4435 return ret
4435
4436
4436 def postincoming(ui, repo, modheads, optupdate, checkout):
4437 def postincoming(ui, repo, modheads, optupdate, checkout):
4437 if modheads == 0:
4438 if modheads == 0:
4438 return
4439 return
4439 if optupdate:
4440 if optupdate:
4440 movemarkfrom = repo['.'].node()
4441 movemarkfrom = repo['.'].node()
4441 try:
4442 try:
4442 ret = hg.update(repo, checkout)
4443 ret = hg.update(repo, checkout)
4443 except util.Abort, inst:
4444 except util.Abort, inst:
4444 ui.warn(_("not updating: %s\n") % str(inst))
4445 ui.warn(_("not updating: %s\n") % str(inst))
4445 return 0
4446 return 0
4446 if not ret and not checkout:
4447 if not ret and not checkout:
4447 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
4448 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
4448 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
4449 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
4449 return ret
4450 return ret
4450 if modheads > 1:
4451 if modheads > 1:
4451 currentbranchheads = len(repo.branchheads())
4452 currentbranchheads = len(repo.branchheads())
4452 if currentbranchheads == modheads:
4453 if currentbranchheads == modheads:
4453 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4454 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4454 elif currentbranchheads > 1:
4455 elif currentbranchheads > 1:
4455 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4456 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4456 "merge)\n"))
4457 "merge)\n"))
4457 else:
4458 else:
4458 ui.status(_("(run 'hg heads' to see heads)\n"))
4459 ui.status(_("(run 'hg heads' to see heads)\n"))
4459 else:
4460 else:
4460 ui.status(_("(run 'hg update' to get a working copy)\n"))
4461 ui.status(_("(run 'hg update' to get a working copy)\n"))
4461
4462
4462 @command('^pull',
4463 @command('^pull',
4463 [('u', 'update', None,
4464 [('u', 'update', None,
4464 _('update to new branch head if changesets were pulled')),
4465 _('update to new branch head if changesets were pulled')),
4465 ('f', 'force', None, _('run even when remote repository is unrelated')),
4466 ('f', 'force', None, _('run even when remote repository is unrelated')),
4466 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4467 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4467 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4468 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4468 ('b', 'branch', [], _('a specific branch you would like to pull'),
4469 ('b', 'branch', [], _('a specific branch you would like to pull'),
4469 _('BRANCH')),
4470 _('BRANCH')),
4470 ] + remoteopts,
4471 ] + remoteopts,
4471 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
4472 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
4472 def pull(ui, repo, source="default", **opts):
4473 def pull(ui, repo, source="default", **opts):
4473 """pull changes from the specified source
4474 """pull changes from the specified source
4474
4475
4475 Pull changes from a remote repository to a local one.
4476 Pull changes from a remote repository to a local one.
4476
4477
4477 This finds all changes from the repository at the specified path
4478 This finds all changes from the repository at the specified path
4478 or URL and adds them to a local repository (the current one unless
4479 or URL and adds them to a local repository (the current one unless
4479 -R is specified). By default, this does not update the copy of the
4480 -R is specified). By default, this does not update the copy of the
4480 project in the working directory.
4481 project in the working directory.
4481
4482
4482 Use :hg:`incoming` if you want to see what would have been added
4483 Use :hg:`incoming` if you want to see what would have been added
4483 by a pull at the time you issued this command. If you then decide
4484 by a pull at the time you issued this command. If you then decide
4484 to add those changes to the repository, you should use :hg:`pull
4485 to add those changes to the repository, you should use :hg:`pull
4485 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4486 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4486
4487
4487 If SOURCE is omitted, the 'default' path will be used.
4488 If SOURCE is omitted, the 'default' path will be used.
4488 See :hg:`help urls` for more information.
4489 See :hg:`help urls` for more information.
4489
4490
4490 Returns 0 on success, 1 if an update had unresolved files.
4491 Returns 0 on success, 1 if an update had unresolved files.
4491 """
4492 """
4492 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4493 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4493 other = hg.peer(repo, opts, source)
4494 other = hg.peer(repo, opts, source)
4494 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4495 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4495 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
4496 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
4496
4497
4497 if opts.get('bookmark'):
4498 if opts.get('bookmark'):
4498 if not revs:
4499 if not revs:
4499 revs = []
4500 revs = []
4500 rb = other.listkeys('bookmarks')
4501 rb = other.listkeys('bookmarks')
4501 for b in opts['bookmark']:
4502 for b in opts['bookmark']:
4502 if b not in rb:
4503 if b not in rb:
4503 raise util.Abort(_('remote bookmark %s not found!') % b)
4504 raise util.Abort(_('remote bookmark %s not found!') % b)
4504 revs.append(rb[b])
4505 revs.append(rb[b])
4505
4506
4506 if revs:
4507 if revs:
4507 try:
4508 try:
4508 revs = [other.lookup(rev) for rev in revs]
4509 revs = [other.lookup(rev) for rev in revs]
4509 except error.CapabilityError:
4510 except error.CapabilityError:
4510 err = _("other repository doesn't support revision lookup, "
4511 err = _("other repository doesn't support revision lookup, "
4511 "so a rev cannot be specified.")
4512 "so a rev cannot be specified.")
4512 raise util.Abort(err)
4513 raise util.Abort(err)
4513
4514
4514 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
4515 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
4515 bookmarks.updatefromremote(ui, repo, other, source)
4516 bookmarks.updatefromremote(ui, repo, other, source)
4516 if checkout:
4517 if checkout:
4517 checkout = str(repo.changelog.rev(other.lookup(checkout)))
4518 checkout = str(repo.changelog.rev(other.lookup(checkout)))
4518 repo._subtoppath = source
4519 repo._subtoppath = source
4519 try:
4520 try:
4520 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
4521 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
4521
4522
4522 finally:
4523 finally:
4523 del repo._subtoppath
4524 del repo._subtoppath
4524
4525
4525 # update specified bookmarks
4526 # update specified bookmarks
4526 if opts.get('bookmark'):
4527 if opts.get('bookmark'):
4527 for b in opts['bookmark']:
4528 for b in opts['bookmark']:
4528 # explicit pull overrides local bookmark if any
4529 # explicit pull overrides local bookmark if any
4529 ui.status(_("importing bookmark %s\n") % b)
4530 ui.status(_("importing bookmark %s\n") % b)
4530 repo._bookmarks[b] = repo[rb[b]].node()
4531 repo._bookmarks[b] = repo[rb[b]].node()
4531 bookmarks.write(repo)
4532 bookmarks.write(repo)
4532
4533
4533 return ret
4534 return ret
4534
4535
4535 @command('^push',
4536 @command('^push',
4536 [('f', 'force', None, _('force push')),
4537 [('f', 'force', None, _('force push')),
4537 ('r', 'rev', [],
4538 ('r', 'rev', [],
4538 _('a changeset intended to be included in the destination'),
4539 _('a changeset intended to be included in the destination'),
4539 _('REV')),
4540 _('REV')),
4540 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4541 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4541 ('b', 'branch', [],
4542 ('b', 'branch', [],
4542 _('a specific branch you would like to push'), _('BRANCH')),
4543 _('a specific branch you would like to push'), _('BRANCH')),
4543 ('', 'new-branch', False, _('allow pushing a new branch')),
4544 ('', 'new-branch', False, _('allow pushing a new branch')),
4544 ] + remoteopts,
4545 ] + remoteopts,
4545 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4546 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4546 def push(ui, repo, dest=None, **opts):
4547 def push(ui, repo, dest=None, **opts):
4547 """push changes to the specified destination
4548 """push changes to the specified destination
4548
4549
4549 Push changesets from the local repository to the specified
4550 Push changesets from the local repository to the specified
4550 destination.
4551 destination.
4551
4552
4552 This operation is symmetrical to pull: it is identical to a pull
4553 This operation is symmetrical to pull: it is identical to a pull
4553 in the destination repository from the current one.
4554 in the destination repository from the current one.
4554
4555
4555 By default, push will not allow creation of new heads at the
4556 By default, push will not allow creation of new heads at the
4556 destination, since multiple heads would make it unclear which head
4557 destination, since multiple heads would make it unclear which head
4557 to use. In this situation, it is recommended to pull and merge
4558 to use. In this situation, it is recommended to pull and merge
4558 before pushing.
4559 before pushing.
4559
4560
4560 Use --new-branch if you want to allow push to create a new named
4561 Use --new-branch if you want to allow push to create a new named
4561 branch that is not present at the destination. This allows you to
4562 branch that is not present at the destination. This allows you to
4562 only create a new branch without forcing other changes.
4563 only create a new branch without forcing other changes.
4563
4564
4564 Use -f/--force to override the default behavior and push all
4565 Use -f/--force to override the default behavior and push all
4565 changesets on all branches.
4566 changesets on all branches.
4566
4567
4567 If -r/--rev is used, the specified revision and all its ancestors
4568 If -r/--rev is used, the specified revision and all its ancestors
4568 will be pushed to the remote repository.
4569 will be pushed to the remote repository.
4569
4570
4570 Please see :hg:`help urls` for important details about ``ssh://``
4571 Please see :hg:`help urls` for important details about ``ssh://``
4571 URLs. If DESTINATION is omitted, a default path will be used.
4572 URLs. If DESTINATION is omitted, a default path will be used.
4572
4573
4573 Returns 0 if push was successful, 1 if nothing to push.
4574 Returns 0 if push was successful, 1 if nothing to push.
4574 """
4575 """
4575
4576
4576 if opts.get('bookmark'):
4577 if opts.get('bookmark'):
4577 for b in opts['bookmark']:
4578 for b in opts['bookmark']:
4578 # translate -B options to -r so changesets get pushed
4579 # translate -B options to -r so changesets get pushed
4579 if b in repo._bookmarks:
4580 if b in repo._bookmarks:
4580 opts.setdefault('rev', []).append(b)
4581 opts.setdefault('rev', []).append(b)
4581 else:
4582 else:
4582 # if we try to push a deleted bookmark, translate it to null
4583 # if we try to push a deleted bookmark, translate it to null
4583 # this lets simultaneous -r, -b options continue working
4584 # this lets simultaneous -r, -b options continue working
4584 opts.setdefault('rev', []).append("null")
4585 opts.setdefault('rev', []).append("null")
4585
4586
4586 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4587 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4587 dest, branches = hg.parseurl(dest, opts.get('branch'))
4588 dest, branches = hg.parseurl(dest, opts.get('branch'))
4588 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4589 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4589 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4590 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4590 other = hg.peer(repo, opts, dest)
4591 other = hg.peer(repo, opts, dest)
4591 if revs:
4592 if revs:
4592 revs = [repo.lookup(rev) for rev in revs]
4593 revs = [repo.lookup(rev) for rev in revs]
4593
4594
4594 repo._subtoppath = dest
4595 repo._subtoppath = dest
4595 try:
4596 try:
4596 # push subrepos depth-first for coherent ordering
4597 # push subrepos depth-first for coherent ordering
4597 c = repo['']
4598 c = repo['']
4598 subs = c.substate # only repos that are committed
4599 subs = c.substate # only repos that are committed
4599 for s in sorted(subs):
4600 for s in sorted(subs):
4600 if c.sub(s).push(opts) == 0:
4601 if c.sub(s).push(opts) == 0:
4601 return False
4602 return False
4602 finally:
4603 finally:
4603 del repo._subtoppath
4604 del repo._subtoppath
4604 result = repo.push(other, opts.get('force'), revs=revs,
4605 result = repo.push(other, opts.get('force'), revs=revs,
4605 newbranch=opts.get('new_branch'))
4606 newbranch=opts.get('new_branch'))
4606
4607
4607 result = not result
4608 result = not result
4608
4609
4609 if opts.get('bookmark'):
4610 if opts.get('bookmark'):
4610 rb = other.listkeys('bookmarks')
4611 rb = other.listkeys('bookmarks')
4611 for b in opts['bookmark']:
4612 for b in opts['bookmark']:
4612 # explicit push overrides remote bookmark if any
4613 # explicit push overrides remote bookmark if any
4613 if b in repo._bookmarks:
4614 if b in repo._bookmarks:
4614 ui.status(_("exporting bookmark %s\n") % b)
4615 ui.status(_("exporting bookmark %s\n") % b)
4615 new = repo[b].hex()
4616 new = repo[b].hex()
4616 elif b in rb:
4617 elif b in rb:
4617 ui.status(_("deleting remote bookmark %s\n") % b)
4618 ui.status(_("deleting remote bookmark %s\n") % b)
4618 new = '' # delete
4619 new = '' # delete
4619 else:
4620 else:
4620 ui.warn(_('bookmark %s does not exist on the local '
4621 ui.warn(_('bookmark %s does not exist on the local '
4621 'or remote repository!\n') % b)
4622 'or remote repository!\n') % b)
4622 return 2
4623 return 2
4623 old = rb.get(b, '')
4624 old = rb.get(b, '')
4624 r = other.pushkey('bookmarks', b, old, new)
4625 r = other.pushkey('bookmarks', b, old, new)
4625 if not r:
4626 if not r:
4626 ui.warn(_('updating bookmark %s failed!\n') % b)
4627 ui.warn(_('updating bookmark %s failed!\n') % b)
4627 if not result:
4628 if not result:
4628 result = 2
4629 result = 2
4629
4630
4630 return result
4631 return result
4631
4632
4632 @command('recover', [])
4633 @command('recover', [])
4633 def recover(ui, repo):
4634 def recover(ui, repo):
4634 """roll back an interrupted transaction
4635 """roll back an interrupted transaction
4635
4636
4636 Recover from an interrupted commit or pull.
4637 Recover from an interrupted commit or pull.
4637
4638
4638 This command tries to fix the repository status after an
4639 This command tries to fix the repository status after an
4639 interrupted operation. It should only be necessary when Mercurial
4640 interrupted operation. It should only be necessary when Mercurial
4640 suggests it.
4641 suggests it.
4641
4642
4642 Returns 0 if successful, 1 if nothing to recover or verify fails.
4643 Returns 0 if successful, 1 if nothing to recover or verify fails.
4643 """
4644 """
4644 if repo.recover():
4645 if repo.recover():
4645 return hg.verify(repo)
4646 return hg.verify(repo)
4646 return 1
4647 return 1
4647
4648
4648 @command('^remove|rm',
4649 @command('^remove|rm',
4649 [('A', 'after', None, _('record delete for missing files')),
4650 [('A', 'after', None, _('record delete for missing files')),
4650 ('f', 'force', None,
4651 ('f', 'force', None,
4651 _('remove (and delete) file even if added or modified')),
4652 _('remove (and delete) file even if added or modified')),
4652 ] + walkopts,
4653 ] + walkopts,
4653 _('[OPTION]... FILE...'))
4654 _('[OPTION]... FILE...'))
4654 def remove(ui, repo, *pats, **opts):
4655 def remove(ui, repo, *pats, **opts):
4655 """remove the specified files on the next commit
4656 """remove the specified files on the next commit
4656
4657
4657 Schedule the indicated files for removal from the current branch.
4658 Schedule the indicated files for removal from the current branch.
4658
4659
4659 This command schedules the files to be removed at the next commit.
4660 This command schedules the files to be removed at the next commit.
4660 To undo a remove before that, see :hg:`revert`. To undo added
4661 To undo a remove before that, see :hg:`revert`. To undo added
4661 files, see :hg:`forget`.
4662 files, see :hg:`forget`.
4662
4663
4663 .. container:: verbose
4664 .. container:: verbose
4664
4665
4665 -A/--after can be used to remove only files that have already
4666 -A/--after can be used to remove only files that have already
4666 been deleted, -f/--force can be used to force deletion, and -Af
4667 been deleted, -f/--force can be used to force deletion, and -Af
4667 can be used to remove files from the next revision without
4668 can be used to remove files from the next revision without
4668 deleting them from the working directory.
4669 deleting them from the working directory.
4669
4670
4670 The following table details the behavior of remove for different
4671 The following table details the behavior of remove for different
4671 file states (columns) and option combinations (rows). The file
4672 file states (columns) and option combinations (rows). The file
4672 states are Added [A], Clean [C], Modified [M] and Missing [!]
4673 states are Added [A], Clean [C], Modified [M] and Missing [!]
4673 (as reported by :hg:`status`). The actions are Warn, Remove
4674 (as reported by :hg:`status`). The actions are Warn, Remove
4674 (from branch) and Delete (from disk):
4675 (from branch) and Delete (from disk):
4675
4676
4676 ======= == == == ==
4677 ======= == == == ==
4677 A C M !
4678 A C M !
4678 ======= == == == ==
4679 ======= == == == ==
4679 none W RD W R
4680 none W RD W R
4680 -f R RD RD R
4681 -f R RD RD R
4681 -A W W W R
4682 -A W W W R
4682 -Af R R R R
4683 -Af R R R R
4683 ======= == == == ==
4684 ======= == == == ==
4684
4685
4685 Note that remove never deletes files in Added [A] state from the
4686 Note that remove never deletes files in Added [A] state from the
4686 working directory, not even if option --force is specified.
4687 working directory, not even if option --force is specified.
4687
4688
4688 Returns 0 on success, 1 if any warnings encountered.
4689 Returns 0 on success, 1 if any warnings encountered.
4689 """
4690 """
4690
4691
4691 ret = 0
4692 ret = 0
4692 after, force = opts.get('after'), opts.get('force')
4693 after, force = opts.get('after'), opts.get('force')
4693 if not pats and not after:
4694 if not pats and not after:
4694 raise util.Abort(_('no files specified'))
4695 raise util.Abort(_('no files specified'))
4695
4696
4696 m = scmutil.match(repo[None], pats, opts)
4697 m = scmutil.match(repo[None], pats, opts)
4697 s = repo.status(match=m, clean=True)
4698 s = repo.status(match=m, clean=True)
4698 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
4699 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
4699
4700
4700 for f in m.files():
4701 for f in m.files():
4701 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
4702 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
4702 if os.path.exists(m.rel(f)):
4703 if os.path.exists(m.rel(f)):
4703 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
4704 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
4704 ret = 1
4705 ret = 1
4705
4706
4706 if force:
4707 if force:
4707 list = modified + deleted + clean + added
4708 list = modified + deleted + clean + added
4708 elif after:
4709 elif after:
4709 list = deleted
4710 list = deleted
4710 for f in modified + added + clean:
4711 for f in modified + added + clean:
4711 ui.warn(_('not removing %s: file still exists (use -f'
4712 ui.warn(_('not removing %s: file still exists (use -f'
4712 ' to force removal)\n') % m.rel(f))
4713 ' to force removal)\n') % m.rel(f))
4713 ret = 1
4714 ret = 1
4714 else:
4715 else:
4715 list = deleted + clean
4716 list = deleted + clean
4716 for f in modified:
4717 for f in modified:
4717 ui.warn(_('not removing %s: file is modified (use -f'
4718 ui.warn(_('not removing %s: file is modified (use -f'
4718 ' to force removal)\n') % m.rel(f))
4719 ' to force removal)\n') % m.rel(f))
4719 ret = 1
4720 ret = 1
4720 for f in added:
4721 for f in added:
4721 ui.warn(_('not removing %s: file has been marked for add'
4722 ui.warn(_('not removing %s: file has been marked for add'
4722 ' (use forget to undo)\n') % m.rel(f))
4723 ' (use forget to undo)\n') % m.rel(f))
4723 ret = 1
4724 ret = 1
4724
4725
4725 for f in sorted(list):
4726 for f in sorted(list):
4726 if ui.verbose or not m.exact(f):
4727 if ui.verbose or not m.exact(f):
4727 ui.status(_('removing %s\n') % m.rel(f))
4728 ui.status(_('removing %s\n') % m.rel(f))
4728
4729
4729 wlock = repo.wlock()
4730 wlock = repo.wlock()
4730 try:
4731 try:
4731 if not after:
4732 if not after:
4732 for f in list:
4733 for f in list:
4733 if f in added:
4734 if f in added:
4734 continue # we never unlink added files on remove
4735 continue # we never unlink added files on remove
4735 try:
4736 try:
4736 util.unlinkpath(repo.wjoin(f))
4737 util.unlinkpath(repo.wjoin(f))
4737 except OSError, inst:
4738 except OSError, inst:
4738 if inst.errno != errno.ENOENT:
4739 if inst.errno != errno.ENOENT:
4739 raise
4740 raise
4740 repo[None].forget(list)
4741 repo[None].forget(list)
4741 finally:
4742 finally:
4742 wlock.release()
4743 wlock.release()
4743
4744
4744 return ret
4745 return ret
4745
4746
4746 @command('rename|move|mv',
4747 @command('rename|move|mv',
4747 [('A', 'after', None, _('record a rename that has already occurred')),
4748 [('A', 'after', None, _('record a rename that has already occurred')),
4748 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4749 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4749 ] + walkopts + dryrunopts,
4750 ] + walkopts + dryrunopts,
4750 _('[OPTION]... SOURCE... DEST'))
4751 _('[OPTION]... SOURCE... DEST'))
4751 def rename(ui, repo, *pats, **opts):
4752 def rename(ui, repo, *pats, **opts):
4752 """rename files; equivalent of copy + remove
4753 """rename files; equivalent of copy + remove
4753
4754
4754 Mark dest as copies of sources; mark sources for deletion. If dest
4755 Mark dest as copies of sources; mark sources for deletion. If dest
4755 is a directory, copies are put in that directory. If dest is a
4756 is a directory, copies are put in that directory. If dest is a
4756 file, there can only be one source.
4757 file, there can only be one source.
4757
4758
4758 By default, this command copies the contents of files as they
4759 By default, this command copies the contents of files as they
4759 exist in the working directory. If invoked with -A/--after, the
4760 exist in the working directory. If invoked with -A/--after, the
4760 operation is recorded, but no copying is performed.
4761 operation is recorded, but no copying is performed.
4761
4762
4762 This command takes effect at the next commit. To undo a rename
4763 This command takes effect at the next commit. To undo a rename
4763 before that, see :hg:`revert`.
4764 before that, see :hg:`revert`.
4764
4765
4765 Returns 0 on success, 1 if errors are encountered.
4766 Returns 0 on success, 1 if errors are encountered.
4766 """
4767 """
4767 wlock = repo.wlock(False)
4768 wlock = repo.wlock(False)
4768 try:
4769 try:
4769 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4770 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4770 finally:
4771 finally:
4771 wlock.release()
4772 wlock.release()
4772
4773
4773 @command('resolve',
4774 @command('resolve',
4774 [('a', 'all', None, _('select all unresolved files')),
4775 [('a', 'all', None, _('select all unresolved files')),
4775 ('l', 'list', None, _('list state of files needing merge')),
4776 ('l', 'list', None, _('list state of files needing merge')),
4776 ('m', 'mark', None, _('mark files as resolved')),
4777 ('m', 'mark', None, _('mark files as resolved')),
4777 ('u', 'unmark', None, _('mark files as unresolved')),
4778 ('u', 'unmark', None, _('mark files as unresolved')),
4778 ('n', 'no-status', None, _('hide status prefix'))]
4779 ('n', 'no-status', None, _('hide status prefix'))]
4779 + mergetoolopts + walkopts,
4780 + mergetoolopts + walkopts,
4780 _('[OPTION]... [FILE]...'))
4781 _('[OPTION]... [FILE]...'))
4781 def resolve(ui, repo, *pats, **opts):
4782 def resolve(ui, repo, *pats, **opts):
4782 """redo merges or set/view the merge status of files
4783 """redo merges or set/view the merge status of files
4783
4784
4784 Merges with unresolved conflicts are often the result of
4785 Merges with unresolved conflicts are often the result of
4785 non-interactive merging using the ``internal:merge`` configuration
4786 non-interactive merging using the ``internal:merge`` configuration
4786 setting, or a command-line merge tool like ``diff3``. The resolve
4787 setting, or a command-line merge tool like ``diff3``. The resolve
4787 command is used to manage the files involved in a merge, after
4788 command is used to manage the files involved in a merge, after
4788 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4789 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4789 working directory must have two parents). See :hg:`help
4790 working directory must have two parents). See :hg:`help
4790 merge-tools` for information on configuring merge tools.
4791 merge-tools` for information on configuring merge tools.
4791
4792
4792 The resolve command can be used in the following ways:
4793 The resolve command can be used in the following ways:
4793
4794
4794 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4795 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4795 files, discarding any previous merge attempts. Re-merging is not
4796 files, discarding any previous merge attempts. Re-merging is not
4796 performed for files already marked as resolved. Use ``--all/-a``
4797 performed for files already marked as resolved. Use ``--all/-a``
4797 to select all unresolved files. ``--tool`` can be used to specify
4798 to select all unresolved files. ``--tool`` can be used to specify
4798 the merge tool used for the given files. It overrides the HGMERGE
4799 the merge tool used for the given files. It overrides the HGMERGE
4799 environment variable and your configuration files. Previous file
4800 environment variable and your configuration files. Previous file
4800 contents are saved with a ``.orig`` suffix.
4801 contents are saved with a ``.orig`` suffix.
4801
4802
4802 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4803 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4803 (e.g. after having manually fixed-up the files). The default is
4804 (e.g. after having manually fixed-up the files). The default is
4804 to mark all unresolved files.
4805 to mark all unresolved files.
4805
4806
4806 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4807 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4807 default is to mark all resolved files.
4808 default is to mark all resolved files.
4808
4809
4809 - :hg:`resolve -l`: list files which had or still have conflicts.
4810 - :hg:`resolve -l`: list files which had or still have conflicts.
4810 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4811 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4811
4812
4812 Note that Mercurial will not let you commit files with unresolved
4813 Note that Mercurial will not let you commit files with unresolved
4813 merge conflicts. You must use :hg:`resolve -m ...` before you can
4814 merge conflicts. You must use :hg:`resolve -m ...` before you can
4814 commit after a conflicting merge.
4815 commit after a conflicting merge.
4815
4816
4816 Returns 0 on success, 1 if any files fail a resolve attempt.
4817 Returns 0 on success, 1 if any files fail a resolve attempt.
4817 """
4818 """
4818
4819
4819 all, mark, unmark, show, nostatus = \
4820 all, mark, unmark, show, nostatus = \
4820 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
4821 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
4821
4822
4822 if (show and (mark or unmark)) or (mark and unmark):
4823 if (show and (mark or unmark)) or (mark and unmark):
4823 raise util.Abort(_("too many options specified"))
4824 raise util.Abort(_("too many options specified"))
4824 if pats and all:
4825 if pats and all:
4825 raise util.Abort(_("can't specify --all and patterns"))
4826 raise util.Abort(_("can't specify --all and patterns"))
4826 if not (all or pats or show or mark or unmark):
4827 if not (all or pats or show or mark or unmark):
4827 raise util.Abort(_('no files or directories specified; '
4828 raise util.Abort(_('no files or directories specified; '
4828 'use --all to remerge all files'))
4829 'use --all to remerge all files'))
4829
4830
4830 ms = mergemod.mergestate(repo)
4831 ms = mergemod.mergestate(repo)
4831 m = scmutil.match(repo[None], pats, opts)
4832 m = scmutil.match(repo[None], pats, opts)
4832 ret = 0
4833 ret = 0
4833
4834
4834 for f in ms:
4835 for f in ms:
4835 if m(f):
4836 if m(f):
4836 if show:
4837 if show:
4837 if nostatus:
4838 if nostatus:
4838 ui.write("%s\n" % f)
4839 ui.write("%s\n" % f)
4839 else:
4840 else:
4840 ui.write("%s %s\n" % (ms[f].upper(), f),
4841 ui.write("%s %s\n" % (ms[f].upper(), f),
4841 label='resolve.' +
4842 label='resolve.' +
4842 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
4843 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
4843 elif mark:
4844 elif mark:
4844 ms.mark(f, "r")
4845 ms.mark(f, "r")
4845 elif unmark:
4846 elif unmark:
4846 ms.mark(f, "u")
4847 ms.mark(f, "u")
4847 else:
4848 else:
4848 wctx = repo[None]
4849 wctx = repo[None]
4849 mctx = wctx.parents()[-1]
4850 mctx = wctx.parents()[-1]
4850
4851
4851 # backup pre-resolve (merge uses .orig for its own purposes)
4852 # backup pre-resolve (merge uses .orig for its own purposes)
4852 a = repo.wjoin(f)
4853 a = repo.wjoin(f)
4853 util.copyfile(a, a + ".resolve")
4854 util.copyfile(a, a + ".resolve")
4854
4855
4855 try:
4856 try:
4856 # resolve file
4857 # resolve file
4857 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4858 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4858 if ms.resolve(f, wctx, mctx):
4859 if ms.resolve(f, wctx, mctx):
4859 ret = 1
4860 ret = 1
4860 finally:
4861 finally:
4861 ui.setconfig('ui', 'forcemerge', '')
4862 ui.setconfig('ui', 'forcemerge', '')
4862
4863
4863 # replace filemerge's .orig file with our resolve file
4864 # replace filemerge's .orig file with our resolve file
4864 util.rename(a + ".resolve", a + ".orig")
4865 util.rename(a + ".resolve", a + ".orig")
4865
4866
4866 ms.commit()
4867 ms.commit()
4867 return ret
4868 return ret
4868
4869
4869 @command('revert',
4870 @command('revert',
4870 [('a', 'all', None, _('revert all changes when no arguments given')),
4871 [('a', 'all', None, _('revert all changes when no arguments given')),
4871 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4872 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4872 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4873 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4873 ('C', 'no-backup', None, _('do not save backup copies of files')),
4874 ('C', 'no-backup', None, _('do not save backup copies of files')),
4874 ] + walkopts + dryrunopts,
4875 ] + walkopts + dryrunopts,
4875 _('[OPTION]... [-r REV] [NAME]...'))
4876 _('[OPTION]... [-r REV] [NAME]...'))
4876 def revert(ui, repo, *pats, **opts):
4877 def revert(ui, repo, *pats, **opts):
4877 """restore files to their checkout state
4878 """restore files to their checkout state
4878
4879
4879 .. note::
4880 .. note::
4880 To check out earlier revisions, you should use :hg:`update REV`.
4881 To check out earlier revisions, you should use :hg:`update REV`.
4881 To cancel a merge (and lose your changes), use :hg:`update --clean .`.
4882 To cancel a merge (and lose your changes), use :hg:`update --clean .`.
4882
4883
4883 With no revision specified, revert the specified files or directories
4884 With no revision specified, revert the specified files or directories
4884 to the contents they had in the parent of the working directory.
4885 to the contents they had in the parent of the working directory.
4885 This restores the contents of files to an unmodified
4886 This restores the contents of files to an unmodified
4886 state and unschedules adds, removes, copies, and renames. If the
4887 state and unschedules adds, removes, copies, and renames. If the
4887 working directory has two parents, you must explicitly specify a
4888 working directory has two parents, you must explicitly specify a
4888 revision.
4889 revision.
4889
4890
4890 Using the -r/--rev or -d/--date options, revert the given files or
4891 Using the -r/--rev or -d/--date options, revert the given files or
4891 directories to their states as of a specific revision. Because
4892 directories to their states as of a specific revision. Because
4892 revert does not change the working directory parents, this will
4893 revert does not change the working directory parents, this will
4893 cause these files to appear modified. This can be helpful to "back
4894 cause these files to appear modified. This can be helpful to "back
4894 out" some or all of an earlier change. See :hg:`backout` for a
4895 out" some or all of an earlier change. See :hg:`backout` for a
4895 related method.
4896 related method.
4896
4897
4897 Modified files are saved with a .orig suffix before reverting.
4898 Modified files are saved with a .orig suffix before reverting.
4898 To disable these backups, use --no-backup.
4899 To disable these backups, use --no-backup.
4899
4900
4900 See :hg:`help dates` for a list of formats valid for -d/--date.
4901 See :hg:`help dates` for a list of formats valid for -d/--date.
4901
4902
4902 Returns 0 on success.
4903 Returns 0 on success.
4903 """
4904 """
4904
4905
4905 if opts.get("date"):
4906 if opts.get("date"):
4906 if opts.get("rev"):
4907 if opts.get("rev"):
4907 raise util.Abort(_("you can't specify a revision and a date"))
4908 raise util.Abort(_("you can't specify a revision and a date"))
4908 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4909 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4909
4910
4910 parent, p2 = repo.dirstate.parents()
4911 parent, p2 = repo.dirstate.parents()
4911 if not opts.get('rev') and p2 != nullid:
4912 if not opts.get('rev') and p2 != nullid:
4912 # revert after merge is a trap for new users (issue2915)
4913 # revert after merge is a trap for new users (issue2915)
4913 raise util.Abort(_('uncommitted merge with no revision specified'),
4914 raise util.Abort(_('uncommitted merge with no revision specified'),
4914 hint=_('use "hg update" or see "hg help revert"'))
4915 hint=_('use "hg update" or see "hg help revert"'))
4915
4916
4916 ctx = scmutil.revsingle(repo, opts.get('rev'))
4917 ctx = scmutil.revsingle(repo, opts.get('rev'))
4917
4918
4918 if not pats and not opts.get('all'):
4919 if not pats and not opts.get('all'):
4919 msg = _("no files or directories specified")
4920 msg = _("no files or directories specified")
4920 if p2 != nullid:
4921 if p2 != nullid:
4921 hint = _("uncommitted merge, use --all to discard all changes,"
4922 hint = _("uncommitted merge, use --all to discard all changes,"
4922 " or 'hg update -C .' to abort the merge")
4923 " or 'hg update -C .' to abort the merge")
4923 raise util.Abort(msg, hint=hint)
4924 raise util.Abort(msg, hint=hint)
4924 dirty = util.any(repo.status())
4925 dirty = util.any(repo.status())
4925 node = ctx.node()
4926 node = ctx.node()
4926 if node != parent:
4927 if node != parent:
4927 if dirty:
4928 if dirty:
4928 hint = _("uncommitted changes, use --all to discard all"
4929 hint = _("uncommitted changes, use --all to discard all"
4929 " changes, or 'hg update %s' to update") % ctx.rev()
4930 " changes, or 'hg update %s' to update") % ctx.rev()
4930 else:
4931 else:
4931 hint = _("use --all to revert all files,"
4932 hint = _("use --all to revert all files,"
4932 " or 'hg update %s' to update") % ctx.rev()
4933 " or 'hg update %s' to update") % ctx.rev()
4933 elif dirty:
4934 elif dirty:
4934 hint = _("uncommitted changes, use --all to discard all changes")
4935 hint = _("uncommitted changes, use --all to discard all changes")
4935 else:
4936 else:
4936 hint = _("use --all to revert all files")
4937 hint = _("use --all to revert all files")
4937 raise util.Abort(msg, hint=hint)
4938 raise util.Abort(msg, hint=hint)
4938
4939
4939 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
4940 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
4940
4941
4941 @command('rollback', dryrunopts +
4942 @command('rollback', dryrunopts +
4942 [('f', 'force', False, _('ignore safety measures'))])
4943 [('f', 'force', False, _('ignore safety measures'))])
4943 def rollback(ui, repo, **opts):
4944 def rollback(ui, repo, **opts):
4944 """roll back the last transaction (dangerous)
4945 """roll back the last transaction (dangerous)
4945
4946
4946 This command should be used with care. There is only one level of
4947 This command should be used with care. There is only one level of
4947 rollback, and there is no way to undo a rollback. It will also
4948 rollback, and there is no way to undo a rollback. It will also
4948 restore the dirstate at the time of the last transaction, losing
4949 restore the dirstate at the time of the last transaction, losing
4949 any dirstate changes since that time. This command does not alter
4950 any dirstate changes since that time. This command does not alter
4950 the working directory.
4951 the working directory.
4951
4952
4952 Transactions are used to encapsulate the effects of all commands
4953 Transactions are used to encapsulate the effects of all commands
4953 that create new changesets or propagate existing changesets into a
4954 that create new changesets or propagate existing changesets into a
4954 repository. For example, the following commands are transactional,
4955 repository. For example, the following commands are transactional,
4955 and their effects can be rolled back:
4956 and their effects can be rolled back:
4956
4957
4957 - commit
4958 - commit
4958 - import
4959 - import
4959 - pull
4960 - pull
4960 - push (with this repository as the destination)
4961 - push (with this repository as the destination)
4961 - unbundle
4962 - unbundle
4962
4963
4963 To avoid permanent data loss, rollback will refuse to rollback a
4964 To avoid permanent data loss, rollback will refuse to rollback a
4964 commit transaction if it isn't checked out. Use --force to
4965 commit transaction if it isn't checked out. Use --force to
4965 override this protection.
4966 override this protection.
4966
4967
4967 This command is not intended for use on public repositories. Once
4968 This command is not intended for use on public repositories. Once
4968 changes are visible for pull by other users, rolling a transaction
4969 changes are visible for pull by other users, rolling a transaction
4969 back locally is ineffective (someone else may already have pulled
4970 back locally is ineffective (someone else may already have pulled
4970 the changes). Furthermore, a race is possible with readers of the
4971 the changes). Furthermore, a race is possible with readers of the
4971 repository; for example an in-progress pull from the repository
4972 repository; for example an in-progress pull from the repository
4972 may fail if a rollback is performed.
4973 may fail if a rollback is performed.
4973
4974
4974 Returns 0 on success, 1 if no rollback data is available.
4975 Returns 0 on success, 1 if no rollback data is available.
4975 """
4976 """
4976 return repo.rollback(dryrun=opts.get('dry_run'),
4977 return repo.rollback(dryrun=opts.get('dry_run'),
4977 force=opts.get('force'))
4978 force=opts.get('force'))
4978
4979
4979 @command('root', [])
4980 @command('root', [])
4980 def root(ui, repo):
4981 def root(ui, repo):
4981 """print the root (top) of the current working directory
4982 """print the root (top) of the current working directory
4982
4983
4983 Print the root directory of the current repository.
4984 Print the root directory of the current repository.
4984
4985
4985 Returns 0 on success.
4986 Returns 0 on success.
4986 """
4987 """
4987 ui.write(repo.root + "\n")
4988 ui.write(repo.root + "\n")
4988
4989
4989 @command('^serve',
4990 @command('^serve',
4990 [('A', 'accesslog', '', _('name of access log file to write to'),
4991 [('A', 'accesslog', '', _('name of access log file to write to'),
4991 _('FILE')),
4992 _('FILE')),
4992 ('d', 'daemon', None, _('run server in background')),
4993 ('d', 'daemon', None, _('run server in background')),
4993 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('NUM')),
4994 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('NUM')),
4994 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4995 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4995 # use string type, then we can check if something was passed
4996 # use string type, then we can check if something was passed
4996 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4997 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4997 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4998 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4998 _('ADDR')),
4999 _('ADDR')),
4999 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5000 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5000 _('PREFIX')),
5001 _('PREFIX')),
5001 ('n', 'name', '',
5002 ('n', 'name', '',
5002 _('name to show in web pages (default: working directory)'), _('NAME')),
5003 _('name to show in web pages (default: working directory)'), _('NAME')),
5003 ('', 'web-conf', '',
5004 ('', 'web-conf', '',
5004 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
5005 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
5005 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5006 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5006 _('FILE')),
5007 _('FILE')),
5007 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5008 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5008 ('', 'stdio', None, _('for remote clients')),
5009 ('', 'stdio', None, _('for remote clients')),
5009 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
5010 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
5010 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5011 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5011 ('', 'style', '', _('template style to use'), _('STYLE')),
5012 ('', 'style', '', _('template style to use'), _('STYLE')),
5012 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5013 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5013 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
5014 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
5014 _('[OPTION]...'))
5015 _('[OPTION]...'))
5015 def serve(ui, repo, **opts):
5016 def serve(ui, repo, **opts):
5016 """start stand-alone webserver
5017 """start stand-alone webserver
5017
5018
5018 Start a local HTTP repository browser and pull server. You can use
5019 Start a local HTTP repository browser and pull server. You can use
5019 this for ad-hoc sharing and browsing of repositories. It is
5020 this for ad-hoc sharing and browsing of repositories. It is
5020 recommended to use a real web server to serve a repository for
5021 recommended to use a real web server to serve a repository for
5021 longer periods of time.
5022 longer periods of time.
5022
5023
5023 Please note that the server does not implement access control.
5024 Please note that the server does not implement access control.
5024 This means that, by default, anybody can read from the server and
5025 This means that, by default, anybody can read from the server and
5025 nobody can write to it by default. Set the ``web.allow_push``
5026 nobody can write to it by default. Set the ``web.allow_push``
5026 option to ``*`` to allow everybody to push to the server. You
5027 option to ``*`` to allow everybody to push to the server. You
5027 should use a real web server if you need to authenticate users.
5028 should use a real web server if you need to authenticate users.
5028
5029
5029 By default, the server logs accesses to stdout and errors to
5030 By default, the server logs accesses to stdout and errors to
5030 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5031 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5031 files.
5032 files.
5032
5033
5033 To have the server choose a free port number to listen on, specify
5034 To have the server choose a free port number to listen on, specify
5034 a port number of 0; in this case, the server will print the port
5035 a port number of 0; in this case, the server will print the port
5035 number it uses.
5036 number it uses.
5036
5037
5037 Returns 0 on success.
5038 Returns 0 on success.
5038 """
5039 """
5039
5040
5040 if opts["stdio"] and opts["cmdserver"]:
5041 if opts["stdio"] and opts["cmdserver"]:
5041 raise util.Abort(_("cannot use --stdio with --cmdserver"))
5042 raise util.Abort(_("cannot use --stdio with --cmdserver"))
5042
5043
5043 def checkrepo():
5044 def checkrepo():
5044 if repo is None:
5045 if repo is None:
5045 raise error.RepoError(_("there is no Mercurial repository here"
5046 raise error.RepoError(_("there is no Mercurial repository here"
5046 " (.hg not found)"))
5047 " (.hg not found)"))
5047
5048
5048 if opts["stdio"]:
5049 if opts["stdio"]:
5049 checkrepo()
5050 checkrepo()
5050 s = sshserver.sshserver(ui, repo)
5051 s = sshserver.sshserver(ui, repo)
5051 s.serve_forever()
5052 s.serve_forever()
5052
5053
5053 if opts["cmdserver"]:
5054 if opts["cmdserver"]:
5054 checkrepo()
5055 checkrepo()
5055 s = commandserver.server(ui, repo, opts["cmdserver"])
5056 s = commandserver.server(ui, repo, opts["cmdserver"])
5056 return s.serve()
5057 return s.serve()
5057
5058
5058 # this way we can check if something was given in the command-line
5059 # this way we can check if something was given in the command-line
5059 if opts.get('port'):
5060 if opts.get('port'):
5060 opts['port'] = util.getport(opts.get('port'))
5061 opts['port'] = util.getport(opts.get('port'))
5061
5062
5062 baseui = repo and repo.baseui or ui
5063 baseui = repo and repo.baseui or ui
5063 optlist = ("name templates style address port prefix ipv6"
5064 optlist = ("name templates style address port prefix ipv6"
5064 " accesslog errorlog certificate encoding")
5065 " accesslog errorlog certificate encoding")
5065 for o in optlist.split():
5066 for o in optlist.split():
5066 val = opts.get(o, '')
5067 val = opts.get(o, '')
5067 if val in (None, ''): # should check against default options instead
5068 if val in (None, ''): # should check against default options instead
5068 continue
5069 continue
5069 baseui.setconfig("web", o, val)
5070 baseui.setconfig("web", o, val)
5070 if repo and repo.ui != baseui:
5071 if repo and repo.ui != baseui:
5071 repo.ui.setconfig("web", o, val)
5072 repo.ui.setconfig("web", o, val)
5072
5073
5073 o = opts.get('web_conf') or opts.get('webdir_conf')
5074 o = opts.get('web_conf') or opts.get('webdir_conf')
5074 if not o:
5075 if not o:
5075 if not repo:
5076 if not repo:
5076 raise error.RepoError(_("there is no Mercurial repository"
5077 raise error.RepoError(_("there is no Mercurial repository"
5077 " here (.hg not found)"))
5078 " here (.hg not found)"))
5078 o = repo.root
5079 o = repo.root
5079
5080
5080 app = hgweb.hgweb(o, baseui=ui)
5081 app = hgweb.hgweb(o, baseui=ui)
5081
5082
5082 class service(object):
5083 class service(object):
5083 def init(self):
5084 def init(self):
5084 util.setsignalhandler()
5085 util.setsignalhandler()
5085 self.httpd = hgweb.server.create_server(ui, app)
5086 self.httpd = hgweb.server.create_server(ui, app)
5086
5087
5087 if opts['port'] and not ui.verbose:
5088 if opts['port'] and not ui.verbose:
5088 return
5089 return
5089
5090
5090 if self.httpd.prefix:
5091 if self.httpd.prefix:
5091 prefix = self.httpd.prefix.strip('/') + '/'
5092 prefix = self.httpd.prefix.strip('/') + '/'
5092 else:
5093 else:
5093 prefix = ''
5094 prefix = ''
5094
5095
5095 port = ':%d' % self.httpd.port
5096 port = ':%d' % self.httpd.port
5096 if port == ':80':
5097 if port == ':80':
5097 port = ''
5098 port = ''
5098
5099
5099 bindaddr = self.httpd.addr
5100 bindaddr = self.httpd.addr
5100 if bindaddr == '0.0.0.0':
5101 if bindaddr == '0.0.0.0':
5101 bindaddr = '*'
5102 bindaddr = '*'
5102 elif ':' in bindaddr: # IPv6
5103 elif ':' in bindaddr: # IPv6
5103 bindaddr = '[%s]' % bindaddr
5104 bindaddr = '[%s]' % bindaddr
5104
5105
5105 fqaddr = self.httpd.fqaddr
5106 fqaddr = self.httpd.fqaddr
5106 if ':' in fqaddr:
5107 if ':' in fqaddr:
5107 fqaddr = '[%s]' % fqaddr
5108 fqaddr = '[%s]' % fqaddr
5108 if opts['port']:
5109 if opts['port']:
5109 write = ui.status
5110 write = ui.status
5110 else:
5111 else:
5111 write = ui.write
5112 write = ui.write
5112 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
5113 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
5113 (fqaddr, port, prefix, bindaddr, self.httpd.port))
5114 (fqaddr, port, prefix, bindaddr, self.httpd.port))
5114
5115
5115 def run(self):
5116 def run(self):
5116 self.httpd.serve_forever()
5117 self.httpd.serve_forever()
5117
5118
5118 service = service()
5119 service = service()
5119
5120
5120 cmdutil.service(opts, initfn=service.init, runfn=service.run)
5121 cmdutil.service(opts, initfn=service.init, runfn=service.run)
5121
5122
5122 @command('showconfig|debugconfig',
5123 @command('showconfig|debugconfig',
5123 [('u', 'untrusted', None, _('show untrusted configuration options'))],
5124 [('u', 'untrusted', None, _('show untrusted configuration options'))],
5124 _('[-u] [NAME]...'))
5125 _('[-u] [NAME]...'))
5125 def showconfig(ui, repo, *values, **opts):
5126 def showconfig(ui, repo, *values, **opts):
5126 """show combined config settings from all hgrc files
5127 """show combined config settings from all hgrc files
5127
5128
5128 With no arguments, print names and values of all config items.
5129 With no arguments, print names and values of all config items.
5129
5130
5130 With one argument of the form section.name, print just the value
5131 With one argument of the form section.name, print just the value
5131 of that config item.
5132 of that config item.
5132
5133
5133 With multiple arguments, print names and values of all config
5134 With multiple arguments, print names and values of all config
5134 items with matching section names.
5135 items with matching section names.
5135
5136
5136 With --debug, the source (filename and line number) is printed
5137 With --debug, the source (filename and line number) is printed
5137 for each config item.
5138 for each config item.
5138
5139
5139 Returns 0 on success.
5140 Returns 0 on success.
5140 """
5141 """
5141
5142
5142 for f in scmutil.rcpath():
5143 for f in scmutil.rcpath():
5143 ui.debug('read config from: %s\n' % f)
5144 ui.debug('read config from: %s\n' % f)
5144 untrusted = bool(opts.get('untrusted'))
5145 untrusted = bool(opts.get('untrusted'))
5145 if values:
5146 if values:
5146 sections = [v for v in values if '.' not in v]
5147 sections = [v for v in values if '.' not in v]
5147 items = [v for v in values if '.' in v]
5148 items = [v for v in values if '.' in v]
5148 if len(items) > 1 or items and sections:
5149 if len(items) > 1 or items and sections:
5149 raise util.Abort(_('only one config item permitted'))
5150 raise util.Abort(_('only one config item permitted'))
5150 for section, name, value in ui.walkconfig(untrusted=untrusted):
5151 for section, name, value in ui.walkconfig(untrusted=untrusted):
5151 value = str(value).replace('\n', '\\n')
5152 value = str(value).replace('\n', '\\n')
5152 sectname = section + '.' + name
5153 sectname = section + '.' + name
5153 if values:
5154 if values:
5154 for v in values:
5155 for v in values:
5155 if v == section:
5156 if v == section:
5156 ui.debug('%s: ' %
5157 ui.debug('%s: ' %
5157 ui.configsource(section, name, untrusted))
5158 ui.configsource(section, name, untrusted))
5158 ui.write('%s=%s\n' % (sectname, value))
5159 ui.write('%s=%s\n' % (sectname, value))
5159 elif v == sectname:
5160 elif v == sectname:
5160 ui.debug('%s: ' %
5161 ui.debug('%s: ' %
5161 ui.configsource(section, name, untrusted))
5162 ui.configsource(section, name, untrusted))
5162 ui.write(value, '\n')
5163 ui.write(value, '\n')
5163 else:
5164 else:
5164 ui.debug('%s: ' %
5165 ui.debug('%s: ' %
5165 ui.configsource(section, name, untrusted))
5166 ui.configsource(section, name, untrusted))
5166 ui.write('%s=%s\n' % (sectname, value))
5167 ui.write('%s=%s\n' % (sectname, value))
5167
5168
5168 @command('^status|st',
5169 @command('^status|st',
5169 [('A', 'all', None, _('show status of all files')),
5170 [('A', 'all', None, _('show status of all files')),
5170 ('m', 'modified', None, _('show only modified files')),
5171 ('m', 'modified', None, _('show only modified files')),
5171 ('a', 'added', None, _('show only added files')),
5172 ('a', 'added', None, _('show only added files')),
5172 ('r', 'removed', None, _('show only removed files')),
5173 ('r', 'removed', None, _('show only removed files')),
5173 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5174 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5174 ('c', 'clean', None, _('show only files without changes')),
5175 ('c', 'clean', None, _('show only files without changes')),
5175 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5176 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5176 ('i', 'ignored', None, _('show only ignored files')),
5177 ('i', 'ignored', None, _('show only ignored files')),
5177 ('n', 'no-status', None, _('hide status prefix')),
5178 ('n', 'no-status', None, _('hide status prefix')),
5178 ('C', 'copies', None, _('show source of copied files')),
5179 ('C', 'copies', None, _('show source of copied files')),
5179 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5180 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5180 ('', 'rev', [], _('show difference from revision'), _('REV')),
5181 ('', 'rev', [], _('show difference from revision'), _('REV')),
5181 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5182 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5182 ] + walkopts + subrepoopts,
5183 ] + walkopts + subrepoopts,
5183 _('[OPTION]... [FILE]...'))
5184 _('[OPTION]... [FILE]...'))
5184 def status(ui, repo, *pats, **opts):
5185 def status(ui, repo, *pats, **opts):
5185 """show changed files in the working directory
5186 """show changed files in the working directory
5186
5187
5187 Show status of files in the repository. If names are given, only
5188 Show status of files in the repository. If names are given, only
5188 files that match are shown. Files that are clean or ignored or
5189 files that match are shown. Files that are clean or ignored or
5189 the source of a copy/move operation, are not listed unless
5190 the source of a copy/move operation, are not listed unless
5190 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5191 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5191 Unless options described with "show only ..." are given, the
5192 Unless options described with "show only ..." are given, the
5192 options -mardu are used.
5193 options -mardu are used.
5193
5194
5194 Option -q/--quiet hides untracked (unknown and ignored) files
5195 Option -q/--quiet hides untracked (unknown and ignored) files
5195 unless explicitly requested with -u/--unknown or -i/--ignored.
5196 unless explicitly requested with -u/--unknown or -i/--ignored.
5196
5197
5197 .. note::
5198 .. note::
5198 status may appear to disagree with diff if permissions have
5199 status may appear to disagree with diff if permissions have
5199 changed or a merge has occurred. The standard diff format does
5200 changed or a merge has occurred. The standard diff format does
5200 not report permission changes and diff only reports changes
5201 not report permission changes and diff only reports changes
5201 relative to one merge parent.
5202 relative to one merge parent.
5202
5203
5203 If one revision is given, it is used as the base revision.
5204 If one revision is given, it is used as the base revision.
5204 If two revisions are given, the differences between them are
5205 If two revisions are given, the differences between them are
5205 shown. The --change option can also be used as a shortcut to list
5206 shown. The --change option can also be used as a shortcut to list
5206 the changed files of a revision from its first parent.
5207 the changed files of a revision from its first parent.
5207
5208
5208 The codes used to show the status of files are::
5209 The codes used to show the status of files are::
5209
5210
5210 M = modified
5211 M = modified
5211 A = added
5212 A = added
5212 R = removed
5213 R = removed
5213 C = clean
5214 C = clean
5214 ! = missing (deleted by non-hg command, but still tracked)
5215 ! = missing (deleted by non-hg command, but still tracked)
5215 ? = not tracked
5216 ? = not tracked
5216 I = ignored
5217 I = ignored
5217 = origin of the previous file listed as A (added)
5218 = origin of the previous file listed as A (added)
5218
5219
5219 .. container:: verbose
5220 .. container:: verbose
5220
5221
5221 Examples:
5222 Examples:
5222
5223
5223 - show changes in the working directory relative to a
5224 - show changes in the working directory relative to a
5224 changeset::
5225 changeset::
5225
5226
5226 hg status --rev 9353
5227 hg status --rev 9353
5227
5228
5228 - show all changes including copies in an existing changeset::
5229 - show all changes including copies in an existing changeset::
5229
5230
5230 hg status --copies --change 9353
5231 hg status --copies --change 9353
5231
5232
5232 - get a NUL separated list of added files, suitable for xargs::
5233 - get a NUL separated list of added files, suitable for xargs::
5233
5234
5234 hg status -an0
5235 hg status -an0
5235
5236
5236 Returns 0 on success.
5237 Returns 0 on success.
5237 """
5238 """
5238
5239
5239 revs = opts.get('rev')
5240 revs = opts.get('rev')
5240 change = opts.get('change')
5241 change = opts.get('change')
5241
5242
5242 if revs and change:
5243 if revs and change:
5243 msg = _('cannot specify --rev and --change at the same time')
5244 msg = _('cannot specify --rev and --change at the same time')
5244 raise util.Abort(msg)
5245 raise util.Abort(msg)
5245 elif change:
5246 elif change:
5246 node2 = scmutil.revsingle(repo, change, None).node()
5247 node2 = scmutil.revsingle(repo, change, None).node()
5247 node1 = repo[node2].p1().node()
5248 node1 = repo[node2].p1().node()
5248 else:
5249 else:
5249 node1, node2 = scmutil.revpair(repo, revs)
5250 node1, node2 = scmutil.revpair(repo, revs)
5250
5251
5251 cwd = (pats and repo.getcwd()) or ''
5252 cwd = (pats and repo.getcwd()) or ''
5252 end = opts.get('print0') and '\0' or '\n'
5253 end = opts.get('print0') and '\0' or '\n'
5253 copy = {}
5254 copy = {}
5254 states = 'modified added removed deleted unknown ignored clean'.split()
5255 states = 'modified added removed deleted unknown ignored clean'.split()
5255 show = [k for k in states if opts.get(k)]
5256 show = [k for k in states if opts.get(k)]
5256 if opts.get('all'):
5257 if opts.get('all'):
5257 show += ui.quiet and (states[:4] + ['clean']) or states
5258 show += ui.quiet and (states[:4] + ['clean']) or states
5258 if not show:
5259 if not show:
5259 show = ui.quiet and states[:4] or states[:5]
5260 show = ui.quiet and states[:4] or states[:5]
5260
5261
5261 stat = repo.status(node1, node2, scmutil.match(repo[node2], pats, opts),
5262 stat = repo.status(node1, node2, scmutil.match(repo[node2], pats, opts),
5262 'ignored' in show, 'clean' in show, 'unknown' in show,
5263 'ignored' in show, 'clean' in show, 'unknown' in show,
5263 opts.get('subrepos'))
5264 opts.get('subrepos'))
5264 changestates = zip(states, 'MAR!?IC', stat)
5265 changestates = zip(states, 'MAR!?IC', stat)
5265
5266
5266 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
5267 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
5267 copy = copies.pathcopies(repo[node1], repo[node2])
5268 copy = copies.pathcopies(repo[node1], repo[node2])
5268
5269
5269 fm = ui.formatter('status', opts)
5270 fm = ui.formatter('status', opts)
5270 format = '%s %s' + end
5271 format = '%s %s' + end
5271 if opts.get('no_status'):
5272 if opts.get('no_status'):
5272 format = '%.0s%s' + end
5273 format = '%.0s%s' + end
5273
5274
5274 for state, char, files in changestates:
5275 for state, char, files in changestates:
5275 if state in show:
5276 if state in show:
5276 label = 'status.' + state
5277 label = 'status.' + state
5277 for f in files:
5278 for f in files:
5278 fm.startitem()
5279 fm.startitem()
5279 fm.write("status path", format, char,
5280 fm.write("status path", format, char,
5280 repo.pathto(f, cwd), label=label)
5281 repo.pathto(f, cwd), label=label)
5281 if f in copy:
5282 if f in copy:
5282 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
5283 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
5283 label='status.copied')
5284 label='status.copied')
5284 fm.end()
5285 fm.end()
5285
5286
5286 @command('^summary|sum',
5287 @command('^summary|sum',
5287 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5288 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5288 def summary(ui, repo, **opts):
5289 def summary(ui, repo, **opts):
5289 """summarize working directory state
5290 """summarize working directory state
5290
5291
5291 This generates a brief summary of the working directory state,
5292 This generates a brief summary of the working directory state,
5292 including parents, branch, commit status, and available updates.
5293 including parents, branch, commit status, and available updates.
5293
5294
5294 With the --remote option, this will check the default paths for
5295 With the --remote option, this will check the default paths for
5295 incoming and outgoing changes. This can be time-consuming.
5296 incoming and outgoing changes. This can be time-consuming.
5296
5297
5297 Returns 0 on success.
5298 Returns 0 on success.
5298 """
5299 """
5299
5300
5300 ctx = repo[None]
5301 ctx = repo[None]
5301 parents = ctx.parents()
5302 parents = ctx.parents()
5302 pnode = parents[0].node()
5303 pnode = parents[0].node()
5303 marks = []
5304 marks = []
5304
5305
5305 for p in parents:
5306 for p in parents:
5306 # label with log.changeset (instead of log.parent) since this
5307 # label with log.changeset (instead of log.parent) since this
5307 # shows a working directory parent *changeset*:
5308 # shows a working directory parent *changeset*:
5308 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5309 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5309 label='log.changeset')
5310 label='log.changeset')
5310 ui.write(' '.join(p.tags()), label='log.tag')
5311 ui.write(' '.join(p.tags()), label='log.tag')
5311 if p.bookmarks():
5312 if p.bookmarks():
5312 marks.extend(p.bookmarks())
5313 marks.extend(p.bookmarks())
5313 if p.rev() == -1:
5314 if p.rev() == -1:
5314 if not len(repo):
5315 if not len(repo):
5315 ui.write(_(' (empty repository)'))
5316 ui.write(_(' (empty repository)'))
5316 else:
5317 else:
5317 ui.write(_(' (no revision checked out)'))
5318 ui.write(_(' (no revision checked out)'))
5318 ui.write('\n')
5319 ui.write('\n')
5319 if p.description():
5320 if p.description():
5320 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5321 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5321 label='log.summary')
5322 label='log.summary')
5322
5323
5323 branch = ctx.branch()
5324 branch = ctx.branch()
5324 bheads = repo.branchheads(branch)
5325 bheads = repo.branchheads(branch)
5325 m = _('branch: %s\n') % branch
5326 m = _('branch: %s\n') % branch
5326 if branch != 'default':
5327 if branch != 'default':
5327 ui.write(m, label='log.branch')
5328 ui.write(m, label='log.branch')
5328 else:
5329 else:
5329 ui.status(m, label='log.branch')
5330 ui.status(m, label='log.branch')
5330
5331
5331 if marks:
5332 if marks:
5332 current = repo._bookmarkcurrent
5333 current = repo._bookmarkcurrent
5333 ui.write(_('bookmarks:'), label='log.bookmark')
5334 ui.write(_('bookmarks:'), label='log.bookmark')
5334 if current is not None:
5335 if current is not None:
5335 try:
5336 try:
5336 marks.remove(current)
5337 marks.remove(current)
5337 ui.write(' *' + current, label='bookmarks.current')
5338 ui.write(' *' + current, label='bookmarks.current')
5338 except ValueError:
5339 except ValueError:
5339 # current bookmark not in parent ctx marks
5340 # current bookmark not in parent ctx marks
5340 pass
5341 pass
5341 for m in marks:
5342 for m in marks:
5342 ui.write(' ' + m, label='log.bookmark')
5343 ui.write(' ' + m, label='log.bookmark')
5343 ui.write('\n', label='log.bookmark')
5344 ui.write('\n', label='log.bookmark')
5344
5345
5345 st = list(repo.status(unknown=True))[:6]
5346 st = list(repo.status(unknown=True))[:6]
5346
5347
5347 c = repo.dirstate.copies()
5348 c = repo.dirstate.copies()
5348 copied, renamed = [], []
5349 copied, renamed = [], []
5349 for d, s in c.iteritems():
5350 for d, s in c.iteritems():
5350 if s in st[2]:
5351 if s in st[2]:
5351 st[2].remove(s)
5352 st[2].remove(s)
5352 renamed.append(d)
5353 renamed.append(d)
5353 else:
5354 else:
5354 copied.append(d)
5355 copied.append(d)
5355 if d in st[1]:
5356 if d in st[1]:
5356 st[1].remove(d)
5357 st[1].remove(d)
5357 st.insert(3, renamed)
5358 st.insert(3, renamed)
5358 st.insert(4, copied)
5359 st.insert(4, copied)
5359
5360
5360 ms = mergemod.mergestate(repo)
5361 ms = mergemod.mergestate(repo)
5361 st.append([f for f in ms if ms[f] == 'u'])
5362 st.append([f for f in ms if ms[f] == 'u'])
5362
5363
5363 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5364 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5364 st.append(subs)
5365 st.append(subs)
5365
5366
5366 labels = [ui.label(_('%d modified'), 'status.modified'),
5367 labels = [ui.label(_('%d modified'), 'status.modified'),
5367 ui.label(_('%d added'), 'status.added'),
5368 ui.label(_('%d added'), 'status.added'),
5368 ui.label(_('%d removed'), 'status.removed'),
5369 ui.label(_('%d removed'), 'status.removed'),
5369 ui.label(_('%d renamed'), 'status.copied'),
5370 ui.label(_('%d renamed'), 'status.copied'),
5370 ui.label(_('%d copied'), 'status.copied'),
5371 ui.label(_('%d copied'), 'status.copied'),
5371 ui.label(_('%d deleted'), 'status.deleted'),
5372 ui.label(_('%d deleted'), 'status.deleted'),
5372 ui.label(_('%d unknown'), 'status.unknown'),
5373 ui.label(_('%d unknown'), 'status.unknown'),
5373 ui.label(_('%d ignored'), 'status.ignored'),
5374 ui.label(_('%d ignored'), 'status.ignored'),
5374 ui.label(_('%d unresolved'), 'resolve.unresolved'),
5375 ui.label(_('%d unresolved'), 'resolve.unresolved'),
5375 ui.label(_('%d subrepos'), 'status.modified')]
5376 ui.label(_('%d subrepos'), 'status.modified')]
5376 t = []
5377 t = []
5377 for s, l in zip(st, labels):
5378 for s, l in zip(st, labels):
5378 if s:
5379 if s:
5379 t.append(l % len(s))
5380 t.append(l % len(s))
5380
5381
5381 t = ', '.join(t)
5382 t = ', '.join(t)
5382 cleanworkdir = False
5383 cleanworkdir = False
5383
5384
5384 if len(parents) > 1:
5385 if len(parents) > 1:
5385 t += _(' (merge)')
5386 t += _(' (merge)')
5386 elif branch != parents[0].branch():
5387 elif branch != parents[0].branch():
5387 t += _(' (new branch)')
5388 t += _(' (new branch)')
5388 elif (parents[0].closesbranch() and
5389 elif (parents[0].closesbranch() and
5389 pnode in repo.branchheads(branch, closed=True)):
5390 pnode in repo.branchheads(branch, closed=True)):
5390 t += _(' (head closed)')
5391 t += _(' (head closed)')
5391 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
5392 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
5392 t += _(' (clean)')
5393 t += _(' (clean)')
5393 cleanworkdir = True
5394 cleanworkdir = True
5394 elif pnode not in bheads:
5395 elif pnode not in bheads:
5395 t += _(' (new branch head)')
5396 t += _(' (new branch head)')
5396
5397
5397 if cleanworkdir:
5398 if cleanworkdir:
5398 ui.status(_('commit: %s\n') % t.strip())
5399 ui.status(_('commit: %s\n') % t.strip())
5399 else:
5400 else:
5400 ui.write(_('commit: %s\n') % t.strip())
5401 ui.write(_('commit: %s\n') % t.strip())
5401
5402
5402 # all ancestors of branch heads - all ancestors of parent = new csets
5403 # all ancestors of branch heads - all ancestors of parent = new csets
5403 new = [0] * len(repo)
5404 new = [0] * len(repo)
5404 cl = repo.changelog
5405 cl = repo.changelog
5405 for a in [cl.rev(n) for n in bheads]:
5406 for a in [cl.rev(n) for n in bheads]:
5406 new[a] = 1
5407 new[a] = 1
5407 for a in cl.ancestors([cl.rev(n) for n in bheads]):
5408 for a in cl.ancestors([cl.rev(n) for n in bheads]):
5408 new[a] = 1
5409 new[a] = 1
5409 for a in [p.rev() for p in parents]:
5410 for a in [p.rev() for p in parents]:
5410 if a >= 0:
5411 if a >= 0:
5411 new[a] = 0
5412 new[a] = 0
5412 for a in cl.ancestors([p.rev() for p in parents]):
5413 for a in cl.ancestors([p.rev() for p in parents]):
5413 new[a] = 0
5414 new[a] = 0
5414 new = sum(new)
5415 new = sum(new)
5415
5416
5416 if new == 0:
5417 if new == 0:
5417 ui.status(_('update: (current)\n'))
5418 ui.status(_('update: (current)\n'))
5418 elif pnode not in bheads:
5419 elif pnode not in bheads:
5419 ui.write(_('update: %d new changesets (update)\n') % new)
5420 ui.write(_('update: %d new changesets (update)\n') % new)
5420 else:
5421 else:
5421 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5422 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5422 (new, len(bheads)))
5423 (new, len(bheads)))
5423
5424
5424 if opts.get('remote'):
5425 if opts.get('remote'):
5425 t = []
5426 t = []
5426 source, branches = hg.parseurl(ui.expandpath('default'))
5427 source, branches = hg.parseurl(ui.expandpath('default'))
5427 other = hg.peer(repo, {}, source)
5428 other = hg.peer(repo, {}, source)
5428 revs, checkout = hg.addbranchrevs(repo, other, branches,
5429 revs, checkout = hg.addbranchrevs(repo, other, branches,
5429 opts.get('rev'))
5430 opts.get('rev'))
5430 ui.debug('comparing with %s\n' % util.hidepassword(source))
5431 ui.debug('comparing with %s\n' % util.hidepassword(source))
5431 repo.ui.pushbuffer()
5432 repo.ui.pushbuffer()
5432 commoninc = discovery.findcommonincoming(repo, other)
5433 commoninc = discovery.findcommonincoming(repo, other)
5433 _common, incoming, _rheads = commoninc
5434 _common, incoming, _rheads = commoninc
5434 repo.ui.popbuffer()
5435 repo.ui.popbuffer()
5435 if incoming:
5436 if incoming:
5436 t.append(_('1 or more incoming'))
5437 t.append(_('1 or more incoming'))
5437
5438
5438 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5439 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5439 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5440 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5440 if source != dest:
5441 if source != dest:
5441 other = hg.peer(repo, {}, dest)
5442 other = hg.peer(repo, {}, dest)
5442 commoninc = None
5443 commoninc = None
5443 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5444 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5444 repo.ui.pushbuffer()
5445 repo.ui.pushbuffer()
5445 outgoing = discovery.findcommonoutgoing(repo, other,
5446 outgoing = discovery.findcommonoutgoing(repo, other,
5446 commoninc=commoninc)
5447 commoninc=commoninc)
5447 repo.ui.popbuffer()
5448 repo.ui.popbuffer()
5448 o = outgoing.missing
5449 o = outgoing.missing
5449 if o:
5450 if o:
5450 t.append(_('%d outgoing') % len(o))
5451 t.append(_('%d outgoing') % len(o))
5451 if 'bookmarks' in other.listkeys('namespaces'):
5452 if 'bookmarks' in other.listkeys('namespaces'):
5452 lmarks = repo.listkeys('bookmarks')
5453 lmarks = repo.listkeys('bookmarks')
5453 rmarks = other.listkeys('bookmarks')
5454 rmarks = other.listkeys('bookmarks')
5454 diff = set(rmarks) - set(lmarks)
5455 diff = set(rmarks) - set(lmarks)
5455 if len(diff) > 0:
5456 if len(diff) > 0:
5456 t.append(_('%d incoming bookmarks') % len(diff))
5457 t.append(_('%d incoming bookmarks') % len(diff))
5457 diff = set(lmarks) - set(rmarks)
5458 diff = set(lmarks) - set(rmarks)
5458 if len(diff) > 0:
5459 if len(diff) > 0:
5459 t.append(_('%d outgoing bookmarks') % len(diff))
5460 t.append(_('%d outgoing bookmarks') % len(diff))
5460
5461
5461 if t:
5462 if t:
5462 ui.write(_('remote: %s\n') % (', '.join(t)))
5463 ui.write(_('remote: %s\n') % (', '.join(t)))
5463 else:
5464 else:
5464 ui.status(_('remote: (synced)\n'))
5465 ui.status(_('remote: (synced)\n'))
5465
5466
5466 @command('tag',
5467 @command('tag',
5467 [('f', 'force', None, _('force tag')),
5468 [('f', 'force', None, _('force tag')),
5468 ('l', 'local', None, _('make the tag local')),
5469 ('l', 'local', None, _('make the tag local')),
5469 ('r', 'rev', '', _('revision to tag'), _('REV')),
5470 ('r', 'rev', '', _('revision to tag'), _('REV')),
5470 ('', 'remove', None, _('remove a tag')),
5471 ('', 'remove', None, _('remove a tag')),
5471 # -l/--local is already there, commitopts cannot be used
5472 # -l/--local is already there, commitopts cannot be used
5472 ('e', 'edit', None, _('edit commit message')),
5473 ('e', 'edit', None, _('edit commit message')),
5473 ('m', 'message', '', _('use <text> as commit message'), _('TEXT')),
5474 ('m', 'message', '', _('use <text> as commit message'), _('TEXT')),
5474 ] + commitopts2,
5475 ] + commitopts2,
5475 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5476 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5476 def tag(ui, repo, name1, *names, **opts):
5477 def tag(ui, repo, name1, *names, **opts):
5477 """add one or more tags for the current or given revision
5478 """add one or more tags for the current or given revision
5478
5479
5479 Name a particular revision using <name>.
5480 Name a particular revision using <name>.
5480
5481
5481 Tags are used to name particular revisions of the repository and are
5482 Tags are used to name particular revisions of the repository and are
5482 very useful to compare different revisions, to go back to significant
5483 very useful to compare different revisions, to go back to significant
5483 earlier versions or to mark branch points as releases, etc. Changing
5484 earlier versions or to mark branch points as releases, etc. Changing
5484 an existing tag is normally disallowed; use -f/--force to override.
5485 an existing tag is normally disallowed; use -f/--force to override.
5485
5486
5486 If no revision is given, the parent of the working directory is
5487 If no revision is given, the parent of the working directory is
5487 used, or tip if no revision is checked out.
5488 used, or tip if no revision is checked out.
5488
5489
5489 To facilitate version control, distribution, and merging of tags,
5490 To facilitate version control, distribution, and merging of tags,
5490 they are stored as a file named ".hgtags" which is managed similarly
5491 they are stored as a file named ".hgtags" which is managed similarly
5491 to other project files and can be hand-edited if necessary. This
5492 to other project files and can be hand-edited if necessary. This
5492 also means that tagging creates a new commit. The file
5493 also means that tagging creates a new commit. The file
5493 ".hg/localtags" is used for local tags (not shared among
5494 ".hg/localtags" is used for local tags (not shared among
5494 repositories).
5495 repositories).
5495
5496
5496 Tag commits are usually made at the head of a branch. If the parent
5497 Tag commits are usually made at the head of a branch. If the parent
5497 of the working directory is not a branch head, :hg:`tag` aborts; use
5498 of the working directory is not a branch head, :hg:`tag` aborts; use
5498 -f/--force to force the tag commit to be based on a non-head
5499 -f/--force to force the tag commit to be based on a non-head
5499 changeset.
5500 changeset.
5500
5501
5501 See :hg:`help dates` for a list of formats valid for -d/--date.
5502 See :hg:`help dates` for a list of formats valid for -d/--date.
5502
5503
5503 Since tag names have priority over branch names during revision
5504 Since tag names have priority over branch names during revision
5504 lookup, using an existing branch name as a tag name is discouraged.
5505 lookup, using an existing branch name as a tag name is discouraged.
5505
5506
5506 Returns 0 on success.
5507 Returns 0 on success.
5507 """
5508 """
5508 wlock = lock = None
5509 wlock = lock = None
5509 try:
5510 try:
5510 wlock = repo.wlock()
5511 wlock = repo.wlock()
5511 lock = repo.lock()
5512 lock = repo.lock()
5512 rev_ = "."
5513 rev_ = "."
5513 names = [t.strip() for t in (name1,) + names]
5514 names = [t.strip() for t in (name1,) + names]
5514 if len(names) != len(set(names)):
5515 if len(names) != len(set(names)):
5515 raise util.Abort(_('tag names must be unique'))
5516 raise util.Abort(_('tag names must be unique'))
5516 for n in names:
5517 for n in names:
5517 if n in ['tip', '.', 'null']:
5518 if n in ['tip', '.', 'null']:
5518 raise util.Abort(_("the name '%s' is reserved") % n)
5519 raise util.Abort(_("the name '%s' is reserved") % n)
5519 if not n:
5520 if not n:
5520 raise util.Abort(_('tag names cannot consist entirely of '
5521 raise util.Abort(_('tag names cannot consist entirely of '
5521 'whitespace'))
5522 'whitespace'))
5522 if opts.get('rev') and opts.get('remove'):
5523 if opts.get('rev') and opts.get('remove'):
5523 raise util.Abort(_("--rev and --remove are incompatible"))
5524 raise util.Abort(_("--rev and --remove are incompatible"))
5524 if opts.get('rev'):
5525 if opts.get('rev'):
5525 rev_ = opts['rev']
5526 rev_ = opts['rev']
5526 message = opts.get('message')
5527 message = opts.get('message')
5527 if opts.get('remove'):
5528 if opts.get('remove'):
5528 expectedtype = opts.get('local') and 'local' or 'global'
5529 expectedtype = opts.get('local') and 'local' or 'global'
5529 for n in names:
5530 for n in names:
5530 if not repo.tagtype(n):
5531 if not repo.tagtype(n):
5531 raise util.Abort(_("tag '%s' does not exist") % n)
5532 raise util.Abort(_("tag '%s' does not exist") % n)
5532 if repo.tagtype(n) != expectedtype:
5533 if repo.tagtype(n) != expectedtype:
5533 if expectedtype == 'global':
5534 if expectedtype == 'global':
5534 raise util.Abort(_("tag '%s' is not a global tag") % n)
5535 raise util.Abort(_("tag '%s' is not a global tag") % n)
5535 else:
5536 else:
5536 raise util.Abort(_("tag '%s' is not a local tag") % n)
5537 raise util.Abort(_("tag '%s' is not a local tag") % n)
5537 rev_ = nullid
5538 rev_ = nullid
5538 if not message:
5539 if not message:
5539 # we don't translate commit messages
5540 # we don't translate commit messages
5540 message = 'Removed tag %s' % ', '.join(names)
5541 message = 'Removed tag %s' % ', '.join(names)
5541 elif not opts.get('force'):
5542 elif not opts.get('force'):
5542 for n in names:
5543 for n in names:
5543 if n in repo.tags():
5544 if n in repo.tags():
5544 raise util.Abort(_("tag '%s' already exists "
5545 raise util.Abort(_("tag '%s' already exists "
5545 "(use -f to force)") % n)
5546 "(use -f to force)") % n)
5546 if not opts.get('local'):
5547 if not opts.get('local'):
5547 p1, p2 = repo.dirstate.parents()
5548 p1, p2 = repo.dirstate.parents()
5548 if p2 != nullid:
5549 if p2 != nullid:
5549 raise util.Abort(_('uncommitted merge'))
5550 raise util.Abort(_('uncommitted merge'))
5550 bheads = repo.branchheads()
5551 bheads = repo.branchheads()
5551 if not opts.get('force') and bheads and p1 not in bheads:
5552 if not opts.get('force') and bheads and p1 not in bheads:
5552 raise util.Abort(_('not at a branch head (use -f to force)'))
5553 raise util.Abort(_('not at a branch head (use -f to force)'))
5553 r = scmutil.revsingle(repo, rev_).node()
5554 r = scmutil.revsingle(repo, rev_).node()
5554
5555
5555 if not message:
5556 if not message:
5556 # we don't translate commit messages
5557 # we don't translate commit messages
5557 message = ('Added tag %s for changeset %s' %
5558 message = ('Added tag %s for changeset %s' %
5558 (', '.join(names), short(r)))
5559 (', '.join(names), short(r)))
5559
5560
5560 date = opts.get('date')
5561 date = opts.get('date')
5561 if date:
5562 if date:
5562 date = util.parsedate(date)
5563 date = util.parsedate(date)
5563
5564
5564 if opts.get('edit'):
5565 if opts.get('edit'):
5565 message = ui.edit(message, ui.username())
5566 message = ui.edit(message, ui.username())
5566
5567
5567 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
5568 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
5568 finally:
5569 finally:
5569 release(lock, wlock)
5570 release(lock, wlock)
5570
5571
5571 @command('tags', [], '')
5572 @command('tags', [], '')
5572 def tags(ui, repo):
5573 def tags(ui, repo):
5573 """list repository tags
5574 """list repository tags
5574
5575
5575 This lists both regular and local tags. When the -v/--verbose
5576 This lists both regular and local tags. When the -v/--verbose
5576 switch is used, a third column "local" is printed for local tags.
5577 switch is used, a third column "local" is printed for local tags.
5577
5578
5578 Returns 0 on success.
5579 Returns 0 on success.
5579 """
5580 """
5580
5581
5581 hexfunc = ui.debugflag and hex or short
5582 hexfunc = ui.debugflag and hex or short
5582 tagtype = ""
5583 tagtype = ""
5583
5584
5584 for t, n in reversed(repo.tagslist()):
5585 for t, n in reversed(repo.tagslist()):
5585 if ui.quiet:
5586 if ui.quiet:
5586 ui.write("%s\n" % t, label='tags.normal')
5587 ui.write("%s\n" % t, label='tags.normal')
5587 continue
5588 continue
5588
5589
5589 hn = hexfunc(n)
5590 hn = hexfunc(n)
5590 r = "%5d:%s" % (repo.changelog.rev(n), hn)
5591 r = "%5d:%s" % (repo.changelog.rev(n), hn)
5591 rev = ui.label(r, 'log.changeset')
5592 rev = ui.label(r, 'log.changeset')
5592 spaces = " " * (30 - encoding.colwidth(t))
5593 spaces = " " * (30 - encoding.colwidth(t))
5593
5594
5594 tag = ui.label(t, 'tags.normal')
5595 tag = ui.label(t, 'tags.normal')
5595 if ui.verbose:
5596 if ui.verbose:
5596 if repo.tagtype(t) == 'local':
5597 if repo.tagtype(t) == 'local':
5597 tagtype = " local"
5598 tagtype = " local"
5598 tag = ui.label(t, 'tags.local')
5599 tag = ui.label(t, 'tags.local')
5599 else:
5600 else:
5600 tagtype = ""
5601 tagtype = ""
5601 ui.write("%s%s %s%s\n" % (tag, spaces, rev, tagtype))
5602 ui.write("%s%s %s%s\n" % (tag, spaces, rev, tagtype))
5602
5603
5603 @command('tip',
5604 @command('tip',
5604 [('p', 'patch', None, _('show patch')),
5605 [('p', 'patch', None, _('show patch')),
5605 ('g', 'git', None, _('use git extended diff format')),
5606 ('g', 'git', None, _('use git extended diff format')),
5606 ] + templateopts,
5607 ] + templateopts,
5607 _('[-p] [-g]'))
5608 _('[-p] [-g]'))
5608 def tip(ui, repo, **opts):
5609 def tip(ui, repo, **opts):
5609 """show the tip revision
5610 """show the tip revision
5610
5611
5611 The tip revision (usually just called the tip) is the changeset
5612 The tip revision (usually just called the tip) is the changeset
5612 most recently added to the repository (and therefore the most
5613 most recently added to the repository (and therefore the most
5613 recently changed head).
5614 recently changed head).
5614
5615
5615 If you have just made a commit, that commit will be the tip. If
5616 If you have just made a commit, that commit will be the tip. If
5616 you have just pulled changes from another repository, the tip of
5617 you have just pulled changes from another repository, the tip of
5617 that repository becomes the current tip. The "tip" tag is special
5618 that repository becomes the current tip. The "tip" tag is special
5618 and cannot be renamed or assigned to a different changeset.
5619 and cannot be renamed or assigned to a different changeset.
5619
5620
5620 Returns 0 on success.
5621 Returns 0 on success.
5621 """
5622 """
5622 displayer = cmdutil.show_changeset(ui, repo, opts)
5623 displayer = cmdutil.show_changeset(ui, repo, opts)
5623 displayer.show(repo[len(repo) - 1])
5624 displayer.show(repo[len(repo) - 1])
5624 displayer.close()
5625 displayer.close()
5625
5626
5626 @command('unbundle',
5627 @command('unbundle',
5627 [('u', 'update', None,
5628 [('u', 'update', None,
5628 _('update to new branch head if changesets were unbundled'))],
5629 _('update to new branch head if changesets were unbundled'))],
5629 _('[-u] FILE...'))
5630 _('[-u] FILE...'))
5630 def unbundle(ui, repo, fname1, *fnames, **opts):
5631 def unbundle(ui, repo, fname1, *fnames, **opts):
5631 """apply one or more changegroup files
5632 """apply one or more changegroup files
5632
5633
5633 Apply one or more compressed changegroup files generated by the
5634 Apply one or more compressed changegroup files generated by the
5634 bundle command.
5635 bundle command.
5635
5636
5636 Returns 0 on success, 1 if an update has unresolved files.
5637 Returns 0 on success, 1 if an update has unresolved files.
5637 """
5638 """
5638 fnames = (fname1,) + fnames
5639 fnames = (fname1,) + fnames
5639
5640
5640 lock = repo.lock()
5641 lock = repo.lock()
5641 wc = repo['.']
5642 wc = repo['.']
5642 try:
5643 try:
5643 for fname in fnames:
5644 for fname in fnames:
5644 f = url.open(ui, fname)
5645 f = url.open(ui, fname)
5645 gen = changegroup.readbundle(f, fname)
5646 gen = changegroup.readbundle(f, fname)
5646 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
5647 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
5647 finally:
5648 finally:
5648 lock.release()
5649 lock.release()
5649 bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
5650 bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
5650 return postincoming(ui, repo, modheads, opts.get('update'), None)
5651 return postincoming(ui, repo, modheads, opts.get('update'), None)
5651
5652
5652 @command('^update|up|checkout|co',
5653 @command('^update|up|checkout|co',
5653 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5654 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5654 ('c', 'check', None,
5655 ('c', 'check', None,
5655 _('update across branches if no uncommitted changes')),
5656 _('update across branches if no uncommitted changes')),
5656 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5657 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5657 ('r', 'rev', '', _('revision'), _('REV'))],
5658 ('r', 'rev', '', _('revision'), _('REV'))],
5658 _('[-c] [-C] [-d DATE] [[-r] REV]'))
5659 _('[-c] [-C] [-d DATE] [[-r] REV]'))
5659 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
5660 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
5660 """update working directory (or switch revisions)
5661 """update working directory (or switch revisions)
5661
5662
5662 Update the repository's working directory to the specified
5663 Update the repository's working directory to the specified
5663 changeset. If no changeset is specified, update to the tip of the
5664 changeset. If no changeset is specified, update to the tip of the
5664 current named branch and move the current bookmark (see :hg:`help
5665 current named branch and move the current bookmark (see :hg:`help
5665 bookmarks`).
5666 bookmarks`).
5666
5667
5667 If the changeset is not a descendant or ancestor of the working
5668 If the changeset is not a descendant or ancestor of the working
5668 directory's parent, the update is aborted. With the -c/--check
5669 directory's parent, the update is aborted. With the -c/--check
5669 option, the working directory is checked for uncommitted changes; if
5670 option, the working directory is checked for uncommitted changes; if
5670 none are found, the working directory is updated to the specified
5671 none are found, the working directory is updated to the specified
5671 changeset.
5672 changeset.
5672
5673
5673 Update sets the working directory's parent revison to the specified
5674 Update sets the working directory's parent revison to the specified
5674 changeset (see :hg:`help parents`).
5675 changeset (see :hg:`help parents`).
5675
5676
5676 The following rules apply when the working directory contains
5677 The following rules apply when the working directory contains
5677 uncommitted changes:
5678 uncommitted changes:
5678
5679
5679 1. If neither -c/--check nor -C/--clean is specified, and if
5680 1. If neither -c/--check nor -C/--clean is specified, and if
5680 the requested changeset is an ancestor or descendant of
5681 the requested changeset is an ancestor or descendant of
5681 the working directory's parent, the uncommitted changes
5682 the working directory's parent, the uncommitted changes
5682 are merged into the requested changeset and the merged
5683 are merged into the requested changeset and the merged
5683 result is left uncommitted. If the requested changeset is
5684 result is left uncommitted. If the requested changeset is
5684 not an ancestor or descendant (that is, it is on another
5685 not an ancestor or descendant (that is, it is on another
5685 branch), the update is aborted and the uncommitted changes
5686 branch), the update is aborted and the uncommitted changes
5686 are preserved.
5687 are preserved.
5687
5688
5688 2. With the -c/--check option, the update is aborted and the
5689 2. With the -c/--check option, the update is aborted and the
5689 uncommitted changes are preserved.
5690 uncommitted changes are preserved.
5690
5691
5691 3. With the -C/--clean option, uncommitted changes are discarded and
5692 3. With the -C/--clean option, uncommitted changes are discarded and
5692 the working directory is updated to the requested changeset.
5693 the working directory is updated to the requested changeset.
5693
5694
5694 Use null as the changeset to remove the working directory (like
5695 Use null as the changeset to remove the working directory (like
5695 :hg:`clone -U`).
5696 :hg:`clone -U`).
5696
5697
5697 If you want to revert just one file to an older revision, use
5698 If you want to revert just one file to an older revision, use
5698 :hg:`revert [-r REV] NAME`.
5699 :hg:`revert [-r REV] NAME`.
5699
5700
5700 See :hg:`help dates` for a list of formats valid for -d/--date.
5701 See :hg:`help dates` for a list of formats valid for -d/--date.
5701
5702
5702 Returns 0 on success, 1 if there are unresolved files.
5703 Returns 0 on success, 1 if there are unresolved files.
5703 """
5704 """
5704 if rev and node:
5705 if rev and node:
5705 raise util.Abort(_("please specify just one revision"))
5706 raise util.Abort(_("please specify just one revision"))
5706
5707
5707 if rev is None or rev == '':
5708 if rev is None or rev == '':
5708 rev = node
5709 rev = node
5709
5710
5710 # with no argument, we also move the current bookmark, if any
5711 # with no argument, we also move the current bookmark, if any
5711 movemarkfrom = None
5712 movemarkfrom = None
5712 if rev is None or node == '':
5713 if rev is None or node == '':
5713 movemarkfrom = repo['.'].node()
5714 movemarkfrom = repo['.'].node()
5714
5715
5715 # if we defined a bookmark, we have to remember the original bookmark name
5716 # if we defined a bookmark, we have to remember the original bookmark name
5716 brev = rev
5717 brev = rev
5717 rev = scmutil.revsingle(repo, rev, rev).rev()
5718 rev = scmutil.revsingle(repo, rev, rev).rev()
5718
5719
5719 if check and clean:
5720 if check and clean:
5720 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
5721 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
5721
5722
5722 if date:
5723 if date:
5723 if rev is not None:
5724 if rev is not None:
5724 raise util.Abort(_("you can't specify a revision and a date"))
5725 raise util.Abort(_("you can't specify a revision and a date"))
5725 rev = cmdutil.finddate(ui, repo, date)
5726 rev = cmdutil.finddate(ui, repo, date)
5726
5727
5727 if check:
5728 if check:
5728 c = repo[None]
5729 c = repo[None]
5729 if c.dirty(merge=False, branch=False):
5730 if c.dirty(merge=False, branch=False):
5730 raise util.Abort(_("uncommitted local changes"))
5731 raise util.Abort(_("uncommitted local changes"))
5731 if rev is None:
5732 if rev is None:
5732 rev = repo[repo[None].branch()].rev()
5733 rev = repo[repo[None].branch()].rev()
5733 mergemod._checkunknown(repo, repo[None], repo[rev])
5734 mergemod._checkunknown(repo, repo[None], repo[rev])
5734
5735
5735 if clean:
5736 if clean:
5736 ret = hg.clean(repo, rev)
5737 ret = hg.clean(repo, rev)
5737 else:
5738 else:
5738 ret = hg.update(repo, rev)
5739 ret = hg.update(repo, rev)
5739
5740
5740 if not ret and movemarkfrom:
5741 if not ret and movemarkfrom:
5741 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
5742 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
5742 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
5743 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
5743 elif brev in repo._bookmarks:
5744 elif brev in repo._bookmarks:
5744 bookmarks.setcurrent(repo, brev)
5745 bookmarks.setcurrent(repo, brev)
5745 elif brev:
5746 elif brev:
5746 bookmarks.unsetcurrent(repo)
5747 bookmarks.unsetcurrent(repo)
5747
5748
5748 return ret
5749 return ret
5749
5750
5750 @command('verify', [])
5751 @command('verify', [])
5751 def verify(ui, repo):
5752 def verify(ui, repo):
5752 """verify the integrity of the repository
5753 """verify the integrity of the repository
5753
5754
5754 Verify the integrity of the current repository.
5755 Verify the integrity of the current repository.
5755
5756
5756 This will perform an extensive check of the repository's
5757 This will perform an extensive check of the repository's
5757 integrity, validating the hashes and checksums of each entry in
5758 integrity, validating the hashes and checksums of each entry in
5758 the changelog, manifest, and tracked files, as well as the
5759 the changelog, manifest, and tracked files, as well as the
5759 integrity of their crosslinks and indices.
5760 integrity of their crosslinks and indices.
5760
5761
5761 Returns 0 on success, 1 if errors are encountered.
5762 Returns 0 on success, 1 if errors are encountered.
5762 """
5763 """
5763 return hg.verify(repo)
5764 return hg.verify(repo)
5764
5765
5765 @command('version', [])
5766 @command('version', [])
5766 def version_(ui):
5767 def version_(ui):
5767 """output version and copyright information"""
5768 """output version and copyright information"""
5768 ui.write(_("Mercurial Distributed SCM (version %s)\n")
5769 ui.write(_("Mercurial Distributed SCM (version %s)\n")
5769 % util.version())
5770 % util.version())
5770 ui.status(_(
5771 ui.status(_(
5771 "(see http://mercurial.selenic.com for more information)\n"
5772 "(see http://mercurial.selenic.com for more information)\n"
5772 "\nCopyright (C) 2005-2012 Matt Mackall and others\n"
5773 "\nCopyright (C) 2005-2012 Matt Mackall and others\n"
5773 "This is free software; see the source for copying conditions. "
5774 "This is free software; see the source for copying conditions. "
5774 "There is NO\nwarranty; "
5775 "There is NO\nwarranty; "
5775 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5776 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5776 ))
5777 ))
5777
5778
5778 norepo = ("clone init version help debugcommands debugcomplete"
5779 norepo = ("clone init version help debugcommands debugcomplete"
5779 " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
5780 " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
5780 " debugknown debuggetbundle debugbundle")
5781 " debugknown debuggetbundle debugbundle")
5781 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
5782 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
5782 " debugdata debugindex debugindexdot debugrevlog")
5783 " debugdata debugindex debugindexdot debugrevlog")
@@ -1,181 +1,184 b''
1 # hook.py - hook support for mercurial
1 # hook.py - hook support for mercurial
2 #
2 #
3 # Copyright 2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 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 i18n import _
8 from i18n import _
9 import os, sys
9 import os, sys
10 import extensions, util
10 import extensions, util
11
11
12 def _pythonhook(ui, repo, name, hname, funcname, args, throw):
12 def _pythonhook(ui, repo, name, hname, funcname, args, throw):
13 '''call python hook. hook is callable object, looked up as
13 '''call python hook. hook is callable object, looked up as
14 name in python module. if callable returns "true", hook
14 name in python module. if callable returns "true", hook
15 fails, else passes. if hook raises exception, treated as
15 fails, else passes. if hook raises exception, treated as
16 hook failure. exception propagates if throw is "true".
16 hook failure. exception propagates if throw is "true".
17
17
18 reason for "true" meaning "hook failed" is so that
18 reason for "true" meaning "hook failed" is so that
19 unmodified commands (e.g. mercurial.commands.update) can
19 unmodified commands (e.g. mercurial.commands.update) can
20 be run as hooks without wrappers to convert return values.'''
20 be run as hooks without wrappers to convert return values.'''
21
21
22 ui.note(_("calling hook %s: %s\n") % (hname, funcname))
22 ui.note(_("calling hook %s: %s\n") % (hname, funcname))
23 obj = funcname
23 obj = funcname
24 if not util.safehasattr(obj, '__call__'):
24 if not util.safehasattr(obj, '__call__'):
25 d = funcname.rfind('.')
25 d = funcname.rfind('.')
26 if d == -1:
26 if d == -1:
27 raise util.Abort(_('%s hook is invalid ("%s" not in '
27 raise util.Abort(_('%s hook is invalid ("%s" not in '
28 'a module)') % (hname, funcname))
28 'a module)') % (hname, funcname))
29 modname = funcname[:d]
29 modname = funcname[:d]
30 oldpaths = sys.path
30 oldpaths = sys.path
31 if util.mainfrozen():
31 if util.mainfrozen():
32 # binary installs require sys.path manipulation
32 # binary installs require sys.path manipulation
33 modpath, modfile = os.path.split(modname)
33 modpath, modfile = os.path.split(modname)
34 if modpath and modfile:
34 if modpath and modfile:
35 sys.path = sys.path[:] + [modpath]
35 sys.path = sys.path[:] + [modpath]
36 modname = modfile
36 modname = modfile
37 try:
37 try:
38 obj = __import__(modname)
38 obj = __import__(modname)
39 except ImportError:
39 except ImportError:
40 e1 = sys.exc_type, sys.exc_value, sys.exc_traceback
40 e1 = sys.exc_type, sys.exc_value, sys.exc_traceback
41 try:
41 try:
42 # extensions are loaded with hgext_ prefix
42 # extensions are loaded with hgext_ prefix
43 obj = __import__("hgext_%s" % modname)
43 obj = __import__("hgext_%s" % modname)
44 except ImportError:
44 except ImportError:
45 e2 = sys.exc_type, sys.exc_value, sys.exc_traceback
45 e2 = sys.exc_type, sys.exc_value, sys.exc_traceback
46 if ui.tracebackflag:
46 if ui.tracebackflag:
47 ui.warn(_('exception from first failed import attempt:\n'))
47 ui.warn(_('exception from first failed import attempt:\n'))
48 ui.traceback(e1)
48 ui.traceback(e1)
49 if ui.tracebackflag:
49 if ui.tracebackflag:
50 ui.warn(_('exception from second failed import attempt:\n'))
50 ui.warn(_('exception from second failed import attempt:\n'))
51 ui.traceback(e2)
51 ui.traceback(e2)
52 raise util.Abort(_('%s hook is invalid '
52 raise util.Abort(_('%s hook is invalid '
53 '(import of "%s" failed)') %
53 '(import of "%s" failed)') %
54 (hname, modname))
54 (hname, modname))
55 sys.path = oldpaths
55 sys.path = oldpaths
56 try:
56 try:
57 for p in funcname.split('.')[1:]:
57 for p in funcname.split('.')[1:]:
58 obj = getattr(obj, p)
58 obj = getattr(obj, p)
59 except AttributeError:
59 except AttributeError:
60 raise util.Abort(_('%s hook is invalid '
60 raise util.Abort(_('%s hook is invalid '
61 '("%s" is not defined)') %
61 '("%s" is not defined)') %
62 (hname, funcname))
62 (hname, funcname))
63 if not util.safehasattr(obj, '__call__'):
63 if not util.safehasattr(obj, '__call__'):
64 raise util.Abort(_('%s hook is invalid '
64 raise util.Abort(_('%s hook is invalid '
65 '("%s" is not callable)') %
65 '("%s" is not callable)') %
66 (hname, funcname))
66 (hname, funcname))
67 try:
67 try:
68 try:
68 try:
69 # redirect IO descriptors the the ui descriptors so hooks
69 # redirect IO descriptors the the ui descriptors so hooks
70 # that write directly to these don't mess up the command
70 # that write directly to these don't mess up the command
71 # protocol when running through the command server
71 # protocol when running through the command server
72 old = sys.stdout, sys.stderr, sys.stdin
72 old = sys.stdout, sys.stderr, sys.stdin
73 sys.stdout, sys.stderr, sys.stdin = ui.fout, ui.ferr, ui.fin
73 sys.stdout, sys.stderr, sys.stdin = ui.fout, ui.ferr, ui.fin
74
74
75 r = obj(ui=ui, repo=repo, hooktype=name, **args)
75 r = obj(ui=ui, repo=repo, hooktype=name, **args)
76 except KeyboardInterrupt:
76 except KeyboardInterrupt:
77 raise
77 raise
78 except Exception, exc:
78 except Exception, exc:
79 if isinstance(exc, util.Abort):
79 if isinstance(exc, util.Abort):
80 ui.warn(_('error: %s hook failed: %s\n') %
80 ui.warn(_('error: %s hook failed: %s\n') %
81 (hname, exc.args[0]))
81 (hname, exc.args[0]))
82 else:
82 else:
83 ui.warn(_('error: %s hook raised an exception: '
83 ui.warn(_('error: %s hook raised an exception: '
84 '%s\n') % (hname, exc))
84 '%s\n') % (hname, exc))
85 if throw:
85 if throw:
86 raise
86 raise
87 ui.traceback()
87 ui.traceback()
88 return True
88 return True
89 finally:
89 finally:
90 sys.stdout, sys.stderr, sys.stdin = old
90 sys.stdout, sys.stderr, sys.stdin = old
91 if r:
91 if r:
92 if throw:
92 if throw:
93 raise util.Abort(_('%s hook failed') % hname)
93 raise util.Abort(_('%s hook failed') % hname)
94 ui.warn(_('warning: %s hook failed\n') % hname)
94 ui.warn(_('warning: %s hook failed\n') % hname)
95 return r
95 return r
96
96
97 def _exthook(ui, repo, name, cmd, args, throw):
97 def _exthook(ui, repo, name, cmd, args, throw):
98 ui.note(_("running hook %s: %s\n") % (name, cmd))
98 ui.note(_("running hook %s: %s\n") % (name, cmd))
99
99
100 env = {}
100 env = {}
101 for k, v in args.iteritems():
101 for k, v in args.iteritems():
102 if util.safehasattr(v, '__call__'):
102 if util.safehasattr(v, '__call__'):
103 v = v()
103 v = v()
104 if isinstance(v, dict):
104 if isinstance(v, dict):
105 # make the dictionary element order stable across Python
105 # make the dictionary element order stable across Python
106 # implementations
106 # implementations
107 v = ('{' +
107 v = ('{' +
108 ', '.join('%r: %r' % i for i in sorted(v.iteritems())) +
108 ', '.join('%r: %r' % i for i in sorted(v.iteritems())) +
109 '}')
109 '}')
110 env['HG_' + k.upper()] = v
110 env['HG_' + k.upper()] = v
111
111
112 if repo:
112 if repo:
113 cwd = repo.root
113 cwd = repo.root
114 else:
114 else:
115 cwd = os.getcwd()
115 cwd = os.getcwd()
116 if 'HG_URL' in env and env['HG_URL'].startswith('remote:http'):
116 if 'HG_URL' in env and env['HG_URL'].startswith('remote:http'):
117 r = util.system(cmd, environ=env, cwd=cwd, out=ui)
117 r = util.system(cmd, environ=env, cwd=cwd, out=ui)
118 else:
118 else:
119 r = util.system(cmd, environ=env, cwd=cwd, out=ui.fout)
119 r = util.system(cmd, environ=env, cwd=cwd, out=ui.fout)
120 if r:
120 if r:
121 desc, r = util.explainexit(r)
121 desc, r = util.explainexit(r)
122 if throw:
122 if throw:
123 raise util.Abort(_('%s hook %s') % (name, desc))
123 raise util.Abort(_('%s hook %s') % (name, desc))
124 ui.warn(_('warning: %s hook %s\n') % (name, desc))
124 ui.warn(_('warning: %s hook %s\n') % (name, desc))
125 return r
125 return r
126
126
127 def _allhooks(ui):
127 def _allhooks(ui):
128 hooks = []
128 hooks = []
129 for name, cmd in ui.configitems('hooks'):
129 for name, cmd in ui.configitems('hooks'):
130 if not name.startswith('priority'):
130 if not name.startswith('priority'):
131 priority = ui.configint('hooks', 'priority.%s' % name, 0)
131 priority = ui.configint('hooks', 'priority.%s' % name, 0)
132 hooks.append((-priority, len(hooks), name, cmd))
132 hooks.append((-priority, len(hooks), name, cmd))
133 return [(k, v) for p, o, k, v in sorted(hooks)]
133 return [(k, v) for p, o, k, v in sorted(hooks)]
134
134
135 _redirect = False
135 _redirect = False
136 def redirect(state):
136 def redirect(state):
137 global _redirect
137 global _redirect
138 _redirect = state
138 _redirect = state
139
139
140 def hook(ui, repo, name, throw=False, **args):
140 def hook(ui, repo, name, throw=False, **args):
141 if not ui.callhooks:
142 return False
143
141 r = False
144 r = False
142
145
143 oldstdout = -1
146 oldstdout = -1
144 if _redirect:
147 if _redirect:
145 try:
148 try:
146 stdoutno = sys.__stdout__.fileno()
149 stdoutno = sys.__stdout__.fileno()
147 stderrno = sys.__stderr__.fileno()
150 stderrno = sys.__stderr__.fileno()
148 # temporarily redirect stdout to stderr, if possible
151 # temporarily redirect stdout to stderr, if possible
149 if stdoutno >= 0 and stderrno >= 0:
152 if stdoutno >= 0 and stderrno >= 0:
150 sys.__stdout__.flush()
153 sys.__stdout__.flush()
151 oldstdout = os.dup(stdoutno)
154 oldstdout = os.dup(stdoutno)
152 os.dup2(stderrno, stdoutno)
155 os.dup2(stderrno, stdoutno)
153 except AttributeError:
156 except AttributeError:
154 # __stdout/err__ doesn't have fileno(), it's not a real file
157 # __stdout/err__ doesn't have fileno(), it's not a real file
155 pass
158 pass
156
159
157 try:
160 try:
158 for hname, cmd in _allhooks(ui):
161 for hname, cmd in _allhooks(ui):
159 if hname.split('.')[0] != name or not cmd:
162 if hname.split('.')[0] != name or not cmd:
160 continue
163 continue
161 if util.safehasattr(cmd, '__call__'):
164 if util.safehasattr(cmd, '__call__'):
162 r = _pythonhook(ui, repo, name, hname, cmd, args, throw) or r
165 r = _pythonhook(ui, repo, name, hname, cmd, args, throw) or r
163 elif cmd.startswith('python:'):
166 elif cmd.startswith('python:'):
164 if cmd.count(':') >= 2:
167 if cmd.count(':') >= 2:
165 path, cmd = cmd[7:].rsplit(':', 1)
168 path, cmd = cmd[7:].rsplit(':', 1)
166 path = util.expandpath(path)
169 path = util.expandpath(path)
167 if repo:
170 if repo:
168 path = os.path.join(repo.root, path)
171 path = os.path.join(repo.root, path)
169 mod = extensions.loadpath(path, 'hghook.%s' % hname)
172 mod = extensions.loadpath(path, 'hghook.%s' % hname)
170 hookfn = getattr(mod, cmd)
173 hookfn = getattr(mod, cmd)
171 else:
174 else:
172 hookfn = cmd[7:].strip()
175 hookfn = cmd[7:].strip()
173 r = _pythonhook(ui, repo, name, hname, hookfn, args, throw) or r
176 r = _pythonhook(ui, repo, name, hname, hookfn, args, throw) or r
174 else:
177 else:
175 r = _exthook(ui, repo, hname, cmd, args, throw) or r
178 r = _exthook(ui, repo, hname, cmd, args, throw) or r
176 finally:
179 finally:
177 if _redirect and oldstdout >= 0:
180 if _redirect and oldstdout >= 0:
178 os.dup2(oldstdout, stdoutno)
181 os.dup2(oldstdout, stdoutno)
179 os.close(oldstdout)
182 os.close(oldstdout)
180
183
181 return r
184 return r
@@ -1,759 +1,761 b''
1 # ui.py - user interface bits for mercurial
1 # ui.py - user interface bits 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 i18n import _
8 from i18n import _
9 import errno, getpass, os, socket, sys, tempfile, traceback
9 import errno, getpass, os, socket, sys, tempfile, traceback
10 import config, scmutil, util, error, formatter
10 import config, scmutil, util, error, formatter
11
11
12 class ui(object):
12 class ui(object):
13 def __init__(self, src=None):
13 def __init__(self, src=None):
14 self._buffers = []
14 self._buffers = []
15 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
15 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
16 self._reportuntrusted = True
16 self._reportuntrusted = True
17 self._ocfg = config.config() # overlay
17 self._ocfg = config.config() # overlay
18 self._tcfg = config.config() # trusted
18 self._tcfg = config.config() # trusted
19 self._ucfg = config.config() # untrusted
19 self._ucfg = config.config() # untrusted
20 self._trustusers = set()
20 self._trustusers = set()
21 self._trustgroups = set()
21 self._trustgroups = set()
22 self.callhooks = True
22
23
23 if src:
24 if src:
24 self.fout = src.fout
25 self.fout = src.fout
25 self.ferr = src.ferr
26 self.ferr = src.ferr
26 self.fin = src.fin
27 self.fin = src.fin
27
28
28 self._tcfg = src._tcfg.copy()
29 self._tcfg = src._tcfg.copy()
29 self._ucfg = src._ucfg.copy()
30 self._ucfg = src._ucfg.copy()
30 self._ocfg = src._ocfg.copy()
31 self._ocfg = src._ocfg.copy()
31 self._trustusers = src._trustusers.copy()
32 self._trustusers = src._trustusers.copy()
32 self._trustgroups = src._trustgroups.copy()
33 self._trustgroups = src._trustgroups.copy()
33 self.environ = src.environ
34 self.environ = src.environ
35 self.callhooks = src.callhooks
34 self.fixconfig()
36 self.fixconfig()
35 else:
37 else:
36 self.fout = sys.stdout
38 self.fout = sys.stdout
37 self.ferr = sys.stderr
39 self.ferr = sys.stderr
38 self.fin = sys.stdin
40 self.fin = sys.stdin
39
41
40 # shared read-only environment
42 # shared read-only environment
41 self.environ = os.environ
43 self.environ = os.environ
42 # we always trust global config files
44 # we always trust global config files
43 for f in scmutil.rcpath():
45 for f in scmutil.rcpath():
44 self.readconfig(f, trust=True)
46 self.readconfig(f, trust=True)
45
47
46 def copy(self):
48 def copy(self):
47 return self.__class__(self)
49 return self.__class__(self)
48
50
49 def formatter(self, topic, opts):
51 def formatter(self, topic, opts):
50 return formatter.formatter(self, topic, opts)
52 return formatter.formatter(self, topic, opts)
51
53
52 def _trusted(self, fp, f):
54 def _trusted(self, fp, f):
53 st = util.fstat(fp)
55 st = util.fstat(fp)
54 if util.isowner(st):
56 if util.isowner(st):
55 return True
57 return True
56
58
57 tusers, tgroups = self._trustusers, self._trustgroups
59 tusers, tgroups = self._trustusers, self._trustgroups
58 if '*' in tusers or '*' in tgroups:
60 if '*' in tusers or '*' in tgroups:
59 return True
61 return True
60
62
61 user = util.username(st.st_uid)
63 user = util.username(st.st_uid)
62 group = util.groupname(st.st_gid)
64 group = util.groupname(st.st_gid)
63 if user in tusers or group in tgroups or user == util.username():
65 if user in tusers or group in tgroups or user == util.username():
64 return True
66 return True
65
67
66 if self._reportuntrusted:
68 if self._reportuntrusted:
67 self.warn(_('not trusting file %s from untrusted '
69 self.warn(_('not trusting file %s from untrusted '
68 'user %s, group %s\n') % (f, user, group))
70 'user %s, group %s\n') % (f, user, group))
69 return False
71 return False
70
72
71 def readconfig(self, filename, root=None, trust=False,
73 def readconfig(self, filename, root=None, trust=False,
72 sections=None, remap=None):
74 sections=None, remap=None):
73 try:
75 try:
74 fp = open(filename)
76 fp = open(filename)
75 except IOError:
77 except IOError:
76 if not sections: # ignore unless we were looking for something
78 if not sections: # ignore unless we were looking for something
77 return
79 return
78 raise
80 raise
79
81
80 cfg = config.config()
82 cfg = config.config()
81 trusted = sections or trust or self._trusted(fp, filename)
83 trusted = sections or trust or self._trusted(fp, filename)
82
84
83 try:
85 try:
84 cfg.read(filename, fp, sections=sections, remap=remap)
86 cfg.read(filename, fp, sections=sections, remap=remap)
85 fp.close()
87 fp.close()
86 except error.ConfigError, inst:
88 except error.ConfigError, inst:
87 if trusted:
89 if trusted:
88 raise
90 raise
89 self.warn(_("ignored: %s\n") % str(inst))
91 self.warn(_("ignored: %s\n") % str(inst))
90
92
91 if self.plain():
93 if self.plain():
92 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
94 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
93 'logtemplate', 'style',
95 'logtemplate', 'style',
94 'traceback', 'verbose'):
96 'traceback', 'verbose'):
95 if k in cfg['ui']:
97 if k in cfg['ui']:
96 del cfg['ui'][k]
98 del cfg['ui'][k]
97 for k, v in cfg.items('defaults'):
99 for k, v in cfg.items('defaults'):
98 del cfg['defaults'][k]
100 del cfg['defaults'][k]
99 # Don't remove aliases from the configuration if in the exceptionlist
101 # Don't remove aliases from the configuration if in the exceptionlist
100 if self.plain('alias'):
102 if self.plain('alias'):
101 for k, v in cfg.items('alias'):
103 for k, v in cfg.items('alias'):
102 del cfg['alias'][k]
104 del cfg['alias'][k]
103
105
104 if trusted:
106 if trusted:
105 self._tcfg.update(cfg)
107 self._tcfg.update(cfg)
106 self._tcfg.update(self._ocfg)
108 self._tcfg.update(self._ocfg)
107 self._ucfg.update(cfg)
109 self._ucfg.update(cfg)
108 self._ucfg.update(self._ocfg)
110 self._ucfg.update(self._ocfg)
109
111
110 if root is None:
112 if root is None:
111 root = os.path.expanduser('~')
113 root = os.path.expanduser('~')
112 self.fixconfig(root=root)
114 self.fixconfig(root=root)
113
115
114 def fixconfig(self, root=None, section=None):
116 def fixconfig(self, root=None, section=None):
115 if section in (None, 'paths'):
117 if section in (None, 'paths'):
116 # expand vars and ~
118 # expand vars and ~
117 # translate paths relative to root (or home) into absolute paths
119 # translate paths relative to root (or home) into absolute paths
118 root = root or os.getcwd()
120 root = root or os.getcwd()
119 for c in self._tcfg, self._ucfg, self._ocfg:
121 for c in self._tcfg, self._ucfg, self._ocfg:
120 for n, p in c.items('paths'):
122 for n, p in c.items('paths'):
121 if not p:
123 if not p:
122 continue
124 continue
123 if '%%' in p:
125 if '%%' in p:
124 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
126 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
125 % (n, p, self.configsource('paths', n)))
127 % (n, p, self.configsource('paths', n)))
126 p = p.replace('%%', '%')
128 p = p.replace('%%', '%')
127 p = util.expandpath(p)
129 p = util.expandpath(p)
128 if not util.hasscheme(p) and not os.path.isabs(p):
130 if not util.hasscheme(p) and not os.path.isabs(p):
129 p = os.path.normpath(os.path.join(root, p))
131 p = os.path.normpath(os.path.join(root, p))
130 c.set("paths", n, p)
132 c.set("paths", n, p)
131
133
132 if section in (None, 'ui'):
134 if section in (None, 'ui'):
133 # update ui options
135 # update ui options
134 self.debugflag = self.configbool('ui', 'debug')
136 self.debugflag = self.configbool('ui', 'debug')
135 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
137 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
136 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
138 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
137 if self.verbose and self.quiet:
139 if self.verbose and self.quiet:
138 self.quiet = self.verbose = False
140 self.quiet = self.verbose = False
139 self._reportuntrusted = self.debugflag or self.configbool("ui",
141 self._reportuntrusted = self.debugflag or self.configbool("ui",
140 "report_untrusted", True)
142 "report_untrusted", True)
141 self.tracebackflag = self.configbool('ui', 'traceback', False)
143 self.tracebackflag = self.configbool('ui', 'traceback', False)
142
144
143 if section in (None, 'trusted'):
145 if section in (None, 'trusted'):
144 # update trust information
146 # update trust information
145 self._trustusers.update(self.configlist('trusted', 'users'))
147 self._trustusers.update(self.configlist('trusted', 'users'))
146 self._trustgroups.update(self.configlist('trusted', 'groups'))
148 self._trustgroups.update(self.configlist('trusted', 'groups'))
147
149
148 def backupconfig(self, section, item):
150 def backupconfig(self, section, item):
149 return (self._ocfg.backup(section, item),
151 return (self._ocfg.backup(section, item),
150 self._tcfg.backup(section, item),
152 self._tcfg.backup(section, item),
151 self._ucfg.backup(section, item),)
153 self._ucfg.backup(section, item),)
152 def restoreconfig(self, data):
154 def restoreconfig(self, data):
153 self._ocfg.restore(data[0])
155 self._ocfg.restore(data[0])
154 self._tcfg.restore(data[1])
156 self._tcfg.restore(data[1])
155 self._ucfg.restore(data[2])
157 self._ucfg.restore(data[2])
156
158
157 def setconfig(self, section, name, value, overlay=True):
159 def setconfig(self, section, name, value, overlay=True):
158 if overlay:
160 if overlay:
159 self._ocfg.set(section, name, value)
161 self._ocfg.set(section, name, value)
160 self._tcfg.set(section, name, value)
162 self._tcfg.set(section, name, value)
161 self._ucfg.set(section, name, value)
163 self._ucfg.set(section, name, value)
162 self.fixconfig(section=section)
164 self.fixconfig(section=section)
163
165
164 def _data(self, untrusted):
166 def _data(self, untrusted):
165 return untrusted and self._ucfg or self._tcfg
167 return untrusted and self._ucfg or self._tcfg
166
168
167 def configsource(self, section, name, untrusted=False):
169 def configsource(self, section, name, untrusted=False):
168 return self._data(untrusted).source(section, name) or 'none'
170 return self._data(untrusted).source(section, name) or 'none'
169
171
170 def config(self, section, name, default=None, untrusted=False):
172 def config(self, section, name, default=None, untrusted=False):
171 if isinstance(name, list):
173 if isinstance(name, list):
172 alternates = name
174 alternates = name
173 else:
175 else:
174 alternates = [name]
176 alternates = [name]
175
177
176 for n in alternates:
178 for n in alternates:
177 value = self._data(untrusted).get(section, name, None)
179 value = self._data(untrusted).get(section, name, None)
178 if value is not None:
180 if value is not None:
179 name = n
181 name = n
180 break
182 break
181 else:
183 else:
182 value = default
184 value = default
183
185
184 if self.debugflag and not untrusted and self._reportuntrusted:
186 if self.debugflag and not untrusted and self._reportuntrusted:
185 uvalue = self._ucfg.get(section, name)
187 uvalue = self._ucfg.get(section, name)
186 if uvalue is not None and uvalue != value:
188 if uvalue is not None and uvalue != value:
187 self.debug("ignoring untrusted configuration option "
189 self.debug("ignoring untrusted configuration option "
188 "%s.%s = %s\n" % (section, name, uvalue))
190 "%s.%s = %s\n" % (section, name, uvalue))
189 return value
191 return value
190
192
191 def configpath(self, section, name, default=None, untrusted=False):
193 def configpath(self, section, name, default=None, untrusted=False):
192 'get a path config item, expanded relative to repo root or config file'
194 'get a path config item, expanded relative to repo root or config file'
193 v = self.config(section, name, default, untrusted)
195 v = self.config(section, name, default, untrusted)
194 if v is None:
196 if v is None:
195 return None
197 return None
196 if not os.path.isabs(v) or "://" not in v:
198 if not os.path.isabs(v) or "://" not in v:
197 src = self.configsource(section, name, untrusted)
199 src = self.configsource(section, name, untrusted)
198 if ':' in src:
200 if ':' in src:
199 base = os.path.dirname(src.rsplit(':')[0])
201 base = os.path.dirname(src.rsplit(':')[0])
200 v = os.path.join(base, os.path.expanduser(v))
202 v = os.path.join(base, os.path.expanduser(v))
201 return v
203 return v
202
204
203 def configbool(self, section, name, default=False, untrusted=False):
205 def configbool(self, section, name, default=False, untrusted=False):
204 """parse a configuration element as a boolean
206 """parse a configuration element as a boolean
205
207
206 >>> u = ui(); s = 'foo'
208 >>> u = ui(); s = 'foo'
207 >>> u.setconfig(s, 'true', 'yes')
209 >>> u.setconfig(s, 'true', 'yes')
208 >>> u.configbool(s, 'true')
210 >>> u.configbool(s, 'true')
209 True
211 True
210 >>> u.setconfig(s, 'false', 'no')
212 >>> u.setconfig(s, 'false', 'no')
211 >>> u.configbool(s, 'false')
213 >>> u.configbool(s, 'false')
212 False
214 False
213 >>> u.configbool(s, 'unknown')
215 >>> u.configbool(s, 'unknown')
214 False
216 False
215 >>> u.configbool(s, 'unknown', True)
217 >>> u.configbool(s, 'unknown', True)
216 True
218 True
217 >>> u.setconfig(s, 'invalid', 'somevalue')
219 >>> u.setconfig(s, 'invalid', 'somevalue')
218 >>> u.configbool(s, 'invalid')
220 >>> u.configbool(s, 'invalid')
219 Traceback (most recent call last):
221 Traceback (most recent call last):
220 ...
222 ...
221 ConfigError: foo.invalid is not a boolean ('somevalue')
223 ConfigError: foo.invalid is not a boolean ('somevalue')
222 """
224 """
223
225
224 v = self.config(section, name, None, untrusted)
226 v = self.config(section, name, None, untrusted)
225 if v is None:
227 if v is None:
226 return default
228 return default
227 if isinstance(v, bool):
229 if isinstance(v, bool):
228 return v
230 return v
229 b = util.parsebool(v)
231 b = util.parsebool(v)
230 if b is None:
232 if b is None:
231 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
233 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
232 % (section, name, v))
234 % (section, name, v))
233 return b
235 return b
234
236
235 def configint(self, section, name, default=None, untrusted=False):
237 def configint(self, section, name, default=None, untrusted=False):
236 """parse a configuration element as an integer
238 """parse a configuration element as an integer
237
239
238 >>> u = ui(); s = 'foo'
240 >>> u = ui(); s = 'foo'
239 >>> u.setconfig(s, 'int1', '42')
241 >>> u.setconfig(s, 'int1', '42')
240 >>> u.configint(s, 'int1')
242 >>> u.configint(s, 'int1')
241 42
243 42
242 >>> u.setconfig(s, 'int2', '-42')
244 >>> u.setconfig(s, 'int2', '-42')
243 >>> u.configint(s, 'int2')
245 >>> u.configint(s, 'int2')
244 -42
246 -42
245 >>> u.configint(s, 'unknown', 7)
247 >>> u.configint(s, 'unknown', 7)
246 7
248 7
247 >>> u.setconfig(s, 'invalid', 'somevalue')
249 >>> u.setconfig(s, 'invalid', 'somevalue')
248 >>> u.configint(s, 'invalid')
250 >>> u.configint(s, 'invalid')
249 Traceback (most recent call last):
251 Traceback (most recent call last):
250 ...
252 ...
251 ConfigError: foo.invalid is not an integer ('somevalue')
253 ConfigError: foo.invalid is not an integer ('somevalue')
252 """
254 """
253
255
254 v = self.config(section, name, None, untrusted)
256 v = self.config(section, name, None, untrusted)
255 if v is None:
257 if v is None:
256 return default
258 return default
257 try:
259 try:
258 return int(v)
260 return int(v)
259 except ValueError:
261 except ValueError:
260 raise error.ConfigError(_("%s.%s is not an integer ('%s')")
262 raise error.ConfigError(_("%s.%s is not an integer ('%s')")
261 % (section, name, v))
263 % (section, name, v))
262
264
263 def configlist(self, section, name, default=None, untrusted=False):
265 def configlist(self, section, name, default=None, untrusted=False):
264 """parse a configuration element as a list of comma/space separated
266 """parse a configuration element as a list of comma/space separated
265 strings
267 strings
266
268
267 >>> u = ui(); s = 'foo'
269 >>> u = ui(); s = 'foo'
268 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
270 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
269 >>> u.configlist(s, 'list1')
271 >>> u.configlist(s, 'list1')
270 ['this', 'is', 'a small', 'test']
272 ['this', 'is', 'a small', 'test']
271 """
273 """
272
274
273 def _parse_plain(parts, s, offset):
275 def _parse_plain(parts, s, offset):
274 whitespace = False
276 whitespace = False
275 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
277 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
276 whitespace = True
278 whitespace = True
277 offset += 1
279 offset += 1
278 if offset >= len(s):
280 if offset >= len(s):
279 return None, parts, offset
281 return None, parts, offset
280 if whitespace:
282 if whitespace:
281 parts.append('')
283 parts.append('')
282 if s[offset] == '"' and not parts[-1]:
284 if s[offset] == '"' and not parts[-1]:
283 return _parse_quote, parts, offset + 1
285 return _parse_quote, parts, offset + 1
284 elif s[offset] == '"' and parts[-1][-1] == '\\':
286 elif s[offset] == '"' and parts[-1][-1] == '\\':
285 parts[-1] = parts[-1][:-1] + s[offset]
287 parts[-1] = parts[-1][:-1] + s[offset]
286 return _parse_plain, parts, offset + 1
288 return _parse_plain, parts, offset + 1
287 parts[-1] += s[offset]
289 parts[-1] += s[offset]
288 return _parse_plain, parts, offset + 1
290 return _parse_plain, parts, offset + 1
289
291
290 def _parse_quote(parts, s, offset):
292 def _parse_quote(parts, s, offset):
291 if offset < len(s) and s[offset] == '"': # ""
293 if offset < len(s) and s[offset] == '"': # ""
292 parts.append('')
294 parts.append('')
293 offset += 1
295 offset += 1
294 while offset < len(s) and (s[offset].isspace() or
296 while offset < len(s) and (s[offset].isspace() or
295 s[offset] == ','):
297 s[offset] == ','):
296 offset += 1
298 offset += 1
297 return _parse_plain, parts, offset
299 return _parse_plain, parts, offset
298
300
299 while offset < len(s) and s[offset] != '"':
301 while offset < len(s) and s[offset] != '"':
300 if (s[offset] == '\\' and offset + 1 < len(s)
302 if (s[offset] == '\\' and offset + 1 < len(s)
301 and s[offset + 1] == '"'):
303 and s[offset + 1] == '"'):
302 offset += 1
304 offset += 1
303 parts[-1] += '"'
305 parts[-1] += '"'
304 else:
306 else:
305 parts[-1] += s[offset]
307 parts[-1] += s[offset]
306 offset += 1
308 offset += 1
307
309
308 if offset >= len(s):
310 if offset >= len(s):
309 real_parts = _configlist(parts[-1])
311 real_parts = _configlist(parts[-1])
310 if not real_parts:
312 if not real_parts:
311 parts[-1] = '"'
313 parts[-1] = '"'
312 else:
314 else:
313 real_parts[0] = '"' + real_parts[0]
315 real_parts[0] = '"' + real_parts[0]
314 parts = parts[:-1]
316 parts = parts[:-1]
315 parts.extend(real_parts)
317 parts.extend(real_parts)
316 return None, parts, offset
318 return None, parts, offset
317
319
318 offset += 1
320 offset += 1
319 while offset < len(s) and s[offset] in [' ', ',']:
321 while offset < len(s) and s[offset] in [' ', ',']:
320 offset += 1
322 offset += 1
321
323
322 if offset < len(s):
324 if offset < len(s):
323 if offset + 1 == len(s) and s[offset] == '"':
325 if offset + 1 == len(s) and s[offset] == '"':
324 parts[-1] += '"'
326 parts[-1] += '"'
325 offset += 1
327 offset += 1
326 else:
328 else:
327 parts.append('')
329 parts.append('')
328 else:
330 else:
329 return None, parts, offset
331 return None, parts, offset
330
332
331 return _parse_plain, parts, offset
333 return _parse_plain, parts, offset
332
334
333 def _configlist(s):
335 def _configlist(s):
334 s = s.rstrip(' ,')
336 s = s.rstrip(' ,')
335 if not s:
337 if not s:
336 return []
338 return []
337 parser, parts, offset = _parse_plain, [''], 0
339 parser, parts, offset = _parse_plain, [''], 0
338 while parser:
340 while parser:
339 parser, parts, offset = parser(parts, s, offset)
341 parser, parts, offset = parser(parts, s, offset)
340 return parts
342 return parts
341
343
342 result = self.config(section, name, untrusted=untrusted)
344 result = self.config(section, name, untrusted=untrusted)
343 if result is None:
345 if result is None:
344 result = default or []
346 result = default or []
345 if isinstance(result, basestring):
347 if isinstance(result, basestring):
346 result = _configlist(result.lstrip(' ,\n'))
348 result = _configlist(result.lstrip(' ,\n'))
347 if result is None:
349 if result is None:
348 result = default or []
350 result = default or []
349 return result
351 return result
350
352
351 def has_section(self, section, untrusted=False):
353 def has_section(self, section, untrusted=False):
352 '''tell whether section exists in config.'''
354 '''tell whether section exists in config.'''
353 return section in self._data(untrusted)
355 return section in self._data(untrusted)
354
356
355 def configitems(self, section, untrusted=False):
357 def configitems(self, section, untrusted=False):
356 items = self._data(untrusted).items(section)
358 items = self._data(untrusted).items(section)
357 if self.debugflag and not untrusted and self._reportuntrusted:
359 if self.debugflag and not untrusted and self._reportuntrusted:
358 for k, v in self._ucfg.items(section):
360 for k, v in self._ucfg.items(section):
359 if self._tcfg.get(section, k) != v:
361 if self._tcfg.get(section, k) != v:
360 self.debug("ignoring untrusted configuration option "
362 self.debug("ignoring untrusted configuration option "
361 "%s.%s = %s\n" % (section, k, v))
363 "%s.%s = %s\n" % (section, k, v))
362 return items
364 return items
363
365
364 def walkconfig(self, untrusted=False):
366 def walkconfig(self, untrusted=False):
365 cfg = self._data(untrusted)
367 cfg = self._data(untrusted)
366 for section in cfg.sections():
368 for section in cfg.sections():
367 for name, value in self.configitems(section, untrusted):
369 for name, value in self.configitems(section, untrusted):
368 yield section, name, value
370 yield section, name, value
369
371
370 def plain(self, feature=None):
372 def plain(self, feature=None):
371 '''is plain mode active?
373 '''is plain mode active?
372
374
373 Plain mode means that all configuration variables which affect
375 Plain mode means that all configuration variables which affect
374 the behavior and output of Mercurial should be
376 the behavior and output of Mercurial should be
375 ignored. Additionally, the output should be stable,
377 ignored. Additionally, the output should be stable,
376 reproducible and suitable for use in scripts or applications.
378 reproducible and suitable for use in scripts or applications.
377
379
378 The only way to trigger plain mode is by setting either the
380 The only way to trigger plain mode is by setting either the
379 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
381 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
380
382
381 The return value can either be
383 The return value can either be
382 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
384 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
383 - True otherwise
385 - True otherwise
384 '''
386 '''
385 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
387 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
386 return False
388 return False
387 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
389 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
388 if feature and exceptions:
390 if feature and exceptions:
389 return feature not in exceptions
391 return feature not in exceptions
390 return True
392 return True
391
393
392 def username(self):
394 def username(self):
393 """Return default username to be used in commits.
395 """Return default username to be used in commits.
394
396
395 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
397 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
396 and stop searching if one of these is set.
398 and stop searching if one of these is set.
397 If not found and ui.askusername is True, ask the user, else use
399 If not found and ui.askusername is True, ask the user, else use
398 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
400 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
399 """
401 """
400 user = os.environ.get("HGUSER")
402 user = os.environ.get("HGUSER")
401 if user is None:
403 if user is None:
402 user = self.config("ui", "username")
404 user = self.config("ui", "username")
403 if user is not None:
405 if user is not None:
404 user = os.path.expandvars(user)
406 user = os.path.expandvars(user)
405 if user is None:
407 if user is None:
406 user = os.environ.get("EMAIL")
408 user = os.environ.get("EMAIL")
407 if user is None and self.configbool("ui", "askusername"):
409 if user is None and self.configbool("ui", "askusername"):
408 user = self.prompt(_("enter a commit username:"), default=None)
410 user = self.prompt(_("enter a commit username:"), default=None)
409 if user is None and not self.interactive():
411 if user is None and not self.interactive():
410 try:
412 try:
411 user = '%s@%s' % (util.getuser(), socket.getfqdn())
413 user = '%s@%s' % (util.getuser(), socket.getfqdn())
412 self.warn(_("no username found, using '%s' instead\n") % user)
414 self.warn(_("no username found, using '%s' instead\n") % user)
413 except KeyError:
415 except KeyError:
414 pass
416 pass
415 if not user:
417 if not user:
416 raise util.Abort(_('no username supplied (see "hg help config")'))
418 raise util.Abort(_('no username supplied (see "hg help config")'))
417 if "\n" in user:
419 if "\n" in user:
418 raise util.Abort(_("username %s contains a newline\n") % repr(user))
420 raise util.Abort(_("username %s contains a newline\n") % repr(user))
419 return user
421 return user
420
422
421 def shortuser(self, user):
423 def shortuser(self, user):
422 """Return a short representation of a user name or email address."""
424 """Return a short representation of a user name or email address."""
423 if not self.verbose:
425 if not self.verbose:
424 user = util.shortuser(user)
426 user = util.shortuser(user)
425 return user
427 return user
426
428
427 def expandpath(self, loc, default=None):
429 def expandpath(self, loc, default=None):
428 """Return repository location relative to cwd or from [paths]"""
430 """Return repository location relative to cwd or from [paths]"""
429 if util.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')):
431 if util.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')):
430 return loc
432 return loc
431
433
432 path = self.config('paths', loc)
434 path = self.config('paths', loc)
433 if not path and default is not None:
435 if not path and default is not None:
434 path = self.config('paths', default)
436 path = self.config('paths', default)
435 return path or loc
437 return path or loc
436
438
437 def pushbuffer(self):
439 def pushbuffer(self):
438 self._buffers.append([])
440 self._buffers.append([])
439
441
440 def popbuffer(self, labeled=False):
442 def popbuffer(self, labeled=False):
441 '''pop the last buffer and return the buffered output
443 '''pop the last buffer and return the buffered output
442
444
443 If labeled is True, any labels associated with buffered
445 If labeled is True, any labels associated with buffered
444 output will be handled. By default, this has no effect
446 output will be handled. By default, this has no effect
445 on the output returned, but extensions and GUI tools may
447 on the output returned, but extensions and GUI tools may
446 handle this argument and returned styled output. If output
448 handle this argument and returned styled output. If output
447 is being buffered so it can be captured and parsed or
449 is being buffered so it can be captured and parsed or
448 processed, labeled should not be set to True.
450 processed, labeled should not be set to True.
449 '''
451 '''
450 return "".join(self._buffers.pop())
452 return "".join(self._buffers.pop())
451
453
452 def write(self, *args, **opts):
454 def write(self, *args, **opts):
453 '''write args to output
455 '''write args to output
454
456
455 By default, this method simply writes to the buffer or stdout,
457 By default, this method simply writes to the buffer or stdout,
456 but extensions or GUI tools may override this method,
458 but extensions or GUI tools may override this method,
457 write_err(), popbuffer(), and label() to style output from
459 write_err(), popbuffer(), and label() to style output from
458 various parts of hg.
460 various parts of hg.
459
461
460 An optional keyword argument, "label", can be passed in.
462 An optional keyword argument, "label", can be passed in.
461 This should be a string containing label names separated by
463 This should be a string containing label names separated by
462 space. Label names take the form of "topic.type". For example,
464 space. Label names take the form of "topic.type". For example,
463 ui.debug() issues a label of "ui.debug".
465 ui.debug() issues a label of "ui.debug".
464
466
465 When labeling output for a specific command, a label of
467 When labeling output for a specific command, a label of
466 "cmdname.type" is recommended. For example, status issues
468 "cmdname.type" is recommended. For example, status issues
467 a label of "status.modified" for modified files.
469 a label of "status.modified" for modified files.
468 '''
470 '''
469 if self._buffers:
471 if self._buffers:
470 self._buffers[-1].extend([str(a) for a in args])
472 self._buffers[-1].extend([str(a) for a in args])
471 else:
473 else:
472 for a in args:
474 for a in args:
473 self.fout.write(str(a))
475 self.fout.write(str(a))
474
476
475 def write_err(self, *args, **opts):
477 def write_err(self, *args, **opts):
476 try:
478 try:
477 if not getattr(self.fout, 'closed', False):
479 if not getattr(self.fout, 'closed', False):
478 self.fout.flush()
480 self.fout.flush()
479 for a in args:
481 for a in args:
480 self.ferr.write(str(a))
482 self.ferr.write(str(a))
481 # stderr may be buffered under win32 when redirected to files,
483 # stderr may be buffered under win32 when redirected to files,
482 # including stdout.
484 # including stdout.
483 if not getattr(self.ferr, 'closed', False):
485 if not getattr(self.ferr, 'closed', False):
484 self.ferr.flush()
486 self.ferr.flush()
485 except IOError, inst:
487 except IOError, inst:
486 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
488 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
487 raise
489 raise
488
490
489 def flush(self):
491 def flush(self):
490 try: self.fout.flush()
492 try: self.fout.flush()
491 except (IOError, ValueError): pass
493 except (IOError, ValueError): pass
492 try: self.ferr.flush()
494 try: self.ferr.flush()
493 except (IOError, ValueError): pass
495 except (IOError, ValueError): pass
494
496
495 def _isatty(self, fh):
497 def _isatty(self, fh):
496 if self.configbool('ui', 'nontty', False):
498 if self.configbool('ui', 'nontty', False):
497 return False
499 return False
498 return util.isatty(fh)
500 return util.isatty(fh)
499
501
500 def interactive(self):
502 def interactive(self):
501 '''is interactive input allowed?
503 '''is interactive input allowed?
502
504
503 An interactive session is a session where input can be reasonably read
505 An interactive session is a session where input can be reasonably read
504 from `sys.stdin'. If this function returns false, any attempt to read
506 from `sys.stdin'. If this function returns false, any attempt to read
505 from stdin should fail with an error, unless a sensible default has been
507 from stdin should fail with an error, unless a sensible default has been
506 specified.
508 specified.
507
509
508 Interactiveness is triggered by the value of the `ui.interactive'
510 Interactiveness is triggered by the value of the `ui.interactive'
509 configuration variable or - if it is unset - when `sys.stdin' points
511 configuration variable or - if it is unset - when `sys.stdin' points
510 to a terminal device.
512 to a terminal device.
511
513
512 This function refers to input only; for output, see `ui.formatted()'.
514 This function refers to input only; for output, see `ui.formatted()'.
513 '''
515 '''
514 i = self.configbool("ui", "interactive", None)
516 i = self.configbool("ui", "interactive", None)
515 if i is None:
517 if i is None:
516 # some environments replace stdin without implementing isatty
518 # some environments replace stdin without implementing isatty
517 # usually those are non-interactive
519 # usually those are non-interactive
518 return self._isatty(self.fin)
520 return self._isatty(self.fin)
519
521
520 return i
522 return i
521
523
522 def termwidth(self):
524 def termwidth(self):
523 '''how wide is the terminal in columns?
525 '''how wide is the terminal in columns?
524 '''
526 '''
525 if 'COLUMNS' in os.environ:
527 if 'COLUMNS' in os.environ:
526 try:
528 try:
527 return int(os.environ['COLUMNS'])
529 return int(os.environ['COLUMNS'])
528 except ValueError:
530 except ValueError:
529 pass
531 pass
530 return util.termwidth()
532 return util.termwidth()
531
533
532 def formatted(self):
534 def formatted(self):
533 '''should formatted output be used?
535 '''should formatted output be used?
534
536
535 It is often desirable to format the output to suite the output medium.
537 It is often desirable to format the output to suite the output medium.
536 Examples of this are truncating long lines or colorizing messages.
538 Examples of this are truncating long lines or colorizing messages.
537 However, this is not often not desirable when piping output into other
539 However, this is not often not desirable when piping output into other
538 utilities, e.g. `grep'.
540 utilities, e.g. `grep'.
539
541
540 Formatted output is triggered by the value of the `ui.formatted'
542 Formatted output is triggered by the value of the `ui.formatted'
541 configuration variable or - if it is unset - when `sys.stdout' points
543 configuration variable or - if it is unset - when `sys.stdout' points
542 to a terminal device. Please note that `ui.formatted' should be
544 to a terminal device. Please note that `ui.formatted' should be
543 considered an implementation detail; it is not intended for use outside
545 considered an implementation detail; it is not intended for use outside
544 Mercurial or its extensions.
546 Mercurial or its extensions.
545
547
546 This function refers to output only; for input, see `ui.interactive()'.
548 This function refers to output only; for input, see `ui.interactive()'.
547 This function always returns false when in plain mode, see `ui.plain()'.
549 This function always returns false when in plain mode, see `ui.plain()'.
548 '''
550 '''
549 if self.plain():
551 if self.plain():
550 return False
552 return False
551
553
552 i = self.configbool("ui", "formatted", None)
554 i = self.configbool("ui", "formatted", None)
553 if i is None:
555 if i is None:
554 # some environments replace stdout without implementing isatty
556 # some environments replace stdout without implementing isatty
555 # usually those are non-interactive
557 # usually those are non-interactive
556 return self._isatty(self.fout)
558 return self._isatty(self.fout)
557
559
558 return i
560 return i
559
561
560 def _readline(self, prompt=''):
562 def _readline(self, prompt=''):
561 if self._isatty(self.fin):
563 if self._isatty(self.fin):
562 try:
564 try:
563 # magically add command line editing support, where
565 # magically add command line editing support, where
564 # available
566 # available
565 import readline
567 import readline
566 # force demandimport to really load the module
568 # force demandimport to really load the module
567 readline.read_history_file
569 readline.read_history_file
568 # windows sometimes raises something other than ImportError
570 # windows sometimes raises something other than ImportError
569 except Exception:
571 except Exception:
570 pass
572 pass
571
573
572 # call write() so output goes through subclassed implementation
574 # call write() so output goes through subclassed implementation
573 # e.g. color extension on Windows
575 # e.g. color extension on Windows
574 self.write(prompt)
576 self.write(prompt)
575
577
576 # instead of trying to emulate raw_input, swap (self.fin,
578 # instead of trying to emulate raw_input, swap (self.fin,
577 # self.fout) with (sys.stdin, sys.stdout)
579 # self.fout) with (sys.stdin, sys.stdout)
578 oldin = sys.stdin
580 oldin = sys.stdin
579 oldout = sys.stdout
581 oldout = sys.stdout
580 sys.stdin = self.fin
582 sys.stdin = self.fin
581 sys.stdout = self.fout
583 sys.stdout = self.fout
582 line = raw_input(' ')
584 line = raw_input(' ')
583 sys.stdin = oldin
585 sys.stdin = oldin
584 sys.stdout = oldout
586 sys.stdout = oldout
585
587
586 # When stdin is in binary mode on Windows, it can cause
588 # When stdin is in binary mode on Windows, it can cause
587 # raw_input() to emit an extra trailing carriage return
589 # raw_input() to emit an extra trailing carriage return
588 if os.linesep == '\r\n' and line and line[-1] == '\r':
590 if os.linesep == '\r\n' and line and line[-1] == '\r':
589 line = line[:-1]
591 line = line[:-1]
590 return line
592 return line
591
593
592 def prompt(self, msg, default="y"):
594 def prompt(self, msg, default="y"):
593 """Prompt user with msg, read response.
595 """Prompt user with msg, read response.
594 If ui is not interactive, the default is returned.
596 If ui is not interactive, the default is returned.
595 """
597 """
596 if not self.interactive():
598 if not self.interactive():
597 self.write(msg, ' ', default, "\n")
599 self.write(msg, ' ', default, "\n")
598 return default
600 return default
599 try:
601 try:
600 r = self._readline(self.label(msg, 'ui.prompt'))
602 r = self._readline(self.label(msg, 'ui.prompt'))
601 if not r:
603 if not r:
602 return default
604 return default
603 return r
605 return r
604 except EOFError:
606 except EOFError:
605 raise util.Abort(_('response expected'))
607 raise util.Abort(_('response expected'))
606
608
607 def promptchoice(self, msg, choices, default=0):
609 def promptchoice(self, msg, choices, default=0):
608 """Prompt user with msg, read response, and ensure it matches
610 """Prompt user with msg, read response, and ensure it matches
609 one of the provided choices. The index of the choice is returned.
611 one of the provided choices. The index of the choice is returned.
610 choices is a sequence of acceptable responses with the format:
612 choices is a sequence of acceptable responses with the format:
611 ('&None', 'E&xec', 'Sym&link') Responses are case insensitive.
613 ('&None', 'E&xec', 'Sym&link') Responses are case insensitive.
612 If ui is not interactive, the default is returned.
614 If ui is not interactive, the default is returned.
613 """
615 """
614 resps = [s[s.index('&')+1].lower() for s in choices]
616 resps = [s[s.index('&')+1].lower() for s in choices]
615 while True:
617 while True:
616 r = self.prompt(msg, resps[default])
618 r = self.prompt(msg, resps[default])
617 if r.lower() in resps:
619 if r.lower() in resps:
618 return resps.index(r.lower())
620 return resps.index(r.lower())
619 self.write(_("unrecognized response\n"))
621 self.write(_("unrecognized response\n"))
620
622
621 def getpass(self, prompt=None, default=None):
623 def getpass(self, prompt=None, default=None):
622 if not self.interactive():
624 if not self.interactive():
623 return default
625 return default
624 try:
626 try:
625 return getpass.getpass(prompt or _('password: '))
627 return getpass.getpass(prompt or _('password: '))
626 except EOFError:
628 except EOFError:
627 raise util.Abort(_('response expected'))
629 raise util.Abort(_('response expected'))
628 def status(self, *msg, **opts):
630 def status(self, *msg, **opts):
629 '''write status message to output (if ui.quiet is False)
631 '''write status message to output (if ui.quiet is False)
630
632
631 This adds an output label of "ui.status".
633 This adds an output label of "ui.status".
632 '''
634 '''
633 if not self.quiet:
635 if not self.quiet:
634 opts['label'] = opts.get('label', '') + ' ui.status'
636 opts['label'] = opts.get('label', '') + ' ui.status'
635 self.write(*msg, **opts)
637 self.write(*msg, **opts)
636 def warn(self, *msg, **opts):
638 def warn(self, *msg, **opts):
637 '''write warning message to output (stderr)
639 '''write warning message to output (stderr)
638
640
639 This adds an output label of "ui.warning".
641 This adds an output label of "ui.warning".
640 '''
642 '''
641 opts['label'] = opts.get('label', '') + ' ui.warning'
643 opts['label'] = opts.get('label', '') + ' ui.warning'
642 self.write_err(*msg, **opts)
644 self.write_err(*msg, **opts)
643 def note(self, *msg, **opts):
645 def note(self, *msg, **opts):
644 '''write note to output (if ui.verbose is True)
646 '''write note to output (if ui.verbose is True)
645
647
646 This adds an output label of "ui.note".
648 This adds an output label of "ui.note".
647 '''
649 '''
648 if self.verbose:
650 if self.verbose:
649 opts['label'] = opts.get('label', '') + ' ui.note'
651 opts['label'] = opts.get('label', '') + ' ui.note'
650 self.write(*msg, **opts)
652 self.write(*msg, **opts)
651 def debug(self, *msg, **opts):
653 def debug(self, *msg, **opts):
652 '''write debug message to output (if ui.debugflag is True)
654 '''write debug message to output (if ui.debugflag is True)
653
655
654 This adds an output label of "ui.debug".
656 This adds an output label of "ui.debug".
655 '''
657 '''
656 if self.debugflag:
658 if self.debugflag:
657 opts['label'] = opts.get('label', '') + ' ui.debug'
659 opts['label'] = opts.get('label', '') + ' ui.debug'
658 self.write(*msg, **opts)
660 self.write(*msg, **opts)
659 def edit(self, text, user):
661 def edit(self, text, user):
660 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
662 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
661 text=True)
663 text=True)
662 try:
664 try:
663 f = os.fdopen(fd, "w")
665 f = os.fdopen(fd, "w")
664 f.write(text)
666 f.write(text)
665 f.close()
667 f.close()
666
668
667 editor = self.geteditor()
669 editor = self.geteditor()
668
670
669 util.system("%s \"%s\"" % (editor, name),
671 util.system("%s \"%s\"" % (editor, name),
670 environ={'HGUSER': user},
672 environ={'HGUSER': user},
671 onerr=util.Abort, errprefix=_("edit failed"),
673 onerr=util.Abort, errprefix=_("edit failed"),
672 out=self.fout)
674 out=self.fout)
673
675
674 f = open(name)
676 f = open(name)
675 t = f.read()
677 t = f.read()
676 f.close()
678 f.close()
677 finally:
679 finally:
678 os.unlink(name)
680 os.unlink(name)
679
681
680 return t
682 return t
681
683
682 def traceback(self, exc=None):
684 def traceback(self, exc=None):
683 '''print exception traceback if traceback printing enabled.
685 '''print exception traceback if traceback printing enabled.
684 only to call in exception handler. returns true if traceback
686 only to call in exception handler. returns true if traceback
685 printed.'''
687 printed.'''
686 if self.tracebackflag:
688 if self.tracebackflag:
687 if exc:
689 if exc:
688 traceback.print_exception(exc[0], exc[1], exc[2],
690 traceback.print_exception(exc[0], exc[1], exc[2],
689 file=self.ferr)
691 file=self.ferr)
690 else:
692 else:
691 traceback.print_exc(file=self.ferr)
693 traceback.print_exc(file=self.ferr)
692 return self.tracebackflag
694 return self.tracebackflag
693
695
694 def geteditor(self):
696 def geteditor(self):
695 '''return editor to use'''
697 '''return editor to use'''
696 if sys.platform == 'plan9':
698 if sys.platform == 'plan9':
697 # vi is the MIPS instruction simulator on Plan 9. We
699 # vi is the MIPS instruction simulator on Plan 9. We
698 # instead default to E to plumb commit messages to
700 # instead default to E to plumb commit messages to
699 # avoid confusion.
701 # avoid confusion.
700 editor = 'E'
702 editor = 'E'
701 else:
703 else:
702 editor = 'vi'
704 editor = 'vi'
703 return (os.environ.get("HGEDITOR") or
705 return (os.environ.get("HGEDITOR") or
704 self.config("ui", "editor") or
706 self.config("ui", "editor") or
705 os.environ.get("VISUAL") or
707 os.environ.get("VISUAL") or
706 os.environ.get("EDITOR", editor))
708 os.environ.get("EDITOR", editor))
707
709
708 def progress(self, topic, pos, item="", unit="", total=None):
710 def progress(self, topic, pos, item="", unit="", total=None):
709 '''show a progress message
711 '''show a progress message
710
712
711 With stock hg, this is simply a debug message that is hidden
713 With stock hg, this is simply a debug message that is hidden
712 by default, but with extensions or GUI tools it may be
714 by default, but with extensions or GUI tools it may be
713 visible. 'topic' is the current operation, 'item' is a
715 visible. 'topic' is the current operation, 'item' is a
714 non-numeric marker of the current position (ie the currently
716 non-numeric marker of the current position (ie the currently
715 in-process file), 'pos' is the current numeric position (ie
717 in-process file), 'pos' is the current numeric position (ie
716 revision, bytes, etc.), unit is a corresponding unit label,
718 revision, bytes, etc.), unit is a corresponding unit label,
717 and total is the highest expected pos.
719 and total is the highest expected pos.
718
720
719 Multiple nested topics may be active at a time.
721 Multiple nested topics may be active at a time.
720
722
721 All topics should be marked closed by setting pos to None at
723 All topics should be marked closed by setting pos to None at
722 termination.
724 termination.
723 '''
725 '''
724
726
725 if pos is None or not self.debugflag:
727 if pos is None or not self.debugflag:
726 return
728 return
727
729
728 if unit:
730 if unit:
729 unit = ' ' + unit
731 unit = ' ' + unit
730 if item:
732 if item:
731 item = ' ' + item
733 item = ' ' + item
732
734
733 if total:
735 if total:
734 pct = 100.0 * pos / total
736 pct = 100.0 * pos / total
735 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
737 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
736 % (topic, item, pos, total, unit, pct))
738 % (topic, item, pos, total, unit, pct))
737 else:
739 else:
738 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
740 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
739
741
740 def log(self, service, message):
742 def log(self, service, message):
741 '''hook for logging facility extensions
743 '''hook for logging facility extensions
742
744
743 service should be a readily-identifiable subsystem, which will
745 service should be a readily-identifiable subsystem, which will
744 allow filtering.
746 allow filtering.
745 message should be a newline-terminated string to log.
747 message should be a newline-terminated string to log.
746 '''
748 '''
747 pass
749 pass
748
750
749 def label(self, msg, label):
751 def label(self, msg, label):
750 '''style msg based on supplied label
752 '''style msg based on supplied label
751
753
752 Like ui.write(), this just returns msg unchanged, but extensions
754 Like ui.write(), this just returns msg unchanged, but extensions
753 and GUI tools can override it to allow styling output without
755 and GUI tools can override it to allow styling output without
754 writing it.
756 writing it.
755
757
756 ui.write(s, 'label') is equivalent to
758 ui.write(s, 'label') is equivalent to
757 ui.write(ui.label(s, 'label')).
759 ui.write(ui.label(s, 'label')).
758 '''
760 '''
759 return msg
761 return msg
@@ -1,349 +1,355 b''
1 $ hg init
1 $ hg init
2
2
3 Setup:
3 Setup:
4
4
5 $ echo a >> a
5 $ echo a >> a
6 $ hg ci -Am 'base'
6 $ hg ci -Am 'base'
7 adding a
7 adding a
8
8
9 Refuse to amend public csets:
9 Refuse to amend public csets:
10
10
11 $ hg phase -r . -p
11 $ hg phase -r . -p
12 $ hg ci --amend
12 $ hg ci --amend
13 abort: cannot amend public changesets
13 abort: cannot amend public changesets
14 [255]
14 [255]
15 $ hg phase -r . -f -d
15 $ hg phase -r . -f -d
16
16
17 $ echo a >> a
17 $ echo a >> a
18 $ hg ci -Am 'base1'
18 $ hg ci -Am 'base1'
19
19
20 Nothing to amend:
20 Nothing to amend:
21
21
22 $ hg ci --amend
22 $ hg ci --amend
23 nothing changed
23 nothing changed
24 [1]
24 [1]
25
25
26 $ echo '[hooks]' >> $HGRCPATH
27 $ echo 'pretxncommit.foo = echo "pretxncommit $HG_NODE"; hg id -r $HG_NODE' >> $HGRCPATH
28
26 Amending changeset with changes in working dir:
29 Amending changeset with changes in working dir:
27
30
28 $ echo a >> a
31 $ echo a >> a
29 $ hg ci --amend -m 'amend base1'
32 $ hg ci --amend -m 'amend base1'
30 saved backup bundle to $TESTTMP/.hg/strip-backup/489edb5b847d-amend-backup.hg (glob)
33 pretxncommit 9cd25b479c51be2f4ed2c38e7abdf7ce67d8e0dc
34 9cd25b479c51 tip
35 saved backup bundle to $TESTTMP/.hg/strip-backup/489edb5b847d-amend-backup.hg
36 $ echo 'pretxncommit.foo = ' >> $HGRCPATH
31 $ hg diff -c .
37 $ hg diff -c .
32 diff -r ad120869acf0 -r 9cd25b479c51 a
38 diff -r ad120869acf0 -r 9cd25b479c51 a
33 --- a/a Thu Jan 01 00:00:00 1970 +0000
39 --- a/a Thu Jan 01 00:00:00 1970 +0000
34 +++ b/a Thu Jan 01 00:00:00 1970 +0000
40 +++ b/a Thu Jan 01 00:00:00 1970 +0000
35 @@ -1,1 +1,3 @@
41 @@ -1,1 +1,3 @@
36 a
42 a
37 +a
43 +a
38 +a
44 +a
39 $ hg log
45 $ hg log
40 changeset: 1:9cd25b479c51
46 changeset: 1:9cd25b479c51
41 tag: tip
47 tag: tip
42 user: test
48 user: test
43 date: Thu Jan 01 00:00:00 1970 +0000
49 date: Thu Jan 01 00:00:00 1970 +0000
44 summary: amend base1
50 summary: amend base1
45
51
46 changeset: 0:ad120869acf0
52 changeset: 0:ad120869acf0
47 user: test
53 user: test
48 date: Thu Jan 01 00:00:00 1970 +0000
54 date: Thu Jan 01 00:00:00 1970 +0000
49 summary: base
55 summary: base
50
56
51
57
52 Add new file:
58 Add new file:
53
59
54 $ echo b > b
60 $ echo b > b
55 $ hg ci --amend -Am 'amend base1 new file'
61 $ hg ci --amend -Am 'amend base1 new file'
56 adding b
62 adding b
57 saved backup bundle to $TESTTMP/.hg/strip-backup/9cd25b479c51-amend-backup.hg (glob)
63 saved backup bundle to $TESTTMP/.hg/strip-backup/9cd25b479c51-amend-backup.hg (glob)
58
64
59 Remove file that was added in amended commit:
65 Remove file that was added in amended commit:
60
66
61 $ hg rm b
67 $ hg rm b
62 $ hg ci --amend -m 'amend base1 remove new file'
68 $ hg ci --amend -m 'amend base1 remove new file'
63 saved backup bundle to $TESTTMP/.hg/strip-backup/e2bb3ecffd2f-amend-backup.hg (glob)
69 saved backup bundle to $TESTTMP/.hg/strip-backup/e2bb3ecffd2f-amend-backup.hg (glob)
64
70
65 $ hg cat b
71 $ hg cat b
66 b: no such file in rev 664a9b2d60cd
72 b: no such file in rev 664a9b2d60cd
67 [1]
73 [1]
68
74
69 No changes, just a different message:
75 No changes, just a different message:
70
76
71 $ hg ci -v --amend -m 'no changes, new message'
77 $ hg ci -v --amend -m 'no changes, new message'
72 amending changeset 664a9b2d60cd
78 amending changeset 664a9b2d60cd
73 copying changeset 664a9b2d60cd to ad120869acf0
79 copying changeset 664a9b2d60cd to ad120869acf0
74 a
80 a
75 stripping amended changeset 664a9b2d60cd
81 stripping amended changeset 664a9b2d60cd
76 1 changesets found
82 1 changesets found
77 saved backup bundle to $TESTTMP/.hg/strip-backup/664a9b2d60cd-amend-backup.hg (glob)
83 saved backup bundle to $TESTTMP/.hg/strip-backup/664a9b2d60cd-amend-backup.hg (glob)
78 1 changesets found
84 1 changesets found
79 adding branch
85 adding branch
80 adding changesets
86 adding changesets
81 adding manifests
87 adding manifests
82 adding file changes
88 adding file changes
83 added 1 changesets with 1 changes to 1 files
89 added 1 changesets with 1 changes to 1 files
84 committed changeset 1:ea6e356ff2ad
90 committed changeset 1:ea6e356ff2ad
85 $ hg diff -c .
91 $ hg diff -c .
86 diff -r ad120869acf0 -r ea6e356ff2ad a
92 diff -r ad120869acf0 -r ea6e356ff2ad a
87 --- a/a Thu Jan 01 00:00:00 1970 +0000
93 --- a/a Thu Jan 01 00:00:00 1970 +0000
88 +++ b/a Thu Jan 01 00:00:00 1970 +0000
94 +++ b/a Thu Jan 01 00:00:00 1970 +0000
89 @@ -1,1 +1,3 @@
95 @@ -1,1 +1,3 @@
90 a
96 a
91 +a
97 +a
92 +a
98 +a
93 $ hg log
99 $ hg log
94 changeset: 1:ea6e356ff2ad
100 changeset: 1:ea6e356ff2ad
95 tag: tip
101 tag: tip
96 user: test
102 user: test
97 date: Thu Jan 01 00:00:00 1970 +0000
103 date: Thu Jan 01 00:00:00 1970 +0000
98 summary: no changes, new message
104 summary: no changes, new message
99
105
100 changeset: 0:ad120869acf0
106 changeset: 0:ad120869acf0
101 user: test
107 user: test
102 date: Thu Jan 01 00:00:00 1970 +0000
108 date: Thu Jan 01 00:00:00 1970 +0000
103 summary: base
109 summary: base
104
110
105
111
106 Disable default date on commit so when -d isn't given, the old date is preserved:
112 Disable default date on commit so when -d isn't given, the old date is preserved:
107
113
108 $ echo '[defaults]' >> $HGRCPATH
114 $ echo '[defaults]' >> $HGRCPATH
109 $ echo 'commit=' >> $HGRCPATH
115 $ echo 'commit=' >> $HGRCPATH
110
116
111 Test -u/-d:
117 Test -u/-d:
112
118
113 $ hg ci --amend -u foo -d '1 0'
119 $ hg ci --amend -u foo -d '1 0'
114 saved backup bundle to $TESTTMP/.hg/strip-backup/ea6e356ff2ad-amend-backup.hg (glob)
120 saved backup bundle to $TESTTMP/.hg/strip-backup/ea6e356ff2ad-amend-backup.hg (glob)
115 $ echo a >> a
121 $ echo a >> a
116 $ hg ci --amend -u foo -d '1 0'
122 $ hg ci --amend -u foo -d '1 0'
117 saved backup bundle to $TESTTMP/.hg/strip-backup/377b91ce8b56-amend-backup.hg (glob)
123 saved backup bundle to $TESTTMP/.hg/strip-backup/377b91ce8b56-amend-backup.hg (glob)
118 $ hg log -r .
124 $ hg log -r .
119 changeset: 1:2c94e4a5756f
125 changeset: 1:2c94e4a5756f
120 tag: tip
126 tag: tip
121 user: foo
127 user: foo
122 date: Thu Jan 01 00:00:01 1970 +0000
128 date: Thu Jan 01 00:00:01 1970 +0000
123 summary: no changes, new message
129 summary: no changes, new message
124
130
125
131
126 Open editor with old commit message if a message isn't given otherwise:
132 Open editor with old commit message if a message isn't given otherwise:
127
133
128 $ cat > editor.sh << '__EOF__'
134 $ cat > editor.sh << '__EOF__'
129 > #!/bin/sh
135 > #!/bin/sh
130 > cat $1
136 > cat $1
131 > echo "another precious commit message" > "$1"
137 > echo "another precious commit message" > "$1"
132 > __EOF__
138 > __EOF__
133 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit --amend -v
139 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit --amend -v
134 amending changeset 2c94e4a5756f
140 amending changeset 2c94e4a5756f
135 copying changeset 2c94e4a5756f to ad120869acf0
141 copying changeset 2c94e4a5756f to ad120869acf0
136 no changes, new message
142 no changes, new message
137
143
138
144
139 HG: Enter commit message. Lines beginning with 'HG:' are removed.
145 HG: Enter commit message. Lines beginning with 'HG:' are removed.
140 HG: Leave message empty to abort commit.
146 HG: Leave message empty to abort commit.
141 HG: --
147 HG: --
142 HG: user: foo
148 HG: user: foo
143 HG: branch 'default'
149 HG: branch 'default'
144 HG: changed a
150 HG: changed a
145 a
151 a
146 stripping amended changeset 2c94e4a5756f
152 stripping amended changeset 2c94e4a5756f
147 1 changesets found
153 1 changesets found
148 saved backup bundle to $TESTTMP/.hg/strip-backup/2c94e4a5756f-amend-backup.hg (glob)
154 saved backup bundle to $TESTTMP/.hg/strip-backup/2c94e4a5756f-amend-backup.hg (glob)
149 1 changesets found
155 1 changesets found
150 adding branch
156 adding branch
151 adding changesets
157 adding changesets
152 adding manifests
158 adding manifests
153 adding file changes
159 adding file changes
154 added 1 changesets with 1 changes to 1 files
160 added 1 changesets with 1 changes to 1 files
155 committed changeset 1:ffb49186f961
161 committed changeset 1:ffb49186f961
156
162
157 Same, but with changes in working dir (different code path):
163 Same, but with changes in working dir (different code path):
158
164
159 $ echo a >> a
165 $ echo a >> a
160 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit --amend -v
166 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit --amend -v
161 amending changeset ffb49186f961
167 amending changeset ffb49186f961
162 another precious commit message
168 another precious commit message
163
169
164
170
165 HG: Enter commit message. Lines beginning with 'HG:' are removed.
171 HG: Enter commit message. Lines beginning with 'HG:' are removed.
166 HG: Leave message empty to abort commit.
172 HG: Leave message empty to abort commit.
167 HG: --
173 HG: --
168 HG: user: foo
174 HG: user: foo
169 HG: branch 'default'
175 HG: branch 'default'
170 HG: changed a
176 HG: changed a
171 a
177 a
172 copying changeset 27f3aacd3011 to ad120869acf0
178 copying changeset 27f3aacd3011 to ad120869acf0
173 a
179 a
174 stripping intermediate changeset 27f3aacd3011
180 stripping intermediate changeset 27f3aacd3011
175 stripping amended changeset ffb49186f961
181 stripping amended changeset ffb49186f961
176 2 changesets found
182 2 changesets found
177 saved backup bundle to $TESTTMP/.hg/strip-backup/ffb49186f961-amend-backup.hg (glob)
183 saved backup bundle to $TESTTMP/.hg/strip-backup/ffb49186f961-amend-backup.hg (glob)
178 1 changesets found
184 1 changesets found
179 adding branch
185 adding branch
180 adding changesets
186 adding changesets
181 adding manifests
187 adding manifests
182 adding file changes
188 adding file changes
183 added 1 changesets with 1 changes to 1 files
189 added 1 changesets with 1 changes to 1 files
184 committed changeset 1:fb6cca43446f
190 committed changeset 1:fb6cca43446f
185
191
186 $ rm editor.sh
192 $ rm editor.sh
187 $ hg log -r .
193 $ hg log -r .
188 changeset: 1:fb6cca43446f
194 changeset: 1:fb6cca43446f
189 tag: tip
195 tag: tip
190 user: foo
196 user: foo
191 date: Thu Jan 01 00:00:01 1970 +0000
197 date: Thu Jan 01 00:00:01 1970 +0000
192 summary: another precious commit message
198 summary: another precious commit message
193
199
194
200
195 Moving bookmarks, preserve active bookmark:
201 Moving bookmarks, preserve active bookmark:
196
202
197 $ hg book book1
203 $ hg book book1
198 $ hg book book2
204 $ hg book book2
199 $ hg ci --amend -m 'move bookmarks'
205 $ hg ci --amend -m 'move bookmarks'
200 saved backup bundle to $TESTTMP/.hg/strip-backup/fb6cca43446f-amend-backup.hg (glob)
206 saved backup bundle to $TESTTMP/.hg/strip-backup/fb6cca43446f-amend-backup.hg (glob)
201 $ hg book
207 $ hg book
202 book1 1:0cf1c7a51bcf
208 book1 1:0cf1c7a51bcf
203 * book2 1:0cf1c7a51bcf
209 * book2 1:0cf1c7a51bcf
204 $ echo a >> a
210 $ echo a >> a
205 $ hg ci --amend -m 'move bookmarks'
211 $ hg ci --amend -m 'move bookmarks'
206 saved backup bundle to $TESTTMP/.hg/strip-backup/0cf1c7a51bcf-amend-backup.hg (glob)
212 saved backup bundle to $TESTTMP/.hg/strip-backup/0cf1c7a51bcf-amend-backup.hg (glob)
207 $ hg book
213 $ hg book
208 book1 1:7344472bd951
214 book1 1:7344472bd951
209 * book2 1:7344472bd951
215 * book2 1:7344472bd951
210
216
211 $ echo '[defaults]' >> $HGRCPATH
217 $ echo '[defaults]' >> $HGRCPATH
212 $ echo "commit=-d '0 0'" >> $HGRCPATH
218 $ echo "commit=-d '0 0'" >> $HGRCPATH
213
219
214 Moving branches:
220 Moving branches:
215
221
216 $ hg branch foo
222 $ hg branch foo
217 marked working directory as branch foo
223 marked working directory as branch foo
218 (branches are permanent and global, did you want a bookmark?)
224 (branches are permanent and global, did you want a bookmark?)
219 $ echo a >> a
225 $ echo a >> a
220 $ hg ci -m 'branch foo'
226 $ hg ci -m 'branch foo'
221 $ hg branch default -f
227 $ hg branch default -f
222 marked working directory as branch default
228 marked working directory as branch default
223 (branches are permanent and global, did you want a bookmark?)
229 (branches are permanent and global, did you want a bookmark?)
224 $ hg ci --amend -m 'back to default'
230 $ hg ci --amend -m 'back to default'
225 saved backup bundle to $TESTTMP/.hg/strip-backup/1661ca36a2db-amend-backup.hg (glob)
231 saved backup bundle to $TESTTMP/.hg/strip-backup/1661ca36a2db-amend-backup.hg (glob)
226 $ hg branches
232 $ hg branches
227 default 2:f24ee5961967
233 default 2:f24ee5961967
228
234
229 Close branch:
235 Close branch:
230
236
231 $ hg up -q 0
237 $ hg up -q 0
232 $ echo b >> b
238 $ echo b >> b
233 $ hg branch foo
239 $ hg branch foo
234 marked working directory as branch foo
240 marked working directory as branch foo
235 (branches are permanent and global, did you want a bookmark?)
241 (branches are permanent and global, did you want a bookmark?)
236 $ hg ci -Am 'fork'
242 $ hg ci -Am 'fork'
237 adding b
243 adding b
238 $ echo b >> b
244 $ echo b >> b
239 $ hg ci -mb
245 $ hg ci -mb
240 $ hg ci --amend --close-branch -m 'closing branch foo'
246 $ hg ci --amend --close-branch -m 'closing branch foo'
241 saved backup bundle to $TESTTMP/.hg/strip-backup/c962248fa264-amend-backup.hg (glob)
247 saved backup bundle to $TESTTMP/.hg/strip-backup/c962248fa264-amend-backup.hg (glob)
242
248
243 Same thing, different code path:
249 Same thing, different code path:
244
250
245 $ echo b >> b
251 $ echo b >> b
246 $ hg ci -m 'reopen branch'
252 $ hg ci -m 'reopen branch'
247 reopening closed branch head 4
253 reopening closed branch head 4
248 $ echo b >> b
254 $ echo b >> b
249 $ hg ci --amend --close-branch
255 $ hg ci --amend --close-branch
250 saved backup bundle to $TESTTMP/.hg/strip-backup/5e302dcc12b8-amend-backup.hg (glob)
256 saved backup bundle to $TESTTMP/.hg/strip-backup/5e302dcc12b8-amend-backup.hg (glob)
251 $ hg branches
257 $ hg branches
252 default 2:f24ee5961967
258 default 2:f24ee5961967
253
259
254 Refuse to amend merges:
260 Refuse to amend merges:
255
261
256 $ hg up -q default
262 $ hg up -q default
257 $ hg merge foo
263 $ hg merge foo
258 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
264 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
259 (branch merge, don't forget to commit)
265 (branch merge, don't forget to commit)
260 $ hg ci --amend
266 $ hg ci --amend
261 abort: cannot amend while merging
267 abort: cannot amend while merging
262 [255]
268 [255]
263 $ hg ci -m 'merge'
269 $ hg ci -m 'merge'
264 $ hg ci --amend
270 $ hg ci --amend
265 abort: cannot amend merge changesets
271 abort: cannot amend merge changesets
266 [255]
272 [255]
267
273
268 Follow copies/renames:
274 Follow copies/renames:
269
275
270 $ hg mv b c
276 $ hg mv b c
271 $ hg ci -m 'b -> c'
277 $ hg ci -m 'b -> c'
272 $ hg mv c d
278 $ hg mv c d
273 $ hg ci --amend -m 'b -> d'
279 $ hg ci --amend -m 'b -> d'
274 saved backup bundle to $TESTTMP/.hg/strip-backup/9c207120aa98-amend-backup.hg (glob)
280 saved backup bundle to $TESTTMP/.hg/strip-backup/9c207120aa98-amend-backup.hg (glob)
275 $ hg st --rev '.^' --copies d
281 $ hg st --rev '.^' --copies d
276 A d
282 A d
277 b
283 b
278 $ hg cp d e
284 $ hg cp d e
279 $ hg ci -m 'e = d'
285 $ hg ci -m 'e = d'
280 $ hg cp e f
286 $ hg cp e f
281 $ hg ci --amend -m 'f = d'
287 $ hg ci --amend -m 'f = d'
282 saved backup bundle to $TESTTMP/.hg/strip-backup/fda2b3b27b22-amend-backup.hg (glob)
288 saved backup bundle to $TESTTMP/.hg/strip-backup/fda2b3b27b22-amend-backup.hg (glob)
283 $ hg st --rev '.^' --copies f
289 $ hg st --rev '.^' --copies f
284 A f
290 A f
285 d
291 d
286
292
287 $ mv f f.orig
293 $ mv f f.orig
288 $ hg rm -A f
294 $ hg rm -A f
289 $ hg ci -m removef
295 $ hg ci -m removef
290 $ hg cp a f
296 $ hg cp a f
291 $ mv f.orig f
297 $ mv f.orig f
292 $ hg ci --amend -m replacef
298 $ hg ci --amend -m replacef
293 saved backup bundle to $TESTTMP/.hg/strip-backup/20a7413547f9-amend-backup.hg (glob)
299 saved backup bundle to $TESTTMP/.hg/strip-backup/20a7413547f9-amend-backup.hg (glob)
294 $ hg st --change . --copies
300 $ hg st --change . --copies
295 $ hg log -r . --template "{file_copies}\n"
301 $ hg log -r . --template "{file_copies}\n"
296
302
297
303
298 Move added file (issue3410):
304 Move added file (issue3410):
299
305
300 $ echo g >> g
306 $ echo g >> g
301 $ hg ci -Am g
307 $ hg ci -Am g
302 adding g
308 adding g
303 $ hg mv g h
309 $ hg mv g h
304 $ hg ci --amend
310 $ hg ci --amend
305 saved backup bundle to $TESTTMP/.hg/strip-backup/5daa77a5d616-amend-backup.hg (glob)
311 saved backup bundle to $TESTTMP/.hg/strip-backup/5daa77a5d616-amend-backup.hg (glob)
306 $ hg st --change . --copies h
312 $ hg st --change . --copies h
307 A h
313 A h
308 $ hg log -r . --template "{file_copies}\n"
314 $ hg log -r . --template "{file_copies}\n"
309
315
310
316
311 Can't rollback an amend:
317 Can't rollback an amend:
312
318
313 $ hg rollback
319 $ hg rollback
314 no rollback information available
320 no rollback information available
315 [1]
321 [1]
316
322
317 Preserve extra dict (issue3430):
323 Preserve extra dict (issue3430):
318
324
319 $ hg branch a
325 $ hg branch a
320 marked working directory as branch a
326 marked working directory as branch a
321 (branches are permanent and global, did you want a bookmark?)
327 (branches are permanent and global, did you want a bookmark?)
322 $ echo a >> a
328 $ echo a >> a
323 $ hg ci -ma
329 $ hg ci -ma
324 $ hg ci --amend -m "a'"
330 $ hg ci --amend -m "a'"
325 saved backup bundle to $TESTTMP/.hg/strip-backup/167f8e3031df-amend-backup.hg (glob)
331 saved backup bundle to $TESTTMP/.hg/strip-backup/167f8e3031df-amend-backup.hg (glob)
326 $ hg log -r . --template "{branch}\n"
332 $ hg log -r . --template "{branch}\n"
327 a
333 a
328 $ hg ci --amend -m "a''"
334 $ hg ci --amend -m "a''"
329 saved backup bundle to $TESTTMP/.hg/strip-backup/ceac1a44c806-amend-backup.hg (glob)
335 saved backup bundle to $TESTTMP/.hg/strip-backup/ceac1a44c806-amend-backup.hg (glob)
330 $ hg log -r . --template "{branch}\n"
336 $ hg log -r . --template "{branch}\n"
331 a
337 a
332
338
333 Also preserve other entries in the dict that are in the old commit,
339 Also preserve other entries in the dict that are in the old commit,
334 first graft something so there's an additional entry:
340 first graft something so there's an additional entry:
335
341
336 $ hg up 0 -q
342 $ hg up 0 -q
337 $ echo z > z
343 $ echo z > z
338 $ hg ci -Am 'fork'
344 $ hg ci -Am 'fork'
339 adding z
345 adding z
340 created new head
346 created new head
341 $ hg up 11
347 $ hg up 11
342 5 files updated, 0 files merged, 1 files removed, 0 files unresolved
348 5 files updated, 0 files merged, 1 files removed, 0 files unresolved
343 $ hg graft 12
349 $ hg graft 12
344 grafting revision 12
350 grafting revision 12
345 $ hg ci --amend -m 'graft amend'
351 $ hg ci --amend -m 'graft amend'
346 saved backup bundle to $TESTTMP/.hg/strip-backup/18a5124daf7a-amend-backup.hg (glob)
352 saved backup bundle to $TESTTMP/.hg/strip-backup/18a5124daf7a-amend-backup.hg (glob)
347 $ hg log -r . --debug | grep extra
353 $ hg log -r . --debug | grep extra
348 extra: branch=a
354 extra: branch=a
349 extra: source=2647734878ef0236dda712fae9c1651cf694ea8a
355 extra: source=2647734878ef0236dda712fae9c1651cf694ea8a
@@ -1,307 +1,360 b''
1 Create a repo with some stuff in it:
1 Create a repo with some stuff in it:
2
2
3 $ hg init a
3 $ hg init a
4 $ cd a
4 $ cd a
5 $ echo a > a
5 $ echo a > a
6 $ echo a > d
6 $ echo a > d
7 $ echo a > e
7 $ echo a > e
8 $ hg ci -qAm0
8 $ hg ci -qAm0
9 $ echo b > a
9 $ echo b > a
10 $ hg ci -m1 -u bar
10 $ hg ci -m1 -u bar
11 $ hg mv a b
11 $ hg mv a b
12 $ hg ci -m2
12 $ hg ci -m2
13 $ hg cp b c
13 $ hg cp b c
14 $ hg ci -m3 -u baz
14 $ hg ci -m3 -u baz
15 $ echo b > d
15 $ echo b > d
16 $ echo f > e
16 $ echo f > e
17 $ hg ci -m4
17 $ hg ci -m4
18 $ hg up -q 3
18 $ hg up -q 3
19 $ echo b > e
19 $ echo b > e
20 $ hg branch -q stable
20 $ hg branch -q stable
21 $ hg ci -m5
21 $ hg ci -m5
22 $ hg merge -q default --tool internal:local
22 $ hg merge -q default --tool internal:local
23 $ hg branch -q default
23 $ hg branch -q default
24 $ hg ci -m6
24 $ hg ci -m6
25 $ hg phase --public 3
25 $ hg phase --public 3
26 $ hg phase --force --secret 6
26 $ hg phase --force --secret 6
27
27
28 $ hg --config extensions.graphlog= log -G --template '{author}@{rev}.{phase}: {desc}\n'
28 $ hg --config extensions.graphlog= log -G --template '{author}@{rev}.{phase}: {desc}\n'
29 @ test@6.secret: 6
29 @ test@6.secret: 6
30 |\
30 |\
31 | o test@5.draft: 5
31 | o test@5.draft: 5
32 | |
32 | |
33 o | test@4.draft: 4
33 o | test@4.draft: 4
34 |/
34 |/
35 o baz@3.public: 3
35 o baz@3.public: 3
36 |
36 |
37 o test@2.public: 2
37 o test@2.public: 2
38 |
38 |
39 o bar@1.public: 1
39 o bar@1.public: 1
40 |
40 |
41 o test@0.public: 0
41 o test@0.public: 0
42
42
43
43
44 Need to specify a rev:
44 Need to specify a rev:
45
45
46 $ hg graft
46 $ hg graft
47 abort: no revisions specified
47 abort: no revisions specified
48 [255]
48 [255]
49
49
50 Can't graft ancestor:
50 Can't graft ancestor:
51
51
52 $ hg graft 1 2
52 $ hg graft 1 2
53 skipping ancestor revision 1
53 skipping ancestor revision 1
54 skipping ancestor revision 2
54 skipping ancestor revision 2
55 [255]
55 [255]
56
56
57 Specify revisions with -r:
57 Specify revisions with -r:
58
58
59 $ hg graft -r 1 -r 2
59 $ hg graft -r 1 -r 2
60 skipping ancestor revision 1
60 skipping ancestor revision 1
61 skipping ancestor revision 2
61 skipping ancestor revision 2
62 [255]
62 [255]
63
63
64 $ hg graft -r 1 2
64 $ hg graft -r 1 2
65 skipping ancestor revision 2
65 skipping ancestor revision 2
66 skipping ancestor revision 1
66 skipping ancestor revision 1
67 [255]
67 [255]
68
68
69 Can't graft with dirty wd:
69 Can't graft with dirty wd:
70
70
71 $ hg up -q 0
71 $ hg up -q 0
72 $ echo foo > a
72 $ echo foo > a
73 $ hg graft 1
73 $ hg graft 1
74 abort: outstanding uncommitted changes
74 abort: outstanding uncommitted changes
75 [255]
75 [255]
76 $ hg revert a
76 $ hg revert a
77
77
78 Graft a rename:
78 Graft a rename:
79
79
80 $ hg graft 2 -u foo
80 $ hg graft 2 -u foo
81 grafting revision 2
81 grafting revision 2
82 merging a and b to b
82 merging a and b to b
83 $ hg export tip --git
83 $ hg export tip --git
84 # HG changeset patch
84 # HG changeset patch
85 # User foo
85 # User foo
86 # Date 0 0
86 # Date 0 0
87 # Node ID ef0ef43d49e79e81ddafdc7997401ba0041efc82
87 # Node ID ef0ef43d49e79e81ddafdc7997401ba0041efc82
88 # Parent 68795b066622ca79a25816a662041d8f78f3cd9e
88 # Parent 68795b066622ca79a25816a662041d8f78f3cd9e
89 2
89 2
90
90
91 diff --git a/a b/b
91 diff --git a/a b/b
92 rename from a
92 rename from a
93 rename to b
93 rename to b
94
94
95 Look for extra:source
95 Look for extra:source
96
96
97 $ hg log --debug -r tip
97 $ hg log --debug -r tip
98 changeset: 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82
98 changeset: 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82
99 tag: tip
99 tag: tip
100 phase: draft
100 phase: draft
101 parent: 0:68795b066622ca79a25816a662041d8f78f3cd9e
101 parent: 0:68795b066622ca79a25816a662041d8f78f3cd9e
102 parent: -1:0000000000000000000000000000000000000000
102 parent: -1:0000000000000000000000000000000000000000
103 manifest: 7:e59b6b228f9cbf9903d5e9abf996e083a1f533eb
103 manifest: 7:e59b6b228f9cbf9903d5e9abf996e083a1f533eb
104 user: foo
104 user: foo
105 date: Thu Jan 01 00:00:00 1970 +0000
105 date: Thu Jan 01 00:00:00 1970 +0000
106 files+: b
106 files+: b
107 files-: a
107 files-: a
108 extra: branch=default
108 extra: branch=default
109 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
109 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
110 description:
110 description:
111 2
111 2
112
112
113
113
114
114
115 Graft out of order, skipping a merge and a duplicate
115 Graft out of order, skipping a merge and a duplicate
116
116
117 $ hg graft 1 5 4 3 'merge()' 2 -n
117 $ hg graft 1 5 4 3 'merge()' 2 -n
118 skipping ungraftable merge revision 6
118 skipping ungraftable merge revision 6
119 skipping already grafted revision 2
119 skipping already grafted revision 2
120 grafting revision 1
120 grafting revision 1
121 grafting revision 5
121 grafting revision 5
122 grafting revision 4
122 grafting revision 4
123 grafting revision 3
123 grafting revision 3
124
124
125 $ hg graft 1 5 4 3 'merge()' 2 --debug
125 $ hg graft 1 5 4 3 'merge()' 2 --debug
126 skipping ungraftable merge revision 6
126 skipping ungraftable merge revision 6
127 scanning for duplicate grafts
127 scanning for duplicate grafts
128 skipping already grafted revision 2
128 skipping already grafted revision 2
129 grafting revision 1
129 grafting revision 1
130 searching for copies back to rev 1
130 searching for copies back to rev 1
131 unmatched files in local:
131 unmatched files in local:
132 b
132 b
133 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
133 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
134 b -> a *
134 b -> a *
135 checking for directory renames
135 checking for directory renames
136 resolving manifests
136 resolving manifests
137 overwrite: False, partial: False
137 overwrite: False, partial: False
138 ancestor: 68795b066622, local: ef0ef43d49e7+, remote: 5d205f8b35b6
138 ancestor: 68795b066622, local: ef0ef43d49e7+, remote: 5d205f8b35b6
139 b: local copied/moved to a -> m
139 b: local copied/moved to a -> m
140 preserving b for resolve of b
140 preserving b for resolve of b
141 updating: b 1/1 files (100.00%)
141 updating: b 1/1 files (100.00%)
142 picked tool 'internal:merge' for b (binary False symlink False)
142 picked tool 'internal:merge' for b (binary False symlink False)
143 merging b and a to b
143 merging b and a to b
144 my b@ef0ef43d49e7+ other a@5d205f8b35b6 ancestor a@68795b066622
144 my b@ef0ef43d49e7+ other a@5d205f8b35b6 ancestor a@68795b066622
145 premerge successful
145 premerge successful
146 b
146 b
147 grafting revision 5
147 grafting revision 5
148 searching for copies back to rev 1
148 searching for copies back to rev 1
149 resolving manifests
149 resolving manifests
150 overwrite: False, partial: False
150 overwrite: False, partial: False
151 ancestor: 4c60f11aa304, local: 6b9e5368ca4e+, remote: 97f8bfe72746
151 ancestor: 4c60f11aa304, local: 6b9e5368ca4e+, remote: 97f8bfe72746
152 e: remote is newer -> g
152 e: remote is newer -> g
153 updating: e 1/1 files (100.00%)
153 updating: e 1/1 files (100.00%)
154 getting e
154 getting e
155 e
155 e
156 grafting revision 4
156 grafting revision 4
157 searching for copies back to rev 1
157 searching for copies back to rev 1
158 resolving manifests
158 resolving manifests
159 overwrite: False, partial: False
159 overwrite: False, partial: False
160 ancestor: 4c60f11aa304, local: 1905859650ec+, remote: 9c233e8e184d
160 ancestor: 4c60f11aa304, local: 1905859650ec+, remote: 9c233e8e184d
161 e: versions differ -> m
161 e: versions differ -> m
162 d: remote is newer -> g
162 d: remote is newer -> g
163 preserving e for resolve of e
163 preserving e for resolve of e
164 updating: d 1/2 files (50.00%)
164 updating: d 1/2 files (50.00%)
165 getting d
165 getting d
166 updating: e 2/2 files (100.00%)
166 updating: e 2/2 files (100.00%)
167 picked tool 'internal:merge' for e (binary False symlink False)
167 picked tool 'internal:merge' for e (binary False symlink False)
168 merging e
168 merging e
169 my e@1905859650ec+ other e@9c233e8e184d ancestor e@68795b066622
169 my e@1905859650ec+ other e@9c233e8e184d ancestor e@68795b066622
170 warning: conflicts during merge.
170 warning: conflicts during merge.
171 merging e incomplete! (edit conflicts, then use 'hg resolve --mark')
171 merging e incomplete! (edit conflicts, then use 'hg resolve --mark')
172 abort: unresolved conflicts, can't continue
172 abort: unresolved conflicts, can't continue
173 (use hg resolve and hg graft --continue)
173 (use hg resolve and hg graft --continue)
174 [255]
174 [255]
175
175
176 Continue without resolve should fail:
176 Continue without resolve should fail:
177
177
178 $ hg graft -c
178 $ hg graft -c
179 grafting revision 4
179 grafting revision 4
180 abort: unresolved merge conflicts (see hg help resolve)
180 abort: unresolved merge conflicts (see hg help resolve)
181 [255]
181 [255]
182
182
183 Fix up:
183 Fix up:
184
184
185 $ echo b > e
185 $ echo b > e
186 $ hg resolve -m e
186 $ hg resolve -m e
187
187
188 Continue with a revision should fail:
188 Continue with a revision should fail:
189
189
190 $ hg graft -c 6
190 $ hg graft -c 6
191 abort: can't specify --continue and revisions
191 abort: can't specify --continue and revisions
192 [255]
192 [255]
193
193
194 $ hg graft -c -r 6
194 $ hg graft -c -r 6
195 abort: can't specify --continue and revisions
195 abort: can't specify --continue and revisions
196 [255]
196 [255]
197
197
198 Continue for real, clobber usernames
198 Continue for real, clobber usernames
199
199
200 $ hg graft -c -U
200 $ hg graft -c -U
201 grafting revision 4
201 grafting revision 4
202 grafting revision 3
202 grafting revision 3
203
203
204 Compare with original:
204 Compare with original:
205
205
206 $ hg diff -r 6
206 $ hg diff -r 6
207 $ hg status --rev 0:. -C
207 $ hg status --rev 0:. -C
208 M d
208 M d
209 M e
209 M e
210 A b
210 A b
211 a
211 a
212 A c
212 A c
213 a
213 a
214 R a
214 R a
215
215
216 View graph:
216 View graph:
217
217
218 $ hg --config extensions.graphlog= log -G --template '{author}@{rev}.{phase}: {desc}\n'
218 $ hg --config extensions.graphlog= log -G --template '{author}@{rev}.{phase}: {desc}\n'
219 @ test@11.draft: 3
219 @ test@11.draft: 3
220 |
220 |
221 o test@10.draft: 4
221 o test@10.draft: 4
222 |
222 |
223 o test@9.draft: 5
223 o test@9.draft: 5
224 |
224 |
225 o bar@8.draft: 1
225 o bar@8.draft: 1
226 |
226 |
227 o foo@7.draft: 2
227 o foo@7.draft: 2
228 |
228 |
229 | o test@6.secret: 6
229 | o test@6.secret: 6
230 | |\
230 | |\
231 | | o test@5.draft: 5
231 | | o test@5.draft: 5
232 | | |
232 | | |
233 | o | test@4.draft: 4
233 | o | test@4.draft: 4
234 | |/
234 | |/
235 | o baz@3.public: 3
235 | o baz@3.public: 3
236 | |
236 | |
237 | o test@2.public: 2
237 | o test@2.public: 2
238 | |
238 | |
239 | o bar@1.public: 1
239 | o bar@1.public: 1
240 |/
240 |/
241 o test@0.public: 0
241 o test@0.public: 0
242
242
243 Graft again onto another branch should preserve the original source
243 Graft again onto another branch should preserve the original source
244 $ hg up -q 0
244 $ hg up -q 0
245 $ echo 'g'>g
245 $ echo 'g'>g
246 $ hg add g
246 $ hg add g
247 $ hg ci -m 7
247 $ hg ci -m 7
248 created new head
248 created new head
249 $ hg graft 7
249 $ hg graft 7
250 grafting revision 7
250 grafting revision 7
251
251
252 $ hg log -r 7 --template '{rev}:{node}\n'
252 $ hg log -r 7 --template '{rev}:{node}\n'
253 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82
253 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82
254 $ hg log -r 2 --template '{rev}:{node}\n'
254 $ hg log -r 2 --template '{rev}:{node}\n'
255 2:5c095ad7e90f871700f02dd1fa5012cb4498a2d4
255 2:5c095ad7e90f871700f02dd1fa5012cb4498a2d4
256
256
257 $ hg log --debug -r tip
257 $ hg log --debug -r tip
258 changeset: 13:9db0f28fd3747e92c57d015f53b5593aeec53c2d
258 changeset: 13:9db0f28fd3747e92c57d015f53b5593aeec53c2d
259 tag: tip
259 tag: tip
260 phase: draft
260 phase: draft
261 parent: 12:b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
261 parent: 12:b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
262 parent: -1:0000000000000000000000000000000000000000
262 parent: -1:0000000000000000000000000000000000000000
263 manifest: 13:dc313617b8c32457c0d589e0dbbedfe71f3cd637
263 manifest: 13:dc313617b8c32457c0d589e0dbbedfe71f3cd637
264 user: foo
264 user: foo
265 date: Thu Jan 01 00:00:00 1970 +0000
265 date: Thu Jan 01 00:00:00 1970 +0000
266 files+: b
266 files+: b
267 files-: a
267 files-: a
268 extra: branch=default
268 extra: branch=default
269 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
269 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
270 description:
270 description:
271 2
271 2
272
272
273
273
274 Disallow grafting an already grafted cset onto its original branch
274 Disallow grafting an already grafted cset onto its original branch
275 $ hg up -q 6
275 $ hg up -q 6
276 $ hg graft 7
276 $ hg graft 7
277 skipping already grafted revision 7 (was grafted from 2)
277 skipping already grafted revision 7 (was grafted from 2)
278 [255]
278 [255]
279
279
280 Disallow grafting already grafted csets with the same origin onto each other
280 Disallow grafting already grafted csets with the same origin onto each other
281 $ hg up -q 13
281 $ hg up -q 13
282 $ hg graft 2
282 $ hg graft 2
283 skipping already grafted revision 2
283 skipping already grafted revision 2
284 [255]
284 [255]
285 $ hg graft 7
285 $ hg graft 7
286 skipping already grafted revision 7 (same origin 2)
286 skipping already grafted revision 7 (same origin 2)
287 [255]
287 [255]
288
288
289 $ hg up -q 7
289 $ hg up -q 7
290 $ hg graft 2
290 $ hg graft 2
291 skipping already grafted revision 2
291 skipping already grafted revision 2
292 [255]
292 [255]
293 $ hg graft tip
293 $ hg graft tip
294 skipping already grafted revision 13 (same origin 2)
294 skipping already grafted revision 13 (same origin 2)
295 [255]
295 [255]
296
296
297 Graft with --log
297 Graft with --log
298
298
299 $ hg up -Cq 1
299 $ hg up -Cq 1
300 $ hg graft 3 --log -u foo
300 $ hg graft 3 --log -u foo
301 grafting revision 3
301 grafting revision 3
302 warning: can't find ancestor for 'c' copied from 'b'!
302 warning: can't find ancestor for 'c' copied from 'b'!
303 $ hg log --template '{rev} {parents} {desc}\n' -r tip
303 $ hg log --template '{rev} {parents} {desc}\n' -r tip
304 14 1:5d205f8b35b6 3
304 14 1:5d205f8b35b6 3
305 (grafted from 4c60f11aa304a54ae1c199feb94e7fc771e51ed8)
305 (grafted from 4c60f11aa304a54ae1c199feb94e7fc771e51ed8)
306
306
307 $ cd ..
307 Resolve conflicted graft
308 $ hg up -q 0
309 $ echo b > a
310 $ hg ci -m 8
311 created new head
312 $ echo a > a
313 $ hg ci -m 9
314 $ hg graft 1 --tool internal:fail
315 grafting revision 1
316 abort: unresolved conflicts, can't continue
317 (use hg resolve and hg graft --continue)
318 [255]
319 $ hg resolve --all
320 merging a
321 $ hg graft -c
322 grafting revision 1
323 $ hg export tip --git
324 # HG changeset patch
325 # User bar
326 # Date 0 0
327 # Node ID 64ecd9071ce83c6e62f538d8ce7709d53f32ebf7
328 # Parent 4bdb9a9d0b84ffee1d30f0dfc7744cade17aa19c
329 1
330
331 diff --git a/a b/a
332 --- a/a
333 +++ b/a
334 @@ -1,1 +1,1 @@
335 -a
336 +b
337
338 Resolve conflicted graft with rename
339 $ echo c > a
340 $ hg ci -m 10
341 $ hg graft 2 --tool internal:fail
342 grafting revision 2
343 abort: unresolved conflicts, can't continue
344 (use hg resolve and hg graft --continue)
345 [255]
346 $ hg resolve --all
347 merging a and b to b
348 $ hg graft -c
349 grafting revision 2
350 $ hg export tip --git
351 # HG changeset patch
352 # User test
353 # Date 0 0
354 # Node ID 2e80e1351d6ed50302fe1e05f8bd1d4d412b6e11
355 # Parent e5a51ae854a8bbaaf25cc5c6a57ff46042dadbb4
356 2
357
358 diff --git a/a b/b
359 rename from a
360 rename to b
@@ -1,90 +1,113 b''
1 $ cat >> $HGRCPATH <<EOF
1 $ cat >> $HGRCPATH <<EOF
2 > [extensions]
2 > [extensions]
3 > graphlog=
3 > graphlog=
4 > rebase=
4 > rebase=
5 >
5 >
6 > [phases]
6 > [phases]
7 > publish=False
7 > publish=False
8 >
8 >
9 > [alias]
9 > [alias]
10 > tglog = log -G --template "{rev}: '{desc}' bookmarks: {bookmarks}\n"
10 > tglog = log -G --template "{rev}: '{desc}' bookmarks: {bookmarks}\n"
11 > EOF
11 > EOF
12
12
13 Create a repo with several bookmarks
13 Create a repo with several bookmarks
14 $ hg init a
14 $ hg init a
15 $ cd a
15 $ cd a
16
16
17 $ echo a > a
17 $ echo a > a
18 $ hg ci -Am A
18 $ hg ci -Am A
19 adding a
19 adding a
20
20
21 $ echo b > b
21 $ echo b > b
22 $ hg ci -Am B
22 $ hg ci -Am B
23 adding b
23 adding b
24 $ hg book 'X'
24 $ hg book 'X'
25 $ hg book 'Y'
25 $ hg book 'Y'
26
26
27 $ echo c > c
27 $ echo c > c
28 $ hg ci -Am C
28 $ hg ci -Am C
29 adding c
29 adding c
30 $ hg book 'Z'
30 $ hg book 'Z'
31
31
32 $ hg up -q 0
32 $ hg up -q 0
33
33
34 $ echo d > d
34 $ echo d > d
35 $ hg ci -Am D
35 $ hg ci -Am D
36 adding d
36 adding d
37 created new head
37 created new head
38
38
39 $ hg book W
40
39 $ hg tglog
41 $ hg tglog
40 @ 3: 'D' bookmarks:
42 @ 3: 'D' bookmarks: W
41 |
43 |
42 | o 2: 'C' bookmarks: Y Z
44 | o 2: 'C' bookmarks: Y Z
43 | |
45 | |
44 | o 1: 'B' bookmarks: X
46 | o 1: 'B' bookmarks: X
45 |/
47 |/
46 o 0: 'A' bookmarks:
48 o 0: 'A' bookmarks:
47
49
48
50
49 Move only rebased bookmarks
51 Move only rebased bookmarks
50
52
51 $ cd ..
53 $ cd ..
52 $ hg clone -q a a1
54 $ hg clone -q a a1
53
55
54 $ cd a1
56 $ cd a1
55 $ hg up -q Z
57 $ hg up -q Z
56
58
57 $ hg rebase -s Y -d 3
59 $ hg rebase -s Y -d 3
58 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/*-backup.hg (glob)
60 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/*-backup.hg (glob)
59
61
60 $ hg tglog
62 $ hg tglog
61 @ 3: 'C' bookmarks: Y Z
63 @ 3: 'C' bookmarks: Y Z
62 |
64 |
63 o 2: 'D' bookmarks:
65 o 2: 'D' bookmarks: W
64 |
66 |
65 | o 1: 'B' bookmarks: X
67 | o 1: 'B' bookmarks: X
66 |/
68 |/
67 o 0: 'A' bookmarks:
69 o 0: 'A' bookmarks:
68
70
69 Keep bookmarks to the correct rebased changeset
71 Keep bookmarks to the correct rebased changeset
70
72
71 $ cd ..
73 $ cd ..
72 $ hg clone -q a a2
74 $ hg clone -q a a2
73
75
74 $ cd a2
76 $ cd a2
75 $ hg up -q Z
77 $ hg up -q Z
76
78
77 $ hg rebase -s 1 -d 3
79 $ hg rebase -s 1 -d 3
78 saved backup bundle to $TESTTMP/a2/.hg/strip-backup/*-backup.hg (glob)
80 saved backup bundle to $TESTTMP/a2/.hg/strip-backup/*-backup.hg (glob)
79
81
80 $ hg tglog
82 $ hg tglog
81 @ 3: 'C' bookmarks: Y Z
83 @ 3: 'C' bookmarks: Y Z
82 |
84 |
83 o 2: 'B' bookmarks: X
85 o 2: 'B' bookmarks: X
84 |
86 |
85 o 1: 'D' bookmarks:
87 o 1: 'D' bookmarks: W
88 |
89 o 0: 'A' bookmarks:
90
91
92 Keep active bookmark on the correct changeset
93
94 $ cd ..
95 $ hg clone -q a a3
96
97 $ cd a3
98 $ hg up -q X
99
100 $ hg rebase -d W
101 saved backup bundle to $TESTTMP/a3/.hg/strip-backup/*-backup.hg (glob)
102
103 $ hg tglog
104 @ 3: 'C' bookmarks: Y Z
105 |
106 o 2: 'B' bookmarks: X
107 |
108 o 1: 'D' bookmarks: W
86 |
109 |
87 o 0: 'A' bookmarks:
110 o 0: 'A' bookmarks:
88
111
89
112
90 $ cd ..
113 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now