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