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