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