/
gitWorkflow.js
133 lines (114 loc) Β· 3.82 KB
/
gitWorkflow.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
'use strict'
const debug = require('debug')('lint-staged:git')
const execGit = require('./execGit')
const STASH_ORGINAL = 'lint-staged backup (original state)'
const STASH_MODIFICATIONS = 'lint-staged backup (modifications)'
let unstagedDiff
/**
* From `array` of strings find index number of string containing `test` string
*
* @param {Array<string>} array - Array of strings
* @param {string} test - Test string
* @returns {number} - Index number
*/
const findIndex = (array, test) => array.findIndex(line => line.includes(test))
/**
* Get names of stashes
*
* @param {Object} [options]
* @returns {Promise<Object>}
*/
async function getStashes(options) {
const stashList = (await execGit(['stash', 'list'], options)).split('\n')
return {
original: `stash@{${findIndex(stashList, STASH_ORGINAL)}}`,
modifications: `stash@{${findIndex(stashList, STASH_MODIFICATIONS)}}`
}
}
/**
* Create backup stashes, one of everything and one of only staged changes
* Leves stages files in index for running tasks
*
* @param {Object} [options]
* @returns {Promise<void>}
*/
async function backupOriginalState(options) {
debug('Backing up original state...')
// Get stash of entire original state, including unstaged changes
// Keep index so that tasks only work on those files
await execGit(['stash', 'save', '--include-untracked', '--keep-index', STASH_ORGINAL], options)
// Since only staged files are now present, get a diff of unstaged changes
// by comparing current index against original stash, but in reverse
const { original } = await getStashes(options)
unstagedDiff = await execGit(
['diff', '--unified=0', '--no-color', '--no-ext-diff', '-p', original, '-R'],
options
)
debug('Done backing up original state!')
}
/**
* Resets everything and applies back unstaged and staged changes,
* possibly with modifications by tasks
*
* @param {Object} [options]
* @returns {Promise<void>}
*/
async function applyModifications(options) {
debug('Applying modifications by tasks...')
// Save index with possible modifications by tasks.
await execGit(['stash', 'save', STASH_MODIFICATIONS], options)
// Reset HEAD
await execGit(['reset'], options)
await execGit(['checkout', '.'], options)
// Get diff of index against reseted HEAD, this includes all staged changes,
// with possible changes by tasks
const { modifications } = await getStashes(options)
const stagedDiff = await execGit(
['diff', '--unified=0', '--no-color', '--no-ext-diff', 'HEAD', '-p', modifications],
options
)
await execGit(['apply', '-v', '--index', '--whitespace=nowarn', '--recount', '--unidiff-zero'], {
...options,
input: `${stagedDiff}\n`
})
if (unstagedDiff) {
await execGit(['apply', '-v', '--whitespace=nowarn', '--recount', '--unidiff-zero'], {
...options,
input: `${unstagedDiff}\n`
})
}
debug('Done applying modifications by tasks!')
}
/**
* Restore original HEAD state in case of errors
*
* @param {Object} [options]
* @returns {Promise<void>}
*/
async function restoreOriginalState(options) {
debug('Restoring original state...')
const { original } = await getStashes(options)
await execGit(['reset', '--hard', 'HEAD'], options)
await execGit(['stash', 'apply', '--index', original], options)
debug('Done restoring original state!')
}
/**
* Drop the created stashes after everything has run
*
* @param {Object} [options]
* @returns {Promise<void>}
*/
async function dropBackupStashes(options) {
debug('Dropping backup stash...')
const { original, modifications } = await getStashes(options)
await execGit(['stash', 'drop', original], options)
await execGit(['stash', 'drop', modifications], options)
debug('Done dropping backup stash!')
}
module.exports = {
execGit,
backupOriginalState,
applyModifications,
restoreOriginalState,
dropBackupStashes
}