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