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