169 fm.write('bookmark', '%s', bm) |
171 fm.write('bookmark', '%s', bm) |
170 fm.write('node', fm.hexfunc(node), fm.hexfunc(node)) |
172 fm.write('node', fm.hexfunc(node), fm.hexfunc(node)) |
171 fm.data(active=bm == active, |
173 fm.data(active=bm == active, |
172 longestbookmarklen=longestname) |
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 @revsetpredicate('_underway([commitage[, headage]])') |
336 @revsetpredicate('_underway([commitage[, headage]])') |
175 def underwayrevset(repo, subset, x): |
337 def underwayrevset(repo, subset, x): |
176 args = revset.getargsdict(x, 'underway', 'commitage headage') |
338 args = revset.getargsdict(x, 'underway', 'commitage headage') |
177 if 'commitage' not in args: |
339 if 'commitage' not in args: |
178 args['commitage'] = None |
340 args['commitage'] = None |