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