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