git-chopstick-core 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/dist/git/exec.js.map +1 -1
- package/dist/lib/fatal-error.d.ts +1 -1
- package/dist/lib/fatal-error.js +1 -1
- package/dist/lib/fatal-error.js.map +1 -1
- package/dist/lib/progress/from-process.js.map +1 -1
- package/dist/lib/progress/index.d.ts +5 -5
- package/dist/lib/progress/index.js +5 -5
- package/dist/lib/progress/index.js.map +1 -1
- package/dist/lib/status-parser.js +0 -12
- package/dist/lib/status-parser.js.map +1 -1
- package/package.json +38 -8
- package/src/git/add.ts +0 -16
- package/src/git/apply.ts +0 -154
- package/src/git/authentication.ts +0 -20
- package/src/git/branch.ts +0 -206
- package/src/git/checkout-index.ts +0 -40
- package/src/git/checkout.ts +0 -235
- package/src/git/cherry-pick.ts +0 -504
- package/src/git/clean.ts +0 -9
- package/src/git/clone.ts +0 -86
- package/src/git/coerce-to-buffer.ts +0 -4
- package/src/git/coerce-to-string.ts +0 -4
- package/src/git/commit.ts +0 -136
- package/src/git/config.ts +0 -392
- package/src/git/core.ts +0 -625
- package/src/git/create-tail-stream.ts +0 -36
- package/src/git/credential.ts +0 -83
- package/src/git/description.ts +0 -33
- package/src/git/diff-check.ts +0 -27
- package/src/git/diff-index.ts +0 -116
- package/src/git/diff.ts +0 -880
- package/src/git/environment.ts +0 -116
- package/src/git/exec.ts +0 -285
- package/src/git/fetch.ts +0 -141
- package/src/git/for-each-ref.ts +0 -160
- package/src/git/format-patch.ts +0 -17
- package/src/git/git-delimiter-parser.ts +0 -95
- package/src/git/gitignore.ts +0 -157
- package/src/git/index.ts +0 -46
- package/src/git/init.ts +0 -11
- package/src/git/interpret-trailers.ts +0 -176
- package/src/git/lfs.ts +0 -100
- package/src/git/log.ts +0 -376
- package/src/git/merge-tree.ts +0 -42
- package/src/git/merge.ts +0 -154
- package/src/git/multi-operation-terminal-output.ts +0 -68
- package/src/git/pull.ts +0 -130
- package/src/git/push-terminal-chunk.ts +0 -41
- package/src/git/push.ts +0 -119
- package/src/git/rebase.ts +0 -627
- package/src/git/reflog.ts +0 -127
- package/src/git/refs.ts +0 -63
- package/src/git/remote.ts +0 -143
- package/src/git/reorder.ts +0 -153
- package/src/git/reset.ts +0 -101
- package/src/git/rev-list.ts +0 -201
- package/src/git/rev-parse.ts +0 -92
- package/src/git/revert.ts +0 -55
- package/src/git/rm.ts +0 -31
- package/src/git/show.ts +0 -88
- package/src/git/spawn.ts +0 -38
- package/src/git/squash.ts +0 -173
- package/src/git/stage.ts +0 -97
- package/src/git/stash.ts +0 -302
- package/src/git/status.ts +0 -502
- package/src/git/submodule.ts +0 -212
- package/src/git/tag.ts +0 -134
- package/src/git/update-index.ts +0 -169
- package/src/git/update-ref.ts +0 -50
- package/src/git/var.ts +0 -42
- package/src/git/worktree-include.ts +0 -146
- package/src/git/worktree.ts +0 -219
- package/src/index.ts +0 -11
- package/src/lib/api.ts +0 -7
- package/src/lib/diff-parser.ts +0 -249
- package/src/lib/directory-exists.ts +0 -10
- package/src/lib/errno-exception.ts +0 -12
- package/src/lib/fatal-error.ts +0 -23
- package/src/lib/feature-flag.ts +0 -29
- package/src/lib/file-system.ts +0 -7
- package/src/lib/get-old-path.ts +0 -11
- package/src/lib/git/environment.ts +0 -14
- package/src/lib/git-perf.ts +0 -3
- package/src/lib/helpers/default-branch.ts +0 -3
- package/src/lib/helpers/path.ts +0 -5
- package/src/lib/hooks/with-hooks-env.ts +0 -7
- package/src/lib/merge.ts +0 -3
- package/src/lib/noop.ts +0 -1
- package/src/lib/patch-formatter.ts +0 -18
- package/src/lib/path-exists.ts +0 -7
- package/src/lib/progress/from-process.ts +0 -10
- package/src/lib/progress/index.ts +0 -43
- package/src/lib/progress/revert.ts +0 -17
- package/src/lib/rebase.ts +0 -3
- package/src/lib/remove-remote-prefix.ts +0 -4
- package/src/lib/resolve-git-proxy.ts +0 -3
- package/src/lib/round.ts +0 -4
- package/src/lib/split-buffer.ts +0 -14
- package/src/lib/status-parser.ts +0 -188
- package/src/lib/stores/helpers/find-default-remote.ts +0 -3
- package/src/lib/trampoline/trampoline-environment.ts +0 -8
- package/src/models/branch.ts +0 -78
- package/src/models/cherry-pick.ts +0 -12
- package/src/models/clone-options.ts +0 -6
- package/src/models/commit-identity.ts +0 -35
- package/src/models/commit.ts +0 -44
- package/src/models/computed-action.ts +0 -6
- package/src/models/diff/diff-data.ts +0 -78
- package/src/models/diff/diff-line.ts +0 -36
- package/src/models/diff/diff-selection.ts +0 -165
- package/src/models/diff/image-diff.ts +0 -6
- package/src/models/diff/image.ts +0 -8
- package/src/models/diff/index.ts +0 -6
- package/src/models/diff/raw-diff.ts +0 -41
- package/src/models/git-author.ts +0 -16
- package/src/models/index.ts +0 -36
- package/src/models/manual-conflict-resolution.ts +0 -4
- package/src/models/merge.ts +0 -6
- package/src/models/multi-commit-operation.ts +0 -6
- package/src/models/progress.ts +0 -67
- package/src/models/rebase.ts +0 -20
- package/src/models/remote.ts +0 -10
- package/src/models/repository.ts +0 -16
- package/src/models/stash-entry.ts +0 -25
- package/src/models/status.ts +0 -275
- package/src/models/submodule.ts +0 -13
- package/src/models/worktree.ts +0 -11
package/src/git/status.ts
DELETED
|
@@ -1,502 +0,0 @@
|
|
|
1
|
-
import { getFilesWithConflictMarkers } from './diff-check.js'
|
|
2
|
-
import {
|
|
3
|
-
WorkingDirectoryStatus,
|
|
4
|
-
WorkingDirectoryFileChange,
|
|
5
|
-
AppFileStatus,
|
|
6
|
-
FileEntry,
|
|
7
|
-
GitStatusEntry,
|
|
8
|
-
AppFileStatusKind,
|
|
9
|
-
UnmergedEntry,
|
|
10
|
-
ConflictedFileStatus,
|
|
11
|
-
UnmergedEntrySummary,
|
|
12
|
-
} from '../models/status.js'
|
|
13
|
-
import {
|
|
14
|
-
parsePorcelainStatus,
|
|
15
|
-
mapStatus,
|
|
16
|
-
IStatusEntry,
|
|
17
|
-
IStatusHeader,
|
|
18
|
-
isStatusHeader,
|
|
19
|
-
isStatusEntry,
|
|
20
|
-
} from '../lib/status-parser.js'
|
|
21
|
-
import { DiffSelectionType, DiffSelection } from '../models/diff/index.js'
|
|
22
|
-
import { Repository } from '../models/repository.js'
|
|
23
|
-
import { IAheadBehind } from '../models/branch.js'
|
|
24
|
-
import { fatalError } from '../lib/fatal-error.js'
|
|
25
|
-
import { isMergeHeadSet, isSquashMsgSet } from './merge.js'
|
|
26
|
-
import { getBinaryPaths } from './diff.js'
|
|
27
|
-
import { getRebaseInternalState } from './rebase.js'
|
|
28
|
-
import { RebaseInternalState } from '../models/rebase.js'
|
|
29
|
-
import { isCherryPickHeadFound } from './cherry-pick.js'
|
|
30
|
-
import { git } from './index.js'
|
|
31
|
-
|
|
32
|
-
/** The encapsulation of the result from 'git status' */
|
|
33
|
-
export interface IStatusResult {
|
|
34
|
-
/** The name of the current branch */
|
|
35
|
-
readonly currentBranch?: string
|
|
36
|
-
|
|
37
|
-
/** The name of the current upstream branch */
|
|
38
|
-
readonly currentUpstreamBranch?: string
|
|
39
|
-
|
|
40
|
-
/** The SHA of the tip commit of the current branch */
|
|
41
|
-
readonly currentTip?: string
|
|
42
|
-
|
|
43
|
-
/** How many commits ahead and behind
|
|
44
|
-
* the `currentBranch` is compared to the `currentUpstreamBranch`
|
|
45
|
-
*/
|
|
46
|
-
readonly branchAheadBehind?: IAheadBehind
|
|
47
|
-
|
|
48
|
-
/** true if the repository exists at the given location */
|
|
49
|
-
readonly exists: boolean
|
|
50
|
-
|
|
51
|
-
/** true if repository is in a conflicted state */
|
|
52
|
-
readonly mergeHeadFound: boolean
|
|
53
|
-
|
|
54
|
-
/** true merge --squash operation started */
|
|
55
|
-
readonly squashMsgFound: boolean
|
|
56
|
-
|
|
57
|
-
/** details about the rebase operation, if found */
|
|
58
|
-
readonly rebaseInternalState: RebaseInternalState | null
|
|
59
|
-
|
|
60
|
-
/** true if repository is in cherry pick state */
|
|
61
|
-
readonly isCherryPickingHeadFound: boolean
|
|
62
|
-
|
|
63
|
-
/** the absolute path to the repository's working directory */
|
|
64
|
-
readonly workingDirectory: WorkingDirectoryStatus
|
|
65
|
-
|
|
66
|
-
/** whether conflicting files present on repository */
|
|
67
|
-
readonly doConflictedFilesExist: boolean
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
interface IStatusHeadersData {
|
|
71
|
-
currentBranch?: string
|
|
72
|
-
currentUpstreamBranch?: string
|
|
73
|
-
currentTip?: string
|
|
74
|
-
branchAheadBehind?: IAheadBehind
|
|
75
|
-
match: RegExpMatchArray | null
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
type ConflictFilesDetails = {
|
|
79
|
-
conflictCountsByPath: ReadonlyMap<string, number>
|
|
80
|
-
binaryFilePaths: ReadonlyArray<string>
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function parseConflictedState(
|
|
84
|
-
entry: UnmergedEntry,
|
|
85
|
-
path: string,
|
|
86
|
-
conflictDetails: ConflictFilesDetails
|
|
87
|
-
): ConflictedFileStatus {
|
|
88
|
-
switch (entry.action) {
|
|
89
|
-
case UnmergedEntrySummary.BothAdded: {
|
|
90
|
-
const isBinary = conflictDetails.binaryFilePaths.includes(path)
|
|
91
|
-
if (!isBinary) {
|
|
92
|
-
return {
|
|
93
|
-
kind: AppFileStatusKind.Conflicted,
|
|
94
|
-
entry,
|
|
95
|
-
conflictMarkerCount:
|
|
96
|
-
conflictDetails.conflictCountsByPath.get(path) || 0,
|
|
97
|
-
}
|
|
98
|
-
} else {
|
|
99
|
-
return {
|
|
100
|
-
kind: AppFileStatusKind.Conflicted,
|
|
101
|
-
entry,
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
case UnmergedEntrySummary.BothModified: {
|
|
106
|
-
const isBinary = conflictDetails.binaryFilePaths.includes(path)
|
|
107
|
-
if (!isBinary) {
|
|
108
|
-
return {
|
|
109
|
-
kind: AppFileStatusKind.Conflicted,
|
|
110
|
-
entry,
|
|
111
|
-
conflictMarkerCount:
|
|
112
|
-
conflictDetails.conflictCountsByPath.get(path) || 0,
|
|
113
|
-
}
|
|
114
|
-
} else {
|
|
115
|
-
return {
|
|
116
|
-
kind: AppFileStatusKind.Conflicted,
|
|
117
|
-
entry,
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
default:
|
|
122
|
-
return {
|
|
123
|
-
kind: AppFileStatusKind.Conflicted,
|
|
124
|
-
entry,
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
function convertToAppStatus(
|
|
130
|
-
path: string,
|
|
131
|
-
entry: FileEntry,
|
|
132
|
-
conflictDetails: ConflictFilesDetails,
|
|
133
|
-
oldPath?: string
|
|
134
|
-
): AppFileStatus {
|
|
135
|
-
if (entry.kind === 'ordinary') {
|
|
136
|
-
switch (entry.type) {
|
|
137
|
-
case 'added':
|
|
138
|
-
return {
|
|
139
|
-
kind: AppFileStatusKind.New,
|
|
140
|
-
submoduleStatus: entry.submoduleStatus,
|
|
141
|
-
}
|
|
142
|
-
case 'modified':
|
|
143
|
-
return {
|
|
144
|
-
kind: AppFileStatusKind.Modified,
|
|
145
|
-
submoduleStatus: entry.submoduleStatus,
|
|
146
|
-
}
|
|
147
|
-
case 'deleted':
|
|
148
|
-
return {
|
|
149
|
-
kind: AppFileStatusKind.Deleted,
|
|
150
|
-
submoduleStatus: entry.submoduleStatus,
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
} else if (entry.kind === 'copied' && oldPath != null) {
|
|
154
|
-
return {
|
|
155
|
-
kind: AppFileStatusKind.Copied,
|
|
156
|
-
oldPath,
|
|
157
|
-
submoduleStatus: entry.submoduleStatus,
|
|
158
|
-
renameIncludesModifications: false,
|
|
159
|
-
}
|
|
160
|
-
} else if (entry.kind === 'renamed' && oldPath != null) {
|
|
161
|
-
return {
|
|
162
|
-
kind: AppFileStatusKind.Renamed,
|
|
163
|
-
oldPath,
|
|
164
|
-
submoduleStatus: entry.submoduleStatus,
|
|
165
|
-
renameIncludesModifications:
|
|
166
|
-
entry.workingTree === GitStatusEntry.Modified ||
|
|
167
|
-
(entry.renameOrCopyScore !== undefined &&
|
|
168
|
-
entry.renameOrCopyScore < 100),
|
|
169
|
-
}
|
|
170
|
-
} else if (entry.kind === 'untracked') {
|
|
171
|
-
return {
|
|
172
|
-
kind: AppFileStatusKind.Untracked,
|
|
173
|
-
submoduleStatus: entry.submoduleStatus,
|
|
174
|
-
}
|
|
175
|
-
} else if (entry.kind === 'conflicted') {
|
|
176
|
-
return parseConflictedState(entry, path, conflictDetails)
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
return fatalError(`Unknown file status ${status}`)
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// List of known conflicted index entries for a file, extracted from mapStatus
|
|
183
|
-
// inside `app/src/lib/status-parser.ts` for convenience
|
|
184
|
-
const conflictStatusCodes = ['DD', 'AU', 'UD', 'UA', 'DU', 'AA', 'UU']
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Retrieve the status for a given repository,
|
|
188
|
-
* and fail gracefully if the location is not a Git repository
|
|
189
|
-
*/
|
|
190
|
-
export async function getStatus(
|
|
191
|
-
repository: Repository
|
|
192
|
-
): Promise<IStatusResult | null>
|
|
193
|
-
export async function getStatus(
|
|
194
|
-
repository: Repository,
|
|
195
|
-
includeUntracked: boolean
|
|
196
|
-
): Promise<IStatusResult | null>
|
|
197
|
-
export async function getStatus(
|
|
198
|
-
repository: Repository,
|
|
199
|
-
includeUntracked: boolean,
|
|
200
|
-
rejectOnError: true
|
|
201
|
-
): Promise<IStatusResult>
|
|
202
|
-
export async function getStatus(
|
|
203
|
-
repository: Repository,
|
|
204
|
-
includeUntracked: boolean,
|
|
205
|
-
rejectOnError: false
|
|
206
|
-
): Promise<IStatusResult | null>
|
|
207
|
-
export async function getStatus(
|
|
208
|
-
repository: Repository,
|
|
209
|
-
includeUntracked = true,
|
|
210
|
-
rejectOnError = false
|
|
211
|
-
): Promise<IStatusResult | null> {
|
|
212
|
-
const args = [
|
|
213
|
-
'--no-optional-locks',
|
|
214
|
-
'status',
|
|
215
|
-
...(includeUntracked ? ['--untracked-files=all'] : []),
|
|
216
|
-
'--branch',
|
|
217
|
-
'--porcelain=2',
|
|
218
|
-
'-z',
|
|
219
|
-
]
|
|
220
|
-
|
|
221
|
-
const { stdout, exitCode } = await git(args, repository.path, 'getStatus', {
|
|
222
|
-
successExitCodes: new Set(rejectOnError ? [0] : [0, 128]),
|
|
223
|
-
encoding: 'buffer',
|
|
224
|
-
})
|
|
225
|
-
|
|
226
|
-
if (exitCode === 128) {
|
|
227
|
-
console.debug(
|
|
228
|
-
`'git status' returned 128 for '${repository.path}' and is likely missing its .git directory`
|
|
229
|
-
)
|
|
230
|
-
return null
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
const parsed = parsePorcelainStatus(stdout)
|
|
234
|
-
const headers = parsed.filter(isStatusHeader)
|
|
235
|
-
const entries = parsed.filter(isStatusEntry)
|
|
236
|
-
|
|
237
|
-
const mergeHeadFound = await isMergeHeadSet(repository)
|
|
238
|
-
const conflictedFilesInIndex = entries.filter(e =>
|
|
239
|
-
conflictStatusCodes.includes(e.statusCode)
|
|
240
|
-
)
|
|
241
|
-
const rebaseInternalState = await getRebaseInternalState(repository)
|
|
242
|
-
|
|
243
|
-
const conflictDetails = await getConflictDetails(
|
|
244
|
-
repository,
|
|
245
|
-
mergeHeadFound,
|
|
246
|
-
conflictedFilesInIndex,
|
|
247
|
-
rebaseInternalState
|
|
248
|
-
)
|
|
249
|
-
|
|
250
|
-
// Map of files keyed on their paths.
|
|
251
|
-
const files = entries.reduce(
|
|
252
|
-
(files, entry) => buildStatusMap(files, entry, conflictDetails),
|
|
253
|
-
new Map<string, WorkingDirectoryFileChange>()
|
|
254
|
-
)
|
|
255
|
-
|
|
256
|
-
const {
|
|
257
|
-
currentBranch,
|
|
258
|
-
currentUpstreamBranch,
|
|
259
|
-
currentTip,
|
|
260
|
-
branchAheadBehind,
|
|
261
|
-
} = headers.reduce(parseStatusHeader, {
|
|
262
|
-
currentBranch: undefined,
|
|
263
|
-
currentUpstreamBranch: undefined,
|
|
264
|
-
currentTip: undefined,
|
|
265
|
-
branchAheadBehind: undefined,
|
|
266
|
-
match: null,
|
|
267
|
-
})
|
|
268
|
-
|
|
269
|
-
const workingDirectory = WorkingDirectoryStatus.fromFiles([...files.values()])
|
|
270
|
-
|
|
271
|
-
const isCherryPickingHeadFound = await isCherryPickHeadFound(repository)
|
|
272
|
-
|
|
273
|
-
const squashMsgFound = await isSquashMsgSet(repository)
|
|
274
|
-
|
|
275
|
-
return {
|
|
276
|
-
currentBranch,
|
|
277
|
-
currentTip,
|
|
278
|
-
currentUpstreamBranch,
|
|
279
|
-
branchAheadBehind,
|
|
280
|
-
exists: true,
|
|
281
|
-
mergeHeadFound,
|
|
282
|
-
rebaseInternalState,
|
|
283
|
-
workingDirectory,
|
|
284
|
-
isCherryPickingHeadFound,
|
|
285
|
-
squashMsgFound,
|
|
286
|
-
doConflictedFilesExist: conflictedFilesInIndex.length > 0,
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
/**
|
|
291
|
-
*
|
|
292
|
-
* Update map of working directory changes with a file status entry.
|
|
293
|
-
* Reducer(ish).
|
|
294
|
-
*
|
|
295
|
-
* (Map is used here to maintain insertion order.)
|
|
296
|
-
*/
|
|
297
|
-
function buildStatusMap(
|
|
298
|
-
files: Map<string, WorkingDirectoryFileChange>,
|
|
299
|
-
entry: IStatusEntry,
|
|
300
|
-
conflictDetails: ConflictFilesDetails
|
|
301
|
-
): Map<string, WorkingDirectoryFileChange> {
|
|
302
|
-
const status = mapStatus(
|
|
303
|
-
entry.statusCode,
|
|
304
|
-
entry.submoduleStatusCode,
|
|
305
|
-
entry.renameOrCopyScore
|
|
306
|
-
)
|
|
307
|
-
|
|
308
|
-
if (status.kind === 'ordinary') {
|
|
309
|
-
// when a file is added in the index but then removed in the working
|
|
310
|
-
// directory, the file won't be part of the commit, so we can skip
|
|
311
|
-
// displaying this entry in the changes list
|
|
312
|
-
if (
|
|
313
|
-
status.index === GitStatusEntry.Added &&
|
|
314
|
-
status.workingTree === GitStatusEntry.Deleted
|
|
315
|
-
) {
|
|
316
|
-
return files
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
if (status.kind === 'untracked') {
|
|
321
|
-
// when a delete has been staged, but an untracked file exists with the
|
|
322
|
-
// same path, we should ensure that we only draw one entry in the
|
|
323
|
-
// changes list - see if an entry already exists for this path and
|
|
324
|
-
// remove it if found
|
|
325
|
-
files.delete(entry.path)
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
// for now we just poke at the existing summary
|
|
329
|
-
const appStatus = convertToAppStatus(
|
|
330
|
-
entry.path,
|
|
331
|
-
status,
|
|
332
|
-
conflictDetails,
|
|
333
|
-
entry.oldPath
|
|
334
|
-
)
|
|
335
|
-
|
|
336
|
-
const initialSelectionType =
|
|
337
|
-
appStatus.kind === AppFileStatusKind.Modified &&
|
|
338
|
-
appStatus.submoduleStatus !== undefined &&
|
|
339
|
-
!appStatus.submoduleStatus.commitChanged
|
|
340
|
-
? DiffSelectionType.None
|
|
341
|
-
: DiffSelectionType.All
|
|
342
|
-
|
|
343
|
-
const selection = DiffSelection.fromInitialSelection(initialSelectionType)
|
|
344
|
-
|
|
345
|
-
files.set(
|
|
346
|
-
entry.path,
|
|
347
|
-
new WorkingDirectoryFileChange(entry.path, appStatus, selection)
|
|
348
|
-
)
|
|
349
|
-
return files
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
/**
|
|
353
|
-
* Update status header based on the current header entry.
|
|
354
|
-
* Reducer.
|
|
355
|
-
*/
|
|
356
|
-
function parseStatusHeader(results: IStatusHeadersData, header: IStatusHeader) {
|
|
357
|
-
let {
|
|
358
|
-
currentBranch,
|
|
359
|
-
currentUpstreamBranch,
|
|
360
|
-
currentTip,
|
|
361
|
-
branchAheadBehind,
|
|
362
|
-
match,
|
|
363
|
-
} = results
|
|
364
|
-
const value = header.value
|
|
365
|
-
|
|
366
|
-
// This intentionally does not match branch.oid initial
|
|
367
|
-
if ((match = value.match(/^branch\.oid ([a-f0-9]+)$/))) {
|
|
368
|
-
currentTip = match[1]
|
|
369
|
-
} else if ((match = value.match(/^branch.head (.*)/))) {
|
|
370
|
-
if (match[1] !== '(detached)') {
|
|
371
|
-
currentBranch = match[1]
|
|
372
|
-
}
|
|
373
|
-
} else if ((match = value.match(/^branch.upstream (.*)/))) {
|
|
374
|
-
currentUpstreamBranch = match[1]
|
|
375
|
-
} else if ((match = value.match(/^branch.ab \+(\d+) -(\d+)$/))) {
|
|
376
|
-
const ahead = parseInt(match[1], 10)
|
|
377
|
-
const behind = parseInt(match[2], 10)
|
|
378
|
-
|
|
379
|
-
if (!isNaN(ahead) && !isNaN(behind)) {
|
|
380
|
-
branchAheadBehind = { ahead, behind }
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
return {
|
|
384
|
-
currentBranch,
|
|
385
|
-
currentUpstreamBranch,
|
|
386
|
-
currentTip,
|
|
387
|
-
branchAheadBehind,
|
|
388
|
-
match,
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
async function getMergeConflictDetails(
|
|
393
|
-
repository: Repository,
|
|
394
|
-
conflictedFilesInIndex: ReadonlyArray<IStatusEntry>
|
|
395
|
-
) {
|
|
396
|
-
const conflictCountsByPath = await getFilesWithConflictMarkers(
|
|
397
|
-
repository.path
|
|
398
|
-
)
|
|
399
|
-
const binaryFilePaths = await getBinaryPaths(
|
|
400
|
-
repository,
|
|
401
|
-
'MERGE_HEAD',
|
|
402
|
-
conflictedFilesInIndex
|
|
403
|
-
)
|
|
404
|
-
return {
|
|
405
|
-
conflictCountsByPath,
|
|
406
|
-
binaryFilePaths,
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
async function getRebaseConflictDetails(
|
|
411
|
-
repository: Repository,
|
|
412
|
-
conflictedFilesInIndex: ReadonlyArray<IStatusEntry>
|
|
413
|
-
) {
|
|
414
|
-
const conflictCountsByPath = await getFilesWithConflictMarkers(
|
|
415
|
-
repository.path
|
|
416
|
-
)
|
|
417
|
-
const binaryFilePaths = await getBinaryPaths(
|
|
418
|
-
repository,
|
|
419
|
-
'REBASE_HEAD',
|
|
420
|
-
conflictedFilesInIndex
|
|
421
|
-
)
|
|
422
|
-
return {
|
|
423
|
-
conflictCountsByPath,
|
|
424
|
-
binaryFilePaths,
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
/**
|
|
429
|
-
* We need to do these operations to detect conflicts that were the result
|
|
430
|
-
* of popping a stash into the index
|
|
431
|
-
*/
|
|
432
|
-
async function getWorkingDirectoryConflictDetails(
|
|
433
|
-
repository: Repository,
|
|
434
|
-
conflictedFilesInIndex: ReadonlyArray<IStatusEntry>
|
|
435
|
-
) {
|
|
436
|
-
const conflictCountsByPath = await getFilesWithConflictMarkers(
|
|
437
|
-
repository.path
|
|
438
|
-
)
|
|
439
|
-
let binaryFilePaths: ReadonlyArray<string> = []
|
|
440
|
-
try {
|
|
441
|
-
// its totally fine if HEAD doesn't exist, which throws an error
|
|
442
|
-
binaryFilePaths = await getBinaryPaths(
|
|
443
|
-
repository,
|
|
444
|
-
'HEAD',
|
|
445
|
-
conflictedFilesInIndex
|
|
446
|
-
)
|
|
447
|
-
} catch (error) {}
|
|
448
|
-
|
|
449
|
-
return {
|
|
450
|
-
conflictCountsByPath,
|
|
451
|
-
binaryFilePaths,
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
/**
|
|
456
|
-
* gets the conflicted files count and binary file paths in a given repository.
|
|
457
|
-
* for computing an `IStatusResult`.
|
|
458
|
-
*
|
|
459
|
-
* @param repository to get details from
|
|
460
|
-
* @param mergeHeadFound whether a merge conflict has been detected
|
|
461
|
-
* @param conflictedFilesInIndex all files marked as being conflicted in the
|
|
462
|
-
* index. Used to check for files using the binary
|
|
463
|
-
* merge driver and whether it looks like a stash
|
|
464
|
-
* has introduced conflicts
|
|
465
|
-
* @param rebaseInternalState details about the current rebase operation (if
|
|
466
|
-
* found)
|
|
467
|
-
*/
|
|
468
|
-
async function getConflictDetails(
|
|
469
|
-
repository: Repository,
|
|
470
|
-
mergeHeadFound: boolean,
|
|
471
|
-
conflictedFilesInIndex: ReadonlyArray<IStatusEntry>,
|
|
472
|
-
rebaseInternalState: RebaseInternalState | null
|
|
473
|
-
): Promise<ConflictFilesDetails> {
|
|
474
|
-
try {
|
|
475
|
-
if (mergeHeadFound) {
|
|
476
|
-
return await getMergeConflictDetails(repository, conflictedFilesInIndex)
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
if (rebaseInternalState !== null) {
|
|
480
|
-
return await getRebaseConflictDetails(repository, conflictedFilesInIndex)
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
// If there's conflicted files in the index but we don't have a merge head
|
|
484
|
-
// or a rebase internal state, then we're likely in a situation where a
|
|
485
|
-
// stash has introduced conflicts
|
|
486
|
-
if (conflictedFilesInIndex.length > 0) {
|
|
487
|
-
return await getWorkingDirectoryConflictDetails(
|
|
488
|
-
repository,
|
|
489
|
-
conflictedFilesInIndex
|
|
490
|
-
)
|
|
491
|
-
}
|
|
492
|
-
} catch (error) {
|
|
493
|
-
console.error(
|
|
494
|
-
'Unexpected error from git operations in getConflictDetails',
|
|
495
|
-
error
|
|
496
|
-
)
|
|
497
|
-
}
|
|
498
|
-
return {
|
|
499
|
-
conflictCountsByPath: new Map<string, number>(),
|
|
500
|
-
binaryFilePaths: new Array<string>(),
|
|
501
|
-
}
|
|
502
|
-
}
|
package/src/git/submodule.ts
DELETED
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
import { git, IGitStringExecutionOptions } from './core.js'
|
|
2
|
-
import { Repository } from '../models/repository.js'
|
|
3
|
-
import { SubmoduleEntry } from '../models/submodule.js'
|
|
4
|
-
import { pathExists } from '../lib/path-exists.js'
|
|
5
|
-
import { executionOptionsWithProgress, IGitOutput } from '../lib/progress/index.js'
|
|
6
|
-
import {
|
|
7
|
-
envForRemoteOperation,
|
|
8
|
-
getFallbackUrlForProxyResolve,
|
|
9
|
-
} from './environment.js'
|
|
10
|
-
import { AuthenticationErrors } from './authentication.js'
|
|
11
|
-
import { IRemote } from '../models/remote.js'
|
|
12
|
-
import { Progress } from '../models/progress.js'
|
|
13
|
-
import { join, resolve } from 'path'
|
|
14
|
-
import { readFile } from 'fs/promises'
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Update submodules after a git operation.
|
|
18
|
-
*
|
|
19
|
-
* @param repository - The repository in which to update submodules
|
|
20
|
-
* @param remote - The remote for environment setup (can be null)
|
|
21
|
-
* @param progressCallback - An optional function which will be invoked
|
|
22
|
-
* with information about the current progress
|
|
23
|
-
* of the submodule update operation.
|
|
24
|
-
* @param progressKind - The kind of progress event ('checkout', 'pull', etc.)
|
|
25
|
-
* @param title - The title to use for progress reporting
|
|
26
|
-
* @param targetOrRemote - The target (for checkout) or remote name (for pull)
|
|
27
|
-
* @param allowFileProtocol - Whether to allow file:// protocol for submodules
|
|
28
|
-
*/
|
|
29
|
-
export async function updateSubmodulesAfterOperation<T extends Progress>(
|
|
30
|
-
repository: Repository,
|
|
31
|
-
remote: IRemote | null,
|
|
32
|
-
progressCallback: ((progress: T) => void) | undefined,
|
|
33
|
-
progressKind: T['kind'],
|
|
34
|
-
title: string,
|
|
35
|
-
targetOrRemote: string,
|
|
36
|
-
allowFileProtocol: boolean
|
|
37
|
-
): Promise<void> {
|
|
38
|
-
const opts: IGitStringExecutionOptions = {
|
|
39
|
-
env: await envForRemoteOperation(
|
|
40
|
-
getFallbackUrlForProxyResolve(repository, remote)
|
|
41
|
-
),
|
|
42
|
-
expectedErrors: AuthenticationErrors,
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const args = [
|
|
46
|
-
...(allowFileProtocol ? ['-c', 'protocol.file.allow=always'] : []),
|
|
47
|
-
'submodule',
|
|
48
|
-
'update',
|
|
49
|
-
'--init',
|
|
50
|
-
'--recursive',
|
|
51
|
-
]
|
|
52
|
-
|
|
53
|
-
if (!progressCallback) {
|
|
54
|
-
await git(args, repository.path, 'updateSubmodules', opts)
|
|
55
|
-
return
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Initial progress
|
|
59
|
-
progressCallback({
|
|
60
|
-
kind: progressKind,
|
|
61
|
-
title,
|
|
62
|
-
description: 'Updating submodules',
|
|
63
|
-
value: 0,
|
|
64
|
-
// Add the target or remote field based on the progress kind
|
|
65
|
-
...(progressKind === 'checkout'
|
|
66
|
-
? { target: targetOrRemote }
|
|
67
|
-
: { remote: targetOrRemote }),
|
|
68
|
-
} as T)
|
|
69
|
-
|
|
70
|
-
let submoduleEventCount = 0
|
|
71
|
-
|
|
72
|
-
const progressOpts = await executionOptionsWithProgress(
|
|
73
|
-
{ ...opts, trackLFSProgress: true },
|
|
74
|
-
{
|
|
75
|
-
parse(line: string): IGitOutput {
|
|
76
|
-
if (
|
|
77
|
-
line.match(/^Submodule path (.)+?: checked out /) ||
|
|
78
|
-
line.startsWith('Cloning into ')
|
|
79
|
-
) {
|
|
80
|
-
submoduleEventCount += 1
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return {
|
|
84
|
-
kind: 'context',
|
|
85
|
-
text: `Updating submodules: ${line}`,
|
|
86
|
-
// Math taken from https://math.stackexchange.com/a/2323106
|
|
87
|
-
// We do this to fake a progress that slows down as we process more
|
|
88
|
-
// events, as we don't know how many submodules there are upfront, or
|
|
89
|
-
// what does git have to do with them (cloning, just checking them
|
|
90
|
-
// out...)
|
|
91
|
-
percent: 1 - Math.exp(-submoduleEventCount * 0.25),
|
|
92
|
-
}
|
|
93
|
-
},
|
|
94
|
-
},
|
|
95
|
-
progress => {
|
|
96
|
-
const description =
|
|
97
|
-
progress.kind === 'progress' ? progress.details.text : progress.text
|
|
98
|
-
|
|
99
|
-
const value = progress.percent
|
|
100
|
-
|
|
101
|
-
progressCallback({
|
|
102
|
-
kind: progressKind,
|
|
103
|
-
title,
|
|
104
|
-
description,
|
|
105
|
-
value,
|
|
106
|
-
...(progressKind === 'checkout'
|
|
107
|
-
? { target: targetOrRemote }
|
|
108
|
-
: { remote: targetOrRemote }),
|
|
109
|
-
} as T)
|
|
110
|
-
}
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
await git(args, repository.path, 'updateSubmodules', progressOpts)
|
|
114
|
-
|
|
115
|
-
// Final progress
|
|
116
|
-
progressCallback({
|
|
117
|
-
kind: progressKind,
|
|
118
|
-
title,
|
|
119
|
-
description: 'Submodules updated',
|
|
120
|
-
value: 1,
|
|
121
|
-
...(progressKind === 'checkout'
|
|
122
|
-
? { target: targetOrRemote }
|
|
123
|
-
: { remote: targetOrRemote }),
|
|
124
|
-
} as T)
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
export async function listSubmodules(
|
|
128
|
-
repository: Repository
|
|
129
|
-
): Promise<ReadonlyArray<SubmoduleEntry>> {
|
|
130
|
-
const [submodulesFile, submodulesDir] = await Promise.all([
|
|
131
|
-
pathExists(join(repository.path, '.gitmodules')),
|
|
132
|
-
pathExists(join(repository.path, '.git', 'modules')),
|
|
133
|
-
])
|
|
134
|
-
|
|
135
|
-
if (!submodulesFile && !submodulesDir) {
|
|
136
|
-
// repo path + .gitmodules and + .git/modules covers the vast majority of
|
|
137
|
-
// "normal" repositories but if we're in a linked worktree the modules
|
|
138
|
-
// directory is actually in the git common dir so we'll also check for the
|
|
139
|
-
// existence of the modules directory there as well before giving up on the
|
|
140
|
-
// existence of submodules in this repo. We're reading the commondir file
|
|
141
|
-
// ourselves here instead of calling out to git to avoid the cost of
|
|
142
|
-
// spawning a process on Windows
|
|
143
|
-
const commonDirPath = join(repository.resolvedGitDir, 'commondir')
|
|
144
|
-
const commonDir = await readFile(commonDirPath, 'utf8')
|
|
145
|
-
.then(content => content.replace(/\r?\n$/, ''))
|
|
146
|
-
.then(p => (p ? resolve(repository.resolvedGitDir, p) : null))
|
|
147
|
-
.catch(() => null)
|
|
148
|
-
|
|
149
|
-
if (!commonDir || !(await pathExists(join(commonDir, 'modules')))) {
|
|
150
|
-
console.info('No submodules found. Skipping "git submodule status"')
|
|
151
|
-
return []
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// We don't recurse when listing submodules here because we don't have a good
|
|
156
|
-
// story about managing these currently. So for now we're only listing
|
|
157
|
-
// changes to the top-level submodules to be consistent with `git status`
|
|
158
|
-
const { stdout, exitCode } = await git(
|
|
159
|
-
['submodule', 'status', '--'],
|
|
160
|
-
repository.path,
|
|
161
|
-
'listSubmodules',
|
|
162
|
-
{ successExitCodes: new Set([0, 128]) }
|
|
163
|
-
)
|
|
164
|
-
|
|
165
|
-
if (exitCode === 128) {
|
|
166
|
-
// unable to parse submodules in repository, giving up
|
|
167
|
-
return []
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const submodules = new Array<SubmoduleEntry>()
|
|
171
|
-
|
|
172
|
-
// entries are of the format:
|
|
173
|
-
// 1eaabe34fc6f486367a176207420378f587d3b48 git (v2.16.0-rc0)
|
|
174
|
-
//
|
|
175
|
-
// first character:
|
|
176
|
-
// - " " if no change
|
|
177
|
-
// - "-" if the submodule is not initialized
|
|
178
|
-
// - "+" if the currently checked out submodule commit does not match the SHA-1 found in the index of the containing repository
|
|
179
|
-
// - "U" if the submodule has merge conflicts
|
|
180
|
-
//
|
|
181
|
-
// then the 40-character SHA represents the current commit
|
|
182
|
-
//
|
|
183
|
-
// then the path to the submodule
|
|
184
|
-
//
|
|
185
|
-
// then the output of `git describe` for the submodule in braces
|
|
186
|
-
// we're not leveraging this in the app, so go and read the docs
|
|
187
|
-
// about it if you want to learn more:
|
|
188
|
-
//
|
|
189
|
-
// https://git-scm.com/docs/git-describe
|
|
190
|
-
const statusRe = /^.([^ ]+) (.+) \((.+?)\)$/gm
|
|
191
|
-
|
|
192
|
-
for (const [, sha, path, describe] of stdout.matchAll(statusRe)) {
|
|
193
|
-
submodules.push({ path, url: '', describe, sha, status: { commitChanged: false, modifiedChanges: false, untrackedChanges: false } })
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
return submodules
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
export async function resetSubmodulePaths(
|
|
200
|
-
repository: Repository,
|
|
201
|
-
paths: ReadonlyArray<string>
|
|
202
|
-
): Promise<void> {
|
|
203
|
-
if (paths.length === 0) {
|
|
204
|
-
return
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
await git(
|
|
208
|
-
['submodule', 'update', '--recursive', '--force', '--', ...paths],
|
|
209
|
-
repository.path,
|
|
210
|
-
'updateSubmodule'
|
|
211
|
-
)
|
|
212
|
-
}
|