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