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