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