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