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