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