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