##// END OF EJS Templates
mq: restore _branchtags() fast path (issue3223)...
Patrick Mezard -
r16101:20ad8f05 stable
parent child Browse files
Show More
@@ -1,3393 +1,3401 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
50
51 from mercurial.i18n import _
51 from mercurial.i18n import _
52 from mercurial.node import bin, hex, short, nullid, nullrev
52 from mercurial.node import bin, hex, short, nullid, nullrev
53 from mercurial.lock import release
53 from mercurial.lock import release
54 from mercurial import commands, cmdutil, hg, scmutil, util, revset
54 from mercurial import commands, cmdutil, hg, scmutil, util, revset
55 from mercurial import repair, extensions, url, error, phases
55 from mercurial import repair, extensions, url, error, phases
56 from mercurial import patch as patchmod
56 from mercurial import patch as patchmod
57 import os, re, errno, shutil
57 import os, re, errno, shutil
58
58
59 commands.norepo += " qclone"
59 commands.norepo += " qclone"
60
60
61 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
61 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
62
62
63 cmdtable = {}
63 cmdtable = {}
64 command = cmdutil.command(cmdtable)
64 command = cmdutil.command(cmdtable)
65
65
66 # Patch names looks like unix-file names.
66 # Patch names looks like unix-file names.
67 # They must be joinable with queue directory and result in the patch path.
67 # They must be joinable with queue directory and result in the patch path.
68 normname = util.normpath
68 normname = util.normpath
69
69
70 class statusentry(object):
70 class statusentry(object):
71 def __init__(self, node, name):
71 def __init__(self, node, name):
72 self.node, self.name = node, name
72 self.node, self.name = node, name
73 def __repr__(self):
73 def __repr__(self):
74 return hex(self.node) + ':' + self.name
74 return hex(self.node) + ':' + self.name
75
75
76 class patchheader(object):
76 class patchheader(object):
77 def __init__(self, pf, plainmode=False):
77 def __init__(self, pf, plainmode=False):
78 def eatdiff(lines):
78 def eatdiff(lines):
79 while lines:
79 while lines:
80 l = lines[-1]
80 l = lines[-1]
81 if (l.startswith("diff -") or
81 if (l.startswith("diff -") or
82 l.startswith("Index:") or
82 l.startswith("Index:") or
83 l.startswith("===========")):
83 l.startswith("===========")):
84 del lines[-1]
84 del lines[-1]
85 else:
85 else:
86 break
86 break
87 def eatempty(lines):
87 def eatempty(lines):
88 while lines:
88 while lines:
89 if not lines[-1].strip():
89 if not lines[-1].strip():
90 del lines[-1]
90 del lines[-1]
91 else:
91 else:
92 break
92 break
93
93
94 message = []
94 message = []
95 comments = []
95 comments = []
96 user = None
96 user = None
97 date = None
97 date = None
98 parent = None
98 parent = None
99 format = None
99 format = None
100 subject = None
100 subject = None
101 branch = None
101 branch = None
102 nodeid = None
102 nodeid = None
103 diffstart = 0
103 diffstart = 0
104
104
105 for line in file(pf):
105 for line in file(pf):
106 line = line.rstrip()
106 line = line.rstrip()
107 if (line.startswith('diff --git')
107 if (line.startswith('diff --git')
108 or (diffstart and line.startswith('+++ '))):
108 or (diffstart and line.startswith('+++ '))):
109 diffstart = 2
109 diffstart = 2
110 break
110 break
111 diffstart = 0 # reset
111 diffstart = 0 # reset
112 if line.startswith("--- "):
112 if line.startswith("--- "):
113 diffstart = 1
113 diffstart = 1
114 continue
114 continue
115 elif format == "hgpatch":
115 elif format == "hgpatch":
116 # parse values when importing the result of an hg export
116 # parse values when importing the result of an hg export
117 if line.startswith("# User "):
117 if line.startswith("# User "):
118 user = line[7:]
118 user = line[7:]
119 elif line.startswith("# Date "):
119 elif line.startswith("# Date "):
120 date = line[7:]
120 date = line[7:]
121 elif line.startswith("# Parent "):
121 elif line.startswith("# Parent "):
122 parent = line[9:].lstrip()
122 parent = line[9:].lstrip()
123 elif line.startswith("# Branch "):
123 elif line.startswith("# Branch "):
124 branch = line[9:]
124 branch = line[9:]
125 elif line.startswith("# Node ID "):
125 elif line.startswith("# Node ID "):
126 nodeid = line[10:]
126 nodeid = line[10:]
127 elif not line.startswith("# ") and line:
127 elif not line.startswith("# ") and line:
128 message.append(line)
128 message.append(line)
129 format = None
129 format = None
130 elif line == '# HG changeset patch':
130 elif line == '# HG changeset patch':
131 message = []
131 message = []
132 format = "hgpatch"
132 format = "hgpatch"
133 elif (format != "tagdone" and (line.startswith("Subject: ") or
133 elif (format != "tagdone" and (line.startswith("Subject: ") or
134 line.startswith("subject: "))):
134 line.startswith("subject: "))):
135 subject = line[9:]
135 subject = line[9:]
136 format = "tag"
136 format = "tag"
137 elif (format != "tagdone" and (line.startswith("From: ") or
137 elif (format != "tagdone" and (line.startswith("From: ") or
138 line.startswith("from: "))):
138 line.startswith("from: "))):
139 user = line[6:]
139 user = line[6:]
140 format = "tag"
140 format = "tag"
141 elif (format != "tagdone" and (line.startswith("Date: ") or
141 elif (format != "tagdone" and (line.startswith("Date: ") or
142 line.startswith("date: "))):
142 line.startswith("date: "))):
143 date = line[6:]
143 date = line[6:]
144 format = "tag"
144 format = "tag"
145 elif format == "tag" and line == "":
145 elif format == "tag" and line == "":
146 # when looking for tags (subject: from: etc) they
146 # when looking for tags (subject: from: etc) they
147 # end once you find a blank line in the source
147 # end once you find a blank line in the source
148 format = "tagdone"
148 format = "tagdone"
149 elif message or line:
149 elif message or line:
150 message.append(line)
150 message.append(line)
151 comments.append(line)
151 comments.append(line)
152
152
153 eatdiff(message)
153 eatdiff(message)
154 eatdiff(comments)
154 eatdiff(comments)
155 # Remember the exact starting line of the patch diffs before consuming
155 # Remember the exact starting line of the patch diffs before consuming
156 # empty lines, for external use by TortoiseHg and others
156 # empty lines, for external use by TortoiseHg and others
157 self.diffstartline = len(comments)
157 self.diffstartline = len(comments)
158 eatempty(message)
158 eatempty(message)
159 eatempty(comments)
159 eatempty(comments)
160
160
161 # make sure message isn't empty
161 # make sure message isn't empty
162 if format and format.startswith("tag") and subject:
162 if format and format.startswith("tag") and subject:
163 message.insert(0, "")
163 message.insert(0, "")
164 message.insert(0, subject)
164 message.insert(0, subject)
165
165
166 self.message = message
166 self.message = message
167 self.comments = comments
167 self.comments = comments
168 self.user = user
168 self.user = user
169 self.date = date
169 self.date = date
170 self.parent = parent
170 self.parent = parent
171 # nodeid and branch are for external use by TortoiseHg and others
171 # nodeid and branch are for external use by TortoiseHg and others
172 self.nodeid = nodeid
172 self.nodeid = nodeid
173 self.branch = branch
173 self.branch = branch
174 self.haspatch = diffstart > 1
174 self.haspatch = diffstart > 1
175 self.plainmode = plainmode
175 self.plainmode = plainmode
176
176
177 def setuser(self, user):
177 def setuser(self, user):
178 if not self.updateheader(['From: ', '# User '], user):
178 if not self.updateheader(['From: ', '# User '], user):
179 try:
179 try:
180 patchheaderat = self.comments.index('# HG changeset patch')
180 patchheaderat = self.comments.index('# HG changeset patch')
181 self.comments.insert(patchheaderat + 1, '# User ' + user)
181 self.comments.insert(patchheaderat + 1, '# User ' + user)
182 except ValueError:
182 except ValueError:
183 if self.plainmode or self._hasheader(['Date: ']):
183 if self.plainmode or self._hasheader(['Date: ']):
184 self.comments = ['From: ' + user] + self.comments
184 self.comments = ['From: ' + user] + self.comments
185 else:
185 else:
186 tmp = ['# HG changeset patch', '# User ' + user, '']
186 tmp = ['# HG changeset patch', '# User ' + user, '']
187 self.comments = tmp + self.comments
187 self.comments = tmp + self.comments
188 self.user = user
188 self.user = user
189
189
190 def setdate(self, date):
190 def setdate(self, date):
191 if not self.updateheader(['Date: ', '# Date '], date):
191 if not self.updateheader(['Date: ', '# Date '], date):
192 try:
192 try:
193 patchheaderat = self.comments.index('# HG changeset patch')
193 patchheaderat = self.comments.index('# HG changeset patch')
194 self.comments.insert(patchheaderat + 1, '# Date ' + date)
194 self.comments.insert(patchheaderat + 1, '# Date ' + date)
195 except ValueError:
195 except ValueError:
196 if self.plainmode or self._hasheader(['From: ']):
196 if self.plainmode or self._hasheader(['From: ']):
197 self.comments = ['Date: ' + date] + self.comments
197 self.comments = ['Date: ' + date] + self.comments
198 else:
198 else:
199 tmp = ['# HG changeset patch', '# Date ' + date, '']
199 tmp = ['# HG changeset patch', '# Date ' + date, '']
200 self.comments = tmp + self.comments
200 self.comments = tmp + self.comments
201 self.date = date
201 self.date = date
202
202
203 def setparent(self, parent):
203 def setparent(self, parent):
204 if not self.updateheader(['# Parent '], parent):
204 if not self.updateheader(['# Parent '], parent):
205 try:
205 try:
206 patchheaderat = self.comments.index('# HG changeset patch')
206 patchheaderat = self.comments.index('# HG changeset patch')
207 self.comments.insert(patchheaderat + 1, '# Parent ' + parent)
207 self.comments.insert(patchheaderat + 1, '# Parent ' + parent)
208 except ValueError:
208 except ValueError:
209 pass
209 pass
210 self.parent = parent
210 self.parent = parent
211
211
212 def setmessage(self, message):
212 def setmessage(self, message):
213 if self.comments:
213 if self.comments:
214 self._delmsg()
214 self._delmsg()
215 self.message = [message]
215 self.message = [message]
216 self.comments += self.message
216 self.comments += self.message
217
217
218 def updateheader(self, prefixes, new):
218 def updateheader(self, prefixes, new):
219 '''Update all references to a field in the patch header.
219 '''Update all references to a field in the patch header.
220 Return whether the field is present.'''
220 Return whether the field is present.'''
221 res = False
221 res = False
222 for prefix in prefixes:
222 for prefix in prefixes:
223 for i in xrange(len(self.comments)):
223 for i in xrange(len(self.comments)):
224 if self.comments[i].startswith(prefix):
224 if self.comments[i].startswith(prefix):
225 self.comments[i] = prefix + new
225 self.comments[i] = prefix + new
226 res = True
226 res = True
227 break
227 break
228 return res
228 return res
229
229
230 def _hasheader(self, prefixes):
230 def _hasheader(self, prefixes):
231 '''Check if a header starts with any of the given prefixes.'''
231 '''Check if a header starts with any of the given prefixes.'''
232 for prefix in prefixes:
232 for prefix in prefixes:
233 for comment in self.comments:
233 for comment in self.comments:
234 if comment.startswith(prefix):
234 if comment.startswith(prefix):
235 return True
235 return True
236 return False
236 return False
237
237
238 def __str__(self):
238 def __str__(self):
239 if not self.comments:
239 if not self.comments:
240 return ''
240 return ''
241 return '\n'.join(self.comments) + '\n\n'
241 return '\n'.join(self.comments) + '\n\n'
242
242
243 def _delmsg(self):
243 def _delmsg(self):
244 '''Remove existing message, keeping the rest of the comments fields.
244 '''Remove existing message, keeping the rest of the comments fields.
245 If comments contains 'subject: ', message will prepend
245 If comments contains 'subject: ', message will prepend
246 the field and a blank line.'''
246 the field and a blank line.'''
247 if self.message:
247 if self.message:
248 subj = 'subject: ' + self.message[0].lower()
248 subj = 'subject: ' + self.message[0].lower()
249 for i in xrange(len(self.comments)):
249 for i in xrange(len(self.comments)):
250 if subj == self.comments[i].lower():
250 if subj == self.comments[i].lower():
251 del self.comments[i]
251 del self.comments[i]
252 self.message = self.message[2:]
252 self.message = self.message[2:]
253 break
253 break
254 ci = 0
254 ci = 0
255 for mi in self.message:
255 for mi in self.message:
256 while mi != self.comments[ci]:
256 while mi != self.comments[ci]:
257 ci += 1
257 ci += 1
258 del self.comments[ci]
258 del self.comments[ci]
259
259
260 def secretcommit(repo, phase, *args, **kwargs):
260 def secretcommit(repo, phase, *args, **kwargs):
261 """helper dedicated to ensure a commit are secret
261 """helper dedicated to ensure a commit are secret
262
262
263 It should be used instead of repo.commit inside the mq source
263 It should be used instead of repo.commit inside the mq source
264 """
264 """
265 if phase is None:
265 if phase is None:
266 if repo.ui.configbool('mq', 'secret', False):
266 if repo.ui.configbool('mq', 'secret', False):
267 phase = phases.secret
267 phase = phases.secret
268 if phase is not None:
268 if phase is not None:
269 backup = repo.ui.backupconfig('phases', 'new-commit')
269 backup = repo.ui.backupconfig('phases', 'new-commit')
270 # Marking the repository as committing an mq patch can be used
271 # to optimize operations like _branchtags().
272 repo._committingpatch = True
270 try:
273 try:
271 if phase is not None:
274 if phase is not None:
272 repo.ui.setconfig('phases', 'new-commit', phase)
275 repo.ui.setconfig('phases', 'new-commit', phase)
273 return repo.commit(*args, **kwargs)
276 return repo.commit(*args, **kwargs)
274 finally:
277 finally:
278 repo._committingpatch = False
275 if phase is not None:
279 if phase is not None:
276 repo.ui.restoreconfig(backup)
280 repo.ui.restoreconfig(backup)
277
281
278 class queue(object):
282 class queue(object):
279 def __init__(self, ui, path, patchdir=None):
283 def __init__(self, ui, path, patchdir=None):
280 self.basepath = path
284 self.basepath = path
281 try:
285 try:
282 fh = open(os.path.join(path, 'patches.queue'))
286 fh = open(os.path.join(path, 'patches.queue'))
283 cur = fh.read().rstrip()
287 cur = fh.read().rstrip()
284 fh.close()
288 fh.close()
285 if not cur:
289 if not cur:
286 curpath = os.path.join(path, 'patches')
290 curpath = os.path.join(path, 'patches')
287 else:
291 else:
288 curpath = os.path.join(path, 'patches-' + cur)
292 curpath = os.path.join(path, 'patches-' + cur)
289 except IOError:
293 except IOError:
290 curpath = os.path.join(path, 'patches')
294 curpath = os.path.join(path, 'patches')
291 self.path = patchdir or curpath
295 self.path = patchdir or curpath
292 self.opener = scmutil.opener(self.path)
296 self.opener = scmutil.opener(self.path)
293 self.ui = ui
297 self.ui = ui
294 self.applieddirty = False
298 self.applieddirty = False
295 self.seriesdirty = False
299 self.seriesdirty = False
296 self.added = []
300 self.added = []
297 self.seriespath = "series"
301 self.seriespath = "series"
298 self.statuspath = "status"
302 self.statuspath = "status"
299 self.guardspath = "guards"
303 self.guardspath = "guards"
300 self.activeguards = None
304 self.activeguards = None
301 self.guardsdirty = False
305 self.guardsdirty = False
302 # Handle mq.git as a bool with extended values
306 # Handle mq.git as a bool with extended values
303 try:
307 try:
304 gitmode = ui.configbool('mq', 'git', None)
308 gitmode = ui.configbool('mq', 'git', None)
305 if gitmode is None:
309 if gitmode is None:
306 raise error.ConfigError()
310 raise error.ConfigError()
307 self.gitmode = gitmode and 'yes' or 'no'
311 self.gitmode = gitmode and 'yes' or 'no'
308 except error.ConfigError:
312 except error.ConfigError:
309 self.gitmode = ui.config('mq', 'git', 'auto').lower()
313 self.gitmode = ui.config('mq', 'git', 'auto').lower()
310 self.plainmode = ui.configbool('mq', 'plain', False)
314 self.plainmode = ui.configbool('mq', 'plain', False)
311
315
312 @util.propertycache
316 @util.propertycache
313 def applied(self):
317 def applied(self):
314 def parselines(lines):
318 def parselines(lines):
315 for l in lines:
319 for l in lines:
316 entry = l.split(':', 1)
320 entry = l.split(':', 1)
317 if len(entry) > 1:
321 if len(entry) > 1:
318 n, name = entry
322 n, name = entry
319 yield statusentry(bin(n), name)
323 yield statusentry(bin(n), name)
320 elif l.strip():
324 elif l.strip():
321 self.ui.warn(_('malformated mq status line: %s\n') % entry)
325 self.ui.warn(_('malformated mq status line: %s\n') % entry)
322 # else we ignore empty lines
326 # else we ignore empty lines
323 try:
327 try:
324 lines = self.opener.read(self.statuspath).splitlines()
328 lines = self.opener.read(self.statuspath).splitlines()
325 return list(parselines(lines))
329 return list(parselines(lines))
326 except IOError, e:
330 except IOError, e:
327 if e.errno == errno.ENOENT:
331 if e.errno == errno.ENOENT:
328 return []
332 return []
329 raise
333 raise
330
334
331 @util.propertycache
335 @util.propertycache
332 def fullseries(self):
336 def fullseries(self):
333 try:
337 try:
334 return self.opener.read(self.seriespath).splitlines()
338 return self.opener.read(self.seriespath).splitlines()
335 except IOError, e:
339 except IOError, e:
336 if e.errno == errno.ENOENT:
340 if e.errno == errno.ENOENT:
337 return []
341 return []
338 raise
342 raise
339
343
340 @util.propertycache
344 @util.propertycache
341 def series(self):
345 def series(self):
342 self.parseseries()
346 self.parseseries()
343 return self.series
347 return self.series
344
348
345 @util.propertycache
349 @util.propertycache
346 def seriesguards(self):
350 def seriesguards(self):
347 self.parseseries()
351 self.parseseries()
348 return self.seriesguards
352 return self.seriesguards
349
353
350 def invalidate(self):
354 def invalidate(self):
351 for a in 'applied fullseries series seriesguards'.split():
355 for a in 'applied fullseries series seriesguards'.split():
352 if a in self.__dict__:
356 if a in self.__dict__:
353 delattr(self, a)
357 delattr(self, a)
354 self.applieddirty = False
358 self.applieddirty = False
355 self.seriesdirty = False
359 self.seriesdirty = False
356 self.guardsdirty = False
360 self.guardsdirty = False
357 self.activeguards = None
361 self.activeguards = None
358
362
359 def diffopts(self, opts={}, patchfn=None):
363 def diffopts(self, opts={}, patchfn=None):
360 diffopts = patchmod.diffopts(self.ui, opts)
364 diffopts = patchmod.diffopts(self.ui, opts)
361 if self.gitmode == 'auto':
365 if self.gitmode == 'auto':
362 diffopts.upgrade = True
366 diffopts.upgrade = True
363 elif self.gitmode == 'keep':
367 elif self.gitmode == 'keep':
364 pass
368 pass
365 elif self.gitmode in ('yes', 'no'):
369 elif self.gitmode in ('yes', 'no'):
366 diffopts.git = self.gitmode == 'yes'
370 diffopts.git = self.gitmode == 'yes'
367 else:
371 else:
368 raise util.Abort(_('mq.git option can be auto/keep/yes/no'
372 raise util.Abort(_('mq.git option can be auto/keep/yes/no'
369 ' got %s') % self.gitmode)
373 ' got %s') % self.gitmode)
370 if patchfn:
374 if patchfn:
371 diffopts = self.patchopts(diffopts, patchfn)
375 diffopts = self.patchopts(diffopts, patchfn)
372 return diffopts
376 return diffopts
373
377
374 def patchopts(self, diffopts, *patches):
378 def patchopts(self, diffopts, *patches):
375 """Return a copy of input diff options with git set to true if
379 """Return a copy of input diff options with git set to true if
376 referenced patch is a git patch and should be preserved as such.
380 referenced patch is a git patch and should be preserved as such.
377 """
381 """
378 diffopts = diffopts.copy()
382 diffopts = diffopts.copy()
379 if not diffopts.git and self.gitmode == 'keep':
383 if not diffopts.git and self.gitmode == 'keep':
380 for patchfn in patches:
384 for patchfn in patches:
381 patchf = self.opener(patchfn, 'r')
385 patchf = self.opener(patchfn, 'r')
382 # if the patch was a git patch, refresh it as a git patch
386 # if the patch was a git patch, refresh it as a git patch
383 for line in patchf:
387 for line in patchf:
384 if line.startswith('diff --git'):
388 if line.startswith('diff --git'):
385 diffopts.git = True
389 diffopts.git = True
386 break
390 break
387 patchf.close()
391 patchf.close()
388 return diffopts
392 return diffopts
389
393
390 def join(self, *p):
394 def join(self, *p):
391 return os.path.join(self.path, *p)
395 return os.path.join(self.path, *p)
392
396
393 def findseries(self, patch):
397 def findseries(self, patch):
394 def matchpatch(l):
398 def matchpatch(l):
395 l = l.split('#', 1)[0]
399 l = l.split('#', 1)[0]
396 return l.strip() == patch
400 return l.strip() == patch
397 for index, l in enumerate(self.fullseries):
401 for index, l in enumerate(self.fullseries):
398 if matchpatch(l):
402 if matchpatch(l):
399 return index
403 return index
400 return None
404 return None
401
405
402 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
406 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
403
407
404 def parseseries(self):
408 def parseseries(self):
405 self.series = []
409 self.series = []
406 self.seriesguards = []
410 self.seriesguards = []
407 for l in self.fullseries:
411 for l in self.fullseries:
408 h = l.find('#')
412 h = l.find('#')
409 if h == -1:
413 if h == -1:
410 patch = l
414 patch = l
411 comment = ''
415 comment = ''
412 elif h == 0:
416 elif h == 0:
413 continue
417 continue
414 else:
418 else:
415 patch = l[:h]
419 patch = l[:h]
416 comment = l[h:]
420 comment = l[h:]
417 patch = patch.strip()
421 patch = patch.strip()
418 if patch:
422 if patch:
419 if patch in self.series:
423 if patch in self.series:
420 raise util.Abort(_('%s appears more than once in %s') %
424 raise util.Abort(_('%s appears more than once in %s') %
421 (patch, self.join(self.seriespath)))
425 (patch, self.join(self.seriespath)))
422 self.series.append(patch)
426 self.series.append(patch)
423 self.seriesguards.append(self.guard_re.findall(comment))
427 self.seriesguards.append(self.guard_re.findall(comment))
424
428
425 def checkguard(self, guard):
429 def checkguard(self, guard):
426 if not guard:
430 if not guard:
427 return _('guard cannot be an empty string')
431 return _('guard cannot be an empty string')
428 bad_chars = '# \t\r\n\f'
432 bad_chars = '# \t\r\n\f'
429 first = guard[0]
433 first = guard[0]
430 if first in '-+':
434 if first in '-+':
431 return (_('guard %r starts with invalid character: %r') %
435 return (_('guard %r starts with invalid character: %r') %
432 (guard, first))
436 (guard, first))
433 for c in bad_chars:
437 for c in bad_chars:
434 if c in guard:
438 if c in guard:
435 return _('invalid character in guard %r: %r') % (guard, c)
439 return _('invalid character in guard %r: %r') % (guard, c)
436
440
437 def setactive(self, guards):
441 def setactive(self, guards):
438 for guard in guards:
442 for guard in guards:
439 bad = self.checkguard(guard)
443 bad = self.checkguard(guard)
440 if bad:
444 if bad:
441 raise util.Abort(bad)
445 raise util.Abort(bad)
442 guards = sorted(set(guards))
446 guards = sorted(set(guards))
443 self.ui.debug('active guards: %s\n' % ' '.join(guards))
447 self.ui.debug('active guards: %s\n' % ' '.join(guards))
444 self.activeguards = guards
448 self.activeguards = guards
445 self.guardsdirty = True
449 self.guardsdirty = True
446
450
447 def active(self):
451 def active(self):
448 if self.activeguards is None:
452 if self.activeguards is None:
449 self.activeguards = []
453 self.activeguards = []
450 try:
454 try:
451 guards = self.opener.read(self.guardspath).split()
455 guards = self.opener.read(self.guardspath).split()
452 except IOError, err:
456 except IOError, err:
453 if err.errno != errno.ENOENT:
457 if err.errno != errno.ENOENT:
454 raise
458 raise
455 guards = []
459 guards = []
456 for i, guard in enumerate(guards):
460 for i, guard in enumerate(guards):
457 bad = self.checkguard(guard)
461 bad = self.checkguard(guard)
458 if bad:
462 if bad:
459 self.ui.warn('%s:%d: %s\n' %
463 self.ui.warn('%s:%d: %s\n' %
460 (self.join(self.guardspath), i + 1, bad))
464 (self.join(self.guardspath), i + 1, bad))
461 else:
465 else:
462 self.activeguards.append(guard)
466 self.activeguards.append(guard)
463 return self.activeguards
467 return self.activeguards
464
468
465 def setguards(self, idx, guards):
469 def setguards(self, idx, guards):
466 for g in guards:
470 for g in guards:
467 if len(g) < 2:
471 if len(g) < 2:
468 raise util.Abort(_('guard %r too short') % g)
472 raise util.Abort(_('guard %r too short') % g)
469 if g[0] not in '-+':
473 if g[0] not in '-+':
470 raise util.Abort(_('guard %r starts with invalid char') % g)
474 raise util.Abort(_('guard %r starts with invalid char') % g)
471 bad = self.checkguard(g[1:])
475 bad = self.checkguard(g[1:])
472 if bad:
476 if bad:
473 raise util.Abort(bad)
477 raise util.Abort(bad)
474 drop = self.guard_re.sub('', self.fullseries[idx])
478 drop = self.guard_re.sub('', self.fullseries[idx])
475 self.fullseries[idx] = drop + ''.join([' #' + g for g in guards])
479 self.fullseries[idx] = drop + ''.join([' #' + g for g in guards])
476 self.parseseries()
480 self.parseseries()
477 self.seriesdirty = True
481 self.seriesdirty = True
478
482
479 def pushable(self, idx):
483 def pushable(self, idx):
480 if isinstance(idx, str):
484 if isinstance(idx, str):
481 idx = self.series.index(idx)
485 idx = self.series.index(idx)
482 patchguards = self.seriesguards[idx]
486 patchguards = self.seriesguards[idx]
483 if not patchguards:
487 if not patchguards:
484 return True, None
488 return True, None
485 guards = self.active()
489 guards = self.active()
486 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
490 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
487 if exactneg:
491 if exactneg:
488 return False, repr(exactneg[0])
492 return False, repr(exactneg[0])
489 pos = [g for g in patchguards if g[0] == '+']
493 pos = [g for g in patchguards if g[0] == '+']
490 exactpos = [g for g in pos if g[1:] in guards]
494 exactpos = [g for g in pos if g[1:] in guards]
491 if pos:
495 if pos:
492 if exactpos:
496 if exactpos:
493 return True, repr(exactpos[0])
497 return True, repr(exactpos[0])
494 return False, ' '.join(map(repr, pos))
498 return False, ' '.join(map(repr, pos))
495 return True, ''
499 return True, ''
496
500
497 def explainpushable(self, idx, all_patches=False):
501 def explainpushable(self, idx, all_patches=False):
498 write = all_patches and self.ui.write or self.ui.warn
502 write = all_patches and self.ui.write or self.ui.warn
499 if all_patches or self.ui.verbose:
503 if all_patches or self.ui.verbose:
500 if isinstance(idx, str):
504 if isinstance(idx, str):
501 idx = self.series.index(idx)
505 idx = self.series.index(idx)
502 pushable, why = self.pushable(idx)
506 pushable, why = self.pushable(idx)
503 if all_patches and pushable:
507 if all_patches and pushable:
504 if why is None:
508 if why is None:
505 write(_('allowing %s - no guards in effect\n') %
509 write(_('allowing %s - no guards in effect\n') %
506 self.series[idx])
510 self.series[idx])
507 else:
511 else:
508 if not why:
512 if not why:
509 write(_('allowing %s - no matching negative guards\n') %
513 write(_('allowing %s - no matching negative guards\n') %
510 self.series[idx])
514 self.series[idx])
511 else:
515 else:
512 write(_('allowing %s - guarded by %s\n') %
516 write(_('allowing %s - guarded by %s\n') %
513 (self.series[idx], why))
517 (self.series[idx], why))
514 if not pushable:
518 if not pushable:
515 if why:
519 if why:
516 write(_('skipping %s - guarded by %s\n') %
520 write(_('skipping %s - guarded by %s\n') %
517 (self.series[idx], why))
521 (self.series[idx], why))
518 else:
522 else:
519 write(_('skipping %s - no matching guards\n') %
523 write(_('skipping %s - no matching guards\n') %
520 self.series[idx])
524 self.series[idx])
521
525
522 def savedirty(self):
526 def savedirty(self):
523 def writelist(items, path):
527 def writelist(items, path):
524 fp = self.opener(path, 'w')
528 fp = self.opener(path, 'w')
525 for i in items:
529 for i in items:
526 fp.write("%s\n" % i)
530 fp.write("%s\n" % i)
527 fp.close()
531 fp.close()
528 if self.applieddirty:
532 if self.applieddirty:
529 writelist(map(str, self.applied), self.statuspath)
533 writelist(map(str, self.applied), self.statuspath)
530 self.applieddirty = False
534 self.applieddirty = False
531 if self.seriesdirty:
535 if self.seriesdirty:
532 writelist(self.fullseries, self.seriespath)
536 writelist(self.fullseries, self.seriespath)
533 self.seriesdirty = False
537 self.seriesdirty = False
534 if self.guardsdirty:
538 if self.guardsdirty:
535 writelist(self.activeguards, self.guardspath)
539 writelist(self.activeguards, self.guardspath)
536 self.guardsdirty = False
540 self.guardsdirty = False
537 if self.added:
541 if self.added:
538 qrepo = self.qrepo()
542 qrepo = self.qrepo()
539 if qrepo:
543 if qrepo:
540 qrepo[None].add(f for f in self.added if f not in qrepo[None])
544 qrepo[None].add(f for f in self.added if f not in qrepo[None])
541 self.added = []
545 self.added = []
542
546
543 def removeundo(self, repo):
547 def removeundo(self, repo):
544 undo = repo.sjoin('undo')
548 undo = repo.sjoin('undo')
545 if not os.path.exists(undo):
549 if not os.path.exists(undo):
546 return
550 return
547 try:
551 try:
548 os.unlink(undo)
552 os.unlink(undo)
549 except OSError, inst:
553 except OSError, inst:
550 self.ui.warn(_('error removing undo: %s\n') % str(inst))
554 self.ui.warn(_('error removing undo: %s\n') % str(inst))
551
555
552 def printdiff(self, repo, diffopts, node1, node2=None, files=None,
556 def printdiff(self, repo, diffopts, node1, node2=None, files=None,
553 fp=None, changes=None, opts={}):
557 fp=None, changes=None, opts={}):
554 stat = opts.get('stat')
558 stat = opts.get('stat')
555 m = scmutil.match(repo[node1], files, opts)
559 m = scmutil.match(repo[node1], files, opts)
556 cmdutil.diffordiffstat(self.ui, repo, diffopts, node1, node2, m,
560 cmdutil.diffordiffstat(self.ui, repo, diffopts, node1, node2, m,
557 changes, stat, fp)
561 changes, stat, fp)
558
562
559 def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
563 def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
560 # first try just applying the patch
564 # first try just applying the patch
561 (err, n) = self.apply(repo, [patch], update_status=False,
565 (err, n) = self.apply(repo, [patch], update_status=False,
562 strict=True, merge=rev)
566 strict=True, merge=rev)
563
567
564 if err == 0:
568 if err == 0:
565 return (err, n)
569 return (err, n)
566
570
567 if n is None:
571 if n is None:
568 raise util.Abort(_("apply failed for patch %s") % patch)
572 raise util.Abort(_("apply failed for patch %s") % patch)
569
573
570 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
574 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
571
575
572 # apply failed, strip away that rev and merge.
576 # apply failed, strip away that rev and merge.
573 hg.clean(repo, head)
577 hg.clean(repo, head)
574 self.strip(repo, [n], update=False, backup='strip')
578 self.strip(repo, [n], update=False, backup='strip')
575
579
576 ctx = repo[rev]
580 ctx = repo[rev]
577 ret = hg.merge(repo, rev)
581 ret = hg.merge(repo, rev)
578 if ret:
582 if ret:
579 raise util.Abort(_("update returned %d") % ret)
583 raise util.Abort(_("update returned %d") % ret)
580 n = secretcommit(repo, None, ctx.description(), ctx.user(), force=True)
584 n = secretcommit(repo, None, ctx.description(), ctx.user(), force=True)
581 if n is None:
585 if n is None:
582 raise util.Abort(_("repo commit failed"))
586 raise util.Abort(_("repo commit failed"))
583 try:
587 try:
584 ph = patchheader(mergeq.join(patch), self.plainmode)
588 ph = patchheader(mergeq.join(patch), self.plainmode)
585 except:
589 except:
586 raise util.Abort(_("unable to read %s") % patch)
590 raise util.Abort(_("unable to read %s") % patch)
587
591
588 diffopts = self.patchopts(diffopts, patch)
592 diffopts = self.patchopts(diffopts, patch)
589 patchf = self.opener(patch, "w")
593 patchf = self.opener(patch, "w")
590 comments = str(ph)
594 comments = str(ph)
591 if comments:
595 if comments:
592 patchf.write(comments)
596 patchf.write(comments)
593 self.printdiff(repo, diffopts, head, n, fp=patchf)
597 self.printdiff(repo, diffopts, head, n, fp=patchf)
594 patchf.close()
598 patchf.close()
595 self.removeundo(repo)
599 self.removeundo(repo)
596 return (0, n)
600 return (0, n)
597
601
598 def qparents(self, repo, rev=None):
602 def qparents(self, repo, rev=None):
599 if rev is None:
603 if rev is None:
600 (p1, p2) = repo.dirstate.parents()
604 (p1, p2) = repo.dirstate.parents()
601 if p2 == nullid:
605 if p2 == nullid:
602 return p1
606 return p1
603 if not self.applied:
607 if not self.applied:
604 return None
608 return None
605 return self.applied[-1].node
609 return self.applied[-1].node
606 p1, p2 = repo.changelog.parents(rev)
610 p1, p2 = repo.changelog.parents(rev)
607 if p2 != nullid and p2 in [x.node for x in self.applied]:
611 if p2 != nullid and p2 in [x.node for x in self.applied]:
608 return p2
612 return p2
609 return p1
613 return p1
610
614
611 def mergepatch(self, repo, mergeq, series, diffopts):
615 def mergepatch(self, repo, mergeq, series, diffopts):
612 if not self.applied:
616 if not self.applied:
613 # each of the patches merged in will have two parents. This
617 # each of the patches merged in will have two parents. This
614 # can confuse the qrefresh, qdiff, and strip code because it
618 # can confuse the qrefresh, qdiff, and strip code because it
615 # needs to know which parent is actually in the patch queue.
619 # needs to know which parent is actually in the patch queue.
616 # so, we insert a merge marker with only one parent. This way
620 # so, we insert a merge marker with only one parent. This way
617 # the first patch in the queue is never a merge patch
621 # the first patch in the queue is never a merge patch
618 #
622 #
619 pname = ".hg.patches.merge.marker"
623 pname = ".hg.patches.merge.marker"
620 n = secretcommit(repo, None, '[mq]: merge marker', force=True)
624 n = secretcommit(repo, None, '[mq]: merge marker', force=True)
621 self.removeundo(repo)
625 self.removeundo(repo)
622 self.applied.append(statusentry(n, pname))
626 self.applied.append(statusentry(n, pname))
623 self.applieddirty = True
627 self.applieddirty = True
624
628
625 head = self.qparents(repo)
629 head = self.qparents(repo)
626
630
627 for patch in series:
631 for patch in series:
628 patch = mergeq.lookup(patch, strict=True)
632 patch = mergeq.lookup(patch, strict=True)
629 if not patch:
633 if not patch:
630 self.ui.warn(_("patch %s does not exist\n") % patch)
634 self.ui.warn(_("patch %s does not exist\n") % patch)
631 return (1, None)
635 return (1, None)
632 pushable, reason = self.pushable(patch)
636 pushable, reason = self.pushable(patch)
633 if not pushable:
637 if not pushable:
634 self.explainpushable(patch, all_patches=True)
638 self.explainpushable(patch, all_patches=True)
635 continue
639 continue
636 info = mergeq.isapplied(patch)
640 info = mergeq.isapplied(patch)
637 if not info:
641 if not info:
638 self.ui.warn(_("patch %s is not applied\n") % patch)
642 self.ui.warn(_("patch %s is not applied\n") % patch)
639 return (1, None)
643 return (1, None)
640 rev = info[1]
644 rev = info[1]
641 err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
645 err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
642 if head:
646 if head:
643 self.applied.append(statusentry(head, patch))
647 self.applied.append(statusentry(head, patch))
644 self.applieddirty = True
648 self.applieddirty = True
645 if err:
649 if err:
646 return (err, head)
650 return (err, head)
647 self.savedirty()
651 self.savedirty()
648 return (0, head)
652 return (0, head)
649
653
650 def patch(self, repo, patchfile):
654 def patch(self, repo, patchfile):
651 '''Apply patchfile to the working directory.
655 '''Apply patchfile to the working directory.
652 patchfile: name of patch file'''
656 patchfile: name of patch file'''
653 files = set()
657 files = set()
654 try:
658 try:
655 fuzz = patchmod.patch(self.ui, repo, patchfile, strip=1,
659 fuzz = patchmod.patch(self.ui, repo, patchfile, strip=1,
656 files=files, eolmode=None)
660 files=files, eolmode=None)
657 return (True, list(files), fuzz)
661 return (True, list(files), fuzz)
658 except Exception, inst:
662 except Exception, inst:
659 self.ui.note(str(inst) + '\n')
663 self.ui.note(str(inst) + '\n')
660 if not self.ui.verbose:
664 if not self.ui.verbose:
661 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
665 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
662 self.ui.traceback()
666 self.ui.traceback()
663 return (False, list(files), False)
667 return (False, list(files), False)
664
668
665 def apply(self, repo, series, list=False, update_status=True,
669 def apply(self, repo, series, list=False, update_status=True,
666 strict=False, patchdir=None, merge=None, all_files=None):
670 strict=False, patchdir=None, merge=None, all_files=None):
667 wlock = lock = tr = None
671 wlock = lock = tr = None
668 try:
672 try:
669 wlock = repo.wlock()
673 wlock = repo.wlock()
670 lock = repo.lock()
674 lock = repo.lock()
671 tr = repo.transaction("qpush")
675 tr = repo.transaction("qpush")
672 try:
676 try:
673 ret = self._apply(repo, series, list, update_status,
677 ret = self._apply(repo, series, list, update_status,
674 strict, patchdir, merge, all_files=all_files)
678 strict, patchdir, merge, all_files=all_files)
675 tr.close()
679 tr.close()
676 self.savedirty()
680 self.savedirty()
677 return ret
681 return ret
678 except:
682 except:
679 try:
683 try:
680 tr.abort()
684 tr.abort()
681 finally:
685 finally:
682 repo.invalidate()
686 repo.invalidate()
683 repo.dirstate.invalidate()
687 repo.dirstate.invalidate()
684 self.invalidate()
688 self.invalidate()
685 raise
689 raise
686 finally:
690 finally:
687 release(tr, lock, wlock)
691 release(tr, lock, wlock)
688 self.removeundo(repo)
692 self.removeundo(repo)
689
693
690 def _apply(self, repo, series, list=False, update_status=True,
694 def _apply(self, repo, series, list=False, update_status=True,
691 strict=False, patchdir=None, merge=None, all_files=None):
695 strict=False, patchdir=None, merge=None, all_files=None):
692 '''returns (error, hash)
696 '''returns (error, hash)
693 error = 1 for unable to read, 2 for patch failed, 3 for patch fuzz'''
697 error = 1 for unable to read, 2 for patch failed, 3 for patch fuzz'''
694 # TODO unify with commands.py
698 # TODO unify with commands.py
695 if not patchdir:
699 if not patchdir:
696 patchdir = self.path
700 patchdir = self.path
697 err = 0
701 err = 0
698 n = None
702 n = None
699 for patchname in series:
703 for patchname in series:
700 pushable, reason = self.pushable(patchname)
704 pushable, reason = self.pushable(patchname)
701 if not pushable:
705 if not pushable:
702 self.explainpushable(patchname, all_patches=True)
706 self.explainpushable(patchname, all_patches=True)
703 continue
707 continue
704 self.ui.status(_("applying %s\n") % patchname)
708 self.ui.status(_("applying %s\n") % patchname)
705 pf = os.path.join(patchdir, patchname)
709 pf = os.path.join(patchdir, patchname)
706
710
707 try:
711 try:
708 ph = patchheader(self.join(patchname), self.plainmode)
712 ph = patchheader(self.join(patchname), self.plainmode)
709 except IOError:
713 except IOError:
710 self.ui.warn(_("unable to read %s\n") % patchname)
714 self.ui.warn(_("unable to read %s\n") % patchname)
711 err = 1
715 err = 1
712 break
716 break
713
717
714 message = ph.message
718 message = ph.message
715 if not message:
719 if not message:
716 # The commit message should not be translated
720 # The commit message should not be translated
717 message = "imported patch %s\n" % patchname
721 message = "imported patch %s\n" % patchname
718 else:
722 else:
719 if list:
723 if list:
720 # The commit message should not be translated
724 # The commit message should not be translated
721 message.append("\nimported patch %s" % patchname)
725 message.append("\nimported patch %s" % patchname)
722 message = '\n'.join(message)
726 message = '\n'.join(message)
723
727
724 if ph.haspatch:
728 if ph.haspatch:
725 (patcherr, files, fuzz) = self.patch(repo, pf)
729 (patcherr, files, fuzz) = self.patch(repo, pf)
726 if all_files is not None:
730 if all_files is not None:
727 all_files.update(files)
731 all_files.update(files)
728 patcherr = not patcherr
732 patcherr = not patcherr
729 else:
733 else:
730 self.ui.warn(_("patch %s is empty\n") % patchname)
734 self.ui.warn(_("patch %s is empty\n") % patchname)
731 patcherr, files, fuzz = 0, [], 0
735 patcherr, files, fuzz = 0, [], 0
732
736
733 if merge and files:
737 if merge and files:
734 # Mark as removed/merged and update dirstate parent info
738 # Mark as removed/merged and update dirstate parent info
735 removed = []
739 removed = []
736 merged = []
740 merged = []
737 for f in files:
741 for f in files:
738 if os.path.lexists(repo.wjoin(f)):
742 if os.path.lexists(repo.wjoin(f)):
739 merged.append(f)
743 merged.append(f)
740 else:
744 else:
741 removed.append(f)
745 removed.append(f)
742 for f in removed:
746 for f in removed:
743 repo.dirstate.remove(f)
747 repo.dirstate.remove(f)
744 for f in merged:
748 for f in merged:
745 repo.dirstate.merge(f)
749 repo.dirstate.merge(f)
746 p1, p2 = repo.dirstate.parents()
750 p1, p2 = repo.dirstate.parents()
747 repo.dirstate.setparents(p1, merge)
751 repo.dirstate.setparents(p1, merge)
748
752
749 match = scmutil.matchfiles(repo, files or [])
753 match = scmutil.matchfiles(repo, files or [])
750 oldtip = repo['tip']
754 oldtip = repo['tip']
751 n = secretcommit(repo, None, message, ph.user, ph.date, match=match,
755 n = secretcommit(repo, None, message, ph.user, ph.date, match=match,
752 force=True)
756 force=True)
753 if repo['tip'] == oldtip:
757 if repo['tip'] == oldtip:
754 raise util.Abort(_("qpush exactly duplicates child changeset"))
758 raise util.Abort(_("qpush exactly duplicates child changeset"))
755 if n is None:
759 if n is None:
756 raise util.Abort(_("repository commit failed"))
760 raise util.Abort(_("repository commit failed"))
757
761
758 if update_status:
762 if update_status:
759 self.applied.append(statusentry(n, patchname))
763 self.applied.append(statusentry(n, patchname))
760
764
761 if patcherr:
765 if patcherr:
762 self.ui.warn(_("patch failed, rejects left in working dir\n"))
766 self.ui.warn(_("patch failed, rejects left in working dir\n"))
763 err = 2
767 err = 2
764 break
768 break
765
769
766 if fuzz and strict:
770 if fuzz and strict:
767 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
771 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
768 err = 3
772 err = 3
769 break
773 break
770 return (err, n)
774 return (err, n)
771
775
772 def _cleanup(self, patches, numrevs, keep=False):
776 def _cleanup(self, patches, numrevs, keep=False):
773 if not keep:
777 if not keep:
774 r = self.qrepo()
778 r = self.qrepo()
775 if r:
779 if r:
776 r[None].forget(patches)
780 r[None].forget(patches)
777 for p in patches:
781 for p in patches:
778 os.unlink(self.join(p))
782 os.unlink(self.join(p))
779
783
780 qfinished = []
784 qfinished = []
781 if numrevs:
785 if numrevs:
782 qfinished = self.applied[:numrevs]
786 qfinished = self.applied[:numrevs]
783 del self.applied[:numrevs]
787 del self.applied[:numrevs]
784 self.applieddirty = True
788 self.applieddirty = True
785
789
786 unknown = []
790 unknown = []
787
791
788 for (i, p) in sorted([(self.findseries(p), p) for p in patches],
792 for (i, p) in sorted([(self.findseries(p), p) for p in patches],
789 reverse=True):
793 reverse=True):
790 if i is not None:
794 if i is not None:
791 del self.fullseries[i]
795 del self.fullseries[i]
792 else:
796 else:
793 unknown.append(p)
797 unknown.append(p)
794
798
795 if unknown:
799 if unknown:
796 if numrevs:
800 if numrevs:
797 rev = dict((entry.name, entry.node) for entry in qfinished)
801 rev = dict((entry.name, entry.node) for entry in qfinished)
798 for p in unknown:
802 for p in unknown:
799 msg = _('revision %s refers to unknown patches: %s\n')
803 msg = _('revision %s refers to unknown patches: %s\n')
800 self.ui.warn(msg % (short(rev[p]), p))
804 self.ui.warn(msg % (short(rev[p]), p))
801 else:
805 else:
802 msg = _('unknown patches: %s\n')
806 msg = _('unknown patches: %s\n')
803 raise util.Abort(''.join(msg % p for p in unknown))
807 raise util.Abort(''.join(msg % p for p in unknown))
804
808
805 self.parseseries()
809 self.parseseries()
806 self.seriesdirty = True
810 self.seriesdirty = True
807 return [entry.node for entry in qfinished]
811 return [entry.node for entry in qfinished]
808
812
809 def _revpatches(self, repo, revs):
813 def _revpatches(self, repo, revs):
810 firstrev = repo[self.applied[0].node].rev()
814 firstrev = repo[self.applied[0].node].rev()
811 patches = []
815 patches = []
812 for i, rev in enumerate(revs):
816 for i, rev in enumerate(revs):
813
817
814 if rev < firstrev:
818 if rev < firstrev:
815 raise util.Abort(_('revision %d is not managed') % rev)
819 raise util.Abort(_('revision %d is not managed') % rev)
816
820
817 ctx = repo[rev]
821 ctx = repo[rev]
818 base = self.applied[i].node
822 base = self.applied[i].node
819 if ctx.node() != base:
823 if ctx.node() != base:
820 msg = _('cannot delete revision %d above applied patches')
824 msg = _('cannot delete revision %d above applied patches')
821 raise util.Abort(msg % rev)
825 raise util.Abort(msg % rev)
822
826
823 patch = self.applied[i].name
827 patch = self.applied[i].name
824 for fmt in ('[mq]: %s', 'imported patch %s'):
828 for fmt in ('[mq]: %s', 'imported patch %s'):
825 if ctx.description() == fmt % patch:
829 if ctx.description() == fmt % patch:
826 msg = _('patch %s finalized without changeset message\n')
830 msg = _('patch %s finalized without changeset message\n')
827 repo.ui.status(msg % patch)
831 repo.ui.status(msg % patch)
828 break
832 break
829
833
830 patches.append(patch)
834 patches.append(patch)
831 return patches
835 return patches
832
836
833 def finish(self, repo, revs):
837 def finish(self, repo, revs):
834 # Manually trigger phase computation to ensure phasedefaults is
838 # Manually trigger phase computation to ensure phasedefaults is
835 # executed before we remove the patches.
839 # executed before we remove the patches.
836 repo._phaserev
840 repo._phaserev
837 patches = self._revpatches(repo, sorted(revs))
841 patches = self._revpatches(repo, sorted(revs))
838 qfinished = self._cleanup(patches, len(patches))
842 qfinished = self._cleanup(patches, len(patches))
839 if qfinished and repo.ui.configbool('mq', 'secret', False):
843 if qfinished and repo.ui.configbool('mq', 'secret', False):
840 # only use this logic when the secret option is added
844 # only use this logic when the secret option is added
841 oldqbase = repo[qfinished[0]]
845 oldqbase = repo[qfinished[0]]
842 if oldqbase.p1().phase() < phases.secret:
846 if oldqbase.p1().phase() < phases.secret:
843 phases.advanceboundary(repo, phases.draft, qfinished)
847 phases.advanceboundary(repo, phases.draft, qfinished)
844
848
845 def delete(self, repo, patches, opts):
849 def delete(self, repo, patches, opts):
846 if not patches and not opts.get('rev'):
850 if not patches and not opts.get('rev'):
847 raise util.Abort(_('qdelete requires at least one revision or '
851 raise util.Abort(_('qdelete requires at least one revision or '
848 'patch name'))
852 'patch name'))
849
853
850 realpatches = []
854 realpatches = []
851 for patch in patches:
855 for patch in patches:
852 patch = self.lookup(patch, strict=True)
856 patch = self.lookup(patch, strict=True)
853 info = self.isapplied(patch)
857 info = self.isapplied(patch)
854 if info:
858 if info:
855 raise util.Abort(_("cannot delete applied patch %s") % patch)
859 raise util.Abort(_("cannot delete applied patch %s") % patch)
856 if patch not in self.series:
860 if patch not in self.series:
857 raise util.Abort(_("patch %s not in series file") % patch)
861 raise util.Abort(_("patch %s not in series file") % patch)
858 if patch not in realpatches:
862 if patch not in realpatches:
859 realpatches.append(patch)
863 realpatches.append(patch)
860
864
861 numrevs = 0
865 numrevs = 0
862 if opts.get('rev'):
866 if opts.get('rev'):
863 if not self.applied:
867 if not self.applied:
864 raise util.Abort(_('no patches applied'))
868 raise util.Abort(_('no patches applied'))
865 revs = scmutil.revrange(repo, opts.get('rev'))
869 revs = scmutil.revrange(repo, opts.get('rev'))
866 if len(revs) > 1 and revs[0] > revs[1]:
870 if len(revs) > 1 and revs[0] > revs[1]:
867 revs.reverse()
871 revs.reverse()
868 revpatches = self._revpatches(repo, revs)
872 revpatches = self._revpatches(repo, revs)
869 realpatches += revpatches
873 realpatches += revpatches
870 numrevs = len(revpatches)
874 numrevs = len(revpatches)
871
875
872 self._cleanup(realpatches, numrevs, opts.get('keep'))
876 self._cleanup(realpatches, numrevs, opts.get('keep'))
873
877
874 def checktoppatch(self, repo):
878 def checktoppatch(self, repo):
875 if self.applied:
879 if self.applied:
876 top = self.applied[-1].node
880 top = self.applied[-1].node
877 patch = self.applied[-1].name
881 patch = self.applied[-1].name
878 pp = repo.dirstate.parents()
882 pp = repo.dirstate.parents()
879 if top not in pp:
883 if top not in pp:
880 raise util.Abort(_("working directory revision is not qtip"))
884 raise util.Abort(_("working directory revision is not qtip"))
881 return top, patch
885 return top, patch
882 return None, None
886 return None, None
883
887
884 def checksubstate(self, repo):
888 def checksubstate(self, repo):
885 '''return list of subrepos at a different revision than substate.
889 '''return list of subrepos at a different revision than substate.
886 Abort if any subrepos have uncommitted changes.'''
890 Abort if any subrepos have uncommitted changes.'''
887 inclsubs = []
891 inclsubs = []
888 wctx = repo[None]
892 wctx = repo[None]
889 for s in wctx.substate:
893 for s in wctx.substate:
890 if wctx.sub(s).dirty(True):
894 if wctx.sub(s).dirty(True):
891 raise util.Abort(
895 raise util.Abort(
892 _("uncommitted changes in subrepository %s") % s)
896 _("uncommitted changes in subrepository %s") % s)
893 elif wctx.sub(s).dirty():
897 elif wctx.sub(s).dirty():
894 inclsubs.append(s)
898 inclsubs.append(s)
895 return inclsubs
899 return inclsubs
896
900
897 def localchangesfound(self, refresh=True):
901 def localchangesfound(self, refresh=True):
898 if refresh:
902 if refresh:
899 raise util.Abort(_("local changes found, refresh first"))
903 raise util.Abort(_("local changes found, refresh first"))
900 else:
904 else:
901 raise util.Abort(_("local changes found"))
905 raise util.Abort(_("local changes found"))
902
906
903 def checklocalchanges(self, repo, force=False, refresh=True):
907 def checklocalchanges(self, repo, force=False, refresh=True):
904 m, a, r, d = repo.status()[:4]
908 m, a, r, d = repo.status()[:4]
905 if (m or a or r or d) and not force:
909 if (m or a or r or d) and not force:
906 self.localchangesfound(refresh)
910 self.localchangesfound(refresh)
907 return m, a, r, d
911 return m, a, r, d
908
912
909 _reserved = ('series', 'status', 'guards', '.', '..')
913 _reserved = ('series', 'status', 'guards', '.', '..')
910 def checkreservedname(self, name):
914 def checkreservedname(self, name):
911 if name in self._reserved:
915 if name in self._reserved:
912 raise util.Abort(_('"%s" cannot be used as the name of a patch')
916 raise util.Abort(_('"%s" cannot be used as the name of a patch')
913 % name)
917 % name)
914 for prefix in ('.hg', '.mq'):
918 for prefix in ('.hg', '.mq'):
915 if name.startswith(prefix):
919 if name.startswith(prefix):
916 raise util.Abort(_('patch name cannot begin with "%s"')
920 raise util.Abort(_('patch name cannot begin with "%s"')
917 % prefix)
921 % prefix)
918 for c in ('#', ':'):
922 for c in ('#', ':'):
919 if c in name:
923 if c in name:
920 raise util.Abort(_('"%s" cannot be used in the name of a patch')
924 raise util.Abort(_('"%s" cannot be used in the name of a patch')
921 % c)
925 % c)
922
926
923 def checkpatchname(self, name, force=False):
927 def checkpatchname(self, name, force=False):
924 self.checkreservedname(name)
928 self.checkreservedname(name)
925 if not force and os.path.exists(self.join(name)):
929 if not force and os.path.exists(self.join(name)):
926 if os.path.isdir(self.join(name)):
930 if os.path.isdir(self.join(name)):
927 raise util.Abort(_('"%s" already exists as a directory')
931 raise util.Abort(_('"%s" already exists as a directory')
928 % name)
932 % name)
929 else:
933 else:
930 raise util.Abort(_('patch "%s" already exists') % name)
934 raise util.Abort(_('patch "%s" already exists') % name)
931
935
932 def new(self, repo, patchfn, *pats, **opts):
936 def new(self, repo, patchfn, *pats, **opts):
933 """options:
937 """options:
934 msg: a string or a no-argument function returning a string
938 msg: a string or a no-argument function returning a string
935 """
939 """
936 msg = opts.get('msg')
940 msg = opts.get('msg')
937 user = opts.get('user')
941 user = opts.get('user')
938 date = opts.get('date')
942 date = opts.get('date')
939 if date:
943 if date:
940 date = util.parsedate(date)
944 date = util.parsedate(date)
941 diffopts = self.diffopts({'git': opts.get('git')})
945 diffopts = self.diffopts({'git': opts.get('git')})
942 if opts.get('checkname', True):
946 if opts.get('checkname', True):
943 self.checkpatchname(patchfn)
947 self.checkpatchname(patchfn)
944 inclsubs = self.checksubstate(repo)
948 inclsubs = self.checksubstate(repo)
945 if inclsubs:
949 if inclsubs:
946 inclsubs.append('.hgsubstate')
950 inclsubs.append('.hgsubstate')
947 if opts.get('include') or opts.get('exclude') or pats:
951 if opts.get('include') or opts.get('exclude') or pats:
948 if inclsubs:
952 if inclsubs:
949 pats = list(pats or []) + inclsubs
953 pats = list(pats or []) + inclsubs
950 match = scmutil.match(repo[None], pats, opts)
954 match = scmutil.match(repo[None], pats, opts)
951 # detect missing files in pats
955 # detect missing files in pats
952 def badfn(f, msg):
956 def badfn(f, msg):
953 if f != '.hgsubstate': # .hgsubstate is auto-created
957 if f != '.hgsubstate': # .hgsubstate is auto-created
954 raise util.Abort('%s: %s' % (f, msg))
958 raise util.Abort('%s: %s' % (f, msg))
955 match.bad = badfn
959 match.bad = badfn
956 m, a, r, d = repo.status(match=match)[:4]
960 m, a, r, d = repo.status(match=match)[:4]
957 else:
961 else:
958 m, a, r, d = self.checklocalchanges(repo, force=True)
962 m, a, r, d = self.checklocalchanges(repo, force=True)
959 match = scmutil.matchfiles(repo, m + a + r + inclsubs)
963 match = scmutil.matchfiles(repo, m + a + r + inclsubs)
960 if len(repo[None].parents()) > 1:
964 if len(repo[None].parents()) > 1:
961 raise util.Abort(_('cannot manage merge changesets'))
965 raise util.Abort(_('cannot manage merge changesets'))
962 commitfiles = m + a + r
966 commitfiles = m + a + r
963 self.checktoppatch(repo)
967 self.checktoppatch(repo)
964 insert = self.fullseriesend()
968 insert = self.fullseriesend()
965 wlock = repo.wlock()
969 wlock = repo.wlock()
966 try:
970 try:
967 try:
971 try:
968 # if patch file write fails, abort early
972 # if patch file write fails, abort early
969 p = self.opener(patchfn, "w")
973 p = self.opener(patchfn, "w")
970 except IOError, e:
974 except IOError, e:
971 raise util.Abort(_('cannot write patch "%s": %s')
975 raise util.Abort(_('cannot write patch "%s": %s')
972 % (patchfn, e.strerror))
976 % (patchfn, e.strerror))
973 try:
977 try:
974 if self.plainmode:
978 if self.plainmode:
975 if user:
979 if user:
976 p.write("From: " + user + "\n")
980 p.write("From: " + user + "\n")
977 if not date:
981 if not date:
978 p.write("\n")
982 p.write("\n")
979 if date:
983 if date:
980 p.write("Date: %d %d\n\n" % date)
984 p.write("Date: %d %d\n\n" % date)
981 else:
985 else:
982 p.write("# HG changeset patch\n")
986 p.write("# HG changeset patch\n")
983 p.write("# Parent "
987 p.write("# Parent "
984 + hex(repo[None].p1().node()) + "\n")
988 + hex(repo[None].p1().node()) + "\n")
985 if user:
989 if user:
986 p.write("# User " + user + "\n")
990 p.write("# User " + user + "\n")
987 if date:
991 if date:
988 p.write("# Date %s %s\n\n" % date)
992 p.write("# Date %s %s\n\n" % date)
989 if util.safehasattr(msg, '__call__'):
993 if util.safehasattr(msg, '__call__'):
990 msg = msg()
994 msg = msg()
991 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
995 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
992 n = secretcommit(repo, None, commitmsg, user, date, match=match,
996 n = secretcommit(repo, None, commitmsg, user, date, match=match,
993 force=True)
997 force=True)
994 if n is None:
998 if n is None:
995 raise util.Abort(_("repo commit failed"))
999 raise util.Abort(_("repo commit failed"))
996 try:
1000 try:
997 self.fullseries[insert:insert] = [patchfn]
1001 self.fullseries[insert:insert] = [patchfn]
998 self.applied.append(statusentry(n, patchfn))
1002 self.applied.append(statusentry(n, patchfn))
999 self.parseseries()
1003 self.parseseries()
1000 self.seriesdirty = True
1004 self.seriesdirty = True
1001 self.applieddirty = True
1005 self.applieddirty = True
1002 if msg:
1006 if msg:
1003 msg = msg + "\n\n"
1007 msg = msg + "\n\n"
1004 p.write(msg)
1008 p.write(msg)
1005 if commitfiles:
1009 if commitfiles:
1006 parent = self.qparents(repo, n)
1010 parent = self.qparents(repo, n)
1007 chunks = patchmod.diff(repo, node1=parent, node2=n,
1011 chunks = patchmod.diff(repo, node1=parent, node2=n,
1008 match=match, opts=diffopts)
1012 match=match, opts=diffopts)
1009 for chunk in chunks:
1013 for chunk in chunks:
1010 p.write(chunk)
1014 p.write(chunk)
1011 p.close()
1015 p.close()
1012 r = self.qrepo()
1016 r = self.qrepo()
1013 if r:
1017 if r:
1014 r[None].add([patchfn])
1018 r[None].add([patchfn])
1015 except:
1019 except:
1016 repo.rollback()
1020 repo.rollback()
1017 raise
1021 raise
1018 except Exception:
1022 except Exception:
1019 patchpath = self.join(patchfn)
1023 patchpath = self.join(patchfn)
1020 try:
1024 try:
1021 os.unlink(patchpath)
1025 os.unlink(patchpath)
1022 except:
1026 except:
1023 self.ui.warn(_('error unlinking %s\n') % patchpath)
1027 self.ui.warn(_('error unlinking %s\n') % patchpath)
1024 raise
1028 raise
1025 self.removeundo(repo)
1029 self.removeundo(repo)
1026 finally:
1030 finally:
1027 release(wlock)
1031 release(wlock)
1028
1032
1029 def strip(self, repo, revs, update=True, backup="all", force=None):
1033 def strip(self, repo, revs, update=True, backup="all", force=None):
1030 wlock = lock = None
1034 wlock = lock = None
1031 try:
1035 try:
1032 wlock = repo.wlock()
1036 wlock = repo.wlock()
1033 lock = repo.lock()
1037 lock = repo.lock()
1034
1038
1035 if update:
1039 if update:
1036 self.checklocalchanges(repo, force=force, refresh=False)
1040 self.checklocalchanges(repo, force=force, refresh=False)
1037 urev = self.qparents(repo, revs[0])
1041 urev = self.qparents(repo, revs[0])
1038 hg.clean(repo, urev)
1042 hg.clean(repo, urev)
1039 repo.dirstate.write()
1043 repo.dirstate.write()
1040
1044
1041 self.removeundo(repo)
1045 self.removeundo(repo)
1042 for rev in revs:
1046 for rev in revs:
1043 repair.strip(self.ui, repo, rev, backup)
1047 repair.strip(self.ui, repo, rev, backup)
1044 # strip may have unbundled a set of backed up revisions after
1048 # strip may have unbundled a set of backed up revisions after
1045 # the actual strip
1049 # the actual strip
1046 self.removeundo(repo)
1050 self.removeundo(repo)
1047 finally:
1051 finally:
1048 release(lock, wlock)
1052 release(lock, wlock)
1049
1053
1050 def isapplied(self, patch):
1054 def isapplied(self, patch):
1051 """returns (index, rev, patch)"""
1055 """returns (index, rev, patch)"""
1052 for i, a in enumerate(self.applied):
1056 for i, a in enumerate(self.applied):
1053 if a.name == patch:
1057 if a.name == patch:
1054 return (i, a.node, a.name)
1058 return (i, a.node, a.name)
1055 return None
1059 return None
1056
1060
1057 # if the exact patch name does not exist, we try a few
1061 # if the exact patch name does not exist, we try a few
1058 # variations. If strict is passed, we try only #1
1062 # variations. If strict is passed, we try only #1
1059 #
1063 #
1060 # 1) a number (as string) to indicate an offset in the series file
1064 # 1) a number (as string) to indicate an offset in the series file
1061 # 2) a unique substring of the patch name was given
1065 # 2) a unique substring of the patch name was given
1062 # 3) patchname[-+]num to indicate an offset in the series file
1066 # 3) patchname[-+]num to indicate an offset in the series file
1063 def lookup(self, patch, strict=False):
1067 def lookup(self, patch, strict=False):
1064 def partialname(s):
1068 def partialname(s):
1065 if s in self.series:
1069 if s in self.series:
1066 return s
1070 return s
1067 matches = [x for x in self.series if s in x]
1071 matches = [x for x in self.series if s in x]
1068 if len(matches) > 1:
1072 if len(matches) > 1:
1069 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
1073 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
1070 for m in matches:
1074 for m in matches:
1071 self.ui.warn(' %s\n' % m)
1075 self.ui.warn(' %s\n' % m)
1072 return None
1076 return None
1073 if matches:
1077 if matches:
1074 return matches[0]
1078 return matches[0]
1075 if self.series and self.applied:
1079 if self.series and self.applied:
1076 if s == 'qtip':
1080 if s == 'qtip':
1077 return self.series[self.seriesend(True)-1]
1081 return self.series[self.seriesend(True)-1]
1078 if s == 'qbase':
1082 if s == 'qbase':
1079 return self.series[0]
1083 return self.series[0]
1080 return None
1084 return None
1081
1085
1082 if patch in self.series:
1086 if patch in self.series:
1083 return patch
1087 return patch
1084
1088
1085 if not os.path.isfile(self.join(patch)):
1089 if not os.path.isfile(self.join(patch)):
1086 try:
1090 try:
1087 sno = int(patch)
1091 sno = int(patch)
1088 except (ValueError, OverflowError):
1092 except (ValueError, OverflowError):
1089 pass
1093 pass
1090 else:
1094 else:
1091 if -len(self.series) <= sno < len(self.series):
1095 if -len(self.series) <= sno < len(self.series):
1092 return self.series[sno]
1096 return self.series[sno]
1093
1097
1094 if not strict:
1098 if not strict:
1095 res = partialname(patch)
1099 res = partialname(patch)
1096 if res:
1100 if res:
1097 return res
1101 return res
1098 minus = patch.rfind('-')
1102 minus = patch.rfind('-')
1099 if minus >= 0:
1103 if minus >= 0:
1100 res = partialname(patch[:minus])
1104 res = partialname(patch[:minus])
1101 if res:
1105 if res:
1102 i = self.series.index(res)
1106 i = self.series.index(res)
1103 try:
1107 try:
1104 off = int(patch[minus + 1:] or 1)
1108 off = int(patch[minus + 1:] or 1)
1105 except (ValueError, OverflowError):
1109 except (ValueError, OverflowError):
1106 pass
1110 pass
1107 else:
1111 else:
1108 if i - off >= 0:
1112 if i - off >= 0:
1109 return self.series[i - off]
1113 return self.series[i - off]
1110 plus = patch.rfind('+')
1114 plus = patch.rfind('+')
1111 if plus >= 0:
1115 if plus >= 0:
1112 res = partialname(patch[:plus])
1116 res = partialname(patch[:plus])
1113 if res:
1117 if res:
1114 i = self.series.index(res)
1118 i = self.series.index(res)
1115 try:
1119 try:
1116 off = int(patch[plus + 1:] or 1)
1120 off = int(patch[plus + 1:] or 1)
1117 except (ValueError, OverflowError):
1121 except (ValueError, OverflowError):
1118 pass
1122 pass
1119 else:
1123 else:
1120 if i + off < len(self.series):
1124 if i + off < len(self.series):
1121 return self.series[i + off]
1125 return self.series[i + off]
1122 raise util.Abort(_("patch %s not in series") % patch)
1126 raise util.Abort(_("patch %s not in series") % patch)
1123
1127
1124 def push(self, repo, patch=None, force=False, list=False,
1128 def push(self, repo, patch=None, force=False, list=False,
1125 mergeq=None, all=False, move=False, exact=False):
1129 mergeq=None, all=False, move=False, exact=False):
1126 diffopts = self.diffopts()
1130 diffopts = self.diffopts()
1127 wlock = repo.wlock()
1131 wlock = repo.wlock()
1128 try:
1132 try:
1129 heads = []
1133 heads = []
1130 for b, ls in repo.branchmap().iteritems():
1134 for b, ls in repo.branchmap().iteritems():
1131 heads += ls
1135 heads += ls
1132 if not heads:
1136 if not heads:
1133 heads = [nullid]
1137 heads = [nullid]
1134 if repo.dirstate.p1() not in heads and not exact:
1138 if repo.dirstate.p1() not in heads and not exact:
1135 self.ui.status(_("(working directory not at a head)\n"))
1139 self.ui.status(_("(working directory not at a head)\n"))
1136
1140
1137 if not self.series:
1141 if not self.series:
1138 self.ui.warn(_('no patches in series\n'))
1142 self.ui.warn(_('no patches in series\n'))
1139 return 0
1143 return 0
1140
1144
1141 # Suppose our series file is: A B C and the current 'top'
1145 # Suppose our series file is: A B C and the current 'top'
1142 # patch is B. qpush C should be performed (moving forward)
1146 # patch is B. qpush C should be performed (moving forward)
1143 # qpush B is a NOP (no change) qpush A is an error (can't
1147 # qpush B is a NOP (no change) qpush A is an error (can't
1144 # go backwards with qpush)
1148 # go backwards with qpush)
1145 if patch:
1149 if patch:
1146 patch = self.lookup(patch)
1150 patch = self.lookup(patch)
1147 info = self.isapplied(patch)
1151 info = self.isapplied(patch)
1148 if info and info[0] >= len(self.applied) - 1:
1152 if info and info[0] >= len(self.applied) - 1:
1149 self.ui.warn(
1153 self.ui.warn(
1150 _('qpush: %s is already at the top\n') % patch)
1154 _('qpush: %s is already at the top\n') % patch)
1151 return 0
1155 return 0
1152
1156
1153 pushable, reason = self.pushable(patch)
1157 pushable, reason = self.pushable(patch)
1154 if pushable:
1158 if pushable:
1155 if self.series.index(patch) < self.seriesend():
1159 if self.series.index(patch) < self.seriesend():
1156 raise util.Abort(
1160 raise util.Abort(
1157 _("cannot push to a previous patch: %s") % patch)
1161 _("cannot push to a previous patch: %s") % patch)
1158 else:
1162 else:
1159 if reason:
1163 if reason:
1160 reason = _('guarded by %s') % reason
1164 reason = _('guarded by %s') % reason
1161 else:
1165 else:
1162 reason = _('no matching guards')
1166 reason = _('no matching guards')
1163 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1167 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1164 return 1
1168 return 1
1165 elif all:
1169 elif all:
1166 patch = self.series[-1]
1170 patch = self.series[-1]
1167 if self.isapplied(patch):
1171 if self.isapplied(patch):
1168 self.ui.warn(_('all patches are currently applied\n'))
1172 self.ui.warn(_('all patches are currently applied\n'))
1169 return 0
1173 return 0
1170
1174
1171 # Following the above example, starting at 'top' of B:
1175 # Following the above example, starting at 'top' of B:
1172 # qpush should be performed (pushes C), but a subsequent
1176 # qpush should be performed (pushes C), but a subsequent
1173 # qpush without an argument is an error (nothing to
1177 # qpush without an argument is an error (nothing to
1174 # apply). This allows a loop of "...while hg qpush..." to
1178 # apply). This allows a loop of "...while hg qpush..." to
1175 # work as it detects an error when done
1179 # work as it detects an error when done
1176 start = self.seriesend()
1180 start = self.seriesend()
1177 if start == len(self.series):
1181 if start == len(self.series):
1178 self.ui.warn(_('patch series already fully applied\n'))
1182 self.ui.warn(_('patch series already fully applied\n'))
1179 return 1
1183 return 1
1180 if not force:
1184 if not force:
1181 self.checklocalchanges(repo, refresh=self.applied)
1185 self.checklocalchanges(repo, refresh=self.applied)
1182
1186
1183 if exact:
1187 if exact:
1184 if move:
1188 if move:
1185 raise util.Abort(_("cannot use --exact and --move together"))
1189 raise util.Abort(_("cannot use --exact and --move together"))
1186 if self.applied:
1190 if self.applied:
1187 raise util.Abort(_("cannot push --exact with applied patches"))
1191 raise util.Abort(_("cannot push --exact with applied patches"))
1188 root = self.series[start]
1192 root = self.series[start]
1189 target = patchheader(self.join(root), self.plainmode).parent
1193 target = patchheader(self.join(root), self.plainmode).parent
1190 if not target:
1194 if not target:
1191 raise util.Abort(_("%s does not have a parent recorded" % root))
1195 raise util.Abort(_("%s does not have a parent recorded" % root))
1192 if not repo[target] == repo['.']:
1196 if not repo[target] == repo['.']:
1193 hg.update(repo, target)
1197 hg.update(repo, target)
1194
1198
1195 if move:
1199 if move:
1196 if not patch:
1200 if not patch:
1197 raise util.Abort(_("please specify the patch to move"))
1201 raise util.Abort(_("please specify the patch to move"))
1198 for i, rpn in enumerate(self.fullseries[start:]):
1202 for i, rpn in enumerate(self.fullseries[start:]):
1199 # strip markers for patch guards
1203 # strip markers for patch guards
1200 if self.guard_re.split(rpn, 1)[0] == patch:
1204 if self.guard_re.split(rpn, 1)[0] == patch:
1201 break
1205 break
1202 index = start + i
1206 index = start + i
1203 assert index < len(self.fullseries)
1207 assert index < len(self.fullseries)
1204 fullpatch = self.fullseries[index]
1208 fullpatch = self.fullseries[index]
1205 del self.fullseries[index]
1209 del self.fullseries[index]
1206 self.fullseries.insert(start, fullpatch)
1210 self.fullseries.insert(start, fullpatch)
1207 self.parseseries()
1211 self.parseseries()
1208 self.seriesdirty = True
1212 self.seriesdirty = True
1209
1213
1210 self.applieddirty = True
1214 self.applieddirty = True
1211 if start > 0:
1215 if start > 0:
1212 self.checktoppatch(repo)
1216 self.checktoppatch(repo)
1213 if not patch:
1217 if not patch:
1214 patch = self.series[start]
1218 patch = self.series[start]
1215 end = start + 1
1219 end = start + 1
1216 else:
1220 else:
1217 end = self.series.index(patch, start) + 1
1221 end = self.series.index(patch, start) + 1
1218
1222
1219 s = self.series[start:end]
1223 s = self.series[start:end]
1220 all_files = set()
1224 all_files = set()
1221 try:
1225 try:
1222 if mergeq:
1226 if mergeq:
1223 ret = self.mergepatch(repo, mergeq, s, diffopts)
1227 ret = self.mergepatch(repo, mergeq, s, diffopts)
1224 else:
1228 else:
1225 ret = self.apply(repo, s, list, all_files=all_files)
1229 ret = self.apply(repo, s, list, all_files=all_files)
1226 except:
1230 except:
1227 self.ui.warn(_('cleaning up working directory...'))
1231 self.ui.warn(_('cleaning up working directory...'))
1228 node = repo.dirstate.p1()
1232 node = repo.dirstate.p1()
1229 hg.revert(repo, node, None)
1233 hg.revert(repo, node, None)
1230 # only remove unknown files that we know we touched or
1234 # only remove unknown files that we know we touched or
1231 # created while patching
1235 # created while patching
1232 for f in all_files:
1236 for f in all_files:
1233 if f not in repo.dirstate:
1237 if f not in repo.dirstate:
1234 try:
1238 try:
1235 util.unlinkpath(repo.wjoin(f))
1239 util.unlinkpath(repo.wjoin(f))
1236 except OSError, inst:
1240 except OSError, inst:
1237 if inst.errno != errno.ENOENT:
1241 if inst.errno != errno.ENOENT:
1238 raise
1242 raise
1239 self.ui.warn(_('done\n'))
1243 self.ui.warn(_('done\n'))
1240 raise
1244 raise
1241
1245
1242 if not self.applied:
1246 if not self.applied:
1243 return ret[0]
1247 return ret[0]
1244 top = self.applied[-1].name
1248 top = self.applied[-1].name
1245 if ret[0] and ret[0] > 1:
1249 if ret[0] and ret[0] > 1:
1246 msg = _("errors during apply, please fix and refresh %s\n")
1250 msg = _("errors during apply, please fix and refresh %s\n")
1247 self.ui.write(msg % top)
1251 self.ui.write(msg % top)
1248 else:
1252 else:
1249 self.ui.write(_("now at: %s\n") % top)
1253 self.ui.write(_("now at: %s\n") % top)
1250 return ret[0]
1254 return ret[0]
1251
1255
1252 finally:
1256 finally:
1253 wlock.release()
1257 wlock.release()
1254
1258
1255 def pop(self, repo, patch=None, force=False, update=True, all=False):
1259 def pop(self, repo, patch=None, force=False, update=True, all=False):
1256 wlock = repo.wlock()
1260 wlock = repo.wlock()
1257 try:
1261 try:
1258 if patch:
1262 if patch:
1259 # index, rev, patch
1263 # index, rev, patch
1260 info = self.isapplied(patch)
1264 info = self.isapplied(patch)
1261 if not info:
1265 if not info:
1262 patch = self.lookup(patch)
1266 patch = self.lookup(patch)
1263 info = self.isapplied(patch)
1267 info = self.isapplied(patch)
1264 if not info:
1268 if not info:
1265 raise util.Abort(_("patch %s is not applied") % patch)
1269 raise util.Abort(_("patch %s is not applied") % patch)
1266
1270
1267 if not self.applied:
1271 if not self.applied:
1268 # Allow qpop -a to work repeatedly,
1272 # Allow qpop -a to work repeatedly,
1269 # but not qpop without an argument
1273 # but not qpop without an argument
1270 self.ui.warn(_("no patches applied\n"))
1274 self.ui.warn(_("no patches applied\n"))
1271 return not all
1275 return not all
1272
1276
1273 if all:
1277 if all:
1274 start = 0
1278 start = 0
1275 elif patch:
1279 elif patch:
1276 start = info[0] + 1
1280 start = info[0] + 1
1277 else:
1281 else:
1278 start = len(self.applied) - 1
1282 start = len(self.applied) - 1
1279
1283
1280 if start >= len(self.applied):
1284 if start >= len(self.applied):
1281 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1285 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1282 return
1286 return
1283
1287
1284 if not update:
1288 if not update:
1285 parents = repo.dirstate.parents()
1289 parents = repo.dirstate.parents()
1286 rr = [x.node for x in self.applied]
1290 rr = [x.node for x in self.applied]
1287 for p in parents:
1291 for p in parents:
1288 if p in rr:
1292 if p in rr:
1289 self.ui.warn(_("qpop: forcing dirstate update\n"))
1293 self.ui.warn(_("qpop: forcing dirstate update\n"))
1290 update = True
1294 update = True
1291 else:
1295 else:
1292 parents = [p.node() for p in repo[None].parents()]
1296 parents = [p.node() for p in repo[None].parents()]
1293 needupdate = False
1297 needupdate = False
1294 for entry in self.applied[start:]:
1298 for entry in self.applied[start:]:
1295 if entry.node in parents:
1299 if entry.node in parents:
1296 needupdate = True
1300 needupdate = True
1297 break
1301 break
1298 update = needupdate
1302 update = needupdate
1299
1303
1300 if not force and update:
1304 if not force and update:
1301 self.checklocalchanges(repo)
1305 self.checklocalchanges(repo)
1302
1306
1303 self.applieddirty = True
1307 self.applieddirty = True
1304 end = len(self.applied)
1308 end = len(self.applied)
1305 rev = self.applied[start].node
1309 rev = self.applied[start].node
1306 if update:
1310 if update:
1307 top = self.checktoppatch(repo)[0]
1311 top = self.checktoppatch(repo)[0]
1308
1312
1309 try:
1313 try:
1310 heads = repo.changelog.heads(rev)
1314 heads = repo.changelog.heads(rev)
1311 except error.LookupError:
1315 except error.LookupError:
1312 node = short(rev)
1316 node = short(rev)
1313 raise util.Abort(_('trying to pop unknown node %s') % node)
1317 raise util.Abort(_('trying to pop unknown node %s') % node)
1314
1318
1315 if heads != [self.applied[-1].node]:
1319 if heads != [self.applied[-1].node]:
1316 raise util.Abort(_("popping would remove a revision not "
1320 raise util.Abort(_("popping would remove a revision not "
1317 "managed by this patch queue"))
1321 "managed by this patch queue"))
1318 if not repo[self.applied[-1].node].mutable():
1322 if not repo[self.applied[-1].node].mutable():
1319 raise util.Abort(
1323 raise util.Abort(
1320 _("popping would remove an immutable revision"),
1324 _("popping would remove an immutable revision"),
1321 hint=_('see "hg help phases" for details'))
1325 hint=_('see "hg help phases" for details'))
1322
1326
1323 # we know there are no local changes, so we can make a simplified
1327 # we know there are no local changes, so we can make a simplified
1324 # form of hg.update.
1328 # form of hg.update.
1325 if update:
1329 if update:
1326 qp = self.qparents(repo, rev)
1330 qp = self.qparents(repo, rev)
1327 ctx = repo[qp]
1331 ctx = repo[qp]
1328 m, a, r, d = repo.status(qp, top)[:4]
1332 m, a, r, d = repo.status(qp, top)[:4]
1329 if d:
1333 if d:
1330 raise util.Abort(_("deletions found between repo revs"))
1334 raise util.Abort(_("deletions found between repo revs"))
1331 for f in a:
1335 for f in a:
1332 try:
1336 try:
1333 util.unlinkpath(repo.wjoin(f))
1337 util.unlinkpath(repo.wjoin(f))
1334 except OSError, e:
1338 except OSError, e:
1335 if e.errno != errno.ENOENT:
1339 if e.errno != errno.ENOENT:
1336 raise
1340 raise
1337 repo.dirstate.drop(f)
1341 repo.dirstate.drop(f)
1338 for f in m + r:
1342 for f in m + r:
1339 fctx = ctx[f]
1343 fctx = ctx[f]
1340 repo.wwrite(f, fctx.data(), fctx.flags())
1344 repo.wwrite(f, fctx.data(), fctx.flags())
1341 repo.dirstate.normal(f)
1345 repo.dirstate.normal(f)
1342 repo.dirstate.setparents(qp, nullid)
1346 repo.dirstate.setparents(qp, nullid)
1343 for patch in reversed(self.applied[start:end]):
1347 for patch in reversed(self.applied[start:end]):
1344 self.ui.status(_("popping %s\n") % patch.name)
1348 self.ui.status(_("popping %s\n") % patch.name)
1345 del self.applied[start:end]
1349 del self.applied[start:end]
1346 self.strip(repo, [rev], update=False, backup='strip')
1350 self.strip(repo, [rev], update=False, backup='strip')
1347 if self.applied:
1351 if self.applied:
1348 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1352 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1349 else:
1353 else:
1350 self.ui.write(_("patch queue now empty\n"))
1354 self.ui.write(_("patch queue now empty\n"))
1351 finally:
1355 finally:
1352 wlock.release()
1356 wlock.release()
1353
1357
1354 def diff(self, repo, pats, opts):
1358 def diff(self, repo, pats, opts):
1355 top, patch = self.checktoppatch(repo)
1359 top, patch = self.checktoppatch(repo)
1356 if not top:
1360 if not top:
1357 self.ui.write(_("no patches applied\n"))
1361 self.ui.write(_("no patches applied\n"))
1358 return
1362 return
1359 qp = self.qparents(repo, top)
1363 qp = self.qparents(repo, top)
1360 if opts.get('reverse'):
1364 if opts.get('reverse'):
1361 node1, node2 = None, qp
1365 node1, node2 = None, qp
1362 else:
1366 else:
1363 node1, node2 = qp, None
1367 node1, node2 = qp, None
1364 diffopts = self.diffopts(opts, patch)
1368 diffopts = self.diffopts(opts, patch)
1365 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1369 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1366
1370
1367 def refresh(self, repo, pats=None, **opts):
1371 def refresh(self, repo, pats=None, **opts):
1368 if not self.applied:
1372 if not self.applied:
1369 self.ui.write(_("no patches applied\n"))
1373 self.ui.write(_("no patches applied\n"))
1370 return 1
1374 return 1
1371 msg = opts.get('msg', '').rstrip()
1375 msg = opts.get('msg', '').rstrip()
1372 newuser = opts.get('user')
1376 newuser = opts.get('user')
1373 newdate = opts.get('date')
1377 newdate = opts.get('date')
1374 if newdate:
1378 if newdate:
1375 newdate = '%d %d' % util.parsedate(newdate)
1379 newdate = '%d %d' % util.parsedate(newdate)
1376 wlock = repo.wlock()
1380 wlock = repo.wlock()
1377
1381
1378 try:
1382 try:
1379 self.checktoppatch(repo)
1383 self.checktoppatch(repo)
1380 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1384 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1381 if repo.changelog.heads(top) != [top]:
1385 if repo.changelog.heads(top) != [top]:
1382 raise util.Abort(_("cannot refresh a revision with children"))
1386 raise util.Abort(_("cannot refresh a revision with children"))
1383 if not repo[top].mutable():
1387 if not repo[top].mutable():
1384 raise util.Abort(_("cannot refresh immutable revision"),
1388 raise util.Abort(_("cannot refresh immutable revision"),
1385 hint=_('see "hg help phases" for details'))
1389 hint=_('see "hg help phases" for details'))
1386
1390
1387 inclsubs = self.checksubstate(repo)
1391 inclsubs = self.checksubstate(repo)
1388
1392
1389 cparents = repo.changelog.parents(top)
1393 cparents = repo.changelog.parents(top)
1390 patchparent = self.qparents(repo, top)
1394 patchparent = self.qparents(repo, top)
1391 ph = patchheader(self.join(patchfn), self.plainmode)
1395 ph = patchheader(self.join(patchfn), self.plainmode)
1392 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1396 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1393 if msg:
1397 if msg:
1394 ph.setmessage(msg)
1398 ph.setmessage(msg)
1395 if newuser:
1399 if newuser:
1396 ph.setuser(newuser)
1400 ph.setuser(newuser)
1397 if newdate:
1401 if newdate:
1398 ph.setdate(newdate)
1402 ph.setdate(newdate)
1399 ph.setparent(hex(patchparent))
1403 ph.setparent(hex(patchparent))
1400
1404
1401 # only commit new patch when write is complete
1405 # only commit new patch when write is complete
1402 patchf = self.opener(patchfn, 'w', atomictemp=True)
1406 patchf = self.opener(patchfn, 'w', atomictemp=True)
1403
1407
1404 comments = str(ph)
1408 comments = str(ph)
1405 if comments:
1409 if comments:
1406 patchf.write(comments)
1410 patchf.write(comments)
1407
1411
1408 # update the dirstate in place, strip off the qtip commit
1412 # update the dirstate in place, strip off the qtip commit
1409 # and then commit.
1413 # and then commit.
1410 #
1414 #
1411 # this should really read:
1415 # this should really read:
1412 # mm, dd, aa = repo.status(top, patchparent)[:3]
1416 # mm, dd, aa = repo.status(top, patchparent)[:3]
1413 # but we do it backwards to take advantage of manifest/chlog
1417 # but we do it backwards to take advantage of manifest/chlog
1414 # caching against the next repo.status call
1418 # caching against the next repo.status call
1415 mm, aa, dd = repo.status(patchparent, top)[:3]
1419 mm, aa, dd = repo.status(patchparent, top)[:3]
1416 changes = repo.changelog.read(top)
1420 changes = repo.changelog.read(top)
1417 man = repo.manifest.read(changes[0])
1421 man = repo.manifest.read(changes[0])
1418 aaa = aa[:]
1422 aaa = aa[:]
1419 matchfn = scmutil.match(repo[None], pats, opts)
1423 matchfn = scmutil.match(repo[None], pats, opts)
1420 # in short mode, we only diff the files included in the
1424 # in short mode, we only diff the files included in the
1421 # patch already plus specified files
1425 # patch already plus specified files
1422 if opts.get('short'):
1426 if opts.get('short'):
1423 # if amending a patch, we start with existing
1427 # if amending a patch, we start with existing
1424 # files plus specified files - unfiltered
1428 # files plus specified files - unfiltered
1425 match = scmutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1429 match = scmutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1426 # filter with inc/exl options
1430 # filter with inc/exl options
1427 matchfn = scmutil.match(repo[None], opts=opts)
1431 matchfn = scmutil.match(repo[None], opts=opts)
1428 else:
1432 else:
1429 match = scmutil.matchall(repo)
1433 match = scmutil.matchall(repo)
1430 m, a, r, d = repo.status(match=match)[:4]
1434 m, a, r, d = repo.status(match=match)[:4]
1431 mm = set(mm)
1435 mm = set(mm)
1432 aa = set(aa)
1436 aa = set(aa)
1433 dd = set(dd)
1437 dd = set(dd)
1434
1438
1435 # we might end up with files that were added between
1439 # we might end up with files that were added between
1436 # qtip and the dirstate parent, but then changed in the
1440 # qtip and the dirstate parent, but then changed in the
1437 # local dirstate. in this case, we want them to only
1441 # local dirstate. in this case, we want them to only
1438 # show up in the added section
1442 # show up in the added section
1439 for x in m:
1443 for x in m:
1440 if x not in aa:
1444 if x not in aa:
1441 mm.add(x)
1445 mm.add(x)
1442 # we might end up with files added by the local dirstate that
1446 # we might end up with files added by the local dirstate that
1443 # were deleted by the patch. In this case, they should only
1447 # were deleted by the patch. In this case, they should only
1444 # show up in the changed section.
1448 # show up in the changed section.
1445 for x in a:
1449 for x in a:
1446 if x in dd:
1450 if x in dd:
1447 dd.remove(x)
1451 dd.remove(x)
1448 mm.add(x)
1452 mm.add(x)
1449 else:
1453 else:
1450 aa.add(x)
1454 aa.add(x)
1451 # make sure any files deleted in the local dirstate
1455 # make sure any files deleted in the local dirstate
1452 # are not in the add or change column of the patch
1456 # are not in the add or change column of the patch
1453 forget = []
1457 forget = []
1454 for x in d + r:
1458 for x in d + r:
1455 if x in aa:
1459 if x in aa:
1456 aa.remove(x)
1460 aa.remove(x)
1457 forget.append(x)
1461 forget.append(x)
1458 continue
1462 continue
1459 else:
1463 else:
1460 mm.discard(x)
1464 mm.discard(x)
1461 dd.add(x)
1465 dd.add(x)
1462
1466
1463 m = list(mm)
1467 m = list(mm)
1464 r = list(dd)
1468 r = list(dd)
1465 a = list(aa)
1469 a = list(aa)
1466 c = [filter(matchfn, l) for l in (m, a, r)]
1470 c = [filter(matchfn, l) for l in (m, a, r)]
1467 match = scmutil.matchfiles(repo, set(c[0] + c[1] + c[2] + inclsubs))
1471 match = scmutil.matchfiles(repo, set(c[0] + c[1] + c[2] + inclsubs))
1468 chunks = patchmod.diff(repo, patchparent, match=match,
1472 chunks = patchmod.diff(repo, patchparent, match=match,
1469 changes=c, opts=diffopts)
1473 changes=c, opts=diffopts)
1470 for chunk in chunks:
1474 for chunk in chunks:
1471 patchf.write(chunk)
1475 patchf.write(chunk)
1472
1476
1473 try:
1477 try:
1474 if diffopts.git or diffopts.upgrade:
1478 if diffopts.git or diffopts.upgrade:
1475 copies = {}
1479 copies = {}
1476 for dst in a:
1480 for dst in a:
1477 src = repo.dirstate.copied(dst)
1481 src = repo.dirstate.copied(dst)
1478 # during qfold, the source file for copies may
1482 # during qfold, the source file for copies may
1479 # be removed. Treat this as a simple add.
1483 # be removed. Treat this as a simple add.
1480 if src is not None and src in repo.dirstate:
1484 if src is not None and src in repo.dirstate:
1481 copies.setdefault(src, []).append(dst)
1485 copies.setdefault(src, []).append(dst)
1482 repo.dirstate.add(dst)
1486 repo.dirstate.add(dst)
1483 # remember the copies between patchparent and qtip
1487 # remember the copies between patchparent and qtip
1484 for dst in aaa:
1488 for dst in aaa:
1485 f = repo.file(dst)
1489 f = repo.file(dst)
1486 src = f.renamed(man[dst])
1490 src = f.renamed(man[dst])
1487 if src:
1491 if src:
1488 copies.setdefault(src[0], []).extend(
1492 copies.setdefault(src[0], []).extend(
1489 copies.get(dst, []))
1493 copies.get(dst, []))
1490 if dst in a:
1494 if dst in a:
1491 copies[src[0]].append(dst)
1495 copies[src[0]].append(dst)
1492 # we can't copy a file created by the patch itself
1496 # we can't copy a file created by the patch itself
1493 if dst in copies:
1497 if dst in copies:
1494 del copies[dst]
1498 del copies[dst]
1495 for src, dsts in copies.iteritems():
1499 for src, dsts in copies.iteritems():
1496 for dst in dsts:
1500 for dst in dsts:
1497 repo.dirstate.copy(src, dst)
1501 repo.dirstate.copy(src, dst)
1498 else:
1502 else:
1499 for dst in a:
1503 for dst in a:
1500 repo.dirstate.add(dst)
1504 repo.dirstate.add(dst)
1501 # Drop useless copy information
1505 # Drop useless copy information
1502 for f in list(repo.dirstate.copies()):
1506 for f in list(repo.dirstate.copies()):
1503 repo.dirstate.copy(None, f)
1507 repo.dirstate.copy(None, f)
1504 for f in r:
1508 for f in r:
1505 repo.dirstate.remove(f)
1509 repo.dirstate.remove(f)
1506 # if the patch excludes a modified file, mark that
1510 # if the patch excludes a modified file, mark that
1507 # file with mtime=0 so status can see it.
1511 # file with mtime=0 so status can see it.
1508 mm = []
1512 mm = []
1509 for i in xrange(len(m)-1, -1, -1):
1513 for i in xrange(len(m)-1, -1, -1):
1510 if not matchfn(m[i]):
1514 if not matchfn(m[i]):
1511 mm.append(m[i])
1515 mm.append(m[i])
1512 del m[i]
1516 del m[i]
1513 for f in m:
1517 for f in m:
1514 repo.dirstate.normal(f)
1518 repo.dirstate.normal(f)
1515 for f in mm:
1519 for f in mm:
1516 repo.dirstate.normallookup(f)
1520 repo.dirstate.normallookup(f)
1517 for f in forget:
1521 for f in forget:
1518 repo.dirstate.drop(f)
1522 repo.dirstate.drop(f)
1519
1523
1520 if not msg:
1524 if not msg:
1521 if not ph.message:
1525 if not ph.message:
1522 message = "[mq]: %s\n" % patchfn
1526 message = "[mq]: %s\n" % patchfn
1523 else:
1527 else:
1524 message = "\n".join(ph.message)
1528 message = "\n".join(ph.message)
1525 else:
1529 else:
1526 message = msg
1530 message = msg
1527
1531
1528 user = ph.user or changes[1]
1532 user = ph.user or changes[1]
1529
1533
1530 oldphase = repo[top].phase()
1534 oldphase = repo[top].phase()
1531
1535
1532 # assumes strip can roll itself back if interrupted
1536 # assumes strip can roll itself back if interrupted
1533 repo.dirstate.setparents(*cparents)
1537 repo.dirstate.setparents(*cparents)
1534 self.applied.pop()
1538 self.applied.pop()
1535 self.applieddirty = True
1539 self.applieddirty = True
1536 self.strip(repo, [top], update=False,
1540 self.strip(repo, [top], update=False,
1537 backup='strip')
1541 backup='strip')
1538 except:
1542 except:
1539 repo.dirstate.invalidate()
1543 repo.dirstate.invalidate()
1540 raise
1544 raise
1541
1545
1542 try:
1546 try:
1543 # might be nice to attempt to roll back strip after this
1547 # might be nice to attempt to roll back strip after this
1544
1548
1545 # Ensure we create a new changeset in the same phase than
1549 # Ensure we create a new changeset in the same phase than
1546 # the old one.
1550 # the old one.
1547 n = secretcommit(repo, oldphase, message, user, ph.date,
1551 n = secretcommit(repo, oldphase, message, user, ph.date,
1548 match=match, force=True)
1552 match=match, force=True)
1549 # only write patch after a successful commit
1553 # only write patch after a successful commit
1550 patchf.close()
1554 patchf.close()
1551 self.applied.append(statusentry(n, patchfn))
1555 self.applied.append(statusentry(n, patchfn))
1552 except:
1556 except:
1553 ctx = repo[cparents[0]]
1557 ctx = repo[cparents[0]]
1554 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1558 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1555 self.savedirty()
1559 self.savedirty()
1556 self.ui.warn(_('refresh interrupted while patch was popped! '
1560 self.ui.warn(_('refresh interrupted while patch was popped! '
1557 '(revert --all, qpush to recover)\n'))
1561 '(revert --all, qpush to recover)\n'))
1558 raise
1562 raise
1559 finally:
1563 finally:
1560 wlock.release()
1564 wlock.release()
1561 self.removeundo(repo)
1565 self.removeundo(repo)
1562
1566
1563 def init(self, repo, create=False):
1567 def init(self, repo, create=False):
1564 if not create and os.path.isdir(self.path):
1568 if not create and os.path.isdir(self.path):
1565 raise util.Abort(_("patch queue directory already exists"))
1569 raise util.Abort(_("patch queue directory already exists"))
1566 try:
1570 try:
1567 os.mkdir(self.path)
1571 os.mkdir(self.path)
1568 except OSError, inst:
1572 except OSError, inst:
1569 if inst.errno != errno.EEXIST or not create:
1573 if inst.errno != errno.EEXIST or not create:
1570 raise
1574 raise
1571 if create:
1575 if create:
1572 return self.qrepo(create=True)
1576 return self.qrepo(create=True)
1573
1577
1574 def unapplied(self, repo, patch=None):
1578 def unapplied(self, repo, patch=None):
1575 if patch and patch not in self.series:
1579 if patch and patch not in self.series:
1576 raise util.Abort(_("patch %s is not in series file") % patch)
1580 raise util.Abort(_("patch %s is not in series file") % patch)
1577 if not patch:
1581 if not patch:
1578 start = self.seriesend()
1582 start = self.seriesend()
1579 else:
1583 else:
1580 start = self.series.index(patch) + 1
1584 start = self.series.index(patch) + 1
1581 unapplied = []
1585 unapplied = []
1582 for i in xrange(start, len(self.series)):
1586 for i in xrange(start, len(self.series)):
1583 pushable, reason = self.pushable(i)
1587 pushable, reason = self.pushable(i)
1584 if pushable:
1588 if pushable:
1585 unapplied.append((i, self.series[i]))
1589 unapplied.append((i, self.series[i]))
1586 self.explainpushable(i)
1590 self.explainpushable(i)
1587 return unapplied
1591 return unapplied
1588
1592
1589 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1593 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1590 summary=False):
1594 summary=False):
1591 def displayname(pfx, patchname, state):
1595 def displayname(pfx, patchname, state):
1592 if pfx:
1596 if pfx:
1593 self.ui.write(pfx)
1597 self.ui.write(pfx)
1594 if summary:
1598 if summary:
1595 ph = patchheader(self.join(patchname), self.plainmode)
1599 ph = patchheader(self.join(patchname), self.plainmode)
1596 msg = ph.message and ph.message[0] or ''
1600 msg = ph.message and ph.message[0] or ''
1597 if self.ui.formatted():
1601 if self.ui.formatted():
1598 width = self.ui.termwidth() - len(pfx) - len(patchname) - 2
1602 width = self.ui.termwidth() - len(pfx) - len(patchname) - 2
1599 if width > 0:
1603 if width > 0:
1600 msg = util.ellipsis(msg, width)
1604 msg = util.ellipsis(msg, width)
1601 else:
1605 else:
1602 msg = ''
1606 msg = ''
1603 self.ui.write(patchname, label='qseries.' + state)
1607 self.ui.write(patchname, label='qseries.' + state)
1604 self.ui.write(': ')
1608 self.ui.write(': ')
1605 self.ui.write(msg, label='qseries.message.' + state)
1609 self.ui.write(msg, label='qseries.message.' + state)
1606 else:
1610 else:
1607 self.ui.write(patchname, label='qseries.' + state)
1611 self.ui.write(patchname, label='qseries.' + state)
1608 self.ui.write('\n')
1612 self.ui.write('\n')
1609
1613
1610 applied = set([p.name for p in self.applied])
1614 applied = set([p.name for p in self.applied])
1611 if length is None:
1615 if length is None:
1612 length = len(self.series) - start
1616 length = len(self.series) - start
1613 if not missing:
1617 if not missing:
1614 if self.ui.verbose:
1618 if self.ui.verbose:
1615 idxwidth = len(str(start + length - 1))
1619 idxwidth = len(str(start + length - 1))
1616 for i in xrange(start, start + length):
1620 for i in xrange(start, start + length):
1617 patch = self.series[i]
1621 patch = self.series[i]
1618 if patch in applied:
1622 if patch in applied:
1619 char, state = 'A', 'applied'
1623 char, state = 'A', 'applied'
1620 elif self.pushable(i)[0]:
1624 elif self.pushable(i)[0]:
1621 char, state = 'U', 'unapplied'
1625 char, state = 'U', 'unapplied'
1622 else:
1626 else:
1623 char, state = 'G', 'guarded'
1627 char, state = 'G', 'guarded'
1624 pfx = ''
1628 pfx = ''
1625 if self.ui.verbose:
1629 if self.ui.verbose:
1626 pfx = '%*d %s ' % (idxwidth, i, char)
1630 pfx = '%*d %s ' % (idxwidth, i, char)
1627 elif status and status != char:
1631 elif status and status != char:
1628 continue
1632 continue
1629 displayname(pfx, patch, state)
1633 displayname(pfx, patch, state)
1630 else:
1634 else:
1631 msng_list = []
1635 msng_list = []
1632 for root, dirs, files in os.walk(self.path):
1636 for root, dirs, files in os.walk(self.path):
1633 d = root[len(self.path) + 1:]
1637 d = root[len(self.path) + 1:]
1634 for f in files:
1638 for f in files:
1635 fl = os.path.join(d, f)
1639 fl = os.path.join(d, f)
1636 if (fl not in self.series and
1640 if (fl not in self.series and
1637 fl not in (self.statuspath, self.seriespath,
1641 fl not in (self.statuspath, self.seriespath,
1638 self.guardspath)
1642 self.guardspath)
1639 and not fl.startswith('.')):
1643 and not fl.startswith('.')):
1640 msng_list.append(fl)
1644 msng_list.append(fl)
1641 for x in sorted(msng_list):
1645 for x in sorted(msng_list):
1642 pfx = self.ui.verbose and ('D ') or ''
1646 pfx = self.ui.verbose and ('D ') or ''
1643 displayname(pfx, x, 'missing')
1647 displayname(pfx, x, 'missing')
1644
1648
1645 def issaveline(self, l):
1649 def issaveline(self, l):
1646 if l.name == '.hg.patches.save.line':
1650 if l.name == '.hg.patches.save.line':
1647 return True
1651 return True
1648
1652
1649 def qrepo(self, create=False):
1653 def qrepo(self, create=False):
1650 ui = self.ui.copy()
1654 ui = self.ui.copy()
1651 ui.setconfig('paths', 'default', '', overlay=False)
1655 ui.setconfig('paths', 'default', '', overlay=False)
1652 ui.setconfig('paths', 'default-push', '', overlay=False)
1656 ui.setconfig('paths', 'default-push', '', overlay=False)
1653 if create or os.path.isdir(self.join(".hg")):
1657 if create or os.path.isdir(self.join(".hg")):
1654 return hg.repository(ui, path=self.path, create=create)
1658 return hg.repository(ui, path=self.path, create=create)
1655
1659
1656 def restore(self, repo, rev, delete=None, qupdate=None):
1660 def restore(self, repo, rev, delete=None, qupdate=None):
1657 desc = repo[rev].description().strip()
1661 desc = repo[rev].description().strip()
1658 lines = desc.splitlines()
1662 lines = desc.splitlines()
1659 i = 0
1663 i = 0
1660 datastart = None
1664 datastart = None
1661 series = []
1665 series = []
1662 applied = []
1666 applied = []
1663 qpp = None
1667 qpp = None
1664 for i, line in enumerate(lines):
1668 for i, line in enumerate(lines):
1665 if line == 'Patch Data:':
1669 if line == 'Patch Data:':
1666 datastart = i + 1
1670 datastart = i + 1
1667 elif line.startswith('Dirstate:'):
1671 elif line.startswith('Dirstate:'):
1668 l = line.rstrip()
1672 l = line.rstrip()
1669 l = l[10:].split(' ')
1673 l = l[10:].split(' ')
1670 qpp = [bin(x) for x in l]
1674 qpp = [bin(x) for x in l]
1671 elif datastart is not None:
1675 elif datastart is not None:
1672 l = line.rstrip()
1676 l = line.rstrip()
1673 n, name = l.split(':', 1)
1677 n, name = l.split(':', 1)
1674 if n:
1678 if n:
1675 applied.append(statusentry(bin(n), name))
1679 applied.append(statusentry(bin(n), name))
1676 else:
1680 else:
1677 series.append(l)
1681 series.append(l)
1678 if datastart is None:
1682 if datastart is None:
1679 self.ui.warn(_("No saved patch data found\n"))
1683 self.ui.warn(_("No saved patch data found\n"))
1680 return 1
1684 return 1
1681 self.ui.warn(_("restoring status: %s\n") % lines[0])
1685 self.ui.warn(_("restoring status: %s\n") % lines[0])
1682 self.fullseries = series
1686 self.fullseries = series
1683 self.applied = applied
1687 self.applied = applied
1684 self.parseseries()
1688 self.parseseries()
1685 self.seriesdirty = True
1689 self.seriesdirty = True
1686 self.applieddirty = True
1690 self.applieddirty = True
1687 heads = repo.changelog.heads()
1691 heads = repo.changelog.heads()
1688 if delete:
1692 if delete:
1689 if rev not in heads:
1693 if rev not in heads:
1690 self.ui.warn(_("save entry has children, leaving it alone\n"))
1694 self.ui.warn(_("save entry has children, leaving it alone\n"))
1691 else:
1695 else:
1692 self.ui.warn(_("removing save entry %s\n") % short(rev))
1696 self.ui.warn(_("removing save entry %s\n") % short(rev))
1693 pp = repo.dirstate.parents()
1697 pp = repo.dirstate.parents()
1694 if rev in pp:
1698 if rev in pp:
1695 update = True
1699 update = True
1696 else:
1700 else:
1697 update = False
1701 update = False
1698 self.strip(repo, [rev], update=update, backup='strip')
1702 self.strip(repo, [rev], update=update, backup='strip')
1699 if qpp:
1703 if qpp:
1700 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1704 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1701 (short(qpp[0]), short(qpp[1])))
1705 (short(qpp[0]), short(qpp[1])))
1702 if qupdate:
1706 if qupdate:
1703 self.ui.status(_("updating queue directory\n"))
1707 self.ui.status(_("updating queue directory\n"))
1704 r = self.qrepo()
1708 r = self.qrepo()
1705 if not r:
1709 if not r:
1706 self.ui.warn(_("Unable to load queue repository\n"))
1710 self.ui.warn(_("Unable to load queue repository\n"))
1707 return 1
1711 return 1
1708 hg.clean(r, qpp[0])
1712 hg.clean(r, qpp[0])
1709
1713
1710 def save(self, repo, msg=None):
1714 def save(self, repo, msg=None):
1711 if not self.applied:
1715 if not self.applied:
1712 self.ui.warn(_("save: no patches applied, exiting\n"))
1716 self.ui.warn(_("save: no patches applied, exiting\n"))
1713 return 1
1717 return 1
1714 if self.issaveline(self.applied[-1]):
1718 if self.issaveline(self.applied[-1]):
1715 self.ui.warn(_("status is already saved\n"))
1719 self.ui.warn(_("status is already saved\n"))
1716 return 1
1720 return 1
1717
1721
1718 if not msg:
1722 if not msg:
1719 msg = _("hg patches saved state")
1723 msg = _("hg patches saved state")
1720 else:
1724 else:
1721 msg = "hg patches: " + msg.rstrip('\r\n')
1725 msg = "hg patches: " + msg.rstrip('\r\n')
1722 r = self.qrepo()
1726 r = self.qrepo()
1723 if r:
1727 if r:
1724 pp = r.dirstate.parents()
1728 pp = r.dirstate.parents()
1725 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1729 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1726 msg += "\n\nPatch Data:\n"
1730 msg += "\n\nPatch Data:\n"
1727 msg += ''.join('%s\n' % x for x in self.applied)
1731 msg += ''.join('%s\n' % x for x in self.applied)
1728 msg += ''.join(':%s\n' % x for x in self.fullseries)
1732 msg += ''.join(':%s\n' % x for x in self.fullseries)
1729 n = repo.commit(msg, force=True)
1733 n = repo.commit(msg, force=True)
1730 if not n:
1734 if not n:
1731 self.ui.warn(_("repo commit failed\n"))
1735 self.ui.warn(_("repo commit failed\n"))
1732 return 1
1736 return 1
1733 self.applied.append(statusentry(n, '.hg.patches.save.line'))
1737 self.applied.append(statusentry(n, '.hg.patches.save.line'))
1734 self.applieddirty = True
1738 self.applieddirty = True
1735 self.removeundo(repo)
1739 self.removeundo(repo)
1736
1740
1737 def fullseriesend(self):
1741 def fullseriesend(self):
1738 if self.applied:
1742 if self.applied:
1739 p = self.applied[-1].name
1743 p = self.applied[-1].name
1740 end = self.findseries(p)
1744 end = self.findseries(p)
1741 if end is None:
1745 if end is None:
1742 return len(self.fullseries)
1746 return len(self.fullseries)
1743 return end + 1
1747 return end + 1
1744 return 0
1748 return 0
1745
1749
1746 def seriesend(self, all_patches=False):
1750 def seriesend(self, all_patches=False):
1747 """If all_patches is False, return the index of the next pushable patch
1751 """If all_patches is False, return the index of the next pushable patch
1748 in the series, or the series length. If all_patches is True, return the
1752 in the series, or the series length. If all_patches is True, return the
1749 index of the first patch past the last applied one.
1753 index of the first patch past the last applied one.
1750 """
1754 """
1751 end = 0
1755 end = 0
1752 def next(start):
1756 def next(start):
1753 if all_patches or start >= len(self.series):
1757 if all_patches or start >= len(self.series):
1754 return start
1758 return start
1755 for i in xrange(start, len(self.series)):
1759 for i in xrange(start, len(self.series)):
1756 p, reason = self.pushable(i)
1760 p, reason = self.pushable(i)
1757 if p:
1761 if p:
1758 return i
1762 return i
1759 self.explainpushable(i)
1763 self.explainpushable(i)
1760 return len(self.series)
1764 return len(self.series)
1761 if self.applied:
1765 if self.applied:
1762 p = self.applied[-1].name
1766 p = self.applied[-1].name
1763 try:
1767 try:
1764 end = self.series.index(p)
1768 end = self.series.index(p)
1765 except ValueError:
1769 except ValueError:
1766 return 0
1770 return 0
1767 return next(end + 1)
1771 return next(end + 1)
1768 return next(end)
1772 return next(end)
1769
1773
1770 def appliedname(self, index):
1774 def appliedname(self, index):
1771 pname = self.applied[index].name
1775 pname = self.applied[index].name
1772 if not self.ui.verbose:
1776 if not self.ui.verbose:
1773 p = pname
1777 p = pname
1774 else:
1778 else:
1775 p = str(self.series.index(pname)) + " " + pname
1779 p = str(self.series.index(pname)) + " " + pname
1776 return p
1780 return p
1777
1781
1778 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1782 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1779 force=None, git=False):
1783 force=None, git=False):
1780 def checkseries(patchname):
1784 def checkseries(patchname):
1781 if patchname in self.series:
1785 if patchname in self.series:
1782 raise util.Abort(_('patch %s is already in the series file')
1786 raise util.Abort(_('patch %s is already in the series file')
1783 % patchname)
1787 % patchname)
1784
1788
1785 if rev:
1789 if rev:
1786 if files:
1790 if files:
1787 raise util.Abort(_('option "-r" not valid when importing '
1791 raise util.Abort(_('option "-r" not valid when importing '
1788 'files'))
1792 'files'))
1789 rev = scmutil.revrange(repo, rev)
1793 rev = scmutil.revrange(repo, rev)
1790 rev.sort(reverse=True)
1794 rev.sort(reverse=True)
1791 if (len(files) > 1 or len(rev) > 1) and patchname:
1795 if (len(files) > 1 or len(rev) > 1) and patchname:
1792 raise util.Abort(_('option "-n" not valid when importing multiple '
1796 raise util.Abort(_('option "-n" not valid when importing multiple '
1793 'patches'))
1797 'patches'))
1794 if rev:
1798 if rev:
1795 # If mq patches are applied, we can only import revisions
1799 # If mq patches are applied, we can only import revisions
1796 # that form a linear path to qbase.
1800 # that form a linear path to qbase.
1797 # Otherwise, they should form a linear path to a head.
1801 # Otherwise, they should form a linear path to a head.
1798 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1802 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1799 if len(heads) > 1:
1803 if len(heads) > 1:
1800 raise util.Abort(_('revision %d is the root of more than one '
1804 raise util.Abort(_('revision %d is the root of more than one '
1801 'branch') % rev[-1])
1805 'branch') % rev[-1])
1802 if self.applied:
1806 if self.applied:
1803 base = repo.changelog.node(rev[0])
1807 base = repo.changelog.node(rev[0])
1804 if base in [n.node for n in self.applied]:
1808 if base in [n.node for n in self.applied]:
1805 raise util.Abort(_('revision %d is already managed')
1809 raise util.Abort(_('revision %d is already managed')
1806 % rev[0])
1810 % rev[0])
1807 if heads != [self.applied[-1].node]:
1811 if heads != [self.applied[-1].node]:
1808 raise util.Abort(_('revision %d is not the parent of '
1812 raise util.Abort(_('revision %d is not the parent of '
1809 'the queue') % rev[0])
1813 'the queue') % rev[0])
1810 base = repo.changelog.rev(self.applied[0].node)
1814 base = repo.changelog.rev(self.applied[0].node)
1811 lastparent = repo.changelog.parentrevs(base)[0]
1815 lastparent = repo.changelog.parentrevs(base)[0]
1812 else:
1816 else:
1813 if heads != [repo.changelog.node(rev[0])]:
1817 if heads != [repo.changelog.node(rev[0])]:
1814 raise util.Abort(_('revision %d has unmanaged children')
1818 raise util.Abort(_('revision %d has unmanaged children')
1815 % rev[0])
1819 % rev[0])
1816 lastparent = None
1820 lastparent = None
1817
1821
1818 diffopts = self.diffopts({'git': git})
1822 diffopts = self.diffopts({'git': git})
1819 for r in rev:
1823 for r in rev:
1820 if not repo[r].mutable():
1824 if not repo[r].mutable():
1821 raise util.Abort(_('revision %d is not mutable') % r,
1825 raise util.Abort(_('revision %d is not mutable') % r,
1822 hint=_('see "hg help phases" for details'))
1826 hint=_('see "hg help phases" for details'))
1823 p1, p2 = repo.changelog.parentrevs(r)
1827 p1, p2 = repo.changelog.parentrevs(r)
1824 n = repo.changelog.node(r)
1828 n = repo.changelog.node(r)
1825 if p2 != nullrev:
1829 if p2 != nullrev:
1826 raise util.Abort(_('cannot import merge revision %d') % r)
1830 raise util.Abort(_('cannot import merge revision %d') % r)
1827 if lastparent and lastparent != r:
1831 if lastparent and lastparent != r:
1828 raise util.Abort(_('revision %d is not the parent of %d')
1832 raise util.Abort(_('revision %d is not the parent of %d')
1829 % (r, lastparent))
1833 % (r, lastparent))
1830 lastparent = p1
1834 lastparent = p1
1831
1835
1832 if not patchname:
1836 if not patchname:
1833 patchname = normname('%d.diff' % r)
1837 patchname = normname('%d.diff' % r)
1834 checkseries(patchname)
1838 checkseries(patchname)
1835 self.checkpatchname(patchname, force)
1839 self.checkpatchname(patchname, force)
1836 self.fullseries.insert(0, patchname)
1840 self.fullseries.insert(0, patchname)
1837
1841
1838 patchf = self.opener(patchname, "w")
1842 patchf = self.opener(patchname, "w")
1839 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
1843 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
1840 patchf.close()
1844 patchf.close()
1841
1845
1842 se = statusentry(n, patchname)
1846 se = statusentry(n, patchname)
1843 self.applied.insert(0, se)
1847 self.applied.insert(0, se)
1844
1848
1845 self.added.append(patchname)
1849 self.added.append(patchname)
1846 patchname = None
1850 patchname = None
1847 if rev and repo.ui.configbool('mq', 'secret', False):
1851 if rev and repo.ui.configbool('mq', 'secret', False):
1848 # if we added anything with --rev, we must move the secret root
1852 # if we added anything with --rev, we must move the secret root
1849 phases.retractboundary(repo, phases.secret, [n])
1853 phases.retractboundary(repo, phases.secret, [n])
1850 self.parseseries()
1854 self.parseseries()
1851 self.applieddirty = True
1855 self.applieddirty = True
1852 self.seriesdirty = True
1856 self.seriesdirty = True
1853
1857
1854 for i, filename in enumerate(files):
1858 for i, filename in enumerate(files):
1855 if existing:
1859 if existing:
1856 if filename == '-':
1860 if filename == '-':
1857 raise util.Abort(_('-e is incompatible with import from -'))
1861 raise util.Abort(_('-e is incompatible with import from -'))
1858 filename = normname(filename)
1862 filename = normname(filename)
1859 self.checkreservedname(filename)
1863 self.checkreservedname(filename)
1860 originpath = self.join(filename)
1864 originpath = self.join(filename)
1861 if not os.path.isfile(originpath):
1865 if not os.path.isfile(originpath):
1862 raise util.Abort(_("patch %s does not exist") % filename)
1866 raise util.Abort(_("patch %s does not exist") % filename)
1863
1867
1864 if patchname:
1868 if patchname:
1865 self.checkpatchname(patchname, force)
1869 self.checkpatchname(patchname, force)
1866
1870
1867 self.ui.write(_('renaming %s to %s\n')
1871 self.ui.write(_('renaming %s to %s\n')
1868 % (filename, patchname))
1872 % (filename, patchname))
1869 util.rename(originpath, self.join(patchname))
1873 util.rename(originpath, self.join(patchname))
1870 else:
1874 else:
1871 patchname = filename
1875 patchname = filename
1872
1876
1873 else:
1877 else:
1874 if filename == '-' and not patchname:
1878 if filename == '-' and not patchname:
1875 raise util.Abort(_('need --name to import a patch from -'))
1879 raise util.Abort(_('need --name to import a patch from -'))
1876 elif not patchname:
1880 elif not patchname:
1877 patchname = normname(os.path.basename(filename.rstrip('/')))
1881 patchname = normname(os.path.basename(filename.rstrip('/')))
1878 self.checkpatchname(patchname, force)
1882 self.checkpatchname(patchname, force)
1879 try:
1883 try:
1880 if filename == '-':
1884 if filename == '-':
1881 text = self.ui.fin.read()
1885 text = self.ui.fin.read()
1882 else:
1886 else:
1883 fp = url.open(self.ui, filename)
1887 fp = url.open(self.ui, filename)
1884 text = fp.read()
1888 text = fp.read()
1885 fp.close()
1889 fp.close()
1886 except (OSError, IOError):
1890 except (OSError, IOError):
1887 raise util.Abort(_("unable to read file %s") % filename)
1891 raise util.Abort(_("unable to read file %s") % filename)
1888 patchf = self.opener(patchname, "w")
1892 patchf = self.opener(patchname, "w")
1889 patchf.write(text)
1893 patchf.write(text)
1890 patchf.close()
1894 patchf.close()
1891 if not force:
1895 if not force:
1892 checkseries(patchname)
1896 checkseries(patchname)
1893 if patchname not in self.series:
1897 if patchname not in self.series:
1894 index = self.fullseriesend() + i
1898 index = self.fullseriesend() + i
1895 self.fullseries[index:index] = [patchname]
1899 self.fullseries[index:index] = [patchname]
1896 self.parseseries()
1900 self.parseseries()
1897 self.seriesdirty = True
1901 self.seriesdirty = True
1898 self.ui.warn(_("adding %s to series file\n") % patchname)
1902 self.ui.warn(_("adding %s to series file\n") % patchname)
1899 self.added.append(patchname)
1903 self.added.append(patchname)
1900 patchname = None
1904 patchname = None
1901
1905
1902 self.removeundo(repo)
1906 self.removeundo(repo)
1903
1907
1904 @command("qdelete|qremove|qrm",
1908 @command("qdelete|qremove|qrm",
1905 [('k', 'keep', None, _('keep patch file')),
1909 [('k', 'keep', None, _('keep patch file')),
1906 ('r', 'rev', [],
1910 ('r', 'rev', [],
1907 _('stop managing a revision (DEPRECATED)'), _('REV'))],
1911 _('stop managing a revision (DEPRECATED)'), _('REV'))],
1908 _('hg qdelete [-k] [PATCH]...'))
1912 _('hg qdelete [-k] [PATCH]...'))
1909 def delete(ui, repo, *patches, **opts):
1913 def delete(ui, repo, *patches, **opts):
1910 """remove patches from queue
1914 """remove patches from queue
1911
1915
1912 The patches must not be applied, and at least one patch is required. Exact
1916 The patches must not be applied, and at least one patch is required. Exact
1913 patch identifiers must be given. With -k/--keep, the patch files are
1917 patch identifiers must be given. With -k/--keep, the patch files are
1914 preserved in the patch directory.
1918 preserved in the patch directory.
1915
1919
1916 To stop managing a patch and move it into permanent history,
1920 To stop managing a patch and move it into permanent history,
1917 use the :hg:`qfinish` command."""
1921 use the :hg:`qfinish` command."""
1918 q = repo.mq
1922 q = repo.mq
1919 q.delete(repo, patches, opts)
1923 q.delete(repo, patches, opts)
1920 q.savedirty()
1924 q.savedirty()
1921 return 0
1925 return 0
1922
1926
1923 @command("qapplied",
1927 @command("qapplied",
1924 [('1', 'last', None, _('show only the last patch'))
1928 [('1', 'last', None, _('show only the last patch'))
1925 ] + seriesopts,
1929 ] + seriesopts,
1926 _('hg qapplied [-1] [-s] [PATCH]'))
1930 _('hg qapplied [-1] [-s] [PATCH]'))
1927 def applied(ui, repo, patch=None, **opts):
1931 def applied(ui, repo, patch=None, **opts):
1928 """print the patches already applied
1932 """print the patches already applied
1929
1933
1930 Returns 0 on success."""
1934 Returns 0 on success."""
1931
1935
1932 q = repo.mq
1936 q = repo.mq
1933
1937
1934 if patch:
1938 if patch:
1935 if patch not in q.series:
1939 if patch not in q.series:
1936 raise util.Abort(_("patch %s is not in series file") % patch)
1940 raise util.Abort(_("patch %s is not in series file") % patch)
1937 end = q.series.index(patch) + 1
1941 end = q.series.index(patch) + 1
1938 else:
1942 else:
1939 end = q.seriesend(True)
1943 end = q.seriesend(True)
1940
1944
1941 if opts.get('last') and not end:
1945 if opts.get('last') and not end:
1942 ui.write(_("no patches applied\n"))
1946 ui.write(_("no patches applied\n"))
1943 return 1
1947 return 1
1944 elif opts.get('last') and end == 1:
1948 elif opts.get('last') and end == 1:
1945 ui.write(_("only one patch applied\n"))
1949 ui.write(_("only one patch applied\n"))
1946 return 1
1950 return 1
1947 elif opts.get('last'):
1951 elif opts.get('last'):
1948 start = end - 2
1952 start = end - 2
1949 end = 1
1953 end = 1
1950 else:
1954 else:
1951 start = 0
1955 start = 0
1952
1956
1953 q.qseries(repo, length=end, start=start, status='A',
1957 q.qseries(repo, length=end, start=start, status='A',
1954 summary=opts.get('summary'))
1958 summary=opts.get('summary'))
1955
1959
1956
1960
1957 @command("qunapplied",
1961 @command("qunapplied",
1958 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
1962 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
1959 _('hg qunapplied [-1] [-s] [PATCH]'))
1963 _('hg qunapplied [-1] [-s] [PATCH]'))
1960 def unapplied(ui, repo, patch=None, **opts):
1964 def unapplied(ui, repo, patch=None, **opts):
1961 """print the patches not yet applied
1965 """print the patches not yet applied
1962
1966
1963 Returns 0 on success."""
1967 Returns 0 on success."""
1964
1968
1965 q = repo.mq
1969 q = repo.mq
1966 if patch:
1970 if patch:
1967 if patch not in q.series:
1971 if patch not in q.series:
1968 raise util.Abort(_("patch %s is not in series file") % patch)
1972 raise util.Abort(_("patch %s is not in series file") % patch)
1969 start = q.series.index(patch) + 1
1973 start = q.series.index(patch) + 1
1970 else:
1974 else:
1971 start = q.seriesend(True)
1975 start = q.seriesend(True)
1972
1976
1973 if start == len(q.series) and opts.get('first'):
1977 if start == len(q.series) and opts.get('first'):
1974 ui.write(_("all patches applied\n"))
1978 ui.write(_("all patches applied\n"))
1975 return 1
1979 return 1
1976
1980
1977 length = opts.get('first') and 1 or None
1981 length = opts.get('first') and 1 or None
1978 q.qseries(repo, start=start, length=length, status='U',
1982 q.qseries(repo, start=start, length=length, status='U',
1979 summary=opts.get('summary'))
1983 summary=opts.get('summary'))
1980
1984
1981 @command("qimport",
1985 @command("qimport",
1982 [('e', 'existing', None, _('import file in patch directory')),
1986 [('e', 'existing', None, _('import file in patch directory')),
1983 ('n', 'name', '',
1987 ('n', 'name', '',
1984 _('name of patch file'), _('NAME')),
1988 _('name of patch file'), _('NAME')),
1985 ('f', 'force', None, _('overwrite existing files')),
1989 ('f', 'force', None, _('overwrite existing files')),
1986 ('r', 'rev', [],
1990 ('r', 'rev', [],
1987 _('place existing revisions under mq control'), _('REV')),
1991 _('place existing revisions under mq control'), _('REV')),
1988 ('g', 'git', None, _('use git extended diff format')),
1992 ('g', 'git', None, _('use git extended diff format')),
1989 ('P', 'push', None, _('qpush after importing'))],
1993 ('P', 'push', None, _('qpush after importing'))],
1990 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...'))
1994 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...'))
1991 def qimport(ui, repo, *filename, **opts):
1995 def qimport(ui, repo, *filename, **opts):
1992 """import a patch
1996 """import a patch
1993
1997
1994 The patch is inserted into the series after the last applied
1998 The patch is inserted into the series after the last applied
1995 patch. If no patches have been applied, qimport prepends the patch
1999 patch. If no patches have been applied, qimport prepends the patch
1996 to the series.
2000 to the series.
1997
2001
1998 The patch will have the same name as its source file unless you
2002 The patch will have the same name as its source file unless you
1999 give it a new one with -n/--name.
2003 give it a new one with -n/--name.
2000
2004
2001 You can register an existing patch inside the patch directory with
2005 You can register an existing patch inside the patch directory with
2002 the -e/--existing flag.
2006 the -e/--existing flag.
2003
2007
2004 With -f/--force, an existing patch of the same name will be
2008 With -f/--force, an existing patch of the same name will be
2005 overwritten.
2009 overwritten.
2006
2010
2007 An existing changeset may be placed under mq control with -r/--rev
2011 An existing changeset may be placed under mq control with -r/--rev
2008 (e.g. qimport --rev tip -n patch will place tip under mq control).
2012 (e.g. qimport --rev tip -n patch will place tip under mq control).
2009 With -g/--git, patches imported with --rev will use the git diff
2013 With -g/--git, patches imported with --rev will use the git diff
2010 format. See the diffs help topic for information on why this is
2014 format. See the diffs help topic for information on why this is
2011 important for preserving rename/copy information and permission
2015 important for preserving rename/copy information and permission
2012 changes. Use :hg:`qfinish` to remove changesets from mq control.
2016 changes. Use :hg:`qfinish` to remove changesets from mq control.
2013
2017
2014 To import a patch from standard input, pass - as the patch file.
2018 To import a patch from standard input, pass - as the patch file.
2015 When importing from standard input, a patch name must be specified
2019 When importing from standard input, a patch name must be specified
2016 using the --name flag.
2020 using the --name flag.
2017
2021
2018 To import an existing patch while renaming it::
2022 To import an existing patch while renaming it::
2019
2023
2020 hg qimport -e existing-patch -n new-name
2024 hg qimport -e existing-patch -n new-name
2021
2025
2022 Returns 0 if import succeeded.
2026 Returns 0 if import succeeded.
2023 """
2027 """
2024 lock = repo.lock() # cause this may move phase
2028 lock = repo.lock() # cause this may move phase
2025 try:
2029 try:
2026 q = repo.mq
2030 q = repo.mq
2027 try:
2031 try:
2028 q.qimport(repo, filename, patchname=opts.get('name'),
2032 q.qimport(repo, filename, patchname=opts.get('name'),
2029 existing=opts.get('existing'), force=opts.get('force'),
2033 existing=opts.get('existing'), force=opts.get('force'),
2030 rev=opts.get('rev'), git=opts.get('git'))
2034 rev=opts.get('rev'), git=opts.get('git'))
2031 finally:
2035 finally:
2032 q.savedirty()
2036 q.savedirty()
2033
2037
2034
2038
2035 if opts.get('push') and not opts.get('rev'):
2039 if opts.get('push') and not opts.get('rev'):
2036 return q.push(repo, None)
2040 return q.push(repo, None)
2037 finally:
2041 finally:
2038 lock.release()
2042 lock.release()
2039 return 0
2043 return 0
2040
2044
2041 def qinit(ui, repo, create):
2045 def qinit(ui, repo, create):
2042 """initialize a new queue repository
2046 """initialize a new queue repository
2043
2047
2044 This command also creates a series file for ordering patches, and
2048 This command also creates a series file for ordering patches, and
2045 an mq-specific .hgignore file in the queue repository, to exclude
2049 an mq-specific .hgignore file in the queue repository, to exclude
2046 the status and guards files (these contain mostly transient state).
2050 the status and guards files (these contain mostly transient state).
2047
2051
2048 Returns 0 if initialization succeeded."""
2052 Returns 0 if initialization succeeded."""
2049 q = repo.mq
2053 q = repo.mq
2050 r = q.init(repo, create)
2054 r = q.init(repo, create)
2051 q.savedirty()
2055 q.savedirty()
2052 if r:
2056 if r:
2053 if not os.path.exists(r.wjoin('.hgignore')):
2057 if not os.path.exists(r.wjoin('.hgignore')):
2054 fp = r.wopener('.hgignore', 'w')
2058 fp = r.wopener('.hgignore', 'w')
2055 fp.write('^\\.hg\n')
2059 fp.write('^\\.hg\n')
2056 fp.write('^\\.mq\n')
2060 fp.write('^\\.mq\n')
2057 fp.write('syntax: glob\n')
2061 fp.write('syntax: glob\n')
2058 fp.write('status\n')
2062 fp.write('status\n')
2059 fp.write('guards\n')
2063 fp.write('guards\n')
2060 fp.close()
2064 fp.close()
2061 if not os.path.exists(r.wjoin('series')):
2065 if not os.path.exists(r.wjoin('series')):
2062 r.wopener('series', 'w').close()
2066 r.wopener('series', 'w').close()
2063 r[None].add(['.hgignore', 'series'])
2067 r[None].add(['.hgignore', 'series'])
2064 commands.add(ui, r)
2068 commands.add(ui, r)
2065 return 0
2069 return 0
2066
2070
2067 @command("^qinit",
2071 @command("^qinit",
2068 [('c', 'create-repo', None, _('create queue repository'))],
2072 [('c', 'create-repo', None, _('create queue repository'))],
2069 _('hg qinit [-c]'))
2073 _('hg qinit [-c]'))
2070 def init(ui, repo, **opts):
2074 def init(ui, repo, **opts):
2071 """init a new queue repository (DEPRECATED)
2075 """init a new queue repository (DEPRECATED)
2072
2076
2073 The queue repository is unversioned by default. If
2077 The queue repository is unversioned by default. If
2074 -c/--create-repo is specified, qinit will create a separate nested
2078 -c/--create-repo is specified, qinit will create a separate nested
2075 repository for patches (qinit -c may also be run later to convert
2079 repository for patches (qinit -c may also be run later to convert
2076 an unversioned patch repository into a versioned one). You can use
2080 an unversioned patch repository into a versioned one). You can use
2077 qcommit to commit changes to this queue repository.
2081 qcommit to commit changes to this queue repository.
2078
2082
2079 This command is deprecated. Without -c, it's implied by other relevant
2083 This command is deprecated. Without -c, it's implied by other relevant
2080 commands. With -c, use :hg:`init --mq` instead."""
2084 commands. With -c, use :hg:`init --mq` instead."""
2081 return qinit(ui, repo, create=opts.get('create_repo'))
2085 return qinit(ui, repo, create=opts.get('create_repo'))
2082
2086
2083 @command("qclone",
2087 @command("qclone",
2084 [('', 'pull', None, _('use pull protocol to copy metadata')),
2088 [('', 'pull', None, _('use pull protocol to copy metadata')),
2085 ('U', 'noupdate', None, _('do not update the new working directories')),
2089 ('U', 'noupdate', None, _('do not update the new working directories')),
2086 ('', 'uncompressed', None,
2090 ('', 'uncompressed', None,
2087 _('use uncompressed transfer (fast over LAN)')),
2091 _('use uncompressed transfer (fast over LAN)')),
2088 ('p', 'patches', '',
2092 ('p', 'patches', '',
2089 _('location of source patch repository'), _('REPO')),
2093 _('location of source patch repository'), _('REPO')),
2090 ] + commands.remoteopts,
2094 ] + commands.remoteopts,
2091 _('hg qclone [OPTION]... SOURCE [DEST]'))
2095 _('hg qclone [OPTION]... SOURCE [DEST]'))
2092 def clone(ui, source, dest=None, **opts):
2096 def clone(ui, source, dest=None, **opts):
2093 '''clone main and patch repository at same time
2097 '''clone main and patch repository at same time
2094
2098
2095 If source is local, destination will have no patches applied. If
2099 If source is local, destination will have no patches applied. If
2096 source is remote, this command can not check if patches are
2100 source is remote, this command can not check if patches are
2097 applied in source, so cannot guarantee that patches are not
2101 applied in source, so cannot guarantee that patches are not
2098 applied in destination. If you clone remote repository, be sure
2102 applied in destination. If you clone remote repository, be sure
2099 before that it has no patches applied.
2103 before that it has no patches applied.
2100
2104
2101 Source patch repository is looked for in <src>/.hg/patches by
2105 Source patch repository is looked for in <src>/.hg/patches by
2102 default. Use -p <url> to change.
2106 default. Use -p <url> to change.
2103
2107
2104 The patch directory must be a nested Mercurial repository, as
2108 The patch directory must be a nested Mercurial repository, as
2105 would be created by :hg:`init --mq`.
2109 would be created by :hg:`init --mq`.
2106
2110
2107 Return 0 on success.
2111 Return 0 on success.
2108 '''
2112 '''
2109 def patchdir(repo):
2113 def patchdir(repo):
2110 """compute a patch repo url from a repo object"""
2114 """compute a patch repo url from a repo object"""
2111 url = repo.url()
2115 url = repo.url()
2112 if url.endswith('/'):
2116 if url.endswith('/'):
2113 url = url[:-1]
2117 url = url[:-1]
2114 return url + '/.hg/patches'
2118 return url + '/.hg/patches'
2115
2119
2116 # main repo (destination and sources)
2120 # main repo (destination and sources)
2117 if dest is None:
2121 if dest is None:
2118 dest = hg.defaultdest(source)
2122 dest = hg.defaultdest(source)
2119 sr = hg.repository(hg.remoteui(ui, opts), ui.expandpath(source))
2123 sr = hg.repository(hg.remoteui(ui, opts), ui.expandpath(source))
2120
2124
2121 # patches repo (source only)
2125 # patches repo (source only)
2122 if opts.get('patches'):
2126 if opts.get('patches'):
2123 patchespath = ui.expandpath(opts.get('patches'))
2127 patchespath = ui.expandpath(opts.get('patches'))
2124 else:
2128 else:
2125 patchespath = patchdir(sr)
2129 patchespath = patchdir(sr)
2126 try:
2130 try:
2127 hg.repository(ui, patchespath)
2131 hg.repository(ui, patchespath)
2128 except error.RepoError:
2132 except error.RepoError:
2129 raise util.Abort(_('versioned patch repository not found'
2133 raise util.Abort(_('versioned patch repository not found'
2130 ' (see init --mq)'))
2134 ' (see init --mq)'))
2131 qbase, destrev = None, None
2135 qbase, destrev = None, None
2132 if sr.local():
2136 if sr.local():
2133 if sr.mq.applied and sr[qbase].phase() != phases.secret:
2137 if sr.mq.applied and sr[qbase].phase() != phases.secret:
2134 qbase = sr.mq.applied[0].node
2138 qbase = sr.mq.applied[0].node
2135 if not hg.islocal(dest):
2139 if not hg.islocal(dest):
2136 heads = set(sr.heads())
2140 heads = set(sr.heads())
2137 destrev = list(heads.difference(sr.heads(qbase)))
2141 destrev = list(heads.difference(sr.heads(qbase)))
2138 destrev.append(sr.changelog.parents(qbase)[0])
2142 destrev.append(sr.changelog.parents(qbase)[0])
2139 elif sr.capable('lookup'):
2143 elif sr.capable('lookup'):
2140 try:
2144 try:
2141 qbase = sr.lookup('qbase')
2145 qbase = sr.lookup('qbase')
2142 except error.RepoError:
2146 except error.RepoError:
2143 pass
2147 pass
2144
2148
2145 ui.note(_('cloning main repository\n'))
2149 ui.note(_('cloning main repository\n'))
2146 sr, dr = hg.clone(ui, opts, sr.url(), dest,
2150 sr, dr = hg.clone(ui, opts, sr.url(), dest,
2147 pull=opts.get('pull'),
2151 pull=opts.get('pull'),
2148 rev=destrev,
2152 rev=destrev,
2149 update=False,
2153 update=False,
2150 stream=opts.get('uncompressed'))
2154 stream=opts.get('uncompressed'))
2151
2155
2152 ui.note(_('cloning patch repository\n'))
2156 ui.note(_('cloning patch repository\n'))
2153 hg.clone(ui, opts, opts.get('patches') or patchdir(sr), patchdir(dr),
2157 hg.clone(ui, opts, opts.get('patches') or patchdir(sr), patchdir(dr),
2154 pull=opts.get('pull'), update=not opts.get('noupdate'),
2158 pull=opts.get('pull'), update=not opts.get('noupdate'),
2155 stream=opts.get('uncompressed'))
2159 stream=opts.get('uncompressed'))
2156
2160
2157 if dr.local():
2161 if dr.local():
2158 if qbase:
2162 if qbase:
2159 ui.note(_('stripping applied patches from destination '
2163 ui.note(_('stripping applied patches from destination '
2160 'repository\n'))
2164 'repository\n'))
2161 dr.mq.strip(dr, [qbase], update=False, backup=None)
2165 dr.mq.strip(dr, [qbase], update=False, backup=None)
2162 if not opts.get('noupdate'):
2166 if not opts.get('noupdate'):
2163 ui.note(_('updating destination repository\n'))
2167 ui.note(_('updating destination repository\n'))
2164 hg.update(dr, dr.changelog.tip())
2168 hg.update(dr, dr.changelog.tip())
2165
2169
2166 @command("qcommit|qci",
2170 @command("qcommit|qci",
2167 commands.table["^commit|ci"][1],
2171 commands.table["^commit|ci"][1],
2168 _('hg qcommit [OPTION]... [FILE]...'))
2172 _('hg qcommit [OPTION]... [FILE]...'))
2169 def commit(ui, repo, *pats, **opts):
2173 def commit(ui, repo, *pats, **opts):
2170 """commit changes in the queue repository (DEPRECATED)
2174 """commit changes in the queue repository (DEPRECATED)
2171
2175
2172 This command is deprecated; use :hg:`commit --mq` instead."""
2176 This command is deprecated; use :hg:`commit --mq` instead."""
2173 q = repo.mq
2177 q = repo.mq
2174 r = q.qrepo()
2178 r = q.qrepo()
2175 if not r:
2179 if not r:
2176 raise util.Abort('no queue repository')
2180 raise util.Abort('no queue repository')
2177 commands.commit(r.ui, r, *pats, **opts)
2181 commands.commit(r.ui, r, *pats, **opts)
2178
2182
2179 @command("qseries",
2183 @command("qseries",
2180 [('m', 'missing', None, _('print patches not in series')),
2184 [('m', 'missing', None, _('print patches not in series')),
2181 ] + seriesopts,
2185 ] + seriesopts,
2182 _('hg qseries [-ms]'))
2186 _('hg qseries [-ms]'))
2183 def series(ui, repo, **opts):
2187 def series(ui, repo, **opts):
2184 """print the entire series file
2188 """print the entire series file
2185
2189
2186 Returns 0 on success."""
2190 Returns 0 on success."""
2187 repo.mq.qseries(repo, missing=opts.get('missing'), summary=opts.get('summary'))
2191 repo.mq.qseries(repo, missing=opts.get('missing'), summary=opts.get('summary'))
2188 return 0
2192 return 0
2189
2193
2190 @command("qtop", seriesopts, _('hg qtop [-s]'))
2194 @command("qtop", seriesopts, _('hg qtop [-s]'))
2191 def top(ui, repo, **opts):
2195 def top(ui, repo, **opts):
2192 """print the name of the current patch
2196 """print the name of the current patch
2193
2197
2194 Returns 0 on success."""
2198 Returns 0 on success."""
2195 q = repo.mq
2199 q = repo.mq
2196 t = q.applied and q.seriesend(True) or 0
2200 t = q.applied and q.seriesend(True) or 0
2197 if t:
2201 if t:
2198 q.qseries(repo, start=t - 1, length=1, status='A',
2202 q.qseries(repo, start=t - 1, length=1, status='A',
2199 summary=opts.get('summary'))
2203 summary=opts.get('summary'))
2200 else:
2204 else:
2201 ui.write(_("no patches applied\n"))
2205 ui.write(_("no patches applied\n"))
2202 return 1
2206 return 1
2203
2207
2204 @command("qnext", seriesopts, _('hg qnext [-s]'))
2208 @command("qnext", seriesopts, _('hg qnext [-s]'))
2205 def next(ui, repo, **opts):
2209 def next(ui, repo, **opts):
2206 """print the name of the next pushable patch
2210 """print the name of the next pushable patch
2207
2211
2208 Returns 0 on success."""
2212 Returns 0 on success."""
2209 q = repo.mq
2213 q = repo.mq
2210 end = q.seriesend()
2214 end = q.seriesend()
2211 if end == len(q.series):
2215 if end == len(q.series):
2212 ui.write(_("all patches applied\n"))
2216 ui.write(_("all patches applied\n"))
2213 return 1
2217 return 1
2214 q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
2218 q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
2215
2219
2216 @command("qprev", seriesopts, _('hg qprev [-s]'))
2220 @command("qprev", seriesopts, _('hg qprev [-s]'))
2217 def prev(ui, repo, **opts):
2221 def prev(ui, repo, **opts):
2218 """print the name of the previous applied patch
2222 """print the name of the previous applied patch
2219
2223
2220 Returns 0 on success."""
2224 Returns 0 on success."""
2221 q = repo.mq
2225 q = repo.mq
2222 l = len(q.applied)
2226 l = len(q.applied)
2223 if l == 1:
2227 if l == 1:
2224 ui.write(_("only one patch applied\n"))
2228 ui.write(_("only one patch applied\n"))
2225 return 1
2229 return 1
2226 if not l:
2230 if not l:
2227 ui.write(_("no patches applied\n"))
2231 ui.write(_("no patches applied\n"))
2228 return 1
2232 return 1
2229 idx = q.series.index(q.applied[-2].name)
2233 idx = q.series.index(q.applied[-2].name)
2230 q.qseries(repo, start=idx, length=1, status='A',
2234 q.qseries(repo, start=idx, length=1, status='A',
2231 summary=opts.get('summary'))
2235 summary=opts.get('summary'))
2232
2236
2233 def setupheaderopts(ui, opts):
2237 def setupheaderopts(ui, opts):
2234 if not opts.get('user') and opts.get('currentuser'):
2238 if not opts.get('user') and opts.get('currentuser'):
2235 opts['user'] = ui.username()
2239 opts['user'] = ui.username()
2236 if not opts.get('date') and opts.get('currentdate'):
2240 if not opts.get('date') and opts.get('currentdate'):
2237 opts['date'] = "%d %d" % util.makedate()
2241 opts['date'] = "%d %d" % util.makedate()
2238
2242
2239 @command("^qnew",
2243 @command("^qnew",
2240 [('e', 'edit', None, _('edit commit message')),
2244 [('e', 'edit', None, _('edit commit message')),
2241 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
2245 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
2242 ('g', 'git', None, _('use git extended diff format')),
2246 ('g', 'git', None, _('use git extended diff format')),
2243 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2247 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2244 ('u', 'user', '',
2248 ('u', 'user', '',
2245 _('add "From: <USER>" to patch'), _('USER')),
2249 _('add "From: <USER>" to patch'), _('USER')),
2246 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2250 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2247 ('d', 'date', '',
2251 ('d', 'date', '',
2248 _('add "Date: <DATE>" to patch'), _('DATE'))
2252 _('add "Date: <DATE>" to patch'), _('DATE'))
2249 ] + commands.walkopts + commands.commitopts,
2253 ] + commands.walkopts + commands.commitopts,
2250 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...'))
2254 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...'))
2251 def new(ui, repo, patch, *args, **opts):
2255 def new(ui, repo, patch, *args, **opts):
2252 """create a new patch
2256 """create a new patch
2253
2257
2254 qnew creates a new patch on top of the currently-applied patch (if
2258 qnew creates a new patch on top of the currently-applied patch (if
2255 any). The patch will be initialized with any outstanding changes
2259 any). The patch will be initialized with any outstanding changes
2256 in the working directory. You may also use -I/--include,
2260 in the working directory. You may also use -I/--include,
2257 -X/--exclude, and/or a list of files after the patch name to add
2261 -X/--exclude, and/or a list of files after the patch name to add
2258 only changes to matching files to the new patch, leaving the rest
2262 only changes to matching files to the new patch, leaving the rest
2259 as uncommitted modifications.
2263 as uncommitted modifications.
2260
2264
2261 -u/--user and -d/--date can be used to set the (given) user and
2265 -u/--user and -d/--date can be used to set the (given) user and
2262 date, respectively. -U/--currentuser and -D/--currentdate set user
2266 date, respectively. -U/--currentuser and -D/--currentdate set user
2263 to current user and date to current date.
2267 to current user and date to current date.
2264
2268
2265 -e/--edit, -m/--message or -l/--logfile set the patch header as
2269 -e/--edit, -m/--message or -l/--logfile set the patch header as
2266 well as the commit message. If none is specified, the header is
2270 well as the commit message. If none is specified, the header is
2267 empty and the commit message is '[mq]: PATCH'.
2271 empty and the commit message is '[mq]: PATCH'.
2268
2272
2269 Use the -g/--git option to keep the patch in the git extended diff
2273 Use the -g/--git option to keep the patch in the git extended diff
2270 format. Read the diffs help topic for more information on why this
2274 format. Read the diffs help topic for more information on why this
2271 is important for preserving permission changes and copy/rename
2275 is important for preserving permission changes and copy/rename
2272 information.
2276 information.
2273
2277
2274 Returns 0 on successful creation of a new patch.
2278 Returns 0 on successful creation of a new patch.
2275 """
2279 """
2276 msg = cmdutil.logmessage(ui, opts)
2280 msg = cmdutil.logmessage(ui, opts)
2277 def getmsg():
2281 def getmsg():
2278 return ui.edit(msg, opts.get('user') or ui.username())
2282 return ui.edit(msg, opts.get('user') or ui.username())
2279 q = repo.mq
2283 q = repo.mq
2280 opts['msg'] = msg
2284 opts['msg'] = msg
2281 if opts.get('edit'):
2285 if opts.get('edit'):
2282 opts['msg'] = getmsg
2286 opts['msg'] = getmsg
2283 else:
2287 else:
2284 opts['msg'] = msg
2288 opts['msg'] = msg
2285 setupheaderopts(ui, opts)
2289 setupheaderopts(ui, opts)
2286 q.new(repo, patch, *args, **opts)
2290 q.new(repo, patch, *args, **opts)
2287 q.savedirty()
2291 q.savedirty()
2288 return 0
2292 return 0
2289
2293
2290 @command("^qrefresh",
2294 @command("^qrefresh",
2291 [('e', 'edit', None, _('edit commit message')),
2295 [('e', 'edit', None, _('edit commit message')),
2292 ('g', 'git', None, _('use git extended diff format')),
2296 ('g', 'git', None, _('use git extended diff format')),
2293 ('s', 'short', None,
2297 ('s', 'short', None,
2294 _('refresh only files already in the patch and specified files')),
2298 _('refresh only files already in the patch and specified files')),
2295 ('U', 'currentuser', None,
2299 ('U', 'currentuser', None,
2296 _('add/update author field in patch with current user')),
2300 _('add/update author field in patch with current user')),
2297 ('u', 'user', '',
2301 ('u', 'user', '',
2298 _('add/update author field in patch with given user'), _('USER')),
2302 _('add/update author field in patch with given user'), _('USER')),
2299 ('D', 'currentdate', None,
2303 ('D', 'currentdate', None,
2300 _('add/update date field in patch with current date')),
2304 _('add/update date field in patch with current date')),
2301 ('d', 'date', '',
2305 ('d', 'date', '',
2302 _('add/update date field in patch with given date'), _('DATE'))
2306 _('add/update date field in patch with given date'), _('DATE'))
2303 ] + commands.walkopts + commands.commitopts,
2307 ] + commands.walkopts + commands.commitopts,
2304 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...'))
2308 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...'))
2305 def refresh(ui, repo, *pats, **opts):
2309 def refresh(ui, repo, *pats, **opts):
2306 """update the current patch
2310 """update the current patch
2307
2311
2308 If any file patterns are provided, the refreshed patch will
2312 If any file patterns are provided, the refreshed patch will
2309 contain only the modifications that match those patterns; the
2313 contain only the modifications that match those patterns; the
2310 remaining modifications will remain in the working directory.
2314 remaining modifications will remain in the working directory.
2311
2315
2312 If -s/--short is specified, files currently included in the patch
2316 If -s/--short is specified, files currently included in the patch
2313 will be refreshed just like matched files and remain in the patch.
2317 will be refreshed just like matched files and remain in the patch.
2314
2318
2315 If -e/--edit is specified, Mercurial will start your configured editor for
2319 If -e/--edit is specified, Mercurial will start your configured editor for
2316 you to enter a message. In case qrefresh fails, you will find a backup of
2320 you to enter a message. In case qrefresh fails, you will find a backup of
2317 your message in ``.hg/last-message.txt``.
2321 your message in ``.hg/last-message.txt``.
2318
2322
2319 hg add/remove/copy/rename work as usual, though you might want to
2323 hg add/remove/copy/rename work as usual, though you might want to
2320 use git-style patches (-g/--git or [diff] git=1) to track copies
2324 use git-style patches (-g/--git or [diff] git=1) to track copies
2321 and renames. See the diffs help topic for more information on the
2325 and renames. See the diffs help topic for more information on the
2322 git diff format.
2326 git diff format.
2323
2327
2324 Returns 0 on success.
2328 Returns 0 on success.
2325 """
2329 """
2326 q = repo.mq
2330 q = repo.mq
2327 message = cmdutil.logmessage(ui, opts)
2331 message = cmdutil.logmessage(ui, opts)
2328 if opts.get('edit'):
2332 if opts.get('edit'):
2329 if not q.applied:
2333 if not q.applied:
2330 ui.write(_("no patches applied\n"))
2334 ui.write(_("no patches applied\n"))
2331 return 1
2335 return 1
2332 if message:
2336 if message:
2333 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2337 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2334 patch = q.applied[-1].name
2338 patch = q.applied[-1].name
2335 ph = patchheader(q.join(patch), q.plainmode)
2339 ph = patchheader(q.join(patch), q.plainmode)
2336 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
2340 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
2337 # We don't want to lose the patch message if qrefresh fails (issue2062)
2341 # We don't want to lose the patch message if qrefresh fails (issue2062)
2338 repo.savecommitmessage(message)
2342 repo.savecommitmessage(message)
2339 setupheaderopts(ui, opts)
2343 setupheaderopts(ui, opts)
2340 wlock = repo.wlock()
2344 wlock = repo.wlock()
2341 try:
2345 try:
2342 ret = q.refresh(repo, pats, msg=message, **opts)
2346 ret = q.refresh(repo, pats, msg=message, **opts)
2343 q.savedirty()
2347 q.savedirty()
2344 return ret
2348 return ret
2345 finally:
2349 finally:
2346 wlock.release()
2350 wlock.release()
2347
2351
2348 @command("^qdiff",
2352 @command("^qdiff",
2349 commands.diffopts + commands.diffopts2 + commands.walkopts,
2353 commands.diffopts + commands.diffopts2 + commands.walkopts,
2350 _('hg qdiff [OPTION]... [FILE]...'))
2354 _('hg qdiff [OPTION]... [FILE]...'))
2351 def diff(ui, repo, *pats, **opts):
2355 def diff(ui, repo, *pats, **opts):
2352 """diff of the current patch and subsequent modifications
2356 """diff of the current patch and subsequent modifications
2353
2357
2354 Shows a diff which includes the current patch as well as any
2358 Shows a diff which includes the current patch as well as any
2355 changes which have been made in the working directory since the
2359 changes which have been made in the working directory since the
2356 last refresh (thus showing what the current patch would become
2360 last refresh (thus showing what the current patch would become
2357 after a qrefresh).
2361 after a qrefresh).
2358
2362
2359 Use :hg:`diff` if you only want to see the changes made since the
2363 Use :hg:`diff` if you only want to see the changes made since the
2360 last qrefresh, or :hg:`export qtip` if you want to see changes
2364 last qrefresh, or :hg:`export qtip` if you want to see changes
2361 made by the current patch without including changes made since the
2365 made by the current patch without including changes made since the
2362 qrefresh.
2366 qrefresh.
2363
2367
2364 Returns 0 on success.
2368 Returns 0 on success.
2365 """
2369 """
2366 repo.mq.diff(repo, pats, opts)
2370 repo.mq.diff(repo, pats, opts)
2367 return 0
2371 return 0
2368
2372
2369 @command('qfold',
2373 @command('qfold',
2370 [('e', 'edit', None, _('edit patch header')),
2374 [('e', 'edit', None, _('edit patch header')),
2371 ('k', 'keep', None, _('keep folded patch files')),
2375 ('k', 'keep', None, _('keep folded patch files')),
2372 ] + commands.commitopts,
2376 ] + commands.commitopts,
2373 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...'))
2377 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...'))
2374 def fold(ui, repo, *files, **opts):
2378 def fold(ui, repo, *files, **opts):
2375 """fold the named patches into the current patch
2379 """fold the named patches into the current patch
2376
2380
2377 Patches must not yet be applied. Each patch will be successively
2381 Patches must not yet be applied. Each patch will be successively
2378 applied to the current patch in the order given. If all the
2382 applied to the current patch in the order given. If all the
2379 patches apply successfully, the current patch will be refreshed
2383 patches apply successfully, the current patch will be refreshed
2380 with the new cumulative patch, and the folded patches will be
2384 with the new cumulative patch, and the folded patches will be
2381 deleted. With -k/--keep, the folded patch files will not be
2385 deleted. With -k/--keep, the folded patch files will not be
2382 removed afterwards.
2386 removed afterwards.
2383
2387
2384 The header for each folded patch will be concatenated with the
2388 The header for each folded patch will be concatenated with the
2385 current patch header, separated by a line of ``* * *``.
2389 current patch header, separated by a line of ``* * *``.
2386
2390
2387 Returns 0 on success."""
2391 Returns 0 on success."""
2388 q = repo.mq
2392 q = repo.mq
2389 if not files:
2393 if not files:
2390 raise util.Abort(_('qfold requires at least one patch name'))
2394 raise util.Abort(_('qfold requires at least one patch name'))
2391 if not q.checktoppatch(repo)[0]:
2395 if not q.checktoppatch(repo)[0]:
2392 raise util.Abort(_('no patches applied'))
2396 raise util.Abort(_('no patches applied'))
2393 q.checklocalchanges(repo)
2397 q.checklocalchanges(repo)
2394
2398
2395 message = cmdutil.logmessage(ui, opts)
2399 message = cmdutil.logmessage(ui, opts)
2396 if opts.get('edit'):
2400 if opts.get('edit'):
2397 if message:
2401 if message:
2398 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2402 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2399
2403
2400 parent = q.lookup('qtip')
2404 parent = q.lookup('qtip')
2401 patches = []
2405 patches = []
2402 messages = []
2406 messages = []
2403 for f in files:
2407 for f in files:
2404 p = q.lookup(f)
2408 p = q.lookup(f)
2405 if p in patches or p == parent:
2409 if p in patches or p == parent:
2406 ui.warn(_('Skipping already folded patch %s\n') % p)
2410 ui.warn(_('Skipping already folded patch %s\n') % p)
2407 if q.isapplied(p):
2411 if q.isapplied(p):
2408 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
2412 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
2409 patches.append(p)
2413 patches.append(p)
2410
2414
2411 for p in patches:
2415 for p in patches:
2412 if not message:
2416 if not message:
2413 ph = patchheader(q.join(p), q.plainmode)
2417 ph = patchheader(q.join(p), q.plainmode)
2414 if ph.message:
2418 if ph.message:
2415 messages.append(ph.message)
2419 messages.append(ph.message)
2416 pf = q.join(p)
2420 pf = q.join(p)
2417 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2421 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2418 if not patchsuccess:
2422 if not patchsuccess:
2419 raise util.Abort(_('error folding patch %s') % p)
2423 raise util.Abort(_('error folding patch %s') % p)
2420
2424
2421 if not message:
2425 if not message:
2422 ph = patchheader(q.join(parent), q.plainmode)
2426 ph = patchheader(q.join(parent), q.plainmode)
2423 message, user = ph.message, ph.user
2427 message, user = ph.message, ph.user
2424 for msg in messages:
2428 for msg in messages:
2425 message.append('* * *')
2429 message.append('* * *')
2426 message.extend(msg)
2430 message.extend(msg)
2427 message = '\n'.join(message)
2431 message = '\n'.join(message)
2428
2432
2429 if opts.get('edit'):
2433 if opts.get('edit'):
2430 message = ui.edit(message, user or ui.username())
2434 message = ui.edit(message, user or ui.username())
2431
2435
2432 diffopts = q.patchopts(q.diffopts(), *patches)
2436 diffopts = q.patchopts(q.diffopts(), *patches)
2433 wlock = repo.wlock()
2437 wlock = repo.wlock()
2434 try:
2438 try:
2435 q.refresh(repo, msg=message, git=diffopts.git)
2439 q.refresh(repo, msg=message, git=diffopts.git)
2436 q.delete(repo, patches, opts)
2440 q.delete(repo, patches, opts)
2437 q.savedirty()
2441 q.savedirty()
2438 finally:
2442 finally:
2439 wlock.release()
2443 wlock.release()
2440
2444
2441 @command("qgoto",
2445 @command("qgoto",
2442 [('f', 'force', None, _('overwrite any local changes'))],
2446 [('f', 'force', None, _('overwrite any local changes'))],
2443 _('hg qgoto [OPTION]... PATCH'))
2447 _('hg qgoto [OPTION]... PATCH'))
2444 def goto(ui, repo, patch, **opts):
2448 def goto(ui, repo, patch, **opts):
2445 '''push or pop patches until named patch is at top of stack
2449 '''push or pop patches until named patch is at top of stack
2446
2450
2447 Returns 0 on success.'''
2451 Returns 0 on success.'''
2448 q = repo.mq
2452 q = repo.mq
2449 patch = q.lookup(patch)
2453 patch = q.lookup(patch)
2450 if q.isapplied(patch):
2454 if q.isapplied(patch):
2451 ret = q.pop(repo, patch, force=opts.get('force'))
2455 ret = q.pop(repo, patch, force=opts.get('force'))
2452 else:
2456 else:
2453 ret = q.push(repo, patch, force=opts.get('force'))
2457 ret = q.push(repo, patch, force=opts.get('force'))
2454 q.savedirty()
2458 q.savedirty()
2455 return ret
2459 return ret
2456
2460
2457 @command("qguard",
2461 @command("qguard",
2458 [('l', 'list', None, _('list all patches and guards')),
2462 [('l', 'list', None, _('list all patches and guards')),
2459 ('n', 'none', None, _('drop all guards'))],
2463 ('n', 'none', None, _('drop all guards'))],
2460 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]'))
2464 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]'))
2461 def guard(ui, repo, *args, **opts):
2465 def guard(ui, repo, *args, **opts):
2462 '''set or print guards for a patch
2466 '''set or print guards for a patch
2463
2467
2464 Guards control whether a patch can be pushed. A patch with no
2468 Guards control whether a patch can be pushed. A patch with no
2465 guards is always pushed. A patch with a positive guard ("+foo") is
2469 guards is always pushed. A patch with a positive guard ("+foo") is
2466 pushed only if the :hg:`qselect` command has activated it. A patch with
2470 pushed only if the :hg:`qselect` command has activated it. A patch with
2467 a negative guard ("-foo") is never pushed if the :hg:`qselect` command
2471 a negative guard ("-foo") is never pushed if the :hg:`qselect` command
2468 has activated it.
2472 has activated it.
2469
2473
2470 With no arguments, print the currently active guards.
2474 With no arguments, print the currently active guards.
2471 With arguments, set guards for the named patch.
2475 With arguments, set guards for the named patch.
2472
2476
2473 .. note::
2477 .. note::
2474 Specifying negative guards now requires '--'.
2478 Specifying negative guards now requires '--'.
2475
2479
2476 To set guards on another patch::
2480 To set guards on another patch::
2477
2481
2478 hg qguard other.patch -- +2.6.17 -stable
2482 hg qguard other.patch -- +2.6.17 -stable
2479
2483
2480 Returns 0 on success.
2484 Returns 0 on success.
2481 '''
2485 '''
2482 def status(idx):
2486 def status(idx):
2483 guards = q.seriesguards[idx] or ['unguarded']
2487 guards = q.seriesguards[idx] or ['unguarded']
2484 if q.series[idx] in applied:
2488 if q.series[idx] in applied:
2485 state = 'applied'
2489 state = 'applied'
2486 elif q.pushable(idx)[0]:
2490 elif q.pushable(idx)[0]:
2487 state = 'unapplied'
2491 state = 'unapplied'
2488 else:
2492 else:
2489 state = 'guarded'
2493 state = 'guarded'
2490 label = 'qguard.patch qguard.%s qseries.%s' % (state, state)
2494 label = 'qguard.patch qguard.%s qseries.%s' % (state, state)
2491 ui.write('%s: ' % ui.label(q.series[idx], label))
2495 ui.write('%s: ' % ui.label(q.series[idx], label))
2492
2496
2493 for i, guard in enumerate(guards):
2497 for i, guard in enumerate(guards):
2494 if guard.startswith('+'):
2498 if guard.startswith('+'):
2495 ui.write(guard, label='qguard.positive')
2499 ui.write(guard, label='qguard.positive')
2496 elif guard.startswith('-'):
2500 elif guard.startswith('-'):
2497 ui.write(guard, label='qguard.negative')
2501 ui.write(guard, label='qguard.negative')
2498 else:
2502 else:
2499 ui.write(guard, label='qguard.unguarded')
2503 ui.write(guard, label='qguard.unguarded')
2500 if i != len(guards) - 1:
2504 if i != len(guards) - 1:
2501 ui.write(' ')
2505 ui.write(' ')
2502 ui.write('\n')
2506 ui.write('\n')
2503 q = repo.mq
2507 q = repo.mq
2504 applied = set(p.name for p in q.applied)
2508 applied = set(p.name for p in q.applied)
2505 patch = None
2509 patch = None
2506 args = list(args)
2510 args = list(args)
2507 if opts.get('list'):
2511 if opts.get('list'):
2508 if args or opts.get('none'):
2512 if args or opts.get('none'):
2509 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
2513 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
2510 for i in xrange(len(q.series)):
2514 for i in xrange(len(q.series)):
2511 status(i)
2515 status(i)
2512 return
2516 return
2513 if not args or args[0][0:1] in '-+':
2517 if not args or args[0][0:1] in '-+':
2514 if not q.applied:
2518 if not q.applied:
2515 raise util.Abort(_('no patches applied'))
2519 raise util.Abort(_('no patches applied'))
2516 patch = q.applied[-1].name
2520 patch = q.applied[-1].name
2517 if patch is None and args[0][0:1] not in '-+':
2521 if patch is None and args[0][0:1] not in '-+':
2518 patch = args.pop(0)
2522 patch = args.pop(0)
2519 if patch is None:
2523 if patch is None:
2520 raise util.Abort(_('no patch to work with'))
2524 raise util.Abort(_('no patch to work with'))
2521 if args or opts.get('none'):
2525 if args or opts.get('none'):
2522 idx = q.findseries(patch)
2526 idx = q.findseries(patch)
2523 if idx is None:
2527 if idx is None:
2524 raise util.Abort(_('no patch named %s') % patch)
2528 raise util.Abort(_('no patch named %s') % patch)
2525 q.setguards(idx, args)
2529 q.setguards(idx, args)
2526 q.savedirty()
2530 q.savedirty()
2527 else:
2531 else:
2528 status(q.series.index(q.lookup(patch)))
2532 status(q.series.index(q.lookup(patch)))
2529
2533
2530 @command("qheader", [], _('hg qheader [PATCH]'))
2534 @command("qheader", [], _('hg qheader [PATCH]'))
2531 def header(ui, repo, patch=None):
2535 def header(ui, repo, patch=None):
2532 """print the header of the topmost or specified patch
2536 """print the header of the topmost or specified patch
2533
2537
2534 Returns 0 on success."""
2538 Returns 0 on success."""
2535 q = repo.mq
2539 q = repo.mq
2536
2540
2537 if patch:
2541 if patch:
2538 patch = q.lookup(patch)
2542 patch = q.lookup(patch)
2539 else:
2543 else:
2540 if not q.applied:
2544 if not q.applied:
2541 ui.write(_('no patches applied\n'))
2545 ui.write(_('no patches applied\n'))
2542 return 1
2546 return 1
2543 patch = q.lookup('qtip')
2547 patch = q.lookup('qtip')
2544 ph = patchheader(q.join(patch), q.plainmode)
2548 ph = patchheader(q.join(patch), q.plainmode)
2545
2549
2546 ui.write('\n'.join(ph.message) + '\n')
2550 ui.write('\n'.join(ph.message) + '\n')
2547
2551
2548 def lastsavename(path):
2552 def lastsavename(path):
2549 (directory, base) = os.path.split(path)
2553 (directory, base) = os.path.split(path)
2550 names = os.listdir(directory)
2554 names = os.listdir(directory)
2551 namere = re.compile("%s.([0-9]+)" % base)
2555 namere = re.compile("%s.([0-9]+)" % base)
2552 maxindex = None
2556 maxindex = None
2553 maxname = None
2557 maxname = None
2554 for f in names:
2558 for f in names:
2555 m = namere.match(f)
2559 m = namere.match(f)
2556 if m:
2560 if m:
2557 index = int(m.group(1))
2561 index = int(m.group(1))
2558 if maxindex is None or index > maxindex:
2562 if maxindex is None or index > maxindex:
2559 maxindex = index
2563 maxindex = index
2560 maxname = f
2564 maxname = f
2561 if maxname:
2565 if maxname:
2562 return (os.path.join(directory, maxname), maxindex)
2566 return (os.path.join(directory, maxname), maxindex)
2563 return (None, None)
2567 return (None, None)
2564
2568
2565 def savename(path):
2569 def savename(path):
2566 (last, index) = lastsavename(path)
2570 (last, index) = lastsavename(path)
2567 if last is None:
2571 if last is None:
2568 index = 0
2572 index = 0
2569 newpath = path + ".%d" % (index + 1)
2573 newpath = path + ".%d" % (index + 1)
2570 return newpath
2574 return newpath
2571
2575
2572 @command("^qpush",
2576 @command("^qpush",
2573 [('f', 'force', None, _('apply on top of local changes')),
2577 [('f', 'force', None, _('apply on top of local changes')),
2574 ('e', 'exact', None, _('apply the target patch to its recorded parent')),
2578 ('e', 'exact', None, _('apply the target patch to its recorded parent')),
2575 ('l', 'list', None, _('list patch name in commit text')),
2579 ('l', 'list', None, _('list patch name in commit text')),
2576 ('a', 'all', None, _('apply all patches')),
2580 ('a', 'all', None, _('apply all patches')),
2577 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2581 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2578 ('n', 'name', '',
2582 ('n', 'name', '',
2579 _('merge queue name (DEPRECATED)'), _('NAME')),
2583 _('merge queue name (DEPRECATED)'), _('NAME')),
2580 ('', 'move', None, _('reorder patch series and apply only the patch'))],
2584 ('', 'move', None, _('reorder patch series and apply only the patch'))],
2581 _('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]'))
2585 _('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]'))
2582 def push(ui, repo, patch=None, **opts):
2586 def push(ui, repo, patch=None, **opts):
2583 """push the next patch onto the stack
2587 """push the next patch onto the stack
2584
2588
2585 When -f/--force is applied, all local changes in patched files
2589 When -f/--force is applied, all local changes in patched files
2586 will be lost.
2590 will be lost.
2587
2591
2588 Return 0 on success.
2592 Return 0 on success.
2589 """
2593 """
2590 q = repo.mq
2594 q = repo.mq
2591 mergeq = None
2595 mergeq = None
2592
2596
2593 if opts.get('merge'):
2597 if opts.get('merge'):
2594 if opts.get('name'):
2598 if opts.get('name'):
2595 newpath = repo.join(opts.get('name'))
2599 newpath = repo.join(opts.get('name'))
2596 else:
2600 else:
2597 newpath, i = lastsavename(q.path)
2601 newpath, i = lastsavename(q.path)
2598 if not newpath:
2602 if not newpath:
2599 ui.warn(_("no saved queues found, please use -n\n"))
2603 ui.warn(_("no saved queues found, please use -n\n"))
2600 return 1
2604 return 1
2601 mergeq = queue(ui, repo.path, newpath)
2605 mergeq = queue(ui, repo.path, newpath)
2602 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2606 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2603 ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
2607 ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
2604 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
2608 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
2605 exact=opts.get('exact'))
2609 exact=opts.get('exact'))
2606 return ret
2610 return ret
2607
2611
2608 @command("^qpop",
2612 @command("^qpop",
2609 [('a', 'all', None, _('pop all patches')),
2613 [('a', 'all', None, _('pop all patches')),
2610 ('n', 'name', '',
2614 ('n', 'name', '',
2611 _('queue name to pop (DEPRECATED)'), _('NAME')),
2615 _('queue name to pop (DEPRECATED)'), _('NAME')),
2612 ('f', 'force', None, _('forget any local changes to patched files'))],
2616 ('f', 'force', None, _('forget any local changes to patched files'))],
2613 _('hg qpop [-a] [-f] [PATCH | INDEX]'))
2617 _('hg qpop [-a] [-f] [PATCH | INDEX]'))
2614 def pop(ui, repo, patch=None, **opts):
2618 def pop(ui, repo, patch=None, **opts):
2615 """pop the current patch off the stack
2619 """pop the current patch off the stack
2616
2620
2617 By default, pops off the top of the patch stack. If given a patch
2621 By default, pops off the top of the patch stack. If given a patch
2618 name, keeps popping off patches until the named patch is at the
2622 name, keeps popping off patches until the named patch is at the
2619 top of the stack.
2623 top of the stack.
2620
2624
2621 Return 0 on success.
2625 Return 0 on success.
2622 """
2626 """
2623 localupdate = True
2627 localupdate = True
2624 if opts.get('name'):
2628 if opts.get('name'):
2625 q = queue(ui, repo.path, repo.join(opts.get('name')))
2629 q = queue(ui, repo.path, repo.join(opts.get('name')))
2626 ui.warn(_('using patch queue: %s\n') % q.path)
2630 ui.warn(_('using patch queue: %s\n') % q.path)
2627 localupdate = False
2631 localupdate = False
2628 else:
2632 else:
2629 q = repo.mq
2633 q = repo.mq
2630 ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate,
2634 ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate,
2631 all=opts.get('all'))
2635 all=opts.get('all'))
2632 q.savedirty()
2636 q.savedirty()
2633 return ret
2637 return ret
2634
2638
2635 @command("qrename|qmv", [], _('hg qrename PATCH1 [PATCH2]'))
2639 @command("qrename|qmv", [], _('hg qrename PATCH1 [PATCH2]'))
2636 def rename(ui, repo, patch, name=None, **opts):
2640 def rename(ui, repo, patch, name=None, **opts):
2637 """rename a patch
2641 """rename a patch
2638
2642
2639 With one argument, renames the current patch to PATCH1.
2643 With one argument, renames the current patch to PATCH1.
2640 With two arguments, renames PATCH1 to PATCH2.
2644 With two arguments, renames PATCH1 to PATCH2.
2641
2645
2642 Returns 0 on success."""
2646 Returns 0 on success."""
2643 q = repo.mq
2647 q = repo.mq
2644 if not name:
2648 if not name:
2645 name = patch
2649 name = patch
2646 patch = None
2650 patch = None
2647
2651
2648 if patch:
2652 if patch:
2649 patch = q.lookup(patch)
2653 patch = q.lookup(patch)
2650 else:
2654 else:
2651 if not q.applied:
2655 if not q.applied:
2652 ui.write(_('no patches applied\n'))
2656 ui.write(_('no patches applied\n'))
2653 return
2657 return
2654 patch = q.lookup('qtip')
2658 patch = q.lookup('qtip')
2655 absdest = q.join(name)
2659 absdest = q.join(name)
2656 if os.path.isdir(absdest):
2660 if os.path.isdir(absdest):
2657 name = normname(os.path.join(name, os.path.basename(patch)))
2661 name = normname(os.path.join(name, os.path.basename(patch)))
2658 absdest = q.join(name)
2662 absdest = q.join(name)
2659 q.checkpatchname(name)
2663 q.checkpatchname(name)
2660
2664
2661 ui.note(_('renaming %s to %s\n') % (patch, name))
2665 ui.note(_('renaming %s to %s\n') % (patch, name))
2662 i = q.findseries(patch)
2666 i = q.findseries(patch)
2663 guards = q.guard_re.findall(q.fullseries[i])
2667 guards = q.guard_re.findall(q.fullseries[i])
2664 q.fullseries[i] = name + ''.join([' #' + g for g in guards])
2668 q.fullseries[i] = name + ''.join([' #' + g for g in guards])
2665 q.parseseries()
2669 q.parseseries()
2666 q.seriesdirty = True
2670 q.seriesdirty = True
2667
2671
2668 info = q.isapplied(patch)
2672 info = q.isapplied(patch)
2669 if info:
2673 if info:
2670 q.applied[info[0]] = statusentry(info[1], name)
2674 q.applied[info[0]] = statusentry(info[1], name)
2671 q.applieddirty = True
2675 q.applieddirty = True
2672
2676
2673 destdir = os.path.dirname(absdest)
2677 destdir = os.path.dirname(absdest)
2674 if not os.path.isdir(destdir):
2678 if not os.path.isdir(destdir):
2675 os.makedirs(destdir)
2679 os.makedirs(destdir)
2676 util.rename(q.join(patch), absdest)
2680 util.rename(q.join(patch), absdest)
2677 r = q.qrepo()
2681 r = q.qrepo()
2678 if r and patch in r.dirstate:
2682 if r and patch in r.dirstate:
2679 wctx = r[None]
2683 wctx = r[None]
2680 wlock = r.wlock()
2684 wlock = r.wlock()
2681 try:
2685 try:
2682 if r.dirstate[patch] == 'a':
2686 if r.dirstate[patch] == 'a':
2683 r.dirstate.drop(patch)
2687 r.dirstate.drop(patch)
2684 r.dirstate.add(name)
2688 r.dirstate.add(name)
2685 else:
2689 else:
2686 wctx.copy(patch, name)
2690 wctx.copy(patch, name)
2687 wctx.forget([patch])
2691 wctx.forget([patch])
2688 finally:
2692 finally:
2689 wlock.release()
2693 wlock.release()
2690
2694
2691 q.savedirty()
2695 q.savedirty()
2692
2696
2693 @command("qrestore",
2697 @command("qrestore",
2694 [('d', 'delete', None, _('delete save entry')),
2698 [('d', 'delete', None, _('delete save entry')),
2695 ('u', 'update', None, _('update queue working directory'))],
2699 ('u', 'update', None, _('update queue working directory'))],
2696 _('hg qrestore [-d] [-u] REV'))
2700 _('hg qrestore [-d] [-u] REV'))
2697 def restore(ui, repo, rev, **opts):
2701 def restore(ui, repo, rev, **opts):
2698 """restore the queue state saved by a revision (DEPRECATED)
2702 """restore the queue state saved by a revision (DEPRECATED)
2699
2703
2700 This command is deprecated, use :hg:`rebase` instead."""
2704 This command is deprecated, use :hg:`rebase` instead."""
2701 rev = repo.lookup(rev)
2705 rev = repo.lookup(rev)
2702 q = repo.mq
2706 q = repo.mq
2703 q.restore(repo, rev, delete=opts.get('delete'),
2707 q.restore(repo, rev, delete=opts.get('delete'),
2704 qupdate=opts.get('update'))
2708 qupdate=opts.get('update'))
2705 q.savedirty()
2709 q.savedirty()
2706 return 0
2710 return 0
2707
2711
2708 @command("qsave",
2712 @command("qsave",
2709 [('c', 'copy', None, _('copy patch directory')),
2713 [('c', 'copy', None, _('copy patch directory')),
2710 ('n', 'name', '',
2714 ('n', 'name', '',
2711 _('copy directory name'), _('NAME')),
2715 _('copy directory name'), _('NAME')),
2712 ('e', 'empty', None, _('clear queue status file')),
2716 ('e', 'empty', None, _('clear queue status file')),
2713 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2717 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2714 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'))
2718 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'))
2715 def save(ui, repo, **opts):
2719 def save(ui, repo, **opts):
2716 """save current queue state (DEPRECATED)
2720 """save current queue state (DEPRECATED)
2717
2721
2718 This command is deprecated, use :hg:`rebase` instead."""
2722 This command is deprecated, use :hg:`rebase` instead."""
2719 q = repo.mq
2723 q = repo.mq
2720 message = cmdutil.logmessage(ui, opts)
2724 message = cmdutil.logmessage(ui, opts)
2721 ret = q.save(repo, msg=message)
2725 ret = q.save(repo, msg=message)
2722 if ret:
2726 if ret:
2723 return ret
2727 return ret
2724 q.savedirty() # save to .hg/patches before copying
2728 q.savedirty() # save to .hg/patches before copying
2725 if opts.get('copy'):
2729 if opts.get('copy'):
2726 path = q.path
2730 path = q.path
2727 if opts.get('name'):
2731 if opts.get('name'):
2728 newpath = os.path.join(q.basepath, opts.get('name'))
2732 newpath = os.path.join(q.basepath, opts.get('name'))
2729 if os.path.exists(newpath):
2733 if os.path.exists(newpath):
2730 if not os.path.isdir(newpath):
2734 if not os.path.isdir(newpath):
2731 raise util.Abort(_('destination %s exists and is not '
2735 raise util.Abort(_('destination %s exists and is not '
2732 'a directory') % newpath)
2736 'a directory') % newpath)
2733 if not opts.get('force'):
2737 if not opts.get('force'):
2734 raise util.Abort(_('destination %s exists, '
2738 raise util.Abort(_('destination %s exists, '
2735 'use -f to force') % newpath)
2739 'use -f to force') % newpath)
2736 else:
2740 else:
2737 newpath = savename(path)
2741 newpath = savename(path)
2738 ui.warn(_("copy %s to %s\n") % (path, newpath))
2742 ui.warn(_("copy %s to %s\n") % (path, newpath))
2739 util.copyfiles(path, newpath)
2743 util.copyfiles(path, newpath)
2740 if opts.get('empty'):
2744 if opts.get('empty'):
2741 del q.applied[:]
2745 del q.applied[:]
2742 q.applieddirty = True
2746 q.applieddirty = True
2743 q.savedirty()
2747 q.savedirty()
2744 return 0
2748 return 0
2745
2749
2746 @command("strip",
2750 @command("strip",
2747 [
2751 [
2748 ('r', 'rev', [], _('strip specified revision (optional, '
2752 ('r', 'rev', [], _('strip specified revision (optional, '
2749 'can specify revisions without this '
2753 'can specify revisions without this '
2750 'option)'), _('REV')),
2754 'option)'), _('REV')),
2751 ('f', 'force', None, _('force removal of changesets, discard '
2755 ('f', 'force', None, _('force removal of changesets, discard '
2752 'uncommitted changes (no backup)')),
2756 'uncommitted changes (no backup)')),
2753 ('b', 'backup', None, _('bundle only changesets with local revision'
2757 ('b', 'backup', None, _('bundle only changesets with local revision'
2754 ' number greater than REV which are not'
2758 ' number greater than REV which are not'
2755 ' descendants of REV (DEPRECATED)')),
2759 ' descendants of REV (DEPRECATED)')),
2756 ('n', 'no-backup', None, _('no backups')),
2760 ('n', 'no-backup', None, _('no backups')),
2757 ('', 'nobackup', None, _('no backups (DEPRECATED)')),
2761 ('', 'nobackup', None, _('no backups (DEPRECATED)')),
2758 ('k', 'keep', None, _("do not modify working copy during strip"))],
2762 ('k', 'keep', None, _("do not modify working copy during strip"))],
2759 _('hg strip [-k] [-f] [-n] REV...'))
2763 _('hg strip [-k] [-f] [-n] REV...'))
2760 def strip(ui, repo, *revs, **opts):
2764 def strip(ui, repo, *revs, **opts):
2761 """strip changesets and all their descendants from the repository
2765 """strip changesets and all their descendants from the repository
2762
2766
2763 The strip command removes the specified changesets and all their
2767 The strip command removes the specified changesets and all their
2764 descendants. If the working directory has uncommitted changes, the
2768 descendants. If the working directory has uncommitted changes, the
2765 operation is aborted unless the --force flag is supplied, in which
2769 operation is aborted unless the --force flag is supplied, in which
2766 case changes will be discarded.
2770 case changes will be discarded.
2767
2771
2768 If a parent of the working directory is stripped, then the working
2772 If a parent of the working directory is stripped, then the working
2769 directory will automatically be updated to the most recent
2773 directory will automatically be updated to the most recent
2770 available ancestor of the stripped parent after the operation
2774 available ancestor of the stripped parent after the operation
2771 completes.
2775 completes.
2772
2776
2773 Any stripped changesets are stored in ``.hg/strip-backup`` as a
2777 Any stripped changesets are stored in ``.hg/strip-backup`` as a
2774 bundle (see :hg:`help bundle` and :hg:`help unbundle`). They can
2778 bundle (see :hg:`help bundle` and :hg:`help unbundle`). They can
2775 be restored by running :hg:`unbundle .hg/strip-backup/BUNDLE`,
2779 be restored by running :hg:`unbundle .hg/strip-backup/BUNDLE`,
2776 where BUNDLE is the bundle file created by the strip. Note that
2780 where BUNDLE is the bundle file created by the strip. Note that
2777 the local revision numbers will in general be different after the
2781 the local revision numbers will in general be different after the
2778 restore.
2782 restore.
2779
2783
2780 Use the --no-backup option to discard the backup bundle once the
2784 Use the --no-backup option to discard the backup bundle once the
2781 operation completes.
2785 operation completes.
2782
2786
2783 Return 0 on success.
2787 Return 0 on success.
2784 """
2788 """
2785 backup = 'all'
2789 backup = 'all'
2786 if opts.get('backup'):
2790 if opts.get('backup'):
2787 backup = 'strip'
2791 backup = 'strip'
2788 elif opts.get('no_backup') or opts.get('nobackup'):
2792 elif opts.get('no_backup') or opts.get('nobackup'):
2789 backup = 'none'
2793 backup = 'none'
2790
2794
2791 cl = repo.changelog
2795 cl = repo.changelog
2792 revs = list(revs) + opts.get('rev')
2796 revs = list(revs) + opts.get('rev')
2793 revs = set(scmutil.revrange(repo, revs))
2797 revs = set(scmutil.revrange(repo, revs))
2794 if not revs:
2798 if not revs:
2795 raise util.Abort(_('empty revision set'))
2799 raise util.Abort(_('empty revision set'))
2796
2800
2797 descendants = set(cl.descendants(*revs))
2801 descendants = set(cl.descendants(*revs))
2798 strippedrevs = revs.union(descendants)
2802 strippedrevs = revs.union(descendants)
2799 roots = revs.difference(descendants)
2803 roots = revs.difference(descendants)
2800
2804
2801 update = False
2805 update = False
2802 # if one of the wdir parent is stripped we'll need
2806 # if one of the wdir parent is stripped we'll need
2803 # to update away to an earlier revision
2807 # to update away to an earlier revision
2804 for p in repo.dirstate.parents():
2808 for p in repo.dirstate.parents():
2805 if p != nullid and cl.rev(p) in strippedrevs:
2809 if p != nullid and cl.rev(p) in strippedrevs:
2806 update = True
2810 update = True
2807 break
2811 break
2808
2812
2809 rootnodes = set(cl.node(r) for r in roots)
2813 rootnodes = set(cl.node(r) for r in roots)
2810
2814
2811 q = repo.mq
2815 q = repo.mq
2812 if q.applied:
2816 if q.applied:
2813 # refresh queue state if we're about to strip
2817 # refresh queue state if we're about to strip
2814 # applied patches
2818 # applied patches
2815 if cl.rev(repo.lookup('qtip')) in strippedrevs:
2819 if cl.rev(repo.lookup('qtip')) in strippedrevs:
2816 q.applieddirty = True
2820 q.applieddirty = True
2817 start = 0
2821 start = 0
2818 end = len(q.applied)
2822 end = len(q.applied)
2819 for i, statusentry in enumerate(q.applied):
2823 for i, statusentry in enumerate(q.applied):
2820 if statusentry.node in rootnodes:
2824 if statusentry.node in rootnodes:
2821 # if one of the stripped roots is an applied
2825 # if one of the stripped roots is an applied
2822 # patch, only part of the queue is stripped
2826 # patch, only part of the queue is stripped
2823 start = i
2827 start = i
2824 break
2828 break
2825 del q.applied[start:end]
2829 del q.applied[start:end]
2826 q.savedirty()
2830 q.savedirty()
2827
2831
2828 revs = list(rootnodes)
2832 revs = list(rootnodes)
2829 if update and opts.get('keep'):
2833 if update and opts.get('keep'):
2830 wlock = repo.wlock()
2834 wlock = repo.wlock()
2831 try:
2835 try:
2832 urev = repo.mq.qparents(repo, revs[0])
2836 urev = repo.mq.qparents(repo, revs[0])
2833 repo.dirstate.rebuild(urev, repo[urev].manifest())
2837 repo.dirstate.rebuild(urev, repo[urev].manifest())
2834 repo.dirstate.write()
2838 repo.dirstate.write()
2835 update = False
2839 update = False
2836 finally:
2840 finally:
2837 wlock.release()
2841 wlock.release()
2838
2842
2839 repo.mq.strip(repo, revs, backup=backup, update=update,
2843 repo.mq.strip(repo, revs, backup=backup, update=update,
2840 force=opts.get('force'))
2844 force=opts.get('force'))
2841 return 0
2845 return 0
2842
2846
2843 @command("qselect",
2847 @command("qselect",
2844 [('n', 'none', None, _('disable all guards')),
2848 [('n', 'none', None, _('disable all guards')),
2845 ('s', 'series', None, _('list all guards in series file')),
2849 ('s', 'series', None, _('list all guards in series file')),
2846 ('', 'pop', None, _('pop to before first guarded applied patch')),
2850 ('', 'pop', None, _('pop to before first guarded applied patch')),
2847 ('', 'reapply', None, _('pop, then reapply patches'))],
2851 ('', 'reapply', None, _('pop, then reapply patches'))],
2848 _('hg qselect [OPTION]... [GUARD]...'))
2852 _('hg qselect [OPTION]... [GUARD]...'))
2849 def select(ui, repo, *args, **opts):
2853 def select(ui, repo, *args, **opts):
2850 '''set or print guarded patches to push
2854 '''set or print guarded patches to push
2851
2855
2852 Use the :hg:`qguard` command to set or print guards on patch, then use
2856 Use the :hg:`qguard` command to set or print guards on patch, then use
2853 qselect to tell mq which guards to use. A patch will be pushed if
2857 qselect to tell mq which guards to use. A patch will be pushed if
2854 it has no guards or any positive guards match the currently
2858 it has no guards or any positive guards match the currently
2855 selected guard, but will not be pushed if any negative guards
2859 selected guard, but will not be pushed if any negative guards
2856 match the current guard. For example::
2860 match the current guard. For example::
2857
2861
2858 qguard foo.patch -- -stable (negative guard)
2862 qguard foo.patch -- -stable (negative guard)
2859 qguard bar.patch +stable (positive guard)
2863 qguard bar.patch +stable (positive guard)
2860 qselect stable
2864 qselect stable
2861
2865
2862 This activates the "stable" guard. mq will skip foo.patch (because
2866 This activates the "stable" guard. mq will skip foo.patch (because
2863 it has a negative match) but push bar.patch (because it has a
2867 it has a negative match) but push bar.patch (because it has a
2864 positive match).
2868 positive match).
2865
2869
2866 With no arguments, prints the currently active guards.
2870 With no arguments, prints the currently active guards.
2867 With one argument, sets the active guard.
2871 With one argument, sets the active guard.
2868
2872
2869 Use -n/--none to deactivate guards (no other arguments needed).
2873 Use -n/--none to deactivate guards (no other arguments needed).
2870 When no guards are active, patches with positive guards are
2874 When no guards are active, patches with positive guards are
2871 skipped and patches with negative guards are pushed.
2875 skipped and patches with negative guards are pushed.
2872
2876
2873 qselect can change the guards on applied patches. It does not pop
2877 qselect can change the guards on applied patches. It does not pop
2874 guarded patches by default. Use --pop to pop back to the last
2878 guarded patches by default. Use --pop to pop back to the last
2875 applied patch that is not guarded. Use --reapply (which implies
2879 applied patch that is not guarded. Use --reapply (which implies
2876 --pop) to push back to the current patch afterwards, but skip
2880 --pop) to push back to the current patch afterwards, but skip
2877 guarded patches.
2881 guarded patches.
2878
2882
2879 Use -s/--series to print a list of all guards in the series file
2883 Use -s/--series to print a list of all guards in the series file
2880 (no other arguments needed). Use -v for more information.
2884 (no other arguments needed). Use -v for more information.
2881
2885
2882 Returns 0 on success.'''
2886 Returns 0 on success.'''
2883
2887
2884 q = repo.mq
2888 q = repo.mq
2885 guards = q.active()
2889 guards = q.active()
2886 if args or opts.get('none'):
2890 if args or opts.get('none'):
2887 old_unapplied = q.unapplied(repo)
2891 old_unapplied = q.unapplied(repo)
2888 old_guarded = [i for i in xrange(len(q.applied)) if
2892 old_guarded = [i for i in xrange(len(q.applied)) if
2889 not q.pushable(i)[0]]
2893 not q.pushable(i)[0]]
2890 q.setactive(args)
2894 q.setactive(args)
2891 q.savedirty()
2895 q.savedirty()
2892 if not args:
2896 if not args:
2893 ui.status(_('guards deactivated\n'))
2897 ui.status(_('guards deactivated\n'))
2894 if not opts.get('pop') and not opts.get('reapply'):
2898 if not opts.get('pop') and not opts.get('reapply'):
2895 unapplied = q.unapplied(repo)
2899 unapplied = q.unapplied(repo)
2896 guarded = [i for i in xrange(len(q.applied))
2900 guarded = [i for i in xrange(len(q.applied))
2897 if not q.pushable(i)[0]]
2901 if not q.pushable(i)[0]]
2898 if len(unapplied) != len(old_unapplied):
2902 if len(unapplied) != len(old_unapplied):
2899 ui.status(_('number of unguarded, unapplied patches has '
2903 ui.status(_('number of unguarded, unapplied patches has '
2900 'changed from %d to %d\n') %
2904 'changed from %d to %d\n') %
2901 (len(old_unapplied), len(unapplied)))
2905 (len(old_unapplied), len(unapplied)))
2902 if len(guarded) != len(old_guarded):
2906 if len(guarded) != len(old_guarded):
2903 ui.status(_('number of guarded, applied patches has changed '
2907 ui.status(_('number of guarded, applied patches has changed '
2904 'from %d to %d\n') %
2908 'from %d to %d\n') %
2905 (len(old_guarded), len(guarded)))
2909 (len(old_guarded), len(guarded)))
2906 elif opts.get('series'):
2910 elif opts.get('series'):
2907 guards = {}
2911 guards = {}
2908 noguards = 0
2912 noguards = 0
2909 for gs in q.seriesguards:
2913 for gs in q.seriesguards:
2910 if not gs:
2914 if not gs:
2911 noguards += 1
2915 noguards += 1
2912 for g in gs:
2916 for g in gs:
2913 guards.setdefault(g, 0)
2917 guards.setdefault(g, 0)
2914 guards[g] += 1
2918 guards[g] += 1
2915 if ui.verbose:
2919 if ui.verbose:
2916 guards['NONE'] = noguards
2920 guards['NONE'] = noguards
2917 guards = guards.items()
2921 guards = guards.items()
2918 guards.sort(key=lambda x: x[0][1:])
2922 guards.sort(key=lambda x: x[0][1:])
2919 if guards:
2923 if guards:
2920 ui.note(_('guards in series file:\n'))
2924 ui.note(_('guards in series file:\n'))
2921 for guard, count in guards:
2925 for guard, count in guards:
2922 ui.note('%2d ' % count)
2926 ui.note('%2d ' % count)
2923 ui.write(guard, '\n')
2927 ui.write(guard, '\n')
2924 else:
2928 else:
2925 ui.note(_('no guards in series file\n'))
2929 ui.note(_('no guards in series file\n'))
2926 else:
2930 else:
2927 if guards:
2931 if guards:
2928 ui.note(_('active guards:\n'))
2932 ui.note(_('active guards:\n'))
2929 for g in guards:
2933 for g in guards:
2930 ui.write(g, '\n')
2934 ui.write(g, '\n')
2931 else:
2935 else:
2932 ui.write(_('no active guards\n'))
2936 ui.write(_('no active guards\n'))
2933 reapply = opts.get('reapply') and q.applied and q.appliedname(-1)
2937 reapply = opts.get('reapply') and q.applied and q.appliedname(-1)
2934 popped = False
2938 popped = False
2935 if opts.get('pop') or opts.get('reapply'):
2939 if opts.get('pop') or opts.get('reapply'):
2936 for i in xrange(len(q.applied)):
2940 for i in xrange(len(q.applied)):
2937 pushable, reason = q.pushable(i)
2941 pushable, reason = q.pushable(i)
2938 if not pushable:
2942 if not pushable:
2939 ui.status(_('popping guarded patches\n'))
2943 ui.status(_('popping guarded patches\n'))
2940 popped = True
2944 popped = True
2941 if i == 0:
2945 if i == 0:
2942 q.pop(repo, all=True)
2946 q.pop(repo, all=True)
2943 else:
2947 else:
2944 q.pop(repo, str(i - 1))
2948 q.pop(repo, str(i - 1))
2945 break
2949 break
2946 if popped:
2950 if popped:
2947 try:
2951 try:
2948 if reapply:
2952 if reapply:
2949 ui.status(_('reapplying unguarded patches\n'))
2953 ui.status(_('reapplying unguarded patches\n'))
2950 q.push(repo, reapply)
2954 q.push(repo, reapply)
2951 finally:
2955 finally:
2952 q.savedirty()
2956 q.savedirty()
2953
2957
2954 @command("qfinish",
2958 @command("qfinish",
2955 [('a', 'applied', None, _('finish all applied changesets'))],
2959 [('a', 'applied', None, _('finish all applied changesets'))],
2956 _('hg qfinish [-a] [REV]...'))
2960 _('hg qfinish [-a] [REV]...'))
2957 def finish(ui, repo, *revrange, **opts):
2961 def finish(ui, repo, *revrange, **opts):
2958 """move applied patches into repository history
2962 """move applied patches into repository history
2959
2963
2960 Finishes the specified revisions (corresponding to applied
2964 Finishes the specified revisions (corresponding to applied
2961 patches) by moving them out of mq control into regular repository
2965 patches) by moving them out of mq control into regular repository
2962 history.
2966 history.
2963
2967
2964 Accepts a revision range or the -a/--applied option. If --applied
2968 Accepts a revision range or the -a/--applied option. If --applied
2965 is specified, all applied mq revisions are removed from mq
2969 is specified, all applied mq revisions are removed from mq
2966 control. Otherwise, the given revisions must be at the base of the
2970 control. Otherwise, the given revisions must be at the base of the
2967 stack of applied patches.
2971 stack of applied patches.
2968
2972
2969 This can be especially useful if your changes have been applied to
2973 This can be especially useful if your changes have been applied to
2970 an upstream repository, or if you are about to push your changes
2974 an upstream repository, or if you are about to push your changes
2971 to upstream.
2975 to upstream.
2972
2976
2973 Returns 0 on success.
2977 Returns 0 on success.
2974 """
2978 """
2975 if not opts.get('applied') and not revrange:
2979 if not opts.get('applied') and not revrange:
2976 raise util.Abort(_('no revisions specified'))
2980 raise util.Abort(_('no revisions specified'))
2977 elif opts.get('applied'):
2981 elif opts.get('applied'):
2978 revrange = ('qbase::qtip',) + revrange
2982 revrange = ('qbase::qtip',) + revrange
2979
2983
2980 q = repo.mq
2984 q = repo.mq
2981 if not q.applied:
2985 if not q.applied:
2982 ui.status(_('no patches applied\n'))
2986 ui.status(_('no patches applied\n'))
2983 return 0
2987 return 0
2984
2988
2985 revs = scmutil.revrange(repo, revrange)
2989 revs = scmutil.revrange(repo, revrange)
2986 if repo['.'].rev() in revs and repo[None].files():
2990 if repo['.'].rev() in revs and repo[None].files():
2987 ui.warn(_('warning: uncommitted changes in the working directory\n'))
2991 ui.warn(_('warning: uncommitted changes in the working directory\n'))
2988 # queue.finish may changes phases but leave the responsability to lock the
2992 # queue.finish may changes phases but leave the responsability to lock the
2989 # repo to the caller to avoid deadlock with wlock. This command code is
2993 # repo to the caller to avoid deadlock with wlock. This command code is
2990 # responsability for this locking.
2994 # responsability for this locking.
2991 lock = repo.lock()
2995 lock = repo.lock()
2992 try:
2996 try:
2993 q.finish(repo, revs)
2997 q.finish(repo, revs)
2994 q.savedirty()
2998 q.savedirty()
2995 finally:
2999 finally:
2996 lock.release()
3000 lock.release()
2997 return 0
3001 return 0
2998
3002
2999 @command("qqueue",
3003 @command("qqueue",
3000 [('l', 'list', False, _('list all available queues')),
3004 [('l', 'list', False, _('list all available queues')),
3001 ('', 'active', False, _('print name of active queue')),
3005 ('', 'active', False, _('print name of active queue')),
3002 ('c', 'create', False, _('create new queue')),
3006 ('c', 'create', False, _('create new queue')),
3003 ('', 'rename', False, _('rename active queue')),
3007 ('', 'rename', False, _('rename active queue')),
3004 ('', 'delete', False, _('delete reference to queue')),
3008 ('', 'delete', False, _('delete reference to queue')),
3005 ('', 'purge', False, _('delete queue, and remove patch dir')),
3009 ('', 'purge', False, _('delete queue, and remove patch dir')),
3006 ],
3010 ],
3007 _('[OPTION] [QUEUE]'))
3011 _('[OPTION] [QUEUE]'))
3008 def qqueue(ui, repo, name=None, **opts):
3012 def qqueue(ui, repo, name=None, **opts):
3009 '''manage multiple patch queues
3013 '''manage multiple patch queues
3010
3014
3011 Supports switching between different patch queues, as well as creating
3015 Supports switching between different patch queues, as well as creating
3012 new patch queues and deleting existing ones.
3016 new patch queues and deleting existing ones.
3013
3017
3014 Omitting a queue name or specifying -l/--list will show you the registered
3018 Omitting a queue name or specifying -l/--list will show you the registered
3015 queues - by default the "normal" patches queue is registered. The currently
3019 queues - by default the "normal" patches queue is registered. The currently
3016 active queue will be marked with "(active)". Specifying --active will print
3020 active queue will be marked with "(active)". Specifying --active will print
3017 only the name of the active queue.
3021 only the name of the active queue.
3018
3022
3019 To create a new queue, use -c/--create. The queue is automatically made
3023 To create a new queue, use -c/--create. The queue is automatically made
3020 active, except in the case where there are applied patches from the
3024 active, except in the case where there are applied patches from the
3021 currently active queue in the repository. Then the queue will only be
3025 currently active queue in the repository. Then the queue will only be
3022 created and switching will fail.
3026 created and switching will fail.
3023
3027
3024 To delete an existing queue, use --delete. You cannot delete the currently
3028 To delete an existing queue, use --delete. You cannot delete the currently
3025 active queue.
3029 active queue.
3026
3030
3027 Returns 0 on success.
3031 Returns 0 on success.
3028 '''
3032 '''
3029 q = repo.mq
3033 q = repo.mq
3030 _defaultqueue = 'patches'
3034 _defaultqueue = 'patches'
3031 _allqueues = 'patches.queues'
3035 _allqueues = 'patches.queues'
3032 _activequeue = 'patches.queue'
3036 _activequeue = 'patches.queue'
3033
3037
3034 def _getcurrent():
3038 def _getcurrent():
3035 cur = os.path.basename(q.path)
3039 cur = os.path.basename(q.path)
3036 if cur.startswith('patches-'):
3040 if cur.startswith('patches-'):
3037 cur = cur[8:]
3041 cur = cur[8:]
3038 return cur
3042 return cur
3039
3043
3040 def _noqueues():
3044 def _noqueues():
3041 try:
3045 try:
3042 fh = repo.opener(_allqueues, 'r')
3046 fh = repo.opener(_allqueues, 'r')
3043 fh.close()
3047 fh.close()
3044 except IOError:
3048 except IOError:
3045 return True
3049 return True
3046
3050
3047 return False
3051 return False
3048
3052
3049 def _getqueues():
3053 def _getqueues():
3050 current = _getcurrent()
3054 current = _getcurrent()
3051
3055
3052 try:
3056 try:
3053 fh = repo.opener(_allqueues, 'r')
3057 fh = repo.opener(_allqueues, 'r')
3054 queues = [queue.strip() for queue in fh if queue.strip()]
3058 queues = [queue.strip() for queue in fh if queue.strip()]
3055 fh.close()
3059 fh.close()
3056 if current not in queues:
3060 if current not in queues:
3057 queues.append(current)
3061 queues.append(current)
3058 except IOError:
3062 except IOError:
3059 queues = [_defaultqueue]
3063 queues = [_defaultqueue]
3060
3064
3061 return sorted(queues)
3065 return sorted(queues)
3062
3066
3063 def _setactive(name):
3067 def _setactive(name):
3064 if q.applied:
3068 if q.applied:
3065 raise util.Abort(_('patches applied - cannot set new queue active'))
3069 raise util.Abort(_('patches applied - cannot set new queue active'))
3066 _setactivenocheck(name)
3070 _setactivenocheck(name)
3067
3071
3068 def _setactivenocheck(name):
3072 def _setactivenocheck(name):
3069 fh = repo.opener(_activequeue, 'w')
3073 fh = repo.opener(_activequeue, 'w')
3070 if name != 'patches':
3074 if name != 'patches':
3071 fh.write(name)
3075 fh.write(name)
3072 fh.close()
3076 fh.close()
3073
3077
3074 def _addqueue(name):
3078 def _addqueue(name):
3075 fh = repo.opener(_allqueues, 'a')
3079 fh = repo.opener(_allqueues, 'a')
3076 fh.write('%s\n' % (name,))
3080 fh.write('%s\n' % (name,))
3077 fh.close()
3081 fh.close()
3078
3082
3079 def _queuedir(name):
3083 def _queuedir(name):
3080 if name == 'patches':
3084 if name == 'patches':
3081 return repo.join('patches')
3085 return repo.join('patches')
3082 else:
3086 else:
3083 return repo.join('patches-' + name)
3087 return repo.join('patches-' + name)
3084
3088
3085 def _validname(name):
3089 def _validname(name):
3086 for n in name:
3090 for n in name:
3087 if n in ':\\/.':
3091 if n in ':\\/.':
3088 return False
3092 return False
3089 return True
3093 return True
3090
3094
3091 def _delete(name):
3095 def _delete(name):
3092 if name not in existing:
3096 if name not in existing:
3093 raise util.Abort(_('cannot delete queue that does not exist'))
3097 raise util.Abort(_('cannot delete queue that does not exist'))
3094
3098
3095 current = _getcurrent()
3099 current = _getcurrent()
3096
3100
3097 if name == current:
3101 if name == current:
3098 raise util.Abort(_('cannot delete currently active queue'))
3102 raise util.Abort(_('cannot delete currently active queue'))
3099
3103
3100 fh = repo.opener('patches.queues.new', 'w')
3104 fh = repo.opener('patches.queues.new', 'w')
3101 for queue in existing:
3105 for queue in existing:
3102 if queue == name:
3106 if queue == name:
3103 continue
3107 continue
3104 fh.write('%s\n' % (queue,))
3108 fh.write('%s\n' % (queue,))
3105 fh.close()
3109 fh.close()
3106 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3110 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3107
3111
3108 if not name or opts.get('list') or opts.get('active'):
3112 if not name or opts.get('list') or opts.get('active'):
3109 current = _getcurrent()
3113 current = _getcurrent()
3110 if opts.get('active'):
3114 if opts.get('active'):
3111 ui.write('%s\n' % (current,))
3115 ui.write('%s\n' % (current,))
3112 return
3116 return
3113 for queue in _getqueues():
3117 for queue in _getqueues():
3114 ui.write('%s' % (queue,))
3118 ui.write('%s' % (queue,))
3115 if queue == current and not ui.quiet:
3119 if queue == current and not ui.quiet:
3116 ui.write(_(' (active)\n'))
3120 ui.write(_(' (active)\n'))
3117 else:
3121 else:
3118 ui.write('\n')
3122 ui.write('\n')
3119 return
3123 return
3120
3124
3121 if not _validname(name):
3125 if not _validname(name):
3122 raise util.Abort(
3126 raise util.Abort(
3123 _('invalid queue name, may not contain the characters ":\\/."'))
3127 _('invalid queue name, may not contain the characters ":\\/."'))
3124
3128
3125 existing = _getqueues()
3129 existing = _getqueues()
3126
3130
3127 if opts.get('create'):
3131 if opts.get('create'):
3128 if name in existing:
3132 if name in existing:
3129 raise util.Abort(_('queue "%s" already exists') % name)
3133 raise util.Abort(_('queue "%s" already exists') % name)
3130 if _noqueues():
3134 if _noqueues():
3131 _addqueue(_defaultqueue)
3135 _addqueue(_defaultqueue)
3132 _addqueue(name)
3136 _addqueue(name)
3133 _setactive(name)
3137 _setactive(name)
3134 elif opts.get('rename'):
3138 elif opts.get('rename'):
3135 current = _getcurrent()
3139 current = _getcurrent()
3136 if name == current:
3140 if name == current:
3137 raise util.Abort(_('can\'t rename "%s" to its current name') % name)
3141 raise util.Abort(_('can\'t rename "%s" to its current name') % name)
3138 if name in existing:
3142 if name in existing:
3139 raise util.Abort(_('queue "%s" already exists') % name)
3143 raise util.Abort(_('queue "%s" already exists') % name)
3140
3144
3141 olddir = _queuedir(current)
3145 olddir = _queuedir(current)
3142 newdir = _queuedir(name)
3146 newdir = _queuedir(name)
3143
3147
3144 if os.path.exists(newdir):
3148 if os.path.exists(newdir):
3145 raise util.Abort(_('non-queue directory "%s" already exists') %
3149 raise util.Abort(_('non-queue directory "%s" already exists') %
3146 newdir)
3150 newdir)
3147
3151
3148 fh = repo.opener('patches.queues.new', 'w')
3152 fh = repo.opener('patches.queues.new', 'w')
3149 for queue in existing:
3153 for queue in existing:
3150 if queue == current:
3154 if queue == current:
3151 fh.write('%s\n' % (name,))
3155 fh.write('%s\n' % (name,))
3152 if os.path.exists(olddir):
3156 if os.path.exists(olddir):
3153 util.rename(olddir, newdir)
3157 util.rename(olddir, newdir)
3154 else:
3158 else:
3155 fh.write('%s\n' % (queue,))
3159 fh.write('%s\n' % (queue,))
3156 fh.close()
3160 fh.close()
3157 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3161 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3158 _setactivenocheck(name)
3162 _setactivenocheck(name)
3159 elif opts.get('delete'):
3163 elif opts.get('delete'):
3160 _delete(name)
3164 _delete(name)
3161 elif opts.get('purge'):
3165 elif opts.get('purge'):
3162 if name in existing:
3166 if name in existing:
3163 _delete(name)
3167 _delete(name)
3164 qdir = _queuedir(name)
3168 qdir = _queuedir(name)
3165 if os.path.exists(qdir):
3169 if os.path.exists(qdir):
3166 shutil.rmtree(qdir)
3170 shutil.rmtree(qdir)
3167 else:
3171 else:
3168 if name not in existing:
3172 if name not in existing:
3169 raise util.Abort(_('use --create to create a new queue'))
3173 raise util.Abort(_('use --create to create a new queue'))
3170 _setactive(name)
3174 _setactive(name)
3171
3175
3172 def mqphasedefaults(repo, roots):
3176 def mqphasedefaults(repo, roots):
3173 """callback used to set mq changeset as secret when no phase data exists"""
3177 """callback used to set mq changeset as secret when no phase data exists"""
3174 if repo.mq.applied:
3178 if repo.mq.applied:
3175 if repo.ui.configbool('mq', 'secret', False):
3179 if repo.ui.configbool('mq', 'secret', False):
3176 mqphase = phases.secret
3180 mqphase = phases.secret
3177 else:
3181 else:
3178 mqphase = phases.draft
3182 mqphase = phases.draft
3179 qbase = repo[repo.mq.applied[0].node]
3183 qbase = repo[repo.mq.applied[0].node]
3180 roots[mqphase].add(qbase.node())
3184 roots[mqphase].add(qbase.node())
3181 return roots
3185 return roots
3182
3186
3183 def reposetup(ui, repo):
3187 def reposetup(ui, repo):
3184 class mqrepo(repo.__class__):
3188 class mqrepo(repo.__class__):
3185 @util.propertycache
3189 @util.propertycache
3186 def mq(self):
3190 def mq(self):
3187 return queue(self.ui, self.path)
3191 return queue(self.ui, self.path)
3188
3192
3189 def abortifwdirpatched(self, errmsg, force=False):
3193 def abortifwdirpatched(self, errmsg, force=False):
3190 if self.mq.applied and not force:
3194 if self.mq.applied and not force:
3191 parents = self.dirstate.parents()
3195 parents = self.dirstate.parents()
3192 patches = [s.node for s in self.mq.applied]
3196 patches = [s.node for s in self.mq.applied]
3193 if parents[0] in patches or parents[1] in patches:
3197 if parents[0] in patches or parents[1] in patches:
3194 raise util.Abort(errmsg)
3198 raise util.Abort(errmsg)
3195
3199
3196 def commit(self, text="", user=None, date=None, match=None,
3200 def commit(self, text="", user=None, date=None, match=None,
3197 force=False, editor=False, extra={}):
3201 force=False, editor=False, extra={}):
3198 self.abortifwdirpatched(
3202 self.abortifwdirpatched(
3199 _('cannot commit over an applied mq patch'),
3203 _('cannot commit over an applied mq patch'),
3200 force)
3204 force)
3201
3205
3202 return super(mqrepo, self).commit(text, user, date, match, force,
3206 return super(mqrepo, self).commit(text, user, date, match, force,
3203 editor, extra)
3207 editor, extra)
3204
3208
3205 def checkpush(self, force, revs):
3209 def checkpush(self, force, revs):
3206 if self.mq.applied and not force:
3210 if self.mq.applied and not force:
3207 outapplied = [e.node for e in self.mq.applied]
3211 outapplied = [e.node for e in self.mq.applied]
3208 if revs:
3212 if revs:
3209 # Assume applied patches have no non-patch descendants and
3213 # Assume applied patches have no non-patch descendants and
3210 # are not on remote already. Filtering any changeset not
3214 # are not on remote already. Filtering any changeset not
3211 # pushed.
3215 # pushed.
3212 heads = set(revs)
3216 heads = set(revs)
3213 for node in reversed(outapplied):
3217 for node in reversed(outapplied):
3214 if node in heads:
3218 if node in heads:
3215 break
3219 break
3216 else:
3220 else:
3217 outapplied.pop()
3221 outapplied.pop()
3218 # looking for pushed and shared changeset
3222 # looking for pushed and shared changeset
3219 for node in outapplied:
3223 for node in outapplied:
3220 if repo[node].phase() < phases.secret:
3224 if repo[node].phase() < phases.secret:
3221 raise util.Abort(_('source has mq patches applied'))
3225 raise util.Abort(_('source has mq patches applied'))
3222 # no non-secret patches pushed
3226 # no non-secret patches pushed
3223 super(mqrepo, self).checkpush(force, revs)
3227 super(mqrepo, self).checkpush(force, revs)
3224
3228
3225 def _findtags(self):
3229 def _findtags(self):
3226 '''augment tags from base class with patch tags'''
3230 '''augment tags from base class with patch tags'''
3227 result = super(mqrepo, self)._findtags()
3231 result = super(mqrepo, self)._findtags()
3228
3232
3229 q = self.mq
3233 q = self.mq
3230 if not q.applied:
3234 if not q.applied:
3231 return result
3235 return result
3232
3236
3233 mqtags = [(patch.node, patch.name) for patch in q.applied]
3237 mqtags = [(patch.node, patch.name) for patch in q.applied]
3234
3238
3235 try:
3239 try:
3236 self.changelog.rev(mqtags[-1][0])
3240 self.changelog.rev(mqtags[-1][0])
3237 except error.LookupError:
3241 except error.LookupError:
3238 self.ui.warn(_('mq status file refers to unknown node %s\n')
3242 self.ui.warn(_('mq status file refers to unknown node %s\n')
3239 % short(mqtags[-1][0]))
3243 % short(mqtags[-1][0]))
3240 return result
3244 return result
3241
3245
3242 mqtags.append((mqtags[-1][0], 'qtip'))
3246 mqtags.append((mqtags[-1][0], 'qtip'))
3243 mqtags.append((mqtags[0][0], 'qbase'))
3247 mqtags.append((mqtags[0][0], 'qbase'))
3244 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
3248 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
3245 tags = result[0]
3249 tags = result[0]
3246 for patch in mqtags:
3250 for patch in mqtags:
3247 if patch[1] in tags:
3251 if patch[1] in tags:
3248 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
3252 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
3249 % patch[1])
3253 % patch[1])
3250 else:
3254 else:
3251 tags[patch[1]] = patch[0]
3255 tags[patch[1]] = patch[0]
3252
3256
3253 return result
3257 return result
3254
3258
3255 def _branchtags(self, partial, lrev):
3259 def _branchtags(self, partial, lrev):
3256 q = self.mq
3260 q = self.mq
3261 cl = self.changelog
3262 qbase = None
3257 if not q.applied:
3263 if not q.applied:
3258 return super(mqrepo, self)._branchtags(partial, lrev)
3264 if getattr(self, '_committingpatch', False):
3259
3265 # Committing a new patch, must be tip
3260 cl = self.changelog
3266 qbase = len(cl) - 1
3261 qbasenode = q.applied[0].node
3267 else:
3262 try:
3268 qbasenode = q.applied[0].node
3263 qbase = cl.rev(qbasenode)
3269 try:
3264 except error.LookupError:
3270 qbase = cl.rev(qbasenode)
3265 self.ui.warn(_('mq status file refers to unknown node %s\n')
3271 except error.LookupError:
3266 % short(qbasenode))
3272 self.ui.warn(_('mq status file refers to unknown node %s\n')
3273 % short(qbasenode))
3274 if qbase is None:
3267 return super(mqrepo, self)._branchtags(partial, lrev)
3275 return super(mqrepo, self)._branchtags(partial, lrev)
3268
3276
3269 start = lrev + 1
3277 start = lrev + 1
3270 if start < qbase:
3278 if start < qbase:
3271 # update the cache (excluding the patches) and save it
3279 # update the cache (excluding the patches) and save it
3272 ctxgen = (self[r] for r in xrange(lrev + 1, qbase))
3280 ctxgen = (self[r] for r in xrange(lrev + 1, qbase))
3273 self._updatebranchcache(partial, ctxgen)
3281 self._updatebranchcache(partial, ctxgen)
3274 self._writebranchcache(partial, cl.node(qbase - 1), qbase - 1)
3282 self._writebranchcache(partial, cl.node(qbase - 1), qbase - 1)
3275 start = qbase
3283 start = qbase
3276 # if start = qbase, the cache is as updated as it should be.
3284 # if start = qbase, the cache is as updated as it should be.
3277 # if start > qbase, the cache includes (part of) the patches.
3285 # if start > qbase, the cache includes (part of) the patches.
3278 # we might as well use it, but we won't save it.
3286 # we might as well use it, but we won't save it.
3279
3287
3280 # update the cache up to the tip
3288 # update the cache up to the tip
3281 ctxgen = (self[r] for r in xrange(start, len(cl)))
3289 ctxgen = (self[r] for r in xrange(start, len(cl)))
3282 self._updatebranchcache(partial, ctxgen)
3290 self._updatebranchcache(partial, ctxgen)
3283
3291
3284 return partial
3292 return partial
3285
3293
3286 if repo.local():
3294 if repo.local():
3287 repo.__class__ = mqrepo
3295 repo.__class__ = mqrepo
3288
3296
3289 repo._phasedefaults.append(mqphasedefaults)
3297 repo._phasedefaults.append(mqphasedefaults)
3290
3298
3291 def mqimport(orig, ui, repo, *args, **kwargs):
3299 def mqimport(orig, ui, repo, *args, **kwargs):
3292 if (hasattr(repo, 'abortifwdirpatched')
3300 if (hasattr(repo, 'abortifwdirpatched')
3293 and not kwargs.get('no_commit', False)):
3301 and not kwargs.get('no_commit', False)):
3294 repo.abortifwdirpatched(_('cannot import over an applied patch'),
3302 repo.abortifwdirpatched(_('cannot import over an applied patch'),
3295 kwargs.get('force'))
3303 kwargs.get('force'))
3296 return orig(ui, repo, *args, **kwargs)
3304 return orig(ui, repo, *args, **kwargs)
3297
3305
3298 def mqinit(orig, ui, *args, **kwargs):
3306 def mqinit(orig, ui, *args, **kwargs):
3299 mq = kwargs.pop('mq', None)
3307 mq = kwargs.pop('mq', None)
3300
3308
3301 if not mq:
3309 if not mq:
3302 return orig(ui, *args, **kwargs)
3310 return orig(ui, *args, **kwargs)
3303
3311
3304 if args:
3312 if args:
3305 repopath = args[0]
3313 repopath = args[0]
3306 if not hg.islocal(repopath):
3314 if not hg.islocal(repopath):
3307 raise util.Abort(_('only a local queue repository '
3315 raise util.Abort(_('only a local queue repository '
3308 'may be initialized'))
3316 'may be initialized'))
3309 else:
3317 else:
3310 repopath = cmdutil.findrepo(os.getcwd())
3318 repopath = cmdutil.findrepo(os.getcwd())
3311 if not repopath:
3319 if not repopath:
3312 raise util.Abort(_('there is no Mercurial repository here '
3320 raise util.Abort(_('there is no Mercurial repository here '
3313 '(.hg not found)'))
3321 '(.hg not found)'))
3314 repo = hg.repository(ui, repopath)
3322 repo = hg.repository(ui, repopath)
3315 return qinit(ui, repo, True)
3323 return qinit(ui, repo, True)
3316
3324
3317 def mqcommand(orig, ui, repo, *args, **kwargs):
3325 def mqcommand(orig, ui, repo, *args, **kwargs):
3318 """Add --mq option to operate on patch repository instead of main"""
3326 """Add --mq option to operate on patch repository instead of main"""
3319
3327
3320 # some commands do not like getting unknown options
3328 # some commands do not like getting unknown options
3321 mq = kwargs.pop('mq', None)
3329 mq = kwargs.pop('mq', None)
3322
3330
3323 if not mq:
3331 if not mq:
3324 return orig(ui, repo, *args, **kwargs)
3332 return orig(ui, repo, *args, **kwargs)
3325
3333
3326 q = repo.mq
3334 q = repo.mq
3327 r = q.qrepo()
3335 r = q.qrepo()
3328 if not r:
3336 if not r:
3329 raise util.Abort(_('no queue repository'))
3337 raise util.Abort(_('no queue repository'))
3330 return orig(r.ui, r, *args, **kwargs)
3338 return orig(r.ui, r, *args, **kwargs)
3331
3339
3332 def summary(orig, ui, repo, *args, **kwargs):
3340 def summary(orig, ui, repo, *args, **kwargs):
3333 r = orig(ui, repo, *args, **kwargs)
3341 r = orig(ui, repo, *args, **kwargs)
3334 q = repo.mq
3342 q = repo.mq
3335 m = []
3343 m = []
3336 a, u = len(q.applied), len(q.unapplied(repo))
3344 a, u = len(q.applied), len(q.unapplied(repo))
3337 if a:
3345 if a:
3338 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
3346 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
3339 if u:
3347 if u:
3340 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
3348 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
3341 if m:
3349 if m:
3342 ui.write("mq: %s\n" % ', '.join(m))
3350 ui.write("mq: %s\n" % ', '.join(m))
3343 else:
3351 else:
3344 ui.note(_("mq: (empty queue)\n"))
3352 ui.note(_("mq: (empty queue)\n"))
3345 return r
3353 return r
3346
3354
3347 def revsetmq(repo, subset, x):
3355 def revsetmq(repo, subset, x):
3348 """``mq()``
3356 """``mq()``
3349 Changesets managed by MQ.
3357 Changesets managed by MQ.
3350 """
3358 """
3351 revset.getargs(x, 0, 0, _("mq takes no arguments"))
3359 revset.getargs(x, 0, 0, _("mq takes no arguments"))
3352 applied = set([repo[r.node].rev() for r in repo.mq.applied])
3360 applied = set([repo[r.node].rev() for r in repo.mq.applied])
3353 return [r for r in subset if r in applied]
3361 return [r for r in subset if r in applied]
3354
3362
3355 def extsetup(ui):
3363 def extsetup(ui):
3356 revset.symbols['mq'] = revsetmq
3364 revset.symbols['mq'] = revsetmq
3357
3365
3358 # tell hggettext to extract docstrings from these functions:
3366 # tell hggettext to extract docstrings from these functions:
3359 i18nfunctions = [revsetmq]
3367 i18nfunctions = [revsetmq]
3360
3368
3361 def uisetup(ui):
3369 def uisetup(ui):
3362 mqopt = [('', 'mq', None, _("operate on patch repository"))]
3370 mqopt = [('', 'mq', None, _("operate on patch repository"))]
3363
3371
3364 extensions.wrapcommand(commands.table, 'import', mqimport)
3372 extensions.wrapcommand(commands.table, 'import', mqimport)
3365 extensions.wrapcommand(commands.table, 'summary', summary)
3373 extensions.wrapcommand(commands.table, 'summary', summary)
3366
3374
3367 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
3375 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
3368 entry[1].extend(mqopt)
3376 entry[1].extend(mqopt)
3369
3377
3370 nowrap = set(commands.norepo.split(" "))
3378 nowrap = set(commands.norepo.split(" "))
3371
3379
3372 def dotable(cmdtable):
3380 def dotable(cmdtable):
3373 for cmd in cmdtable.keys():
3381 for cmd in cmdtable.keys():
3374 cmd = cmdutil.parsealiases(cmd)[0]
3382 cmd = cmdutil.parsealiases(cmd)[0]
3375 if cmd in nowrap:
3383 if cmd in nowrap:
3376 continue
3384 continue
3377 entry = extensions.wrapcommand(cmdtable, cmd, mqcommand)
3385 entry = extensions.wrapcommand(cmdtable, cmd, mqcommand)
3378 entry[1].extend(mqopt)
3386 entry[1].extend(mqopt)
3379
3387
3380 dotable(commands.table)
3388 dotable(commands.table)
3381
3389
3382 for extname, extmodule in extensions.extensions():
3390 for extname, extmodule in extensions.extensions():
3383 if extmodule.__file__ != __file__:
3391 if extmodule.__file__ != __file__:
3384 dotable(getattr(extmodule, 'cmdtable', {}))
3392 dotable(getattr(extmodule, 'cmdtable', {}))
3385
3393
3386
3394
3387 colortable = {'qguard.negative': 'red',
3395 colortable = {'qguard.negative': 'red',
3388 'qguard.positive': 'yellow',
3396 'qguard.positive': 'yellow',
3389 'qguard.unguarded': 'green',
3397 'qguard.unguarded': 'green',
3390 'qseries.applied': 'blue bold underline',
3398 'qseries.applied': 'blue bold underline',
3391 'qseries.guarded': 'black bold',
3399 'qseries.guarded': 'black bold',
3392 'qseries.missing': 'red bold',
3400 'qseries.missing': 'red bold',
3393 'qseries.unapplied': 'black bold'}
3401 'qseries.unapplied': 'black bold'}
@@ -1,1108 +1,1107 b''
1 $ "$TESTDIR/hghave" symlink unix-permissions serve || exit 80
1 $ "$TESTDIR/hghave" symlink unix-permissions serve || exit 80
2
2
3 $ cat <<EOF >> $HGRCPATH
3 $ cat <<EOF >> $HGRCPATH
4 > [extensions]
4 > [extensions]
5 > keyword =
5 > keyword =
6 > mq =
6 > mq =
7 > notify =
7 > notify =
8 > record =
8 > record =
9 > transplant =
9 > transplant =
10 > [ui]
10 > [ui]
11 > interactive = true
11 > interactive = true
12 > EOF
12 > EOF
13
13
14 Run kwdemo before [keyword] files are set up
14 Run kwdemo before [keyword] files are set up
15 as it would succeed without uisetup otherwise
15 as it would succeed without uisetup otherwise
16
16
17 $ hg --quiet kwdemo
17 $ hg --quiet kwdemo
18 [extensions]
18 [extensions]
19 keyword =
19 keyword =
20 [keyword]
20 [keyword]
21 demo.txt =
21 demo.txt =
22 [keywordset]
22 [keywordset]
23 svn = False
23 svn = False
24 [keywordmaps]
24 [keywordmaps]
25 Author = {author|user}
25 Author = {author|user}
26 Date = {date|utcdate}
26 Date = {date|utcdate}
27 Header = {root}/{file},v {node|short} {date|utcdate} {author|user}
27 Header = {root}/{file},v {node|short} {date|utcdate} {author|user}
28 Id = {file|basename},v {node|short} {date|utcdate} {author|user}
28 Id = {file|basename},v {node|short} {date|utcdate} {author|user}
29 RCSFile = {file|basename},v
29 RCSFile = {file|basename},v
30 RCSfile = {file|basename},v
30 RCSfile = {file|basename},v
31 Revision = {node|short}
31 Revision = {node|short}
32 Source = {root}/{file},v
32 Source = {root}/{file},v
33 $Author: test $
33 $Author: test $
34 $Date: ????/??/?? ??:??:?? $ (glob)
34 $Date: ????/??/?? ??:??:?? $ (glob)
35 $Header: */demo.txt,v ???????????? ????/??/?? ??:??:?? test $ (glob)
35 $Header: */demo.txt,v ???????????? ????/??/?? ??:??:?? test $ (glob)
36 $Id: demo.txt,v ???????????? ????/??/?? ??:??:?? test $ (glob)
36 $Id: demo.txt,v ???????????? ????/??/?? ??:??:?? test $ (glob)
37 $RCSFile: demo.txt,v $
37 $RCSFile: demo.txt,v $
38 $RCSfile: demo.txt,v $
38 $RCSfile: demo.txt,v $
39 $Revision: ???????????? $ (glob)
39 $Revision: ???????????? $ (glob)
40 $Source: */demo.txt,v $ (glob)
40 $Source: */demo.txt,v $ (glob)
41
41
42 $ hg --quiet kwdemo "Branch = {branches}"
42 $ hg --quiet kwdemo "Branch = {branches}"
43 [extensions]
43 [extensions]
44 keyword =
44 keyword =
45 [keyword]
45 [keyword]
46 demo.txt =
46 demo.txt =
47 [keywordset]
47 [keywordset]
48 svn = False
48 svn = False
49 [keywordmaps]
49 [keywordmaps]
50 Branch = {branches}
50 Branch = {branches}
51 $Branch: demobranch $
51 $Branch: demobranch $
52
52
53 $ cat <<EOF >> $HGRCPATH
53 $ cat <<EOF >> $HGRCPATH
54 > [keyword]
54 > [keyword]
55 > ** =
55 > ** =
56 > b = ignore
56 > b = ignore
57 > i = ignore
57 > i = ignore
58 > [hooks]
58 > [hooks]
59 > EOF
59 > EOF
60 $ cp $HGRCPATH $HGRCPATH.nohooks
60 $ cp $HGRCPATH $HGRCPATH.nohooks
61 > cat <<EOF >> $HGRCPATH
61 > cat <<EOF >> $HGRCPATH
62 > commit=
62 > commit=
63 > commit.test=cp a hooktest
63 > commit.test=cp a hooktest
64 > EOF
64 > EOF
65
65
66 $ hg init Test-bndl
66 $ hg init Test-bndl
67 $ cd Test-bndl
67 $ cd Test-bndl
68
68
69 kwshrink should exit silently in empty/invalid repo
69 kwshrink should exit silently in empty/invalid repo
70
70
71 $ hg kwshrink
71 $ hg kwshrink
72
72
73 Symlinks cannot be created on Windows.
73 Symlinks cannot be created on Windows.
74 A bundle to test this was made with:
74 A bundle to test this was made with:
75 hg init t
75 hg init t
76 cd t
76 cd t
77 echo a > a
77 echo a > a
78 ln -s a sym
78 ln -s a sym
79 hg add sym
79 hg add sym
80 hg ci -m addsym -u mercurial
80 hg ci -m addsym -u mercurial
81 hg bundle --base null ../test-keyword.hg
81 hg bundle --base null ../test-keyword.hg
82
82
83 $ hg pull -u "$TESTDIR"/bundles/test-keyword.hg
83 $ hg pull -u "$TESTDIR"/bundles/test-keyword.hg
84 pulling from *test-keyword.hg (glob)
84 pulling from *test-keyword.hg (glob)
85 requesting all changes
85 requesting all changes
86 adding changesets
86 adding changesets
87 adding manifests
87 adding manifests
88 adding file changes
88 adding file changes
89 added 1 changesets with 1 changes to 1 files
89 added 1 changesets with 1 changes to 1 files
90 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
90 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
91
91
92 $ echo 'expand $Id$' > a
92 $ echo 'expand $Id$' > a
93 $ echo 'do not process $Id:' >> a
93 $ echo 'do not process $Id:' >> a
94 $ echo 'xxx $' >> a
94 $ echo 'xxx $' >> a
95 $ echo 'ignore $Id$' > b
95 $ echo 'ignore $Id$' > b
96
96
97 Output files as they were created
97 Output files as they were created
98
98
99 $ cat a b
99 $ cat a b
100 expand $Id$
100 expand $Id$
101 do not process $Id:
101 do not process $Id:
102 xxx $
102 xxx $
103 ignore $Id$
103 ignore $Id$
104
104
105 no kwfiles
105 no kwfiles
106
106
107 $ hg kwfiles
107 $ hg kwfiles
108
108
109 untracked candidates
109 untracked candidates
110
110
111 $ hg -v kwfiles --unknown
111 $ hg -v kwfiles --unknown
112 k a
112 k a
113
113
114 Add files and check status
114 Add files and check status
115
115
116 $ hg addremove
116 $ hg addremove
117 adding a
117 adding a
118 adding b
118 adding b
119 $ hg status
119 $ hg status
120 A a
120 A a
121 A b
121 A b
122
122
123
123
124 Default keyword expansion including commit hook
124 Default keyword expansion including commit hook
125 Interrupted commit should not change state or run commit hook
125 Interrupted commit should not change state or run commit hook
126
126
127 $ hg --debug commit
127 $ hg --debug commit
128 abort: empty commit message
128 abort: empty commit message
129 [255]
129 [255]
130 $ hg status
130 $ hg status
131 A a
131 A a
132 A b
132 A b
133
133
134 Commit with several checks
134 Commit with several checks
135
135
136 $ hg --debug commit -mabsym -u 'User Name <user@example.com>'
136 $ hg --debug commit -mabsym -u 'User Name <user@example.com>'
137 a
137 a
138 b
138 b
139 overwriting a expanding keywords
139 overwriting a expanding keywords
140 running hook commit.test: cp a hooktest
140 running hook commit.test: cp a hooktest
141 committed changeset 1:ef63ca68695bc9495032c6fda1350c71e6d256e9
141 committed changeset 1:ef63ca68695bc9495032c6fda1350c71e6d256e9
142 $ hg status
142 $ hg status
143 ? hooktest
143 ? hooktest
144 $ hg debugrebuildstate
144 $ hg debugrebuildstate
145 $ hg --quiet identify
145 $ hg --quiet identify
146 ef63ca68695b
146 ef63ca68695b
147
147
148 cat files in working directory with keywords expanded
148 cat files in working directory with keywords expanded
149
149
150 $ cat a b
150 $ cat a b
151 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
151 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
152 do not process $Id:
152 do not process $Id:
153 xxx $
153 xxx $
154 ignore $Id$
154 ignore $Id$
155
155
156 hg cat files and symlink, no expansion
156 hg cat files and symlink, no expansion
157
157
158 $ hg cat sym a b && echo
158 $ hg cat sym a b && echo
159 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
159 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
160 do not process $Id:
160 do not process $Id:
161 xxx $
161 xxx $
162 ignore $Id$
162 ignore $Id$
163 a
163 a
164
164
165 $ diff a hooktest
165 $ diff a hooktest
166
166
167 $ cp $HGRCPATH.nohooks $HGRCPATH
167 $ cp $HGRCPATH.nohooks $HGRCPATH
168 $ rm hooktest
168 $ rm hooktest
169
169
170 hg status of kw-ignored binary file starting with '\1\n'
170 hg status of kw-ignored binary file starting with '\1\n'
171
171
172 >>> open("i", "wb").write("\1\nfoo")
172 >>> open("i", "wb").write("\1\nfoo")
173 $ hg -q commit -Am metasep i
173 $ hg -q commit -Am metasep i
174 $ hg status
174 $ hg status
175 >>> open("i", "wb").write("\1\nbar")
175 >>> open("i", "wb").write("\1\nbar")
176 $ hg status
176 $ hg status
177 M i
177 M i
178 $ hg -q commit -m "modify metasep" i
178 $ hg -q commit -m "modify metasep" i
179 $ hg status --rev 2:3
179 $ hg status --rev 2:3
180 M i
180 M i
181 $ touch empty
181 $ touch empty
182 $ hg -q commit -A -m "another file"
182 $ hg -q commit -A -m "another file"
183 $ hg status -A --rev 3:4 i
183 $ hg status -A --rev 3:4 i
184 C i
184 C i
185
185
186 $ hg -q strip -n 2
186 $ hg -q strip -n 2
187
187
188 Test hook execution
188 Test hook execution
189
189
190 bundle
190 bundle
191
191
192 $ hg bundle --base null ../kw.hg
192 $ hg bundle --base null ../kw.hg
193 2 changesets found
193 2 changesets found
194 $ cd ..
194 $ cd ..
195 $ hg init Test
195 $ hg init Test
196 $ cd Test
196 $ cd Test
197
197
198 Notify on pull to check whether keywords stay as is in email
198 Notify on pull to check whether keywords stay as is in email
199 ie. if patch.diff wrapper acts as it should
199 ie. if patch.diff wrapper acts as it should
200
200
201 $ cat <<EOF >> $HGRCPATH
201 $ cat <<EOF >> $HGRCPATH
202 > [hooks]
202 > [hooks]
203 > incoming.notify = python:hgext.notify.hook
203 > incoming.notify = python:hgext.notify.hook
204 > [notify]
204 > [notify]
205 > sources = pull
205 > sources = pull
206 > diffstat = False
206 > diffstat = False
207 > maxsubject = 15
207 > maxsubject = 15
208 > [reposubs]
208 > [reposubs]
209 > * = Test
209 > * = Test
210 > EOF
210 > EOF
211
211
212 Pull from bundle and trigger notify
212 Pull from bundle and trigger notify
213
213
214 $ hg pull -u ../kw.hg
214 $ hg pull -u ../kw.hg
215 pulling from ../kw.hg
215 pulling from ../kw.hg
216 requesting all changes
216 requesting all changes
217 adding changesets
217 adding changesets
218 adding manifests
218 adding manifests
219 adding file changes
219 adding file changes
220 added 2 changesets with 3 changes to 3 files
220 added 2 changesets with 3 changes to 3 files
221 Content-Type: text/plain; charset="us-ascii"
221 Content-Type: text/plain; charset="us-ascii"
222 MIME-Version: 1.0
222 MIME-Version: 1.0
223 Content-Transfer-Encoding: 7bit
223 Content-Transfer-Encoding: 7bit
224 Date: * (glob)
224 Date: * (glob)
225 Subject: changeset in...
225 Subject: changeset in...
226 From: mercurial
226 From: mercurial
227 X-Hg-Notification: changeset a2392c293916
227 X-Hg-Notification: changeset a2392c293916
228 Message-Id: <hg.a2392c293916*> (glob)
228 Message-Id: <hg.a2392c293916*> (glob)
229 To: Test
229 To: Test
230
230
231 changeset a2392c293916 in $TESTTMP/Test (glob)
231 changeset a2392c293916 in $TESTTMP/Test (glob)
232 details: $TESTTMP/Test?cmd=changeset;node=a2392c293916
232 details: $TESTTMP/Test?cmd=changeset;node=a2392c293916
233 description:
233 description:
234 addsym
234 addsym
235
235
236 diffs (6 lines):
236 diffs (6 lines):
237
237
238 diff -r 000000000000 -r a2392c293916 sym
238 diff -r 000000000000 -r a2392c293916 sym
239 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
239 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
240 +++ b/sym Sat Feb 09 20:25:47 2008 +0100
240 +++ b/sym Sat Feb 09 20:25:47 2008 +0100
241 @@ -0,0 +1,1 @@
241 @@ -0,0 +1,1 @@
242 +a
242 +a
243 \ No newline at end of file
243 \ No newline at end of file
244 Content-Type: text/plain; charset="us-ascii"
244 Content-Type: text/plain; charset="us-ascii"
245 MIME-Version: 1.0
245 MIME-Version: 1.0
246 Content-Transfer-Encoding: 7bit
246 Content-Transfer-Encoding: 7bit
247 Date:* (glob)
247 Date:* (glob)
248 Subject: changeset in...
248 Subject: changeset in...
249 From: User Name <user@example.com>
249 From: User Name <user@example.com>
250 X-Hg-Notification: changeset ef63ca68695b
250 X-Hg-Notification: changeset ef63ca68695b
251 Message-Id: <hg.ef63ca68695b*> (glob)
251 Message-Id: <hg.ef63ca68695b*> (glob)
252 To: Test
252 To: Test
253
253
254 changeset ef63ca68695b in $TESTTMP/Test (glob)
254 changeset ef63ca68695b in $TESTTMP/Test (glob)
255 details: $TESTTMP/Test?cmd=changeset;node=ef63ca68695b
255 details: $TESTTMP/Test?cmd=changeset;node=ef63ca68695b
256 description:
256 description:
257 absym
257 absym
258
258
259 diffs (12 lines):
259 diffs (12 lines):
260
260
261 diff -r a2392c293916 -r ef63ca68695b a
261 diff -r a2392c293916 -r ef63ca68695b a
262 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
262 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
263 +++ b/a Thu Jan 01 00:00:00 1970 +0000
263 +++ b/a Thu Jan 01 00:00:00 1970 +0000
264 @@ -0,0 +1,3 @@
264 @@ -0,0 +1,3 @@
265 +expand $Id$
265 +expand $Id$
266 +do not process $Id:
266 +do not process $Id:
267 +xxx $
267 +xxx $
268 diff -r a2392c293916 -r ef63ca68695b b
268 diff -r a2392c293916 -r ef63ca68695b b
269 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
269 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
270 +++ b/b Thu Jan 01 00:00:00 1970 +0000
270 +++ b/b Thu Jan 01 00:00:00 1970 +0000
271 @@ -0,0 +1,1 @@
271 @@ -0,0 +1,1 @@
272 +ignore $Id$
272 +ignore $Id$
273 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
273 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
274
274
275 $ cp $HGRCPATH.nohooks $HGRCPATH
275 $ cp $HGRCPATH.nohooks $HGRCPATH
276
276
277 Touch files and check with status
277 Touch files and check with status
278
278
279 $ touch a b
279 $ touch a b
280 $ hg status
280 $ hg status
281
281
282 Update and expand
282 Update and expand
283
283
284 $ rm sym a b
284 $ rm sym a b
285 $ hg update -C
285 $ hg update -C
286 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
286 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
287 $ cat a b
287 $ cat a b
288 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
288 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
289 do not process $Id:
289 do not process $Id:
290 xxx $
290 xxx $
291 ignore $Id$
291 ignore $Id$
292
292
293 Check whether expansion is filewise and file mode is preserved
293 Check whether expansion is filewise and file mode is preserved
294
294
295 $ echo '$Id$' > c
295 $ echo '$Id$' > c
296 $ echo 'tests for different changenodes' >> c
296 $ echo 'tests for different changenodes' >> c
297 $ chmod 600 c
297 $ chmod 600 c
298 $ ls -l c | cut -b 1-10
298 $ ls -l c | cut -b 1-10
299 -rw-------
299 -rw-------
300
300
301 commit file c
301 commit file c
302
302
303 $ hg commit -A -mcndiff -d '1 0' -u 'User Name <user@example.com>'
303 $ hg commit -A -mcndiff -d '1 0' -u 'User Name <user@example.com>'
304 adding c
304 adding c
305 $ ls -l c | cut -b 1-10
305 $ ls -l c | cut -b 1-10
306 -rw-------
306 -rw-------
307
307
308 force expansion
308 force expansion
309
309
310 $ hg -v kwexpand
310 $ hg -v kwexpand
311 overwriting a expanding keywords
311 overwriting a expanding keywords
312 overwriting c expanding keywords
312 overwriting c expanding keywords
313
313
314 compare changenodes in a and c
314 compare changenodes in a and c
315
315
316 $ cat a c
316 $ cat a c
317 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
317 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
318 do not process $Id:
318 do not process $Id:
319 xxx $
319 xxx $
320 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
320 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
321 tests for different changenodes
321 tests for different changenodes
322
322
323 record
323 record
324
324
325 $ echo '$Id$' > r
325 $ echo '$Id$' > r
326 $ hg add r
326 $ hg add r
327
327
328 record chunk
328 record chunk
329
329
330 >>> lines = open('a').readlines()
330 >>> lines = open('a').readlines()
331 >>> lines.insert(1, 'foo\n')
331 >>> lines.insert(1, 'foo\n')
332 >>> lines.append('bar\n')
332 >>> lines.append('bar\n')
333 >>> open('a', 'w').writelines(lines)
333 >>> open('a', 'w').writelines(lines)
334 $ hg record -d '1 10' -m rectest a<<EOF
334 $ hg record -d '1 10' -m rectest a<<EOF
335 > y
335 > y
336 > y
336 > y
337 > n
337 > n
338 > EOF
338 > EOF
339 diff --git a/a b/a
339 diff --git a/a b/a
340 2 hunks, 2 lines changed
340 2 hunks, 2 lines changed
341 examine changes to 'a'? [Ynsfdaq?]
341 examine changes to 'a'? [Ynsfdaq?]
342 @@ -1,3 +1,4 @@
342 @@ -1,3 +1,4 @@
343 expand $Id$
343 expand $Id$
344 +foo
344 +foo
345 do not process $Id:
345 do not process $Id:
346 xxx $
346 xxx $
347 record change 1/2 to 'a'? [Ynsfdaq?]
347 record change 1/2 to 'a'? [Ynsfdaq?]
348 @@ -2,2 +3,3 @@
348 @@ -2,2 +3,3 @@
349 do not process $Id:
349 do not process $Id:
350 xxx $
350 xxx $
351 +bar
351 +bar
352 record change 2/2 to 'a'? [Ynsfdaq?]
352 record change 2/2 to 'a'? [Ynsfdaq?]
353
353
354 $ hg identify
354 $ hg identify
355 d17e03c92c97+ tip
355 d17e03c92c97+ tip
356 $ hg status
356 $ hg status
357 M a
357 M a
358 A r
358 A r
359
359
360 Cat modified file a
360 Cat modified file a
361
361
362 $ cat a
362 $ cat a
363 expand $Id: a,v d17e03c92c97 1970/01/01 00:00:01 test $
363 expand $Id: a,v d17e03c92c97 1970/01/01 00:00:01 test $
364 foo
364 foo
365 do not process $Id:
365 do not process $Id:
366 xxx $
366 xxx $
367 bar
367 bar
368
368
369 Diff remaining chunk
369 Diff remaining chunk
370
370
371 $ hg diff a
371 $ hg diff a
372 diff -r d17e03c92c97 a
372 diff -r d17e03c92c97 a
373 --- a/a Wed Dec 31 23:59:51 1969 -0000
373 --- a/a Wed Dec 31 23:59:51 1969 -0000
374 +++ b/a * (glob)
374 +++ b/a * (glob)
375 @@ -2,3 +2,4 @@
375 @@ -2,3 +2,4 @@
376 foo
376 foo
377 do not process $Id:
377 do not process $Id:
378 xxx $
378 xxx $
379 +bar
379 +bar
380
380
381 $ hg rollback
381 $ hg rollback
382 repository tip rolled back to revision 2 (undo commit)
382 repository tip rolled back to revision 2 (undo commit)
383 working directory now based on revision 2
383 working directory now based on revision 2
384
384
385 Record all chunks in file a
385 Record all chunks in file a
386
386
387 $ echo foo > msg
387 $ echo foo > msg
388
388
389 - do not use "hg record -m" here!
389 - do not use "hg record -m" here!
390
390
391 $ hg record -l msg -d '1 11' a<<EOF
391 $ hg record -l msg -d '1 11' a<<EOF
392 > y
392 > y
393 > y
393 > y
394 > y
394 > y
395 > EOF
395 > EOF
396 diff --git a/a b/a
396 diff --git a/a b/a
397 2 hunks, 2 lines changed
397 2 hunks, 2 lines changed
398 examine changes to 'a'? [Ynsfdaq?]
398 examine changes to 'a'? [Ynsfdaq?]
399 @@ -1,3 +1,4 @@
399 @@ -1,3 +1,4 @@
400 expand $Id$
400 expand $Id$
401 +foo
401 +foo
402 do not process $Id:
402 do not process $Id:
403 xxx $
403 xxx $
404 record change 1/2 to 'a'? [Ynsfdaq?]
404 record change 1/2 to 'a'? [Ynsfdaq?]
405 @@ -2,2 +3,3 @@
405 @@ -2,2 +3,3 @@
406 do not process $Id:
406 do not process $Id:
407 xxx $
407 xxx $
408 +bar
408 +bar
409 record change 2/2 to 'a'? [Ynsfdaq?]
409 record change 2/2 to 'a'? [Ynsfdaq?]
410
410
411 File a should be clean
411 File a should be clean
412
412
413 $ hg status -A a
413 $ hg status -A a
414 C a
414 C a
415
415
416 rollback and revert expansion
416 rollback and revert expansion
417
417
418 $ cat a
418 $ cat a
419 expand $Id: a,v 59f969a3b52c 1970/01/01 00:00:01 test $
419 expand $Id: a,v 59f969a3b52c 1970/01/01 00:00:01 test $
420 foo
420 foo
421 do not process $Id:
421 do not process $Id:
422 xxx $
422 xxx $
423 bar
423 bar
424 $ hg --verbose rollback
424 $ hg --verbose rollback
425 repository tip rolled back to revision 2 (undo commit)
425 repository tip rolled back to revision 2 (undo commit)
426 working directory now based on revision 2
426 working directory now based on revision 2
427 overwriting a expanding keywords
427 overwriting a expanding keywords
428 $ hg status a
428 $ hg status a
429 M a
429 M a
430 $ cat a
430 $ cat a
431 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
431 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
432 foo
432 foo
433 do not process $Id:
433 do not process $Id:
434 xxx $
434 xxx $
435 bar
435 bar
436 $ echo '$Id$' > y
436 $ echo '$Id$' > y
437 $ echo '$Id$' > z
437 $ echo '$Id$' > z
438 $ hg add y
438 $ hg add y
439 $ hg commit -Am "rollback only" z
439 $ hg commit -Am "rollback only" z
440 $ cat z
440 $ cat z
441 $Id: z,v 45a5d3adce53 1970/01/01 00:00:00 test $
441 $Id: z,v 45a5d3adce53 1970/01/01 00:00:00 test $
442 $ hg --verbose rollback
442 $ hg --verbose rollback
443 repository tip rolled back to revision 2 (undo commit)
443 repository tip rolled back to revision 2 (undo commit)
444 working directory now based on revision 2
444 working directory now based on revision 2
445 overwriting z shrinking keywords
445 overwriting z shrinking keywords
446
446
447 Only z should be overwritten
447 Only z should be overwritten
448
448
449 $ hg status a y z
449 $ hg status a y z
450 M a
450 M a
451 A y
451 A y
452 A z
452 A z
453 $ cat z
453 $ cat z
454 $Id$
454 $Id$
455 $ hg forget y z
455 $ hg forget y z
456 $ rm y z
456 $ rm y z
457
457
458 record added file alone
458 record added file alone
459
459
460 $ hg -v record -l msg -d '1 12' r<<EOF
460 $ hg -v record -l msg -d '1 12' r<<EOF
461 > y
461 > y
462 > EOF
462 > EOF
463 diff --git a/r b/r
463 diff --git a/r b/r
464 new file mode 100644
464 new file mode 100644
465 examine changes to 'r'? [Ynsfdaq?]
465 examine changes to 'r'? [Ynsfdaq?]
466 r
466 r
467 committed changeset 3:899491280810
467 committed changeset 3:899491280810
468 overwriting r expanding keywords
468 overwriting r expanding keywords
469 - status call required for dirstate.normallookup() check
469 - status call required for dirstate.normallookup() check
470 $ hg status r
470 $ hg status r
471 $ hg --verbose rollback
471 $ hg --verbose rollback
472 repository tip rolled back to revision 2 (undo commit)
472 repository tip rolled back to revision 2 (undo commit)
473 working directory now based on revision 2
473 working directory now based on revision 2
474 overwriting r shrinking keywords
474 overwriting r shrinking keywords
475 $ hg forget r
475 $ hg forget r
476 $ rm msg r
476 $ rm msg r
477 $ hg update -C
477 $ hg update -C
478 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
478 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
479
479
480 record added keyword ignored file
480 record added keyword ignored file
481
481
482 $ echo '$Id$' > i
482 $ echo '$Id$' > i
483 $ hg add i
483 $ hg add i
484 $ hg --verbose record -d '1 13' -m recignored<<EOF
484 $ hg --verbose record -d '1 13' -m recignored<<EOF
485 > y
485 > y
486 > EOF
486 > EOF
487 diff --git a/i b/i
487 diff --git a/i b/i
488 new file mode 100644
488 new file mode 100644
489 examine changes to 'i'? [Ynsfdaq?]
489 examine changes to 'i'? [Ynsfdaq?]
490 i
490 i
491 committed changeset 3:5f40fe93bbdc
491 committed changeset 3:5f40fe93bbdc
492 $ cat i
492 $ cat i
493 $Id$
493 $Id$
494 $ hg -q rollback
494 $ hg -q rollback
495 $ hg forget i
495 $ hg forget i
496 $ rm i
496 $ rm i
497
497
498 Test patch queue repo
498 Test patch queue repo
499
499
500 $ hg init --mq
500 $ hg init --mq
501 $ hg qimport -r tip -n mqtest.diff
501 $ hg qimport -r tip -n mqtest.diff
502 $ hg commit --mq -m mqtest
502 $ hg commit --mq -m mqtest
503
503
504 Keywords should not be expanded in patch
504 Keywords should not be expanded in patch
505
505
506 $ cat .hg/patches/mqtest.diff
506 $ cat .hg/patches/mqtest.diff
507 # HG changeset patch
507 # HG changeset patch
508 # User User Name <user@example.com>
508 # User User Name <user@example.com>
509 # Date 1 0
509 # Date 1 0
510 # Node ID 40a904bbbe4cd4ab0a1f28411e35db26341a40ad
510 # Node ID 40a904bbbe4cd4ab0a1f28411e35db26341a40ad
511 # Parent ef63ca68695bc9495032c6fda1350c71e6d256e9
511 # Parent ef63ca68695bc9495032c6fda1350c71e6d256e9
512 cndiff
512 cndiff
513
513
514 diff -r ef63ca68695b -r 40a904bbbe4c c
514 diff -r ef63ca68695b -r 40a904bbbe4c c
515 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
515 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
516 +++ b/c Thu Jan 01 00:00:01 1970 +0000
516 +++ b/c Thu Jan 01 00:00:01 1970 +0000
517 @@ -0,0 +1,2 @@
517 @@ -0,0 +1,2 @@
518 +$Id$
518 +$Id$
519 +tests for different changenodes
519 +tests for different changenodes
520
520
521 $ hg qpop
521 $ hg qpop
522 popping mqtest.diff
522 popping mqtest.diff
523 patch queue now empty
523 patch queue now empty
524
524
525 qgoto, implying qpush, should expand
525 qgoto, implying qpush, should expand
526
526
527 $ hg qgoto mqtest.diff
527 $ hg qgoto mqtest.diff
528 applying mqtest.diff
528 applying mqtest.diff
529 now at: mqtest.diff
529 now at: mqtest.diff
530 $ cat c
530 $ cat c
531 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
531 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
532 tests for different changenodes
532 tests for different changenodes
533 $ hg cat c
533 $ hg cat c
534 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
534 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
535 tests for different changenodes
535 tests for different changenodes
536
536
537 Keywords should not be expanded in filelog
537 Keywords should not be expanded in filelog
538
538
539 $ hg --config 'extensions.keyword=!' cat c
539 $ hg --config 'extensions.keyword=!' cat c
540 $Id$
540 $Id$
541 tests for different changenodes
541 tests for different changenodes
542
542
543 qpop and move on
543 qpop and move on
544
544
545 $ hg qpop
545 $ hg qpop
546 popping mqtest.diff
546 popping mqtest.diff
547 patch queue now empty
547 patch queue now empty
548
548
549 Copy and show added kwfiles
549 Copy and show added kwfiles
550
550
551 $ hg cp a c
551 $ hg cp a c
552 $ hg kwfiles
552 $ hg kwfiles
553 a
553 a
554 c
554 c
555
555
556 Commit and show expansion in original and copy
556 Commit and show expansion in original and copy
557
557
558 $ hg --debug commit -ma2c -d '1 0' -u 'User Name <user@example.com>'
558 $ hg --debug commit -ma2c -d '1 0' -u 'User Name <user@example.com>'
559 invalidating branch cache (tip differs)
560 c
559 c
561 c: copy a:0045e12f6c5791aac80ca6cbfd97709a88307292
560 c: copy a:0045e12f6c5791aac80ca6cbfd97709a88307292
562 overwriting c expanding keywords
561 overwriting c expanding keywords
563 committed changeset 2:25736cf2f5cbe41f6be4e6784ef6ecf9f3bbcc7d
562 committed changeset 2:25736cf2f5cbe41f6be4e6784ef6ecf9f3bbcc7d
564 $ cat a c
563 $ cat a c
565 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
564 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
566 do not process $Id:
565 do not process $Id:
567 xxx $
566 xxx $
568 expand $Id: c,v 25736cf2f5cb 1970/01/01 00:00:01 user $
567 expand $Id: c,v 25736cf2f5cb 1970/01/01 00:00:01 user $
569 do not process $Id:
568 do not process $Id:
570 xxx $
569 xxx $
571
570
572 Touch copied c and check its status
571 Touch copied c and check its status
573
572
574 $ touch c
573 $ touch c
575 $ hg status
574 $ hg status
576
575
577 Copy kwfile to keyword ignored file unexpanding keywords
576 Copy kwfile to keyword ignored file unexpanding keywords
578
577
579 $ hg --verbose copy a i
578 $ hg --verbose copy a i
580 copying a to i
579 copying a to i
581 overwriting i shrinking keywords
580 overwriting i shrinking keywords
582 $ head -n 1 i
581 $ head -n 1 i
583 expand $Id$
582 expand $Id$
584 $ hg forget i
583 $ hg forget i
585 $ rm i
584 $ rm i
586
585
587 Copy ignored file to ignored file: no overwriting
586 Copy ignored file to ignored file: no overwriting
588
587
589 $ hg --verbose copy b i
588 $ hg --verbose copy b i
590 copying b to i
589 copying b to i
591 $ hg forget i
590 $ hg forget i
592 $ rm i
591 $ rm i
593
592
594 cp symlink file; hg cp -A symlink file (part1)
593 cp symlink file; hg cp -A symlink file (part1)
595 - copied symlink points to kwfile: overwrite
594 - copied symlink points to kwfile: overwrite
596
595
597 $ cp sym i
596 $ cp sym i
598 $ ls -l i
597 $ ls -l i
599 -rw-r--r--* (glob)
598 -rw-r--r--* (glob)
600 $ head -1 i
599 $ head -1 i
601 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
600 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
602 $ hg copy --after --verbose sym i
601 $ hg copy --after --verbose sym i
603 copying sym to i
602 copying sym to i
604 overwriting i shrinking keywords
603 overwriting i shrinking keywords
605 $ head -1 i
604 $ head -1 i
606 expand $Id$
605 expand $Id$
607 $ hg forget i
606 $ hg forget i
608 $ rm i
607 $ rm i
609
608
610 Test different options of hg kwfiles
609 Test different options of hg kwfiles
611
610
612 $ hg kwfiles
611 $ hg kwfiles
613 a
612 a
614 c
613 c
615 $ hg -v kwfiles --ignore
614 $ hg -v kwfiles --ignore
616 I b
615 I b
617 I sym
616 I sym
618 $ hg kwfiles --all
617 $ hg kwfiles --all
619 K a
618 K a
620 K c
619 K c
621 I b
620 I b
622 I sym
621 I sym
623
622
624 Diff specific revision
623 Diff specific revision
625
624
626 $ hg diff --rev 1
625 $ hg diff --rev 1
627 diff -r ef63ca68695b c
626 diff -r ef63ca68695b c
628 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
627 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
629 +++ b/c * (glob)
628 +++ b/c * (glob)
630 @@ -0,0 +1,3 @@
629 @@ -0,0 +1,3 @@
631 +expand $Id$
630 +expand $Id$
632 +do not process $Id:
631 +do not process $Id:
633 +xxx $
632 +xxx $
634
633
635 Status after rollback:
634 Status after rollback:
636
635
637 $ hg rollback
636 $ hg rollback
638 repository tip rolled back to revision 1 (undo commit)
637 repository tip rolled back to revision 1 (undo commit)
639 working directory now based on revision 1
638 working directory now based on revision 1
640 $ hg status
639 $ hg status
641 A c
640 A c
642 $ hg update --clean
641 $ hg update --clean
643 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
642 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
644
643
645 cp symlink file; hg cp -A symlink file (part2)
644 cp symlink file; hg cp -A symlink file (part2)
646 - copied symlink points to kw ignored file: do not overwrite
645 - copied symlink points to kw ignored file: do not overwrite
647
646
648 $ cat a > i
647 $ cat a > i
649 $ ln -s i symignored
648 $ ln -s i symignored
650 $ hg commit -Am 'fake expansion in ignored and symlink' i symignored
649 $ hg commit -Am 'fake expansion in ignored and symlink' i symignored
651 $ cp symignored x
650 $ cp symignored x
652 $ hg copy --after --verbose symignored x
651 $ hg copy --after --verbose symignored x
653 copying symignored to x
652 copying symignored to x
654 $ head -n 1 x
653 $ head -n 1 x
655 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
654 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
656 $ hg forget x
655 $ hg forget x
657 $ rm x
656 $ rm x
658
657
659 $ hg rollback
658 $ hg rollback
660 repository tip rolled back to revision 1 (undo commit)
659 repository tip rolled back to revision 1 (undo commit)
661 working directory now based on revision 1
660 working directory now based on revision 1
662 $ hg update --clean
661 $ hg update --clean
663 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
662 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
664 $ rm i symignored
663 $ rm i symignored
665
664
666 Custom keywordmaps as argument to kwdemo
665 Custom keywordmaps as argument to kwdemo
667
666
668 $ hg --quiet kwdemo "Xinfo = {author}: {desc}"
667 $ hg --quiet kwdemo "Xinfo = {author}: {desc}"
669 [extensions]
668 [extensions]
670 keyword =
669 keyword =
671 [keyword]
670 [keyword]
672 ** =
671 ** =
673 b = ignore
672 b = ignore
674 demo.txt =
673 demo.txt =
675 i = ignore
674 i = ignore
676 [keywordset]
675 [keywordset]
677 svn = False
676 svn = False
678 [keywordmaps]
677 [keywordmaps]
679 Xinfo = {author}: {desc}
678 Xinfo = {author}: {desc}
680 $Xinfo: test: hg keyword configuration and expansion example $
679 $Xinfo: test: hg keyword configuration and expansion example $
681
680
682 Configure custom keywordmaps
681 Configure custom keywordmaps
683
682
684 $ cat <<EOF >>$HGRCPATH
683 $ cat <<EOF >>$HGRCPATH
685 > [keywordmaps]
684 > [keywordmaps]
686 > Id = {file} {node|short} {date|rfc822date} {author|user}
685 > Id = {file} {node|short} {date|rfc822date} {author|user}
687 > Xinfo = {author}: {desc}
686 > Xinfo = {author}: {desc}
688 > EOF
687 > EOF
689
688
690 Cat and hg cat files before custom expansion
689 Cat and hg cat files before custom expansion
691
690
692 $ cat a b
691 $ cat a b
693 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
692 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
694 do not process $Id:
693 do not process $Id:
695 xxx $
694 xxx $
696 ignore $Id$
695 ignore $Id$
697 $ hg cat sym a b && echo
696 $ hg cat sym a b && echo
698 expand $Id: a ef63ca68695b Thu, 01 Jan 1970 00:00:00 +0000 user $
697 expand $Id: a ef63ca68695b Thu, 01 Jan 1970 00:00:00 +0000 user $
699 do not process $Id:
698 do not process $Id:
700 xxx $
699 xxx $
701 ignore $Id$
700 ignore $Id$
702 a
701 a
703
702
704 Write custom keyword and prepare multiline commit message
703 Write custom keyword and prepare multiline commit message
705
704
706 $ echo '$Xinfo$' >> a
705 $ echo '$Xinfo$' >> a
707 $ cat <<EOF >> log
706 $ cat <<EOF >> log
708 > firstline
707 > firstline
709 > secondline
708 > secondline
710 > EOF
709 > EOF
711
710
712 Interrupted commit should not change state
711 Interrupted commit should not change state
713
712
714 $ hg commit
713 $ hg commit
715 abort: empty commit message
714 abort: empty commit message
716 [255]
715 [255]
717 $ hg status
716 $ hg status
718 M a
717 M a
719 ? c
718 ? c
720 ? log
719 ? log
721
720
722 Commit with multiline message and custom expansion
721 Commit with multiline message and custom expansion
723
722
724 $ hg --debug commit -l log -d '2 0' -u 'User Name <user@example.com>'
723 $ hg --debug commit -l log -d '2 0' -u 'User Name <user@example.com>'
725 a
724 a
726 overwriting a expanding keywords
725 overwriting a expanding keywords
727 committed changeset 2:bb948857c743469b22bbf51f7ec8112279ca5d83
726 committed changeset 2:bb948857c743469b22bbf51f7ec8112279ca5d83
728 $ rm log
727 $ rm log
729
728
730 Stat, verify and show custom expansion (firstline)
729 Stat, verify and show custom expansion (firstline)
731
730
732 $ hg status
731 $ hg status
733 ? c
732 ? c
734 $ hg verify
733 $ hg verify
735 checking changesets
734 checking changesets
736 checking manifests
735 checking manifests
737 crosschecking files in changesets and manifests
736 crosschecking files in changesets and manifests
738 checking files
737 checking files
739 3 files, 3 changesets, 4 total revisions
738 3 files, 3 changesets, 4 total revisions
740 $ cat a b
739 $ cat a b
741 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
740 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
742 do not process $Id:
741 do not process $Id:
743 xxx $
742 xxx $
744 $Xinfo: User Name <user@example.com>: firstline $
743 $Xinfo: User Name <user@example.com>: firstline $
745 ignore $Id$
744 ignore $Id$
746 $ hg cat sym a b && echo
745 $ hg cat sym a b && echo
747 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
746 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
748 do not process $Id:
747 do not process $Id:
749 xxx $
748 xxx $
750 $Xinfo: User Name <user@example.com>: firstline $
749 $Xinfo: User Name <user@example.com>: firstline $
751 ignore $Id$
750 ignore $Id$
752 a
751 a
753
752
754 annotate
753 annotate
755
754
756 $ hg annotate a
755 $ hg annotate a
757 1: expand $Id$
756 1: expand $Id$
758 1: do not process $Id:
757 1: do not process $Id:
759 1: xxx $
758 1: xxx $
760 2: $Xinfo$
759 2: $Xinfo$
761
760
762 remove with status checks
761 remove with status checks
763
762
764 $ hg debugrebuildstate
763 $ hg debugrebuildstate
765 $ hg remove a
764 $ hg remove a
766 $ hg --debug commit -m rma
765 $ hg --debug commit -m rma
767 committed changeset 3:d14c712653769de926994cf7fbb06c8fbd68f012
766 committed changeset 3:d14c712653769de926994cf7fbb06c8fbd68f012
768 $ hg status
767 $ hg status
769 ? c
768 ? c
770
769
771 Rollback, revert, and check expansion
770 Rollback, revert, and check expansion
772
771
773 $ hg rollback
772 $ hg rollback
774 repository tip rolled back to revision 2 (undo commit)
773 repository tip rolled back to revision 2 (undo commit)
775 working directory now based on revision 2
774 working directory now based on revision 2
776 $ hg status
775 $ hg status
777 R a
776 R a
778 ? c
777 ? c
779 $ hg revert --no-backup --rev tip a
778 $ hg revert --no-backup --rev tip a
780 $ cat a
779 $ cat a
781 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
780 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
782 do not process $Id:
781 do not process $Id:
783 xxx $
782 xxx $
784 $Xinfo: User Name <user@example.com>: firstline $
783 $Xinfo: User Name <user@example.com>: firstline $
785
784
786 Clone to test global and local configurations
785 Clone to test global and local configurations
787
786
788 $ cd ..
787 $ cd ..
789
788
790 Expansion in destinaton with global configuration
789 Expansion in destinaton with global configuration
791
790
792 $ hg --quiet clone Test globalconf
791 $ hg --quiet clone Test globalconf
793 $ cat globalconf/a
792 $ cat globalconf/a
794 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
793 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
795 do not process $Id:
794 do not process $Id:
796 xxx $
795 xxx $
797 $Xinfo: User Name <user@example.com>: firstline $
796 $Xinfo: User Name <user@example.com>: firstline $
798
797
799 No expansion in destination with local configuration in origin only
798 No expansion in destination with local configuration in origin only
800
799
801 $ hg --quiet --config 'keyword.**=ignore' clone Test localconf
800 $ hg --quiet --config 'keyword.**=ignore' clone Test localconf
802 $ cat localconf/a
801 $ cat localconf/a
803 expand $Id$
802 expand $Id$
804 do not process $Id:
803 do not process $Id:
805 xxx $
804 xxx $
806 $Xinfo$
805 $Xinfo$
807
806
808 Clone to test incoming
807 Clone to test incoming
809
808
810 $ hg clone -r1 Test Test-a
809 $ hg clone -r1 Test Test-a
811 adding changesets
810 adding changesets
812 adding manifests
811 adding manifests
813 adding file changes
812 adding file changes
814 added 2 changesets with 3 changes to 3 files
813 added 2 changesets with 3 changes to 3 files
815 updating to branch default
814 updating to branch default
816 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
815 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
817 $ cd Test-a
816 $ cd Test-a
818 $ cat <<EOF >> .hg/hgrc
817 $ cat <<EOF >> .hg/hgrc
819 > [paths]
818 > [paths]
820 > default = ../Test
819 > default = ../Test
821 > EOF
820 > EOF
822 $ hg incoming
821 $ hg incoming
823 comparing with $TESTTMP/Test (glob)
822 comparing with $TESTTMP/Test (glob)
824 searching for changes
823 searching for changes
825 changeset: 2:bb948857c743
824 changeset: 2:bb948857c743
826 tag: tip
825 tag: tip
827 user: User Name <user@example.com>
826 user: User Name <user@example.com>
828 date: Thu Jan 01 00:00:02 1970 +0000
827 date: Thu Jan 01 00:00:02 1970 +0000
829 summary: firstline
828 summary: firstline
830
829
831 Imported patch should not be rejected
830 Imported patch should not be rejected
832
831
833 >>> import re
832 >>> import re
834 >>> text = re.sub(r'(Id.*)', r'\1 rejecttest', open('a').read())
833 >>> text = re.sub(r'(Id.*)', r'\1 rejecttest', open('a').read())
835 >>> open('a', 'wb').write(text)
834 >>> open('a', 'wb').write(text)
836 $ hg --debug commit -m'rejects?' -d '3 0' -u 'User Name <user@example.com>'
835 $ hg --debug commit -m'rejects?' -d '3 0' -u 'User Name <user@example.com>'
837 a
836 a
838 overwriting a expanding keywords
837 overwriting a expanding keywords
839 committed changeset 2:85e279d709ffc28c9fdd1b868570985fc3d87082
838 committed changeset 2:85e279d709ffc28c9fdd1b868570985fc3d87082
840 $ hg export -o ../rejecttest.diff tip
839 $ hg export -o ../rejecttest.diff tip
841 $ cd ../Test
840 $ cd ../Test
842 $ hg import ../rejecttest.diff
841 $ hg import ../rejecttest.diff
843 applying ../rejecttest.diff
842 applying ../rejecttest.diff
844 $ cat a b
843 $ cat a b
845 expand $Id: a 4e0994474d25 Thu, 01 Jan 1970 00:00:03 +0000 user $ rejecttest
844 expand $Id: a 4e0994474d25 Thu, 01 Jan 1970 00:00:03 +0000 user $ rejecttest
846 do not process $Id: rejecttest
845 do not process $Id: rejecttest
847 xxx $
846 xxx $
848 $Xinfo: User Name <user@example.com>: rejects? $
847 $Xinfo: User Name <user@example.com>: rejects? $
849 ignore $Id$
848 ignore $Id$
850
849
851 $ hg rollback
850 $ hg rollback
852 repository tip rolled back to revision 2 (undo import)
851 repository tip rolled back to revision 2 (undo import)
853 working directory now based on revision 2
852 working directory now based on revision 2
854 $ hg update --clean
853 $ hg update --clean
855 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
854 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
856
855
857 kwexpand/kwshrink on selected files
856 kwexpand/kwshrink on selected files
858
857
859 $ mkdir x
858 $ mkdir x
860 $ hg copy a x/a
859 $ hg copy a x/a
861 $ hg --verbose kwshrink a
860 $ hg --verbose kwshrink a
862 overwriting a shrinking keywords
861 overwriting a shrinking keywords
863 - sleep required for dirstate.normal() check
862 - sleep required for dirstate.normal() check
864 $ sleep 1
863 $ sleep 1
865 $ hg status a
864 $ hg status a
866 $ hg --verbose kwexpand a
865 $ hg --verbose kwexpand a
867 overwriting a expanding keywords
866 overwriting a expanding keywords
868 $ hg status a
867 $ hg status a
869
868
870 kwexpand x/a should abort
869 kwexpand x/a should abort
871
870
872 $ hg --verbose kwexpand x/a
871 $ hg --verbose kwexpand x/a
873 abort: outstanding uncommitted changes
872 abort: outstanding uncommitted changes
874 [255]
873 [255]
875 $ cd x
874 $ cd x
876 $ hg --debug commit -m xa -d '3 0' -u 'User Name <user@example.com>'
875 $ hg --debug commit -m xa -d '3 0' -u 'User Name <user@example.com>'
877 x/a
876 x/a
878 x/a: copy a:779c764182ce5d43e2b1eb66ce06d7b47bfe342e
877 x/a: copy a:779c764182ce5d43e2b1eb66ce06d7b47bfe342e
879 overwriting x/a expanding keywords
878 overwriting x/a expanding keywords
880 committed changeset 3:b4560182a3f9a358179fd2d835c15e9da379c1e4
879 committed changeset 3:b4560182a3f9a358179fd2d835c15e9da379c1e4
881 $ cat a
880 $ cat a
882 expand $Id: x/a b4560182a3f9 Thu, 01 Jan 1970 00:00:03 +0000 user $
881 expand $Id: x/a b4560182a3f9 Thu, 01 Jan 1970 00:00:03 +0000 user $
883 do not process $Id:
882 do not process $Id:
884 xxx $
883 xxx $
885 $Xinfo: User Name <user@example.com>: xa $
884 $Xinfo: User Name <user@example.com>: xa $
886
885
887 kwshrink a inside directory x
886 kwshrink a inside directory x
888
887
889 $ hg --verbose kwshrink a
888 $ hg --verbose kwshrink a
890 overwriting x/a shrinking keywords
889 overwriting x/a shrinking keywords
891 $ cat a
890 $ cat a
892 expand $Id$
891 expand $Id$
893 do not process $Id:
892 do not process $Id:
894 xxx $
893 xxx $
895 $Xinfo$
894 $Xinfo$
896 $ cd ..
895 $ cd ..
897
896
898 kwexpand nonexistent
897 kwexpand nonexistent
899
898
900 $ hg kwexpand nonexistent
899 $ hg kwexpand nonexistent
901 nonexistent:* (glob)
900 nonexistent:* (glob)
902
901
903
902
904 hg serve
903 hg serve
905 - expand with hgweb file
904 - expand with hgweb file
906 - no expansion with hgweb annotate/changeset/filediff
905 - no expansion with hgweb annotate/changeset/filediff
907 - check errors
906 - check errors
908
907
909 $ hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
908 $ hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
910 $ cat hg.pid >> $DAEMON_PIDS
909 $ cat hg.pid >> $DAEMON_PIDS
911 $ $TESTDIR/get-with-headers.py localhost:$HGPORT '/file/tip/a/?style=raw'
910 $ $TESTDIR/get-with-headers.py localhost:$HGPORT '/file/tip/a/?style=raw'
912 200 Script output follows
911 200 Script output follows
913
912
914 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
913 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
915 do not process $Id:
914 do not process $Id:
916 xxx $
915 xxx $
917 $Xinfo: User Name <user@example.com>: firstline $
916 $Xinfo: User Name <user@example.com>: firstline $
918 $ $TESTDIR/get-with-headers.py localhost:$HGPORT '/annotate/tip/a/?style=raw'
917 $ $TESTDIR/get-with-headers.py localhost:$HGPORT '/annotate/tip/a/?style=raw'
919 200 Script output follows
918 200 Script output follows
920
919
921
920
922 user@1: expand $Id$
921 user@1: expand $Id$
923 user@1: do not process $Id:
922 user@1: do not process $Id:
924 user@1: xxx $
923 user@1: xxx $
925 user@2: $Xinfo$
924 user@2: $Xinfo$
926
925
927
926
928
927
929
928
930 $ $TESTDIR/get-with-headers.py localhost:$HGPORT '/rev/tip/?style=raw'
929 $ $TESTDIR/get-with-headers.py localhost:$HGPORT '/rev/tip/?style=raw'
931 200 Script output follows
930 200 Script output follows
932
931
933
932
934 # HG changeset patch
933 # HG changeset patch
935 # User User Name <user@example.com>
934 # User User Name <user@example.com>
936 # Date 3 0
935 # Date 3 0
937 # Node ID b4560182a3f9a358179fd2d835c15e9da379c1e4
936 # Node ID b4560182a3f9a358179fd2d835c15e9da379c1e4
938 # Parent bb948857c743469b22bbf51f7ec8112279ca5d83
937 # Parent bb948857c743469b22bbf51f7ec8112279ca5d83
939 xa
938 xa
940
939
941 diff -r bb948857c743 -r b4560182a3f9 x/a
940 diff -r bb948857c743 -r b4560182a3f9 x/a
942 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
941 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
943 +++ b/x/a Thu Jan 01 00:00:03 1970 +0000
942 +++ b/x/a Thu Jan 01 00:00:03 1970 +0000
944 @@ -0,0 +1,4 @@
943 @@ -0,0 +1,4 @@
945 +expand $Id$
944 +expand $Id$
946 +do not process $Id:
945 +do not process $Id:
947 +xxx $
946 +xxx $
948 +$Xinfo$
947 +$Xinfo$
949
948
950 $ $TESTDIR/get-with-headers.py localhost:$HGPORT '/diff/bb948857c743/a?style=raw'
949 $ $TESTDIR/get-with-headers.py localhost:$HGPORT '/diff/bb948857c743/a?style=raw'
951 200 Script output follows
950 200 Script output follows
952
951
953
952
954 diff -r ef63ca68695b -r bb948857c743 a
953 diff -r ef63ca68695b -r bb948857c743 a
955 --- a/a Thu Jan 01 00:00:00 1970 +0000
954 --- a/a Thu Jan 01 00:00:00 1970 +0000
956 +++ b/a Thu Jan 01 00:00:02 1970 +0000
955 +++ b/a Thu Jan 01 00:00:02 1970 +0000
957 @@ -1,3 +1,4 @@
956 @@ -1,3 +1,4 @@
958 expand $Id$
957 expand $Id$
959 do not process $Id:
958 do not process $Id:
960 xxx $
959 xxx $
961 +$Xinfo$
960 +$Xinfo$
962
961
963
962
964
963
965
964
966 $ cat errors.log
965 $ cat errors.log
967
966
968 Prepare merge and resolve tests
967 Prepare merge and resolve tests
969
968
970 $ echo '$Id$' > m
969 $ echo '$Id$' > m
971 $ hg add m
970 $ hg add m
972 $ hg commit -m 4kw
971 $ hg commit -m 4kw
973 $ echo foo >> m
972 $ echo foo >> m
974 $ hg commit -m 5foo
973 $ hg commit -m 5foo
975
974
976 simplemerge
975 simplemerge
977
976
978 $ hg update 4
977 $ hg update 4
979 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
978 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
980 $ echo foo >> m
979 $ echo foo >> m
981 $ hg commit -m 6foo
980 $ hg commit -m 6foo
982 created new head
981 created new head
983 $ hg merge
982 $ hg merge
984 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
983 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
985 (branch merge, don't forget to commit)
984 (branch merge, don't forget to commit)
986 $ hg commit -m simplemerge
985 $ hg commit -m simplemerge
987 $ cat m
986 $ cat m
988 $Id: m 27d48ee14f67 Thu, 01 Jan 1970 00:00:00 +0000 test $
987 $Id: m 27d48ee14f67 Thu, 01 Jan 1970 00:00:00 +0000 test $
989 foo
988 foo
990
989
991 conflict: keyword should stay outside conflict zone
990 conflict: keyword should stay outside conflict zone
992
991
993 $ hg update 4
992 $ hg update 4
994 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
993 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
995 $ echo bar >> m
994 $ echo bar >> m
996 $ hg commit -m 8bar
995 $ hg commit -m 8bar
997 created new head
996 created new head
998 $ hg merge
997 $ hg merge
999 merging m
998 merging m
1000 warning: conflicts during merge.
999 warning: conflicts during merge.
1001 merging m incomplete! (edit conflicts, then use 'hg resolve --mark')
1000 merging m incomplete! (edit conflicts, then use 'hg resolve --mark')
1002 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
1001 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
1003 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
1002 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
1004 [1]
1003 [1]
1005 $ cat m
1004 $ cat m
1006 $Id$
1005 $Id$
1007 <<<<<<< local
1006 <<<<<<< local
1008 bar
1007 bar
1009 =======
1008 =======
1010 foo
1009 foo
1011 >>>>>>> other
1010 >>>>>>> other
1012
1011
1013 resolve to local
1012 resolve to local
1014
1013
1015 $ HGMERGE=internal:local hg resolve -a
1014 $ HGMERGE=internal:local hg resolve -a
1016 $ hg commit -m localresolve
1015 $ hg commit -m localresolve
1017 $ cat m
1016 $ cat m
1018 $Id: m 800511b3a22d Thu, 01 Jan 1970 00:00:00 +0000 test $
1017 $Id: m 800511b3a22d Thu, 01 Jan 1970 00:00:00 +0000 test $
1019 bar
1018 bar
1020
1019
1021 Test restricted mode with transplant -b
1020 Test restricted mode with transplant -b
1022
1021
1023 $ hg update 6
1022 $ hg update 6
1024 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1023 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1025 $ hg branch foo
1024 $ hg branch foo
1026 marked working directory as branch foo
1025 marked working directory as branch foo
1027 (branches are permanent and global, did you want a bookmark?)
1026 (branches are permanent and global, did you want a bookmark?)
1028 $ mv a a.bak
1027 $ mv a a.bak
1029 $ echo foobranch > a
1028 $ echo foobranch > a
1030 $ cat a.bak >> a
1029 $ cat a.bak >> a
1031 $ rm a.bak
1030 $ rm a.bak
1032 $ hg commit -m 9foobranch
1031 $ hg commit -m 9foobranch
1033 $ hg update default
1032 $ hg update default
1034 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1033 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1035 $ hg -y transplant -b foo tip
1034 $ hg -y transplant -b foo tip
1036 applying 4aa30d025d50
1035 applying 4aa30d025d50
1037 4aa30d025d50 transplanted to e00abbf63521
1036 4aa30d025d50 transplanted to e00abbf63521
1038
1037
1039 Expansion in changeset but not in file
1038 Expansion in changeset but not in file
1040
1039
1041 $ hg tip -p
1040 $ hg tip -p
1042 changeset: 11:e00abbf63521
1041 changeset: 11:e00abbf63521
1043 tag: tip
1042 tag: tip
1044 parent: 9:800511b3a22d
1043 parent: 9:800511b3a22d
1045 user: test
1044 user: test
1046 date: Thu Jan 01 00:00:00 1970 +0000
1045 date: Thu Jan 01 00:00:00 1970 +0000
1047 summary: 9foobranch
1046 summary: 9foobranch
1048
1047
1049 diff -r 800511b3a22d -r e00abbf63521 a
1048 diff -r 800511b3a22d -r e00abbf63521 a
1050 --- a/a Thu Jan 01 00:00:00 1970 +0000
1049 --- a/a Thu Jan 01 00:00:00 1970 +0000
1051 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1050 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1052 @@ -1,3 +1,4 @@
1051 @@ -1,3 +1,4 @@
1053 +foobranch
1052 +foobranch
1054 expand $Id$
1053 expand $Id$
1055 do not process $Id:
1054 do not process $Id:
1056 xxx $
1055 xxx $
1057
1056
1058 $ head -n 2 a
1057 $ head -n 2 a
1059 foobranch
1058 foobranch
1060 expand $Id: a e00abbf63521 Thu, 01 Jan 1970 00:00:00 +0000 test $
1059 expand $Id: a e00abbf63521 Thu, 01 Jan 1970 00:00:00 +0000 test $
1061
1060
1062 Turn off expansion
1061 Turn off expansion
1063
1062
1064 $ hg -q rollback
1063 $ hg -q rollback
1065 $ hg -q update -C
1064 $ hg -q update -C
1066
1065
1067 kwshrink with unknown file u
1066 kwshrink with unknown file u
1068
1067
1069 $ cp a u
1068 $ cp a u
1070 $ hg --verbose kwshrink
1069 $ hg --verbose kwshrink
1071 overwriting a shrinking keywords
1070 overwriting a shrinking keywords
1072 overwriting m shrinking keywords
1071 overwriting m shrinking keywords
1073 overwriting x/a shrinking keywords
1072 overwriting x/a shrinking keywords
1074
1073
1075 Keywords shrunk in working directory, but not yet disabled
1074 Keywords shrunk in working directory, but not yet disabled
1076 - cat shows unexpanded keywords
1075 - cat shows unexpanded keywords
1077 - hg cat shows expanded keywords
1076 - hg cat shows expanded keywords
1078
1077
1079 $ cat a b
1078 $ cat a b
1080 expand $Id$
1079 expand $Id$
1081 do not process $Id:
1080 do not process $Id:
1082 xxx $
1081 xxx $
1083 $Xinfo$
1082 $Xinfo$
1084 ignore $Id$
1083 ignore $Id$
1085 $ hg cat sym a b && echo
1084 $ hg cat sym a b && echo
1086 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
1085 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
1087 do not process $Id:
1086 do not process $Id:
1088 xxx $
1087 xxx $
1089 $Xinfo: User Name <user@example.com>: firstline $
1088 $Xinfo: User Name <user@example.com>: firstline $
1090 ignore $Id$
1089 ignore $Id$
1091 a
1090 a
1092
1091
1093 Now disable keyword expansion
1092 Now disable keyword expansion
1094
1093
1095 $ rm "$HGRCPATH"
1094 $ rm "$HGRCPATH"
1096 $ cat a b
1095 $ cat a b
1097 expand $Id$
1096 expand $Id$
1098 do not process $Id:
1097 do not process $Id:
1099 xxx $
1098 xxx $
1100 $Xinfo$
1099 $Xinfo$
1101 ignore $Id$
1100 ignore $Id$
1102 $ hg cat sym a b && echo
1101 $ hg cat sym a b && echo
1103 expand $Id$
1102 expand $Id$
1104 do not process $Id:
1103 do not process $Id:
1105 xxx $
1104 xxx $
1106 $Xinfo$
1105 $Xinfo$
1107 ignore $Id$
1106 ignore $Id$
1108 a
1107 a
@@ -1,126 +1,124 b''
1 $ branches=.hg/cache/branchheads
1 $ branches=.hg/cache/branchheads
2 $ echo '[extensions]' >> $HGRCPATH
2 $ echo '[extensions]' >> $HGRCPATH
3 $ echo 'mq =' >> $HGRCPATH
3 $ echo 'mq =' >> $HGRCPATH
4
4
5 $ show_branch_cache()
5 $ show_branch_cache()
6 > {
6 > {
7 > # force cache (re)generation
7 > # force cache (re)generation
8 > hg log -r does-not-exist 2> /dev/null
8 > hg log -r does-not-exist 2> /dev/null
9 > hg log -r tip --template 'tip: {rev}\n'
9 > hg log -r tip --template 'tip: {rev}\n'
10 > if [ -f $branches ]; then
10 > if [ -f $branches ]; then
11 > sort $branches
11 > sort $branches
12 > else
12 > else
13 > echo No branch cache
13 > echo No branch cache
14 > fi
14 > fi
15 > if [ "$1" = 1 ]; then
15 > if [ "$1" = 1 ]; then
16 > for b in foo bar; do
16 > for b in foo bar; do
17 > hg log -r $b --template "branch $b: "'{rev}\n'
17 > hg log -r $b --template "branch $b: "'{rev}\n'
18 > done
18 > done
19 > fi
19 > fi
20 > }
20 > }
21
21
22 $ hg init a
22 $ hg init a
23 $ cd a
23 $ cd a
24 $ hg qinit -c
24 $ hg qinit -c
25
25
26
26
27 mq patch on an empty repo
27 mq patch on an empty repo
28
28
29 $ hg qnew -d '0 0' p1
29 $ hg qnew -d '0 0' p1
30 $ show_branch_cache
30 $ show_branch_cache
31 tip: 0
31 tip: 0
32 d986d5caac23a7d44a46efc0ddaf5eb9665844cf 0
32 No branch cache
33 d986d5caac23a7d44a46efc0ddaf5eb9665844cf default
34
33
35 $ echo > pfile
34 $ echo > pfile
36 $ hg add pfile
35 $ hg add pfile
37 $ hg qrefresh -m 'patch 1'
36 $ hg qrefresh -m 'patch 1'
38 $ show_branch_cache
37 $ show_branch_cache
39 tip: 0
38 tip: 0
40 a7977e38ed2c2942fa6c278030badfef3d180979 0
39 No branch cache
41 a7977e38ed2c2942fa6c278030badfef3d180979 default
42
40
43 some regular revisions
41 some regular revisions
44
42
45 $ hg qpop
43 $ hg qpop
46 popping p1
44 popping p1
47 patch queue now empty
45 patch queue now empty
48 $ echo foo > foo
46 $ echo foo > foo
49 $ hg add foo
47 $ hg add foo
50 $ echo foo > .hg/branch
48 $ echo foo > .hg/branch
51 $ hg ci -m 'branch foo'
49 $ hg ci -m 'branch foo'
52
50
53 $ echo bar > bar
51 $ echo bar > bar
54 $ hg add bar
52 $ hg add bar
55 $ echo bar > .hg/branch
53 $ echo bar > .hg/branch
56 $ hg ci -m 'branch bar'
54 $ hg ci -m 'branch bar'
57 $ show_branch_cache
55 $ show_branch_cache
58 tip: 1
56 tip: 1
59 c229711f16da3d7591f89b1b8d963b79bda22714 1
57 c229711f16da3d7591f89b1b8d963b79bda22714 1
60 c229711f16da3d7591f89b1b8d963b79bda22714 bar
58 c229711f16da3d7591f89b1b8d963b79bda22714 bar
61 dc25e3827021582e979f600811852e36cbe57341 foo
59 dc25e3827021582e979f600811852e36cbe57341 foo
62
60
63 add some mq patches
61 add some mq patches
64
62
65 $ hg qpush
63 $ hg qpush
66 applying p1
64 applying p1
67 now at: p1
65 now at: p1
68 $ show_branch_cache
66 $ show_branch_cache
69 tip: 2
67 tip: 2
70 982611f6955f9c48d3365decea203217c945ef0d 2
68 c229711f16da3d7591f89b1b8d963b79bda22714 1
71 982611f6955f9c48d3365decea203217c945ef0d bar
69 c229711f16da3d7591f89b1b8d963b79bda22714 bar
72 dc25e3827021582e979f600811852e36cbe57341 foo
70 dc25e3827021582e979f600811852e36cbe57341 foo
73
71
74 $ hg qnew -d '0 0' p2
72 $ hg qnew -d '0 0' p2
75 $ echo foo > .hg/branch
73 $ echo foo > .hg/branch
76 $ echo foo2 >> foo
74 $ echo foo2 >> foo
77 $ hg qrefresh -m 'patch 2'
75 $ hg qrefresh -m 'patch 2'
78 $ show_branch_cache 1
76 $ show_branch_cache 1
79 tip: 3
77 tip: 3
80 982611f6955f9c48d3365decea203217c945ef0d 2
78 c229711f16da3d7591f89b1b8d963b79bda22714 1
81 982611f6955f9c48d3365decea203217c945ef0d bar
79 c229711f16da3d7591f89b1b8d963b79bda22714 bar
82 dc25e3827021582e979f600811852e36cbe57341 foo
80 dc25e3827021582e979f600811852e36cbe57341 foo
83 branch foo: 3
81 branch foo: 3
84 branch bar: 2
82 branch bar: 2
85
83
86 removing the cache
84 removing the cache
87
85
88 $ rm $branches
86 $ rm $branches
89 $ show_branch_cache 1
87 $ show_branch_cache 1
90 tip: 3
88 tip: 3
91 c229711f16da3d7591f89b1b8d963b79bda22714 1
89 c229711f16da3d7591f89b1b8d963b79bda22714 1
92 c229711f16da3d7591f89b1b8d963b79bda22714 bar
90 c229711f16da3d7591f89b1b8d963b79bda22714 bar
93 dc25e3827021582e979f600811852e36cbe57341 foo
91 dc25e3827021582e979f600811852e36cbe57341 foo
94 branch foo: 3
92 branch foo: 3
95 branch bar: 2
93 branch bar: 2
96
94
97 importing rev 1 (the cache now ends in one of the patches)
95 importing rev 1 (the cache now ends in one of the patches)
98
96
99 $ hg qimport -r 1 -n p0
97 $ hg qimport -r 1 -n p0
100 $ show_branch_cache 1
98 $ show_branch_cache 1
101 tip: 3
99 tip: 3
102 c229711f16da3d7591f89b1b8d963b79bda22714 1
100 c229711f16da3d7591f89b1b8d963b79bda22714 1
103 c229711f16da3d7591f89b1b8d963b79bda22714 bar
101 c229711f16da3d7591f89b1b8d963b79bda22714 bar
104 dc25e3827021582e979f600811852e36cbe57341 foo
102 dc25e3827021582e979f600811852e36cbe57341 foo
105 branch foo: 3
103 branch foo: 3
106 branch bar: 2
104 branch bar: 2
107 $ hg log -r qbase --template 'qbase: {rev}\n'
105 $ hg log -r qbase --template 'qbase: {rev}\n'
108 qbase: 1
106 qbase: 1
109
107
110 detect an invalid cache
108 detect an invalid cache
111
109
112 $ hg qpop -a
110 $ hg qpop -a
113 popping p2
111 popping p2
114 popping p1
112 popping p1
115 popping p0
113 popping p0
116 patch queue now empty
114 patch queue now empty
117 $ hg qpush -a
115 $ hg qpush -a
118 applying p0
116 applying p0
119 applying p1
117 applying p1
120 applying p2
118 applying p2
121 now at: p2
119 now at: p2
122 $ show_branch_cache
120 $ show_branch_cache
123 tip: 3
121 tip: 3
124 3fe2e3b237359b5c55cec6ed172ac41d3850fade 1
122 dc25e3827021582e979f600811852e36cbe57341 0
125 3fe2e3b237359b5c55cec6ed172ac41d3850fade foo
123 dc25e3827021582e979f600811852e36cbe57341 foo
126
124
General Comments 0
You need to be logged in to leave comments. Login now