| 1 | /** |
| 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. |
| 3 | * |
| 4 | * This source code is licensed under the MIT license found in the |
| 5 | * LICENSE file in the root directory of this source tree. |
| 6 | */ |
| 7 | |
| 8 | import type {CommitStackState} from './commitStackState'; |
| 9 | |
| 10 | import {cached} from 'shared/LRU'; |
| 11 | import {firstLine, nullthrows} from 'shared/utils'; |
| 12 | import {Dag, DagCommitInfo} from '../dag/dag'; |
| 13 | import {WDIR_NODE, YOU_ARE_HERE_VIRTUAL_COMMIT} from '../dag/virtualCommit'; |
| 14 | |
| 15 | /** |
| 16 | * Calculate a virtual "Dag" purely from "stack". |
| 17 | * The virtual "Dag" does not have to be a subset of the main dag being displayed. |
| 18 | * This avoids race conditions and various issues when the "stack" does not |
| 19 | * match the main dag. |
| 20 | * The nodes (commit hashes) of the returned dag is the "key" of commits in |
| 21 | * the "stack", not real commit hashes. This is to support various stack edit |
| 22 | * operations like splitting, inserting new commits, etc. |
| 23 | */ |
| 24 | function calculateDagFromStackImpl(stack: CommitStackState): Dag { |
| 25 | let dag = new Dag(); |
| 26 | |
| 27 | // Figure out the "dot" from the initial exported stack. |
| 28 | // "dot" is the parent of "wdir()". |
| 29 | // The exported stack might not have "wdir()" if not requested. |
| 30 | // Run `sl debugexportstack -r '.+wdir()' | python3 -m json.tool` to get a sense of the output. |
| 31 | let dotNode: string | undefined = undefined; |
| 32 | let dotKey: string | undefined = undefined; |
| 33 | for (const exportedCommit of stack.originalStack) { |
| 34 | if (exportedCommit.node === WDIR_NODE) { |
| 35 | dotNode = exportedCommit.parents?.at(0); |
| 36 | break; |
| 37 | } |
| 38 | } |
| 39 | |
| 40 | if (dotNode != null) { |
| 41 | const maybeDotCommit = stack.stack.findLast(commit => commit.originalNodes.contains(dotNode)); |
| 42 | if (maybeDotCommit != null) { |
| 43 | dotKey = maybeDotCommit.key; |
| 44 | const wdirRev = stack.findLastRev(commit => commit.originalNodes.contains(WDIR_NODE)); |
| 45 | dag = dag.add([YOU_ARE_HERE_VIRTUAL_COMMIT.merge({parents: [dotKey], stackRev: wdirRev})]); |
| 46 | } |
| 47 | } |
| 48 | |
| 49 | // Insert commits from the stack. |
| 50 | // Since we've already inserted the "wdir()" commit, skip it from the stack. |
| 51 | dag = dag.add( |
| 52 | stack |
| 53 | .revs() |
| 54 | .filter(rev => !stack.get(rev)?.originalNodes?.contains(WDIR_NODE)) |
| 55 | .map(rev => { |
| 56 | const commit = nullthrows(stack.get(rev)); |
| 57 | return DagCommitInfo.fromCommitInfo({ |
| 58 | title: firstLine(commit.text), |
| 59 | hash: commit.key, |
| 60 | parents: commit.parents |
| 61 | .flatMap(parentRev => { |
| 62 | const parent = stack.get(parentRev); |
| 63 | return parent == null ? [] : [parent.key]; |
| 64 | }) |
| 65 | .toArray(), |
| 66 | phase: commit.immutableKind === 'hash' ? 'public' : 'draft', |
| 67 | author: commit.author, |
| 68 | date: new Date(commit.date.unix), |
| 69 | isDot: commit.key === dotKey, |
| 70 | stackRev: rev, |
| 71 | // Other fields are omitted for now, since nobody uses them yet. |
| 72 | }); |
| 73 | }), |
| 74 | ); |
| 75 | |
| 76 | return dag; |
| 77 | } |
| 78 | |
| 79 | /** |
| 80 | * Provides a `Dag` that just contains the `stack`. |
| 81 | * If `dotRev` is set, add a "YouAreHere" virtual commit as a child of the rev. |
| 82 | */ |
| 83 | export const calculateDagFromStack = cached(calculateDagFromStackImpl); |
| 84 | |