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