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