Show More
@@ -0,0 +1,220 b'' | |||
|
1 | $ cat >> $HGRCPATH << EOF | |
|
2 | > [extensions] | |
|
3 | > show = | |
|
4 | > EOF | |
|
5 | ||
|
6 | $ hg init repo0 | |
|
7 | $ cd repo0 | |
|
8 | ||
|
9 | Empty repo / no checkout results in error | |
|
10 | ||
|
11 | $ hg show stack | |
|
12 | abort: stack view only available when there is a working directory | |
|
13 | [255] | |
|
14 | ||
|
15 | Stack displays single draft changeset as root revision | |
|
16 | ||
|
17 | $ echo 0 > foo | |
|
18 | $ hg -q commit -A -m 'commit 0' | |
|
19 | $ hg show stack | |
|
20 | @ 9f171 commit 0 | |
|
21 | ||
|
22 | Stack displays multiple draft changesets | |
|
23 | ||
|
24 | $ echo 1 > foo | |
|
25 | $ hg commit -m 'commit 1' | |
|
26 | $ echo 2 > foo | |
|
27 | $ hg commit -m 'commit 2' | |
|
28 | $ echo 3 > foo | |
|
29 | $ hg commit -m 'commit 3' | |
|
30 | $ echo 4 > foo | |
|
31 | $ hg commit -m 'commit 4' | |
|
32 | $ hg show stack | |
|
33 | @ 2737b commit 4 | |
|
34 | o d1a69 commit 3 | |
|
35 | o 128c8 commit 2 | |
|
36 | o 181cc commit 1 | |
|
37 | o 9f171 commit 0 | |
|
38 | ||
|
39 | Public parent of draft base is displayed, separated from stack | |
|
40 | ||
|
41 | $ hg phase --public -r 0 | |
|
42 | $ hg show stack | |
|
43 | @ 2737b commit 4 | |
|
44 | o d1a69 commit 3 | |
|
45 | o 128c8 commit 2 | |
|
46 | o 181cc commit 1 | |
|
47 | / (stack base) | |
|
48 | o 9f171 commit 0 | |
|
49 | ||
|
50 | $ hg phase --public -r 1 | |
|
51 | $ hg show stack | |
|
52 | @ 2737b commit 4 | |
|
53 | o d1a69 commit 3 | |
|
54 | o 128c8 commit 2 | |
|
55 | / (stack base) | |
|
56 | o 181cc commit 1 | |
|
57 | ||
|
58 | Draft descendants are shown | |
|
59 | ||
|
60 | $ hg -q up 2 | |
|
61 | $ hg show stack | |
|
62 | o 2737b commit 4 | |
|
63 | o d1a69 commit 3 | |
|
64 | @ 128c8 commit 2 | |
|
65 | / (stack base) | |
|
66 | o 181cc commit 1 | |
|
67 | ||
|
68 | $ hg -q up 3 | |
|
69 | $ hg show stack | |
|
70 | o 2737b commit 4 | |
|
71 | @ d1a69 commit 3 | |
|
72 | o 128c8 commit 2 | |
|
73 | / (stack base) | |
|
74 | o 181cc commit 1 | |
|
75 | ||
|
76 | working dir on public changeset should display special message | |
|
77 | ||
|
78 | $ hg -q up 1 | |
|
79 | $ hg show stack | |
|
80 | (empty stack; working directory is a published changeset) | |
|
81 | ||
|
82 | Branch point in descendants displayed at top of graph | |
|
83 | ||
|
84 | $ hg -q up 3 | |
|
85 | $ echo b > foo | |
|
86 | $ hg commit -m 'commit 5 (new dag branch)' | |
|
87 | created new head | |
|
88 | $ hg -q up 2 | |
|
89 | $ hg show stack | |
|
90 | \ / (multiple children) | |
|
91 | | | |
|
92 | o d1a69 commit 3 | |
|
93 | @ 128c8 commit 2 | |
|
94 | / (stack base) | |
|
95 | o 181cc commit 1 | |
|
96 | ||
|
97 | $ cd .. | |
|
98 | ||
|
99 | Base is stopped at merges | |
|
100 | ||
|
101 | $ hg init merge-base | |
|
102 | $ cd merge-base | |
|
103 | $ echo 0 > foo | |
|
104 | $ hg -q commit -A -m initial | |
|
105 | $ echo h1 > foo | |
|
106 | $ hg commit -m 'head 1' | |
|
107 | $ hg -q up 0 | |
|
108 | $ echo h2 > foo | |
|
109 | $ hg -q commit -m 'head 2' | |
|
110 | $ hg phase --public -r 0:tip | |
|
111 | $ hg -q up 1 | |
|
112 | $ hg merge -t :local 2 | |
|
113 | 0 files updated, 1 files merged, 0 files removed, 0 files unresolved | |
|
114 | (branch merge, don't forget to commit) | |
|
115 | $ hg commit -m 'merge heads' | |
|
116 | ||
|
117 | TODO doesn't yet handle case where wdir is a draft merge | |
|
118 | ||
|
119 | $ hg show stack | |
|
120 | @ 8ee90 merge heads | |
|
121 | / (stack base) | |
|
122 | o 59478 head 1 | |
|
123 | ||
|
124 | $ echo d1 > foo | |
|
125 | $ hg commit -m 'draft 1' | |
|
126 | $ echo d2 > foo | |
|
127 | $ hg commit -m 'draft 2' | |
|
128 | ||
|
129 | $ hg show stack | |
|
130 | @ 430d5 draft 2 | |
|
131 | o 787b1 draft 1 | |
|
132 | / (stack base) | |
|
133 | o 8ee90 merge heads | |
|
134 | ||
|
135 | $ cd .. | |
|
136 | ||
|
137 | Now move on to stacks when there are more commits after the base branchpoint | |
|
138 | ||
|
139 | $ hg init public-rebase | |
|
140 | $ cd public-rebase | |
|
141 | $ echo 0 > foo | |
|
142 | $ hg -q commit -A -m 'base' | |
|
143 | $ hg phase --public -r . | |
|
144 | $ echo d1 > foo | |
|
145 | $ hg commit -m 'draft 1' | |
|
146 | $ echo d2 > foo | |
|
147 | $ hg commit -m 'draft 2' | |
|
148 | $ hg -q up 0 | |
|
149 | $ echo 1 > foo | |
|
150 | $ hg commit -m 'new 1' | |
|
151 | created new head | |
|
152 | $ echo 2 > foo | |
|
153 | $ hg commit -m 'new 2' | |
|
154 | $ hg -q up 2 | |
|
155 | ||
|
156 | Newer draft heads don't impact output | |
|
157 | ||
|
158 | $ hg show stack | |
|
159 | @ eaffc draft 2 | |
|
160 | o 2b218 draft 1 | |
|
161 | / (stack base) | |
|
162 | o b66bb base | |
|
163 | ||
|
164 | Newer public heads are rendered | |
|
165 | ||
|
166 | $ hg phase --public -r '::tip' | |
|
167 | ||
|
168 | $ hg show stack | |
|
169 | o baa4b new 2 | |
|
170 | / (2 commits ahead) | |
|
171 | : | |
|
172 | : (stack head) | |
|
173 | : @ eaffc draft 2 | |
|
174 | : o 2b218 draft 1 | |
|
175 | :/ (stack base) | |
|
176 | o b66bb base | |
|
177 | ||
|
178 | If rebase is available, we show a hint how to rebase to that head | |
|
179 | ||
|
180 | $ hg --config extensions.rebase= show stack | |
|
181 | o baa4b new 2 | |
|
182 | / (2 commits ahead; hg rebase --source 2b218 --dest baa4b) | |
|
183 | : | |
|
184 | : (stack head) | |
|
185 | : @ eaffc draft 2 | |
|
186 | : o 2b218 draft 1 | |
|
187 | :/ (stack base) | |
|
188 | o b66bb base | |
|
189 | ||
|
190 | Similar tests but for multiple heads | |
|
191 | ||
|
192 | $ hg -q up 0 | |
|
193 | $ echo h2 > foo | |
|
194 | $ hg -q commit -m 'new head 2' | |
|
195 | $ hg phase --public -r . | |
|
196 | $ hg -q up 2 | |
|
197 | ||
|
198 | $ hg show stack | |
|
199 | o baa4b new 2 | |
|
200 | / (2 commits ahead) | |
|
201 | : o 9a848 new head 2 | |
|
202 | :/ (1 commits ahead) | |
|
203 | : | |
|
204 | : (stack head) | |
|
205 | : @ eaffc draft 2 | |
|
206 | : o 2b218 draft 1 | |
|
207 | :/ (stack base) | |
|
208 | o b66bb base | |
|
209 | ||
|
210 | $ hg --config extensions.rebase= show stack | |
|
211 | o baa4b new 2 | |
|
212 | / (2 commits ahead; hg rebase --source 2b218 --dest baa4b) | |
|
213 | : o 9a848 new head 2 | |
|
214 | :/ (1 commits ahead; hg rebase --source 2b218 --dest 9a848) | |
|
215 | : | |
|
216 | : (stack head) | |
|
217 | : @ eaffc draft 2 | |
|
218 | : o 2b218 draft 1 | |
|
219 | :/ (stack base) | |
|
220 | o b66bb base |
@@ -32,9 +32,11 b' from mercurial.node import nullrev' | |||
|
32 | 32 | from mercurial import ( |
|
33 | 33 | cmdutil, |
|
34 | 34 | commands, |
|
35 | destutil, | |
|
35 | 36 | error, |
|
36 | 37 | formatter, |
|
37 | 38 | graphmod, |
|
39 | phases, | |
|
38 | 40 | pycompat, |
|
39 | 41 | registrar, |
|
40 | 42 | revset, |
@@ -171,6 +173,166 b' def showbookmarks(ui, repo, fm):' | |||
|
171 | 173 | fm.data(active=bm == active, |
|
172 | 174 | longestbookmarklen=longestname) |
|
173 | 175 | |
|
176 | @showview('stack', csettopic='stack') | |
|
177 | def showstack(ui, repo, displayer): | |
|
178 | """current line of work""" | |
|
179 | wdirctx = repo['.'] | |
|
180 | if wdirctx.rev() == nullrev: | |
|
181 | raise error.Abort(_('stack view only available when there is a ' | |
|
182 | 'working directory')) | |
|
183 | ||
|
184 | if wdirctx.phase() == phases.public: | |
|
185 | ui.write(_('(empty stack; working directory is a published ' | |
|
186 | 'changeset)\n')) | |
|
187 | return | |
|
188 | ||
|
189 | # TODO extract "find stack" into a function to facilitate | |
|
190 | # customization and reuse. | |
|
191 | ||
|
192 | baserev = destutil.stackbase(ui, repo) | |
|
193 | basectx = None | |
|
194 | ||
|
195 | if baserev is None: | |
|
196 | baserev = wdirctx.rev() | |
|
197 | stackrevs = {wdirctx.rev()} | |
|
198 | else: | |
|
199 | stackrevs = set(repo.revs('%d::.', baserev)) | |
|
200 | ||
|
201 | ctx = repo[baserev] | |
|
202 | if ctx.p1().rev() != nullrev: | |
|
203 | basectx = ctx.p1() | |
|
204 | ||
|
205 | # And relevant descendants. | |
|
206 | branchpointattip = False | |
|
207 | cl = repo.changelog | |
|
208 | ||
|
209 | for rev in cl.descendants([wdirctx.rev()]): | |
|
210 | ctx = repo[rev] | |
|
211 | ||
|
212 | # Will only happen if . is public. | |
|
213 | if ctx.phase() == phases.public: | |
|
214 | break | |
|
215 | ||
|
216 | stackrevs.add(ctx.rev()) | |
|
217 | ||
|
218 | if len(ctx.children()) > 1: | |
|
219 | branchpointattip = True | |
|
220 | break | |
|
221 | ||
|
222 | stackrevs = list(reversed(sorted(stackrevs))) | |
|
223 | ||
|
224 | # Find likely target heads for the current stack. These are likely | |
|
225 | # merge or rebase targets. | |
|
226 | if basectx: | |
|
227 | # TODO make this customizable? | |
|
228 | newheads = set(repo.revs('heads(%d::) - %ld - not public()', | |
|
229 | basectx.rev(), stackrevs)) | |
|
230 | else: | |
|
231 | newheads = set() | |
|
232 | ||
|
233 | try: | |
|
234 | cmdutil.findcmd('rebase', commands.table) | |
|
235 | haverebase = True | |
|
236 | except error.UnknownCommand: | |
|
237 | haverebase = False | |
|
238 | ||
|
239 | # TODO use templating. | |
|
240 | # TODO consider using graphmod. But it may not be necessary given | |
|
241 | # our simplicity and the customizations required. | |
|
242 | # TODO use proper graph symbols from graphmod | |
|
243 | ||
|
244 | shortesttmpl = formatter.maketemplater(ui, '{shortest(node, 5)}') | |
|
245 | def shortest(ctx): | |
|
246 | return shortesttmpl.render({'ctx': ctx, 'node': ctx.hex()}) | |
|
247 | ||
|
248 | # We write out new heads to aid in DAG awareness and to help with decision | |
|
249 | # making on how the stack should be reconciled with commits made since the | |
|
250 | # branch point. | |
|
251 | if newheads: | |
|
252 | # Calculate distance from base so we can render the count and so we can | |
|
253 | # sort display order by commit distance. | |
|
254 | revdistance = {} | |
|
255 | for head in newheads: | |
|
256 | # There is some redundancy in DAG traversal here and therefore | |
|
257 | # room to optimize. | |
|
258 | ancestors = cl.ancestors([head], stoprev=basectx.rev()) | |
|
259 | revdistance[head] = len(list(ancestors)) | |
|
260 | ||
|
261 | sourcectx = repo[stackrevs[-1]] | |
|
262 | ||
|
263 | sortedheads = sorted(newheads, key=lambda x: revdistance[x], | |
|
264 | reverse=True) | |
|
265 | ||
|
266 | for i, rev in enumerate(sortedheads): | |
|
267 | ctx = repo[rev] | |
|
268 | ||
|
269 | if i: | |
|
270 | ui.write(': ') | |
|
271 | else: | |
|
272 | ui.write(' ') | |
|
273 | ||
|
274 | ui.write(('o ')) | |
|
275 | displayer.show(ctx) | |
|
276 | displayer.flush(ctx) | |
|
277 | ui.write('\n') | |
|
278 | ||
|
279 | if i: | |
|
280 | ui.write(':/') | |
|
281 | else: | |
|
282 | ui.write(' /') | |
|
283 | ||
|
284 | ui.write(' (') | |
|
285 | ui.write(_('%d commits ahead') % revdistance[rev], | |
|
286 | label='stack.commitdistance') | |
|
287 | ||
|
288 | if haverebase: | |
|
289 | # TODO may be able to omit --source in some scenarios | |
|
290 | ui.write('; ') | |
|
291 | ui.write(('hg rebase --source %s --dest %s' % ( | |
|
292 | shortest(sourcectx), shortest(ctx))), | |
|
293 | label='stack.rebasehint') | |
|
294 | ||
|
295 | ui.write(')\n') | |
|
296 | ||
|
297 | ui.write(':\n: ') | |
|
298 | ui.write(_('(stack head)\n'), label='stack.label') | |
|
299 | ||
|
300 | if branchpointattip: | |
|
301 | ui.write(' \\ / ') | |
|
302 | ui.write(_('(multiple children)\n'), label='stack.label') | |
|
303 | ui.write(' |\n') | |
|
304 | ||
|
305 | for rev in stackrevs: | |
|
306 | ctx = repo[rev] | |
|
307 | symbol = '@' if rev == wdirctx.rev() else 'o' | |
|
308 | ||
|
309 | if newheads: | |
|
310 | ui.write(': ') | |
|
311 | else: | |
|
312 | ui.write(' ') | |
|
313 | ||
|
314 | ui.write(symbol, ' ') | |
|
315 | displayer.show(ctx) | |
|
316 | displayer.flush(ctx) | |
|
317 | ui.write('\n') | |
|
318 | ||
|
319 | # TODO display histedit hint? | |
|
320 | ||
|
321 | if basectx: | |
|
322 | # Vertically and horizontally separate stack base from parent | |
|
323 | # to reinforce stack boundary. | |
|
324 | if newheads: | |
|
325 | ui.write(':/ ') | |
|
326 | else: | |
|
327 | ui.write(' / ') | |
|
328 | ||
|
329 | ui.write(_('(stack base)'), '\n', label='stack.label') | |
|
330 | ui.write(('o ')) | |
|
331 | ||
|
332 | displayer.show(basectx) | |
|
333 | displayer.flush(basectx) | |
|
334 | ui.write('\n') | |
|
335 | ||
|
174 | 336 | @revsetpredicate('_underway([commitage[, headage]])') |
|
175 | 337 | def underwayrevset(repo, subset, x): |
|
176 | 338 | args = revset.getargsdict(x, 'underway', 'commitage headage') |
@@ -354,6 +354,12 b' def desthistedit(ui, repo):' | |||
|
354 | 354 | |
|
355 | 355 | return None |
|
356 | 356 | |
|
357 | def stackbase(ui, repo): | |
|
358 | # The histedit default base stops at public changesets, branchpoints, | |
|
359 | # and merges, which is exactly what we want for a stack. | |
|
360 | revs = scmutil.revrange(repo, [histeditdefaultrevset]) | |
|
361 | return revs.last() if revs else None | |
|
362 | ||
|
357 | 363 | def _statusotherbook(ui, repo): |
|
358 | 364 | bmheads = bookmarks.headsforactive(repo) |
|
359 | 365 | curhead = repo[repo._activebookmark].node() |
@@ -6,6 +6,7 b'' | |||
|
6 | 6 | showbookmarks = '{if(active, "*", " ")} {pad(bookmark, longestbookmarklen + 4)}{shortest(node, 5)}\n' |
|
7 | 7 | |
|
8 | 8 | showwork = '{cset_shortnode}{namespaces % cset_namespace} {cset_shortdesc}' |
|
9 | showstack = '{showwork}' | |
|
9 | 10 | |
|
10 | 11 | cset_shortnode = '{label("log.changeset changeset.{phase}", shortest(node, 5))}' |
|
11 | 12 |
@@ -11,6 +11,7 b' No arguments shows available views' | |||
|
11 | 11 | available views: |
|
12 | 12 | |
|
13 | 13 | bookmarks -- bookmarks and their associated changeset |
|
14 | stack -- current line of work | |
|
14 | 15 | work -- changesets that aren't finished |
|
15 | 16 | |
|
16 | 17 | abort: no view requested |
@@ -40,6 +41,8 b' No arguments shows available views' | |||
|
40 | 41 | |
|
41 | 42 | bookmarks bookmarks and their associated changeset |
|
42 | 43 | |
|
44 | stack current line of work | |
|
45 | ||
|
43 | 46 | work changesets that aren't finished |
|
44 | 47 | |
|
45 | 48 | (use 'hg help -e show' to show help for the show extension) |
General Comments 0
You need to be logged in to leave comments.
Login now