Mercurial > hg > mercurial-crew-with-dirclash
comparison hgext/mq.py @ 2830:49988d9f0758
Merge with crew, fix most tests
author | Matt Mackall <mpm@selenic.com> |
---|---|
date | Wed, 09 Aug 2006 13:55:18 -0500 |
parents | 30f59f4a327e 05316bb57d01 |
children | 582cbc4392cb 046a8b03ea59 |
comparison
equal
deleted
inserted
replaced
2829:4870f795f681 | 2830:49988d9f0758 |
---|---|
33 from mercurial.demandload import * | 33 from mercurial.demandload import * |
34 demandload(globals(), "os sys re struct traceback errno bz2") | 34 demandload(globals(), "os sys re struct traceback errno bz2") |
35 from mercurial.i18n import gettext as _ | 35 from mercurial.i18n import gettext as _ |
36 from mercurial import ui, hg, revlog, commands, util | 36 from mercurial import ui, hg, revlog, commands, util |
37 | 37 |
38 versionstr = "0.45" | |
39 | |
40 commands.norepo += " qclone qversion" | 38 commands.norepo += " qclone qversion" |
41 | 39 |
42 class StatusEntry: | 40 class statusentry: |
43 def __init__(self, rev, name=None): | 41 def __init__(self, rev, name=None): |
44 if not name: | 42 if not name: |
45 self.rev, self.name = rev.split(':') | 43 fields = rev.split(':') |
44 if len(fields) == 2: | |
45 self.rev, self.name = fields | |
46 else: | |
47 self.rev, self.name = None, None | |
46 else: | 48 else: |
47 self.rev, self.name = rev, name | 49 self.rev, self.name = rev, name |
48 | 50 |
49 def __str__(self): | 51 def __str__(self): |
50 return self.rev + ':' + self.name | 52 return self.rev + ':' + self.name |
51 | 53 |
52 class queue: | 54 class queue: |
53 def __init__(self, ui, path, patchdir=None): | 55 def __init__(self, ui, path, patchdir=None): |
54 self.basepath = path | 56 self.basepath = path |
55 if patchdir: | 57 self.path = patchdir or os.path.join(path, "patches") |
56 self.path = patchdir | |
57 else: | |
58 self.path = os.path.join(path, "patches") | |
59 self.opener = util.opener(self.path) | 58 self.opener = util.opener(self.path) |
60 self.ui = ui | 59 self.ui = ui |
61 self.applied = [] | 60 self.applied = [] |
62 self.full_series = [] | 61 self.full_series = [] |
63 self.applied_dirty = 0 | 62 self.applied_dirty = 0 |
64 self.series_dirty = 0 | 63 self.series_dirty = 0 |
65 self.series_path = "series" | 64 self.series_path = "series" |
66 self.status_path = "status" | 65 self.status_path = "status" |
67 | 66 self.guards_path = "guards" |
68 if os.path.exists(os.path.join(self.path, self.series_path)): | 67 self.active_guards = None |
68 self.guards_dirty = False | |
69 | |
70 if os.path.exists(self.join(self.series_path)): | |
69 self.full_series = self.opener(self.series_path).read().splitlines() | 71 self.full_series = self.opener(self.series_path).read().splitlines() |
70 self.parse_series() | 72 self.parse_series() |
71 | 73 |
72 if os.path.exists(os.path.join(self.path, self.status_path)): | 74 if os.path.exists(self.join(self.status_path)): |
73 self.applied = [StatusEntry(l) | 75 lines = self.opener(self.status_path).read().splitlines() |
74 for l in self.opener(self.status_path).read().splitlines()] | 76 self.applied = [statusentry(l) for l in lines] |
77 | |
78 def join(self, *p): | |
79 return os.path.join(self.path, *p) | |
75 | 80 |
76 def find_series(self, patch): | 81 def find_series(self, patch): |
77 pre = re.compile("(\s*)([^#]+)") | 82 pre = re.compile("(\s*)([^#]+)") |
78 index = 0 | 83 index = 0 |
79 for l in self.full_series: | 84 for l in self.full_series: |
84 if s == patch: | 89 if s == patch: |
85 return index | 90 return index |
86 index += 1 | 91 index += 1 |
87 return None | 92 return None |
88 | 93 |
94 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)') | |
95 | |
89 def parse_series(self): | 96 def parse_series(self): |
90 self.series = [] | 97 self.series = [] |
98 self.series_guards = [] | |
91 for l in self.full_series: | 99 for l in self.full_series: |
92 s = l.split('#', 1)[0].strip() | 100 h = l.find('#') |
93 if s: | 101 if h == -1: |
94 self.series.append(s) | 102 patch = l |
103 comment = '' | |
104 elif h == 0: | |
105 continue | |
106 else: | |
107 patch = l[:h] | |
108 comment = l[h:] | |
109 patch = patch.strip() | |
110 if patch: | |
111 self.series.append(patch) | |
112 self.series_guards.append(self.guard_re.findall(comment)) | |
113 | |
114 def check_guard(self, guard): | |
115 bad_chars = '# \t\r\n\f' | |
116 first = guard[0] | |
117 for c in '-+': | |
118 if first == c: | |
119 return (_('guard %r starts with invalid character: %r') % | |
120 (guard, c)) | |
121 for c in bad_chars: | |
122 if c in guard: | |
123 return _('invalid character in guard %r: %r') % (guard, c) | |
124 | |
125 def set_active(self, guards): | |
126 for guard in guards: | |
127 bad = self.check_guard(guard) | |
128 if bad: | |
129 raise util.Abort(bad) | |
130 guards = dict.fromkeys(guards).keys() | |
131 guards.sort() | |
132 self.ui.debug('active guards: %s\n' % ' '.join(guards)) | |
133 self.active_guards = guards | |
134 self.guards_dirty = True | |
135 | |
136 def active(self): | |
137 if self.active_guards is None: | |
138 self.active_guards = [] | |
139 try: | |
140 guards = self.opener(self.guards_path).read().split() | |
141 except IOError, err: | |
142 if err.errno != errno.ENOENT: raise | |
143 guards = [] | |
144 for i, guard in enumerate(guards): | |
145 bad = self.check_guard(guard) | |
146 if bad: | |
147 self.ui.warn('%s:%d: %s\n' % | |
148 (self.join(self.guards_path), i + 1, bad)) | |
149 else: | |
150 self.active_guards.append(guard) | |
151 return self.active_guards | |
152 | |
153 def set_guards(self, idx, guards): | |
154 for g in guards: | |
155 if len(g) < 2: | |
156 raise util.Abort(_('guard %r too short') % g) | |
157 if g[0] not in '-+': | |
158 raise util.Abort(_('guard %r starts with invalid char') % g) | |
159 bad = self.check_guard(g[1:]) | |
160 if bad: | |
161 raise util.Abort(bad) | |
162 drop = self.guard_re.sub('', self.full_series[idx]) | |
163 self.full_series[idx] = drop + ''.join([' #' + g for g in guards]) | |
164 self.parse_series() | |
165 self.series_dirty = True | |
166 | |
167 def pushable(self, idx): | |
168 if isinstance(idx, str): | |
169 idx = self.series.index(idx) | |
170 patchguards = self.series_guards[idx] | |
171 if not patchguards: | |
172 return True, None | |
173 default = False | |
174 guards = self.active() | |
175 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards] | |
176 if exactneg: | |
177 return False, exactneg[0] | |
178 pos = [g for g in patchguards if g[0] == '+'] | |
179 nonpos = [g for g in pos if g[1:] not in guards] | |
180 if pos: | |
181 if not nonpos: | |
182 return True, '' | |
183 return False, nonpos | |
184 return True, '' | |
185 | |
186 def explain_pushable(self, idx, all_patches=False): | |
187 write = all_patches and self.ui.write or self.ui.warn | |
188 if all_patches or self.ui.verbose: | |
189 if isinstance(idx, str): | |
190 idx = self.series.index(idx) | |
191 pushable, why = self.pushable(idx) | |
192 if all_patches and pushable: | |
193 if why is None: | |
194 write(_('allowing %s - no guards in effect\n') % | |
195 self.series[idx]) | |
196 else: | |
197 if not why: | |
198 write(_('allowing %s - no matching negative guards\n') % | |
199 self.series[idx]) | |
200 else: | |
201 write(_('allowing %s - guarded by %r\n') % | |
202 (self.series[idx], why)) | |
203 if not pushable: | |
204 if why: | |
205 write(_('skipping %s - guarded by %r\n') % | |
206 (self.series[idx], ' '.join(why))) | |
207 else: | |
208 write(_('skipping %s - no matching guards\n') % | |
209 self.series[idx]) | |
95 | 210 |
96 def save_dirty(self): | 211 def save_dirty(self): |
97 def write_list(items, path): | 212 def write_list(items, path): |
98 fp = self.opener(path, 'w') | 213 fp = self.opener(path, 'w') |
99 for i in items: | 214 for i in items: |
100 print >> fp, i | 215 print >> fp, i |
101 fp.close() | 216 fp.close() |
102 if self.applied_dirty: write_list(map(str, self.applied), self.status_path) | 217 if self.applied_dirty: write_list(map(str, self.applied), self.status_path) |
103 if self.series_dirty: write_list(self.full_series, self.series_path) | 218 if self.series_dirty: write_list(self.full_series, self.series_path) |
219 if self.guards_dirty: write_list(self.active_guards, self.guards_path) | |
104 | 220 |
105 def readheaders(self, patch): | 221 def readheaders(self, patch): |
106 def eatdiff(lines): | 222 def eatdiff(lines): |
107 while lines: | 223 while lines: |
108 l = lines[-1] | 224 l = lines[-1] |
118 if re.match('\s*$', l): | 234 if re.match('\s*$', l): |
119 del lines[-1] | 235 del lines[-1] |
120 else: | 236 else: |
121 break | 237 break |
122 | 238 |
123 pf = os.path.join(self.path, patch) | 239 pf = self.join(patch) |
124 message = [] | 240 message = [] |
125 comments = [] | 241 comments = [] |
126 user = None | 242 user = None |
127 date = None | 243 date = None |
128 format = None | 244 format = None |
241 # the first patch in the queue is never a merge patch | 357 # the first patch in the queue is never a merge patch |
242 # | 358 # |
243 pname = ".hg.patches.merge.marker" | 359 pname = ".hg.patches.merge.marker" |
244 n = repo.commit(None, '[mq]: merge marker', user=None, force=1, | 360 n = repo.commit(None, '[mq]: merge marker', user=None, force=1, |
245 wlock=wlock) | 361 wlock=wlock) |
246 self.applied.append(StatusEntry(revlog.hex(n), pname)) | 362 self.applied.append(statusentry(revlog.hex(n), pname)) |
247 self.applied_dirty = 1 | 363 self.applied_dirty = 1 |
248 | 364 |
249 head = self.qparents(repo) | 365 head = self.qparents(repo) |
250 | 366 |
251 for patch in series: | 367 for patch in series: |
252 patch = mergeq.lookup(patch, strict=True) | 368 patch = mergeq.lookup(patch, strict=True) |
253 if not patch: | 369 if not patch: |
254 self.ui.warn("patch %s does not exist\n" % patch) | 370 self.ui.warn("patch %s does not exist\n" % patch) |
255 return (1, None) | 371 return (1, None) |
256 | 372 pushable, reason = self.pushable(patch) |
373 if not pushable: | |
374 self.explain_pushable(patch, all_patches=True) | |
375 continue | |
257 info = mergeq.isapplied(patch) | 376 info = mergeq.isapplied(patch) |
258 if not info: | 377 if not info: |
259 self.ui.warn("patch %s is not applied\n" % patch) | 378 self.ui.warn("patch %s is not applied\n" % patch) |
260 return (1, None) | 379 return (1, None) |
261 rev = revlog.bin(info[1]) | 380 rev = revlog.bin(info[1]) |
262 (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock) | 381 (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock) |
263 if head: | 382 if head: |
264 self.applied.append(StatusEntry(revlog.hex(head), patch)) | 383 self.applied.append(statusentry(revlog.hex(head), patch)) |
265 self.applied_dirty = 1 | 384 self.applied_dirty = 1 |
266 if err: | 385 if err: |
267 return (err, head) | 386 return (err, head) |
268 return (0, head) | 387 return (0, head) |
269 | 388 |
315 wlock = repo.wlock() | 434 wlock = repo.wlock() |
316 lock = repo.lock() | 435 lock = repo.lock() |
317 tr = repo.transaction() | 436 tr = repo.transaction() |
318 n = None | 437 n = None |
319 for patch in series: | 438 for patch in series: |
439 pushable, reason = self.pushable(patch) | |
440 if not pushable: | |
441 self.explain_pushable(patch, all_patches=True) | |
442 continue | |
320 self.ui.warn("applying %s\n" % patch) | 443 self.ui.warn("applying %s\n" % patch) |
321 pf = os.path.join(patchdir, patch) | 444 pf = os.path.join(patchdir, patch) |
322 | 445 |
323 try: | 446 try: |
324 message, comments, user, date, patchfound = self.readheaders(patch) | 447 message, comments, user, date, patchfound = self.readheaders(patch) |
354 | 477 |
355 if n == None: | 478 if n == None: |
356 raise util.Abort(_("repo commit failed")) | 479 raise util.Abort(_("repo commit failed")) |
357 | 480 |
358 if update_status: | 481 if update_status: |
359 self.applied.append(StatusEntry(revlog.hex(n), patch)) | 482 self.applied.append(statusentry(revlog.hex(n), patch)) |
360 | 483 |
361 if patcherr: | 484 if patcherr: |
362 if not patchfound: | 485 if not patchfound: |
363 self.ui.warn("patch %s is empty\n" % patch) | 486 self.ui.warn("patch %s is empty\n" % patch) |
364 err = 0 | 487 err = 0 |
384 if force: | 507 if force: |
385 r = self.qrepo() | 508 r = self.qrepo() |
386 if r: | 509 if r: |
387 r.remove([patch], True) | 510 r.remove([patch], True) |
388 else: | 511 else: |
389 os.unlink(os.path.join(self.path, patch)) | 512 os.unlink(self.join(patch)) |
390 i = self.find_series(patch) | 513 i = self.find_series(patch) |
391 del self.full_series[i] | 514 del self.full_series[i] |
392 self.parse_series() | 515 self.parse_series() |
393 self.series_dirty = 1 | 516 self.series_dirty = 1 |
394 | 517 |
403 def check_localchanges(self, repo): | 526 def check_localchanges(self, repo): |
404 (c, a, r, d, u) = repo.changes(None, None) | 527 (c, a, r, d, u) = repo.changes(None, None) |
405 if c or a or d or r: | 528 if c or a or d or r: |
406 raise util.Abort(_("local changes found, refresh first")) | 529 raise util.Abort(_("local changes found, refresh first")) |
407 def new(self, repo, patch, msg=None, force=None): | 530 def new(self, repo, patch, msg=None, force=None): |
408 if os.path.exists(os.path.join(self.path, patch)): | 531 if os.path.exists(self.join(patch)): |
409 raise util.Abort(_('patch "%s" already exists') % patch) | 532 raise util.Abort(_('patch "%s" already exists') % patch) |
410 commitfiles = [] | 533 commitfiles = [] |
411 (c, a, r, d, u) = repo.changes(None, None) | 534 (c, a, r, d, u) = repo.changes(None, None) |
412 if c or a or d or r: | 535 if c or a or d or r: |
413 if not force: | 536 if not force: |
423 n = repo.commit(commitfiles, | 546 n = repo.commit(commitfiles, |
424 "New patch: %s" % patch, force=True, wlock=wlock) | 547 "New patch: %s" % patch, force=True, wlock=wlock) |
425 if n == None: | 548 if n == None: |
426 raise util.Abort(_("repo commit failed")) | 549 raise util.Abort(_("repo commit failed")) |
427 self.full_series[insert:insert] = [patch] | 550 self.full_series[insert:insert] = [patch] |
428 self.applied.append(StatusEntry(revlog.hex(n), patch)) | 551 self.applied.append(statusentry(revlog.hex(n), patch)) |
429 self.parse_series() | 552 self.parse_series() |
430 self.series_dirty = 1 | 553 self.series_dirty = 1 |
431 self.applied_dirty = 1 | 554 self.applied_dirty = 1 |
432 p = self.opener(patch, "w") | 555 p = self.opener(patch, "w") |
433 if msg: | 556 if msg: |
626 # sure the file name passed in does not exist (checked below) | 749 # sure the file name passed in does not exist (checked below) |
627 res = partial_name(patch) | 750 res = partial_name(patch) |
628 if res and res == patch: | 751 if res and res == patch: |
629 return res | 752 return res |
630 | 753 |
631 if not os.path.isfile(os.path.join(self.path, patch)): | 754 if not os.path.isfile(self.join(patch)): |
632 try: | 755 try: |
633 sno = int(patch) | 756 sno = int(patch) |
634 except(ValueError, OverflowError): | 757 except(ValueError, OverflowError): |
635 pass | 758 pass |
636 else: | 759 else: |
637 if sno < len(self.series): | 760 if sno < len(self.series): |
638 patch = self.series[sno] | 761 return self.series[sno] |
639 return patch | |
640 if not strict: | 762 if not strict: |
641 # return any partial match made above | 763 # return any partial match made above |
642 if res: | 764 if res: |
643 return res | 765 return res |
644 minus = patch.rsplit('-', 1) | 766 minus = patch.rsplit('-', 1) |
898 else: | 1020 else: |
899 message = msg | 1021 message = msg |
900 | 1022 |
901 self.strip(repo, top, update=False, backup='strip', wlock=wlock) | 1023 self.strip(repo, top, update=False, backup='strip', wlock=wlock) |
902 n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock) | 1024 n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock) |
903 self.applied[-1] = StatusEntry(revlog.hex(n), patch) | 1025 self.applied[-1] = statusentry(revlog.hex(n), patch) |
904 self.applied_dirty = 1 | 1026 self.applied_dirty = 1 |
905 else: | 1027 else: |
906 commands.dodiff(patchf, self.ui, repo, patchparent, None) | 1028 commands.dodiff(patchf, self.ui, repo, patchparent, None) |
907 patchf.close() | 1029 patchf.close() |
908 self.pop(repo, force=True, wlock=wlock) | 1030 self.pop(repo, force=True, wlock=wlock) |
920 raise util.Abort(_("patch %s is not in series file") % patch) | 1042 raise util.Abort(_("patch %s is not in series file") % patch) |
921 if not patch: | 1043 if not patch: |
922 start = self.series_end() | 1044 start = self.series_end() |
923 else: | 1045 else: |
924 start = self.series.index(patch) + 1 | 1046 start = self.series.index(patch) + 1 |
925 return [(i, self.series[i]) for i in xrange(start, len(self.series))] | 1047 unapplied = [] |
1048 for i in xrange(start, len(self.series)): | |
1049 pushable, reason = self.pushable(i) | |
1050 if pushable: | |
1051 unapplied.append((i, self.series[i])) | |
1052 self.explain_pushable(i) | |
1053 return unapplied | |
926 | 1054 |
927 def qseries(self, repo, missing=None, summary=False): | 1055 def qseries(self, repo, missing=None, summary=False): |
928 start = self.series_end() | 1056 start = self.series_end(all_patches=True) |
929 if not missing: | 1057 if not missing: |
930 for i in range(len(self.series)): | 1058 for i in range(len(self.series)): |
931 patch = self.series[i] | 1059 patch = self.series[i] |
932 if self.ui.verbose: | 1060 if self.ui.verbose: |
933 if i < start: | 1061 if i < start: |
934 status = 'A' | 1062 status = 'A' |
1063 elif self.pushable(i)[0]: | |
1064 status = 'U' | |
935 else: | 1065 else: |
936 status = 'U' | 1066 status = 'G' |
937 self.ui.write('%d %s ' % (i, status)) | 1067 self.ui.write('%d %s ' % (i, status)) |
938 if summary: | 1068 if summary: |
939 msg = self.readheaders(patch)[0] | 1069 msg = self.readheaders(patch)[0] |
940 msg = msg and ': ' + msg[0] or ': ' | 1070 msg = msg and ': ' + msg[0] or ': ' |
941 else: | 1071 else: |
956 if self.ui.verbose: | 1086 if self.ui.verbose: |
957 self.ui.write("D ") | 1087 self.ui.write("D ") |
958 self.ui.write("%s\n" % x) | 1088 self.ui.write("%s\n" % x) |
959 | 1089 |
960 def issaveline(self, l): | 1090 def issaveline(self, l): |
961 name = l.split(':')[1] | 1091 if l.name == '.hg.patches.save.line': |
962 if name == '.hg.patches.save.line': | |
963 return True | 1092 return True |
964 | 1093 |
965 def qrepo(self, create=False): | 1094 def qrepo(self, create=False): |
966 if create or os.path.isdir(os.path.join(self.path, ".hg")): | 1095 if create or os.path.isdir(self.join(".hg")): |
967 return hg.repository(self.ui, path=self.path, create=create) | 1096 return hg.repository(self.ui, path=self.path, create=create) |
968 | 1097 |
969 def restore(self, repo, rev, delete=None, qupdate=None): | 1098 def restore(self, repo, rev, delete=None, qupdate=None): |
970 c = repo.changelog.read(rev) | 1099 c = repo.changelog.read(rev) |
971 desc = c[4].strip() | 1100 desc = c[4].strip() |
982 l = lines[i].rstrip() | 1111 l = lines[i].rstrip() |
983 l = l[10:].split(' ') | 1112 l = l[10:].split(' ') |
984 qpp = [ hg.bin(x) for x in l ] | 1113 qpp = [ hg.bin(x) for x in l ] |
985 elif datastart != None: | 1114 elif datastart != None: |
986 l = lines[i].rstrip() | 1115 l = lines[i].rstrip() |
987 se = StatusEntry(l) | 1116 se = statusentry(l) |
988 file_ = se.name | 1117 file_ = se.name |
989 if se.rev: | 1118 if se.rev: |
990 applied.append(se) | 1119 applied.append(se) |
991 series.append(file_) | 1120 series.append(file_) |
992 if datastart == None: | 1121 if datastart == None: |
1037 r = self.qrepo() | 1166 r = self.qrepo() |
1038 if r: | 1167 if r: |
1039 pp = r.dirstate.parents() | 1168 pp = r.dirstate.parents() |
1040 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1])) | 1169 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1])) |
1041 msg += "\n\nPatch Data:\n" | 1170 msg += "\n\nPatch Data:\n" |
1042 text = msg + "\n".join(str(self.applied)) + '\n' + (ar and "\n".join(ar) | 1171 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and |
1043 + '\n' or "") | 1172 "\n".join(ar) + '\n' or "") |
1044 n = repo.commit(None, text, user=None, force=1) | 1173 n = repo.commit(None, text, user=None, force=1) |
1045 if not n: | 1174 if not n: |
1046 self.ui.warn("repo commit failed\n") | 1175 self.ui.warn("repo commit failed\n") |
1047 return 1 | 1176 return 1 |
1048 self.applied.append(StatusEntry(revlog.hex(n),'.hg.patches.save.line')) | 1177 self.applied.append(statusentry(revlog.hex(n),'.hg.patches.save.line')) |
1049 self.applied_dirty = 1 | 1178 self.applied_dirty = 1 |
1050 | 1179 |
1051 def full_series_end(self): | 1180 def full_series_end(self): |
1052 if len(self.applied) > 0: | 1181 if len(self.applied) > 0: |
1053 p = self.applied[-1].name | 1182 p = self.applied[-1].name |
1055 if end == None: | 1184 if end == None: |
1056 return len(self.full_series) | 1185 return len(self.full_series) |
1057 return end + 1 | 1186 return end + 1 |
1058 return 0 | 1187 return 0 |
1059 | 1188 |
1060 def series_end(self): | 1189 def series_end(self, all_patches=False): |
1061 end = 0 | 1190 end = 0 |
1191 def next(start): | |
1192 if all_patches: | |
1193 return start | |
1194 i = start | |
1195 while i < len(self.series): | |
1196 p, reason = self.pushable(i) | |
1197 if p: | |
1198 break | |
1199 self.explain_pushable(i) | |
1200 i += 1 | |
1201 return i | |
1062 if len(self.applied) > 0: | 1202 if len(self.applied) > 0: |
1063 p = self.applied[-1].name | 1203 p = self.applied[-1].name |
1064 try: | 1204 try: |
1065 end = self.series.index(p) | 1205 end = self.series.index(p) |
1066 except ValueError: | 1206 except ValueError: |
1067 return 0 | 1207 return 0 |
1068 return end + 1 | 1208 return next(end + 1) |
1069 return end | 1209 return next(end) |
1070 | 1210 |
1071 def qapplied(self, repo, patch=None): | 1211 def qapplied(self, repo, patch=None): |
1072 if patch and patch not in self.series: | 1212 if patch and patch not in self.series: |
1073 raise util.Abort(_("patch %s is not in series file") % patch) | 1213 raise util.Abort(_("patch %s is not in series file") % patch) |
1074 if not patch: | 1214 if not patch: |
1121 added = [] | 1261 added = [] |
1122 for filename in files: | 1262 for filename in files: |
1123 if existing: | 1263 if existing: |
1124 if not patch: | 1264 if not patch: |
1125 patch = filename | 1265 patch = filename |
1126 if not os.path.isfile(os.path.join(self.path, patch)): | 1266 if not os.path.isfile(self.join(patch)): |
1127 raise util.Abort(_("patch %s does not exist") % patch) | 1267 raise util.Abort(_("patch %s does not exist") % patch) |
1128 else: | 1268 else: |
1129 try: | 1269 try: |
1130 text = file(filename).read() | 1270 text = file(filename).read() |
1131 except IOError: | 1271 except IOError: |
1132 raise util.Abort(_("unable to read %s") % patch) | 1272 raise util.Abort(_("unable to read %s") % patch) |
1133 if not patch: | 1273 if not patch: |
1134 patch = os.path.split(filename)[1] | 1274 patch = os.path.split(filename)[1] |
1135 if not force and os.path.exists(os.path.join(self.path, patch)): | 1275 if not force and os.path.exists(self.join(patch)): |
1136 raise util.Abort(_('patch "%s" already exists') % patch) | 1276 raise util.Abort(_('patch "%s" already exists') % patch) |
1137 patchf = self.opener(patch, "w") | 1277 patchf = self.opener(patch, "w") |
1138 patchf.write(text) | 1278 patchf.write(text) |
1139 if patch in self.series: | 1279 if patch in self.series: |
1140 raise util.Abort(_('patch %s is already in the series file') | 1280 raise util.Abort(_('patch %s is already in the series file') |
1345 patches.append(patch) | 1485 patches.append(patch) |
1346 | 1486 |
1347 for patch in patches: | 1487 for patch in patches: |
1348 if not message: | 1488 if not message: |
1349 messages.append(q.readheaders(patch)[0]) | 1489 messages.append(q.readheaders(patch)[0]) |
1350 pf = os.path.join(q.path, patch) | 1490 pf = q.join(patch) |
1351 (patchsuccess, files, fuzz) = q.patch(repo, pf) | 1491 (patchsuccess, files, fuzz) = q.patch(repo, pf) |
1352 if not patchsuccess: | 1492 if not patchsuccess: |
1353 raise util.Abort(_('Error folding patch %s') % patch) | 1493 raise util.Abort(_('Error folding patch %s') % patch) |
1354 | 1494 |
1355 if not message: | 1495 if not message: |
1366 | 1506 |
1367 for patch in patches: | 1507 for patch in patches: |
1368 q.delete(repo, patch, force=opts['force']) | 1508 q.delete(repo, patch, force=opts['force']) |
1369 | 1509 |
1370 q.save_dirty() | 1510 q.save_dirty() |
1511 | |
1512 def guard(ui, repo, *args, **opts): | |
1513 '''set or print guards for a patch | |
1514 | |
1515 guards control whether a patch can be pushed. a patch with no | |
1516 guards is aways pushed. a patch with posative guard ("+foo") is | |
1517 pushed only if qselect command enables guard "foo". a patch with | |
1518 nagative guard ("-foo") is never pushed if qselect command enables | |
1519 guard "foo". | |
1520 | |
1521 with no arguments, default is to print current active guards. | |
1522 with arguments, set active guards for patch. | |
1523 | |
1524 to set nagative guard "-foo" on topmost patch ("--" is needed so | |
1525 hg will not interpret "-foo" as argument): | |
1526 hg qguard -- -foo | |
1527 | |
1528 to set guards on other patch: | |
1529 hg qguard other.patch +2.6.17 -stable | |
1530 ''' | |
1531 def status(idx): | |
1532 guards = q.series_guards[idx] or ['unguarded'] | |
1533 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards))) | |
1534 q = repo.mq | |
1535 patch = None | |
1536 args = list(args) | |
1537 if opts['list']: | |
1538 if args or opts['none']: | |
1539 raise util.Abort(_('cannot mix -l/--list with options or arguments')) | |
1540 for i in xrange(len(q.series)): | |
1541 status(i) | |
1542 return | |
1543 if not args or args[0][0:1] in '-+': | |
1544 if not q.applied: | |
1545 raise util.Abort(_('no patches applied')) | |
1546 patch = q.applied[-1].name | |
1547 if patch is None and args[0][0:1] not in '-+': | |
1548 patch = args.pop(0) | |
1549 if patch is None: | |
1550 raise util.Abort(_('no patch to work with')) | |
1551 if args or opts['none']: | |
1552 q.set_guards(q.find_series(patch), args) | |
1553 q.save_dirty() | |
1554 else: | |
1555 status(q.series.index(q.lookup(patch))) | |
1371 | 1556 |
1372 def header(ui, repo, patch=None): | 1557 def header(ui, repo, patch=None): |
1373 """Print the header of the topmost or specified patch""" | 1558 """Print the header of the topmost or specified patch""" |
1374 q = repo.mq | 1559 q = repo.mq |
1375 | 1560 |
1456 patch = None | 1641 patch = None |
1457 | 1642 |
1458 if name in q.series: | 1643 if name in q.series: |
1459 raise util.Abort(_('A patch named %s already exists in the series file') % name) | 1644 raise util.Abort(_('A patch named %s already exists in the series file') % name) |
1460 | 1645 |
1461 absdest = os.path.join(q.path, name) | 1646 absdest = q.join(name) |
1462 if os.path.exists(absdest): | 1647 if os.path.exists(absdest): |
1463 raise util.Abort(_('%s already exists') % absdest) | 1648 raise util.Abort(_('%s already exists') % absdest) |
1464 | 1649 |
1465 if patch: | 1650 if patch: |
1466 patch = q.lookup(patch) | 1651 patch = q.lookup(patch) |
1477 q.parse_series() | 1662 q.parse_series() |
1478 q.series_dirty = 1 | 1663 q.series_dirty = 1 |
1479 | 1664 |
1480 info = q.isapplied(patch) | 1665 info = q.isapplied(patch) |
1481 if info: | 1666 if info: |
1482 q.applied[info[0]] = StatusEntry(info[1], name) | 1667 q.applied[info[0]] = statusentry(info[1], name) |
1483 q.applied_dirty = 1 | 1668 q.applied_dirty = 1 |
1484 | 1669 |
1485 util.rename(os.path.join(q.path, patch), absdest) | 1670 util.rename(q.join(patch), absdest) |
1486 r = q.qrepo() | 1671 r = q.qrepo() |
1487 if r: | 1672 if r: |
1488 wlock = r.wlock() | 1673 wlock = r.wlock() |
1489 if r.dirstate.state(name) == 'r': | 1674 if r.dirstate.state(name) == 'r': |
1490 r.undelete([name], wlock) | 1675 r.undelete([name], wlock) |
1525 newpath = savename(path) | 1710 newpath = savename(path) |
1526 ui.warn("copy %s to %s\n" % (path, newpath)) | 1711 ui.warn("copy %s to %s\n" % (path, newpath)) |
1527 util.copyfiles(path, newpath) | 1712 util.copyfiles(path, newpath) |
1528 if opts['empty']: | 1713 if opts['empty']: |
1529 try: | 1714 try: |
1530 os.unlink(os.path.join(q.path, q.status_path)) | 1715 os.unlink(q.join(q.status_path)) |
1531 except: | 1716 except: |
1532 pass | 1717 pass |
1533 return 0 | 1718 return 0 |
1534 | 1719 |
1535 def strip(ui, repo, rev, **opts): | 1720 def strip(ui, repo, rev, **opts): |
1541 elif opts['nobackup']: | 1726 elif opts['nobackup']: |
1542 backup = 'none' | 1727 backup = 'none' |
1543 repo.mq.strip(repo, rev, backup=backup) | 1728 repo.mq.strip(repo, rev, backup=backup) |
1544 return 0 | 1729 return 0 |
1545 | 1730 |
1546 def version(ui, q=None): | 1731 def select(ui, repo, *args, **opts): |
1547 """print the version number of the mq extension""" | 1732 '''set or print guarded patches to push |
1548 ui.write("mq version %s\n" % versionstr) | 1733 |
1549 return 0 | 1734 use qguard command to set or print guards on patch. then use |
1735 qselect to tell mq which guards to use. example: | |
1736 | |
1737 qguard foo.patch -stable (nagative guard) | |
1738 qguard bar.patch +stable (posative guard) | |
1739 qselect stable | |
1740 | |
1741 this sets "stable" guard. mq will skip foo.patch (because it has | |
1742 nagative match) but push bar.patch (because it has posative | |
1743 match). patch is pushed only if all posative guards match and no | |
1744 nagative guards match. | |
1745 | |
1746 with no arguments, default is to print current active guards. | |
1747 with arguments, set active guards as given. | |
1748 | |
1749 use -n/--none to deactivate guards (no other arguments needed). | |
1750 when no guards active, patches with posative guards are skipped, | |
1751 patches with nagative guards are pushed. | |
1752 | |
1753 use -s/--series to print list of all guards in series file (no | |
1754 other arguments needed). use -v for more information.''' | |
1755 | |
1756 q = repo.mq | |
1757 guards = q.active() | |
1758 if args or opts['none']: | |
1759 q.set_active(args) | |
1760 q.save_dirty() | |
1761 if not args: | |
1762 ui.status(_('guards deactivated\n')) | |
1763 if q.series: | |
1764 ui.status(_('%d of %d unapplied patches active\n') % | |
1765 (len(q.unapplied(repo)), len(q.series))) | |
1766 elif opts['series']: | |
1767 guards = {} | |
1768 noguards = 0 | |
1769 for gs in q.series_guards: | |
1770 if not gs: | |
1771 noguards += 1 | |
1772 for g in gs: | |
1773 guards.setdefault(g, 0) | |
1774 guards[g] += 1 | |
1775 if ui.verbose: | |
1776 guards['NONE'] = noguards | |
1777 guards = guards.items() | |
1778 guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:])) | |
1779 if guards: | |
1780 ui.note(_('guards in series file:\n')) | |
1781 for guard, count in guards: | |
1782 ui.note('%2d ' % count) | |
1783 ui.write(guard, '\n') | |
1784 else: | |
1785 ui.note(_('no guards in series file\n')) | |
1786 else: | |
1787 if guards: | |
1788 ui.note(_('active guards:\n')) | |
1789 for g in guards: | |
1790 ui.write(g, '\n') | |
1791 else: | |
1792 ui.write(_('no active guards\n')) | |
1550 | 1793 |
1551 def reposetup(ui, repo): | 1794 def reposetup(ui, repo): |
1552 class MqRepo(repo.__class__): | 1795 class mqrepo(repo.__class__): |
1553 def tags(self): | 1796 def tags(self): |
1554 if self.tagscache: | 1797 if self.tagscache: |
1555 return self.tagscache | 1798 return self.tagscache |
1556 | 1799 |
1557 tagscache = super(MqRepo, self).tags() | 1800 tagscache = super(mqrepo, self).tags() |
1558 | 1801 |
1559 q = self.mq | 1802 q = self.mq |
1560 if not q.applied: | 1803 if not q.applied: |
1561 return tagscache | 1804 return tagscache |
1562 | 1805 |
1569 else: | 1812 else: |
1570 tagscache[patch[1]] = revlog.bin(patch[0]) | 1813 tagscache[patch[1]] = revlog.bin(patch[0]) |
1571 | 1814 |
1572 return tagscache | 1815 return tagscache |
1573 | 1816 |
1574 repo.__class__ = MqRepo | 1817 repo.__class__ = mqrepo |
1575 repo.mq = queue(ui, repo.join("")) | 1818 repo.mq = queue(ui, repo.join("")) |
1576 | 1819 |
1577 cmdtable = { | 1820 cmdtable = { |
1578 "qapplied": (applied, [], 'hg qapplied [PATCH]'), | 1821 "qapplied": (applied, [], 'hg qapplied [PATCH]'), |
1579 "qclone": (clone, | 1822 "qclone": (clone, |
1600 [('e', 'edit', None, _('edit patch header')), | 1843 [('e', 'edit', None, _('edit patch header')), |
1601 ('f', 'force', None, _('delete folded patch files')), | 1844 ('f', 'force', None, _('delete folded patch files')), |
1602 ('m', 'message', '', _('set patch header to <text>')), | 1845 ('m', 'message', '', _('set patch header to <text>')), |
1603 ('l', 'logfile', '', _('set patch header to contents of <file>'))], | 1846 ('l', 'logfile', '', _('set patch header to contents of <file>'))], |
1604 'hg qfold [-e] [-m <text>] [-l <file] PATCH...'), | 1847 'hg qfold [-e] [-m <text>] [-l <file] PATCH...'), |
1848 'qguard': (guard, [('l', 'list', None, _('list all patches and guards')), | |
1849 ('n', 'none', None, _('drop all guards'))], | |
1850 'hg qguard [PATCH] [+GUARD...] [-GUARD...]'), | |
1605 'qheader': (header, [], | 1851 'qheader': (header, [], |
1606 _('hg qheader [PATCH]')), | 1852 _('hg qheader [PATCH]')), |
1607 "^qimport": | 1853 "^qimport": |
1608 (qimport, | 1854 (qimport, |
1609 [('e', 'existing', None, 'import file in patch dir'), | 1855 [('e', 'existing', None, 'import file in patch dir'), |
1657 ('c', 'copy', None, 'copy patch directory'), | 1903 ('c', 'copy', None, 'copy patch directory'), |
1658 ('n', 'name', '', 'copy directory name'), | 1904 ('n', 'name', '', 'copy directory name'), |
1659 ('e', 'empty', None, 'clear queue status file'), | 1905 ('e', 'empty', None, 'clear queue status file'), |
1660 ('f', 'force', None, 'force copy')], | 1906 ('f', 'force', None, 'force copy')], |
1661 'hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'), | 1907 'hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'), |
1908 "qselect": (select, | |
1909 [('n', 'none', None, _('disable all guards')), | |
1910 ('s', 'series', None, _('list all guards in series file'))], | |
1911 'hg qselect [GUARDS]'), | |
1662 "qseries": | 1912 "qseries": |
1663 (series, | 1913 (series, |
1664 [('m', 'missing', None, 'print patches not in series'), | 1914 [('m', 'missing', None, 'print patches not in series'), |
1665 ('s', 'summary', None, _('print first line of patch header'))], | 1915 ('s', 'summary', None, _('print first line of patch header'))], |
1666 'hg qseries [-m]'), | 1916 'hg qseries [-m]'), |
1670 ('b', 'backup', None, 'bundle unrelated changesets'), | 1920 ('b', 'backup', None, 'bundle unrelated changesets'), |
1671 ('n', 'nobackup', None, 'no backups')], | 1921 ('n', 'nobackup', None, 'no backups')], |
1672 'hg strip [-f] [-b] [-n] REV'), | 1922 'hg strip [-f] [-b] [-n] REV'), |
1673 "qtop": (top, [], 'hg qtop'), | 1923 "qtop": (top, [], 'hg qtop'), |
1674 "qunapplied": (unapplied, [], 'hg qunapplied [PATCH]'), | 1924 "qunapplied": (unapplied, [], 'hg qunapplied [PATCH]'), |
1675 "qversion": (version, [], 'hg qversion') | |
1676 } | 1925 } |
1677 | 1926 |