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