Skip to content

Commit 38d36a9

Browse files
anish3333isomorphic-git-bot
authored andcommitted
feat: add 'create' operation to stash API (#2211) fix #2207
* feat: add 'create' operation to stash API, allowing stash commits without modifying working directory * docs: add @anish3333 as a contributor * refactor: improve readability of stash create tests by using consistent variable declarations and formatting * fix: format changed files with Prettier and ESLint * refactor: extract common stash commit logic to _createStashCommit
1 parent 0311a59 commit 38d36a9

File tree

17 files changed

+163
-63
lines changed

17 files changed

+163
-63
lines changed

docs/en/next/stash.html

Lines changed: 10 additions & 5 deletions
Large diffs are not rendered by default.

docs/en/next/stash/index.html

Lines changed: 10 additions & 5 deletions
Large diffs are not rendered by default.

docs/en/stash.html

Lines changed: 10 additions & 5 deletions
Large diffs are not rendered by default.

docs/en/stash/index.html

Lines changed: 10 additions & 5 deletions
Large diffs are not rendered by default.

js/isomorphic-git/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,10 @@ Thanks goes to these wonderful people ([emoji key](https://github.com/kentcdodds
392392
<td align="center"><a href="https://github.com/gnillev"><img src="https://avatars.githubusercontent.com/u/8965094?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Mathias Nisted Velling</b></sub></a><br /><a href="https://github.com/isomorphic-git/isomorphic-git/commits?author=gnillev" title="Code">💻</a> <a href="https://github.com/isomorphic-git/isomorphic-git/commits?author=gnillev" title="Tests">⚠️</a></td>
393393
<td align="center"><a href="https://github.com/acandoo"><img src="https://avatars.githubusercontent.com/u/117209328?v=4?s=60" width="60px;" alt=""/><br /><sub><b>acandoo</b></sub></a><br /><a href="#platform-acandoo" title="Packaging/porting to new platform">📦</a> <a href="#userTesting-acandoo" title="User Testing">📓</a></td>
394394
<td align="center"><a href="https://github.com/bekatan"><img src="https://avatars.githubusercontent.com/u/19550476?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Bekatan Satyev</b></sub></a><br /><a href="https://github.com/isomorphic-git/isomorphic-git/commits?author=bekatan" title="Code">💻</a> <a href="https://github.com/isomorphic-git/isomorphic-git/commits?author=bekatan" title="Tests">⚠️</a></td>
395+
</tr>
396+
<tr>
395397
<td align="center"><a href="https://github.com/hemanthkini"><img src="https://avatars.githubusercontent.com/u/3934055?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Hemanth Kini</b></sub></a><br /><a href="https://github.com/isomorphic-git/isomorphic-git/commits?author=hemanthkini" title="Code">💻</a></td>
398+
<td align="center"><a href="https://github.com/anish3333"><img src="https://avatars.githubusercontent.com/u/128889867?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Anish Awasthi</b></sub></a><br /><a href="https://github.com/isomorphic-git/isomorphic-git/commits?author=anish3333" title="Code">💻</a> <a href="https://github.com/isomorphic-git/isomorphic-git/commits?author=anish3333" title="Documentation">📖</a> <a href="https://github.com/isomorphic-git/isomorphic-git/commits?author=anish3333" title="Tests">⚠️</a></td>
396399
</tr>
397400
</table>
398401

js/isomorphic-git/index.cjs

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15013,16 +15013,19 @@ class GitStashManager {
1501315013
return []
1501415014
}
1501515015

15016-
const reflogBuffer = await this.fs.read(this.refLogsStashPath);
15017-
const reflogString = reflogBuffer.toString();
15016+
const reflogString = await this.fs.read(this.refLogsStashPath, 'utf8');
1501815017

1501915018
return GitRefStash.getStashReflogEntry(reflogString, parsed)
1502015019
}
1502115020
}
1502215021

1502315022
// @ts-check
1502415023

15025-
async function _stashPush({ fs, dir, gitdir, message = '' }) {
15024+
/**
15025+
* Common logic for creating a stash commit
15026+
* @private
15027+
*/
15028+
async function _createStashCommit({ fs, dir, gitdir, message = '' }) {
1502615029
const stashMgr = new GitStashManager({ fs, dir, gitdir });
1502715030

1502815031
await stashMgr.getAuthor(); // ensure there is an author
@@ -15057,7 +15060,7 @@ async function _stashPush({ fs, dir, gitdir, message = '' }) {
1505715060
// create a commit from the index tree, which has one parent, the current branch HEAD
1505815061
const stashCommitOne = await stashMgr.writeStashCommit({
1505915062
message: `stash-Index: WIP on ${branch} - ${new Date().toISOString()}`,
15060-
tree: indexTree, // stashCommitTree
15063+
tree: indexTree,
1506115064
parent: stashCommitParents,
1506215065
});
1506315066
stashCommitParents.push(stashCommitOne);
@@ -15098,6 +15101,17 @@ async function _stashPush({ fs, dir, gitdir, message = '' }) {
1509815101
parent: stashCommitParents,
1509915102
});
1510015103

15104+
return { stashCommit, stashMsg, branch, stashMgr }
15105+
}
15106+
15107+
async function _stashPush({ fs, dir, gitdir, message = '' }) {
15108+
const { stashCommit, stashMsg, branch, stashMgr } = await _createStashCommit({
15109+
fs,
15110+
dir,
15111+
gitdir,
15112+
message,
15113+
});
15114+
1510115115
// next, write this commit into .git/refs/stash:
1510215116
await stashMgr.writeStashRef(stashCommit);
1510315117

@@ -15120,6 +15134,18 @@ async function _stashPush({ fs, dir, gitdir, message = '' }) {
1512015134
return stashCommit
1512115135
}
1512215136

15137+
async function _stashCreate({ fs, dir, gitdir, message = '' }) {
15138+
const { stashCommit } = await _createStashCommit({
15139+
fs,
15140+
dir,
15141+
gitdir,
15142+
message,
15143+
});
15144+
15145+
// Return the stash commit hash without modifying refs or working directory
15146+
return stashCommit
15147+
}
15148+
1512315149
async function _stashApply({ fs, dir, gitdir, refIdx = 0 }) {
1512415150
const stashMgr = new GitStashManager({ fs, dir, gitdir });
1512515151

@@ -15223,21 +15249,22 @@ async function _stashPop({ fs, dir, gitdir, refIdx = 0 }) {
1522315249
// @ts-check
1522415250

1522515251
/**
15226-
* stash api, supports {'push' | 'pop' | 'apply' | 'drop' | 'list' | 'clear'} StashOp
15252+
* stash api, supports {'push' | 'pop' | 'apply' | 'drop' | 'list' | 'clear' | 'create'} StashOp
1522715253
* _note_,
1522815254
* - all stash operations are done on tracked files only with loose objects, no packed objects
1522915255
* - when op === 'push', both working directory and index (staged) changes will be stashed, tracked files only
1523015256
* - when op === 'push', message is optional, and only applicable when op === 'push'
1523115257
* - when op === 'apply | pop', the stashed changes will overwrite the working directory, no abort when conflicts
15258+
* - when op === 'create', creates a stash commit without modifying working directory or refs, returns the commit hash
1523215259
*
1523315260
* @param {object} args
1523415261
* @param {FsClient} args.fs - [required] a file system client
1523515262
* @param {string} [args.dir] - [required] The [working tree](dir-vs-gitdir.md) directory path
1523615263
* @param {string} [args.gitdir=join(dir,'.git')] - [optional] The [git directory](dir-vs-gitdir.md) path
15237-
* @param {'push' | 'pop' | 'apply' | 'drop' | 'list' | 'clear'} [args.op = 'push'] - [optional] name of stash operation, default to 'push'
15238-
* @param {string} [args.message = ''] - [optional] message to be used for the stash entry, only applicable when op === 'push'
15264+
* @param {'push' | 'pop' | 'apply' | 'drop' | 'list' | 'clear' | 'create'} [args.op = 'push'] - [optional] name of stash operation, default to 'push'
15265+
* @param {string} [args.message = ''] - [optional] message to be used for the stash entry, only applicable when op === 'push' or 'create'
1523915266
* @param {number} [args.refIdx = 0] - [optional - Number] stash ref index of entry, only applicable when op === ['apply' | 'drop' | 'pop'], refIdx >= 0 and < num of stash pushed
15240-
* @returns {Promise<string | void>} Resolves successfully when stash operations are complete
15267+
* @returns {Promise<string | void>} Resolves successfully when stash operations are complete. Returns commit hash for 'create' operation.
1524115268
*
1524215269
* @example
1524315270
* // stash changes in the working directory and index
@@ -15272,6 +15299,10 @@ async function _stashPop({ fs, dir, gitdir, refIdx = 0 }) {
1527215299
*
1527315300
* console.log(await git.status({ fs, dir, filepath: 'a.txt' })) // 'modified'
1527415301
* console.log(await git.status({ fs, dir, filepath: 'b.txt' })) // '*modified'
15302+
*
15303+
* // create a stash commit without modifying working directory
15304+
* const stashCommitHash = await git.stash({ fs, dir, op: 'create', message: 'my stash' })
15305+
* console.log(stashCommitHash) // returns the stash commit hash
1527515306
*/
1527615307

1527715308
async function stash({
@@ -15294,6 +15325,7 @@ async function stash({
1529415325
list: _stashList,
1529515326
clear: _stashClear,
1529615327
pop: _stashPop,
15328+
create: _stashCreate,
1529715329
};
1529815330

1529915331
const opsNeedRefIdx = ['apply', 'drop', 'pop'];

js/isomorphic-git/index.d.cts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3033,21 +3033,22 @@ export function setConfig({ fs: _fs, dir, gitdir, path, value, append, }: {
30333033
append?: boolean | undefined;
30343034
}): Promise<void>;
30353035
/**
3036-
* stash api, supports {'push' | 'pop' | 'apply' | 'drop' | 'list' | 'clear'} StashOp
3036+
* stash api, supports {'push' | 'pop' | 'apply' | 'drop' | 'list' | 'clear' | 'create'} StashOp
30373037
* _note_,
30383038
* - all stash operations are done on tracked files only with loose objects, no packed objects
30393039
* - when op === 'push', both working directory and index (staged) changes will be stashed, tracked files only
30403040
* - when op === 'push', message is optional, and only applicable when op === 'push'
30413041
* - when op === 'apply | pop', the stashed changes will overwrite the working directory, no abort when conflicts
3042+
* - when op === 'create', creates a stash commit without modifying working directory or refs, returns the commit hash
30423043
*
30433044
* @param {object} args
30443045
* @param {FsClient} args.fs - [required] a file system client
30453046
* @param {string} [args.dir] - [required] The [working tree](dir-vs-gitdir.md) directory path
30463047
* @param {string} [args.gitdir=join(dir,'.git')] - [optional] The [git directory](dir-vs-gitdir.md) path
3047-
* @param {'push' | 'pop' | 'apply' | 'drop' | 'list' | 'clear'} [args.op = 'push'] - [optional] name of stash operation, default to 'push'
3048-
* @param {string} [args.message = ''] - [optional] message to be used for the stash entry, only applicable when op === 'push'
3048+
* @param {'push' | 'pop' | 'apply' | 'drop' | 'list' | 'clear' | 'create'} [args.op = 'push'] - [optional] name of stash operation, default to 'push'
3049+
* @param {string} [args.message = ''] - [optional] message to be used for the stash entry, only applicable when op === 'push' or 'create'
30493050
* @param {number} [args.refIdx = 0] - [optional - Number] stash ref index of entry, only applicable when op === ['apply' | 'drop' | 'pop'], refIdx >= 0 and < num of stash pushed
3050-
* @returns {Promise<string | void>} Resolves successfully when stash operations are complete
3051+
* @returns {Promise<string | void>} Resolves successfully when stash operations are complete. Returns commit hash for 'create' operation.
30513052
*
30523053
* @example
30533054
* // stash changes in the working directory and index
@@ -3082,12 +3083,16 @@ export function setConfig({ fs: _fs, dir, gitdir, path, value, append, }: {
30823083
*
30833084
* console.log(await git.status({ fs, dir, filepath: 'a.txt' })) // 'modified'
30843085
* console.log(await git.status({ fs, dir, filepath: 'b.txt' })) // '*modified'
3086+
*
3087+
* // create a stash commit without modifying working directory
3088+
* const stashCommitHash = await git.stash({ fs, dir, op: 'create', message: 'my stash' })
3089+
* console.log(stashCommitHash) // returns the stash commit hash
30853090
*/
30863091
export function stash({ fs, dir, gitdir, op, message, refIdx, }: {
30873092
fs: FsClient;
30883093
dir?: string | undefined;
30893094
gitdir?: string | undefined;
3090-
op?: "pop" | "push" | "clear" | "drop" | "apply" | "list" | undefined;
3095+
op?: "pop" | "push" | "clear" | "drop" | "apply" | "list" | "create" | undefined;
30913096
message?: string | undefined;
30923097
refIdx?: number | undefined;
30933098
}): Promise<string | void>;

js/isomorphic-git/index.d.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3032,21 +3032,22 @@ export function setConfig({ fs: _fs, dir, gitdir, path, value, append, }: {
30323032
append?: boolean | undefined;
30333033
}): Promise<void>;
30343034
/**
3035-
* stash api, supports {'push' | 'pop' | 'apply' | 'drop' | 'list' | 'clear'} StashOp
3035+
* stash api, supports {'push' | 'pop' | 'apply' | 'drop' | 'list' | 'clear' | 'create'} StashOp
30363036
* _note_,
30373037
* - all stash operations are done on tracked files only with loose objects, no packed objects
30383038
* - when op === 'push', both working directory and index (staged) changes will be stashed, tracked files only
30393039
* - when op === 'push', message is optional, and only applicable when op === 'push'
30403040
* - when op === 'apply | pop', the stashed changes will overwrite the working directory, no abort when conflicts
3041+
* - when op === 'create', creates a stash commit without modifying working directory or refs, returns the commit hash
30413042
*
30423043
* @param {object} args
30433044
* @param {FsClient} args.fs - [required] a file system client
30443045
* @param {string} [args.dir] - [required] The [working tree](dir-vs-gitdir.md) directory path
30453046
* @param {string} [args.gitdir=join(dir,'.git')] - [optional] The [git directory](dir-vs-gitdir.md) path
3046-
* @param {'push' | 'pop' | 'apply' | 'drop' | 'list' | 'clear'} [args.op = 'push'] - [optional] name of stash operation, default to 'push'
3047-
* @param {string} [args.message = ''] - [optional] message to be used for the stash entry, only applicable when op === 'push'
3047+
* @param {'push' | 'pop' | 'apply' | 'drop' | 'list' | 'clear' | 'create'} [args.op = 'push'] - [optional] name of stash operation, default to 'push'
3048+
* @param {string} [args.message = ''] - [optional] message to be used for the stash entry, only applicable when op === 'push' or 'create'
30483049
* @param {number} [args.refIdx = 0] - [optional - Number] stash ref index of entry, only applicable when op === ['apply' | 'drop' | 'pop'], refIdx >= 0 and < num of stash pushed
3049-
* @returns {Promise<string | void>} Resolves successfully when stash operations are complete
3050+
* @returns {Promise<string | void>} Resolves successfully when stash operations are complete. Returns commit hash for 'create' operation.
30503051
*
30513052
* @example
30523053
* // stash changes in the working directory and index
@@ -3081,12 +3082,16 @@ export function setConfig({ fs: _fs, dir, gitdir, path, value, append, }: {
30813082
*
30823083
* console.log(await git.status({ fs, dir, filepath: 'a.txt' })) // 'modified'
30833084
* console.log(await git.status({ fs, dir, filepath: 'b.txt' })) // '*modified'
3085+
*
3086+
* // create a stash commit without modifying working directory
3087+
* const stashCommitHash = await git.stash({ fs, dir, op: 'create', message: 'my stash' })
3088+
* console.log(stashCommitHash) // returns the stash commit hash
30843089
*/
30853090
export function stash({ fs, dir, gitdir, op, message, refIdx, }: {
30863091
fs: FsClient;
30873092
dir?: string | undefined;
30883093
gitdir?: string | undefined;
3089-
op?: "pop" | "push" | "clear" | "drop" | "apply" | "list" | undefined;
3094+
op?: "pop" | "push" | "clear" | "drop" | "apply" | "list" | "create" | undefined;
30903095
message?: string | undefined;
30913096
refIdx?: number | undefined;
30923097
}): Promise<string | void>;

js/isomorphic-git/index.js

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15007,16 +15007,19 @@ class GitStashManager {
1500715007
return []
1500815008
}
1500915009

15010-
const reflogBuffer = await this.fs.read(this.refLogsStashPath);
15011-
const reflogString = reflogBuffer.toString();
15010+
const reflogString = await this.fs.read(this.refLogsStashPath, 'utf8');
1501215011

1501315012
return GitRefStash.getStashReflogEntry(reflogString, parsed)
1501415013
}
1501515014
}
1501615015

1501715016
// @ts-check
1501815017

15019-
async function _stashPush({ fs, dir, gitdir, message = '' }) {
15018+
/**
15019+
* Common logic for creating a stash commit
15020+
* @private
15021+
*/
15022+
async function _createStashCommit({ fs, dir, gitdir, message = '' }) {
1502015023
const stashMgr = new GitStashManager({ fs, dir, gitdir });
1502115024

1502215025
await stashMgr.getAuthor(); // ensure there is an author
@@ -15051,7 +15054,7 @@ async function _stashPush({ fs, dir, gitdir, message = '' }) {
1505115054
// create a commit from the index tree, which has one parent, the current branch HEAD
1505215055
const stashCommitOne = await stashMgr.writeStashCommit({
1505315056
message: `stash-Index: WIP on ${branch} - ${new Date().toISOString()}`,
15054-
tree: indexTree, // stashCommitTree
15057+
tree: indexTree,
1505515058
parent: stashCommitParents,
1505615059
});
1505715060
stashCommitParents.push(stashCommitOne);
@@ -15092,6 +15095,17 @@ async function _stashPush({ fs, dir, gitdir, message = '' }) {
1509215095
parent: stashCommitParents,
1509315096
});
1509415097

15098+
return { stashCommit, stashMsg, branch, stashMgr }
15099+
}
15100+
15101+
async function _stashPush({ fs, dir, gitdir, message = '' }) {
15102+
const { stashCommit, stashMsg, branch, stashMgr } = await _createStashCommit({
15103+
fs,
15104+
dir,
15105+
gitdir,
15106+
message,
15107+
});
15108+
1509515109
// next, write this commit into .git/refs/stash:
1509615110
await stashMgr.writeStashRef(stashCommit);
1509715111

@@ -15114,6 +15128,18 @@ async function _stashPush({ fs, dir, gitdir, message = '' }) {
1511415128
return stashCommit
1511515129
}
1511615130

15131+
async function _stashCreate({ fs, dir, gitdir, message = '' }) {
15132+
const { stashCommit } = await _createStashCommit({
15133+
fs,
15134+
dir,
15135+
gitdir,
15136+
message,
15137+
});
15138+
15139+
// Return the stash commit hash without modifying refs or working directory
15140+
return stashCommit
15141+
}
15142+
1511715143
async function _stashApply({ fs, dir, gitdir, refIdx = 0 }) {
1511815144
const stashMgr = new GitStashManager({ fs, dir, gitdir });
1511915145

@@ -15217,21 +15243,22 @@ async function _stashPop({ fs, dir, gitdir, refIdx = 0 }) {
1521715243
// @ts-check
1521815244

1521915245
/**
15220-
* stash api, supports {'push' | 'pop' | 'apply' | 'drop' | 'list' | 'clear'} StashOp
15246+
* stash api, supports {'push' | 'pop' | 'apply' | 'drop' | 'list' | 'clear' | 'create'} StashOp
1522115247
* _note_,
1522215248
* - all stash operations are done on tracked files only with loose objects, no packed objects
1522315249
* - when op === 'push', both working directory and index (staged) changes will be stashed, tracked files only
1522415250
* - when op === 'push', message is optional, and only applicable when op === 'push'
1522515251
* - when op === 'apply | pop', the stashed changes will overwrite the working directory, no abort when conflicts
15252+
* - when op === 'create', creates a stash commit without modifying working directory or refs, returns the commit hash
1522615253
*
1522715254
* @param {object} args
1522815255
* @param {FsClient} args.fs - [required] a file system client
1522915256
* @param {string} [args.dir] - [required] The [working tree](dir-vs-gitdir.md) directory path
1523015257
* @param {string} [args.gitdir=join(dir,'.git')] - [optional] The [git directory](dir-vs-gitdir.md) path
15231-
* @param {'push' | 'pop' | 'apply' | 'drop' | 'list' | 'clear'} [args.op = 'push'] - [optional] name of stash operation, default to 'push'
15232-
* @param {string} [args.message = ''] - [optional] message to be used for the stash entry, only applicable when op === 'push'
15258+
* @param {'push' | 'pop' | 'apply' | 'drop' | 'list' | 'clear' | 'create'} [args.op = 'push'] - [optional] name of stash operation, default to 'push'
15259+
* @param {string} [args.message = ''] - [optional] message to be used for the stash entry, only applicable when op === 'push' or 'create'
1523315260
* @param {number} [args.refIdx = 0] - [optional - Number] stash ref index of entry, only applicable when op === ['apply' | 'drop' | 'pop'], refIdx >= 0 and < num of stash pushed
15234-
* @returns {Promise<string | void>} Resolves successfully when stash operations are complete
15261+
* @returns {Promise<string | void>} Resolves successfully when stash operations are complete. Returns commit hash for 'create' operation.
1523515262
*
1523615263
* @example
1523715264
* // stash changes in the working directory and index
@@ -15266,6 +15293,10 @@ async function _stashPop({ fs, dir, gitdir, refIdx = 0 }) {
1526615293
*
1526715294
* console.log(await git.status({ fs, dir, filepath: 'a.txt' })) // 'modified'
1526815295
* console.log(await git.status({ fs, dir, filepath: 'b.txt' })) // '*modified'
15296+
*
15297+
* // create a stash commit without modifying working directory
15298+
* const stashCommitHash = await git.stash({ fs, dir, op: 'create', message: 'my stash' })
15299+
* console.log(stashCommitHash) // returns the stash commit hash
1526915300
*/
1527015301

1527115302
async function stash({
@@ -15288,6 +15319,7 @@ async function stash({
1528815319
list: _stashList,
1528915320
clear: _stashClear,
1529015321
pop: _stashPop,
15322+
create: _stashCreate,
1529115323
};
1529215324

1529315325
const opsNeedRefIdx = ['apply', 'drop', 'pop'];

0 commit comments

Comments
 (0)