Skip to content
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

Include deletions in modified files mask #718

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
37 changes: 34 additions & 3 deletions docs/src/content/docs/api/namespaces/git.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ All content is auto-generated using a oneRepo command:
-->

<!-- start-onerepo-sentinel -->
<!-- @generated SignedSource<<1665a5998f94c28ec13035a22848de34>> -->
<!-- @generated SignedSource<<404018405a003585ff749cadc45fc59a>> -->

Special handlers for managing complex queries and manipulation of the git repository's state.

Expand Down Expand Up @@ -296,10 +296,14 @@ const mergeBase = await getMergeBase();
### getModifiedFiles()

```ts
getModifiedFiles(modified?, options?): Promise<string[]>
getModifiedFiles(
modified?,
options?,
includeDeletions?): Promise<string[]>
```

Get a map of the currently modified files based on their status. If `from` and `through` are not provided, this will current merge-base determination to best get the change to the working tree using `git diff` and `git diff-tree`.
Modified files include files that were added, copied, modified, and renamed. if you wish to include deleted files pass true to `includeDeletions`.

```ts
const changesSinceMergeBase = await git.getModifiedFiles();
Expand All @@ -308,12 +312,39 @@ const betweenRefs = await git.getModifiedFiles('v1.2.3', 'v2.0.0');

**Parameters:**

| Parameter | Type |
| :------------------ | :----------------------------------------------------------------------------------- |
| `modified`? | [`ModifiedStaged`](#modifiedstaged) \| [`ModifiedFromThrough`](#modifiedfromthrough) |
| `options`? | [`Options`](#options) |
| `includeDeletions`? | `boolean` |

**Returns:** `Promise`\<`string`[]\>
**Source:** [modules/git/src/index.ts](https://github.com/paularmstrong/onerepo/blob/main/modules/git/src/index.ts)

---

### getModifiedFilesByStatus()

```ts
getModifiedFilesByStatus(modified?, options?): Promise<ModifiedByStatus>
```

Get a map of the currently modified files sorted by status. If `from` and `through` are not provided, this will current merge-base determination to best get the change to the working tree using `git diff` and `git diff-tree`.
Modified files are returned in an object grouped by the operation that was performed on them according to git.

```ts
const changesSinceMergeBase = await git.getModifiedFilesByStatus();
const betweenRefs = await git.getModifiedFilesByStatus('v1.2.3', 'v2.0.0');
```

**Parameters:**

| Parameter | Type |
| :---------- | :----------------------------------------------------------------------------------- |
| `modified`? | [`ModifiedStaged`](#modifiedstaged) \| [`ModifiedFromThrough`](#modifiedfromthrough) |
| `options`? | [`Options`](#options) |

**Returns:** `Promise`\<`string`[]\>
**Returns:** `Promise`\<`ModifiedByStatus`\>
**Source:** [modules/git/src/index.ts](https://github.com/paularmstrong/onerepo/blob/main/modules/git/src/index.ts)

---
Expand Down
5 changes: 5 additions & 0 deletions modules/git/.changes/000-full-pears-shop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
type: patch
---

The git.getModifiedFiles results now includes deleted files
6 changes: 6 additions & 0 deletions modules/git/.changes/001-new-monkeys-think.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
type: minor
---

Added a git utility method to get modified files and bucket them based on the modification operation.
Updated tasks command to use the new git getModifiedByStatus method so deleted files are included as changes when generating tasks.
95 changes: 91 additions & 4 deletions modules/git/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ export type Options = {
step?: LogStep;
};

type ModifiedByStatus = {
added: Array<string>;
copied: Array<string>;
modified: Array<string>;
deleted: Array<string>;
renamed: Array<string>;
};

/**
* Get the name of the current branch. Equivalent to `git rev-parse --abbrev-ref HEAD`.
*
Expand Down Expand Up @@ -169,13 +177,18 @@ export type ModifiedStaged = {

/**
* Get a map of the currently modified files based on their status. If `from` and `through` are not provided, this will current merge-base determination to best get the change to the working tree using `git diff` and `git diff-tree`.
* Modified files include files that were added, copied, modified, and renamed. if you wish to include deleted files pass true to `includeDeletions`.
*
* ```ts
* const changesSinceMergeBase = await git.getModifiedFiles();
* const betweenRefs = await git.getModifiedFiles('v1.2.3', 'v2.0.0');
* ```
*/
export async function getModifiedFiles(modified: ModifiedStaged | ModifiedFromThrough = {}, options: Options = {}) {
export async function getModifiedFiles(
modified: ModifiedStaged | ModifiedFromThrough = {},
options: Options = {},
includeDeletions = false,
paularmstrong marked this conversation as resolved.
Show resolved Hide resolved
) {
const { from, staged, through } = modified;
const { step } = options;
return stepWrapper({ step, name: 'Get modified files' }, async (step) => {
Expand All @@ -184,17 +197,19 @@ export async function getModifiedFiles(modified: ModifiedStaged | ModifiedFromTh

const isMain = base === currentSha;
const isCleanState = await isClean({ step });
const diffFilter = `ACMR${includeDeletions ? 'D' : ''}`;
const fileNameFormat = includeDeletions ? '--name-status' : '--name-only';

const uncleanArgs = ['diff', '--name-only', '-z', '--diff-filter', 'ACMR'];
const uncleanArgs = ['diff', fileNameFormat, '-z', '--diff-filter', diffFilter];
uncleanArgs.push(!staged ? base : '--cached');
const cleanMainArgs = [
'diff-tree',
'-r',
'-z',
'--name-only',
fileNameFormat,
'--no-commit-id',
'--diff-filter',
'ACMR',
diffFilter,
isMain ? `${currentSha}^` : base,
isMain ? currentSha : 'HEAD',
];
Expand All @@ -214,6 +229,78 @@ export async function getModifiedFiles(modified: ModifiedStaged | ModifiedFromTh
});
}

/**
* Get a map of the currently modified files sorted by status. If `from` and `through` are not provided, this will current merge-base determination to best get the change to the working tree using `git diff` and `git diff-tree`.
* Modified files are returned in an object grouped by the operation that was performed on them according to git.
*
* ```ts
* const changesSinceMergeBase = await git.getModifiedFilesByStatus();
* const betweenRefs = await git.getModifiedFilesByStatus('v1.2.3', 'v2.0.0');
* ```
*/
export async function getModifiedFilesByStatus(
modified: ModifiedStaged | ModifiedFromThrough = {},
options: Options = {},
): Promise<ModifiedByStatus> {
const { step } = options;
return stepWrapper({ step, name: 'Get modified files by status' }, async (step) => {
const files = await getModifiedFiles(modified, options, true);

const modifiedByType: ModifiedByStatus = {
modified: [],
added: [],
copied: [],
renamed: [],
deleted: [],
};

let opPtr = 0;
let filePtr = 1;

if (files.length === 0) {
step.warn('No modified files detected.');
return modifiedByType;
}

if (files.length % 2 !== 0 || files.length < 2) {
step.warn('File diff response from Github is in an unexpected format.');
return modifiedByType;
}

const iterations = files.length / 2;
for (let x = 0; x < iterations; x++) {
const operation = files[opPtr];

switch (operation) {
case 'A':
modifiedByType.added.push(files[filePtr]);
break;
case 'C':
modifiedByType.copied.push(files[filePtr]);
break;
case 'M':
modifiedByType.modified.push(files[filePtr]);
break;
case 'R':
modifiedByType.renamed.push(files[filePtr]);
break;
case 'D':
modifiedByType.deleted.push(files[filePtr]);
break;
default:
step.warn(
`Unknown file diff operation ${operation} detected. This file ${files[filePtr]} will not be included in the results.`,
);
}

opPtr += 2;
filePtr += 2;
}

return modifiedByType;
});
}

/**
* Get the current sha ref. This is equivalent to `git rev-parse HEAD`.
*
Expand Down
6 changes: 6 additions & 0 deletions modules/onerepo/.changes/002-new-monkeys-think.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
type: minor
---

Added a git utility method to get modified files and bucket them based on the modification operation.
Updated tasks command to use the new git getModifiedByStatus method so deleted files are included as changes when generating tasks.
2 changes: 1 addition & 1 deletion modules/onerepo/src/core/tasks/tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ export const handler: Handler<Argv> = async (argv, { getWorkspaces, graph, logge
const modifiedOpts = staged
? { staged: true, step: setupStep }
: { from: fromRef, through: throughRef, step: setupStep };
const allFiles = await git.getModifiedFiles(modifiedOpts, { step: setupStep });
const allFiles = await git.getModifiedFiles(modifiedOpts, { step: setupStep }, true);
const files = allFiles.filter((file) => !ignore.some((ignore) => minimatch(file, ignore)));

if (!files.length && !workspaceNames.length) {
Expand Down
Loading