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