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