2.9 KB105 lines
Blame
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
8import type {Dag} from '../previews';
9import type {CommitInfo} from '../types';
10
11import {firstLine} from 'shared/utils';
12import {CommitPreview} from '../previews';
13import {exactRevset} from '../types';
14import {Operation} from './Operation';
15
16/**
17 * Returns [bottom, top] of an array.
18 */
19function ends<T>(range: Array<T>): [T, T] {
20 return [range[0], range[range.length - 1]];
21}
22
23export class FoldOperation extends Operation {
24 constructor(
25 private foldRange: Array<CommitInfo>,
26 newMessage: string,
27 ) {
28 super('FoldOperation');
29 this.newTitle = firstLine(newMessage);
30 this.newDescription = newMessage.substring(firstLine(newMessage).length + 1);
31 }
32 private newTitle: string;
33 private newDescription: string;
34
35 static opName = 'Fold';
36
37 getArgs() {
38 const [bottom, top] = ends(this.foldRange);
39 return [
40 'fold',
41 '--exact',
42 exactRevset(`${bottom.hash}::${top.hash}`),
43 '--message',
44 `${this.newTitle}\n${this.newDescription}`,
45 ];
46 }
47
48 public getFoldRange(): Array<CommitInfo> {
49 return this.foldRange;
50 }
51 public getFoldedMessage(): [string, string] {
52 return [this.newTitle, this.newDescription];
53 }
54
55 previewDag(dag: Dag): Dag {
56 return this.calculateDagPreview(dag, true);
57 }
58
59 optimisticDag(dag: Dag): Dag {
60 return this.calculateDagPreview(dag, false);
61 }
62
63 private calculateDagPreview(dag: Dag, isPreview: boolean): Dag {
64 const hashes = this.foldRange.map(info => info.hash);
65 const top = hashes.at(-1);
66 const parents = dag.get(hashes.at(0))?.parents;
67 if (top == null || parents == null) {
68 return dag;
69 }
70 const hash = getFoldRangeCommitHash(this.foldRange, isPreview);
71 const bookmarks = hashes.flatMap(h => dag.get(h)?.bookmarks ?? []).sort();
72 return dag
73 .replaceWith(hashes, (h, c) => {
74 if (h !== top && c == null) {
75 return undefined;
76 }
77 return c?.merge({
78 date: new Date(),
79 hash,
80 bookmarks,
81 title: this.newTitle,
82 description: this.newDescription,
83 previewType: isPreview ? CommitPreview.FOLD_PREVIEW : CommitPreview.FOLD,
84 parents,
85 });
86 })
87 .replaceWith(dag.children(top), (_h, c) => {
88 return c?.set(
89 'parents',
90 c.parents.map(p => (p === top ? hash : p)),
91 );
92 });
93 }
94}
95
96export const FOLD_COMMIT_PREVIEW_HASH_PREFIX = 'OPTIMISTIC_FOLDED_PREVIEW_';
97export const FOLD_COMMIT_OPTIMISTIC_HASH_PREFIX = 'OPTIMISTIC_FOLDED_';
98export function getFoldRangeCommitHash(range: Array<CommitInfo>, isPreview: boolean): string {
99 const [bottom, top] = ends(range);
100 return (
101 (isPreview ? FOLD_COMMIT_PREVIEW_HASH_PREFIX : FOLD_COMMIT_OPTIMISTIC_HASH_PREFIX) +
102 `${bottom.hash}:${top.hash}`
103 );
104}
105