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