##// END OF EJS Templates
Better help for mq: Corrected synopses, get qcommit options from commands.py.
Thomas Arendsen Hein -
r2185:5acd6487 default
parent child Browse files
Show More
@@ -1,1311 +1,1305 b''
1 # queue.py - patch queues for mercurial
1 # queue.py - patch queues for mercurial
2 #
2 #
3 # Copyright 2005 Chris Mason <mason@suse.com>
3 # Copyright 2005 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 from mercurial.demandload import *
8 from mercurial.demandload import *
9 demandload(globals(), "os sys re struct traceback errno bz2")
9 demandload(globals(), "os sys re struct traceback errno bz2")
10 from mercurial.i18n import gettext as _
10 from mercurial.i18n import gettext as _
11 from mercurial import ui, hg, revlog, commands, util
11 from mercurial import ui, hg, revlog, commands, util
12
12
13 versionstr = "0.45"
13 versionstr = "0.45"
14
14
15 repomap = {}
15 repomap = {}
16
16
17 commands.norepo += " qversion"
17 commands.norepo += " qversion"
18 class queue:
18 class queue:
19 def __init__(self, ui, path, patchdir=None):
19 def __init__(self, ui, path, patchdir=None):
20 self.basepath = path
20 self.basepath = path
21 if patchdir:
21 if patchdir:
22 self.path = patchdir
22 self.path = patchdir
23 else:
23 else:
24 self.path = os.path.join(path, "patches")
24 self.path = os.path.join(path, "patches")
25 self.opener = util.opener(self.path)
25 self.opener = util.opener(self.path)
26 self.ui = ui
26 self.ui = ui
27 self.applied = []
27 self.applied = []
28 self.full_series = []
28 self.full_series = []
29 self.applied_dirty = 0
29 self.applied_dirty = 0
30 self.series_dirty = 0
30 self.series_dirty = 0
31 self.series_path = "series"
31 self.series_path = "series"
32 self.status_path = "status"
32 self.status_path = "status"
33
33
34 if os.path.exists(os.path.join(self.path, self.series_path)):
34 if os.path.exists(os.path.join(self.path, self.series_path)):
35 self.full_series = self.opener(self.series_path).read().splitlines()
35 self.full_series = self.opener(self.series_path).read().splitlines()
36 self.read_series(self.full_series)
36 self.read_series(self.full_series)
37
37
38 if os.path.exists(os.path.join(self.path, self.status_path)):
38 if os.path.exists(os.path.join(self.path, self.status_path)):
39 self.applied = self.opener(self.status_path).read().splitlines()
39 self.applied = self.opener(self.status_path).read().splitlines()
40
40
41 def find_series(self, patch):
41 def find_series(self, patch):
42 pre = re.compile("(\s*)([^#]+)")
42 pre = re.compile("(\s*)([^#]+)")
43 index = 0
43 index = 0
44 for l in self.full_series:
44 for l in self.full_series:
45 m = pre.match(l)
45 m = pre.match(l)
46 if m:
46 if m:
47 s = m.group(2)
47 s = m.group(2)
48 s = s.rstrip()
48 s = s.rstrip()
49 if s == patch:
49 if s == patch:
50 return index
50 return index
51 index += 1
51 index += 1
52 return None
52 return None
53
53
54 def read_series(self, list):
54 def read_series(self, list):
55 def matcher(list):
55 def matcher(list):
56 pre = re.compile("(\s*)([^#]+)")
56 pre = re.compile("(\s*)([^#]+)")
57 for l in list:
57 for l in list:
58 m = pre.match(l)
58 m = pre.match(l)
59 if m:
59 if m:
60 s = m.group(2)
60 s = m.group(2)
61 s = s.rstrip()
61 s = s.rstrip()
62 if len(s) > 0:
62 if len(s) > 0:
63 yield s
63 yield s
64 self.series = []
64 self.series = []
65 self.series = [ x for x in matcher(list) ]
65 self.series = [ x for x in matcher(list) ]
66
66
67 def save_dirty(self):
67 def save_dirty(self):
68 if self.applied_dirty:
68 if self.applied_dirty:
69 if len(self.applied) > 0:
69 if len(self.applied) > 0:
70 nl = "\n"
70 nl = "\n"
71 else:
71 else:
72 nl = ""
72 nl = ""
73 f = self.opener(self.status_path, "w")
73 f = self.opener(self.status_path, "w")
74 f.write("\n".join(self.applied) + nl)
74 f.write("\n".join(self.applied) + nl)
75 if self.series_dirty:
75 if self.series_dirty:
76 if len(self.full_series) > 0:
76 if len(self.full_series) > 0:
77 nl = "\n"
77 nl = "\n"
78 else:
78 else:
79 nl = ""
79 nl = ""
80 f = self.opener(self.series_path, "w")
80 f = self.opener(self.series_path, "w")
81 f.write("\n".join(self.full_series) + nl)
81 f.write("\n".join(self.full_series) + nl)
82
82
83 def readheaders(self, patch):
83 def readheaders(self, patch):
84 def eatdiff(lines):
84 def eatdiff(lines):
85 while lines:
85 while lines:
86 l = lines[-1]
86 l = lines[-1]
87 if (l.startswith("diff -") or
87 if (l.startswith("diff -") or
88 l.startswith("Index:") or
88 l.startswith("Index:") or
89 l.startswith("===========")):
89 l.startswith("===========")):
90 del lines[-1]
90 del lines[-1]
91 else:
91 else:
92 break
92 break
93 def eatempty(lines):
93 def eatempty(lines):
94 while lines:
94 while lines:
95 l = lines[-1]
95 l = lines[-1]
96 if re.match('\s*$', l):
96 if re.match('\s*$', l):
97 del lines[-1]
97 del lines[-1]
98 else:
98 else:
99 break
99 break
100
100
101 pf = os.path.join(self.path, patch)
101 pf = os.path.join(self.path, patch)
102 message = []
102 message = []
103 comments = []
103 comments = []
104 user = None
104 user = None
105 format = None
105 format = None
106 subject = None
106 subject = None
107 diffstart = 0
107 diffstart = 0
108
108
109 for line in file(pf):
109 for line in file(pf):
110 line = line.rstrip()
110 line = line.rstrip()
111 if diffstart:
111 if diffstart:
112 if line.startswith('+++ '):
112 if line.startswith('+++ '):
113 diffstart = 2
113 diffstart = 2
114 break
114 break
115 if line.startswith("--- "):
115 if line.startswith("--- "):
116 diffstart = 1
116 diffstart = 1
117 continue
117 continue
118 elif format == "hgpatch":
118 elif format == "hgpatch":
119 # parse values when importing the result of an hg export
119 # parse values when importing the result of an hg export
120 if line.startswith("# User "):
120 if line.startswith("# User "):
121 user = line[7:]
121 user = line[7:]
122 elif not line.startswith("# ") and line:
122 elif not line.startswith("# ") and line:
123 message.append(line)
123 message.append(line)
124 format = None
124 format = None
125 elif line == '# HG changeset patch':
125 elif line == '# HG changeset patch':
126 format = "hgpatch"
126 format = "hgpatch"
127 elif (format != "tagdone" and (line.startswith("Subject: ") or
127 elif (format != "tagdone" and (line.startswith("Subject: ") or
128 line.startswith("subject: "))):
128 line.startswith("subject: "))):
129 subject = line[9:]
129 subject = line[9:]
130 format = "tag"
130 format = "tag"
131 elif (format != "tagdone" and (line.startswith("From: ") or
131 elif (format != "tagdone" and (line.startswith("From: ") or
132 line.startswith("from: "))):
132 line.startswith("from: "))):
133 user = line[6:]
133 user = line[6:]
134 format = "tag"
134 format = "tag"
135 elif format == "tag" and line == "":
135 elif format == "tag" and line == "":
136 # when looking for tags (subject: from: etc) they
136 # when looking for tags (subject: from: etc) they
137 # end once you find a blank line in the source
137 # end once you find a blank line in the source
138 format = "tagdone"
138 format = "tagdone"
139 else:
139 else:
140 message.append(line)
140 message.append(line)
141 comments.append(line)
141 comments.append(line)
142
142
143 eatdiff(message)
143 eatdiff(message)
144 eatdiff(comments)
144 eatdiff(comments)
145 eatempty(message)
145 eatempty(message)
146 eatempty(comments)
146 eatempty(comments)
147
147
148 # make sure message isn't empty
148 # make sure message isn't empty
149 if format and format.startswith("tag") and subject:
149 if format and format.startswith("tag") and subject:
150 message.insert(0, "")
150 message.insert(0, "")
151 message.insert(0, subject)
151 message.insert(0, subject)
152 return (message, comments, user, diffstart > 1)
152 return (message, comments, user, diffstart > 1)
153
153
154 def mergeone(self, repo, mergeq, head, patch, rev, wlock):
154 def mergeone(self, repo, mergeq, head, patch, rev, wlock):
155 # first try just applying the patch
155 # first try just applying the patch
156 (err, n) = self.apply(repo, [ patch ], update_status=False,
156 (err, n) = self.apply(repo, [ patch ], update_status=False,
157 strict=True, merge=rev, wlock=wlock)
157 strict=True, merge=rev, wlock=wlock)
158
158
159 if err == 0:
159 if err == 0:
160 return (err, n)
160 return (err, n)
161
161
162 if n is None:
162 if n is None:
163 self.ui.warn("apply failed for patch %s\n" % patch)
163 self.ui.warn("apply failed for patch %s\n" % patch)
164 sys.exit(1)
164 sys.exit(1)
165
165
166 self.ui.warn("patch didn't work out, merging %s\n" % patch)
166 self.ui.warn("patch didn't work out, merging %s\n" % patch)
167
167
168 # apply failed, strip away that rev and merge.
168 # apply failed, strip away that rev and merge.
169 repo.update(head, allow=False, force=True, wlock=wlock)
169 repo.update(head, allow=False, force=True, wlock=wlock)
170 self.strip(repo, n, update=False, backup='strip', wlock=wlock)
170 self.strip(repo, n, update=False, backup='strip', wlock=wlock)
171
171
172 c = repo.changelog.read(rev)
172 c = repo.changelog.read(rev)
173 ret = repo.update(rev, allow=True, wlock=wlock)
173 ret = repo.update(rev, allow=True, wlock=wlock)
174 if ret:
174 if ret:
175 self.ui.warn("update returned %d\n" % ret)
175 self.ui.warn("update returned %d\n" % ret)
176 sys.exit(1)
176 sys.exit(1)
177 n = repo.commit(None, c[4], c[1], force=1, wlock=wlock)
177 n = repo.commit(None, c[4], c[1], force=1, wlock=wlock)
178 if n == None:
178 if n == None:
179 self.ui.warn("repo commit failed\n")
179 self.ui.warn("repo commit failed\n")
180 sys.exit(1)
180 sys.exit(1)
181 try:
181 try:
182 message, comments, user, patchfound = mergeq.readheaders(patch)
182 message, comments, user, patchfound = mergeq.readheaders(patch)
183 except:
183 except:
184 self.ui.warn("Unable to read %s\n" % patch)
184 self.ui.warn("Unable to read %s\n" % patch)
185 sys.exit(1)
185 sys.exit(1)
186
186
187 patchf = self.opener(patch, "w")
187 patchf = self.opener(patch, "w")
188 if comments:
188 if comments:
189 comments = "\n".join(comments) + '\n\n'
189 comments = "\n".join(comments) + '\n\n'
190 patchf.write(comments)
190 patchf.write(comments)
191 commands.dodiff(patchf, self.ui, repo, head, n)
191 commands.dodiff(patchf, self.ui, repo, head, n)
192 patchf.close()
192 patchf.close()
193 return (0, n)
193 return (0, n)
194
194
195 def qparents(self, repo, rev=None):
195 def qparents(self, repo, rev=None):
196 if rev is None:
196 if rev is None:
197 (p1, p2) = repo.dirstate.parents()
197 (p1, p2) = repo.dirstate.parents()
198 if p2 == revlog.nullid:
198 if p2 == revlog.nullid:
199 return p1
199 return p1
200 if len(self.applied) == 0:
200 if len(self.applied) == 0:
201 return None
201 return None
202 (top, patch) = self.applied[-1].split(':')
202 (top, patch) = self.applied[-1].split(':')
203 top = revlog.bin(top)
203 top = revlog.bin(top)
204 return top
204 return top
205 pp = repo.changelog.parents(rev)
205 pp = repo.changelog.parents(rev)
206 if pp[1] != revlog.nullid:
206 if pp[1] != revlog.nullid:
207 arevs = [ x.split(':')[0] for x in self.applied ]
207 arevs = [ x.split(':')[0] for x in self.applied ]
208 p0 = revlog.hex(pp[0])
208 p0 = revlog.hex(pp[0])
209 p1 = revlog.hex(pp[1])
209 p1 = revlog.hex(pp[1])
210 if p0 in arevs:
210 if p0 in arevs:
211 return pp[0]
211 return pp[0]
212 if p1 in arevs:
212 if p1 in arevs:
213 return pp[1]
213 return pp[1]
214 return None
214 return None
215 return pp[0]
215 return pp[0]
216
216
217 def mergepatch(self, repo, mergeq, series, wlock):
217 def mergepatch(self, repo, mergeq, series, wlock):
218 if len(self.applied) == 0:
218 if len(self.applied) == 0:
219 # each of the patches merged in will have two parents. This
219 # each of the patches merged in will have two parents. This
220 # can confuse the qrefresh, qdiff, and strip code because it
220 # can confuse the qrefresh, qdiff, and strip code because it
221 # needs to know which parent is actually in the patch queue.
221 # needs to know which parent is actually in the patch queue.
222 # so, we insert a merge marker with only one parent. This way
222 # so, we insert a merge marker with only one parent. This way
223 # the first patch in the queue is never a merge patch
223 # the first patch in the queue is never a merge patch
224 #
224 #
225 pname = ".hg.patches.merge.marker"
225 pname = ".hg.patches.merge.marker"
226 n = repo.commit(None, '[mq]: merge marker', user=None, force=1,
226 n = repo.commit(None, '[mq]: merge marker', user=None, force=1,
227 wlock=wlock)
227 wlock=wlock)
228 self.applied.append(revlog.hex(n) + ":" + pname)
228 self.applied.append(revlog.hex(n) + ":" + pname)
229 self.applied_dirty = 1
229 self.applied_dirty = 1
230
230
231 head = self.qparents(repo)
231 head = self.qparents(repo)
232
232
233 for patch in series:
233 for patch in series:
234 patch = mergeq.lookup(patch)
234 patch = mergeq.lookup(patch)
235 if not patch:
235 if not patch:
236 self.ui.warn("patch %s does not exist\n" % patch)
236 self.ui.warn("patch %s does not exist\n" % patch)
237 return (1, None)
237 return (1, None)
238
238
239 info = mergeq.isapplied(patch)
239 info = mergeq.isapplied(patch)
240 if not info:
240 if not info:
241 self.ui.warn("patch %s is not applied\n" % patch)
241 self.ui.warn("patch %s is not applied\n" % patch)
242 return (1, None)
242 return (1, None)
243 rev = revlog.bin(info[1])
243 rev = revlog.bin(info[1])
244 (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock)
244 (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock)
245 if head:
245 if head:
246 self.applied.append(revlog.hex(head) + ":" + patch)
246 self.applied.append(revlog.hex(head) + ":" + patch)
247 self.applied_dirty = 1
247 self.applied_dirty = 1
248 if err:
248 if err:
249 return (err, head)
249 return (err, head)
250 return (0, head)
250 return (0, head)
251
251
252 def apply(self, repo, series, list=False, update_status=True,
252 def apply(self, repo, series, list=False, update_status=True,
253 strict=False, patchdir=None, merge=None, wlock=None):
253 strict=False, patchdir=None, merge=None, wlock=None):
254 # TODO unify with commands.py
254 # TODO unify with commands.py
255 if not patchdir:
255 if not patchdir:
256 patchdir = self.path
256 patchdir = self.path
257 pwd = os.getcwd()
257 pwd = os.getcwd()
258 os.chdir(repo.root)
258 os.chdir(repo.root)
259 err = 0
259 err = 0
260 if not wlock:
260 if not wlock:
261 wlock = repo.wlock()
261 wlock = repo.wlock()
262 lock = repo.lock()
262 lock = repo.lock()
263 tr = repo.transaction()
263 tr = repo.transaction()
264 n = None
264 n = None
265 for patch in series:
265 for patch in series:
266 self.ui.warn("applying %s\n" % patch)
266 self.ui.warn("applying %s\n" % patch)
267 pf = os.path.join(patchdir, patch)
267 pf = os.path.join(patchdir, patch)
268
268
269 try:
269 try:
270 message, comments, user, patchfound = self.readheaders(patch)
270 message, comments, user, patchfound = self.readheaders(patch)
271 except:
271 except:
272 self.ui.warn("Unable to read %s\n" % pf)
272 self.ui.warn("Unable to read %s\n" % pf)
273 err = 1
273 err = 1
274 break
274 break
275
275
276 if not message:
276 if not message:
277 message = "imported patch %s\n" % patch
277 message = "imported patch %s\n" % patch
278 else:
278 else:
279 if list:
279 if list:
280 message.append("\nimported patch %s" % patch)
280 message.append("\nimported patch %s" % patch)
281 message = '\n'.join(message)
281 message = '\n'.join(message)
282
282
283 try:
283 try:
284 f = os.popen("patch -p1 --no-backup-if-mismatch < '%s'" % (pf))
284 f = os.popen("patch -p1 --no-backup-if-mismatch < '%s'" % (pf))
285 except:
285 except:
286 self.ui.warn("patch failed, unable to continue (try -v)\n")
286 self.ui.warn("patch failed, unable to continue (try -v)\n")
287 err = 1
287 err = 1
288 break
288 break
289 files = []
289 files = []
290 fuzz = False
290 fuzz = False
291 for l in f:
291 for l in f:
292 l = l.rstrip('\r\n');
292 l = l.rstrip('\r\n');
293 if self.ui.verbose:
293 if self.ui.verbose:
294 self.ui.warn(l + "\n")
294 self.ui.warn(l + "\n")
295 if l[:14] == 'patching file ':
295 if l[:14] == 'patching file ':
296 pf = os.path.normpath(l[14:])
296 pf = os.path.normpath(l[14:])
297 # when patch finds a space in the file name, it puts
297 # when patch finds a space in the file name, it puts
298 # single quotes around the filename. strip them off
298 # single quotes around the filename. strip them off
299 if pf[0] == "'" and pf[-1] == "'":
299 if pf[0] == "'" and pf[-1] == "'":
300 pf = pf[1:-1]
300 pf = pf[1:-1]
301 if pf not in files:
301 if pf not in files:
302 files.append(pf)
302 files.append(pf)
303 printed_file = False
303 printed_file = False
304 file_str = l
304 file_str = l
305 elif l.find('with fuzz') >= 0:
305 elif l.find('with fuzz') >= 0:
306 if not printed_file:
306 if not printed_file:
307 self.ui.warn(file_str + '\n')
307 self.ui.warn(file_str + '\n')
308 printed_file = True
308 printed_file = True
309 self.ui.warn(l + '\n')
309 self.ui.warn(l + '\n')
310 fuzz = True
310 fuzz = True
311 elif l.find('saving rejects to file') >= 0:
311 elif l.find('saving rejects to file') >= 0:
312 self.ui.warn(l + '\n')
312 self.ui.warn(l + '\n')
313 elif l.find('FAILED') >= 0:
313 elif l.find('FAILED') >= 0:
314 if not printed_file:
314 if not printed_file:
315 self.ui.warn(file_str + '\n')
315 self.ui.warn(file_str + '\n')
316 printed_file = True
316 printed_file = True
317 self.ui.warn(l + '\n')
317 self.ui.warn(l + '\n')
318 patcherr = f.close()
318 patcherr = f.close()
319
319
320 if merge and len(files) > 0:
320 if merge and len(files) > 0:
321 # Mark as merged and update dirstate parent info
321 # Mark as merged and update dirstate parent info
322 repo.dirstate.update(repo.dirstate.filterfiles(files), 'm')
322 repo.dirstate.update(repo.dirstate.filterfiles(files), 'm')
323 p1, p2 = repo.dirstate.parents()
323 p1, p2 = repo.dirstate.parents()
324 repo.dirstate.setparents(p1, merge)
324 repo.dirstate.setparents(p1, merge)
325 if len(files) > 0:
325 if len(files) > 0:
326 commands.addremove_lock(self.ui, repo, files,
326 commands.addremove_lock(self.ui, repo, files,
327 opts={}, wlock=wlock)
327 opts={}, wlock=wlock)
328 n = repo.commit(files, message, user, force=1, lock=lock,
328 n = repo.commit(files, message, user, force=1, lock=lock,
329 wlock=wlock)
329 wlock=wlock)
330
330
331 if n == None:
331 if n == None:
332 self.ui.warn("repo commit failed\n")
332 self.ui.warn("repo commit failed\n")
333 sys.exit(1)
333 sys.exit(1)
334
334
335 if update_status:
335 if update_status:
336 self.applied.append(revlog.hex(n) + ":" + patch)
336 self.applied.append(revlog.hex(n) + ":" + patch)
337
337
338 if patcherr:
338 if patcherr:
339 if not patchfound:
339 if not patchfound:
340 self.ui.warn("patch %s is empty\n" % patch)
340 self.ui.warn("patch %s is empty\n" % patch)
341 err = 0
341 err = 0
342 else:
342 else:
343 self.ui.warn("patch failed, rejects left in working dir\n")
343 self.ui.warn("patch failed, rejects left in working dir\n")
344 err = 1
344 err = 1
345 break
345 break
346
346
347 if fuzz and strict:
347 if fuzz and strict:
348 self.ui.warn("fuzz found when applying patch, stopping\n")
348 self.ui.warn("fuzz found when applying patch, stopping\n")
349 err = 1
349 err = 1
350 break
350 break
351 tr.close()
351 tr.close()
352 os.chdir(pwd)
352 os.chdir(pwd)
353 return (err, n)
353 return (err, n)
354
354
355 def delete(self, repo, patch):
355 def delete(self, repo, patch):
356 patch = self.lookup(patch)
356 patch = self.lookup(patch)
357 info = self.isapplied(patch)
357 info = self.isapplied(patch)
358 if info:
358 if info:
359 self.ui.warn("cannot delete applied patch %s\n" % patch)
359 self.ui.warn("cannot delete applied patch %s\n" % patch)
360 sys.exit(1)
360 sys.exit(1)
361 if patch not in self.series:
361 if patch not in self.series:
362 self.ui.warn("patch %s not in series file\n" % patch)
362 self.ui.warn("patch %s not in series file\n" % patch)
363 sys.exit(1)
363 sys.exit(1)
364 i = self.find_series(patch)
364 i = self.find_series(patch)
365 del self.full_series[i]
365 del self.full_series[i]
366 self.read_series(self.full_series)
366 self.read_series(self.full_series)
367 self.series_dirty = 1
367 self.series_dirty = 1
368
368
369 def check_toppatch(self, repo):
369 def check_toppatch(self, repo):
370 if len(self.applied) > 0:
370 if len(self.applied) > 0:
371 (top, patch) = self.applied[-1].split(':')
371 (top, patch) = self.applied[-1].split(':')
372 top = revlog.bin(top)
372 top = revlog.bin(top)
373 pp = repo.dirstate.parents()
373 pp = repo.dirstate.parents()
374 if top not in pp:
374 if top not in pp:
375 self.ui.warn("queue top not at dirstate parents. top %s dirstate %s %s\n" %( revlog.short(top), revlog.short(pp[0]), revlog.short(pp[1])))
375 self.ui.warn("queue top not at dirstate parents. top %s dirstate %s %s\n" %( revlog.short(top), revlog.short(pp[0]), revlog.short(pp[1])))
376 sys.exit(1)
376 sys.exit(1)
377 return top
377 return top
378 return None
378 return None
379 def check_localchanges(self, repo):
379 def check_localchanges(self, repo):
380 (c, a, r, d, u) = repo.changes(None, None)
380 (c, a, r, d, u) = repo.changes(None, None)
381 if c or a or d or r:
381 if c or a or d or r:
382 self.ui.write("Local changes found, refresh first\n")
382 self.ui.write("Local changes found, refresh first\n")
383 sys.exit(1)
383 sys.exit(1)
384 def new(self, repo, patch, msg=None, force=None):
384 def new(self, repo, patch, msg=None, force=None):
385 if not force:
385 if not force:
386 self.check_localchanges(repo)
386 self.check_localchanges(repo)
387 self.check_toppatch(repo)
387 self.check_toppatch(repo)
388 wlock = repo.wlock()
388 wlock = repo.wlock()
389 insert = self.series_end()
389 insert = self.series_end()
390 if msg:
390 if msg:
391 n = repo.commit([], "[mq]: %s" % msg, force=True, wlock=wlock)
391 n = repo.commit([], "[mq]: %s" % msg, force=True, wlock=wlock)
392 else:
392 else:
393 n = repo.commit([],
393 n = repo.commit([],
394 "New patch: %s" % patch, force=True, wlock=wlock)
394 "New patch: %s" % patch, force=True, wlock=wlock)
395 if n == None:
395 if n == None:
396 self.ui.warn("repo commit failed\n")
396 self.ui.warn("repo commit failed\n")
397 sys.exit(1)
397 sys.exit(1)
398 self.full_series[insert:insert] = [patch]
398 self.full_series[insert:insert] = [patch]
399 self.applied.append(revlog.hex(n) + ":" + patch)
399 self.applied.append(revlog.hex(n) + ":" + patch)
400 self.read_series(self.full_series)
400 self.read_series(self.full_series)
401 self.series_dirty = 1
401 self.series_dirty = 1
402 self.applied_dirty = 1
402 self.applied_dirty = 1
403 p = self.opener(patch, "w")
403 p = self.opener(patch, "w")
404 if msg:
404 if msg:
405 msg = msg + "\n"
405 msg = msg + "\n"
406 p.write(msg)
406 p.write(msg)
407 p.close()
407 p.close()
408 wlock = None
408 wlock = None
409 r = self.qrepo()
409 r = self.qrepo()
410 if r: r.add([patch])
410 if r: r.add([patch])
411
411
412 def strip(self, repo, rev, update=True, backup="all", wlock=None):
412 def strip(self, repo, rev, update=True, backup="all", wlock=None):
413 def limitheads(chlog, stop):
413 def limitheads(chlog, stop):
414 """return the list of all nodes that have no children"""
414 """return the list of all nodes that have no children"""
415 p = {}
415 p = {}
416 h = []
416 h = []
417 stoprev = 0
417 stoprev = 0
418 if stop in chlog.nodemap:
418 if stop in chlog.nodemap:
419 stoprev = chlog.rev(stop)
419 stoprev = chlog.rev(stop)
420
420
421 for r in range(chlog.count() - 1, -1, -1):
421 for r in range(chlog.count() - 1, -1, -1):
422 n = chlog.node(r)
422 n = chlog.node(r)
423 if n not in p:
423 if n not in p:
424 h.append(n)
424 h.append(n)
425 if n == stop:
425 if n == stop:
426 break
426 break
427 if r < stoprev:
427 if r < stoprev:
428 break
428 break
429 for pn in chlog.parents(n):
429 for pn in chlog.parents(n):
430 p[pn] = 1
430 p[pn] = 1
431 return h
431 return h
432
432
433 def bundle(cg):
433 def bundle(cg):
434 backupdir = repo.join("strip-backup")
434 backupdir = repo.join("strip-backup")
435 if not os.path.isdir(backupdir):
435 if not os.path.isdir(backupdir):
436 os.mkdir(backupdir)
436 os.mkdir(backupdir)
437 name = os.path.join(backupdir, "%s" % revlog.short(rev))
437 name = os.path.join(backupdir, "%s" % revlog.short(rev))
438 name = savename(name)
438 name = savename(name)
439 self.ui.warn("saving bundle to %s\n" % name)
439 self.ui.warn("saving bundle to %s\n" % name)
440 # TODO, exclusive open
440 # TODO, exclusive open
441 f = open(name, "wb")
441 f = open(name, "wb")
442 try:
442 try:
443 f.write("HG10")
443 f.write("HG10")
444 z = bz2.BZ2Compressor(9)
444 z = bz2.BZ2Compressor(9)
445 while 1:
445 while 1:
446 chunk = cg.read(4096)
446 chunk = cg.read(4096)
447 if not chunk:
447 if not chunk:
448 break
448 break
449 f.write(z.compress(chunk))
449 f.write(z.compress(chunk))
450 f.write(z.flush())
450 f.write(z.flush())
451 except:
451 except:
452 os.unlink(name)
452 os.unlink(name)
453 raise
453 raise
454 f.close()
454 f.close()
455 return name
455 return name
456
456
457 def stripall(rev, revnum):
457 def stripall(rev, revnum):
458 cl = repo.changelog
458 cl = repo.changelog
459 c = cl.read(rev)
459 c = cl.read(rev)
460 mm = repo.manifest.read(c[0])
460 mm = repo.manifest.read(c[0])
461 seen = {}
461 seen = {}
462
462
463 for x in xrange(revnum, cl.count()):
463 for x in xrange(revnum, cl.count()):
464 c = cl.read(cl.node(x))
464 c = cl.read(cl.node(x))
465 for f in c[3]:
465 for f in c[3]:
466 if f in seen:
466 if f in seen:
467 continue
467 continue
468 seen[f] = 1
468 seen[f] = 1
469 if f in mm:
469 if f in mm:
470 filerev = mm[f]
470 filerev = mm[f]
471 else:
471 else:
472 filerev = 0
472 filerev = 0
473 seen[f] = filerev
473 seen[f] = filerev
474 # we go in two steps here so the strip loop happens in a
474 # we go in two steps here so the strip loop happens in a
475 # sensible order. When stripping many files, this helps keep
475 # sensible order. When stripping many files, this helps keep
476 # our disk access patterns under control.
476 # our disk access patterns under control.
477 list = seen.keys()
477 list = seen.keys()
478 list.sort()
478 list.sort()
479 for f in list:
479 for f in list:
480 ff = repo.file(f)
480 ff = repo.file(f)
481 filerev = seen[f]
481 filerev = seen[f]
482 if filerev != 0:
482 if filerev != 0:
483 if filerev in ff.nodemap:
483 if filerev in ff.nodemap:
484 filerev = ff.rev(filerev)
484 filerev = ff.rev(filerev)
485 else:
485 else:
486 filerev = 0
486 filerev = 0
487 ff.strip(filerev, revnum)
487 ff.strip(filerev, revnum)
488
488
489 if not wlock:
489 if not wlock:
490 wlock = repo.wlock()
490 wlock = repo.wlock()
491 lock = repo.lock()
491 lock = repo.lock()
492 chlog = repo.changelog
492 chlog = repo.changelog
493 # TODO delete the undo files, and handle undo of merge sets
493 # TODO delete the undo files, and handle undo of merge sets
494 pp = chlog.parents(rev)
494 pp = chlog.parents(rev)
495 revnum = chlog.rev(rev)
495 revnum = chlog.rev(rev)
496
496
497 if update:
497 if update:
498 urev = self.qparents(repo, rev)
498 urev = self.qparents(repo, rev)
499 repo.update(urev, allow=False, force=True, wlock=wlock)
499 repo.update(urev, allow=False, force=True, wlock=wlock)
500 repo.dirstate.write()
500 repo.dirstate.write()
501
501
502 # save is a list of all the branches we are truncating away
502 # save is a list of all the branches we are truncating away
503 # that we actually want to keep. changegroup will be used
503 # that we actually want to keep. changegroup will be used
504 # to preserve them and add them back after the truncate
504 # to preserve them and add them back after the truncate
505 saveheads = []
505 saveheads = []
506 savebases = {}
506 savebases = {}
507
507
508 tip = chlog.tip()
508 tip = chlog.tip()
509 heads = limitheads(chlog, rev)
509 heads = limitheads(chlog, rev)
510 seen = {}
510 seen = {}
511
511
512 # search through all the heads, finding those where the revision
512 # search through all the heads, finding those where the revision
513 # we want to strip away is an ancestor. Also look for merges
513 # we want to strip away is an ancestor. Also look for merges
514 # that might be turned into new heads by the strip.
514 # that might be turned into new heads by the strip.
515 while heads:
515 while heads:
516 h = heads.pop()
516 h = heads.pop()
517 n = h
517 n = h
518 while True:
518 while True:
519 seen[n] = 1
519 seen[n] = 1
520 pp = chlog.parents(n)
520 pp = chlog.parents(n)
521 if pp[1] != revlog.nullid and chlog.rev(pp[1]) > revnum:
521 if pp[1] != revlog.nullid and chlog.rev(pp[1]) > revnum:
522 if pp[1] not in seen:
522 if pp[1] not in seen:
523 heads.append(pp[1])
523 heads.append(pp[1])
524 if pp[0] == revlog.nullid:
524 if pp[0] == revlog.nullid:
525 break
525 break
526 if chlog.rev(pp[0]) < revnum:
526 if chlog.rev(pp[0]) < revnum:
527 break
527 break
528 n = pp[0]
528 n = pp[0]
529 if n == rev:
529 if n == rev:
530 break
530 break
531 r = chlog.reachable(h, rev)
531 r = chlog.reachable(h, rev)
532 if rev not in r:
532 if rev not in r:
533 saveheads.append(h)
533 saveheads.append(h)
534 for x in r:
534 for x in r:
535 if chlog.rev(x) > revnum:
535 if chlog.rev(x) > revnum:
536 savebases[x] = 1
536 savebases[x] = 1
537
537
538 # create a changegroup for all the branches we need to keep
538 # create a changegroup for all the branches we need to keep
539 if backup is "all":
539 if backup is "all":
540 backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
540 backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
541 bundle(backupch)
541 bundle(backupch)
542 if saveheads:
542 if saveheads:
543 backupch = repo.changegroupsubset(savebases.keys(), saveheads, 'strip')
543 backupch = repo.changegroupsubset(savebases.keys(), saveheads, 'strip')
544 chgrpfile = bundle(backupch)
544 chgrpfile = bundle(backupch)
545
545
546 stripall(rev, revnum)
546 stripall(rev, revnum)
547
547
548 change = chlog.read(rev)
548 change = chlog.read(rev)
549 repo.manifest.strip(repo.manifest.rev(change[0]), revnum)
549 repo.manifest.strip(repo.manifest.rev(change[0]), revnum)
550 chlog.strip(revnum, revnum)
550 chlog.strip(revnum, revnum)
551 if saveheads:
551 if saveheads:
552 self.ui.status("adding branch\n")
552 self.ui.status("adding branch\n")
553 commands.unbundle(self.ui, repo, chgrpfile, update=False)
553 commands.unbundle(self.ui, repo, chgrpfile, update=False)
554 if backup is not "strip":
554 if backup is not "strip":
555 os.unlink(chgrpfile)
555 os.unlink(chgrpfile)
556
556
557 def isapplied(self, patch):
557 def isapplied(self, patch):
558 """returns (index, rev, patch)"""
558 """returns (index, rev, patch)"""
559 for i in xrange(len(self.applied)):
559 for i in xrange(len(self.applied)):
560 p = self.applied[i]
560 p = self.applied[i]
561 a = p.split(':')
561 a = p.split(':')
562 if a[1] == patch:
562 if a[1] == patch:
563 return (i, a[0], a[1])
563 return (i, a[0], a[1])
564 return None
564 return None
565
565
566 def lookup(self, patch):
566 def lookup(self, patch):
567 if patch == None:
567 if patch == None:
568 return None
568 return None
569 if patch in self.series:
569 if patch in self.series:
570 return patch
570 return patch
571 if not os.path.isfile(os.path.join(self.path, patch)):
571 if not os.path.isfile(os.path.join(self.path, patch)):
572 try:
572 try:
573 sno = int(patch)
573 sno = int(patch)
574 except(ValueError, OverflowError):
574 except(ValueError, OverflowError):
575 self.ui.warn("patch %s not in series\n" % patch)
575 self.ui.warn("patch %s not in series\n" % patch)
576 sys.exit(1)
576 sys.exit(1)
577 if sno >= len(self.series):
577 if sno >= len(self.series):
578 self.ui.warn("patch number %d is out of range\n" % sno)
578 self.ui.warn("patch number %d is out of range\n" % sno)
579 sys.exit(1)
579 sys.exit(1)
580 patch = self.series[sno]
580 patch = self.series[sno]
581 else:
581 else:
582 self.ui.warn("patch %s not in series\n" % patch)
582 self.ui.warn("patch %s not in series\n" % patch)
583 sys.exit(1)
583 sys.exit(1)
584 return patch
584 return patch
585
585
586 def push(self, repo, patch=None, force=False, list=False,
586 def push(self, repo, patch=None, force=False, list=False,
587 mergeq=None, wlock=None):
587 mergeq=None, wlock=None):
588 if not wlock:
588 if not wlock:
589 wlock = repo.wlock()
589 wlock = repo.wlock()
590 patch = self.lookup(patch)
590 patch = self.lookup(patch)
591 if patch and self.isapplied(patch):
591 if patch and self.isapplied(patch):
592 self.ui.warn("patch %s is already applied\n" % patch)
592 self.ui.warn("patch %s is already applied\n" % patch)
593 sys.exit(1)
593 sys.exit(1)
594 if self.series_end() == len(self.series):
594 if self.series_end() == len(self.series):
595 self.ui.warn("File series fully applied\n")
595 self.ui.warn("File series fully applied\n")
596 sys.exit(1)
596 sys.exit(1)
597 if not force:
597 if not force:
598 self.check_localchanges(repo)
598 self.check_localchanges(repo)
599
599
600 self.applied_dirty = 1;
600 self.applied_dirty = 1;
601 start = self.series_end()
601 start = self.series_end()
602 if start > 0:
602 if start > 0:
603 self.check_toppatch(repo)
603 self.check_toppatch(repo)
604 if not patch:
604 if not patch:
605 patch = self.series[start]
605 patch = self.series[start]
606 end = start + 1
606 end = start + 1
607 else:
607 else:
608 end = self.series.index(patch, start) + 1
608 end = self.series.index(patch, start) + 1
609 s = self.series[start:end]
609 s = self.series[start:end]
610 if mergeq:
610 if mergeq:
611 ret = self.mergepatch(repo, mergeq, s, wlock)
611 ret = self.mergepatch(repo, mergeq, s, wlock)
612 else:
612 else:
613 ret = self.apply(repo, s, list, wlock=wlock)
613 ret = self.apply(repo, s, list, wlock=wlock)
614 top = self.applied[-1].split(':')[1]
614 top = self.applied[-1].split(':')[1]
615 if ret[0]:
615 if ret[0]:
616 self.ui.write("Errors during apply, please fix and refresh %s\n" %
616 self.ui.write("Errors during apply, please fix and refresh %s\n" %
617 top)
617 top)
618 else:
618 else:
619 self.ui.write("Now at: %s\n" % top)
619 self.ui.write("Now at: %s\n" % top)
620 return ret[0]
620 return ret[0]
621
621
622 def pop(self, repo, patch=None, force=False, update=True, wlock=None):
622 def pop(self, repo, patch=None, force=False, update=True, wlock=None):
623 def getfile(f, rev):
623 def getfile(f, rev):
624 t = repo.file(f).read(rev)
624 t = repo.file(f).read(rev)
625 try:
625 try:
626 repo.wfile(f, "w").write(t)
626 repo.wfile(f, "w").write(t)
627 except IOError:
627 except IOError:
628 try:
628 try:
629 os.makedirs(os.path.dirname(repo.wjoin(f)))
629 os.makedirs(os.path.dirname(repo.wjoin(f)))
630 except OSError, err:
630 except OSError, err:
631 if err.errno != errno.EEXIST: raise
631 if err.errno != errno.EEXIST: raise
632 repo.wfile(f, "w").write(t)
632 repo.wfile(f, "w").write(t)
633
633
634 if not wlock:
634 if not wlock:
635 wlock = repo.wlock()
635 wlock = repo.wlock()
636 if patch:
636 if patch:
637 # index, rev, patch
637 # index, rev, patch
638 info = self.isapplied(patch)
638 info = self.isapplied(patch)
639 if not info:
639 if not info:
640 patch = self.lookup(patch)
640 patch = self.lookup(patch)
641 info = self.isapplied(patch)
641 info = self.isapplied(patch)
642 if not info:
642 if not info:
643 self.ui.warn("patch %s is not applied\n" % patch)
643 self.ui.warn("patch %s is not applied\n" % patch)
644 sys.exit(1)
644 sys.exit(1)
645 if len(self.applied) == 0:
645 if len(self.applied) == 0:
646 self.ui.warn("No patches applied\n")
646 self.ui.warn("No patches applied\n")
647 sys.exit(1)
647 sys.exit(1)
648
648
649 if not update:
649 if not update:
650 parents = repo.dirstate.parents()
650 parents = repo.dirstate.parents()
651 rr = [ revlog.bin(x.split(':')[0]) for x in self.applied ]
651 rr = [ revlog.bin(x.split(':')[0]) for x in self.applied ]
652 for p in parents:
652 for p in parents:
653 if p in rr:
653 if p in rr:
654 self.ui.warn("qpop: forcing dirstate update\n")
654 self.ui.warn("qpop: forcing dirstate update\n")
655 update = True
655 update = True
656
656
657 if not force and update:
657 if not force and update:
658 self.check_localchanges(repo)
658 self.check_localchanges(repo)
659
659
660 self.applied_dirty = 1;
660 self.applied_dirty = 1;
661 end = len(self.applied)
661 end = len(self.applied)
662 if not patch:
662 if not patch:
663 info = [len(self.applied) - 1] + self.applied[-1].split(':')
663 info = [len(self.applied) - 1] + self.applied[-1].split(':')
664 start = info[0]
664 start = info[0]
665 rev = revlog.bin(info[1])
665 rev = revlog.bin(info[1])
666
666
667 # we know there are no local changes, so we can make a simplified
667 # we know there are no local changes, so we can make a simplified
668 # form of hg.update.
668 # form of hg.update.
669 if update:
669 if update:
670 top = self.check_toppatch(repo)
670 top = self.check_toppatch(repo)
671 qp = self.qparents(repo, rev)
671 qp = self.qparents(repo, rev)
672 changes = repo.changelog.read(qp)
672 changes = repo.changelog.read(qp)
673 mf1 = repo.manifest.readflags(changes[0])
673 mf1 = repo.manifest.readflags(changes[0])
674 mmap = repo.manifest.read(changes[0])
674 mmap = repo.manifest.read(changes[0])
675 (c, a, r, d, u) = repo.changes(qp, top)
675 (c, a, r, d, u) = repo.changes(qp, top)
676 if d:
676 if d:
677 raise util.Abort("deletions found between repo revs")
677 raise util.Abort("deletions found between repo revs")
678 for f in c:
678 for f in c:
679 getfile(f, mmap[f])
679 getfile(f, mmap[f])
680 for f in r:
680 for f in r:
681 getfile(f, mmap[f])
681 getfile(f, mmap[f])
682 util.set_exec(repo.wjoin(f), mf1[f])
682 util.set_exec(repo.wjoin(f), mf1[f])
683 repo.dirstate.update(c + r, 'n')
683 repo.dirstate.update(c + r, 'n')
684 for f in a:
684 for f in a:
685 try: os.unlink(repo.wjoin(f))
685 try: os.unlink(repo.wjoin(f))
686 except: raise
686 except: raise
687 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
687 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
688 except: pass
688 except: pass
689 if a:
689 if a:
690 repo.dirstate.forget(a)
690 repo.dirstate.forget(a)
691 repo.dirstate.setparents(qp, revlog.nullid)
691 repo.dirstate.setparents(qp, revlog.nullid)
692 self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
692 self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
693 del self.applied[start:end]
693 del self.applied[start:end]
694 if len(self.applied):
694 if len(self.applied):
695 self.ui.write("Now at: %s\n" % self.applied[-1].split(':')[1])
695 self.ui.write("Now at: %s\n" % self.applied[-1].split(':')[1])
696 else:
696 else:
697 self.ui.write("Patch queue now empty\n")
697 self.ui.write("Patch queue now empty\n")
698
698
699 def diff(self, repo, files):
699 def diff(self, repo, files):
700 top = self.check_toppatch(repo)
700 top = self.check_toppatch(repo)
701 if not top:
701 if not top:
702 self.ui.write("No patches applied\n")
702 self.ui.write("No patches applied\n")
703 return
703 return
704 qp = self.qparents(repo, top)
704 qp = self.qparents(repo, top)
705 commands.dodiff(sys.stdout, self.ui, repo, qp, None, files)
705 commands.dodiff(sys.stdout, self.ui, repo, qp, None, files)
706
706
707 def refresh(self, repo, short=False):
707 def refresh(self, repo, short=False):
708 if len(self.applied) == 0:
708 if len(self.applied) == 0:
709 self.ui.write("No patches applied\n")
709 self.ui.write("No patches applied\n")
710 return
710 return
711 wlock = repo.wlock()
711 wlock = repo.wlock()
712 self.check_toppatch(repo)
712 self.check_toppatch(repo)
713 qp = self.qparents(repo)
713 qp = self.qparents(repo)
714 (top, patch) = self.applied[-1].split(':')
714 (top, patch) = self.applied[-1].split(':')
715 top = revlog.bin(top)
715 top = revlog.bin(top)
716 cparents = repo.changelog.parents(top)
716 cparents = repo.changelog.parents(top)
717 patchparent = self.qparents(repo, top)
717 patchparent = self.qparents(repo, top)
718 message, comments, user, patchfound = self.readheaders(patch)
718 message, comments, user, patchfound = self.readheaders(patch)
719
719
720 patchf = self.opener(patch, "w")
720 patchf = self.opener(patch, "w")
721 if comments:
721 if comments:
722 comments = "\n".join(comments) + '\n\n'
722 comments = "\n".join(comments) + '\n\n'
723 patchf.write(comments)
723 patchf.write(comments)
724
724
725 tip = repo.changelog.tip()
725 tip = repo.changelog.tip()
726 if top == tip:
726 if top == tip:
727 # if the top of our patch queue is also the tip, there is an
727 # if the top of our patch queue is also the tip, there is an
728 # optimization here. We update the dirstate in place and strip
728 # optimization here. We update the dirstate in place and strip
729 # off the tip commit. Then just commit the current directory
729 # off the tip commit. Then just commit the current directory
730 # tree. We can also send repo.commit the list of files
730 # tree. We can also send repo.commit the list of files
731 # changed to speed up the diff
731 # changed to speed up the diff
732 #
732 #
733 # in short mode, we only diff the files included in the
733 # in short mode, we only diff the files included in the
734 # patch already
734 # patch already
735 #
735 #
736 # this should really read:
736 # this should really read:
737 #(cc, dd, aa, aa2, uu) = repo.changes(tip, patchparent)
737 #(cc, dd, aa, aa2, uu) = repo.changes(tip, patchparent)
738 # but we do it backwards to take advantage of manifest/chlog
738 # but we do it backwards to take advantage of manifest/chlog
739 # caching against the next repo.changes call
739 # caching against the next repo.changes call
740 #
740 #
741 (cc, aa, dd, aa2, uu) = repo.changes(patchparent, tip)
741 (cc, aa, dd, aa2, uu) = repo.changes(patchparent, tip)
742 if short:
742 if short:
743 filelist = cc + aa + dd
743 filelist = cc + aa + dd
744 else:
744 else:
745 filelist = None
745 filelist = None
746 (c, a, r, d, u) = repo.changes(None, None, filelist)
746 (c, a, r, d, u) = repo.changes(None, None, filelist)
747
747
748 # we might end up with files that were added between tip and
748 # we might end up with files that were added between tip and
749 # the dirstate parent, but then changed in the local dirstate.
749 # the dirstate parent, but then changed in the local dirstate.
750 # in this case, we want them to only show up in the added section
750 # in this case, we want them to only show up in the added section
751 for x in c:
751 for x in c:
752 if x not in aa:
752 if x not in aa:
753 cc.append(x)
753 cc.append(x)
754 # we might end up with files added by the local dirstate that
754 # we might end up with files added by the local dirstate that
755 # were deleted by the patch. In this case, they should only
755 # were deleted by the patch. In this case, they should only
756 # show up in the changed section.
756 # show up in the changed section.
757 for x in a:
757 for x in a:
758 if x in dd:
758 if x in dd:
759 del dd[dd.index(x)]
759 del dd[dd.index(x)]
760 cc.append(x)
760 cc.append(x)
761 else:
761 else:
762 aa.append(x)
762 aa.append(x)
763 # make sure any files deleted in the local dirstate
763 # make sure any files deleted in the local dirstate
764 # are not in the add or change column of the patch
764 # are not in the add or change column of the patch
765 forget = []
765 forget = []
766 for x in d + r:
766 for x in d + r:
767 if x in aa:
767 if x in aa:
768 del aa[aa.index(x)]
768 del aa[aa.index(x)]
769 forget.append(x)
769 forget.append(x)
770 continue
770 continue
771 elif x in cc:
771 elif x in cc:
772 del cc[cc.index(x)]
772 del cc[cc.index(x)]
773 dd.append(x)
773 dd.append(x)
774
774
775 c = list(util.unique(cc))
775 c = list(util.unique(cc))
776 r = list(util.unique(dd))
776 r = list(util.unique(dd))
777 a = list(util.unique(aa))
777 a = list(util.unique(aa))
778 filelist = list(util.unique(c + r + a ))
778 filelist = list(util.unique(c + r + a ))
779 commands.dodiff(patchf, self.ui, repo, patchparent, None,
779 commands.dodiff(patchf, self.ui, repo, patchparent, None,
780 filelist, changes=(c, a, r, [], u))
780 filelist, changes=(c, a, r, [], u))
781 patchf.close()
781 patchf.close()
782
782
783 changes = repo.changelog.read(tip)
783 changes = repo.changelog.read(tip)
784 repo.dirstate.setparents(*cparents)
784 repo.dirstate.setparents(*cparents)
785 repo.dirstate.update(a, 'a')
785 repo.dirstate.update(a, 'a')
786 repo.dirstate.update(r, 'r')
786 repo.dirstate.update(r, 'r')
787 repo.dirstate.update(c, 'n')
787 repo.dirstate.update(c, 'n')
788 repo.dirstate.forget(forget)
788 repo.dirstate.forget(forget)
789
789
790 if not message:
790 if not message:
791 message = "patch queue: %s\n" % patch
791 message = "patch queue: %s\n" % patch
792 else:
792 else:
793 message = "\n".join(message)
793 message = "\n".join(message)
794 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
794 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
795 n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
795 n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
796 self.applied[-1] = revlog.hex(n) + ':' + patch
796 self.applied[-1] = revlog.hex(n) + ':' + patch
797 self.applied_dirty = 1
797 self.applied_dirty = 1
798 else:
798 else:
799 commands.dodiff(patchf, self.ui, repo, patchparent, None)
799 commands.dodiff(patchf, self.ui, repo, patchparent, None)
800 patchf.close()
800 patchf.close()
801 self.pop(repo, force=True, wlock=wlock)
801 self.pop(repo, force=True, wlock=wlock)
802 self.push(repo, force=True, wlock=wlock)
802 self.push(repo, force=True, wlock=wlock)
803
803
804 def init(self, repo, create=False):
804 def init(self, repo, create=False):
805 if os.path.isdir(self.path):
805 if os.path.isdir(self.path):
806 raise util.Abort("patch queue directory already exists")
806 raise util.Abort("patch queue directory already exists")
807 os.mkdir(self.path)
807 os.mkdir(self.path)
808 if create:
808 if create:
809 return self.qrepo(create=True)
809 return self.qrepo(create=True)
810
810
811 def unapplied(self, repo, patch=None):
811 def unapplied(self, repo, patch=None):
812 if patch and patch not in self.series:
812 if patch and patch not in self.series:
813 self.ui.warn("%s not in the series file\n" % patch)
813 self.ui.warn("%s not in the series file\n" % patch)
814 sys.exit(1)
814 sys.exit(1)
815 if not patch:
815 if not patch:
816 start = self.series_end()
816 start = self.series_end()
817 else:
817 else:
818 start = self.series.index(patch) + 1
818 start = self.series.index(patch) + 1
819 for p in self.series[start:]:
819 for p in self.series[start:]:
820 self.ui.write("%s\n" % p)
820 self.ui.write("%s\n" % p)
821
821
822 def qseries(self, repo, missing=None):
822 def qseries(self, repo, missing=None):
823 start = self.series_end()
823 start = self.series_end()
824 if not missing:
824 if not missing:
825 for p in self.series[:start]:
825 for p in self.series[:start]:
826 if self.ui.verbose:
826 if self.ui.verbose:
827 self.ui.write("%d A " % self.series.index(p))
827 self.ui.write("%d A " % self.series.index(p))
828 self.ui.write("%s\n" % p)
828 self.ui.write("%s\n" % p)
829 for p in self.series[start:]:
829 for p in self.series[start:]:
830 if self.ui.verbose:
830 if self.ui.verbose:
831 self.ui.write("%d U " % self.series.index(p))
831 self.ui.write("%d U " % self.series.index(p))
832 self.ui.write("%s\n" % p)
832 self.ui.write("%s\n" % p)
833 else:
833 else:
834 list = []
834 list = []
835 for root, dirs, files in os.walk(self.path):
835 for root, dirs, files in os.walk(self.path):
836 d = root[len(self.path) + 1:]
836 d = root[len(self.path) + 1:]
837 for f in files:
837 for f in files:
838 fl = os.path.join(d, f)
838 fl = os.path.join(d, f)
839 if (fl not in self.series and
839 if (fl not in self.series and
840 fl not in (self.status_path, self.series_path)
840 fl not in (self.status_path, self.series_path)
841 and not fl.startswith('.')):
841 and not fl.startswith('.')):
842 list.append(fl)
842 list.append(fl)
843 list.sort()
843 list.sort()
844 if list:
844 if list:
845 for x in list:
845 for x in list:
846 if self.ui.verbose:
846 if self.ui.verbose:
847 self.ui.write("D ")
847 self.ui.write("D ")
848 self.ui.write("%s\n" % x)
848 self.ui.write("%s\n" % x)
849
849
850 def issaveline(self, l):
850 def issaveline(self, l):
851 name = l.split(':')[1]
851 name = l.split(':')[1]
852 if name == '.hg.patches.save.line':
852 if name == '.hg.patches.save.line':
853 return True
853 return True
854
854
855 def qrepo(self, create=False):
855 def qrepo(self, create=False):
856 if create or os.path.isdir(os.path.join(self.path, ".hg")):
856 if create or os.path.isdir(os.path.join(self.path, ".hg")):
857 return hg.repository(self.ui, path=self.path, create=create)
857 return hg.repository(self.ui, path=self.path, create=create)
858
858
859 def restore(self, repo, rev, delete=None, qupdate=None):
859 def restore(self, repo, rev, delete=None, qupdate=None):
860 c = repo.changelog.read(rev)
860 c = repo.changelog.read(rev)
861 desc = c[4].strip()
861 desc = c[4].strip()
862 lines = desc.splitlines()
862 lines = desc.splitlines()
863 i = 0
863 i = 0
864 datastart = None
864 datastart = None
865 series = []
865 series = []
866 applied = []
866 applied = []
867 qpp = None
867 qpp = None
868 for i in xrange(0, len(lines)):
868 for i in xrange(0, len(lines)):
869 if lines[i] == 'Patch Data:':
869 if lines[i] == 'Patch Data:':
870 datastart = i + 1
870 datastart = i + 1
871 elif lines[i].startswith('Dirstate:'):
871 elif lines[i].startswith('Dirstate:'):
872 l = lines[i].rstrip()
872 l = lines[i].rstrip()
873 l = l[10:].split(' ')
873 l = l[10:].split(' ')
874 qpp = [ hg.bin(x) for x in l ]
874 qpp = [ hg.bin(x) for x in l ]
875 elif datastart != None:
875 elif datastart != None:
876 l = lines[i].rstrip()
876 l = lines[i].rstrip()
877 index = l.index(':')
877 index = l.index(':')
878 id = l[:index]
878 id = l[:index]
879 file = l[index + 1:]
879 file = l[index + 1:]
880 if id:
880 if id:
881 applied.append(l)
881 applied.append(l)
882 series.append(file)
882 series.append(file)
883 if datastart == None:
883 if datastart == None:
884 self.ui.warn("No saved patch data found\n")
884 self.ui.warn("No saved patch data found\n")
885 return 1
885 return 1
886 self.ui.warn("restoring status: %s\n" % lines[0])
886 self.ui.warn("restoring status: %s\n" % lines[0])
887 self.full_series = series
887 self.full_series = series
888 self.applied = applied
888 self.applied = applied
889 self.read_series(self.full_series)
889 self.read_series(self.full_series)
890 self.series_dirty = 1
890 self.series_dirty = 1
891 self.applied_dirty = 1
891 self.applied_dirty = 1
892 heads = repo.changelog.heads()
892 heads = repo.changelog.heads()
893 if delete:
893 if delete:
894 if rev not in heads:
894 if rev not in heads:
895 self.ui.warn("save entry has children, leaving it alone\n")
895 self.ui.warn("save entry has children, leaving it alone\n")
896 else:
896 else:
897 self.ui.warn("removing save entry %s\n" % hg.short(rev))
897 self.ui.warn("removing save entry %s\n" % hg.short(rev))
898 pp = repo.dirstate.parents()
898 pp = repo.dirstate.parents()
899 if rev in pp:
899 if rev in pp:
900 update = True
900 update = True
901 else:
901 else:
902 update = False
902 update = False
903 self.strip(repo, rev, update=update, backup='strip')
903 self.strip(repo, rev, update=update, backup='strip')
904 if qpp:
904 if qpp:
905 self.ui.warn("saved queue repository parents: %s %s\n" %
905 self.ui.warn("saved queue repository parents: %s %s\n" %
906 (hg.short(qpp[0]), hg.short(qpp[1])))
906 (hg.short(qpp[0]), hg.short(qpp[1])))
907 if qupdate:
907 if qupdate:
908 print "queue directory updating"
908 print "queue directory updating"
909 r = self.qrepo()
909 r = self.qrepo()
910 if not r:
910 if not r:
911 self.ui.warn("Unable to load queue repository\n")
911 self.ui.warn("Unable to load queue repository\n")
912 return 1
912 return 1
913 r.update(qpp[0], allow=False, force=True)
913 r.update(qpp[0], allow=False, force=True)
914
914
915 def save(self, repo, msg=None):
915 def save(self, repo, msg=None):
916 if len(self.applied) == 0:
916 if len(self.applied) == 0:
917 self.ui.warn("save: no patches applied, exiting\n")
917 self.ui.warn("save: no patches applied, exiting\n")
918 return 1
918 return 1
919 if self.issaveline(self.applied[-1]):
919 if self.issaveline(self.applied[-1]):
920 self.ui.warn("status is already saved\n")
920 self.ui.warn("status is already saved\n")
921 return 1
921 return 1
922
922
923 ar = [ ':' + x for x in self.full_series ]
923 ar = [ ':' + x for x in self.full_series ]
924 if not msg:
924 if not msg:
925 msg = "hg patches saved state"
925 msg = "hg patches saved state"
926 else:
926 else:
927 msg = "hg patches: " + msg.rstrip('\r\n')
927 msg = "hg patches: " + msg.rstrip('\r\n')
928 r = self.qrepo()
928 r = self.qrepo()
929 if r:
929 if r:
930 pp = r.dirstate.parents()
930 pp = r.dirstate.parents()
931 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
931 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
932 msg += "\n\nPatch Data:\n"
932 msg += "\n\nPatch Data:\n"
933 text = msg + "\n".join(self.applied) + '\n' + (ar and "\n".join(ar)
933 text = msg + "\n".join(self.applied) + '\n' + (ar and "\n".join(ar)
934 + '\n' or "")
934 + '\n' or "")
935 n = repo.commit(None, text, user=None, force=1)
935 n = repo.commit(None, text, user=None, force=1)
936 if not n:
936 if not n:
937 self.ui.warn("repo commit failed\n")
937 self.ui.warn("repo commit failed\n")
938 return 1
938 return 1
939 self.applied.append(revlog.hex(n) + ":" + '.hg.patches.save.line')
939 self.applied.append(revlog.hex(n) + ":" + '.hg.patches.save.line')
940 self.applied_dirty = 1
940 self.applied_dirty = 1
941
941
942 def series_end(self):
942 def series_end(self):
943 end = 0
943 end = 0
944 if len(self.applied) > 0:
944 if len(self.applied) > 0:
945 (top, p) = self.applied[-1].split(':')
945 (top, p) = self.applied[-1].split(':')
946 try:
946 try:
947 end = self.series.index(p)
947 end = self.series.index(p)
948 except ValueError:
948 except ValueError:
949 return 0
949 return 0
950 return end + 1
950 return end + 1
951 return end
951 return end
952
952
953 def qapplied(self, repo, patch=None):
953 def qapplied(self, repo, patch=None):
954 if patch and patch not in self.series:
954 if patch and patch not in self.series:
955 self.ui.warn("%s not in the series file\n" % patch)
955 self.ui.warn("%s not in the series file\n" % patch)
956 sys.exit(1)
956 sys.exit(1)
957 if not patch:
957 if not patch:
958 end = len(self.applied)
958 end = len(self.applied)
959 else:
959 else:
960 end = self.series.index(patch) + 1
960 end = self.series.index(patch) + 1
961 for x in xrange(end):
961 for x in xrange(end):
962 p = self.appliedname(x)
962 p = self.appliedname(x)
963 self.ui.write("%s\n" % p)
963 self.ui.write("%s\n" % p)
964
964
965 def appliedname(self, index):
965 def appliedname(self, index):
966 p = self.applied[index]
966 p = self.applied[index]
967 if not self.ui.verbose:
967 if not self.ui.verbose:
968 p = p.split(':')[1]
968 p = p.split(':')[1]
969 return p
969 return p
970
970
971 def top(self, repo):
971 def top(self, repo):
972 if len(self.applied):
972 if len(self.applied):
973 p = self.appliedname(-1)
973 p = self.appliedname(-1)
974 self.ui.write(p + '\n')
974 self.ui.write(p + '\n')
975 else:
975 else:
976 self.ui.write("No patches applied\n")
976 self.ui.write("No patches applied\n")
977
977
978 def next(self, repo):
978 def next(self, repo):
979 end = self.series_end()
979 end = self.series_end()
980 if end == len(self.series):
980 if end == len(self.series):
981 self.ui.write("All patches applied\n")
981 self.ui.write("All patches applied\n")
982 else:
982 else:
983 self.ui.write(self.series[end] + '\n')
983 self.ui.write(self.series[end] + '\n')
984
984
985 def prev(self, repo):
985 def prev(self, repo):
986 if len(self.applied) > 1:
986 if len(self.applied) > 1:
987 p = self.appliedname(-2)
987 p = self.appliedname(-2)
988 self.ui.write(p + '\n')
988 self.ui.write(p + '\n')
989 elif len(self.applied) == 1:
989 elif len(self.applied) == 1:
990 self.ui.write("Only one patch applied\n")
990 self.ui.write("Only one patch applied\n")
991 else:
991 else:
992 self.ui.write("No patches applied\n")
992 self.ui.write("No patches applied\n")
993
993
994 def qimport(self, repo, files, patch=None, existing=None, force=None):
994 def qimport(self, repo, files, patch=None, existing=None, force=None):
995 if len(files) > 1 and patch:
995 if len(files) > 1 and patch:
996 self.ui.warn("-n option not valid when importing multiple files\n")
996 self.ui.warn("-n option not valid when importing multiple files\n")
997 sys.exit(1)
997 sys.exit(1)
998 i = 0
998 i = 0
999 for filename in files:
999 for filename in files:
1000 if existing:
1000 if existing:
1001 if not patch:
1001 if not patch:
1002 patch = filename
1002 patch = filename
1003 if not os.path.isfile(os.path.join(self.path, patch)):
1003 if not os.path.isfile(os.path.join(self.path, patch)):
1004 self.ui.warn("patch %s does not exist\n" % patch)
1004 self.ui.warn("patch %s does not exist\n" % patch)
1005 sys.exit(1)
1005 sys.exit(1)
1006 else:
1006 else:
1007 try:
1007 try:
1008 text = file(filename).read()
1008 text = file(filename).read()
1009 except IOError:
1009 except IOError:
1010 self.ui.warn("Unable to read %s\n" % patch)
1010 self.ui.warn("Unable to read %s\n" % patch)
1011 sys.exit(1)
1011 sys.exit(1)
1012 if not patch:
1012 if not patch:
1013 patch = os.path.split(filename)[1]
1013 patch = os.path.split(filename)[1]
1014 if not force and os.path.isfile(os.path.join(self.path, patch)):
1014 if not force and os.path.isfile(os.path.join(self.path, patch)):
1015 self.ui.warn("patch %s already exists\n" % patch)
1015 self.ui.warn("patch %s already exists\n" % patch)
1016 sys.exit(1)
1016 sys.exit(1)
1017 patchf = self.opener(patch, "w")
1017 patchf = self.opener(patch, "w")
1018 patchf.write(text)
1018 patchf.write(text)
1019 if patch in self.series:
1019 if patch in self.series:
1020 self.ui.warn("patch %s is already in the series file\n" % patch)
1020 self.ui.warn("patch %s is already in the series file\n" % patch)
1021 sys.exit(1)
1021 sys.exit(1)
1022 index = self.series_end() + i
1022 index = self.series_end() + i
1023 self.full_series[index:index] = [patch]
1023 self.full_series[index:index] = [patch]
1024 self.read_series(self.full_series)
1024 self.read_series(self.full_series)
1025 self.ui.warn("adding %s to series file\n" % patch)
1025 self.ui.warn("adding %s to series file\n" % patch)
1026 i += 1
1026 i += 1
1027 patch = None
1027 patch = None
1028 self.series_dirty = 1
1028 self.series_dirty = 1
1029
1029
1030 def delete(ui, repo, patch, **opts):
1030 def delete(ui, repo, patch, **opts):
1031 """remove a patch from the series file"""
1031 """remove a patch from the series file"""
1032 q = repomap[repo]
1032 q = repomap[repo]
1033 q.delete(repo, patch)
1033 q.delete(repo, patch)
1034 q.save_dirty()
1034 q.save_dirty()
1035 return 0
1035 return 0
1036
1036
1037 def applied(ui, repo, patch=None, **opts):
1037 def applied(ui, repo, patch=None, **opts):
1038 """print the patches already applied"""
1038 """print the patches already applied"""
1039 repomap[repo].qapplied(repo, patch)
1039 repomap[repo].qapplied(repo, patch)
1040 return 0
1040 return 0
1041
1041
1042 def unapplied(ui, repo, patch=None, **opts):
1042 def unapplied(ui, repo, patch=None, **opts):
1043 """print the patches not yet applied"""
1043 """print the patches not yet applied"""
1044 repomap[repo].unapplied(repo, patch)
1044 repomap[repo].unapplied(repo, patch)
1045 return 0
1045 return 0
1046
1046
1047 def qimport(ui, repo, *filename, **opts):
1047 def qimport(ui, repo, *filename, **opts):
1048 """import a patch"""
1048 """import a patch"""
1049 q = repomap[repo]
1049 q = repomap[repo]
1050 q.qimport(repo, filename, patch=opts['name'],
1050 q.qimport(repo, filename, patch=opts['name'],
1051 existing=opts['existing'], force=opts['force'])
1051 existing=opts['existing'], force=opts['force'])
1052 q.save_dirty()
1052 q.save_dirty()
1053 return 0
1053 return 0
1054
1054
1055 def init(ui, repo, **opts):
1055 def init(ui, repo, **opts):
1056 """init a new queue repository"""
1056 """init a new queue repository"""
1057 q = repomap[repo]
1057 q = repomap[repo]
1058 r = q.init(repo, create=opts['create_repo'])
1058 r = q.init(repo, create=opts['create_repo'])
1059 q.save_dirty()
1059 q.save_dirty()
1060 if r:
1060 if r:
1061 fp = r.wopener('.hgignore', 'w')
1061 fp = r.wopener('.hgignore', 'w')
1062 print >> fp, 'syntax: glob'
1062 print >> fp, 'syntax: glob'
1063 print >> fp, 'status'
1063 print >> fp, 'status'
1064 fp.close()
1064 fp.close()
1065 r.wopener('series', 'w').close()
1065 r.wopener('series', 'w').close()
1066 r.add(['.hgignore', 'series'])
1066 r.add(['.hgignore', 'series'])
1067 return 0
1067 return 0
1068
1068
1069 def commit(ui, repo, *pats, **opts):
1069 def commit(ui, repo, *pats, **opts):
1070 q = repomap[repo]
1070 q = repomap[repo]
1071 r = q.qrepo()
1071 r = q.qrepo()
1072 if not r: raise util.Abort('no queue repository')
1072 if not r: raise util.Abort('no queue repository')
1073 commands.commit(r.ui, r, *pats, **opts)
1073 commands.commit(r.ui, r, *pats, **opts)
1074
1074
1075 def series(ui, repo, **opts):
1075 def series(ui, repo, **opts):
1076 """print the entire series file"""
1076 """print the entire series file"""
1077 repomap[repo].qseries(repo, missing=opts['missing'])
1077 repomap[repo].qseries(repo, missing=opts['missing'])
1078 return 0
1078 return 0
1079
1079
1080 def top(ui, repo, **opts):
1080 def top(ui, repo, **opts):
1081 """print the name of the current patch"""
1081 """print the name of the current patch"""
1082 repomap[repo].top(repo)
1082 repomap[repo].top(repo)
1083 return 0
1083 return 0
1084
1084
1085 def next(ui, repo, **opts):
1085 def next(ui, repo, **opts):
1086 """print the name of the next patch"""
1086 """print the name of the next patch"""
1087 repomap[repo].next(repo)
1087 repomap[repo].next(repo)
1088 return 0
1088 return 0
1089
1089
1090 def prev(ui, repo, **opts):
1090 def prev(ui, repo, **opts):
1091 """print the name of the previous patch"""
1091 """print the name of the previous patch"""
1092 repomap[repo].prev(repo)
1092 repomap[repo].prev(repo)
1093 return 0
1093 return 0
1094
1094
1095 def new(ui, repo, patch, **opts):
1095 def new(ui, repo, patch, **opts):
1096 """create a new patch"""
1096 """create a new patch"""
1097 q = repomap[repo]
1097 q = repomap[repo]
1098 q.new(repo, patch, msg=opts['message'], force=opts['force'])
1098 q.new(repo, patch, msg=opts['message'], force=opts['force'])
1099 q.save_dirty()
1099 q.save_dirty()
1100 return 0
1100 return 0
1101
1101
1102 def refresh(ui, repo, **opts):
1102 def refresh(ui, repo, **opts):
1103 """update the current patch"""
1103 """update the current patch"""
1104 q = repomap[repo]
1104 q = repomap[repo]
1105 q.refresh(repo, short=opts['short'])
1105 q.refresh(repo, short=opts['short'])
1106 q.save_dirty()
1106 q.save_dirty()
1107 return 0
1107 return 0
1108
1108
1109 def diff(ui, repo, *files, **opts):
1109 def diff(ui, repo, *files, **opts):
1110 """diff of the current patch"""
1110 """diff of the current patch"""
1111 # deep in the dirstate code, the walkhelper method wants a list, not a tuple
1111 # deep in the dirstate code, the walkhelper method wants a list, not a tuple
1112 repomap[repo].diff(repo, list(files))
1112 repomap[repo].diff(repo, list(files))
1113 return 0
1113 return 0
1114
1114
1115 def lastsavename(path):
1115 def lastsavename(path):
1116 (dir, base) = os.path.split(path)
1116 (dir, base) = os.path.split(path)
1117 names = os.listdir(dir)
1117 names = os.listdir(dir)
1118 namere = re.compile("%s.([0-9]+)" % base)
1118 namere = re.compile("%s.([0-9]+)" % base)
1119 max = None
1119 max = None
1120 maxname = None
1120 maxname = None
1121 for f in names:
1121 for f in names:
1122 m = namere.match(f)
1122 m = namere.match(f)
1123 if m:
1123 if m:
1124 index = int(m.group(1))
1124 index = int(m.group(1))
1125 if max == None or index > max:
1125 if max == None or index > max:
1126 max = index
1126 max = index
1127 maxname = f
1127 maxname = f
1128 if maxname:
1128 if maxname:
1129 return (os.path.join(dir, maxname), max)
1129 return (os.path.join(dir, maxname), max)
1130 return (None, None)
1130 return (None, None)
1131
1131
1132 def savename(path):
1132 def savename(path):
1133 (last, index) = lastsavename(path)
1133 (last, index) = lastsavename(path)
1134 if last is None:
1134 if last is None:
1135 index = 0
1135 index = 0
1136 newpath = path + ".%d" % (index + 1)
1136 newpath = path + ".%d" % (index + 1)
1137 return newpath
1137 return newpath
1138
1138
1139 def push(ui, repo, patch=None, **opts):
1139 def push(ui, repo, patch=None, **opts):
1140 """push the next patch onto the stack"""
1140 """push the next patch onto the stack"""
1141 q = repomap[repo]
1141 q = repomap[repo]
1142 mergeq = None
1142 mergeq = None
1143
1143
1144 if opts['all']:
1144 if opts['all']:
1145 patch = q.series[-1]
1145 patch = q.series[-1]
1146 if opts['merge']:
1146 if opts['merge']:
1147 if opts['name']:
1147 if opts['name']:
1148 newpath = opts['name']
1148 newpath = opts['name']
1149 else:
1149 else:
1150 newpath, i = lastsavename(q.path)
1150 newpath, i = lastsavename(q.path)
1151 if not newpath:
1151 if not newpath:
1152 ui.warn("no saved queues found, please use -n\n")
1152 ui.warn("no saved queues found, please use -n\n")
1153 return 1
1153 return 1
1154 mergeq = queue(ui, repo.join(""), newpath)
1154 mergeq = queue(ui, repo.join(""), newpath)
1155 ui.warn("merging with queue at: %s\n" % mergeq.path)
1155 ui.warn("merging with queue at: %s\n" % mergeq.path)
1156 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1156 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1157 mergeq=mergeq)
1157 mergeq=mergeq)
1158 q.save_dirty()
1158 q.save_dirty()
1159 return ret
1159 return ret
1160
1160
1161 def pop(ui, repo, patch=None, **opts):
1161 def pop(ui, repo, patch=None, **opts):
1162 """pop the current patch off the stack"""
1162 """pop the current patch off the stack"""
1163 localupdate = True
1163 localupdate = True
1164 if opts['name']:
1164 if opts['name']:
1165 q = queue(ui, repo.join(""), repo.join(opts['name']))
1165 q = queue(ui, repo.join(""), repo.join(opts['name']))
1166 ui.warn('using patch queue: %s\n' % q.path)
1166 ui.warn('using patch queue: %s\n' % q.path)
1167 localupdate = False
1167 localupdate = False
1168 else:
1168 else:
1169 q = repomap[repo]
1169 q = repomap[repo]
1170 if opts['all'] and len(q.applied) > 0:
1170 if opts['all'] and len(q.applied) > 0:
1171 patch = q.applied[0].split(':')[1]
1171 patch = q.applied[0].split(':')[1]
1172 q.pop(repo, patch, force=opts['force'], update=localupdate)
1172 q.pop(repo, patch, force=opts['force'], update=localupdate)
1173 q.save_dirty()
1173 q.save_dirty()
1174 return 0
1174 return 0
1175
1175
1176 def restore(ui, repo, rev, **opts):
1176 def restore(ui, repo, rev, **opts):
1177 """restore the queue state saved by a rev"""
1177 """restore the queue state saved by a rev"""
1178 rev = repo.lookup(rev)
1178 rev = repo.lookup(rev)
1179 q = repomap[repo]
1179 q = repomap[repo]
1180 q.restore(repo, rev, delete=opts['delete'],
1180 q.restore(repo, rev, delete=opts['delete'],
1181 qupdate=opts['update'])
1181 qupdate=opts['update'])
1182 q.save_dirty()
1182 q.save_dirty()
1183 return 0
1183 return 0
1184
1184
1185 def save(ui, repo, **opts):
1185 def save(ui, repo, **opts):
1186 """save current queue state"""
1186 """save current queue state"""
1187 q = repomap[repo]
1187 q = repomap[repo]
1188 ret = q.save(repo, msg=opts['message'])
1188 ret = q.save(repo, msg=opts['message'])
1189 if ret:
1189 if ret:
1190 return ret
1190 return ret
1191 q.save_dirty()
1191 q.save_dirty()
1192 if opts['copy']:
1192 if opts['copy']:
1193 path = q.path
1193 path = q.path
1194 if opts['name']:
1194 if opts['name']:
1195 newpath = os.path.join(q.basepath, opts['name'])
1195 newpath = os.path.join(q.basepath, opts['name'])
1196 if os.path.exists(newpath):
1196 if os.path.exists(newpath):
1197 if not os.path.isdir(newpath):
1197 if not os.path.isdir(newpath):
1198 ui.warn("destination %s exists and is not a directory\n" %
1198 ui.warn("destination %s exists and is not a directory\n" %
1199 newpath)
1199 newpath)
1200 sys.exit(1)
1200 sys.exit(1)
1201 if not opts['force']:
1201 if not opts['force']:
1202 ui.warn("destination %s exists, use -f to force\n" %
1202 ui.warn("destination %s exists, use -f to force\n" %
1203 newpath)
1203 newpath)
1204 sys.exit(1)
1204 sys.exit(1)
1205 else:
1205 else:
1206 newpath = savename(path)
1206 newpath = savename(path)
1207 ui.warn("copy %s to %s\n" % (path, newpath))
1207 ui.warn("copy %s to %s\n" % (path, newpath))
1208 util.copyfiles(path, newpath)
1208 util.copyfiles(path, newpath)
1209 if opts['empty']:
1209 if opts['empty']:
1210 try:
1210 try:
1211 os.unlink(os.path.join(q.path, q.status_path))
1211 os.unlink(os.path.join(q.path, q.status_path))
1212 except:
1212 except:
1213 pass
1213 pass
1214 return 0
1214 return 0
1215
1215
1216 def strip(ui, repo, rev, **opts):
1216 def strip(ui, repo, rev, **opts):
1217 """strip a revision and all later revs on the same branch"""
1217 """strip a revision and all later revs on the same branch"""
1218 rev = repo.lookup(rev)
1218 rev = repo.lookup(rev)
1219 backup = 'all'
1219 backup = 'all'
1220 if opts['backup']:
1220 if opts['backup']:
1221 backup = 'strip'
1221 backup = 'strip'
1222 elif opts['nobackup']:
1222 elif opts['nobackup']:
1223 backup = 'none'
1223 backup = 'none'
1224 repomap[repo].strip(repo, rev, backup=backup)
1224 repomap[repo].strip(repo, rev, backup=backup)
1225 return 0
1225 return 0
1226
1226
1227 def version(ui, q=None):
1227 def version(ui, q=None):
1228 """print the version number"""
1228 """print the version number"""
1229 ui.write("mq version %s\n" % versionstr)
1229 ui.write("mq version %s\n" % versionstr)
1230 return 0
1230 return 0
1231
1231
1232 def reposetup(ui, repo):
1232 def reposetup(ui, repo):
1233 repomap[repo] = queue(ui, repo.join(""))
1233 repomap[repo] = queue(ui, repo.join(""))
1234
1234
1235 cmdtable = {
1235 cmdtable = {
1236 "qapplied": (applied, [], 'hg qapplied [patch]'),
1236 "qapplied": (applied, [], 'hg qapplied [PATCH]'),
1237 "qcommit|qci":
1237 "qcommit|qci":
1238 (commit,
1238 (commit,
1239 [('A', 'addremove', None, _('run addremove during commit')),
1239 commands.table["^commit|ci"][1],
1240 ('I', 'include', [], _('include names matching the given patterns')),
1240 'hg qcommit [OPTION]... [FILE]...'),
1241 ('X', 'exclude', [], _('exclude names matching the given patterns')),
1241 "^qdiff": (diff, [], 'hg qdiff [FILE]...'),
1242 ('m', 'message', '', _('use <text> as commit message')),
1242 "qdelete": (delete, [], 'hg qdelete PATCH'),
1243 ('l', 'logfile', '', _('read the commit message from <file>')),
1244 ('d', 'date', '', _('record datecode as commit date')),
1245 ('u', 'user', '', _('record user as commiter'))],
1246 'hg qcommit [options] [files]'),
1247 "^qdiff": (diff, [], 'hg qdiff [files]'),
1248 "qdelete": (delete, [], 'hg qdelete [patch]'),
1249 "^qimport":
1243 "^qimport":
1250 (qimport,
1244 (qimport,
1251 [('e', 'existing', None, 'import file in patch dir'),
1245 [('e', 'existing', None, 'import file in patch dir'),
1252 ('n', 'name', '', 'patch file name'),
1246 ('n', 'name', '', 'patch file name'),
1253 ('f', 'force', None, 'overwrite existing files')],
1247 ('f', 'force', None, 'overwrite existing files')],
1254 'hg qimport'),
1248 'hg qimport [-e] [-n NAME] [-f] FILE...'),
1255 "^qinit":
1249 "^qinit":
1256 (init,
1250 (init,
1257 [('c', 'create-repo', None, 'create patch repository')],
1251 [('c', 'create-repo', None, 'create patch repository')],
1258 'hg [-c] qinit'),
1252 'hg qinit [-c]'),
1259 "qnew":
1253 "qnew":
1260 (new,
1254 (new,
1261 [('m', 'message', '', 'commit message'),
1255 [('m', 'message', '', 'commit message'),
1262 ('f', 'force', None, 'force')],
1256 ('f', 'force', None, 'force')],
1263 'hg qnew [-m message ] patch'),
1257 'hg qnew [-m TEXT] [-f] PATCH'),
1264 "qnext": (next, [], 'hg qnext'),
1258 "qnext": (next, [], 'hg qnext'),
1265 "qprev": (prev, [], 'hg qprev'),
1259 "qprev": (prev, [], 'hg qprev'),
1266 "^qpop":
1260 "^qpop":
1267 (pop,
1261 (pop,
1268 [('a', 'all', None, 'pop all patches'),
1262 [('a', 'all', None, 'pop all patches'),
1269 ('n', 'name', '', 'queue name to pop'),
1263 ('n', 'name', '', 'queue name to pop'),
1270 ('f', 'force', None, 'forget any local changes')],
1264 ('f', 'force', None, 'forget any local changes')],
1271 'hg qpop [options] [patch/index]'),
1265 'hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]'),
1272 "^qpush":
1266 "^qpush":
1273 (push,
1267 (push,
1274 [('f', 'force', None, 'apply if the patch has rejects'),
1268 [('f', 'force', None, 'apply if the patch has rejects'),
1275 ('l', 'list', None, 'list patch name in commit text'),
1269 ('l', 'list', None, 'list patch name in commit text'),
1276 ('a', 'all', None, 'apply all patches'),
1270 ('a', 'all', None, 'apply all patches'),
1277 ('m', 'merge', None, 'merge from another queue'),
1271 ('m', 'merge', None, 'merge from another queue'),
1278 ('n', 'name', '', 'merge queue name')],
1272 ('n', 'name', '', 'merge queue name')],
1279 'hg qpush [options] [patch/index]'),
1273 'hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]'),
1280 "^qrefresh":
1274 "^qrefresh":
1281 (refresh,
1275 (refresh,
1282 [('s', 'short', None, 'short refresh')],
1276 [('s', 'short', None, 'short refresh')],
1283 'hg qrefresh'),
1277 'hg qrefresh [-s]'),
1284 "qrestore":
1278 "qrestore":
1285 (restore,
1279 (restore,
1286 [('d', 'delete', None, 'delete save entry'),
1280 [('d', 'delete', None, 'delete save entry'),
1287 ('u', 'update', None, 'update queue working dir')],
1281 ('u', 'update', None, 'update queue working dir')],
1288 'hg qrestore rev'),
1282 'hg qrestore [-d] [-u] REV'),
1289 "qsave":
1283 "qsave":
1290 (save,
1284 (save,
1291 [('m', 'message', '', 'commit message'),
1285 [('m', 'message', '', 'commit message'),
1292 ('c', 'copy', None, 'copy patch directory'),
1286 ('c', 'copy', None, 'copy patch directory'),
1293 ('n', 'name', '', 'copy directory name'),
1287 ('n', 'name', '', 'copy directory name'),
1294 ('e', 'empty', None, 'clear queue status file'),
1288 ('e', 'empty', None, 'clear queue status file'),
1295 ('f', 'force', None, 'force copy')],
1289 ('f', 'force', None, 'force copy')],
1296 'hg qsave'),
1290 'hg qsave [-m TEXT] [-c] [-n NAME] [-e] [-f]'),
1297 "qseries":
1291 "qseries":
1298 (series,
1292 (series,
1299 [('m', 'missing', None, 'print patches not in series')],
1293 [('m', 'missing', None, 'print patches not in series')],
1300 'hg qseries'),
1294 'hg qseries [-m]'),
1301 "^strip":
1295 "^strip":
1302 (strip,
1296 (strip,
1303 [('f', 'force', None, 'force multi-head removal'),
1297 [('f', 'force', None, 'force multi-head removal'),
1304 ('b', 'backup', None, 'bundle unrelated changesets'),
1298 ('b', 'backup', None, 'bundle unrelated changesets'),
1305 ('n', 'nobackup', None, 'no backups')],
1299 ('n', 'nobackup', None, 'no backups')],
1306 'hg strip rev'),
1300 'hg strip [-f] [-b] [-n] REV'),
1307 "qtop": (top, [], 'hg qtop'),
1301 "qtop": (top, [], 'hg qtop'),
1308 "qunapplied": (unapplied, [], 'hg qunapplied [patch]'),
1302 "qunapplied": (unapplied, [], 'hg qunapplied [PATCH]'),
1309 "qversion": (version, [], 'hg qversion')
1303 "qversion": (version, [], 'hg qversion')
1310 }
1304 }
1311
1305
General Comments 0
You need to be logged in to leave comments. Login now