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