Show More
@@ -0,0 +1,62 | |||
|
1 | #!/bin/sh | |
|
2 | ||
|
3 | HGRCPATH=$HGTMP/.hgrc; export HGRCPATH | |
|
4 | echo "[extensions]" >> $HGTMP/.hgrc | |
|
5 | echo "mq=" >> $HGTMP/.hgrc | |
|
6 | ||
|
7 | hg init | |
|
8 | hg qinit | |
|
9 | ||
|
10 | echo x > x | |
|
11 | hg ci -Ama | |
|
12 | ||
|
13 | hg qnew a.patch | |
|
14 | echo a > a | |
|
15 | hg add a | |
|
16 | hg qrefresh | |
|
17 | ||
|
18 | hg qnew b.patch | |
|
19 | echo b > b | |
|
20 | hg add b | |
|
21 | hg qrefresh | |
|
22 | ||
|
23 | hg qnew c.patch | |
|
24 | echo c > c | |
|
25 | hg add c | |
|
26 | hg qrefresh | |
|
27 | ||
|
28 | hg qpop -a | |
|
29 | ||
|
30 | echo % should fail | |
|
31 | hg qguard +fail | |
|
32 | ||
|
33 | hg qpush | |
|
34 | echo % should guard a.patch | |
|
35 | hg qguard +a | |
|
36 | echo % should print +a | |
|
37 | hg qguard | |
|
38 | hg qpop | |
|
39 | ||
|
40 | hg qguard a.patch | |
|
41 | echo % should push b.patch | |
|
42 | hg qpush | |
|
43 | ||
|
44 | hg qpop | |
|
45 | hg qselect a | |
|
46 | echo % should push a.patch | |
|
47 | hg qpush | |
|
48 | ||
|
49 | hg qguard c.patch -a | |
|
50 | echo % should print -a | |
|
51 | hg qguard c.patch | |
|
52 | ||
|
53 | echo % should skip c.patch | |
|
54 | hg qpush -a | |
|
55 | ||
|
56 | hg qguard -n c.patch | |
|
57 | echo % should push c.patch | |
|
58 | hg qpush -a | |
|
59 | ||
|
60 | hg qpop -a | |
|
61 | hg qselect -n | |
|
62 | hg qpush -a |
@@ -65,14 +65,17 class queue: | |||
|
65 | 65 | self.series_dirty = 0 |
|
66 | 66 | self.series_path = "series" |
|
67 | 67 | self.status_path = "status" |
|
68 | self.guards_path = "guards" | |
|
69 | self.active_guards = None | |
|
70 | self.guards_dirty = False | |
|
68 | 71 | |
|
69 | 72 | if os.path.exists(self.join(self.series_path)): |
|
70 | 73 | self.full_series = self.opener(self.series_path).read().splitlines() |
|
71 | 74 | self.parse_series() |
|
72 | 75 | |
|
73 | 76 | if os.path.exists(self.join(self.status_path)): |
|
74 | self.applied = [statusentry(l) | |
|
75 | for l in self.opener(self.status_path).read().splitlines()] | |
|
77 | lines = self.opener(self.status_path).read().splitlines() | |
|
78 | self.applied = [statusentry(l) for l in lines] | |
|
76 | 79 | |
|
77 | 80 | def join(self, *p): |
|
78 | 81 | return os.path.join(self.path, *p) |
@@ -90,12 +93,122 class queue: | |||
|
90 | 93 | index += 1 |
|
91 | 94 | return None |
|
92 | 95 | |
|
96 | guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)') | |
|
97 | ||
|
93 | 98 | def parse_series(self): |
|
94 | 99 | self.series = [] |
|
100 | self.series_guards = [] | |
|
95 | 101 | for l in self.full_series: |
|
96 |
|
|
|
97 |
if |
|
|
98 | self.series.append(s) | |
|
102 | h = l.find('#') | |
|
103 | if h == -1: | |
|
104 | patch = l | |
|
105 | comment = '' | |
|
106 | elif h == 0: | |
|
107 | continue | |
|
108 | else: | |
|
109 | patch = l[:h] | |
|
110 | comment = l[h:] | |
|
111 | patch = patch.strip() | |
|
112 | if patch: | |
|
113 | self.series.append(patch) | |
|
114 | self.series_guards.append(self.guard_re.findall(comment)) | |
|
115 | ||
|
116 | def check_guard(self, guard): | |
|
117 | bad_chars = '# \t\r\n\f' | |
|
118 | first = guard[0] | |
|
119 | for c in '-+': | |
|
120 | if first == c: | |
|
121 | return (_('guard %r starts with invalid character: %r') % | |
|
122 | (guard, c)) | |
|
123 | for c in bad_chars: | |
|
124 | if c in guard: | |
|
125 | return _('invalid character in guard %r: %r') % (guard, c) | |
|
126 | ||
|
127 | def set_active(self, guards): | |
|
128 | for guard in guards: | |
|
129 | bad = self.check_guard(guard) | |
|
130 | if bad: | |
|
131 | raise util.Abort(bad) | |
|
132 | guards = dict.fromkeys(guards).keys() | |
|
133 | guards.sort() | |
|
134 | self.ui.debug('active guards: %s\n' % ' '.join(guards)) | |
|
135 | self.active_guards = guards | |
|
136 | self.guards_dirty = True | |
|
137 | ||
|
138 | def active(self): | |
|
139 | if self.active_guards is None: | |
|
140 | self.active_guards = [] | |
|
141 | try: | |
|
142 | guards = self.opener(self.guards_path).read().split() | |
|
143 | except IOError, err: | |
|
144 | if err.errno != errno.ENOENT: raise | |
|
145 | guards = [] | |
|
146 | for i, guard in enumerate(guards): | |
|
147 | bad = self.check_guard(guard) | |
|
148 | if bad: | |
|
149 | self.ui.warn('%s:%d: %s\n' % | |
|
150 | (self.join(self.guards_path), i + 1, bad)) | |
|
151 | else: | |
|
152 | self.active_guards.append(guard) | |
|
153 | return self.active_guards | |
|
154 | ||
|
155 | def set_guards(self, idx, guards): | |
|
156 | for g in guards: | |
|
157 | if len(g) < 2: | |
|
158 | raise util.Abort(_('guard %r too short') % g) | |
|
159 | if g[0] not in '-+': | |
|
160 | raise util.Abort(_('guard %r starts with invalid char') % g) | |
|
161 | bad = self.check_guard(g[1:]) | |
|
162 | if bad: | |
|
163 | raise util.Abort(bad) | |
|
164 | drop = self.guard_re.sub('', self.full_series[idx]) | |
|
165 | self.full_series[idx] = drop + ''.join([' #' + g for g in guards]) | |
|
166 | self.parse_series() | |
|
167 | self.series_dirty = True | |
|
168 | ||
|
169 | def pushable(self, idx): | |
|
170 | if isinstance(idx, str): | |
|
171 | idx = self.series.index(idx) | |
|
172 | patchguards = self.series_guards[idx] | |
|
173 | if not patchguards: | |
|
174 | return True, None | |
|
175 | default = False | |
|
176 | guards = self.active() | |
|
177 | exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards] | |
|
178 | if exactneg: | |
|
179 | return False, exactneg[0] | |
|
180 | pos = [g for g in patchguards if g[0] == '+'] | |
|
181 | exactpos = [g for g in pos if g[1:] in guards] | |
|
182 | if pos: | |
|
183 | if exactpos: | |
|
184 | return True, exactpos[0] | |
|
185 | return False, '' | |
|
186 | return True, '' | |
|
187 | ||
|
188 | def explain_pushable(self, idx, all_patches=False): | |
|
189 | write = all_patches and self.ui.write or self.ui.warn | |
|
190 | if all_patches or self.ui.verbose: | |
|
191 | if isinstance(idx, str): | |
|
192 | idx = self.series.index(idx) | |
|
193 | pushable, why = self.pushable(idx) | |
|
194 | if all_patches and pushable: | |
|
195 | if why is None: | |
|
196 | write(_('allowing %s - no guards in effect\n') % | |
|
197 | self.series[idx]) | |
|
198 | else: | |
|
199 | if not why: | |
|
200 | write(_('allowing %s - no matching negative guards\n') % | |
|
201 | self.series[idx]) | |
|
202 | else: | |
|
203 | write(_('allowing %s - guarded by %r\n') % | |
|
204 | (self.series[idx], why)) | |
|
205 | if not pushable: | |
|
206 | if why and why[0] in '-+': | |
|
207 | write(_('skipping %s - guarded by %r\n') % | |
|
208 | (self.series[idx], why)) | |
|
209 | else: | |
|
210 | write(_('skipping %s - no matching guards\n') % | |
|
211 | self.series[idx]) | |
|
99 | 212 | |
|
100 | 213 | def save_dirty(self): |
|
101 | 214 | def write_list(items, path): |
@@ -105,6 +218,7 class queue: | |||
|
105 | 218 | fp.close() |
|
106 | 219 | if self.applied_dirty: write_list(map(str, self.applied), self.status_path) |
|
107 | 220 | if self.series_dirty: write_list(self.full_series, self.series_path) |
|
221 | if self.guards_dirty: write_list(self.active_guards, self.guards_path) | |
|
108 | 222 | |
|
109 | 223 | def readheaders(self, patch): |
|
110 | 224 | def eatdiff(lines): |
@@ -257,7 +371,10 class queue: | |||
|
257 | 371 | if not patch: |
|
258 | 372 | self.ui.warn("patch %s does not exist\n" % patch) |
|
259 | 373 | return (1, None) |
|
260 | ||
|
374 | pushable, reason = self.pushable(patch) | |
|
375 | if not pushable: | |
|
376 | self.explain_pushable(patch, all_patches=True) | |
|
377 | continue | |
|
261 | 378 | info = mergeq.isapplied(patch) |
|
262 | 379 | if not info: |
|
263 | 380 | self.ui.warn("patch %s is not applied\n" % patch) |
@@ -321,6 +438,10 class queue: | |||
|
321 | 438 | tr = repo.transaction() |
|
322 | 439 | n = None |
|
323 | 440 | for patch in series: |
|
441 | pushable, reason = self.pushable(patch) | |
|
442 | if not pushable: | |
|
443 | self.explain_pushable(patch, all_patches=True) | |
|
444 | continue | |
|
324 | 445 | self.ui.warn("applying %s\n" % patch) |
|
325 | 446 | pf = os.path.join(patchdir, patch) |
|
326 | 447 | |
@@ -639,8 +760,7 class queue: | |||
|
639 | 760 | pass |
|
640 | 761 | else: |
|
641 | 762 | if sno < len(self.series): |
|
642 |
|
|
|
643 | return patch | |
|
763 | return self.series[sno] | |
|
644 | 764 | if not strict: |
|
645 | 765 | # return any partial match made above |
|
646 | 766 | if res: |
@@ -926,18 +1046,26 class queue: | |||
|
926 | 1046 | start = self.series_end() |
|
927 | 1047 | else: |
|
928 | 1048 | start = self.series.index(patch) + 1 |
|
929 | return [(i, self.series[i]) for i in xrange(start, len(self.series))] | |
|
1049 | unapplied = [] | |
|
1050 | for i in xrange(start, len(self.series)): | |
|
1051 | pushable, reason = self.pushable(i) | |
|
1052 | if pushable: | |
|
1053 | unapplied.append((i, self.series[i])) | |
|
1054 | self.explain_pushable(i) | |
|
1055 | return unapplied | |
|
930 | 1056 | |
|
931 | 1057 | def qseries(self, repo, missing=None, summary=False): |
|
932 | start = self.series_end() | |
|
1058 | start = self.series_end(all_patches=True) | |
|
933 | 1059 | if not missing: |
|
934 | 1060 | for i in range(len(self.series)): |
|
935 | 1061 | patch = self.series[i] |
|
936 | 1062 | if self.ui.verbose: |
|
937 | 1063 | if i < start: |
|
938 | 1064 | status = 'A' |
|
1065 | elif self.pushable(i)[0]: | |
|
1066 | status = 'U' | |
|
939 | 1067 | else: |
|
940 |
status = ' |
|
|
1068 | status = 'G' | |
|
941 | 1069 | self.ui.write('%d %s ' % (i, status)) |
|
942 | 1070 | if summary: |
|
943 | 1071 | msg = self.readheaders(patch)[0] |
@@ -1060,16 +1188,27 class queue: | |||
|
1060 | 1188 | return end + 1 |
|
1061 | 1189 | return 0 |
|
1062 | 1190 | |
|
1063 | def series_end(self): | |
|
1191 | def series_end(self, all_patches=False): | |
|
1064 | 1192 | end = 0 |
|
1193 | def next(start): | |
|
1194 | if all_patches: | |
|
1195 | return start | |
|
1196 | i = start | |
|
1197 | while i < len(self.series): | |
|
1198 | p, reason = self.pushable(i) | |
|
1199 | if p: | |
|
1200 | break | |
|
1201 | self.explain_pushable(i) | |
|
1202 | i += 1 | |
|
1203 | return i | |
|
1065 | 1204 | if len(self.applied) > 0: |
|
1066 | 1205 | p = self.applied[-1].name |
|
1067 | 1206 | try: |
|
1068 | 1207 | end = self.series.index(p) |
|
1069 | 1208 | except ValueError: |
|
1070 | 1209 | return 0 |
|
1071 | return end + 1 | |
|
1072 | return end | |
|
1210 | return next(end + 1) | |
|
1211 | return next(end) | |
|
1073 | 1212 | |
|
1074 | 1213 | def qapplied(self, repo, patch=None): |
|
1075 | 1214 | if patch and patch not in self.series: |
@@ -1372,6 +1511,51 def fold(ui, repo, *files, **opts): | |||
|
1372 | 1511 | |
|
1373 | 1512 | q.save_dirty() |
|
1374 | 1513 | |
|
1514 | def guard(ui, repo, *args, **opts): | |
|
1515 | '''set or print guards for a patch | |
|
1516 | ||
|
1517 | guards control whether a patch can be pushed. a patch with no | |
|
1518 | guards is aways pushed. a patch with posative guard ("+foo") is | |
|
1519 | pushed only if qselect command enables guard "foo". a patch with | |
|
1520 | nagative guard ("-foo") is never pushed if qselect command enables | |
|
1521 | guard "foo". | |
|
1522 | ||
|
1523 | with no arguments, default is to print current active guards. | |
|
1524 | with arguments, set active guards for patch. | |
|
1525 | ||
|
1526 | to set nagative guard "-foo" on topmost patch ("--" is needed so | |
|
1527 | hg will not interpret "-foo" as argument): | |
|
1528 | hg qguard -- -foo | |
|
1529 | ||
|
1530 | to set guards on other patch: | |
|
1531 | hg qguard other.patch +2.6.17 -stable | |
|
1532 | ''' | |
|
1533 | def status(idx): | |
|
1534 | guards = q.series_guards[idx] or ['unguarded'] | |
|
1535 | ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards))) | |
|
1536 | q = repo.mq | |
|
1537 | patch = None | |
|
1538 | args = list(args) | |
|
1539 | if opts['list']: | |
|
1540 | if args or opts['none']: | |
|
1541 | raise util.Abort(_('cannot mix -l/--list with options or arguments')) | |
|
1542 | for i in xrange(len(q.series)): | |
|
1543 | status(i) | |
|
1544 | return | |
|
1545 | if not args or args[0][0:1] in '-+': | |
|
1546 | if not q.applied: | |
|
1547 | raise util.Abort(_('no patches applied')) | |
|
1548 | patch = q.applied[-1].name | |
|
1549 | if patch is None and args[0][0:1] not in '-+': | |
|
1550 | patch = args.pop(0) | |
|
1551 | if patch is None: | |
|
1552 | raise util.Abort(_('no patch to work with')) | |
|
1553 | if args or opts['none']: | |
|
1554 | q.set_guards(q.find_series(patch), args) | |
|
1555 | q.save_dirty() | |
|
1556 | else: | |
|
1557 | status(q.series.index(q.lookup(patch))) | |
|
1558 | ||
|
1375 | 1559 | def header(ui, repo, patch=None): |
|
1376 | 1560 | """Print the header of the topmost or specified patch""" |
|
1377 | 1561 | q = repo.mq |
@@ -1546,6 +1730,69 def strip(ui, repo, rev, **opts): | |||
|
1546 | 1730 | repo.mq.strip(repo, rev, backup=backup) |
|
1547 | 1731 | return 0 |
|
1548 | 1732 | |
|
1733 | def select(ui, repo, *args, **opts): | |
|
1734 | '''set or print guarded patches to push | |
|
1735 | ||
|
1736 | use qguard command to set or print guards on patch. then use | |
|
1737 | qselect to tell mq which guards to use. example: | |
|
1738 | ||
|
1739 | qguard foo.patch -stable (nagative guard) | |
|
1740 | qguard bar.patch +stable (posative guard) | |
|
1741 | qselect stable | |
|
1742 | ||
|
1743 | this sets "stable" guard. mq will skip foo.patch (because it has | |
|
1744 | nagative match) but push bar.patch (because it has posative | |
|
1745 | match). | |
|
1746 | ||
|
1747 | with no arguments, default is to print current active guards. | |
|
1748 | with arguments, set active guards as given. | |
|
1749 | ||
|
1750 | use -n/--none to deactivate guards (no other arguments needed). | |
|
1751 | when no guards active, patches with posative guards are skipped, | |
|
1752 | patches with nagative guards are pushed. | |
|
1753 | ||
|
1754 | use -s/--series to print list of all guards in series file (no | |
|
1755 | other arguments needed). use -v for more information.''' | |
|
1756 | ||
|
1757 | q = repo.mq | |
|
1758 | guards = q.active() | |
|
1759 | if args or opts['none']: | |
|
1760 | q.set_active(args) | |
|
1761 | q.save_dirty() | |
|
1762 | if not args: | |
|
1763 | ui.status(_('guards deactivated\n')) | |
|
1764 | if q.series: | |
|
1765 | pushable = [p for p in q.unapplied(repo) if q.pushable(p[0])[0]] | |
|
1766 | ui.status(_('%d of %d unapplied patches active\n') % | |
|
1767 | (len(pushable), len(q.series))) | |
|
1768 | elif opts['series']: | |
|
1769 | guards = {} | |
|
1770 | noguards = 0 | |
|
1771 | for gs in q.series_guards: | |
|
1772 | if not gs: | |
|
1773 | noguards += 1 | |
|
1774 | for g in gs: | |
|
1775 | guards.setdefault(g, 0) | |
|
1776 | guards[g] += 1 | |
|
1777 | if ui.verbose: | |
|
1778 | guards['NONE'] = noguards | |
|
1779 | guards = guards.items() | |
|
1780 | guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:])) | |
|
1781 | if guards: | |
|
1782 | ui.note(_('guards in series file:\n')) | |
|
1783 | for guard, count in guards: | |
|
1784 | ui.note('%2d ' % count) | |
|
1785 | ui.write(guard, '\n') | |
|
1786 | else: | |
|
1787 | ui.note(_('no guards in series file\n')) | |
|
1788 | else: | |
|
1789 | if guards: | |
|
1790 | ui.note(_('active guards:\n')) | |
|
1791 | for g in guards: | |
|
1792 | ui.write(g, '\n') | |
|
1793 | else: | |
|
1794 | ui.write(_('no active guards\n')) | |
|
1795 | ||
|
1549 | 1796 | def version(ui, q=None): |
|
1550 | 1797 | """print the version number of the mq extension""" |
|
1551 | 1798 | ui.write("mq version %s\n" % versionstr) |
@@ -1605,6 +1852,9 cmdtable = { | |||
|
1605 | 1852 | ('m', 'message', '', _('set patch header to <text>')), |
|
1606 | 1853 | ('l', 'logfile', '', _('set patch header to contents of <file>'))], |
|
1607 | 1854 | 'hg qfold [-e] [-m <text>] [-l <file] PATCH...'), |
|
1855 | 'qguard': (guard, [('l', 'list', None, _('list all patches and guards')), | |
|
1856 | ('n', 'none', None, _('drop all guards'))], | |
|
1857 | 'hg qguard [PATCH] [+GUARD...] [-GUARD...]'), | |
|
1608 | 1858 | 'qheader': (header, [], |
|
1609 | 1859 | _('hg qheader [PATCH]')), |
|
1610 | 1860 | "^qimport": |
@@ -1662,6 +1912,10 cmdtable = { | |||
|
1662 | 1912 | ('e', 'empty', None, 'clear queue status file'), |
|
1663 | 1913 | ('f', 'force', None, 'force copy')], |
|
1664 | 1914 | 'hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'), |
|
1915 | "qselect": (select, | |
|
1916 | [('n', 'none', None, _('disable all guards')), | |
|
1917 | ('s', 'series', None, _('list all guards in series file'))], | |
|
1918 | 'hg qselect [GUARDS]'), | |
|
1665 | 1919 | "qseries": |
|
1666 | 1920 | (series, |
|
1667 | 1921 | [('m', 'missing', None, 'print patches not in series'), |
@@ -30,6 +30,7 list of commands (use "hg help -v mq" to | |||
|
30 | 30 | qdelete remove a patch from the series file |
|
31 | 31 | qdiff diff of the current patch |
|
32 | 32 | qfold fold the named patches into the current patch |
|
33 | qguard set or print guards for a patch | |
|
33 | 34 | qheader Print the header of the topmost or specified patch |
|
34 | 35 | qimport import a patch |
|
35 | 36 | qinit init a new queue repository |
@@ -42,6 +43,7 list of commands (use "hg help -v mq" to | |||
|
42 | 43 | qrename rename a patch |
|
43 | 44 | qrestore restore the queue state saved by a rev |
|
44 | 45 | qsave save current queue state |
|
46 | qselect set or print guarded patches to push | |
|
45 | 47 | qseries print the entire series file |
|
46 | 48 | qtop print the name of the current patch |
|
47 | 49 | qunapplied print the patches not yet applied |
General Comments 0
You need to be logged in to leave comments.
Login now