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