addons/isl/src/operations/FoldOperation.tsblame
View source
b69ab311/**
b69ab312 * Copyright (c) Meta Platforms, Inc. and affiliates.
b69ab313 *
b69ab314 * This source code is licensed under the MIT license found in the
b69ab315 * LICENSE file in the root directory of this source tree.
b69ab316 */
b69ab317
b69ab318import type {Dag} from '../previews';
b69ab319import type {CommitInfo} from '../types';
b69ab3110
b69ab3111import {firstLine} from 'shared/utils';
b69ab3112import {CommitPreview} from '../previews';
b69ab3113import {exactRevset} from '../types';
b69ab3114import {Operation} from './Operation';
b69ab3115
b69ab3116/**
b69ab3117 * Returns [bottom, top] of an array.
b69ab3118 */
b69ab3119function ends<T>(range: Array<T>): [T, T] {
b69ab3120 return [range[0], range[range.length - 1]];
b69ab3121}
b69ab3122
b69ab3123export class FoldOperation extends Operation {
b69ab3124 constructor(
b69ab3125 private foldRange: Array<CommitInfo>,
b69ab3126 newMessage: string,
b69ab3127 ) {
b69ab3128 super('FoldOperation');
b69ab3129 this.newTitle = firstLine(newMessage);
b69ab3130 this.newDescription = newMessage.substring(firstLine(newMessage).length + 1);
b69ab3131 }
b69ab3132 private newTitle: string;
b69ab3133 private newDescription: string;
b69ab3134
b69ab3135 static opName = 'Fold';
b69ab3136
b69ab3137 getArgs() {
b69ab3138 const [bottom, top] = ends(this.foldRange);
b69ab3139 return [
b69ab3140 'fold',
b69ab3141 '--exact',
b69ab3142 exactRevset(`${bottom.hash}::${top.hash}`),
b69ab3143 '--message',
b69ab3144 `${this.newTitle}\n${this.newDescription}`,
b69ab3145 ];
b69ab3146 }
b69ab3147
b69ab3148 public getFoldRange(): Array<CommitInfo> {
b69ab3149 return this.foldRange;
b69ab3150 }
b69ab3151 public getFoldedMessage(): [string, string] {
b69ab3152 return [this.newTitle, this.newDescription];
b69ab3153 }
b69ab3154
b69ab3155 previewDag(dag: Dag): Dag {
b69ab3156 return this.calculateDagPreview(dag, true);
b69ab3157 }
b69ab3158
b69ab3159 optimisticDag(dag: Dag): Dag {
b69ab3160 return this.calculateDagPreview(dag, false);
b69ab3161 }
b69ab3162
b69ab3163 private calculateDagPreview(dag: Dag, isPreview: boolean): Dag {
b69ab3164 const hashes = this.foldRange.map(info => info.hash);
b69ab3165 const top = hashes.at(-1);
b69ab3166 const parents = dag.get(hashes.at(0))?.parents;
b69ab3167 if (top == null || parents == null) {
b69ab3168 return dag;
b69ab3169 }
b69ab3170 const hash = getFoldRangeCommitHash(this.foldRange, isPreview);
b69ab3171 const bookmarks = hashes.flatMap(h => dag.get(h)?.bookmarks ?? []).sort();
b69ab3172 return dag
b69ab3173 .replaceWith(hashes, (h, c) => {
b69ab3174 if (h !== top && c == null) {
b69ab3175 return undefined;
b69ab3176 }
b69ab3177 return c?.merge({
b69ab3178 date: new Date(),
b69ab3179 hash,
b69ab3180 bookmarks,
b69ab3181 title: this.newTitle,
b69ab3182 description: this.newDescription,
b69ab3183 previewType: isPreview ? CommitPreview.FOLD_PREVIEW : CommitPreview.FOLD,
b69ab3184 parents,
b69ab3185 });
b69ab3186 })
b69ab3187 .replaceWith(dag.children(top), (_h, c) => {
b69ab3188 return c?.set(
b69ab3189 'parents',
b69ab3190 c.parents.map(p => (p === top ? hash : p)),
b69ab3191 );
b69ab3192 });
b69ab3193 }
b69ab3194}
b69ab3195
b69ab3196export const FOLD_COMMIT_PREVIEW_HASH_PREFIX = 'OPTIMISTIC_FOLDED_PREVIEW_';
b69ab3197export const FOLD_COMMIT_OPTIMISTIC_HASH_PREFIX = 'OPTIMISTIC_FOLDED_';
b69ab3198export function getFoldRangeCommitHash(range: Array<CommitInfo>, isPreview: boolean): string {
b69ab3199 const [bottom, top] = ends(range);
b69ab31100 return (
b69ab31101 (isPreview ? FOLD_COMMIT_PREVIEW_HASH_PREFIX : FOLD_COMMIT_OPTIMISTIC_HASH_PREFIX) +
b69ab31102 `${bottom.hash}:${top.hash}`
b69ab31103 );
b69ab31104}