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