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