##// END OF EJS Templates
qpop/qrefresh: update self.applied before calling strip...
Alexis S. L. Carvalho -
r5987:f2201aee default
parent child Browse files
Show More
@@ -1,2289 +1,2290 b''
1 # queue.py - patch queues for mercurial
1 # queue.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
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 '''patch management and development
8 '''patch management and development
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 prepare repository to work with patches qinit
19 prepare repository to work with patches qinit
20 create new patch qnew
20 create new patch qnew
21 import existing patch qimport
21 import existing patch qimport
22
22
23 print patch series qseries
23 print patch series qseries
24 print applied patches qapplied
24 print applied patches qapplied
25 print name of top applied patch qtop
25 print name of top applied patch qtop
26
26
27 add known patch to applied stack qpush
27 add known patch to applied stack qpush
28 remove patch from applied stack qpop
28 remove patch from applied stack qpop
29 refresh contents of top applied patch qrefresh
29 refresh contents of top applied patch qrefresh
30 '''
30 '''
31
31
32 from mercurial.i18n import _
32 from mercurial.i18n import _
33 from mercurial import commands, cmdutil, hg, patch, revlog, util
33 from mercurial import commands, cmdutil, hg, patch, revlog, util
34 from mercurial import repair
34 from mercurial import repair
35 import os, sys, re, errno
35 import os, sys, re, errno
36
36
37 commands.norepo += " qclone qversion"
37 commands.norepo += " qclone qversion"
38
38
39 # Patch names looks like unix-file names.
39 # Patch names looks like unix-file names.
40 # They must be joinable with queue directory and result in the patch path.
40 # They must be joinable with queue directory and result in the patch path.
41 normname = util.normpath
41 normname = util.normpath
42
42
43 class statusentry:
43 class statusentry:
44 def __init__(self, rev, name=None):
44 def __init__(self, rev, name=None):
45 if not name:
45 if not name:
46 fields = rev.split(':', 1)
46 fields = rev.split(':', 1)
47 if len(fields) == 2:
47 if len(fields) == 2:
48 self.rev, self.name = fields
48 self.rev, self.name = fields
49 else:
49 else:
50 self.rev, self.name = None, None
50 self.rev, self.name = None, None
51 else:
51 else:
52 self.rev, self.name = rev, name
52 self.rev, self.name = rev, name
53
53
54 def __str__(self):
54 def __str__(self):
55 return self.rev + ':' + self.name
55 return self.rev + ':' + self.name
56
56
57 class queue:
57 class queue:
58 def __init__(self, ui, path, patchdir=None):
58 def __init__(self, ui, path, patchdir=None):
59 self.basepath = path
59 self.basepath = path
60 self.path = patchdir or os.path.join(path, "patches")
60 self.path = patchdir or os.path.join(path, "patches")
61 self.opener = util.opener(self.path)
61 self.opener = util.opener(self.path)
62 self.ui = ui
62 self.ui = ui
63 self.applied = []
63 self.applied = []
64 self.full_series = []
64 self.full_series = []
65 self.applied_dirty = 0
65 self.applied_dirty = 0
66 self.series_dirty = 0
66 self.series_dirty = 0
67 self.series_path = "series"
67 self.series_path = "series"
68 self.status_path = "status"
68 self.status_path = "status"
69 self.guards_path = "guards"
69 self.guards_path = "guards"
70 self.active_guards = None
70 self.active_guards = None
71 self.guards_dirty = False
71 self.guards_dirty = False
72 self._diffopts = None
72 self._diffopts = None
73
73
74 if os.path.exists(self.join(self.series_path)):
74 if os.path.exists(self.join(self.series_path)):
75 self.full_series = self.opener(self.series_path).read().splitlines()
75 self.full_series = self.opener(self.series_path).read().splitlines()
76 self.parse_series()
76 self.parse_series()
77
77
78 if os.path.exists(self.join(self.status_path)):
78 if os.path.exists(self.join(self.status_path)):
79 lines = self.opener(self.status_path).read().splitlines()
79 lines = self.opener(self.status_path).read().splitlines()
80 self.applied = [statusentry(l) for l in lines]
80 self.applied = [statusentry(l) for l in lines]
81
81
82 def diffopts(self):
82 def diffopts(self):
83 if self._diffopts is None:
83 if self._diffopts is None:
84 self._diffopts = patch.diffopts(self.ui)
84 self._diffopts = patch.diffopts(self.ui)
85 return self._diffopts
85 return self._diffopts
86
86
87 def join(self, *p):
87 def join(self, *p):
88 return os.path.join(self.path, *p)
88 return os.path.join(self.path, *p)
89
89
90 def find_series(self, patch):
90 def find_series(self, patch):
91 pre = re.compile("(\s*)([^#]+)")
91 pre = re.compile("(\s*)([^#]+)")
92 index = 0
92 index = 0
93 for l in self.full_series:
93 for l in self.full_series:
94 m = pre.match(l)
94 m = pre.match(l)
95 if m:
95 if m:
96 s = m.group(2)
96 s = m.group(2)
97 s = s.rstrip()
97 s = s.rstrip()
98 if s == patch:
98 if s == patch:
99 return index
99 return index
100 index += 1
100 index += 1
101 return None
101 return None
102
102
103 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
103 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
104
104
105 def parse_series(self):
105 def parse_series(self):
106 self.series = []
106 self.series = []
107 self.series_guards = []
107 self.series_guards = []
108 for l in self.full_series:
108 for l in self.full_series:
109 h = l.find('#')
109 h = l.find('#')
110 if h == -1:
110 if h == -1:
111 patch = l
111 patch = l
112 comment = ''
112 comment = ''
113 elif h == 0:
113 elif h == 0:
114 continue
114 continue
115 else:
115 else:
116 patch = l[:h]
116 patch = l[:h]
117 comment = l[h:]
117 comment = l[h:]
118 patch = patch.strip()
118 patch = patch.strip()
119 if patch:
119 if patch:
120 if patch in self.series:
120 if patch in self.series:
121 raise util.Abort(_('%s appears more than once in %s') %
121 raise util.Abort(_('%s appears more than once in %s') %
122 (patch, self.join(self.series_path)))
122 (patch, self.join(self.series_path)))
123 self.series.append(patch)
123 self.series.append(patch)
124 self.series_guards.append(self.guard_re.findall(comment))
124 self.series_guards.append(self.guard_re.findall(comment))
125
125
126 def check_guard(self, guard):
126 def check_guard(self, guard):
127 bad_chars = '# \t\r\n\f'
127 bad_chars = '# \t\r\n\f'
128 first = guard[0]
128 first = guard[0]
129 for c in '-+':
129 for c in '-+':
130 if first == c:
130 if first == c:
131 return (_('guard %r starts with invalid character: %r') %
131 return (_('guard %r starts with invalid character: %r') %
132 (guard, c))
132 (guard, c))
133 for c in bad_chars:
133 for c in bad_chars:
134 if c in guard:
134 if c in guard:
135 return _('invalid character in guard %r: %r') % (guard, c)
135 return _('invalid character in guard %r: %r') % (guard, c)
136
136
137 def set_active(self, guards):
137 def set_active(self, guards):
138 for guard in guards:
138 for guard in guards:
139 bad = self.check_guard(guard)
139 bad = self.check_guard(guard)
140 if bad:
140 if bad:
141 raise util.Abort(bad)
141 raise util.Abort(bad)
142 guards = dict.fromkeys(guards).keys()
142 guards = dict.fromkeys(guards).keys()
143 guards.sort()
143 guards.sort()
144 self.ui.debug('active guards: %s\n' % ' '.join(guards))
144 self.ui.debug('active guards: %s\n' % ' '.join(guards))
145 self.active_guards = guards
145 self.active_guards = guards
146 self.guards_dirty = True
146 self.guards_dirty = True
147
147
148 def active(self):
148 def active(self):
149 if self.active_guards is None:
149 if self.active_guards is None:
150 self.active_guards = []
150 self.active_guards = []
151 try:
151 try:
152 guards = self.opener(self.guards_path).read().split()
152 guards = self.opener(self.guards_path).read().split()
153 except IOError, err:
153 except IOError, err:
154 if err.errno != errno.ENOENT: raise
154 if err.errno != errno.ENOENT: raise
155 guards = []
155 guards = []
156 for i, guard in enumerate(guards):
156 for i, guard in enumerate(guards):
157 bad = self.check_guard(guard)
157 bad = self.check_guard(guard)
158 if bad:
158 if bad:
159 self.ui.warn('%s:%d: %s\n' %
159 self.ui.warn('%s:%d: %s\n' %
160 (self.join(self.guards_path), i + 1, bad))
160 (self.join(self.guards_path), i + 1, bad))
161 else:
161 else:
162 self.active_guards.append(guard)
162 self.active_guards.append(guard)
163 return self.active_guards
163 return self.active_guards
164
164
165 def set_guards(self, idx, guards):
165 def set_guards(self, idx, guards):
166 for g in guards:
166 for g in guards:
167 if len(g) < 2:
167 if len(g) < 2:
168 raise util.Abort(_('guard %r too short') % g)
168 raise util.Abort(_('guard %r too short') % g)
169 if g[0] not in '-+':
169 if g[0] not in '-+':
170 raise util.Abort(_('guard %r starts with invalid char') % g)
170 raise util.Abort(_('guard %r starts with invalid char') % g)
171 bad = self.check_guard(g[1:])
171 bad = self.check_guard(g[1:])
172 if bad:
172 if bad:
173 raise util.Abort(bad)
173 raise util.Abort(bad)
174 drop = self.guard_re.sub('', self.full_series[idx])
174 drop = self.guard_re.sub('', self.full_series[idx])
175 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
175 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
176 self.parse_series()
176 self.parse_series()
177 self.series_dirty = True
177 self.series_dirty = True
178
178
179 def pushable(self, idx):
179 def pushable(self, idx):
180 if isinstance(idx, str):
180 if isinstance(idx, str):
181 idx = self.series.index(idx)
181 idx = self.series.index(idx)
182 patchguards = self.series_guards[idx]
182 patchguards = self.series_guards[idx]
183 if not patchguards:
183 if not patchguards:
184 return True, None
184 return True, None
185 default = False
185 default = False
186 guards = self.active()
186 guards = self.active()
187 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
187 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
188 if exactneg:
188 if exactneg:
189 return False, exactneg[0]
189 return False, exactneg[0]
190 pos = [g for g in patchguards if g[0] == '+']
190 pos = [g for g in patchguards if g[0] == '+']
191 exactpos = [g for g in pos if g[1:] in guards]
191 exactpos = [g for g in pos if g[1:] in guards]
192 if pos:
192 if pos:
193 if exactpos:
193 if exactpos:
194 return True, exactpos[0]
194 return True, exactpos[0]
195 return False, pos
195 return False, pos
196 return True, ''
196 return True, ''
197
197
198 def explain_pushable(self, idx, all_patches=False):
198 def explain_pushable(self, idx, all_patches=False):
199 write = all_patches and self.ui.write or self.ui.warn
199 write = all_patches and self.ui.write or self.ui.warn
200 if all_patches or self.ui.verbose:
200 if all_patches or self.ui.verbose:
201 if isinstance(idx, str):
201 if isinstance(idx, str):
202 idx = self.series.index(idx)
202 idx = self.series.index(idx)
203 pushable, why = self.pushable(idx)
203 pushable, why = self.pushable(idx)
204 if all_patches and pushable:
204 if all_patches and pushable:
205 if why is None:
205 if why is None:
206 write(_('allowing %s - no guards in effect\n') %
206 write(_('allowing %s - no guards in effect\n') %
207 self.series[idx])
207 self.series[idx])
208 else:
208 else:
209 if not why:
209 if not why:
210 write(_('allowing %s - no matching negative guards\n') %
210 write(_('allowing %s - no matching negative guards\n') %
211 self.series[idx])
211 self.series[idx])
212 else:
212 else:
213 write(_('allowing %s - guarded by %r\n') %
213 write(_('allowing %s - guarded by %r\n') %
214 (self.series[idx], why))
214 (self.series[idx], why))
215 if not pushable:
215 if not pushable:
216 if why:
216 if why:
217 write(_('skipping %s - guarded by %r\n') %
217 write(_('skipping %s - guarded by %r\n') %
218 (self.series[idx], why))
218 (self.series[idx], why))
219 else:
219 else:
220 write(_('skipping %s - no matching guards\n') %
220 write(_('skipping %s - no matching guards\n') %
221 self.series[idx])
221 self.series[idx])
222
222
223 def save_dirty(self):
223 def save_dirty(self):
224 def write_list(items, path):
224 def write_list(items, path):
225 fp = self.opener(path, 'w')
225 fp = self.opener(path, 'w')
226 for i in items:
226 for i in items:
227 print >> fp, i
227 print >> fp, i
228 fp.close()
228 fp.close()
229 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
229 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
230 if self.series_dirty: write_list(self.full_series, self.series_path)
230 if self.series_dirty: write_list(self.full_series, self.series_path)
231 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
231 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
232
232
233 def readheaders(self, patch):
233 def readheaders(self, patch):
234 def eatdiff(lines):
234 def eatdiff(lines):
235 while lines:
235 while lines:
236 l = lines[-1]
236 l = lines[-1]
237 if (l.startswith("diff -") or
237 if (l.startswith("diff -") or
238 l.startswith("Index:") or
238 l.startswith("Index:") or
239 l.startswith("===========")):
239 l.startswith("===========")):
240 del lines[-1]
240 del lines[-1]
241 else:
241 else:
242 break
242 break
243 def eatempty(lines):
243 def eatempty(lines):
244 while lines:
244 while lines:
245 l = lines[-1]
245 l = lines[-1]
246 if re.match('\s*$', l):
246 if re.match('\s*$', l):
247 del lines[-1]
247 del lines[-1]
248 else:
248 else:
249 break
249 break
250
250
251 pf = self.join(patch)
251 pf = self.join(patch)
252 message = []
252 message = []
253 comments = []
253 comments = []
254 user = None
254 user = None
255 date = None
255 date = None
256 format = None
256 format = None
257 subject = None
257 subject = None
258 diffstart = 0
258 diffstart = 0
259
259
260 for line in file(pf):
260 for line in file(pf):
261 line = line.rstrip()
261 line = line.rstrip()
262 if line.startswith('diff --git'):
262 if line.startswith('diff --git'):
263 diffstart = 2
263 diffstart = 2
264 break
264 break
265 if diffstart:
265 if diffstart:
266 if line.startswith('+++ '):
266 if line.startswith('+++ '):
267 diffstart = 2
267 diffstart = 2
268 break
268 break
269 if line.startswith("--- "):
269 if line.startswith("--- "):
270 diffstart = 1
270 diffstart = 1
271 continue
271 continue
272 elif format == "hgpatch":
272 elif format == "hgpatch":
273 # parse values when importing the result of an hg export
273 # parse values when importing the result of an hg export
274 if line.startswith("# User "):
274 if line.startswith("# User "):
275 user = line[7:]
275 user = line[7:]
276 elif line.startswith("# Date "):
276 elif line.startswith("# Date "):
277 date = line[7:]
277 date = line[7:]
278 elif not line.startswith("# ") and line:
278 elif not line.startswith("# ") and line:
279 message.append(line)
279 message.append(line)
280 format = None
280 format = None
281 elif line == '# HG changeset patch':
281 elif line == '# HG changeset patch':
282 format = "hgpatch"
282 format = "hgpatch"
283 elif (format != "tagdone" and (line.startswith("Subject: ") or
283 elif (format != "tagdone" and (line.startswith("Subject: ") or
284 line.startswith("subject: "))):
284 line.startswith("subject: "))):
285 subject = line[9:]
285 subject = line[9:]
286 format = "tag"
286 format = "tag"
287 elif (format != "tagdone" and (line.startswith("From: ") or
287 elif (format != "tagdone" and (line.startswith("From: ") or
288 line.startswith("from: "))):
288 line.startswith("from: "))):
289 user = line[6:]
289 user = line[6:]
290 format = "tag"
290 format = "tag"
291 elif format == "tag" and line == "":
291 elif format == "tag" and line == "":
292 # when looking for tags (subject: from: etc) they
292 # when looking for tags (subject: from: etc) they
293 # end once you find a blank line in the source
293 # end once you find a blank line in the source
294 format = "tagdone"
294 format = "tagdone"
295 elif message or line:
295 elif message or line:
296 message.append(line)
296 message.append(line)
297 comments.append(line)
297 comments.append(line)
298
298
299 eatdiff(message)
299 eatdiff(message)
300 eatdiff(comments)
300 eatdiff(comments)
301 eatempty(message)
301 eatempty(message)
302 eatempty(comments)
302 eatempty(comments)
303
303
304 # make sure message isn't empty
304 # make sure message isn't empty
305 if format and format.startswith("tag") and subject:
305 if format and format.startswith("tag") and subject:
306 message.insert(0, "")
306 message.insert(0, "")
307 message.insert(0, subject)
307 message.insert(0, subject)
308 return (message, comments, user, date, diffstart > 1)
308 return (message, comments, user, date, diffstart > 1)
309
309
310 def removeundo(self, repo):
310 def removeundo(self, repo):
311 undo = repo.sjoin('undo')
311 undo = repo.sjoin('undo')
312 if not os.path.exists(undo):
312 if not os.path.exists(undo):
313 return
313 return
314 try:
314 try:
315 os.unlink(undo)
315 os.unlink(undo)
316 except OSError, inst:
316 except OSError, inst:
317 self.ui.warn('error removing undo: %s\n' % str(inst))
317 self.ui.warn('error removing undo: %s\n' % str(inst))
318
318
319 def printdiff(self, repo, node1, node2=None, files=None,
319 def printdiff(self, repo, node1, node2=None, files=None,
320 fp=None, changes=None, opts={}):
320 fp=None, changes=None, opts={}):
321 fns, matchfn, anypats = cmdutil.matchpats(repo, files, opts)
321 fns, matchfn, anypats = cmdutil.matchpats(repo, files, opts)
322
322
323 patch.diff(repo, node1, node2, fns, match=matchfn,
323 patch.diff(repo, node1, node2, fns, match=matchfn,
324 fp=fp, changes=changes, opts=self.diffopts())
324 fp=fp, changes=changes, opts=self.diffopts())
325
325
326 def mergeone(self, repo, mergeq, head, patch, rev):
326 def mergeone(self, repo, mergeq, head, patch, rev):
327 # first try just applying the patch
327 # first try just applying the patch
328 (err, n) = self.apply(repo, [ patch ], update_status=False,
328 (err, n) = self.apply(repo, [ patch ], update_status=False,
329 strict=True, merge=rev)
329 strict=True, merge=rev)
330
330
331 if err == 0:
331 if err == 0:
332 return (err, n)
332 return (err, n)
333
333
334 if n is None:
334 if n is None:
335 raise util.Abort(_("apply failed for patch %s") % patch)
335 raise util.Abort(_("apply failed for patch %s") % patch)
336
336
337 self.ui.warn("patch didn't work out, merging %s\n" % patch)
337 self.ui.warn("patch didn't work out, merging %s\n" % patch)
338
338
339 # apply failed, strip away that rev and merge.
339 # apply failed, strip away that rev and merge.
340 hg.clean(repo, head)
340 hg.clean(repo, head)
341 self.strip(repo, n, update=False, backup='strip')
341 self.strip(repo, n, update=False, backup='strip')
342
342
343 ctx = repo.changectx(rev)
343 ctx = repo.changectx(rev)
344 ret = hg.merge(repo, rev)
344 ret = hg.merge(repo, rev)
345 if ret:
345 if ret:
346 raise util.Abort(_("update returned %d") % ret)
346 raise util.Abort(_("update returned %d") % ret)
347 n = repo.commit(None, ctx.description(), ctx.user(), force=1)
347 n = repo.commit(None, ctx.description(), ctx.user(), force=1)
348 if n == None:
348 if n == None:
349 raise util.Abort(_("repo commit failed"))
349 raise util.Abort(_("repo commit failed"))
350 try:
350 try:
351 message, comments, user, date, patchfound = mergeq.readheaders(patch)
351 message, comments, user, date, patchfound = mergeq.readheaders(patch)
352 except:
352 except:
353 raise util.Abort(_("unable to read %s") % patch)
353 raise util.Abort(_("unable to read %s") % patch)
354
354
355 patchf = self.opener(patch, "w")
355 patchf = self.opener(patch, "w")
356 if comments:
356 if comments:
357 comments = "\n".join(comments) + '\n\n'
357 comments = "\n".join(comments) + '\n\n'
358 patchf.write(comments)
358 patchf.write(comments)
359 self.printdiff(repo, head, n, fp=patchf)
359 self.printdiff(repo, head, n, fp=patchf)
360 patchf.close()
360 patchf.close()
361 self.removeundo(repo)
361 self.removeundo(repo)
362 return (0, n)
362 return (0, n)
363
363
364 def qparents(self, repo, rev=None):
364 def qparents(self, repo, rev=None):
365 if rev is None:
365 if rev is None:
366 (p1, p2) = repo.dirstate.parents()
366 (p1, p2) = repo.dirstate.parents()
367 if p2 == revlog.nullid:
367 if p2 == revlog.nullid:
368 return p1
368 return p1
369 if len(self.applied) == 0:
369 if len(self.applied) == 0:
370 return None
370 return None
371 return revlog.bin(self.applied[-1].rev)
371 return revlog.bin(self.applied[-1].rev)
372 pp = repo.changelog.parents(rev)
372 pp = repo.changelog.parents(rev)
373 if pp[1] != revlog.nullid:
373 if pp[1] != revlog.nullid:
374 arevs = [ x.rev for x in self.applied ]
374 arevs = [ x.rev for x in self.applied ]
375 p0 = revlog.hex(pp[0])
375 p0 = revlog.hex(pp[0])
376 p1 = revlog.hex(pp[1])
376 p1 = revlog.hex(pp[1])
377 if p0 in arevs:
377 if p0 in arevs:
378 return pp[0]
378 return pp[0]
379 if p1 in arevs:
379 if p1 in arevs:
380 return pp[1]
380 return pp[1]
381 return pp[0]
381 return pp[0]
382
382
383 def mergepatch(self, repo, mergeq, series):
383 def mergepatch(self, repo, mergeq, series):
384 if len(self.applied) == 0:
384 if len(self.applied) == 0:
385 # each of the patches merged in will have two parents. This
385 # each of the patches merged in will have two parents. This
386 # can confuse the qrefresh, qdiff, and strip code because it
386 # can confuse the qrefresh, qdiff, and strip code because it
387 # needs to know which parent is actually in the patch queue.
387 # needs to know which parent is actually in the patch queue.
388 # so, we insert a merge marker with only one parent. This way
388 # so, we insert a merge marker with only one parent. This way
389 # the first patch in the queue is never a merge patch
389 # the first patch in the queue is never a merge patch
390 #
390 #
391 pname = ".hg.patches.merge.marker"
391 pname = ".hg.patches.merge.marker"
392 n = repo.commit(None, '[mq]: merge marker', user=None, force=1)
392 n = repo.commit(None, '[mq]: merge marker', user=None, force=1)
393 self.removeundo(repo)
393 self.removeundo(repo)
394 self.applied.append(statusentry(revlog.hex(n), pname))
394 self.applied.append(statusentry(revlog.hex(n), pname))
395 self.applied_dirty = 1
395 self.applied_dirty = 1
396
396
397 head = self.qparents(repo)
397 head = self.qparents(repo)
398
398
399 for patch in series:
399 for patch in series:
400 patch = mergeq.lookup(patch, strict=True)
400 patch = mergeq.lookup(patch, strict=True)
401 if not patch:
401 if not patch:
402 self.ui.warn("patch %s does not exist\n" % patch)
402 self.ui.warn("patch %s does not exist\n" % patch)
403 return (1, None)
403 return (1, None)
404 pushable, reason = self.pushable(patch)
404 pushable, reason = self.pushable(patch)
405 if not pushable:
405 if not pushable:
406 self.explain_pushable(patch, all_patches=True)
406 self.explain_pushable(patch, all_patches=True)
407 continue
407 continue
408 info = mergeq.isapplied(patch)
408 info = mergeq.isapplied(patch)
409 if not info:
409 if not info:
410 self.ui.warn("patch %s is not applied\n" % patch)
410 self.ui.warn("patch %s is not applied\n" % patch)
411 return (1, None)
411 return (1, None)
412 rev = revlog.bin(info[1])
412 rev = revlog.bin(info[1])
413 (err, head) = self.mergeone(repo, mergeq, head, patch, rev)
413 (err, head) = self.mergeone(repo, mergeq, head, patch, rev)
414 if head:
414 if head:
415 self.applied.append(statusentry(revlog.hex(head), patch))
415 self.applied.append(statusentry(revlog.hex(head), patch))
416 self.applied_dirty = 1
416 self.applied_dirty = 1
417 if err:
417 if err:
418 return (err, head)
418 return (err, head)
419 self.save_dirty()
419 self.save_dirty()
420 return (0, head)
420 return (0, head)
421
421
422 def patch(self, repo, patchfile):
422 def patch(self, repo, patchfile):
423 '''Apply patchfile to the working directory.
423 '''Apply patchfile to the working directory.
424 patchfile: file name of patch'''
424 patchfile: file name of patch'''
425 files = {}
425 files = {}
426 try:
426 try:
427 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
427 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
428 files=files)
428 files=files)
429 except Exception, inst:
429 except Exception, inst:
430 self.ui.note(str(inst) + '\n')
430 self.ui.note(str(inst) + '\n')
431 if not self.ui.verbose:
431 if not self.ui.verbose:
432 self.ui.warn("patch failed, unable to continue (try -v)\n")
432 self.ui.warn("patch failed, unable to continue (try -v)\n")
433 return (False, files, False)
433 return (False, files, False)
434
434
435 return (True, files, fuzz)
435 return (True, files, fuzz)
436
436
437 def apply(self, repo, series, list=False, update_status=True,
437 def apply(self, repo, series, list=False, update_status=True,
438 strict=False, patchdir=None, merge=None, all_files={}):
438 strict=False, patchdir=None, merge=None, all_files={}):
439 wlock = lock = tr = None
439 wlock = lock = tr = None
440 try:
440 try:
441 wlock = repo.wlock()
441 wlock = repo.wlock()
442 lock = repo.lock()
442 lock = repo.lock()
443 tr = repo.transaction()
443 tr = repo.transaction()
444 try:
444 try:
445 ret = self._apply(repo, series, list, update_status,
445 ret = self._apply(repo, series, list, update_status,
446 strict, patchdir, merge, all_files=all_files)
446 strict, patchdir, merge, all_files=all_files)
447 tr.close()
447 tr.close()
448 self.save_dirty()
448 self.save_dirty()
449 return ret
449 return ret
450 except:
450 except:
451 try:
451 try:
452 tr.abort()
452 tr.abort()
453 finally:
453 finally:
454 repo.invalidate()
454 repo.invalidate()
455 repo.dirstate.invalidate()
455 repo.dirstate.invalidate()
456 raise
456 raise
457 finally:
457 finally:
458 del tr, lock, wlock
458 del tr, lock, wlock
459 self.removeundo(repo)
459 self.removeundo(repo)
460
460
461 def _apply(self, repo, series, list=False, update_status=True,
461 def _apply(self, repo, series, list=False, update_status=True,
462 strict=False, patchdir=None, merge=None, all_files={}):
462 strict=False, patchdir=None, merge=None, all_files={}):
463 # TODO unify with commands.py
463 # TODO unify with commands.py
464 if not patchdir:
464 if not patchdir:
465 patchdir = self.path
465 patchdir = self.path
466 err = 0
466 err = 0
467 n = None
467 n = None
468 for patchname in series:
468 for patchname in series:
469 pushable, reason = self.pushable(patchname)
469 pushable, reason = self.pushable(patchname)
470 if not pushable:
470 if not pushable:
471 self.explain_pushable(patchname, all_patches=True)
471 self.explain_pushable(patchname, all_patches=True)
472 continue
472 continue
473 self.ui.warn("applying %s\n" % patchname)
473 self.ui.warn("applying %s\n" % patchname)
474 pf = os.path.join(patchdir, patchname)
474 pf = os.path.join(patchdir, patchname)
475
475
476 try:
476 try:
477 message, comments, user, date, patchfound = self.readheaders(patchname)
477 message, comments, user, date, patchfound = self.readheaders(patchname)
478 except:
478 except:
479 self.ui.warn("Unable to read %s\n" % patchname)
479 self.ui.warn("Unable to read %s\n" % patchname)
480 err = 1
480 err = 1
481 break
481 break
482
482
483 if not message:
483 if not message:
484 message = "imported patch %s\n" % patchname
484 message = "imported patch %s\n" % patchname
485 else:
485 else:
486 if list:
486 if list:
487 message.append("\nimported patch %s" % patchname)
487 message.append("\nimported patch %s" % patchname)
488 message = '\n'.join(message)
488 message = '\n'.join(message)
489
489
490 (patcherr, files, fuzz) = self.patch(repo, pf)
490 (patcherr, files, fuzz) = self.patch(repo, pf)
491 all_files.update(files)
491 all_files.update(files)
492 patcherr = not patcherr
492 patcherr = not patcherr
493
493
494 if merge and files:
494 if merge and files:
495 # Mark as removed/merged and update dirstate parent info
495 # Mark as removed/merged and update dirstate parent info
496 removed = []
496 removed = []
497 merged = []
497 merged = []
498 for f in files:
498 for f in files:
499 if os.path.exists(repo.wjoin(f)):
499 if os.path.exists(repo.wjoin(f)):
500 merged.append(f)
500 merged.append(f)
501 else:
501 else:
502 removed.append(f)
502 removed.append(f)
503 for f in removed:
503 for f in removed:
504 repo.dirstate.remove(f)
504 repo.dirstate.remove(f)
505 for f in merged:
505 for f in merged:
506 repo.dirstate.merge(f)
506 repo.dirstate.merge(f)
507 p1, p2 = repo.dirstate.parents()
507 p1, p2 = repo.dirstate.parents()
508 repo.dirstate.setparents(p1, merge)
508 repo.dirstate.setparents(p1, merge)
509 files = patch.updatedir(self.ui, repo, files)
509 files = patch.updatedir(self.ui, repo, files)
510 n = repo.commit(files, message, user, date, force=1)
510 n = repo.commit(files, message, user, date, force=1)
511
511
512 if n == None:
512 if n == None:
513 raise util.Abort(_("repo commit failed"))
513 raise util.Abort(_("repo commit failed"))
514
514
515 if update_status:
515 if update_status:
516 self.applied.append(statusentry(revlog.hex(n), patchname))
516 self.applied.append(statusentry(revlog.hex(n), patchname))
517
517
518 if patcherr:
518 if patcherr:
519 if not patchfound:
519 if not patchfound:
520 self.ui.warn("patch %s is empty\n" % patchname)
520 self.ui.warn("patch %s is empty\n" % patchname)
521 err = 0
521 err = 0
522 else:
522 else:
523 self.ui.warn("patch failed, rejects left in working dir\n")
523 self.ui.warn("patch failed, rejects left in working dir\n")
524 err = 1
524 err = 1
525 break
525 break
526
526
527 if fuzz and strict:
527 if fuzz and strict:
528 self.ui.warn("fuzz found when applying patch, stopping\n")
528 self.ui.warn("fuzz found when applying patch, stopping\n")
529 err = 1
529 err = 1
530 break
530 break
531 return (err, n)
531 return (err, n)
532
532
533 def delete(self, repo, patches, opts):
533 def delete(self, repo, patches, opts):
534 if not patches and not opts.get('rev'):
534 if not patches and not opts.get('rev'):
535 raise util.Abort(_('qdelete requires at least one revision or '
535 raise util.Abort(_('qdelete requires at least one revision or '
536 'patch name'))
536 'patch name'))
537
537
538 realpatches = []
538 realpatches = []
539 for patch in patches:
539 for patch in patches:
540 patch = self.lookup(patch, strict=True)
540 patch = self.lookup(patch, strict=True)
541 info = self.isapplied(patch)
541 info = self.isapplied(patch)
542 if info:
542 if info:
543 raise util.Abort(_("cannot delete applied patch %s") % patch)
543 raise util.Abort(_("cannot delete applied patch %s") % patch)
544 if patch not in self.series:
544 if patch not in self.series:
545 raise util.Abort(_("patch %s not in series file") % patch)
545 raise util.Abort(_("patch %s not in series file") % patch)
546 realpatches.append(patch)
546 realpatches.append(patch)
547
547
548 appliedbase = 0
548 appliedbase = 0
549 if opts.get('rev'):
549 if opts.get('rev'):
550 if not self.applied:
550 if not self.applied:
551 raise util.Abort(_('no patches applied'))
551 raise util.Abort(_('no patches applied'))
552 revs = cmdutil.revrange(repo, opts['rev'])
552 revs = cmdutil.revrange(repo, opts['rev'])
553 if len(revs) > 1 and revs[0] > revs[1]:
553 if len(revs) > 1 and revs[0] > revs[1]:
554 revs.reverse()
554 revs.reverse()
555 for rev in revs:
555 for rev in revs:
556 if appliedbase >= len(self.applied):
556 if appliedbase >= len(self.applied):
557 raise util.Abort(_("revision %d is not managed") % rev)
557 raise util.Abort(_("revision %d is not managed") % rev)
558
558
559 base = revlog.bin(self.applied[appliedbase].rev)
559 base = revlog.bin(self.applied[appliedbase].rev)
560 node = repo.changelog.node(rev)
560 node = repo.changelog.node(rev)
561 if node != base:
561 if node != base:
562 raise util.Abort(_("cannot delete revision %d above "
562 raise util.Abort(_("cannot delete revision %d above "
563 "applied patches") % rev)
563 "applied patches") % rev)
564 realpatches.append(self.applied[appliedbase].name)
564 realpatches.append(self.applied[appliedbase].name)
565 appliedbase += 1
565 appliedbase += 1
566
566
567 if not opts.get('keep'):
567 if not opts.get('keep'):
568 r = self.qrepo()
568 r = self.qrepo()
569 if r:
569 if r:
570 r.remove(realpatches, True)
570 r.remove(realpatches, True)
571 else:
571 else:
572 for p in realpatches:
572 for p in realpatches:
573 os.unlink(self.join(p))
573 os.unlink(self.join(p))
574
574
575 if appliedbase:
575 if appliedbase:
576 del self.applied[:appliedbase]
576 del self.applied[:appliedbase]
577 self.applied_dirty = 1
577 self.applied_dirty = 1
578 indices = [self.find_series(p) for p in realpatches]
578 indices = [self.find_series(p) for p in realpatches]
579 indices.sort()
579 indices.sort()
580 for i in indices[-1::-1]:
580 for i in indices[-1::-1]:
581 del self.full_series[i]
581 del self.full_series[i]
582 self.parse_series()
582 self.parse_series()
583 self.series_dirty = 1
583 self.series_dirty = 1
584
584
585 def check_toppatch(self, repo):
585 def check_toppatch(self, repo):
586 if len(self.applied) > 0:
586 if len(self.applied) > 0:
587 top = revlog.bin(self.applied[-1].rev)
587 top = revlog.bin(self.applied[-1].rev)
588 pp = repo.dirstate.parents()
588 pp = repo.dirstate.parents()
589 if top not in pp:
589 if top not in pp:
590 raise util.Abort(_("working directory revision is not qtip"))
590 raise util.Abort(_("working directory revision is not qtip"))
591 return top
591 return top
592 return None
592 return None
593 def check_localchanges(self, repo, force=False, refresh=True):
593 def check_localchanges(self, repo, force=False, refresh=True):
594 m, a, r, d = repo.status()[:4]
594 m, a, r, d = repo.status()[:4]
595 if m or a or r or d:
595 if m or a or r or d:
596 if not force:
596 if not force:
597 if refresh:
597 if refresh:
598 raise util.Abort(_("local changes found, refresh first"))
598 raise util.Abort(_("local changes found, refresh first"))
599 else:
599 else:
600 raise util.Abort(_("local changes found"))
600 raise util.Abort(_("local changes found"))
601 return m, a, r, d
601 return m, a, r, d
602
602
603 _reserved = ('series', 'status', 'guards')
603 _reserved = ('series', 'status', 'guards')
604 def check_reserved_name(self, name):
604 def check_reserved_name(self, name):
605 if (name in self._reserved or name.startswith('.hg')
605 if (name in self._reserved or name.startswith('.hg')
606 or name.startswith('.mq')):
606 or name.startswith('.mq')):
607 raise util.Abort(_('"%s" cannot be used as the name of a patch')
607 raise util.Abort(_('"%s" cannot be used as the name of a patch')
608 % name)
608 % name)
609
609
610 def new(self, repo, patch, *pats, **opts):
610 def new(self, repo, patch, *pats, **opts):
611 msg = opts.get('msg')
611 msg = opts.get('msg')
612 force = opts.get('force')
612 force = opts.get('force')
613 self.check_reserved_name(patch)
613 self.check_reserved_name(patch)
614 if os.path.exists(self.join(patch)):
614 if os.path.exists(self.join(patch)):
615 raise util.Abort(_('patch "%s" already exists') % patch)
615 raise util.Abort(_('patch "%s" already exists') % patch)
616 if opts.get('include') or opts.get('exclude') or pats:
616 if opts.get('include') or opts.get('exclude') or pats:
617 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
617 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
618 m, a, r, d = repo.status(files=fns, match=match)[:4]
618 m, a, r, d = repo.status(files=fns, match=match)[:4]
619 else:
619 else:
620 m, a, r, d = self.check_localchanges(repo, force)
620 m, a, r, d = self.check_localchanges(repo, force)
621 fns, match, anypats = cmdutil.matchpats(repo, m + a + r)
621 fns, match, anypats = cmdutil.matchpats(repo, m + a + r)
622 commitfiles = m + a + r
622 commitfiles = m + a + r
623 self.check_toppatch(repo)
623 self.check_toppatch(repo)
624 wlock = repo.wlock()
624 wlock = repo.wlock()
625 try:
625 try:
626 insert = self.full_series_end()
626 insert = self.full_series_end()
627 commitmsg = msg and msg or ("[mq]: %s" % patch)
627 commitmsg = msg and msg or ("[mq]: %s" % patch)
628 n = repo.commit(commitfiles, commitmsg, match=match, force=True)
628 n = repo.commit(commitfiles, commitmsg, match=match, force=True)
629 if n == None:
629 if n == None:
630 raise util.Abort(_("repo commit failed"))
630 raise util.Abort(_("repo commit failed"))
631 self.full_series[insert:insert] = [patch]
631 self.full_series[insert:insert] = [patch]
632 self.applied.append(statusentry(revlog.hex(n), patch))
632 self.applied.append(statusentry(revlog.hex(n), patch))
633 self.parse_series()
633 self.parse_series()
634 self.series_dirty = 1
634 self.series_dirty = 1
635 self.applied_dirty = 1
635 self.applied_dirty = 1
636 p = self.opener(patch, "w")
636 p = self.opener(patch, "w")
637 if msg:
637 if msg:
638 msg = msg + "\n"
638 msg = msg + "\n"
639 p.write(msg)
639 p.write(msg)
640 p.close()
640 p.close()
641 wlock = None
641 wlock = None
642 r = self.qrepo()
642 r = self.qrepo()
643 if r: r.add([patch])
643 if r: r.add([patch])
644 if commitfiles:
644 if commitfiles:
645 self.refresh(repo, short=True, git=opts.get('git'))
645 self.refresh(repo, short=True, git=opts.get('git'))
646 self.removeundo(repo)
646 self.removeundo(repo)
647 finally:
647 finally:
648 del wlock
648 del wlock
649
649
650 def strip(self, repo, rev, update=True, backup="all"):
650 def strip(self, repo, rev, update=True, backup="all"):
651 wlock = lock = None
651 wlock = lock = None
652 try:
652 try:
653 wlock = repo.wlock()
653 wlock = repo.wlock()
654 lock = repo.lock()
654 lock = repo.lock()
655
655
656 if update:
656 if update:
657 self.check_localchanges(repo, refresh=False)
657 self.check_localchanges(repo, refresh=False)
658 urev = self.qparents(repo, rev)
658 urev = self.qparents(repo, rev)
659 hg.clean(repo, urev)
659 hg.clean(repo, urev)
660 repo.dirstate.write()
660 repo.dirstate.write()
661
661
662 self.removeundo(repo)
662 self.removeundo(repo)
663 repair.strip(self.ui, repo, rev, backup)
663 repair.strip(self.ui, repo, rev, backup)
664 # strip may have unbundled a set of backed up revisions after
664 # strip may have unbundled a set of backed up revisions after
665 # the actual strip
665 # the actual strip
666 self.removeundo(repo)
666 self.removeundo(repo)
667 finally:
667 finally:
668 del lock, wlock
668 del lock, wlock
669
669
670 def isapplied(self, patch):
670 def isapplied(self, patch):
671 """returns (index, rev, patch)"""
671 """returns (index, rev, patch)"""
672 for i in xrange(len(self.applied)):
672 for i in xrange(len(self.applied)):
673 a = self.applied[i]
673 a = self.applied[i]
674 if a.name == patch:
674 if a.name == patch:
675 return (i, a.rev, a.name)
675 return (i, a.rev, a.name)
676 return None
676 return None
677
677
678 # if the exact patch name does not exist, we try a few
678 # if the exact patch name does not exist, we try a few
679 # variations. If strict is passed, we try only #1
679 # variations. If strict is passed, we try only #1
680 #
680 #
681 # 1) a number to indicate an offset in the series file
681 # 1) a number to indicate an offset in the series file
682 # 2) a unique substring of the patch name was given
682 # 2) a unique substring of the patch name was given
683 # 3) patchname[-+]num to indicate an offset in the series file
683 # 3) patchname[-+]num to indicate an offset in the series file
684 def lookup(self, patch, strict=False):
684 def lookup(self, patch, strict=False):
685 patch = patch and str(patch)
685 patch = patch and str(patch)
686
686
687 def partial_name(s):
687 def partial_name(s):
688 if s in self.series:
688 if s in self.series:
689 return s
689 return s
690 matches = [x for x in self.series if s in x]
690 matches = [x for x in self.series if s in x]
691 if len(matches) > 1:
691 if len(matches) > 1:
692 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
692 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
693 for m in matches:
693 for m in matches:
694 self.ui.warn(' %s\n' % m)
694 self.ui.warn(' %s\n' % m)
695 return None
695 return None
696 if matches:
696 if matches:
697 return matches[0]
697 return matches[0]
698 if len(self.series) > 0 and len(self.applied) > 0:
698 if len(self.series) > 0 and len(self.applied) > 0:
699 if s == 'qtip':
699 if s == 'qtip':
700 return self.series[self.series_end(True)-1]
700 return self.series[self.series_end(True)-1]
701 if s == 'qbase':
701 if s == 'qbase':
702 return self.series[0]
702 return self.series[0]
703 return None
703 return None
704 if patch == None:
704 if patch == None:
705 return None
705 return None
706
706
707 # we don't want to return a partial match until we make
707 # we don't want to return a partial match until we make
708 # sure the file name passed in does not exist (checked below)
708 # sure the file name passed in does not exist (checked below)
709 res = partial_name(patch)
709 res = partial_name(patch)
710 if res and res == patch:
710 if res and res == patch:
711 return res
711 return res
712
712
713 if not os.path.isfile(self.join(patch)):
713 if not os.path.isfile(self.join(patch)):
714 try:
714 try:
715 sno = int(patch)
715 sno = int(patch)
716 except(ValueError, OverflowError):
716 except(ValueError, OverflowError):
717 pass
717 pass
718 else:
718 else:
719 if sno < len(self.series):
719 if sno < len(self.series):
720 return self.series[sno]
720 return self.series[sno]
721 if not strict:
721 if not strict:
722 # return any partial match made above
722 # return any partial match made above
723 if res:
723 if res:
724 return res
724 return res
725 minus = patch.rfind('-')
725 minus = patch.rfind('-')
726 if minus >= 0:
726 if minus >= 0:
727 res = partial_name(patch[:minus])
727 res = partial_name(patch[:minus])
728 if res:
728 if res:
729 i = self.series.index(res)
729 i = self.series.index(res)
730 try:
730 try:
731 off = int(patch[minus+1:] or 1)
731 off = int(patch[minus+1:] or 1)
732 except(ValueError, OverflowError):
732 except(ValueError, OverflowError):
733 pass
733 pass
734 else:
734 else:
735 if i - off >= 0:
735 if i - off >= 0:
736 return self.series[i - off]
736 return self.series[i - off]
737 plus = patch.rfind('+')
737 plus = patch.rfind('+')
738 if plus >= 0:
738 if plus >= 0:
739 res = partial_name(patch[:plus])
739 res = partial_name(patch[:plus])
740 if res:
740 if res:
741 i = self.series.index(res)
741 i = self.series.index(res)
742 try:
742 try:
743 off = int(patch[plus+1:] or 1)
743 off = int(patch[plus+1:] or 1)
744 except(ValueError, OverflowError):
744 except(ValueError, OverflowError):
745 pass
745 pass
746 else:
746 else:
747 if i + off < len(self.series):
747 if i + off < len(self.series):
748 return self.series[i + off]
748 return self.series[i + off]
749 raise util.Abort(_("patch %s not in series") % patch)
749 raise util.Abort(_("patch %s not in series") % patch)
750
750
751 def push(self, repo, patch=None, force=False, list=False,
751 def push(self, repo, patch=None, force=False, list=False,
752 mergeq=None):
752 mergeq=None):
753 wlock = repo.wlock()
753 wlock = repo.wlock()
754 try:
754 try:
755 patch = self.lookup(patch)
755 patch = self.lookup(patch)
756 # Suppose our series file is: A B C and the current 'top'
756 # Suppose our series file is: A B C and the current 'top'
757 # patch is B. qpush C should be performed (moving forward)
757 # patch is B. qpush C should be performed (moving forward)
758 # qpush B is a NOP (no change) qpush A is an error (can't
758 # qpush B is a NOP (no change) qpush A is an error (can't
759 # go backwards with qpush)
759 # go backwards with qpush)
760 if patch:
760 if patch:
761 info = self.isapplied(patch)
761 info = self.isapplied(patch)
762 if info:
762 if info:
763 if info[0] < len(self.applied) - 1:
763 if info[0] < len(self.applied) - 1:
764 raise util.Abort(
764 raise util.Abort(
765 _("cannot push to a previous patch: %s") % patch)
765 _("cannot push to a previous patch: %s") % patch)
766 if info[0] < len(self.series) - 1:
766 if info[0] < len(self.series) - 1:
767 self.ui.warn(
767 self.ui.warn(
768 _('qpush: %s is already at the top\n') % patch)
768 _('qpush: %s is already at the top\n') % patch)
769 else:
769 else:
770 self.ui.warn(_('all patches are currently applied\n'))
770 self.ui.warn(_('all patches are currently applied\n'))
771 return
771 return
772
772
773 # Following the above example, starting at 'top' of B:
773 # Following the above example, starting at 'top' of B:
774 # qpush should be performed (pushes C), but a subsequent
774 # qpush should be performed (pushes C), but a subsequent
775 # qpush without an argument is an error (nothing to
775 # qpush without an argument is an error (nothing to
776 # apply). This allows a loop of "...while hg qpush..." to
776 # apply). This allows a loop of "...while hg qpush..." to
777 # work as it detects an error when done
777 # work as it detects an error when done
778 if self.series_end() == len(self.series):
778 if self.series_end() == len(self.series):
779 self.ui.warn(_('patch series already fully applied\n'))
779 self.ui.warn(_('patch series already fully applied\n'))
780 return 1
780 return 1
781 if not force:
781 if not force:
782 self.check_localchanges(repo)
782 self.check_localchanges(repo)
783
783
784 self.applied_dirty = 1;
784 self.applied_dirty = 1;
785 start = self.series_end()
785 start = self.series_end()
786 if start > 0:
786 if start > 0:
787 self.check_toppatch(repo)
787 self.check_toppatch(repo)
788 if not patch:
788 if not patch:
789 patch = self.series[start]
789 patch = self.series[start]
790 end = start + 1
790 end = start + 1
791 else:
791 else:
792 end = self.series.index(patch, start) + 1
792 end = self.series.index(patch, start) + 1
793 s = self.series[start:end]
793 s = self.series[start:end]
794 all_files = {}
794 all_files = {}
795 try:
795 try:
796 if mergeq:
796 if mergeq:
797 ret = self.mergepatch(repo, mergeq, s)
797 ret = self.mergepatch(repo, mergeq, s)
798 else:
798 else:
799 ret = self.apply(repo, s, list, all_files=all_files)
799 ret = self.apply(repo, s, list, all_files=all_files)
800 except:
800 except:
801 self.ui.warn(_('cleaning up working directory...'))
801 self.ui.warn(_('cleaning up working directory...'))
802 node = repo.dirstate.parents()[0]
802 node = repo.dirstate.parents()[0]
803 hg.revert(repo, node, None)
803 hg.revert(repo, node, None)
804 unknown = repo.status()[4]
804 unknown = repo.status()[4]
805 # only remove unknown files that we know we touched or
805 # only remove unknown files that we know we touched or
806 # created while patching
806 # created while patching
807 for f in unknown:
807 for f in unknown:
808 if f in all_files:
808 if f in all_files:
809 util.unlink(repo.wjoin(f))
809 util.unlink(repo.wjoin(f))
810 self.ui.warn(_('done\n'))
810 self.ui.warn(_('done\n'))
811 raise
811 raise
812 top = self.applied[-1].name
812 top = self.applied[-1].name
813 if ret[0]:
813 if ret[0]:
814 self.ui.write(
814 self.ui.write(
815 "Errors during apply, please fix and refresh %s\n" % top)
815 "Errors during apply, please fix and refresh %s\n" % top)
816 else:
816 else:
817 self.ui.write("Now at: %s\n" % top)
817 self.ui.write("Now at: %s\n" % top)
818 return ret[0]
818 return ret[0]
819 finally:
819 finally:
820 del wlock
820 del wlock
821
821
822 def pop(self, repo, patch=None, force=False, update=True, all=False):
822 def pop(self, repo, patch=None, force=False, update=True, all=False):
823 def getfile(f, rev, flags):
823 def getfile(f, rev, flags):
824 t = repo.file(f).read(rev)
824 t = repo.file(f).read(rev)
825 repo.wwrite(f, t, flags)
825 repo.wwrite(f, t, flags)
826
826
827 wlock = repo.wlock()
827 wlock = repo.wlock()
828 try:
828 try:
829 if patch:
829 if patch:
830 # index, rev, patch
830 # index, rev, patch
831 info = self.isapplied(patch)
831 info = self.isapplied(patch)
832 if not info:
832 if not info:
833 patch = self.lookup(patch)
833 patch = self.lookup(patch)
834 info = self.isapplied(patch)
834 info = self.isapplied(patch)
835 if not info:
835 if not info:
836 raise util.Abort(_("patch %s is not applied") % patch)
836 raise util.Abort(_("patch %s is not applied") % patch)
837
837
838 if len(self.applied) == 0:
838 if len(self.applied) == 0:
839 # Allow qpop -a to work repeatedly,
839 # Allow qpop -a to work repeatedly,
840 # but not qpop without an argument
840 # but not qpop without an argument
841 self.ui.warn(_("no patches applied\n"))
841 self.ui.warn(_("no patches applied\n"))
842 return not all
842 return not all
843
843
844 if not update:
844 if not update:
845 parents = repo.dirstate.parents()
845 parents = repo.dirstate.parents()
846 rr = [ revlog.bin(x.rev) for x in self.applied ]
846 rr = [ revlog.bin(x.rev) for x in self.applied ]
847 for p in parents:
847 for p in parents:
848 if p in rr:
848 if p in rr:
849 self.ui.warn("qpop: forcing dirstate update\n")
849 self.ui.warn("qpop: forcing dirstate update\n")
850 update = True
850 update = True
851
851
852 if not force and update:
852 if not force and update:
853 self.check_localchanges(repo)
853 self.check_localchanges(repo)
854
854
855 self.applied_dirty = 1;
855 self.applied_dirty = 1;
856 end = len(self.applied)
856 end = len(self.applied)
857 if not patch:
857 if not patch:
858 if all:
858 if all:
859 popi = 0
859 popi = 0
860 else:
860 else:
861 popi = len(self.applied) - 1
861 popi = len(self.applied) - 1
862 else:
862 else:
863 popi = info[0] + 1
863 popi = info[0] + 1
864 if popi >= end:
864 if popi >= end:
865 self.ui.warn("qpop: %s is already at the top\n" % patch)
865 self.ui.warn("qpop: %s is already at the top\n" % patch)
866 return
866 return
867 info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name]
867 info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name]
868
868
869 start = info[0]
869 start = info[0]
870 rev = revlog.bin(info[1])
870 rev = revlog.bin(info[1])
871
871
872 if update:
872 if update:
873 top = self.check_toppatch(repo)
873 top = self.check_toppatch(repo)
874
874
875 if repo.changelog.heads(rev) != [revlog.bin(self.applied[-1].rev)]:
875 if repo.changelog.heads(rev) != [revlog.bin(self.applied[-1].rev)]:
876 raise util.Abort("popping would remove a revision not "
876 raise util.Abort("popping would remove a revision not "
877 "managed by this patch queue")
877 "managed by this patch queue")
878
878
879 # we know there are no local changes, so we can make a simplified
879 # we know there are no local changes, so we can make a simplified
880 # form of hg.update.
880 # form of hg.update.
881 if update:
881 if update:
882 qp = self.qparents(repo, rev)
882 qp = self.qparents(repo, rev)
883 changes = repo.changelog.read(qp)
883 changes = repo.changelog.read(qp)
884 mmap = repo.manifest.read(changes[0])
884 mmap = repo.manifest.read(changes[0])
885 m, a, r, d, u = repo.status(qp, top)[:5]
885 m, a, r, d, u = repo.status(qp, top)[:5]
886 if d:
886 if d:
887 raise util.Abort("deletions found between repo revs")
887 raise util.Abort("deletions found between repo revs")
888 for f in m:
888 for f in m:
889 getfile(f, mmap[f], mmap.flags(f))
889 getfile(f, mmap[f], mmap.flags(f))
890 for f in r:
890 for f in r:
891 getfile(f, mmap[f], mmap.flags(f))
891 getfile(f, mmap[f], mmap.flags(f))
892 for f in m + r:
892 for f in m + r:
893 repo.dirstate.normal(f)
893 repo.dirstate.normal(f)
894 for f in a:
894 for f in a:
895 try:
895 try:
896 os.unlink(repo.wjoin(f))
896 os.unlink(repo.wjoin(f))
897 except OSError, e:
897 except OSError, e:
898 if e.errno != errno.ENOENT:
898 if e.errno != errno.ENOENT:
899 raise
899 raise
900 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
900 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
901 except: pass
901 except: pass
902 repo.dirstate.forget(f)
902 repo.dirstate.forget(f)
903 repo.dirstate.setparents(qp, revlog.nullid)
903 repo.dirstate.setparents(qp, revlog.nullid)
904 del self.applied[start:end]
904 self.strip(repo, rev, update=False, backup='strip')
905 self.strip(repo, rev, update=False, backup='strip')
905 del self.applied[start:end]
906 if len(self.applied):
906 if len(self.applied):
907 self.ui.write("Now at: %s\n" % self.applied[-1].name)
907 self.ui.write("Now at: %s\n" % self.applied[-1].name)
908 else:
908 else:
909 self.ui.write("Patch queue now empty\n")
909 self.ui.write("Patch queue now empty\n")
910 finally:
910 finally:
911 del wlock
911 del wlock
912
912
913 def diff(self, repo, pats, opts):
913 def diff(self, repo, pats, opts):
914 top = self.check_toppatch(repo)
914 top = self.check_toppatch(repo)
915 if not top:
915 if not top:
916 self.ui.write("No patches applied\n")
916 self.ui.write("No patches applied\n")
917 return
917 return
918 qp = self.qparents(repo, top)
918 qp = self.qparents(repo, top)
919 if opts.get('git'):
919 if opts.get('git'):
920 self.diffopts().git = True
920 self.diffopts().git = True
921 self.printdiff(repo, qp, files=pats, opts=opts)
921 self.printdiff(repo, qp, files=pats, opts=opts)
922
922
923 def refresh(self, repo, pats=None, **opts):
923 def refresh(self, repo, pats=None, **opts):
924 if len(self.applied) == 0:
924 if len(self.applied) == 0:
925 self.ui.write("No patches applied\n")
925 self.ui.write("No patches applied\n")
926 return 1
926 return 1
927 wlock = repo.wlock()
927 wlock = repo.wlock()
928 try:
928 try:
929 self.check_toppatch(repo)
929 self.check_toppatch(repo)
930 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
930 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
931 top = revlog.bin(top)
931 top = revlog.bin(top)
932 if repo.changelog.heads(top) != [top]:
932 if repo.changelog.heads(top) != [top]:
933 raise util.Abort("cannot refresh a revision with children")
933 raise util.Abort("cannot refresh a revision with children")
934 cparents = repo.changelog.parents(top)
934 cparents = repo.changelog.parents(top)
935 patchparent = self.qparents(repo, top)
935 patchparent = self.qparents(repo, top)
936 message, comments, user, date, patchfound = self.readheaders(patchfn)
936 message, comments, user, date, patchfound = self.readheaders(patchfn)
937
937
938 patchf = self.opener(patchfn, 'r+')
938 patchf = self.opener(patchfn, 'r+')
939
939
940 # if the patch was a git patch, refresh it as a git patch
940 # if the patch was a git patch, refresh it as a git patch
941 for line in patchf:
941 for line in patchf:
942 if line.startswith('diff --git'):
942 if line.startswith('diff --git'):
943 self.diffopts().git = True
943 self.diffopts().git = True
944 break
944 break
945
945
946 msg = opts.get('msg', '').rstrip()
946 msg = opts.get('msg', '').rstrip()
947 if msg and comments:
947 if msg and comments:
948 # Remove existing message, keeping the rest of the comments
948 # Remove existing message, keeping the rest of the comments
949 # fields.
949 # fields.
950 # If comments contains 'subject: ', message will prepend
950 # If comments contains 'subject: ', message will prepend
951 # the field and a blank line.
951 # the field and a blank line.
952 if message:
952 if message:
953 subj = 'subject: ' + message[0].lower()
953 subj = 'subject: ' + message[0].lower()
954 for i in xrange(len(comments)):
954 for i in xrange(len(comments)):
955 if subj == comments[i].lower():
955 if subj == comments[i].lower():
956 del comments[i]
956 del comments[i]
957 message = message[2:]
957 message = message[2:]
958 break
958 break
959 ci = 0
959 ci = 0
960 for mi in xrange(len(message)):
960 for mi in xrange(len(message)):
961 while message[mi] != comments[ci]:
961 while message[mi] != comments[ci]:
962 ci += 1
962 ci += 1
963 del comments[ci]
963 del comments[ci]
964 if msg:
964 if msg:
965 comments.append(msg)
965 comments.append(msg)
966
966
967 patchf.seek(0)
967 patchf.seek(0)
968 patchf.truncate()
968 patchf.truncate()
969
969
970 if comments:
970 if comments:
971 comments = "\n".join(comments) + '\n\n'
971 comments = "\n".join(comments) + '\n\n'
972 patchf.write(comments)
972 patchf.write(comments)
973
973
974 if opts.get('git'):
974 if opts.get('git'):
975 self.diffopts().git = True
975 self.diffopts().git = True
976 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
976 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
977 tip = repo.changelog.tip()
977 tip = repo.changelog.tip()
978 if top == tip:
978 if top == tip:
979 # if the top of our patch queue is also the tip, there is an
979 # if the top of our patch queue is also the tip, there is an
980 # optimization here. We update the dirstate in place and strip
980 # optimization here. We update the dirstate in place and strip
981 # off the tip commit. Then just commit the current directory
981 # off the tip commit. Then just commit the current directory
982 # tree. We can also send repo.commit the list of files
982 # tree. We can also send repo.commit the list of files
983 # changed to speed up the diff
983 # changed to speed up the diff
984 #
984 #
985 # in short mode, we only diff the files included in the
985 # in short mode, we only diff the files included in the
986 # patch already
986 # patch already
987 #
987 #
988 # this should really read:
988 # this should really read:
989 # mm, dd, aa, aa2, uu = repo.status(tip, patchparent)[:5]
989 # mm, dd, aa, aa2, uu = repo.status(tip, patchparent)[:5]
990 # but we do it backwards to take advantage of manifest/chlog
990 # but we do it backwards to take advantage of manifest/chlog
991 # caching against the next repo.status call
991 # caching against the next repo.status call
992 #
992 #
993 mm, aa, dd, aa2, uu = repo.status(patchparent, tip)[:5]
993 mm, aa, dd, aa2, uu = repo.status(patchparent, tip)[:5]
994 changes = repo.changelog.read(tip)
994 changes = repo.changelog.read(tip)
995 man = repo.manifest.read(changes[0])
995 man = repo.manifest.read(changes[0])
996 aaa = aa[:]
996 aaa = aa[:]
997 if opts.get('short'):
997 if opts.get('short'):
998 filelist = mm + aa + dd
998 filelist = mm + aa + dd
999 match = dict.fromkeys(filelist).__contains__
999 match = dict.fromkeys(filelist).__contains__
1000 else:
1000 else:
1001 filelist = None
1001 filelist = None
1002 match = util.always
1002 match = util.always
1003 m, a, r, d, u = repo.status(files=filelist, match=match)[:5]
1003 m, a, r, d, u = repo.status(files=filelist, match=match)[:5]
1004
1004
1005 # we might end up with files that were added between
1005 # we might end up with files that were added between
1006 # tip and the dirstate parent, but then changed in the
1006 # tip and the dirstate parent, but then changed in the
1007 # local dirstate. in this case, we want them to only
1007 # local dirstate. in this case, we want them to only
1008 # show up in the added section
1008 # show up in the added section
1009 for x in m:
1009 for x in m:
1010 if x not in aa:
1010 if x not in aa:
1011 mm.append(x)
1011 mm.append(x)
1012 # we might end up with files added by the local dirstate that
1012 # we might end up with files added by the local dirstate that
1013 # were deleted by the patch. In this case, they should only
1013 # were deleted by the patch. In this case, they should only
1014 # show up in the changed section.
1014 # show up in the changed section.
1015 for x in a:
1015 for x in a:
1016 if x in dd:
1016 if x in dd:
1017 del dd[dd.index(x)]
1017 del dd[dd.index(x)]
1018 mm.append(x)
1018 mm.append(x)
1019 else:
1019 else:
1020 aa.append(x)
1020 aa.append(x)
1021 # make sure any files deleted in the local dirstate
1021 # make sure any files deleted in the local dirstate
1022 # are not in the add or change column of the patch
1022 # are not in the add or change column of the patch
1023 forget = []
1023 forget = []
1024 for x in d + r:
1024 for x in d + r:
1025 if x in aa:
1025 if x in aa:
1026 del aa[aa.index(x)]
1026 del aa[aa.index(x)]
1027 forget.append(x)
1027 forget.append(x)
1028 continue
1028 continue
1029 elif x in mm:
1029 elif x in mm:
1030 del mm[mm.index(x)]
1030 del mm[mm.index(x)]
1031 dd.append(x)
1031 dd.append(x)
1032
1032
1033 m = util.unique(mm)
1033 m = util.unique(mm)
1034 r = util.unique(dd)
1034 r = util.unique(dd)
1035 a = util.unique(aa)
1035 a = util.unique(aa)
1036 c = [filter(matchfn, l) for l in (m, a, r, [], u)]
1036 c = [filter(matchfn, l) for l in (m, a, r, [], u)]
1037 filelist = util.unique(c[0] + c[1] + c[2])
1037 filelist = util.unique(c[0] + c[1] + c[2])
1038 patch.diff(repo, patchparent, files=filelist, match=matchfn,
1038 patch.diff(repo, patchparent, files=filelist, match=matchfn,
1039 fp=patchf, changes=c, opts=self.diffopts())
1039 fp=patchf, changes=c, opts=self.diffopts())
1040 patchf.close()
1040 patchf.close()
1041
1041
1042 repo.dirstate.setparents(*cparents)
1042 repo.dirstate.setparents(*cparents)
1043 copies = {}
1043 copies = {}
1044 for dst in a:
1044 for dst in a:
1045 src = repo.dirstate.copied(dst)
1045 src = repo.dirstate.copied(dst)
1046 if src is not None:
1046 if src is not None:
1047 copies.setdefault(src, []).append(dst)
1047 copies.setdefault(src, []).append(dst)
1048 repo.dirstate.add(dst)
1048 repo.dirstate.add(dst)
1049 # remember the copies between patchparent and tip
1049 # remember the copies between patchparent and tip
1050 # this may be slow, so don't do it if we're not tracking copies
1050 # this may be slow, so don't do it if we're not tracking copies
1051 if self.diffopts().git:
1051 if self.diffopts().git:
1052 for dst in aaa:
1052 for dst in aaa:
1053 f = repo.file(dst)
1053 f = repo.file(dst)
1054 src = f.renamed(man[dst])
1054 src = f.renamed(man[dst])
1055 if src:
1055 if src:
1056 copies[src[0]] = copies.get(dst, [])
1056 copies[src[0]] = copies.get(dst, [])
1057 if dst in a:
1057 if dst in a:
1058 copies[src[0]].append(dst)
1058 copies[src[0]].append(dst)
1059 # we can't copy a file created by the patch itself
1059 # we can't copy a file created by the patch itself
1060 if dst in copies:
1060 if dst in copies:
1061 del copies[dst]
1061 del copies[dst]
1062 for src, dsts in copies.iteritems():
1062 for src, dsts in copies.iteritems():
1063 for dst in dsts:
1063 for dst in dsts:
1064 repo.dirstate.copy(src, dst)
1064 repo.dirstate.copy(src, dst)
1065 for f in r:
1065 for f in r:
1066 repo.dirstate.remove(f)
1066 repo.dirstate.remove(f)
1067 # if the patch excludes a modified file, mark that
1067 # if the patch excludes a modified file, mark that
1068 # file with mtime=0 so status can see it.
1068 # file with mtime=0 so status can see it.
1069 mm = []
1069 mm = []
1070 for i in xrange(len(m)-1, -1, -1):
1070 for i in xrange(len(m)-1, -1, -1):
1071 if not matchfn(m[i]):
1071 if not matchfn(m[i]):
1072 mm.append(m[i])
1072 mm.append(m[i])
1073 del m[i]
1073 del m[i]
1074 for f in m:
1074 for f in m:
1075 repo.dirstate.normal(f)
1075 repo.dirstate.normal(f)
1076 for f in mm:
1076 for f in mm:
1077 repo.dirstate.normallookup(f)
1077 repo.dirstate.normallookup(f)
1078 for f in forget:
1078 for f in forget:
1079 repo.dirstate.forget(f)
1079 repo.dirstate.forget(f)
1080
1080
1081 if not msg:
1081 if not msg:
1082 if not message:
1082 if not message:
1083 message = "[mq]: %s\n" % patchfn
1083 message = "[mq]: %s\n" % patchfn
1084 else:
1084 else:
1085 message = "\n".join(message)
1085 message = "\n".join(message)
1086 else:
1086 else:
1087 message = msg
1087 message = msg
1088
1088
1089 self.applied.pop()
1090 self.applied_dirty = 1
1089 self.strip(repo, top, update=False,
1091 self.strip(repo, top, update=False,
1090 backup='strip')
1092 backup='strip')
1091 n = repo.commit(filelist, message, changes[1], match=matchfn,
1093 n = repo.commit(filelist, message, changes[1], match=matchfn,
1092 force=1)
1094 force=1)
1093 self.applied[-1] = statusentry(revlog.hex(n), patchfn)
1095 self.applied.append(statusentry(revlog.hex(n), patchfn))
1094 self.applied_dirty = 1
1095 self.removeundo(repo)
1096 self.removeundo(repo)
1096 else:
1097 else:
1097 self.printdiff(repo, patchparent, fp=patchf)
1098 self.printdiff(repo, patchparent, fp=patchf)
1098 patchf.close()
1099 patchf.close()
1099 added = repo.status()[1]
1100 added = repo.status()[1]
1100 for a in added:
1101 for a in added:
1101 f = repo.wjoin(a)
1102 f = repo.wjoin(a)
1102 try:
1103 try:
1103 os.unlink(f)
1104 os.unlink(f)
1104 except OSError, e:
1105 except OSError, e:
1105 if e.errno != errno.ENOENT:
1106 if e.errno != errno.ENOENT:
1106 raise
1107 raise
1107 try: os.removedirs(os.path.dirname(f))
1108 try: os.removedirs(os.path.dirname(f))
1108 except: pass
1109 except: pass
1109 # forget the file copies in the dirstate
1110 # forget the file copies in the dirstate
1110 # push should readd the files later on
1111 # push should readd the files later on
1111 repo.dirstate.forget(a)
1112 repo.dirstate.forget(a)
1112 self.pop(repo, force=True)
1113 self.pop(repo, force=True)
1113 self.push(repo, force=True)
1114 self.push(repo, force=True)
1114 finally:
1115 finally:
1115 del wlock
1116 del wlock
1116
1117
1117 def init(self, repo, create=False):
1118 def init(self, repo, create=False):
1118 if not create and os.path.isdir(self.path):
1119 if not create and os.path.isdir(self.path):
1119 raise util.Abort(_("patch queue directory already exists"))
1120 raise util.Abort(_("patch queue directory already exists"))
1120 try:
1121 try:
1121 os.mkdir(self.path)
1122 os.mkdir(self.path)
1122 except OSError, inst:
1123 except OSError, inst:
1123 if inst.errno != errno.EEXIST or not create:
1124 if inst.errno != errno.EEXIST or not create:
1124 raise
1125 raise
1125 if create:
1126 if create:
1126 return self.qrepo(create=True)
1127 return self.qrepo(create=True)
1127
1128
1128 def unapplied(self, repo, patch=None):
1129 def unapplied(self, repo, patch=None):
1129 if patch and patch not in self.series:
1130 if patch and patch not in self.series:
1130 raise util.Abort(_("patch %s is not in series file") % patch)
1131 raise util.Abort(_("patch %s is not in series file") % patch)
1131 if not patch:
1132 if not patch:
1132 start = self.series_end()
1133 start = self.series_end()
1133 else:
1134 else:
1134 start = self.series.index(patch) + 1
1135 start = self.series.index(patch) + 1
1135 unapplied = []
1136 unapplied = []
1136 for i in xrange(start, len(self.series)):
1137 for i in xrange(start, len(self.series)):
1137 pushable, reason = self.pushable(i)
1138 pushable, reason = self.pushable(i)
1138 if pushable:
1139 if pushable:
1139 unapplied.append((i, self.series[i]))
1140 unapplied.append((i, self.series[i]))
1140 self.explain_pushable(i)
1141 self.explain_pushable(i)
1141 return unapplied
1142 return unapplied
1142
1143
1143 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1144 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1144 summary=False):
1145 summary=False):
1145 def displayname(patchname):
1146 def displayname(patchname):
1146 if summary:
1147 if summary:
1147 msg = self.readheaders(patchname)[0]
1148 msg = self.readheaders(patchname)[0]
1148 msg = msg and ': ' + msg[0] or ': '
1149 msg = msg and ': ' + msg[0] or ': '
1149 else:
1150 else:
1150 msg = ''
1151 msg = ''
1151 return '%s%s' % (patchname, msg)
1152 return '%s%s' % (patchname, msg)
1152
1153
1153 applied = dict.fromkeys([p.name for p in self.applied])
1154 applied = dict.fromkeys([p.name for p in self.applied])
1154 if length is None:
1155 if length is None:
1155 length = len(self.series) - start
1156 length = len(self.series) - start
1156 if not missing:
1157 if not missing:
1157 for i in xrange(start, start+length):
1158 for i in xrange(start, start+length):
1158 patch = self.series[i]
1159 patch = self.series[i]
1159 if patch in applied:
1160 if patch in applied:
1160 stat = 'A'
1161 stat = 'A'
1161 elif self.pushable(i)[0]:
1162 elif self.pushable(i)[0]:
1162 stat = 'U'
1163 stat = 'U'
1163 else:
1164 else:
1164 stat = 'G'
1165 stat = 'G'
1165 pfx = ''
1166 pfx = ''
1166 if self.ui.verbose:
1167 if self.ui.verbose:
1167 pfx = '%d %s ' % (i, stat)
1168 pfx = '%d %s ' % (i, stat)
1168 elif status and status != stat:
1169 elif status and status != stat:
1169 continue
1170 continue
1170 self.ui.write('%s%s\n' % (pfx, displayname(patch)))
1171 self.ui.write('%s%s\n' % (pfx, displayname(patch)))
1171 else:
1172 else:
1172 msng_list = []
1173 msng_list = []
1173 for root, dirs, files in os.walk(self.path):
1174 for root, dirs, files in os.walk(self.path):
1174 d = root[len(self.path) + 1:]
1175 d = root[len(self.path) + 1:]
1175 for f in files:
1176 for f in files:
1176 fl = os.path.join(d, f)
1177 fl = os.path.join(d, f)
1177 if (fl not in self.series and
1178 if (fl not in self.series and
1178 fl not in (self.status_path, self.series_path,
1179 fl not in (self.status_path, self.series_path,
1179 self.guards_path)
1180 self.guards_path)
1180 and not fl.startswith('.')):
1181 and not fl.startswith('.')):
1181 msng_list.append(fl)
1182 msng_list.append(fl)
1182 msng_list.sort()
1183 msng_list.sort()
1183 for x in msng_list:
1184 for x in msng_list:
1184 pfx = self.ui.verbose and ('D ') or ''
1185 pfx = self.ui.verbose and ('D ') or ''
1185 self.ui.write("%s%s\n" % (pfx, displayname(x)))
1186 self.ui.write("%s%s\n" % (pfx, displayname(x)))
1186
1187
1187 def issaveline(self, l):
1188 def issaveline(self, l):
1188 if l.name == '.hg.patches.save.line':
1189 if l.name == '.hg.patches.save.line':
1189 return True
1190 return True
1190
1191
1191 def qrepo(self, create=False):
1192 def qrepo(self, create=False):
1192 if create or os.path.isdir(self.join(".hg")):
1193 if create or os.path.isdir(self.join(".hg")):
1193 return hg.repository(self.ui, path=self.path, create=create)
1194 return hg.repository(self.ui, path=self.path, create=create)
1194
1195
1195 def restore(self, repo, rev, delete=None, qupdate=None):
1196 def restore(self, repo, rev, delete=None, qupdate=None):
1196 c = repo.changelog.read(rev)
1197 c = repo.changelog.read(rev)
1197 desc = c[4].strip()
1198 desc = c[4].strip()
1198 lines = desc.splitlines()
1199 lines = desc.splitlines()
1199 i = 0
1200 i = 0
1200 datastart = None
1201 datastart = None
1201 series = []
1202 series = []
1202 applied = []
1203 applied = []
1203 qpp = None
1204 qpp = None
1204 for i in xrange(0, len(lines)):
1205 for i in xrange(0, len(lines)):
1205 if lines[i] == 'Patch Data:':
1206 if lines[i] == 'Patch Data:':
1206 datastart = i + 1
1207 datastart = i + 1
1207 elif lines[i].startswith('Dirstate:'):
1208 elif lines[i].startswith('Dirstate:'):
1208 l = lines[i].rstrip()
1209 l = lines[i].rstrip()
1209 l = l[10:].split(' ')
1210 l = l[10:].split(' ')
1210 qpp = [ hg.bin(x) for x in l ]
1211 qpp = [ hg.bin(x) for x in l ]
1211 elif datastart != None:
1212 elif datastart != None:
1212 l = lines[i].rstrip()
1213 l = lines[i].rstrip()
1213 se = statusentry(l)
1214 se = statusentry(l)
1214 file_ = se.name
1215 file_ = se.name
1215 if se.rev:
1216 if se.rev:
1216 applied.append(se)
1217 applied.append(se)
1217 else:
1218 else:
1218 series.append(file_)
1219 series.append(file_)
1219 if datastart == None:
1220 if datastart == None:
1220 self.ui.warn("No saved patch data found\n")
1221 self.ui.warn("No saved patch data found\n")
1221 return 1
1222 return 1
1222 self.ui.warn("restoring status: %s\n" % lines[0])
1223 self.ui.warn("restoring status: %s\n" % lines[0])
1223 self.full_series = series
1224 self.full_series = series
1224 self.applied = applied
1225 self.applied = applied
1225 self.parse_series()
1226 self.parse_series()
1226 self.series_dirty = 1
1227 self.series_dirty = 1
1227 self.applied_dirty = 1
1228 self.applied_dirty = 1
1228 heads = repo.changelog.heads()
1229 heads = repo.changelog.heads()
1229 if delete:
1230 if delete:
1230 if rev not in heads:
1231 if rev not in heads:
1231 self.ui.warn("save entry has children, leaving it alone\n")
1232 self.ui.warn("save entry has children, leaving it alone\n")
1232 else:
1233 else:
1233 self.ui.warn("removing save entry %s\n" % hg.short(rev))
1234 self.ui.warn("removing save entry %s\n" % hg.short(rev))
1234 pp = repo.dirstate.parents()
1235 pp = repo.dirstate.parents()
1235 if rev in pp:
1236 if rev in pp:
1236 update = True
1237 update = True
1237 else:
1238 else:
1238 update = False
1239 update = False
1239 self.strip(repo, rev, update=update, backup='strip')
1240 self.strip(repo, rev, update=update, backup='strip')
1240 if qpp:
1241 if qpp:
1241 self.ui.warn("saved queue repository parents: %s %s\n" %
1242 self.ui.warn("saved queue repository parents: %s %s\n" %
1242 (hg.short(qpp[0]), hg.short(qpp[1])))
1243 (hg.short(qpp[0]), hg.short(qpp[1])))
1243 if qupdate:
1244 if qupdate:
1244 print "queue directory updating"
1245 print "queue directory updating"
1245 r = self.qrepo()
1246 r = self.qrepo()
1246 if not r:
1247 if not r:
1247 self.ui.warn("Unable to load queue repository\n")
1248 self.ui.warn("Unable to load queue repository\n")
1248 return 1
1249 return 1
1249 hg.clean(r, qpp[0])
1250 hg.clean(r, qpp[0])
1250
1251
1251 def save(self, repo, msg=None):
1252 def save(self, repo, msg=None):
1252 if len(self.applied) == 0:
1253 if len(self.applied) == 0:
1253 self.ui.warn("save: no patches applied, exiting\n")
1254 self.ui.warn("save: no patches applied, exiting\n")
1254 return 1
1255 return 1
1255 if self.issaveline(self.applied[-1]):
1256 if self.issaveline(self.applied[-1]):
1256 self.ui.warn("status is already saved\n")
1257 self.ui.warn("status is already saved\n")
1257 return 1
1258 return 1
1258
1259
1259 ar = [ ':' + x for x in self.full_series ]
1260 ar = [ ':' + x for x in self.full_series ]
1260 if not msg:
1261 if not msg:
1261 msg = "hg patches saved state"
1262 msg = "hg patches saved state"
1262 else:
1263 else:
1263 msg = "hg patches: " + msg.rstrip('\r\n')
1264 msg = "hg patches: " + msg.rstrip('\r\n')
1264 r = self.qrepo()
1265 r = self.qrepo()
1265 if r:
1266 if r:
1266 pp = r.dirstate.parents()
1267 pp = r.dirstate.parents()
1267 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
1268 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
1268 msg += "\n\nPatch Data:\n"
1269 msg += "\n\nPatch Data:\n"
1269 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1270 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1270 "\n".join(ar) + '\n' or "")
1271 "\n".join(ar) + '\n' or "")
1271 n = repo.commit(None, text, user=None, force=1)
1272 n = repo.commit(None, text, user=None, force=1)
1272 if not n:
1273 if not n:
1273 self.ui.warn("repo commit failed\n")
1274 self.ui.warn("repo commit failed\n")
1274 return 1
1275 return 1
1275 self.applied.append(statusentry(revlog.hex(n),'.hg.patches.save.line'))
1276 self.applied.append(statusentry(revlog.hex(n),'.hg.patches.save.line'))
1276 self.applied_dirty = 1
1277 self.applied_dirty = 1
1277 self.removeundo(repo)
1278 self.removeundo(repo)
1278
1279
1279 def full_series_end(self):
1280 def full_series_end(self):
1280 if len(self.applied) > 0:
1281 if len(self.applied) > 0:
1281 p = self.applied[-1].name
1282 p = self.applied[-1].name
1282 end = self.find_series(p)
1283 end = self.find_series(p)
1283 if end == None:
1284 if end == None:
1284 return len(self.full_series)
1285 return len(self.full_series)
1285 return end + 1
1286 return end + 1
1286 return 0
1287 return 0
1287
1288
1288 def series_end(self, all_patches=False):
1289 def series_end(self, all_patches=False):
1289 """If all_patches is False, return the index of the next pushable patch
1290 """If all_patches is False, return the index of the next pushable patch
1290 in the series, or the series length. If all_patches is True, return the
1291 in the series, or the series length. If all_patches is True, return the
1291 index of the first patch past the last applied one.
1292 index of the first patch past the last applied one.
1292 """
1293 """
1293 end = 0
1294 end = 0
1294 def next(start):
1295 def next(start):
1295 if all_patches:
1296 if all_patches:
1296 return start
1297 return start
1297 i = start
1298 i = start
1298 while i < len(self.series):
1299 while i < len(self.series):
1299 p, reason = self.pushable(i)
1300 p, reason = self.pushable(i)
1300 if p:
1301 if p:
1301 break
1302 break
1302 self.explain_pushable(i)
1303 self.explain_pushable(i)
1303 i += 1
1304 i += 1
1304 return i
1305 return i
1305 if len(self.applied) > 0:
1306 if len(self.applied) > 0:
1306 p = self.applied[-1].name
1307 p = self.applied[-1].name
1307 try:
1308 try:
1308 end = self.series.index(p)
1309 end = self.series.index(p)
1309 except ValueError:
1310 except ValueError:
1310 return 0
1311 return 0
1311 return next(end + 1)
1312 return next(end + 1)
1312 return next(end)
1313 return next(end)
1313
1314
1314 def appliedname(self, index):
1315 def appliedname(self, index):
1315 pname = self.applied[index].name
1316 pname = self.applied[index].name
1316 if not self.ui.verbose:
1317 if not self.ui.verbose:
1317 p = pname
1318 p = pname
1318 else:
1319 else:
1319 p = str(self.series.index(pname)) + " " + pname
1320 p = str(self.series.index(pname)) + " " + pname
1320 return p
1321 return p
1321
1322
1322 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1323 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1323 force=None, git=False):
1324 force=None, git=False):
1324 def checkseries(patchname):
1325 def checkseries(patchname):
1325 if patchname in self.series:
1326 if patchname in self.series:
1326 raise util.Abort(_('patch %s is already in the series file')
1327 raise util.Abort(_('patch %s is already in the series file')
1327 % patchname)
1328 % patchname)
1328 def checkfile(patchname):
1329 def checkfile(patchname):
1329 if not force and os.path.exists(self.join(patchname)):
1330 if not force and os.path.exists(self.join(patchname)):
1330 raise util.Abort(_('patch "%s" already exists')
1331 raise util.Abort(_('patch "%s" already exists')
1331 % patchname)
1332 % patchname)
1332
1333
1333 if rev:
1334 if rev:
1334 if files:
1335 if files:
1335 raise util.Abort(_('option "-r" not valid when importing '
1336 raise util.Abort(_('option "-r" not valid when importing '
1336 'files'))
1337 'files'))
1337 rev = cmdutil.revrange(repo, rev)
1338 rev = cmdutil.revrange(repo, rev)
1338 rev.sort(lambda x, y: cmp(y, x))
1339 rev.sort(lambda x, y: cmp(y, x))
1339 if (len(files) > 1 or len(rev) > 1) and patchname:
1340 if (len(files) > 1 or len(rev) > 1) and patchname:
1340 raise util.Abort(_('option "-n" not valid when importing multiple '
1341 raise util.Abort(_('option "-n" not valid when importing multiple '
1341 'patches'))
1342 'patches'))
1342 i = 0
1343 i = 0
1343 added = []
1344 added = []
1344 if rev:
1345 if rev:
1345 # If mq patches are applied, we can only import revisions
1346 # If mq patches are applied, we can only import revisions
1346 # that form a linear path to qbase.
1347 # that form a linear path to qbase.
1347 # Otherwise, they should form a linear path to a head.
1348 # Otherwise, they should form a linear path to a head.
1348 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1349 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1349 if len(heads) > 1:
1350 if len(heads) > 1:
1350 raise util.Abort(_('revision %d is the root of more than one '
1351 raise util.Abort(_('revision %d is the root of more than one '
1351 'branch') % rev[-1])
1352 'branch') % rev[-1])
1352 if self.applied:
1353 if self.applied:
1353 base = revlog.hex(repo.changelog.node(rev[0]))
1354 base = revlog.hex(repo.changelog.node(rev[0]))
1354 if base in [n.rev for n in self.applied]:
1355 if base in [n.rev for n in self.applied]:
1355 raise util.Abort(_('revision %d is already managed')
1356 raise util.Abort(_('revision %d is already managed')
1356 % rev[0])
1357 % rev[0])
1357 if heads != [revlog.bin(self.applied[-1].rev)]:
1358 if heads != [revlog.bin(self.applied[-1].rev)]:
1358 raise util.Abort(_('revision %d is not the parent of '
1359 raise util.Abort(_('revision %d is not the parent of '
1359 'the queue') % rev[0])
1360 'the queue') % rev[0])
1360 base = repo.changelog.rev(revlog.bin(self.applied[0].rev))
1361 base = repo.changelog.rev(revlog.bin(self.applied[0].rev))
1361 lastparent = repo.changelog.parentrevs(base)[0]
1362 lastparent = repo.changelog.parentrevs(base)[0]
1362 else:
1363 else:
1363 if heads != [repo.changelog.node(rev[0])]:
1364 if heads != [repo.changelog.node(rev[0])]:
1364 raise util.Abort(_('revision %d has unmanaged children')
1365 raise util.Abort(_('revision %d has unmanaged children')
1365 % rev[0])
1366 % rev[0])
1366 lastparent = None
1367 lastparent = None
1367
1368
1368 if git:
1369 if git:
1369 self.diffopts().git = True
1370 self.diffopts().git = True
1370
1371
1371 for r in rev:
1372 for r in rev:
1372 p1, p2 = repo.changelog.parentrevs(r)
1373 p1, p2 = repo.changelog.parentrevs(r)
1373 n = repo.changelog.node(r)
1374 n = repo.changelog.node(r)
1374 if p2 != revlog.nullrev:
1375 if p2 != revlog.nullrev:
1375 raise util.Abort(_('cannot import merge revision %d') % r)
1376 raise util.Abort(_('cannot import merge revision %d') % r)
1376 if lastparent and lastparent != r:
1377 if lastparent and lastparent != r:
1377 raise util.Abort(_('revision %d is not the parent of %d')
1378 raise util.Abort(_('revision %d is not the parent of %d')
1378 % (r, lastparent))
1379 % (r, lastparent))
1379 lastparent = p1
1380 lastparent = p1
1380
1381
1381 if not patchname:
1382 if not patchname:
1382 patchname = normname('%d.diff' % r)
1383 patchname = normname('%d.diff' % r)
1383 self.check_reserved_name(patchname)
1384 self.check_reserved_name(patchname)
1384 checkseries(patchname)
1385 checkseries(patchname)
1385 checkfile(patchname)
1386 checkfile(patchname)
1386 self.full_series.insert(0, patchname)
1387 self.full_series.insert(0, patchname)
1387
1388
1388 patchf = self.opener(patchname, "w")
1389 patchf = self.opener(patchname, "w")
1389 patch.export(repo, [n], fp=patchf, opts=self.diffopts())
1390 patch.export(repo, [n], fp=patchf, opts=self.diffopts())
1390 patchf.close()
1391 patchf.close()
1391
1392
1392 se = statusentry(revlog.hex(n), patchname)
1393 se = statusentry(revlog.hex(n), patchname)
1393 self.applied.insert(0, se)
1394 self.applied.insert(0, se)
1394
1395
1395 added.append(patchname)
1396 added.append(patchname)
1396 patchname = None
1397 patchname = None
1397 self.parse_series()
1398 self.parse_series()
1398 self.applied_dirty = 1
1399 self.applied_dirty = 1
1399
1400
1400 for filename in files:
1401 for filename in files:
1401 if existing:
1402 if existing:
1402 if filename == '-':
1403 if filename == '-':
1403 raise util.Abort(_('-e is incompatible with import from -'))
1404 raise util.Abort(_('-e is incompatible with import from -'))
1404 if not patchname:
1405 if not patchname:
1405 patchname = normname(filename)
1406 patchname = normname(filename)
1406 self.check_reserved_name(patchname)
1407 self.check_reserved_name(patchname)
1407 if not os.path.isfile(self.join(patchname)):
1408 if not os.path.isfile(self.join(patchname)):
1408 raise util.Abort(_("patch %s does not exist") % patchname)
1409 raise util.Abort(_("patch %s does not exist") % patchname)
1409 else:
1410 else:
1410 try:
1411 try:
1411 if filename == '-':
1412 if filename == '-':
1412 if not patchname:
1413 if not patchname:
1413 raise util.Abort(_('need --name to import a patch from -'))
1414 raise util.Abort(_('need --name to import a patch from -'))
1414 text = sys.stdin.read()
1415 text = sys.stdin.read()
1415 else:
1416 else:
1416 text = file(filename).read()
1417 text = file(filename).read()
1417 except IOError:
1418 except IOError:
1418 raise util.Abort(_("unable to read %s") % patchname)
1419 raise util.Abort(_("unable to read %s") % patchname)
1419 if not patchname:
1420 if not patchname:
1420 patchname = normname(os.path.basename(filename))
1421 patchname = normname(os.path.basename(filename))
1421 self.check_reserved_name(patchname)
1422 self.check_reserved_name(patchname)
1422 checkfile(patchname)
1423 checkfile(patchname)
1423 patchf = self.opener(patchname, "w")
1424 patchf = self.opener(patchname, "w")
1424 patchf.write(text)
1425 patchf.write(text)
1425 checkseries(patchname)
1426 checkseries(patchname)
1426 index = self.full_series_end() + i
1427 index = self.full_series_end() + i
1427 self.full_series[index:index] = [patchname]
1428 self.full_series[index:index] = [patchname]
1428 self.parse_series()
1429 self.parse_series()
1429 self.ui.warn("adding %s to series file\n" % patchname)
1430 self.ui.warn("adding %s to series file\n" % patchname)
1430 i += 1
1431 i += 1
1431 added.append(patchname)
1432 added.append(patchname)
1432 patchname = None
1433 patchname = None
1433 self.series_dirty = 1
1434 self.series_dirty = 1
1434 qrepo = self.qrepo()
1435 qrepo = self.qrepo()
1435 if qrepo:
1436 if qrepo:
1436 qrepo.add(added)
1437 qrepo.add(added)
1437
1438
1438 def delete(ui, repo, *patches, **opts):
1439 def delete(ui, repo, *patches, **opts):
1439 """remove patches from queue
1440 """remove patches from queue
1440
1441
1441 The patches must not be applied, unless they are arguments to
1442 The patches must not be applied, unless they are arguments to
1442 the --rev parameter. At least one patch or revision is required.
1443 the --rev parameter. At least one patch or revision is required.
1443
1444
1444 With --rev, mq will stop managing the named revisions (converting
1445 With --rev, mq will stop managing the named revisions (converting
1445 them to regular mercurial changesets). The patches must be applied
1446 them to regular mercurial changesets). The patches must be applied
1446 and at the base of the stack. This option is useful when the patches
1447 and at the base of the stack. This option is useful when the patches
1447 have been applied upstream.
1448 have been applied upstream.
1448
1449
1449 With --keep, the patch files are preserved in the patch directory."""
1450 With --keep, the patch files are preserved in the patch directory."""
1450 q = repo.mq
1451 q = repo.mq
1451 q.delete(repo, patches, opts)
1452 q.delete(repo, patches, opts)
1452 q.save_dirty()
1453 q.save_dirty()
1453 return 0
1454 return 0
1454
1455
1455 def applied(ui, repo, patch=None, **opts):
1456 def applied(ui, repo, patch=None, **opts):
1456 """print the patches already applied"""
1457 """print the patches already applied"""
1457 q = repo.mq
1458 q = repo.mq
1458 if patch:
1459 if patch:
1459 if patch not in q.series:
1460 if patch not in q.series:
1460 raise util.Abort(_("patch %s is not in series file") % patch)
1461 raise util.Abort(_("patch %s is not in series file") % patch)
1461 end = q.series.index(patch) + 1
1462 end = q.series.index(patch) + 1
1462 else:
1463 else:
1463 end = q.series_end(True)
1464 end = q.series_end(True)
1464 return q.qseries(repo, length=end, status='A', summary=opts.get('summary'))
1465 return q.qseries(repo, length=end, status='A', summary=opts.get('summary'))
1465
1466
1466 def unapplied(ui, repo, patch=None, **opts):
1467 def unapplied(ui, repo, patch=None, **opts):
1467 """print the patches not yet applied"""
1468 """print the patches not yet applied"""
1468 q = repo.mq
1469 q = repo.mq
1469 if patch:
1470 if patch:
1470 if patch not in q.series:
1471 if patch not in q.series:
1471 raise util.Abort(_("patch %s is not in series file") % patch)
1472 raise util.Abort(_("patch %s is not in series file") % patch)
1472 start = q.series.index(patch) + 1
1473 start = q.series.index(patch) + 1
1473 else:
1474 else:
1474 start = q.series_end(True)
1475 start = q.series_end(True)
1475 q.qseries(repo, start=start, status='U', summary=opts.get('summary'))
1476 q.qseries(repo, start=start, status='U', summary=opts.get('summary'))
1476
1477
1477 def qimport(ui, repo, *filename, **opts):
1478 def qimport(ui, repo, *filename, **opts):
1478 """import a patch
1479 """import a patch
1479
1480
1480 The patch will have the same name as its source file unless you
1481 The patch will have the same name as its source file unless you
1481 give it a new one with --name.
1482 give it a new one with --name.
1482
1483
1483 You can register an existing patch inside the patch directory
1484 You can register an existing patch inside the patch directory
1484 with the --existing flag.
1485 with the --existing flag.
1485
1486
1486 With --force, an existing patch of the same name will be overwritten.
1487 With --force, an existing patch of the same name will be overwritten.
1487
1488
1488 An existing changeset may be placed under mq control with --rev
1489 An existing changeset may be placed under mq control with --rev
1489 (e.g. qimport --rev tip -n patch will place tip under mq control).
1490 (e.g. qimport --rev tip -n patch will place tip under mq control).
1490 With --git, patches imported with --rev will use the git diff
1491 With --git, patches imported with --rev will use the git diff
1491 format.
1492 format.
1492 """
1493 """
1493 q = repo.mq
1494 q = repo.mq
1494 q.qimport(repo, filename, patchname=opts['name'],
1495 q.qimport(repo, filename, patchname=opts['name'],
1495 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1496 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1496 git=opts['git'])
1497 git=opts['git'])
1497 q.save_dirty()
1498 q.save_dirty()
1498 return 0
1499 return 0
1499
1500
1500 def init(ui, repo, **opts):
1501 def init(ui, repo, **opts):
1501 """init a new queue repository
1502 """init a new queue repository
1502
1503
1503 The queue repository is unversioned by default. If -c is
1504 The queue repository is unversioned by default. If -c is
1504 specified, qinit will create a separate nested repository
1505 specified, qinit will create a separate nested repository
1505 for patches (qinit -c may also be run later to convert
1506 for patches (qinit -c may also be run later to convert
1506 an unversioned patch repository into a versioned one).
1507 an unversioned patch repository into a versioned one).
1507 You can use qcommit to commit changes to this queue repository."""
1508 You can use qcommit to commit changes to this queue repository."""
1508 q = repo.mq
1509 q = repo.mq
1509 r = q.init(repo, create=opts['create_repo'])
1510 r = q.init(repo, create=opts['create_repo'])
1510 q.save_dirty()
1511 q.save_dirty()
1511 if r:
1512 if r:
1512 if not os.path.exists(r.wjoin('.hgignore')):
1513 if not os.path.exists(r.wjoin('.hgignore')):
1513 fp = r.wopener('.hgignore', 'w')
1514 fp = r.wopener('.hgignore', 'w')
1514 fp.write('syntax: glob\n')
1515 fp.write('syntax: glob\n')
1515 fp.write('status\n')
1516 fp.write('status\n')
1516 fp.write('guards\n')
1517 fp.write('guards\n')
1517 fp.close()
1518 fp.close()
1518 if not os.path.exists(r.wjoin('series')):
1519 if not os.path.exists(r.wjoin('series')):
1519 r.wopener('series', 'w').close()
1520 r.wopener('series', 'w').close()
1520 r.add(['.hgignore', 'series'])
1521 r.add(['.hgignore', 'series'])
1521 commands.add(ui, r)
1522 commands.add(ui, r)
1522 return 0
1523 return 0
1523
1524
1524 def clone(ui, source, dest=None, **opts):
1525 def clone(ui, source, dest=None, **opts):
1525 '''clone main and patch repository at same time
1526 '''clone main and patch repository at same time
1526
1527
1527 If source is local, destination will have no patches applied. If
1528 If source is local, destination will have no patches applied. If
1528 source is remote, this command can not check if patches are
1529 source is remote, this command can not check if patches are
1529 applied in source, so cannot guarantee that patches are not
1530 applied in source, so cannot guarantee that patches are not
1530 applied in destination. If you clone remote repository, be sure
1531 applied in destination. If you clone remote repository, be sure
1531 before that it has no patches applied.
1532 before that it has no patches applied.
1532
1533
1533 Source patch repository is looked for in <src>/.hg/patches by
1534 Source patch repository is looked for in <src>/.hg/patches by
1534 default. Use -p <url> to change.
1535 default. Use -p <url> to change.
1535
1536
1536 The patch directory must be a nested mercurial repository, as
1537 The patch directory must be a nested mercurial repository, as
1537 would be created by qinit -c.
1538 would be created by qinit -c.
1538 '''
1539 '''
1539 def patchdir(repo):
1540 def patchdir(repo):
1540 url = repo.url()
1541 url = repo.url()
1541 if url.endswith('/'):
1542 if url.endswith('/'):
1542 url = url[:-1]
1543 url = url[:-1]
1543 return url + '/.hg/patches'
1544 return url + '/.hg/patches'
1544 cmdutil.setremoteconfig(ui, opts)
1545 cmdutil.setremoteconfig(ui, opts)
1545 if dest is None:
1546 if dest is None:
1546 dest = hg.defaultdest(source)
1547 dest = hg.defaultdest(source)
1547 sr = hg.repository(ui, ui.expandpath(source))
1548 sr = hg.repository(ui, ui.expandpath(source))
1548 patchespath = opts['patches'] or patchdir(sr)
1549 patchespath = opts['patches'] or patchdir(sr)
1549 try:
1550 try:
1550 pr = hg.repository(ui, patchespath)
1551 pr = hg.repository(ui, patchespath)
1551 except hg.RepoError:
1552 except hg.RepoError:
1552 raise util.Abort(_('versioned patch repository not found'
1553 raise util.Abort(_('versioned patch repository not found'
1553 ' (see qinit -c)'))
1554 ' (see qinit -c)'))
1554 qbase, destrev = None, None
1555 qbase, destrev = None, None
1555 if sr.local():
1556 if sr.local():
1556 if sr.mq.applied:
1557 if sr.mq.applied:
1557 qbase = revlog.bin(sr.mq.applied[0].rev)
1558 qbase = revlog.bin(sr.mq.applied[0].rev)
1558 if not hg.islocal(dest):
1559 if not hg.islocal(dest):
1559 heads = dict.fromkeys(sr.heads())
1560 heads = dict.fromkeys(sr.heads())
1560 for h in sr.heads(qbase):
1561 for h in sr.heads(qbase):
1561 del heads[h]
1562 del heads[h]
1562 destrev = heads.keys()
1563 destrev = heads.keys()
1563 destrev.append(sr.changelog.parents(qbase)[0])
1564 destrev.append(sr.changelog.parents(qbase)[0])
1564 ui.note(_('cloning main repo\n'))
1565 ui.note(_('cloning main repo\n'))
1565 sr, dr = hg.clone(ui, sr.url(), dest,
1566 sr, dr = hg.clone(ui, sr.url(), dest,
1566 pull=opts['pull'],
1567 pull=opts['pull'],
1567 rev=destrev,
1568 rev=destrev,
1568 update=False,
1569 update=False,
1569 stream=opts['uncompressed'])
1570 stream=opts['uncompressed'])
1570 ui.note(_('cloning patch repo\n'))
1571 ui.note(_('cloning patch repo\n'))
1571 spr, dpr = hg.clone(ui, opts['patches'] or patchdir(sr), patchdir(dr),
1572 spr, dpr = hg.clone(ui, opts['patches'] or patchdir(sr), patchdir(dr),
1572 pull=opts['pull'], update=not opts['noupdate'],
1573 pull=opts['pull'], update=not opts['noupdate'],
1573 stream=opts['uncompressed'])
1574 stream=opts['uncompressed'])
1574 if dr.local():
1575 if dr.local():
1575 if qbase:
1576 if qbase:
1576 ui.note(_('stripping applied patches from destination repo\n'))
1577 ui.note(_('stripping applied patches from destination repo\n'))
1577 dr.mq.strip(dr, qbase, update=False, backup=None)
1578 dr.mq.strip(dr, qbase, update=False, backup=None)
1578 if not opts['noupdate']:
1579 if not opts['noupdate']:
1579 ui.note(_('updating destination repo\n'))
1580 ui.note(_('updating destination repo\n'))
1580 hg.update(dr, dr.changelog.tip())
1581 hg.update(dr, dr.changelog.tip())
1581
1582
1582 def commit(ui, repo, *pats, **opts):
1583 def commit(ui, repo, *pats, **opts):
1583 """commit changes in the queue repository"""
1584 """commit changes in the queue repository"""
1584 q = repo.mq
1585 q = repo.mq
1585 r = q.qrepo()
1586 r = q.qrepo()
1586 if not r: raise util.Abort('no queue repository')
1587 if not r: raise util.Abort('no queue repository')
1587 commands.commit(r.ui, r, *pats, **opts)
1588 commands.commit(r.ui, r, *pats, **opts)
1588
1589
1589 def series(ui, repo, **opts):
1590 def series(ui, repo, **opts):
1590 """print the entire series file"""
1591 """print the entire series file"""
1591 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1592 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1592 return 0
1593 return 0
1593
1594
1594 def top(ui, repo, **opts):
1595 def top(ui, repo, **opts):
1595 """print the name of the current patch"""
1596 """print the name of the current patch"""
1596 q = repo.mq
1597 q = repo.mq
1597 t = q.applied and q.series_end(True) or 0
1598 t = q.applied and q.series_end(True) or 0
1598 if t:
1599 if t:
1599 return q.qseries(repo, start=t-1, length=1, status='A',
1600 return q.qseries(repo, start=t-1, length=1, status='A',
1600 summary=opts.get('summary'))
1601 summary=opts.get('summary'))
1601 else:
1602 else:
1602 ui.write("No patches applied\n")
1603 ui.write("No patches applied\n")
1603 return 1
1604 return 1
1604
1605
1605 def next(ui, repo, **opts):
1606 def next(ui, repo, **opts):
1606 """print the name of the next patch"""
1607 """print the name of the next patch"""
1607 q = repo.mq
1608 q = repo.mq
1608 end = q.series_end()
1609 end = q.series_end()
1609 if end == len(q.series):
1610 if end == len(q.series):
1610 ui.write("All patches applied\n")
1611 ui.write("All patches applied\n")
1611 return 1
1612 return 1
1612 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1613 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1613
1614
1614 def prev(ui, repo, **opts):
1615 def prev(ui, repo, **opts):
1615 """print the name of the previous patch"""
1616 """print the name of the previous patch"""
1616 q = repo.mq
1617 q = repo.mq
1617 l = len(q.applied)
1618 l = len(q.applied)
1618 if l == 1:
1619 if l == 1:
1619 ui.write("Only one patch applied\n")
1620 ui.write("Only one patch applied\n")
1620 return 1
1621 return 1
1621 if not l:
1622 if not l:
1622 ui.write("No patches applied\n")
1623 ui.write("No patches applied\n")
1623 return 1
1624 return 1
1624 return q.qseries(repo, start=l-2, length=1, status='A',
1625 return q.qseries(repo, start=l-2, length=1, status='A',
1625 summary=opts.get('summary'))
1626 summary=opts.get('summary'))
1626
1627
1627 def new(ui, repo, patch, *args, **opts):
1628 def new(ui, repo, patch, *args, **opts):
1628 """create a new patch
1629 """create a new patch
1629
1630
1630 qnew creates a new patch on top of the currently-applied patch
1631 qnew creates a new patch on top of the currently-applied patch
1631 (if any). It will refuse to run if there are any outstanding
1632 (if any). It will refuse to run if there are any outstanding
1632 changes unless -f is specified, in which case the patch will
1633 changes unless -f is specified, in which case the patch will
1633 be initialised with them. You may also use -I, -X, and/or a list of
1634 be initialised with them. You may also use -I, -X, and/or a list of
1634 files after the patch name to add only changes to matching files
1635 files after the patch name to add only changes to matching files
1635 to the new patch, leaving the rest as uncommitted modifications.
1636 to the new patch, leaving the rest as uncommitted modifications.
1636
1637
1637 -e, -m or -l set the patch header as well as the commit message.
1638 -e, -m or -l set the patch header as well as the commit message.
1638 If none is specified, the patch header is empty and the
1639 If none is specified, the patch header is empty and the
1639 commit message is '[mq]: PATCH'"""
1640 commit message is '[mq]: PATCH'"""
1640 q = repo.mq
1641 q = repo.mq
1641 message = cmdutil.logmessage(opts)
1642 message = cmdutil.logmessage(opts)
1642 if opts['edit']:
1643 if opts['edit']:
1643 message = ui.edit(message, ui.username())
1644 message = ui.edit(message, ui.username())
1644 opts['msg'] = message
1645 opts['msg'] = message
1645 q.new(repo, patch, *args, **opts)
1646 q.new(repo, patch, *args, **opts)
1646 q.save_dirty()
1647 q.save_dirty()
1647 return 0
1648 return 0
1648
1649
1649 def refresh(ui, repo, *pats, **opts):
1650 def refresh(ui, repo, *pats, **opts):
1650 """update the current patch
1651 """update the current patch
1651
1652
1652 If any file patterns are provided, the refreshed patch will contain only
1653 If any file patterns are provided, the refreshed patch will contain only
1653 the modifications that match those patterns; the remaining modifications
1654 the modifications that match those patterns; the remaining modifications
1654 will remain in the working directory.
1655 will remain in the working directory.
1655
1656
1656 hg add/remove/copy/rename work as usual, though you might want to use
1657 hg add/remove/copy/rename work as usual, though you might want to use
1657 git-style patches (--git or [diff] git=1) to track copies and renames.
1658 git-style patches (--git or [diff] git=1) to track copies and renames.
1658 """
1659 """
1659 q = repo.mq
1660 q = repo.mq
1660 message = cmdutil.logmessage(opts)
1661 message = cmdutil.logmessage(opts)
1661 if opts['edit']:
1662 if opts['edit']:
1662 if not q.applied:
1663 if not q.applied:
1663 ui.write(_("No patches applied\n"))
1664 ui.write(_("No patches applied\n"))
1664 return 1
1665 return 1
1665 if message:
1666 if message:
1666 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1667 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1667 patch = q.applied[-1].name
1668 patch = q.applied[-1].name
1668 (message, comment, user, date, hasdiff) = q.readheaders(patch)
1669 (message, comment, user, date, hasdiff) = q.readheaders(patch)
1669 message = ui.edit('\n'.join(message), user or ui.username())
1670 message = ui.edit('\n'.join(message), user or ui.username())
1670 ret = q.refresh(repo, pats, msg=message, **opts)
1671 ret = q.refresh(repo, pats, msg=message, **opts)
1671 q.save_dirty()
1672 q.save_dirty()
1672 return ret
1673 return ret
1673
1674
1674 def diff(ui, repo, *pats, **opts):
1675 def diff(ui, repo, *pats, **opts):
1675 """diff of the current patch"""
1676 """diff of the current patch"""
1676 repo.mq.diff(repo, pats, opts)
1677 repo.mq.diff(repo, pats, opts)
1677 return 0
1678 return 0
1678
1679
1679 def fold(ui, repo, *files, **opts):
1680 def fold(ui, repo, *files, **opts):
1680 """fold the named patches into the current patch
1681 """fold the named patches into the current patch
1681
1682
1682 Patches must not yet be applied. Each patch will be successively
1683 Patches must not yet be applied. Each patch will be successively
1683 applied to the current patch in the order given. If all the
1684 applied to the current patch in the order given. If all the
1684 patches apply successfully, the current patch will be refreshed
1685 patches apply successfully, the current patch will be refreshed
1685 with the new cumulative patch, and the folded patches will
1686 with the new cumulative patch, and the folded patches will
1686 be deleted. With -k/--keep, the folded patch files will not
1687 be deleted. With -k/--keep, the folded patch files will not
1687 be removed afterwards.
1688 be removed afterwards.
1688
1689
1689 The header for each folded patch will be concatenated with
1690 The header for each folded patch will be concatenated with
1690 the current patch header, separated by a line of '* * *'."""
1691 the current patch header, separated by a line of '* * *'."""
1691
1692
1692 q = repo.mq
1693 q = repo.mq
1693
1694
1694 if not files:
1695 if not files:
1695 raise util.Abort(_('qfold requires at least one patch name'))
1696 raise util.Abort(_('qfold requires at least one patch name'))
1696 if not q.check_toppatch(repo):
1697 if not q.check_toppatch(repo):
1697 raise util.Abort(_('No patches applied'))
1698 raise util.Abort(_('No patches applied'))
1698
1699
1699 message = cmdutil.logmessage(opts)
1700 message = cmdutil.logmessage(opts)
1700 if opts['edit']:
1701 if opts['edit']:
1701 if message:
1702 if message:
1702 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1703 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1703
1704
1704 parent = q.lookup('qtip')
1705 parent = q.lookup('qtip')
1705 patches = []
1706 patches = []
1706 messages = []
1707 messages = []
1707 for f in files:
1708 for f in files:
1708 p = q.lookup(f)
1709 p = q.lookup(f)
1709 if p in patches or p == parent:
1710 if p in patches or p == parent:
1710 ui.warn(_('Skipping already folded patch %s') % p)
1711 ui.warn(_('Skipping already folded patch %s') % p)
1711 if q.isapplied(p):
1712 if q.isapplied(p):
1712 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
1713 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
1713 patches.append(p)
1714 patches.append(p)
1714
1715
1715 for p in patches:
1716 for p in patches:
1716 if not message:
1717 if not message:
1717 messages.append(q.readheaders(p)[0])
1718 messages.append(q.readheaders(p)[0])
1718 pf = q.join(p)
1719 pf = q.join(p)
1719 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1720 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1720 if not patchsuccess:
1721 if not patchsuccess:
1721 raise util.Abort(_('Error folding patch %s') % p)
1722 raise util.Abort(_('Error folding patch %s') % p)
1722 patch.updatedir(ui, repo, files)
1723 patch.updatedir(ui, repo, files)
1723
1724
1724 if not message:
1725 if not message:
1725 message, comments, user = q.readheaders(parent)[0:3]
1726 message, comments, user = q.readheaders(parent)[0:3]
1726 for msg in messages:
1727 for msg in messages:
1727 message.append('* * *')
1728 message.append('* * *')
1728 message.extend(msg)
1729 message.extend(msg)
1729 message = '\n'.join(message)
1730 message = '\n'.join(message)
1730
1731
1731 if opts['edit']:
1732 if opts['edit']:
1732 message = ui.edit(message, user or ui.username())
1733 message = ui.edit(message, user or ui.username())
1733
1734
1734 q.refresh(repo, msg=message)
1735 q.refresh(repo, msg=message)
1735 q.delete(repo, patches, opts)
1736 q.delete(repo, patches, opts)
1736 q.save_dirty()
1737 q.save_dirty()
1737
1738
1738 def goto(ui, repo, patch, **opts):
1739 def goto(ui, repo, patch, **opts):
1739 '''push or pop patches until named patch is at top of stack'''
1740 '''push or pop patches until named patch is at top of stack'''
1740 q = repo.mq
1741 q = repo.mq
1741 patch = q.lookup(patch)
1742 patch = q.lookup(patch)
1742 if q.isapplied(patch):
1743 if q.isapplied(patch):
1743 ret = q.pop(repo, patch, force=opts['force'])
1744 ret = q.pop(repo, patch, force=opts['force'])
1744 else:
1745 else:
1745 ret = q.push(repo, patch, force=opts['force'])
1746 ret = q.push(repo, patch, force=opts['force'])
1746 q.save_dirty()
1747 q.save_dirty()
1747 return ret
1748 return ret
1748
1749
1749 def guard(ui, repo, *args, **opts):
1750 def guard(ui, repo, *args, **opts):
1750 '''set or print guards for a patch
1751 '''set or print guards for a patch
1751
1752
1752 Guards control whether a patch can be pushed. A patch with no
1753 Guards control whether a patch can be pushed. A patch with no
1753 guards is always pushed. A patch with a positive guard ("+foo") is
1754 guards is always pushed. A patch with a positive guard ("+foo") is
1754 pushed only if the qselect command has activated it. A patch with
1755 pushed only if the qselect command has activated it. A patch with
1755 a negative guard ("-foo") is never pushed if the qselect command
1756 a negative guard ("-foo") is never pushed if the qselect command
1756 has activated it.
1757 has activated it.
1757
1758
1758 With no arguments, print the currently active guards.
1759 With no arguments, print the currently active guards.
1759 With arguments, set guards for the named patch.
1760 With arguments, set guards for the named patch.
1760
1761
1761 To set a negative guard "-foo" on topmost patch ("--" is needed so
1762 To set a negative guard "-foo" on topmost patch ("--" is needed so
1762 hg will not interpret "-foo" as an option):
1763 hg will not interpret "-foo" as an option):
1763 hg qguard -- -foo
1764 hg qguard -- -foo
1764
1765
1765 To set guards on another patch:
1766 To set guards on another patch:
1766 hg qguard other.patch +2.6.17 -stable
1767 hg qguard other.patch +2.6.17 -stable
1767 '''
1768 '''
1768 def status(idx):
1769 def status(idx):
1769 guards = q.series_guards[idx] or ['unguarded']
1770 guards = q.series_guards[idx] or ['unguarded']
1770 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
1771 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
1771 q = repo.mq
1772 q = repo.mq
1772 patch = None
1773 patch = None
1773 args = list(args)
1774 args = list(args)
1774 if opts['list']:
1775 if opts['list']:
1775 if args or opts['none']:
1776 if args or opts['none']:
1776 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
1777 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
1777 for i in xrange(len(q.series)):
1778 for i in xrange(len(q.series)):
1778 status(i)
1779 status(i)
1779 return
1780 return
1780 if not args or args[0][0:1] in '-+':
1781 if not args or args[0][0:1] in '-+':
1781 if not q.applied:
1782 if not q.applied:
1782 raise util.Abort(_('no patches applied'))
1783 raise util.Abort(_('no patches applied'))
1783 patch = q.applied[-1].name
1784 patch = q.applied[-1].name
1784 if patch is None and args[0][0:1] not in '-+':
1785 if patch is None and args[0][0:1] not in '-+':
1785 patch = args.pop(0)
1786 patch = args.pop(0)
1786 if patch is None:
1787 if patch is None:
1787 raise util.Abort(_('no patch to work with'))
1788 raise util.Abort(_('no patch to work with'))
1788 if args or opts['none']:
1789 if args or opts['none']:
1789 idx = q.find_series(patch)
1790 idx = q.find_series(patch)
1790 if idx is None:
1791 if idx is None:
1791 raise util.Abort(_('no patch named %s') % patch)
1792 raise util.Abort(_('no patch named %s') % patch)
1792 q.set_guards(idx, args)
1793 q.set_guards(idx, args)
1793 q.save_dirty()
1794 q.save_dirty()
1794 else:
1795 else:
1795 status(q.series.index(q.lookup(patch)))
1796 status(q.series.index(q.lookup(patch)))
1796
1797
1797 def header(ui, repo, patch=None):
1798 def header(ui, repo, patch=None):
1798 """Print the header of the topmost or specified patch"""
1799 """Print the header of the topmost or specified patch"""
1799 q = repo.mq
1800 q = repo.mq
1800
1801
1801 if patch:
1802 if patch:
1802 patch = q.lookup(patch)
1803 patch = q.lookup(patch)
1803 else:
1804 else:
1804 if not q.applied:
1805 if not q.applied:
1805 ui.write('No patches applied\n')
1806 ui.write('No patches applied\n')
1806 return 1
1807 return 1
1807 patch = q.lookup('qtip')
1808 patch = q.lookup('qtip')
1808 message = repo.mq.readheaders(patch)[0]
1809 message = repo.mq.readheaders(patch)[0]
1809
1810
1810 ui.write('\n'.join(message) + '\n')
1811 ui.write('\n'.join(message) + '\n')
1811
1812
1812 def lastsavename(path):
1813 def lastsavename(path):
1813 (directory, base) = os.path.split(path)
1814 (directory, base) = os.path.split(path)
1814 names = os.listdir(directory)
1815 names = os.listdir(directory)
1815 namere = re.compile("%s.([0-9]+)" % base)
1816 namere = re.compile("%s.([0-9]+)" % base)
1816 maxindex = None
1817 maxindex = None
1817 maxname = None
1818 maxname = None
1818 for f in names:
1819 for f in names:
1819 m = namere.match(f)
1820 m = namere.match(f)
1820 if m:
1821 if m:
1821 index = int(m.group(1))
1822 index = int(m.group(1))
1822 if maxindex == None or index > maxindex:
1823 if maxindex == None or index > maxindex:
1823 maxindex = index
1824 maxindex = index
1824 maxname = f
1825 maxname = f
1825 if maxname:
1826 if maxname:
1826 return (os.path.join(directory, maxname), maxindex)
1827 return (os.path.join(directory, maxname), maxindex)
1827 return (None, None)
1828 return (None, None)
1828
1829
1829 def savename(path):
1830 def savename(path):
1830 (last, index) = lastsavename(path)
1831 (last, index) = lastsavename(path)
1831 if last is None:
1832 if last is None:
1832 index = 0
1833 index = 0
1833 newpath = path + ".%d" % (index + 1)
1834 newpath = path + ".%d" % (index + 1)
1834 return newpath
1835 return newpath
1835
1836
1836 def push(ui, repo, patch=None, **opts):
1837 def push(ui, repo, patch=None, **opts):
1837 """push the next patch onto the stack"""
1838 """push the next patch onto the stack"""
1838 q = repo.mq
1839 q = repo.mq
1839 mergeq = None
1840 mergeq = None
1840
1841
1841 if opts['all']:
1842 if opts['all']:
1842 if not q.series:
1843 if not q.series:
1843 ui.warn(_('no patches in series\n'))
1844 ui.warn(_('no patches in series\n'))
1844 return 0
1845 return 0
1845 patch = q.series[-1]
1846 patch = q.series[-1]
1846 if opts['merge']:
1847 if opts['merge']:
1847 if opts['name']:
1848 if opts['name']:
1848 newpath = opts['name']
1849 newpath = opts['name']
1849 else:
1850 else:
1850 newpath, i = lastsavename(q.path)
1851 newpath, i = lastsavename(q.path)
1851 if not newpath:
1852 if not newpath:
1852 ui.warn("no saved queues found, please use -n\n")
1853 ui.warn("no saved queues found, please use -n\n")
1853 return 1
1854 return 1
1854 mergeq = queue(ui, repo.join(""), newpath)
1855 mergeq = queue(ui, repo.join(""), newpath)
1855 ui.warn("merging with queue at: %s\n" % mergeq.path)
1856 ui.warn("merging with queue at: %s\n" % mergeq.path)
1856 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1857 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1857 mergeq=mergeq)
1858 mergeq=mergeq)
1858 return ret
1859 return ret
1859
1860
1860 def pop(ui, repo, patch=None, **opts):
1861 def pop(ui, repo, patch=None, **opts):
1861 """pop the current patch off the stack"""
1862 """pop the current patch off the stack"""
1862 localupdate = True
1863 localupdate = True
1863 if opts['name']:
1864 if opts['name']:
1864 q = queue(ui, repo.join(""), repo.join(opts['name']))
1865 q = queue(ui, repo.join(""), repo.join(opts['name']))
1865 ui.warn('using patch queue: %s\n' % q.path)
1866 ui.warn('using patch queue: %s\n' % q.path)
1866 localupdate = False
1867 localupdate = False
1867 else:
1868 else:
1868 q = repo.mq
1869 q = repo.mq
1869 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
1870 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
1870 all=opts['all'])
1871 all=opts['all'])
1871 q.save_dirty()
1872 q.save_dirty()
1872 return ret
1873 return ret
1873
1874
1874 def rename(ui, repo, patch, name=None, **opts):
1875 def rename(ui, repo, patch, name=None, **opts):
1875 """rename a patch
1876 """rename a patch
1876
1877
1877 With one argument, renames the current patch to PATCH1.
1878 With one argument, renames the current patch to PATCH1.
1878 With two arguments, renames PATCH1 to PATCH2."""
1879 With two arguments, renames PATCH1 to PATCH2."""
1879
1880
1880 q = repo.mq
1881 q = repo.mq
1881
1882
1882 if not name:
1883 if not name:
1883 name = patch
1884 name = patch
1884 patch = None
1885 patch = None
1885
1886
1886 if patch:
1887 if patch:
1887 patch = q.lookup(patch)
1888 patch = q.lookup(patch)
1888 else:
1889 else:
1889 if not q.applied:
1890 if not q.applied:
1890 ui.write(_('No patches applied\n'))
1891 ui.write(_('No patches applied\n'))
1891 return
1892 return
1892 patch = q.lookup('qtip')
1893 patch = q.lookup('qtip')
1893 absdest = q.join(name)
1894 absdest = q.join(name)
1894 if os.path.isdir(absdest):
1895 if os.path.isdir(absdest):
1895 name = normname(os.path.join(name, os.path.basename(patch)))
1896 name = normname(os.path.join(name, os.path.basename(patch)))
1896 absdest = q.join(name)
1897 absdest = q.join(name)
1897 if os.path.exists(absdest):
1898 if os.path.exists(absdest):
1898 raise util.Abort(_('%s already exists') % absdest)
1899 raise util.Abort(_('%s already exists') % absdest)
1899
1900
1900 if name in q.series:
1901 if name in q.series:
1901 raise util.Abort(_('A patch named %s already exists in the series file') % name)
1902 raise util.Abort(_('A patch named %s already exists in the series file') % name)
1902
1903
1903 if ui.verbose:
1904 if ui.verbose:
1904 ui.write('Renaming %s to %s\n' % (patch, name))
1905 ui.write('Renaming %s to %s\n' % (patch, name))
1905 i = q.find_series(patch)
1906 i = q.find_series(patch)
1906 guards = q.guard_re.findall(q.full_series[i])
1907 guards = q.guard_re.findall(q.full_series[i])
1907 q.full_series[i] = name + ''.join([' #' + g for g in guards])
1908 q.full_series[i] = name + ''.join([' #' + g for g in guards])
1908 q.parse_series()
1909 q.parse_series()
1909 q.series_dirty = 1
1910 q.series_dirty = 1
1910
1911
1911 info = q.isapplied(patch)
1912 info = q.isapplied(patch)
1912 if info:
1913 if info:
1913 q.applied[info[0]] = statusentry(info[1], name)
1914 q.applied[info[0]] = statusentry(info[1], name)
1914 q.applied_dirty = 1
1915 q.applied_dirty = 1
1915
1916
1916 util.rename(q.join(patch), absdest)
1917 util.rename(q.join(patch), absdest)
1917 r = q.qrepo()
1918 r = q.qrepo()
1918 if r:
1919 if r:
1919 wlock = r.wlock()
1920 wlock = r.wlock()
1920 try:
1921 try:
1921 if r.dirstate[name] == 'r':
1922 if r.dirstate[name] == 'r':
1922 r.undelete([name])
1923 r.undelete([name])
1923 r.copy(patch, name)
1924 r.copy(patch, name)
1924 r.remove([patch], False)
1925 r.remove([patch], False)
1925 finally:
1926 finally:
1926 del wlock
1927 del wlock
1927
1928
1928 q.save_dirty()
1929 q.save_dirty()
1929
1930
1930 def restore(ui, repo, rev, **opts):
1931 def restore(ui, repo, rev, **opts):
1931 """restore the queue state saved by a rev"""
1932 """restore the queue state saved by a rev"""
1932 rev = repo.lookup(rev)
1933 rev = repo.lookup(rev)
1933 q = repo.mq
1934 q = repo.mq
1934 q.restore(repo, rev, delete=opts['delete'],
1935 q.restore(repo, rev, delete=opts['delete'],
1935 qupdate=opts['update'])
1936 qupdate=opts['update'])
1936 q.save_dirty()
1937 q.save_dirty()
1937 return 0
1938 return 0
1938
1939
1939 def save(ui, repo, **opts):
1940 def save(ui, repo, **opts):
1940 """save current queue state"""
1941 """save current queue state"""
1941 q = repo.mq
1942 q = repo.mq
1942 message = cmdutil.logmessage(opts)
1943 message = cmdutil.logmessage(opts)
1943 ret = q.save(repo, msg=message)
1944 ret = q.save(repo, msg=message)
1944 if ret:
1945 if ret:
1945 return ret
1946 return ret
1946 q.save_dirty()
1947 q.save_dirty()
1947 if opts['copy']:
1948 if opts['copy']:
1948 path = q.path
1949 path = q.path
1949 if opts['name']:
1950 if opts['name']:
1950 newpath = os.path.join(q.basepath, opts['name'])
1951 newpath = os.path.join(q.basepath, opts['name'])
1951 if os.path.exists(newpath):
1952 if os.path.exists(newpath):
1952 if not os.path.isdir(newpath):
1953 if not os.path.isdir(newpath):
1953 raise util.Abort(_('destination %s exists and is not '
1954 raise util.Abort(_('destination %s exists and is not '
1954 'a directory') % newpath)
1955 'a directory') % newpath)
1955 if not opts['force']:
1956 if not opts['force']:
1956 raise util.Abort(_('destination %s exists, '
1957 raise util.Abort(_('destination %s exists, '
1957 'use -f to force') % newpath)
1958 'use -f to force') % newpath)
1958 else:
1959 else:
1959 newpath = savename(path)
1960 newpath = savename(path)
1960 ui.warn("copy %s to %s\n" % (path, newpath))
1961 ui.warn("copy %s to %s\n" % (path, newpath))
1961 util.copyfiles(path, newpath)
1962 util.copyfiles(path, newpath)
1962 if opts['empty']:
1963 if opts['empty']:
1963 try:
1964 try:
1964 os.unlink(q.join(q.status_path))
1965 os.unlink(q.join(q.status_path))
1965 except:
1966 except:
1966 pass
1967 pass
1967 return 0
1968 return 0
1968
1969
1969 def strip(ui, repo, rev, **opts):
1970 def strip(ui, repo, rev, **opts):
1970 """strip a revision and all later revs on the same branch"""
1971 """strip a revision and all later revs on the same branch"""
1971 rev = repo.lookup(rev)
1972 rev = repo.lookup(rev)
1972 backup = 'all'
1973 backup = 'all'
1973 if opts['backup']:
1974 if opts['backup']:
1974 backup = 'strip'
1975 backup = 'strip'
1975 elif opts['nobackup']:
1976 elif opts['nobackup']:
1976 backup = 'none'
1977 backup = 'none'
1977 update = repo.dirstate.parents()[0] != revlog.nullid
1978 update = repo.dirstate.parents()[0] != revlog.nullid
1978 repo.mq.strip(repo, rev, backup=backup, update=update)
1979 repo.mq.strip(repo, rev, backup=backup, update=update)
1979 return 0
1980 return 0
1980
1981
1981 def select(ui, repo, *args, **opts):
1982 def select(ui, repo, *args, **opts):
1982 '''set or print guarded patches to push
1983 '''set or print guarded patches to push
1983
1984
1984 Use the qguard command to set or print guards on patch, then use
1985 Use the qguard command to set or print guards on patch, then use
1985 qselect to tell mq which guards to use. A patch will be pushed if it
1986 qselect to tell mq which guards to use. A patch will be pushed if it
1986 has no guards or any positive guards match the currently selected guard,
1987 has no guards or any positive guards match the currently selected guard,
1987 but will not be pushed if any negative guards match the current guard.
1988 but will not be pushed if any negative guards match the current guard.
1988 For example:
1989 For example:
1989
1990
1990 qguard foo.patch -stable (negative guard)
1991 qguard foo.patch -stable (negative guard)
1991 qguard bar.patch +stable (positive guard)
1992 qguard bar.patch +stable (positive guard)
1992 qselect stable
1993 qselect stable
1993
1994
1994 This activates the "stable" guard. mq will skip foo.patch (because
1995 This activates the "stable" guard. mq will skip foo.patch (because
1995 it has a negative match) but push bar.patch (because it
1996 it has a negative match) but push bar.patch (because it
1996 has a positive match).
1997 has a positive match).
1997
1998
1998 With no arguments, prints the currently active guards.
1999 With no arguments, prints the currently active guards.
1999 With one argument, sets the active guard.
2000 With one argument, sets the active guard.
2000
2001
2001 Use -n/--none to deactivate guards (no other arguments needed).
2002 Use -n/--none to deactivate guards (no other arguments needed).
2002 When no guards are active, patches with positive guards are skipped
2003 When no guards are active, patches with positive guards are skipped
2003 and patches with negative guards are pushed.
2004 and patches with negative guards are pushed.
2004
2005
2005 qselect can change the guards on applied patches. It does not pop
2006 qselect can change the guards on applied patches. It does not pop
2006 guarded patches by default. Use --pop to pop back to the last applied
2007 guarded patches by default. Use --pop to pop back to the last applied
2007 patch that is not guarded. Use --reapply (which implies --pop) to push
2008 patch that is not guarded. Use --reapply (which implies --pop) to push
2008 back to the current patch afterwards, but skip guarded patches.
2009 back to the current patch afterwards, but skip guarded patches.
2009
2010
2010 Use -s/--series to print a list of all guards in the series file (no
2011 Use -s/--series to print a list of all guards in the series file (no
2011 other arguments needed). Use -v for more information.'''
2012 other arguments needed). Use -v for more information.'''
2012
2013
2013 q = repo.mq
2014 q = repo.mq
2014 guards = q.active()
2015 guards = q.active()
2015 if args or opts['none']:
2016 if args or opts['none']:
2016 old_unapplied = q.unapplied(repo)
2017 old_unapplied = q.unapplied(repo)
2017 old_guarded = [i for i in xrange(len(q.applied)) if
2018 old_guarded = [i for i in xrange(len(q.applied)) if
2018 not q.pushable(i)[0]]
2019 not q.pushable(i)[0]]
2019 q.set_active(args)
2020 q.set_active(args)
2020 q.save_dirty()
2021 q.save_dirty()
2021 if not args:
2022 if not args:
2022 ui.status(_('guards deactivated\n'))
2023 ui.status(_('guards deactivated\n'))
2023 if not opts['pop'] and not opts['reapply']:
2024 if not opts['pop'] and not opts['reapply']:
2024 unapplied = q.unapplied(repo)
2025 unapplied = q.unapplied(repo)
2025 guarded = [i for i in xrange(len(q.applied))
2026 guarded = [i for i in xrange(len(q.applied))
2026 if not q.pushable(i)[0]]
2027 if not q.pushable(i)[0]]
2027 if len(unapplied) != len(old_unapplied):
2028 if len(unapplied) != len(old_unapplied):
2028 ui.status(_('number of unguarded, unapplied patches has '
2029 ui.status(_('number of unguarded, unapplied patches has '
2029 'changed from %d to %d\n') %
2030 'changed from %d to %d\n') %
2030 (len(old_unapplied), len(unapplied)))
2031 (len(old_unapplied), len(unapplied)))
2031 if len(guarded) != len(old_guarded):
2032 if len(guarded) != len(old_guarded):
2032 ui.status(_('number of guarded, applied patches has changed '
2033 ui.status(_('number of guarded, applied patches has changed '
2033 'from %d to %d\n') %
2034 'from %d to %d\n') %
2034 (len(old_guarded), len(guarded)))
2035 (len(old_guarded), len(guarded)))
2035 elif opts['series']:
2036 elif opts['series']:
2036 guards = {}
2037 guards = {}
2037 noguards = 0
2038 noguards = 0
2038 for gs in q.series_guards:
2039 for gs in q.series_guards:
2039 if not gs:
2040 if not gs:
2040 noguards += 1
2041 noguards += 1
2041 for g in gs:
2042 for g in gs:
2042 guards.setdefault(g, 0)
2043 guards.setdefault(g, 0)
2043 guards[g] += 1
2044 guards[g] += 1
2044 if ui.verbose:
2045 if ui.verbose:
2045 guards['NONE'] = noguards
2046 guards['NONE'] = noguards
2046 guards = guards.items()
2047 guards = guards.items()
2047 guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
2048 guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
2048 if guards:
2049 if guards:
2049 ui.note(_('guards in series file:\n'))
2050 ui.note(_('guards in series file:\n'))
2050 for guard, count in guards:
2051 for guard, count in guards:
2051 ui.note('%2d ' % count)
2052 ui.note('%2d ' % count)
2052 ui.write(guard, '\n')
2053 ui.write(guard, '\n')
2053 else:
2054 else:
2054 ui.note(_('no guards in series file\n'))
2055 ui.note(_('no guards in series file\n'))
2055 else:
2056 else:
2056 if guards:
2057 if guards:
2057 ui.note(_('active guards:\n'))
2058 ui.note(_('active guards:\n'))
2058 for g in guards:
2059 for g in guards:
2059 ui.write(g, '\n')
2060 ui.write(g, '\n')
2060 else:
2061 else:
2061 ui.write(_('no active guards\n'))
2062 ui.write(_('no active guards\n'))
2062 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2063 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2063 popped = False
2064 popped = False
2064 if opts['pop'] or opts['reapply']:
2065 if opts['pop'] or opts['reapply']:
2065 for i in xrange(len(q.applied)):
2066 for i in xrange(len(q.applied)):
2066 pushable, reason = q.pushable(i)
2067 pushable, reason = q.pushable(i)
2067 if not pushable:
2068 if not pushable:
2068 ui.status(_('popping guarded patches\n'))
2069 ui.status(_('popping guarded patches\n'))
2069 popped = True
2070 popped = True
2070 if i == 0:
2071 if i == 0:
2071 q.pop(repo, all=True)
2072 q.pop(repo, all=True)
2072 else:
2073 else:
2073 q.pop(repo, i-1)
2074 q.pop(repo, i-1)
2074 break
2075 break
2075 if popped:
2076 if popped:
2076 try:
2077 try:
2077 if reapply:
2078 if reapply:
2078 ui.status(_('reapplying unguarded patches\n'))
2079 ui.status(_('reapplying unguarded patches\n'))
2079 q.push(repo, reapply)
2080 q.push(repo, reapply)
2080 finally:
2081 finally:
2081 q.save_dirty()
2082 q.save_dirty()
2082
2083
2083 def reposetup(ui, repo):
2084 def reposetup(ui, repo):
2084 class mqrepo(repo.__class__):
2085 class mqrepo(repo.__class__):
2085 def abort_if_wdir_patched(self, errmsg, force=False):
2086 def abort_if_wdir_patched(self, errmsg, force=False):
2086 if self.mq.applied and not force:
2087 if self.mq.applied and not force:
2087 parent = revlog.hex(self.dirstate.parents()[0])
2088 parent = revlog.hex(self.dirstate.parents()[0])
2088 if parent in [s.rev for s in self.mq.applied]:
2089 if parent in [s.rev for s in self.mq.applied]:
2089 raise util.Abort(errmsg)
2090 raise util.Abort(errmsg)
2090
2091
2091 def commit(self, *args, **opts):
2092 def commit(self, *args, **opts):
2092 if len(args) >= 6:
2093 if len(args) >= 6:
2093 force = args[5]
2094 force = args[5]
2094 else:
2095 else:
2095 force = opts.get('force')
2096 force = opts.get('force')
2096 self.abort_if_wdir_patched(
2097 self.abort_if_wdir_patched(
2097 _('cannot commit over an applied mq patch'),
2098 _('cannot commit over an applied mq patch'),
2098 force)
2099 force)
2099
2100
2100 return super(mqrepo, self).commit(*args, **opts)
2101 return super(mqrepo, self).commit(*args, **opts)
2101
2102
2102 def push(self, remote, force=False, revs=None):
2103 def push(self, remote, force=False, revs=None):
2103 if self.mq.applied and not force and not revs:
2104 if self.mq.applied and not force and not revs:
2104 raise util.Abort(_('source has mq patches applied'))
2105 raise util.Abort(_('source has mq patches applied'))
2105 return super(mqrepo, self).push(remote, force, revs)
2106 return super(mqrepo, self).push(remote, force, revs)
2106
2107
2107 def tags(self):
2108 def tags(self):
2108 if self.tagscache:
2109 if self.tagscache:
2109 return self.tagscache
2110 return self.tagscache
2110
2111
2111 tagscache = super(mqrepo, self).tags()
2112 tagscache = super(mqrepo, self).tags()
2112
2113
2113 q = self.mq
2114 q = self.mq
2114 if not q.applied:
2115 if not q.applied:
2115 return tagscache
2116 return tagscache
2116
2117
2117 mqtags = [(revlog.bin(patch.rev), patch.name) for patch in q.applied]
2118 mqtags = [(revlog.bin(patch.rev), patch.name) for patch in q.applied]
2118
2119
2119 if mqtags[-1][0] not in self.changelog.nodemap:
2120 if mqtags[-1][0] not in self.changelog.nodemap:
2120 self.ui.warn('mq status file refers to unknown node %s\n'
2121 self.ui.warn('mq status file refers to unknown node %s\n'
2121 % revlog.short(mqtags[-1][0]))
2122 % revlog.short(mqtags[-1][0]))
2122 return tagscache
2123 return tagscache
2123
2124
2124 mqtags.append((mqtags[-1][0], 'qtip'))
2125 mqtags.append((mqtags[-1][0], 'qtip'))
2125 mqtags.append((mqtags[0][0], 'qbase'))
2126 mqtags.append((mqtags[0][0], 'qbase'))
2126 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2127 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2127 for patch in mqtags:
2128 for patch in mqtags:
2128 if patch[1] in tagscache:
2129 if patch[1] in tagscache:
2129 self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
2130 self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
2130 else:
2131 else:
2131 tagscache[patch[1]] = patch[0]
2132 tagscache[patch[1]] = patch[0]
2132
2133
2133 return tagscache
2134 return tagscache
2134
2135
2135 def _branchtags(self):
2136 def _branchtags(self):
2136 q = self.mq
2137 q = self.mq
2137 if not q.applied:
2138 if not q.applied:
2138 return super(mqrepo, self)._branchtags()
2139 return super(mqrepo, self)._branchtags()
2139
2140
2140 cl = self.changelog
2141 cl = self.changelog
2141 qbasenode = revlog.bin(q.applied[0].rev)
2142 qbasenode = revlog.bin(q.applied[0].rev)
2142 if qbasenode not in cl.nodemap:
2143 if qbasenode not in cl.nodemap:
2143 self.ui.warn('mq status file refers to unknown node %s\n'
2144 self.ui.warn('mq status file refers to unknown node %s\n'
2144 % revlog.short(qbasenode))
2145 % revlog.short(qbasenode))
2145 return super(mqrepo, self)._branchtags()
2146 return super(mqrepo, self)._branchtags()
2146
2147
2147 self.branchcache = {} # avoid recursion in changectx
2148 self.branchcache = {} # avoid recursion in changectx
2148 partial, last, lrev = self._readbranchcache()
2149 partial, last, lrev = self._readbranchcache()
2149
2150
2150 qbase = cl.rev(qbasenode)
2151 qbase = cl.rev(qbasenode)
2151 start = lrev + 1
2152 start = lrev + 1
2152 if start < qbase:
2153 if start < qbase:
2153 # update the cache (excluding the patches) and save it
2154 # update the cache (excluding the patches) and save it
2154 self._updatebranchcache(partial, lrev+1, qbase)
2155 self._updatebranchcache(partial, lrev+1, qbase)
2155 self._writebranchcache(partial, cl.node(qbase-1), qbase-1)
2156 self._writebranchcache(partial, cl.node(qbase-1), qbase-1)
2156 start = qbase
2157 start = qbase
2157 # if start = qbase, the cache is as updated as it should be.
2158 # if start = qbase, the cache is as updated as it should be.
2158 # if start > qbase, the cache includes (part of) the patches.
2159 # if start > qbase, the cache includes (part of) the patches.
2159 # we might as well use it, but we won't save it.
2160 # we might as well use it, but we won't save it.
2160
2161
2161 # update the cache up to the tip
2162 # update the cache up to the tip
2162 self._updatebranchcache(partial, start, cl.count())
2163 self._updatebranchcache(partial, start, cl.count())
2163
2164
2164 return partial
2165 return partial
2165
2166
2166 if repo.local():
2167 if repo.local():
2167 repo.__class__ = mqrepo
2168 repo.__class__ = mqrepo
2168 repo.mq = queue(ui, repo.join(""))
2169 repo.mq = queue(ui, repo.join(""))
2169
2170
2170 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2171 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2171
2172
2172 cmdtable = {
2173 cmdtable = {
2173 "qapplied": (applied, [] + seriesopts, _('hg qapplied [-s] [PATCH]')),
2174 "qapplied": (applied, [] + seriesopts, _('hg qapplied [-s] [PATCH]')),
2174 "qclone":
2175 "qclone":
2175 (clone,
2176 (clone,
2176 [('', 'pull', None, _('use pull protocol to copy metadata')),
2177 [('', 'pull', None, _('use pull protocol to copy metadata')),
2177 ('U', 'noupdate', None, _('do not update the new working directories')),
2178 ('U', 'noupdate', None, _('do not update the new working directories')),
2178 ('', 'uncompressed', None,
2179 ('', 'uncompressed', None,
2179 _('use uncompressed transfer (fast over LAN)')),
2180 _('use uncompressed transfer (fast over LAN)')),
2180 ('p', 'patches', '', _('location of source patch repo')),
2181 ('p', 'patches', '', _('location of source patch repo')),
2181 ] + commands.remoteopts,
2182 ] + commands.remoteopts,
2182 _('hg qclone [OPTION]... SOURCE [DEST]')),
2183 _('hg qclone [OPTION]... SOURCE [DEST]')),
2183 "qcommit|qci":
2184 "qcommit|qci":
2184 (commit,
2185 (commit,
2185 commands.table["^commit|ci"][1],
2186 commands.table["^commit|ci"][1],
2186 _('hg qcommit [OPTION]... [FILE]...')),
2187 _('hg qcommit [OPTION]... [FILE]...')),
2187 "^qdiff":
2188 "^qdiff":
2188 (diff,
2189 (diff,
2189 [('g', 'git', None, _('use git extended diff format')),
2190 [('g', 'git', None, _('use git extended diff format')),
2190 ] + commands.walkopts,
2191 ] + commands.walkopts,
2191 _('hg qdiff [-I] [-X] [-g] [FILE]...')),
2192 _('hg qdiff [-I] [-X] [-g] [FILE]...')),
2192 "qdelete|qremove|qrm":
2193 "qdelete|qremove|qrm":
2193 (delete,
2194 (delete,
2194 [('k', 'keep', None, _('keep patch file')),
2195 [('k', 'keep', None, _('keep patch file')),
2195 ('r', 'rev', [], _('stop managing a revision'))],
2196 ('r', 'rev', [], _('stop managing a revision'))],
2196 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2197 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2197 'qfold':
2198 'qfold':
2198 (fold,
2199 (fold,
2199 [('e', 'edit', None, _('edit patch header')),
2200 [('e', 'edit', None, _('edit patch header')),
2200 ('k', 'keep', None, _('keep folded patch files')),
2201 ('k', 'keep', None, _('keep folded patch files')),
2201 ] + commands.commitopts,
2202 ] + commands.commitopts,
2202 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2203 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2203 'qgoto':
2204 'qgoto':
2204 (goto,
2205 (goto,
2205 [('f', 'force', None, _('overwrite any local changes'))],
2206 [('f', 'force', None, _('overwrite any local changes'))],
2206 _('hg qgoto [OPTION]... PATCH')),
2207 _('hg qgoto [OPTION]... PATCH')),
2207 'qguard':
2208 'qguard':
2208 (guard,
2209 (guard,
2209 [('l', 'list', None, _('list all patches and guards')),
2210 [('l', 'list', None, _('list all patches and guards')),
2210 ('n', 'none', None, _('drop all guards'))],
2211 ('n', 'none', None, _('drop all guards'))],
2211 _('hg qguard [-l] [-n] [PATCH] [+GUARD]... [-GUARD]...')),
2212 _('hg qguard [-l] [-n] [PATCH] [+GUARD]... [-GUARD]...')),
2212 'qheader': (header, [], _('hg qheader [PATCH]')),
2213 'qheader': (header, [], _('hg qheader [PATCH]')),
2213 "^qimport":
2214 "^qimport":
2214 (qimport,
2215 (qimport,
2215 [('e', 'existing', None, 'import file in patch dir'),
2216 [('e', 'existing', None, 'import file in patch dir'),
2216 ('n', 'name', '', 'patch file name'),
2217 ('n', 'name', '', 'patch file name'),
2217 ('f', 'force', None, 'overwrite existing files'),
2218 ('f', 'force', None, 'overwrite existing files'),
2218 ('r', 'rev', [], 'place existing revisions under mq control'),
2219 ('r', 'rev', [], 'place existing revisions under mq control'),
2219 ('g', 'git', None, _('use git extended diff format'))],
2220 ('g', 'git', None, _('use git extended diff format'))],
2220 _('hg qimport [-e] [-n NAME] [-f] [-g] [-r REV]... FILE...')),
2221 _('hg qimport [-e] [-n NAME] [-f] [-g] [-r REV]... FILE...')),
2221 "^qinit":
2222 "^qinit":
2222 (init,
2223 (init,
2223 [('c', 'create-repo', None, 'create queue repository')],
2224 [('c', 'create-repo', None, 'create queue repository')],
2224 _('hg qinit [-c]')),
2225 _('hg qinit [-c]')),
2225 "qnew":
2226 "qnew":
2226 (new,
2227 (new,
2227 [('e', 'edit', None, _('edit commit message')),
2228 [('e', 'edit', None, _('edit commit message')),
2228 ('f', 'force', None, _('import uncommitted changes into patch')),
2229 ('f', 'force', None, _('import uncommitted changes into patch')),
2229 ('g', 'git', None, _('use git extended diff format')),
2230 ('g', 'git', None, _('use git extended diff format')),
2230 ] + commands.walkopts + commands.commitopts,
2231 ] + commands.walkopts + commands.commitopts,
2231 _('hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH [FILE]...')),
2232 _('hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH [FILE]...')),
2232 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2233 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2233 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2234 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2234 "^qpop":
2235 "^qpop":
2235 (pop,
2236 (pop,
2236 [('a', 'all', None, _('pop all patches')),
2237 [('a', 'all', None, _('pop all patches')),
2237 ('n', 'name', '', _('queue name to pop')),
2238 ('n', 'name', '', _('queue name to pop')),
2238 ('f', 'force', None, _('forget any local changes'))],
2239 ('f', 'force', None, _('forget any local changes'))],
2239 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2240 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2240 "^qpush":
2241 "^qpush":
2241 (push,
2242 (push,
2242 [('f', 'force', None, _('apply if the patch has rejects')),
2243 [('f', 'force', None, _('apply if the patch has rejects')),
2243 ('l', 'list', None, _('list patch name in commit text')),
2244 ('l', 'list', None, _('list patch name in commit text')),
2244 ('a', 'all', None, _('apply all patches')),
2245 ('a', 'all', None, _('apply all patches')),
2245 ('m', 'merge', None, _('merge from another queue')),
2246 ('m', 'merge', None, _('merge from another queue')),
2246 ('n', 'name', '', _('merge queue name'))],
2247 ('n', 'name', '', _('merge queue name'))],
2247 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]')),
2248 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]')),
2248 "^qrefresh":
2249 "^qrefresh":
2249 (refresh,
2250 (refresh,
2250 [('e', 'edit', None, _('edit commit message')),
2251 [('e', 'edit', None, _('edit commit message')),
2251 ('g', 'git', None, _('use git extended diff format')),
2252 ('g', 'git', None, _('use git extended diff format')),
2252 ('s', 'short', None, _('refresh only files already in the patch')),
2253 ('s', 'short', None, _('refresh only files already in the patch')),
2253 ] + commands.walkopts + commands.commitopts,
2254 ] + commands.walkopts + commands.commitopts,
2254 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2255 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2255 'qrename|qmv':
2256 'qrename|qmv':
2256 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2257 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2257 "qrestore":
2258 "qrestore":
2258 (restore,
2259 (restore,
2259 [('d', 'delete', None, _('delete save entry')),
2260 [('d', 'delete', None, _('delete save entry')),
2260 ('u', 'update', None, _('update queue working dir'))],
2261 ('u', 'update', None, _('update queue working dir'))],
2261 _('hg qrestore [-d] [-u] REV')),
2262 _('hg qrestore [-d] [-u] REV')),
2262 "qsave":
2263 "qsave":
2263 (save,
2264 (save,
2264 [('c', 'copy', None, _('copy patch directory')),
2265 [('c', 'copy', None, _('copy patch directory')),
2265 ('n', 'name', '', _('copy directory name')),
2266 ('n', 'name', '', _('copy directory name')),
2266 ('e', 'empty', None, _('clear queue status file')),
2267 ('e', 'empty', None, _('clear queue status file')),
2267 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2268 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2268 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2269 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2269 "qselect":
2270 "qselect":
2270 (select,
2271 (select,
2271 [('n', 'none', None, _('disable all guards')),
2272 [('n', 'none', None, _('disable all guards')),
2272 ('s', 'series', None, _('list all guards in series file')),
2273 ('s', 'series', None, _('list all guards in series file')),
2273 ('', 'pop', None, _('pop to before first guarded applied patch')),
2274 ('', 'pop', None, _('pop to before first guarded applied patch')),
2274 ('', 'reapply', None, _('pop, then reapply patches'))],
2275 ('', 'reapply', None, _('pop, then reapply patches'))],
2275 _('hg qselect [OPTION]... [GUARD]...')),
2276 _('hg qselect [OPTION]... [GUARD]...')),
2276 "qseries":
2277 "qseries":
2277 (series,
2278 (series,
2278 [('m', 'missing', None, _('print patches not in series')),
2279 [('m', 'missing', None, _('print patches not in series')),
2279 ] + seriesopts,
2280 ] + seriesopts,
2280 _('hg qseries [-ms]')),
2281 _('hg qseries [-ms]')),
2281 "^strip":
2282 "^strip":
2282 (strip,
2283 (strip,
2283 [('f', 'force', None, _('force multi-head removal')),
2284 [('f', 'force', None, _('force multi-head removal')),
2284 ('b', 'backup', None, _('bundle unrelated changesets')),
2285 ('b', 'backup', None, _('bundle unrelated changesets')),
2285 ('n', 'nobackup', None, _('no backups'))],
2286 ('n', 'nobackup', None, _('no backups'))],
2286 _('hg strip [-f] [-b] [-n] REV')),
2287 _('hg strip [-f] [-b] [-n] REV')),
2287 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
2288 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
2288 "qunapplied": (unapplied, [] + seriesopts, _('hg qunapplied [-s] [PATCH]')),
2289 "qunapplied": (unapplied, [] + seriesopts, _('hg qunapplied [-s] [PATCH]')),
2289 }
2290 }
General Comments 0
You need to be logged in to leave comments. Login now