Skip to content

[rush] Expose hasUncommittedChanges flag on input snapshot #5222

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@microsoft/rush",
"comment": "Add `hasUncommittedChanges` to `IInputSnapshot` for use by plugins.",
"type": "none"
}
],
"packageName": "@microsoft/rush"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@rushstack/package-deps-hash",
"comment": "Add `getDetailedRepoState` API to expose `hasSubmodules` and `hasUncommittedChanges` in addition to the results returned by `getRepoState`.",
"type": "minor"
}
],
"packageName": "@rushstack/package-deps-hash"
}
10 changes: 10 additions & 0 deletions common/reviews/api/package-deps-hash.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
// @public
export function ensureGitMinimumVersion(gitPath?: string): void;

// @beta
export function getDetailedRepoStateAsync(rootDirectory: string, additionalRelativePathsToHash?: string[], gitPath?: string, filterPath?: string[]): Promise<IDetailedRepoState>;

// @public
export function getGitHashForFiles(filesToHash: string[], packagePath: string, gitPath?: string): Map<string, string>;

Expand All @@ -25,6 +28,13 @@ export function getRepoStateAsync(rootDirectory: string, additionalRelativePaths
// @beta
export function hashFilesAsync(rootDirectory: string, filesToHash: Iterable<string> | AsyncIterable<string>, gitPath?: string): Promise<Iterable<[string, string]>>;

// @beta
export interface IDetailedRepoState {
files: Map<string, string>;
hasSubmodules: boolean;
hasUncommittedChanges: boolean;
}

// @beta
export interface IFileDiffStatus {
// (undocumented)
Expand Down
1 change: 1 addition & 0 deletions common/reviews/api/rush-lib.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,7 @@ export interface IInputsSnapshot {
getOperationOwnStateHash(project: IRushConfigurationProjectForSnapshot, operationName?: string): string;
getTrackedFileHashesForOperation(project: IRushConfigurationProjectForSnapshot, operationName?: string): ReadonlyMap<string, string>;
readonly hashes: ReadonlyMap<string, string>;
readonly hasUncommittedChanges: boolean;
readonly rootDirectory: string;
}

Expand Down
54 changes: 52 additions & 2 deletions libraries/package-deps-hash/src/getRepoState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,49 @@ export async function getRepoStateAsync(
gitPath?: string,
filterPath?: string[]
): Promise<Map<string, string>> {
const { files } = await getDetailedRepoStateAsync(
rootDirectory,
additionalRelativePathsToHash,
gitPath,
filterPath
);

return files;
}

/**
* Information about the detailed state of the Git repository.
* @beta
*/
export interface IDetailedRepoState {
/**
* The Git file hashes for all files in the repository, including uncommitted changes.
*/
files: Map<string, string>;
/**
* A boolean indicating whether the repository has submodules.
*/
hasSubmodules: boolean;
/**
* A boolean indicating whether the repository has uncommitted changes.
*/
hasUncommittedChanges: boolean;
}

/**
* Gets the object hashes for all files in the Git repo, combining the current commit with working tree state.
* Uses async operations and runs all primary Git calls in parallel.
* @param rootDirectory - The root directory of the Git repository
* @param additionalRelativePathsToHash - Root-relative file paths to have Git hash and include in the results
* @param gitPath - The path to the Git executable
* @beta
*/
export async function getDetailedRepoStateAsync(
rootDirectory: string,
additionalRelativePathsToHash?: string[],
gitPath?: string,
filterPath?: string[]
): Promise<IDetailedRepoState> {
const statePromise: Promise<IGitTreeState> = spawnGitAsync(
gitPath,
STANDARD_GIT_OPTIONS.concat([
Expand Down Expand Up @@ -428,7 +471,10 @@ export async function getRepoStateAsync(
gitPath
);

const [{ files, submodules }] = await Promise.all([statePromise, locallyModifiedPromise]);
const [{ files, submodules }, locallyModifiedFiles] = await Promise.all([
statePromise,
locallyModifiedPromise
]);

// The result of "git hash-object" will be a list of file hashes delimited by newlines
for (const [filePath, hash] of await hashObjectPromise) {
Expand All @@ -453,7 +499,11 @@ export async function getRepoStateAsync(
}
}

return files;
return {
hasSubmodules,
hasUncommittedChanges: locallyModifiedFiles.size > 0,
files
};
}

/**
Expand Down
2 changes: 2 additions & 0 deletions libraries/package-deps-hash/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
export { getPackageDeps, getGitHashForFiles } from './getPackageDeps';
export {
type IFileDiffStatus,
type IDetailedRepoState,
getDetailedRepoStateAsync,
getRepoChanges,
getRepoRoot,
getRepoStateAsync,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,85 +1,113 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`getRepoStateAsync can handle adding one file 1`] = `
exports[`getDetailedRepoStateAsync can handle adding one file 1`] = `
Object {
"nestedTestProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576",
"nestedTestProject/src/file 1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34",
"testProject/a.txt": "2e65efe2a145dda7ee51d1741299f848e5bf752e",
"testProject/file 2.txt": "a385f754ec4fede884a4864d090064d9aeef8ccb",
"testProject/file1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34",
"testProject/file蝴蝶.txt": "ae814af81e16cb2ae8c57503c77e2cab6b5462ba",
"testProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576",
"files": Object {
"nestedTestProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576",
"nestedTestProject/src/file 1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34",
"testProject/a.txt": "2e65efe2a145dda7ee51d1741299f848e5bf752e",
"testProject/file 2.txt": "a385f754ec4fede884a4864d090064d9aeef8ccb",
"testProject/file1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34",
"testProject/file蝴蝶.txt": "ae814af81e16cb2ae8c57503c77e2cab6b5462ba",
"testProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576",
},
"hasSubmodules": false,
"hasUncommittedChanges": true,
}
`;

exports[`getRepoStateAsync can handle adding two files 1`] = `
exports[`getDetailedRepoStateAsync can handle adding two files 1`] = `
Object {
"nestedTestProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576",
"nestedTestProject/src/file 1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34",
"testProject/a.txt": "2e65efe2a145dda7ee51d1741299f848e5bf752e",
"testProject/b.txt": "2e65efe2a145dda7ee51d1741299f848e5bf752e",
"testProject/file 2.txt": "a385f754ec4fede884a4864d090064d9aeef8ccb",
"testProject/file1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34",
"testProject/file蝴蝶.txt": "ae814af81e16cb2ae8c57503c77e2cab6b5462ba",
"testProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576",
"files": Object {
"nestedTestProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576",
"nestedTestProject/src/file 1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34",
"testProject/a.txt": "2e65efe2a145dda7ee51d1741299f848e5bf752e",
"testProject/b.txt": "2e65efe2a145dda7ee51d1741299f848e5bf752e",
"testProject/file 2.txt": "a385f754ec4fede884a4864d090064d9aeef8ccb",
"testProject/file1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34",
"testProject/file蝴蝶.txt": "ae814af81e16cb2ae8c57503c77e2cab6b5462ba",
"testProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576",
},
"hasSubmodules": false,
"hasUncommittedChanges": true,
}
`;

exports[`getRepoStateAsync can handle changing one file 1`] = `
exports[`getDetailedRepoStateAsync can handle changing one file 1`] = `
Object {
"nestedTestProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576",
"nestedTestProject/src/file 1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34",
"testProject/file 2.txt": "a385f754ec4fede884a4864d090064d9aeef8ccb",
"testProject/file1.txt": "f2ba8f84ab5c1bce84a7b441cb1959cfc7093b7f",
"testProject/file蝴蝶.txt": "ae814af81e16cb2ae8c57503c77e2cab6b5462ba",
"testProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576",
"files": Object {
"nestedTestProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576",
"nestedTestProject/src/file 1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34",
"testProject/file 2.txt": "a385f754ec4fede884a4864d090064d9aeef8ccb",
"testProject/file1.txt": "f2ba8f84ab5c1bce84a7b441cb1959cfc7093b7f",
"testProject/file蝴蝶.txt": "ae814af81e16cb2ae8c57503c77e2cab6b5462ba",
"testProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576",
},
"hasSubmodules": false,
"hasUncommittedChanges": true,
}
`;

exports[`getRepoStateAsync can handle removing one file 1`] = `
exports[`getDetailedRepoStateAsync can handle removing one file 1`] = `
Object {
"nestedTestProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576",
"nestedTestProject/src/file 1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34",
"testProject/file 2.txt": "a385f754ec4fede884a4864d090064d9aeef8ccb",
"testProject/file蝴蝶.txt": "ae814af81e16cb2ae8c57503c77e2cab6b5462ba",
"testProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576",
"files": Object {
"nestedTestProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576",
"nestedTestProject/src/file 1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34",
"testProject/file 2.txt": "a385f754ec4fede884a4864d090064d9aeef8ccb",
"testProject/file蝴蝶.txt": "ae814af81e16cb2ae8c57503c77e2cab6b5462ba",
"testProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576",
},
"hasSubmodules": false,
"hasUncommittedChanges": true,
}
`;

exports[`getRepoStateAsync can handle uncommitted filenames with spaces and non-ASCII characters 1`] = `
exports[`getDetailedRepoStateAsync can handle uncommitted filenames with spaces and non-ASCII characters 1`] = `
Object {
"nestedTestProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576",
"nestedTestProject/src/file 1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34",
"testProject/a file name.txt": "2e65efe2a145dda7ee51d1741299f848e5bf752e",
"testProject/a file.txt": "2e65efe2a145dda7ee51d1741299f848e5bf752e",
"testProject/file 2.txt": "a385f754ec4fede884a4864d090064d9aeef8ccb",
"testProject/file1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34",
"testProject/file蝴蝶.txt": "ae814af81e16cb2ae8c57503c77e2cab6b5462ba",
"testProject/newFile批把.txt": "2e65efe2a145dda7ee51d1741299f848e5bf752e",
"testProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576",
"files": Object {
"nestedTestProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576",
"nestedTestProject/src/file 1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34",
"testProject/a file name.txt": "2e65efe2a145dda7ee51d1741299f848e5bf752e",
"testProject/a file.txt": "2e65efe2a145dda7ee51d1741299f848e5bf752e",
"testProject/file 2.txt": "a385f754ec4fede884a4864d090064d9aeef8ccb",
"testProject/file1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34",
"testProject/file蝴蝶.txt": "ae814af81e16cb2ae8c57503c77e2cab6b5462ba",
"testProject/newFile批把.txt": "2e65efe2a145dda7ee51d1741299f848e5bf752e",
"testProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576",
},
"hasSubmodules": false,
"hasUncommittedChanges": true,
}
`;

exports[`getRepoStateAsync can parse committed files 1`] = `
exports[`getDetailedRepoStateAsync can parse committed files 1`] = `
Object {
"nestedTestProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576",
"nestedTestProject/src/file 1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34",
"testProject/file 2.txt": "a385f754ec4fede884a4864d090064d9aeef8ccb",
"testProject/file1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34",
"testProject/file蝴蝶.txt": "ae814af81e16cb2ae8c57503c77e2cab6b5462ba",
"testProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576",
"files": Object {
"nestedTestProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576",
"nestedTestProject/src/file 1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34",
"testProject/file 2.txt": "a385f754ec4fede884a4864d090064d9aeef8ccb",
"testProject/file1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34",
"testProject/file蝴蝶.txt": "ae814af81e16cb2ae8c57503c77e2cab6b5462ba",
"testProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576",
},
"hasSubmodules": false,
"hasUncommittedChanges": false,
}
`;

exports[`getRepoStateAsync handles requests for additional files 1`] = `
exports[`getDetailedRepoStateAsync handles requests for additional files 1`] = `
Object {
"nestedTestProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576",
"nestedTestProject/src/file 1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34",
"testProject/file 2.txt": "a385f754ec4fede884a4864d090064d9aeef8ccb",
"testProject/file1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34",
"testProject/file蝴蝶.txt": "ae814af81e16cb2ae8c57503c77e2cab6b5462ba",
"testProject/log.log": "2e65efe2a145dda7ee51d1741299f848e5bf752e",
"testProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576",
"files": Object {
"nestedTestProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576",
"nestedTestProject/src/file 1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34",
"testProject/file 2.txt": "a385f754ec4fede884a4864d090064d9aeef8ccb",
"testProject/file1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34",
"testProject/file蝴蝶.txt": "ae814af81e16cb2ae8c57503c77e2cab6b5462ba",
"testProject/log.log": "2e65efe2a145dda7ee51d1741299f848e5bf752e",
"testProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576",
},
"hasSubmodules": false,
"hasUncommittedChanges": false,
}
`;

Expand Down
Loading
Loading