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