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