2.8 KB89 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 {PartialSelection} from '../partialSelection';
9import type {
10 ApplyUncommittedChangesPreviewsFuncType,
11 UncommittedChangesPreviewContext,
12} from '../previews';
13import type {CommandArg, RepoRelativePath, UncommittedChanges} from '../types';
14
15import {Operation} from './Operation';
16
17export class ShelveOperation extends Operation {
18 /**
19 * @param name the name of the shelved changes. This makes it easier to find the change later.
20 * @param filesPathsToCommit if provided, only these file paths will be included in the shelve operation. If undefined, ALL uncommitted changes are included. Paths should be relative to repo root.
21 */
22 constructor(
23 private name?: string,
24 private filesPathsToCommit?: Array<RepoRelativePath>,
25 ) {
26 super('ShelveOperation');
27 }
28
29 static opName = 'Shelve';
30
31 getArgs() {
32 const args: Array<CommandArg> = ['shelve', '--unknown'];
33 if (this.name) {
34 args.push('--name', this.name);
35 }
36 if (this.filesPathsToCommit) {
37 args.push(
38 ...this.filesPathsToCommit.map(file =>
39 // tag file arguments specially so the remote repo can convert them to the proper cwd-relative format.
40 ({
41 type: 'repo-relative-file' as const,
42 path: file,
43 }),
44 ),
45 );
46 }
47 return args;
48 }
49
50 makeOptimisticUncommittedChangesApplier?(
51 context: UncommittedChangesPreviewContext,
52 ): ApplyUncommittedChangesPreviewsFuncType | undefined {
53 const filesToCommit = new Set(this.filesPathsToCommit);
54 // optimistic state is over when there's no uncommitted changes that we wanted to shelve left
55 if (
56 context.uncommittedChanges.length === 0 ||
57 (filesToCommit.size > 0 &&
58 context.uncommittedChanges.every(change => !filesToCommit.has(change.path)))
59 ) {
60 return undefined;
61 }
62
63 const func: ApplyUncommittedChangesPreviewsFuncType = (changes: UncommittedChanges) => {
64 if (this.filesPathsToCommit != null) {
65 return changes.filter(change => !filesToCommit.has(change.path));
66 } else {
67 return [];
68 }
69 };
70 return func;
71 }
72}
73
74/** Find appropriate ShelveOperation based on selection. */
75export function getShelveOperation(
76 name: string | undefined,
77 selection: PartialSelection,
78 allFiles: Array<RepoRelativePath>,
79): ShelveOperation {
80 if (selection.hasChunkSelection()) {
81 throw new Error('Cannot shelve partially selected hunks.');
82 } else if (selection.isEverythingSelected(() => allFiles)) {
83 return new ShelveOperation(name);
84 } else {
85 const selectedFiles = allFiles.filter(path => selection.isFullyOrPartiallySelected(path));
86 return new ShelveOperation(name, selectedFiles);
87 }
88}
89