git-chopstick-core 0.1.2 → 0.1.4

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.
Files changed (145) hide show
  1. package/README.md +42 -10
  2. package/dist/git/apply.d.ts +1 -1
  3. package/dist/git/apply.js +1 -1
  4. package/dist/git/apply.js.map +1 -1
  5. package/dist/git/environment.d.ts +1 -1
  6. package/dist/git/environment.js +1 -1
  7. package/dist/git/environment.js.map +1 -1
  8. package/dist/git/exec.js.map +1 -1
  9. package/dist/git/gitignore.js +1 -1
  10. package/dist/git/gitignore.js.map +1 -1
  11. package/dist/git/index.d.ts +1 -0
  12. package/dist/git/index.js +3 -0
  13. package/dist/git/index.js.map +1 -1
  14. package/dist/lib/fatal-error.d.ts +1 -1
  15. package/dist/lib/fatal-error.js +1 -1
  16. package/dist/lib/fatal-error.js.map +1 -1
  17. package/dist/lib/progress/from-process.js.map +1 -1
  18. package/dist/lib/progress/index.d.ts +5 -5
  19. package/dist/lib/progress/index.js +5 -5
  20. package/dist/lib/progress/index.js.map +1 -1
  21. package/dist/lib/progress/revert.d.ts +1 -1
  22. package/dist/lib/progress/revert.js +1 -1
  23. package/dist/lib/progress/revert.js.map +1 -1
  24. package/dist/lib/status-parser.js +0 -12
  25. package/dist/lib/status-parser.js.map +1 -1
  26. package/dist/models/repository.d.ts +1 -1
  27. package/dist/models/repository.js +1 -1
  28. package/dist/models/repository.js.map +1 -1
  29. package/package.json +39 -9
  30. package/src/git/add.ts +0 -16
  31. package/src/git/apply.ts +0 -154
  32. package/src/git/authentication.ts +0 -20
  33. package/src/git/branch.ts +0 -206
  34. package/src/git/checkout-index.ts +0 -40
  35. package/src/git/checkout.ts +0 -235
  36. package/src/git/cherry-pick.ts +0 -504
  37. package/src/git/clean.ts +0 -9
  38. package/src/git/clone.ts +0 -86
  39. package/src/git/coerce-to-buffer.ts +0 -4
  40. package/src/git/coerce-to-string.ts +0 -4
  41. package/src/git/commit.ts +0 -136
  42. package/src/git/config.ts +0 -392
  43. package/src/git/core.ts +0 -625
  44. package/src/git/create-tail-stream.ts +0 -36
  45. package/src/git/credential.ts +0 -83
  46. package/src/git/description.ts +0 -33
  47. package/src/git/diff-check.ts +0 -27
  48. package/src/git/diff-index.ts +0 -116
  49. package/src/git/diff.ts +0 -880
  50. package/src/git/environment.ts +0 -116
  51. package/src/git/exec.ts +0 -285
  52. package/src/git/fetch.ts +0 -141
  53. package/src/git/for-each-ref.ts +0 -160
  54. package/src/git/format-patch.ts +0 -17
  55. package/src/git/git-delimiter-parser.ts +0 -95
  56. package/src/git/gitignore.ts +0 -157
  57. package/src/git/index.ts +0 -46
  58. package/src/git/init.ts +0 -11
  59. package/src/git/interpret-trailers.ts +0 -176
  60. package/src/git/lfs.ts +0 -100
  61. package/src/git/log.ts +0 -376
  62. package/src/git/merge-tree.ts +0 -42
  63. package/src/git/merge.ts +0 -154
  64. package/src/git/multi-operation-terminal-output.ts +0 -68
  65. package/src/git/pull.ts +0 -130
  66. package/src/git/push-terminal-chunk.ts +0 -41
  67. package/src/git/push.ts +0 -119
  68. package/src/git/rebase.ts +0 -627
  69. package/src/git/reflog.ts +0 -127
  70. package/src/git/refs.ts +0 -63
  71. package/src/git/remote.ts +0 -143
  72. package/src/git/reorder.ts +0 -153
  73. package/src/git/reset.ts +0 -101
  74. package/src/git/rev-list.ts +0 -201
  75. package/src/git/rev-parse.ts +0 -92
  76. package/src/git/revert.ts +0 -55
  77. package/src/git/rm.ts +0 -31
  78. package/src/git/show.ts +0 -88
  79. package/src/git/spawn.ts +0 -38
  80. package/src/git/squash.ts +0 -173
  81. package/src/git/stage.ts +0 -97
  82. package/src/git/stash.ts +0 -302
  83. package/src/git/status.ts +0 -502
  84. package/src/git/submodule.ts +0 -212
  85. package/src/git/tag.ts +0 -134
  86. package/src/git/update-index.ts +0 -169
  87. package/src/git/update-ref.ts +0 -50
  88. package/src/git/var.ts +0 -42
  89. package/src/git/worktree-include.ts +0 -146
  90. package/src/git/worktree.ts +0 -219
  91. package/src/index.ts +0 -11
  92. package/src/lib/api.ts +0 -7
  93. package/src/lib/diff-parser.ts +0 -249
  94. package/src/lib/directory-exists.ts +0 -10
  95. package/src/lib/errno-exception.ts +0 -12
  96. package/src/lib/fatal-error.ts +0 -23
  97. package/src/lib/feature-flag.ts +0 -29
  98. package/src/lib/file-system.ts +0 -7
  99. package/src/lib/get-old-path.ts +0 -11
  100. package/src/lib/git/environment.ts +0 -14
  101. package/src/lib/git-perf.ts +0 -3
  102. package/src/lib/helpers/default-branch.ts +0 -3
  103. package/src/lib/helpers/path.ts +0 -5
  104. package/src/lib/hooks/with-hooks-env.ts +0 -7
  105. package/src/lib/merge.ts +0 -3
  106. package/src/lib/noop.ts +0 -1
  107. package/src/lib/patch-formatter.ts +0 -18
  108. package/src/lib/path-exists.ts +0 -7
  109. package/src/lib/progress/from-process.ts +0 -10
  110. package/src/lib/progress/index.ts +0 -43
  111. package/src/lib/progress/revert.ts +0 -17
  112. package/src/lib/rebase.ts +0 -3
  113. package/src/lib/remove-remote-prefix.ts +0 -4
  114. package/src/lib/resolve-git-proxy.ts +0 -3
  115. package/src/lib/round.ts +0 -4
  116. package/src/lib/split-buffer.ts +0 -14
  117. package/src/lib/status-parser.ts +0 -188
  118. package/src/lib/stores/helpers/find-default-remote.ts +0 -3
  119. package/src/lib/trampoline/trampoline-environment.ts +0 -8
  120. package/src/models/branch.ts +0 -78
  121. package/src/models/cherry-pick.ts +0 -12
  122. package/src/models/clone-options.ts +0 -6
  123. package/src/models/commit-identity.ts +0 -35
  124. package/src/models/commit.ts +0 -44
  125. package/src/models/computed-action.ts +0 -6
  126. package/src/models/diff/diff-data.ts +0 -78
  127. package/src/models/diff/diff-line.ts +0 -36
  128. package/src/models/diff/diff-selection.ts +0 -165
  129. package/src/models/diff/image-diff.ts +0 -6
  130. package/src/models/diff/image.ts +0 -8
  131. package/src/models/diff/index.ts +0 -6
  132. package/src/models/diff/raw-diff.ts +0 -41
  133. package/src/models/git-author.ts +0 -16
  134. package/src/models/index.ts +0 -36
  135. package/src/models/manual-conflict-resolution.ts +0 -4
  136. package/src/models/merge.ts +0 -6
  137. package/src/models/multi-commit-operation.ts +0 -6
  138. package/src/models/progress.ts +0 -67
  139. package/src/models/rebase.ts +0 -20
  140. package/src/models/remote.ts +0 -10
  141. package/src/models/repository.ts +0 -16
  142. package/src/models/stash-entry.ts +0 -25
  143. package/src/models/status.ts +0 -275
  144. package/src/models/submodule.ts +0 -13
  145. package/src/models/worktree.ts +0 -11
package/src/git/rebase.ts DELETED
@@ -1,627 +0,0 @@
1
- import { ChildProcess } from 'child_process'
2
- import { GitError } from './exec.js'
3
- import byline from 'byline'
4
-
5
- import { Repository } from '../models/repository.js'
6
- import { RebaseInternalState, RebaseProgressOptions } from '../models/rebase.js'
7
- import { IMultiCommitOperationProgress } from '../models/progress.js'
8
- import {
9
- WorkingDirectoryFileChange,
10
- AppFileStatusKind,
11
- } from '../models/status.js'
12
- import { ManualConflictResolution } from '../models/manual-conflict-resolution.js'
13
- import { Commit, CommitOneLine } from '../models/commit.js'
14
-
15
- import { merge } from '../lib/merge.js'
16
- import { formatRebaseValue } from '../lib/rebase.js'
17
-
18
- import {
19
- git,
20
- IGitExecutionOptions,
21
- gitRebaseArguments,
22
- IGitStringExecutionOptions,
23
- IGitStringResult,
24
- HookCallbackOptions,
25
- } from './core.js'
26
- import { stageManualConflictResolution } from './stage.js'
27
- import { stageFiles } from './update-index.js'
28
- import { getStatus } from './status.js'
29
- import { getCommitsBetweenCommits } from './rev-list.js'
30
- import { Branch } from '../models/branch.js'
31
- import { join } from 'path'
32
- import { readFile } from 'fs/promises'
33
- import { pathExists } from '../lib/path-exists.js'
34
-
35
- /** The app-specific results from attempting to rebase a repository */
36
- export enum RebaseResult {
37
- /**
38
- * Git completed the rebase without reporting any errors, and the caller can
39
- * signal success to the user.
40
- */
41
- CompletedWithoutError = 'CompletedWithoutError',
42
- /**
43
- * Git completed the rebase without reporting any errors, but the branch was
44
- * already up to date and there was nothing to do.
45
- */
46
- AlreadyUpToDate = 'AlreadyUpToDate',
47
- /**
48
- * The rebase encountered conflicts while attempting to rebase, and these
49
- * need to be resolved by the user before the rebase can continue.
50
- */
51
- ConflictsEncountered = 'ConflictsEncountered',
52
- /**
53
- * The rebase was not able to continue as tracked files were not staged in
54
- * the index.
55
- */
56
- OutstandingFilesNotStaged = 'OutstandingFilesNotStaged',
57
- /**
58
- * The rebase was not attempted because it could not check the status of the
59
- * repository. The caller needs to confirm the repository is in a usable
60
- * state.
61
- */
62
- Aborted = 'Aborted',
63
- /**
64
- * An unexpected error as part of the rebase flow was caught and handled.
65
- *
66
- * Check the logs to find the relevant Git details.
67
- */
68
- Error = 'Error',
69
- }
70
-
71
- /**
72
- * Check the `.git/REBASE_HEAD` file exists in a repository to confirm
73
- * a rebase operation is underway.
74
- */
75
- function isRebaseHeadSet(repository: Repository) {
76
- const path = join(repository.resolvedGitDir, 'REBASE_HEAD')
77
- return pathExists(path)
78
- }
79
-
80
- /**
81
- * Get the internal state about the rebase being performed on a repository. This
82
- * information is required to help Desktop display information to the user
83
- * about the current action as well as the options available.
84
- *
85
- * Returns `null` if no rebase is detected, or if the expected information
86
- * cannot be found in the repository.
87
- */
88
- export async function getRebaseInternalState(
89
- repository: Repository
90
- ): Promise<RebaseInternalState | null> {
91
- const isRebase = await isRebaseHeadSet(repository)
92
-
93
- if (!isRebase) {
94
- return null
95
- }
96
-
97
- let originalBranchTip: string | null = null
98
- let targetBranch: string | null = null
99
- let baseBranchTip: string | null = null
100
-
101
- try {
102
- originalBranchTip = await readFile(
103
- join(repository.resolvedGitDir, 'rebase-merge', 'orig-head'),
104
- 'utf8'
105
- )
106
-
107
- originalBranchTip = originalBranchTip.trim()
108
-
109
- targetBranch = await readFile(
110
- join(repository.resolvedGitDir, 'rebase-merge', 'head-name'),
111
- 'utf8'
112
- )
113
-
114
- if (targetBranch.startsWith('refs/heads/')) {
115
- targetBranch = targetBranch.substring(11).trim()
116
- }
117
-
118
- baseBranchTip = await readFile(
119
- join(repository.resolvedGitDir, 'rebase-merge', 'onto'),
120
- 'utf8'
121
- )
122
-
123
- baseBranchTip = baseBranchTip.trim()
124
- } catch {}
125
-
126
- if (
127
- originalBranchTip != null &&
128
- targetBranch != null &&
129
- baseBranchTip != null
130
- ) {
131
- return { originalBranchTip, targetBranch, baseBranchTip }
132
- }
133
-
134
- // unable to resolve the rebase state of this repository
135
-
136
- return null
137
- }
138
-
139
- /**
140
- * Inspect the `.git/rebase-merge` folder and convert the current rebase state
141
- * into data that can be provided to the rebase flow to update the application
142
- * state.
143
- *
144
- * This is required when Desktop is not responsible for initiating the rebase:
145
- *
146
- * - when a rebase outside Desktop encounters conflicts
147
- * - when a `git pull --rebase` was run and encounters conflicts
148
- *
149
- */
150
- export async function getRebaseSnapshot(repository: Repository): Promise<{
151
- progress: IMultiCommitOperationProgress
152
- commits: ReadonlyArray<CommitOneLine>
153
- } | null> {
154
- const rebaseHead = await isRebaseHeadSet(repository)
155
- if (!rebaseHead) {
156
- return null
157
- }
158
-
159
- let next: number = -1
160
- let last: number = -1
161
- let originalBranchTip: string | null = null
162
- let baseBranchTip: string | null = null
163
-
164
- // if the repository is in the middle of a rebase `.git/rebase-merge` will
165
- // contain all the patches of commits that are being rebased into
166
- // auto-incrementing files, e.g. `0001`, `0002`, `0003`, etc ...
167
-
168
- try {
169
- // this contains the patch number that was recently applied to the repository
170
- const nextText = await readFile(
171
- join(repository.resolvedGitDir, 'rebase-merge', 'msgnum'),
172
- 'utf8'
173
- )
174
-
175
- next = parseInt(nextText, 10)
176
-
177
- if (isNaN(next)) {
178
- console.warn(
179
- `[getCurrentProgress] found '${nextText}' in .git/rebase-merge/msgnum which could not be parsed to a valid number`
180
- )
181
- next = -1
182
- }
183
-
184
- // this contains the total number of patches to be applied to the repository
185
- const lastText = await readFile(
186
- join(repository.resolvedGitDir, 'rebase-merge', 'end'),
187
- 'utf8'
188
- )
189
-
190
- last = parseInt(lastText, 10)
191
-
192
- if (isNaN(last)) {
193
- console.warn(
194
- `[getCurrentProgress] found '${lastText}' in .git/rebase-merge/last which could not be parsed to a valid number`
195
- )
196
- last = -1
197
- }
198
-
199
- originalBranchTip = await readFile(
200
- join(repository.resolvedGitDir, 'rebase-merge', 'orig-head'),
201
- 'utf8'
202
- )
203
-
204
- originalBranchTip = originalBranchTip.trim()
205
-
206
- baseBranchTip = await readFile(
207
- join(repository.resolvedGitDir, 'rebase-merge', 'onto'),
208
- 'utf8'
209
- )
210
-
211
- baseBranchTip = baseBranchTip.trim()
212
- } catch {}
213
-
214
- if (
215
- next > 0 &&
216
- last > 0 &&
217
- originalBranchTip !== null &&
218
- baseBranchTip !== null
219
- ) {
220
- const percentage = next / last
221
- const value = formatRebaseValue(percentage)
222
-
223
- const commits = await getCommitsBetweenCommits(
224
- repository,
225
- baseBranchTip,
226
- originalBranchTip
227
- )
228
-
229
- if (commits === null || commits.length === 0) {
230
- return null
231
- }
232
-
233
- // this number starts from 1, but our array of commits starts from 0
234
- const nextCommitIndex = next - 1
235
-
236
- const hasValidCommit =
237
- commits.length > 0 &&
238
- nextCommitIndex >= 0 &&
239
- nextCommitIndex < commits.length
240
-
241
- const currentCommitSummary = hasValidCommit
242
- ? commits[nextCommitIndex].summary
243
- : ''
244
-
245
- return {
246
- progress: {
247
- kind: 'multiCommitOperation',
248
- value,
249
- position: next,
250
- totalCommitCount: last,
251
- currentCommitSummary,
252
- },
253
- commits,
254
- }
255
- }
256
-
257
- return null
258
- }
259
-
260
- /**
261
- * Attempt to read the `.git/REBASE_HEAD` file inside a repository to confirm
262
- * the rebase is still active.
263
- */
264
- async function readRebaseHead(repository: Repository): Promise<string | null> {
265
- try {
266
- const rebaseHead = join(repository.resolvedGitDir, 'REBASE_HEAD')
267
- const rebaseCurrentCommitOutput = await readFile(rebaseHead, 'utf8')
268
- return rebaseCurrentCommitOutput.trim()
269
- } catch (err) {
270
- console.warn(
271
- '[rebase] a problem was encountered reading .git/REBASE_HEAD, so it is unsafe to continue rebasing',
272
- err
273
- )
274
- return null
275
- }
276
- }
277
-
278
- /** Regex for identifying when rebase applied each commit onto the base branch */
279
- const rebasingRe = /^Rebasing \((\d+)\/(\d+)\)$/
280
-
281
- /**
282
- * A parser to read and emit rebase progress from Git `stderr`
283
- */
284
- class GitRebaseParser {
285
- public constructor(private readonly commits: ReadonlyArray<CommitOneLine>) {}
286
-
287
- public parse(line: string): IMultiCommitOperationProgress | null {
288
- const match = rebasingRe.exec(line)
289
- if (match === null || match.length !== 3) {
290
- // Git will sometimes emit other output (for example, when it tries to
291
- // resolve conflicts) and this does not match the expected output
292
- return null
293
- }
294
-
295
- const rebasedCommitCount = parseInt(match[1], 10)
296
- const totalCommitCount = parseInt(match[2], 10)
297
-
298
- if (isNaN(rebasedCommitCount) || isNaN(totalCommitCount)) {
299
- return null
300
- }
301
-
302
- const currentCommitSummary =
303
- this.commits[rebasedCommitCount - 1]?.summary ?? ''
304
-
305
- const progress = rebasedCommitCount / totalCommitCount
306
- const value = formatRebaseValue(progress)
307
-
308
- return {
309
- kind: 'multiCommitOperation',
310
- value,
311
- position: rebasedCommitCount,
312
- totalCommitCount: totalCommitCount,
313
- currentCommitSummary,
314
- }
315
- }
316
- }
317
-
318
- function configureOptionsForRebase<T extends IGitExecutionOptions>(
319
- options: T,
320
- progress?: RebaseProgressOptions
321
- ) {
322
- if (progress === undefined) {
323
- return options
324
- }
325
-
326
- const { commits, progressCallback } = progress
327
-
328
- return merge(options, {
329
- processCallback: (process: ChildProcess) => {
330
- // If Node.js encounters a synchronous runtime error while spawning
331
- // `stderr` will be undefined and the error will be emitted asynchronously
332
- if (process.stderr === null) {
333
- return
334
- }
335
- const parser = new GitRebaseParser(commits)
336
-
337
- byline(process.stderr).on('data', (line: string) => {
338
- const progress = parser.parse(line)
339
-
340
- if (progress != null) {
341
- progressCallback(progress)
342
- }
343
- })
344
- },
345
- })
346
- }
347
-
348
- /**
349
- * A stub function to use for initiating rebase in the app.
350
- *
351
- * If the rebase fails, the repository will be in an indeterminate state where
352
- * the rebase is stuck.
353
- *
354
- * If the rebase completes without error, `featureBranch` will be checked out
355
- * and it will probably have a different commit history.
356
- *
357
- * @param baseBranch the ref to start the rebase from
358
- * @param targetBranch the ref to rebase onto `baseBranch`
359
- */
360
- export async function rebase(
361
- repository: Repository,
362
- baseBranch: Branch,
363
- targetBranch: Branch,
364
- progressCallback?: (progress: IMultiCommitOperationProgress) => void
365
- ): Promise<RebaseResult> {
366
- const baseOptions: IGitStringExecutionOptions = {
367
- expectedErrors: new Set([GitError.RebaseConflicts]),
368
- }
369
-
370
- let options = baseOptions
371
-
372
- if (progressCallback !== undefined) {
373
- const commits = await getCommitsBetweenCommits(
374
- repository,
375
- baseBranch.tip.sha,
376
- targetBranch.tip.sha
377
- )
378
-
379
- if (commits === null) {
380
- // BadRevision can be raised here if git rev-list is unable to resolve a
381
- // ref to a commit ID, so we need to signal to the caller that this rebase
382
- // is not possible to perform
383
- console.warn(
384
- 'Unable to rebase these branches because one or both of the refs do not exist in the repository'
385
- )
386
- return RebaseResult.Error
387
- }
388
-
389
- options = configureOptionsForRebase(baseOptions, {
390
- commits,
391
- progressCallback,
392
- })
393
- }
394
-
395
- const result = await git(
396
- [...gitRebaseArguments(), 'rebase', baseBranch.name, targetBranch.name],
397
- repository.path,
398
- 'rebase',
399
- options
400
- )
401
-
402
- return parseRebaseResult(result)
403
- }
404
-
405
- /** Abandon the current rebase operation */
406
- export async function abortRebase(repository: Repository) {
407
- await git(['rebase', '--abort'], repository.path, 'abortRebase')
408
- }
409
-
410
- function parseRebaseResult(result: IGitStringResult): RebaseResult {
411
- if (result.exitCode === 0) {
412
- if (result.stdout.trim().match(/^Current branch [^ ]+ is up to date.$/i)) {
413
- return RebaseResult.AlreadyUpToDate
414
- }
415
-
416
- return RebaseResult.CompletedWithoutError
417
- }
418
-
419
- if (result.gitError === GitError.RebaseConflicts) {
420
- return RebaseResult.ConflictsEncountered
421
- }
422
-
423
- if (result.gitError === GitError.UnresolvedConflicts) {
424
- return RebaseResult.OutstandingFilesNotStaged
425
- }
426
-
427
- throw new Error(`Unhandled result found: '${JSON.stringify(result)}'`)
428
- }
429
-
430
- /**
431
- * Proceed with the current rebase operation and report back on whether it completed
432
- *
433
- * It is expected that the index has staged files which are cleanly rebased onto
434
- * the base branch, and the remaining unstaged files are those which need manual
435
- * resolution or were changed by the user to address inline conflicts.
436
- *
437
- */
438
- export async function continueRebase(
439
- repository: Repository,
440
- files: ReadonlyArray<WorkingDirectoryFileChange>,
441
- manualResolutions: ReadonlyMap<string, ManualConflictResolution> = new Map(),
442
- opts?: RebaseInteractiveOptions
443
- ): Promise<RebaseResult> {
444
- const trackedFiles = files.filter(f => {
445
- return f.status.kind !== AppFileStatusKind.Untracked
446
- })
447
-
448
- // apply conflict resolutions
449
- for (const [path, resolution] of manualResolutions) {
450
- const file = files.find(f => f.path === path)
451
- if (file !== undefined) {
452
- await stageManualConflictResolution(repository, file, resolution)
453
- } else {
454
- console.error(
455
- `[continueRebase] couldn't find file ${path} even though there's a manual resolution for it`
456
- )
457
- }
458
- }
459
-
460
- const otherFiles = trackedFiles.filter(f => !manualResolutions.has(f.path))
461
-
462
- await stageFiles(repository, otherFiles)
463
-
464
- const status = await getStatus(repository, false)
465
- if (status == null) {
466
- console.warn(
467
- `[continueRebase] unable to get status after staging changes, skipping any other steps`
468
- )
469
- return RebaseResult.Aborted
470
- }
471
-
472
- const rebaseCurrentCommit = await readRebaseHead(repository)
473
- if (rebaseCurrentCommit === null) {
474
- return RebaseResult.Aborted
475
- }
476
-
477
- const trackedFilesAfter = status.workingDirectory.files.filter(
478
- f => f.status.kind !== AppFileStatusKind.Untracked
479
- )
480
-
481
- const baseOptions: IGitStringExecutionOptions = {
482
- expectedErrors: new Set([
483
- GitError.RebaseConflicts,
484
- GitError.UnresolvedConflicts,
485
- ]),
486
- env: {
487
- GIT_EDITOR: opts?.gitEditor ?? ':',
488
- },
489
- }
490
-
491
- let options = baseOptions
492
-
493
- if (opts?.progressCallback) {
494
- const snapshot = await getRebaseSnapshot(repository)
495
-
496
- if (snapshot === null) {
497
- console.warn(
498
- `[continueRebase] unable to get rebase status, skipping any other steps`
499
- )
500
- return RebaseResult.Aborted
501
- }
502
-
503
- options = configureOptionsForRebase(baseOptions, {
504
- commits: snapshot.commits,
505
- progressCallback: opts.progressCallback,
506
- })
507
- }
508
-
509
- options = {
510
- ...options,
511
- onTerminalOutputAvailable: opts?.onTerminalOutputAvailable,
512
- onHookFailure: opts?.onHookFailure,
513
- onHookProgress: opts?.onHookProgress,
514
- }
515
-
516
- if (trackedFilesAfter.length === 0) {
517
- console.warn(
518
- `[rebase] no tracked changes to commit for ${rebaseCurrentCommit}, continuing rebase but skipping this commit`
519
- )
520
-
521
- const result = await git(
522
- ['rebase', '--skip', ...(opts?.noVerify ? ['--no-verify'] : [])],
523
- repository.path,
524
- 'continueRebaseSkipCurrentCommit',
525
- options
526
- )
527
-
528
- return parseRebaseResult(result)
529
- }
530
-
531
- const result = await git(
532
- ['rebase', '--continue', ...(opts?.noVerify ? ['--no-verify'] : [])],
533
- repository.path,
534
- 'continueRebase',
535
- options
536
- )
537
-
538
- return parseRebaseResult(result)
539
- }
540
-
541
- export type RebaseInteractiveOptions = {
542
- /**
543
- * a description of the action to be displayed in the progress dialog - i.e. Squash, Amend, etc..
544
- */
545
- action?: string
546
-
547
- /**
548
- * the GIT_EDITOR environment variable to use during the interactive rebase,
549
- * defaults to ':' which is a no-op command
550
- */
551
- gitEditor?: string
552
- progressCallback?: (progress: IMultiCommitOperationProgress) => void
553
- commits?: ReadonlyArray<Commit>
554
- noVerify?: boolean
555
- } & HookCallbackOptions
556
-
557
- /**
558
- * Method for initiating interactive rebase in the app.
559
- *
560
- * In order to modify the interactive todo list during interactive rebase, we
561
- * create a temporary todo list of our own. Pass that file's path into our
562
- * interactive rebase and using the sequence.editor to cat replace the
563
- * interactive todo list with the contents of our generated one.
564
- *
565
- * @param pathOfGeneratedTodo path to generated todo list for interactive rebase
566
- * @param lastRetainedCommitRef the commit before the earliest commit to be
567
- * changed during the interactive rebase or null if commit is root (first commit
568
- * in history) of branch
569
- */
570
- export async function rebaseInteractive(
571
- repository: Repository,
572
- pathOfGeneratedTodo: string,
573
- lastRetainedCommitRef: string | null,
574
- opts?: RebaseInteractiveOptions
575
- ): Promise<RebaseResult> {
576
- const baseOptions: IGitStringExecutionOptions = {
577
- expectedErrors: new Set([GitError.RebaseConflicts]),
578
- env: {
579
- GIT_SEQUENCE_EDITOR: undefined,
580
- GIT_EDITOR: opts?.gitEditor ?? ':',
581
- },
582
- }
583
-
584
- let options = baseOptions
585
-
586
- const { progressCallback, commits } = opts ?? {}
587
-
588
- if (progressCallback) {
589
- if (!commits) {
590
- console.warn(`Unable to interactively rebase if no commits`)
591
- return RebaseResult.Error
592
- }
593
-
594
- options = configureOptionsForRebase(baseOptions, {
595
- commits,
596
- progressCallback,
597
- })
598
- }
599
-
600
- options = {
601
- ...options,
602
- onHookProgress: opts?.onHookProgress,
603
- onHookFailure: opts?.onHookFailure,
604
- onTerminalOutputAvailable: opts?.onTerminalOutputAvailable,
605
- }
606
-
607
- /* If the commit is the first commit in the branch, we cannot reference it
608
- using the sha thus if lastRetainedCommitRef is null (we couldn't define it),
609
- we must use the --root flag */
610
- const ref = lastRetainedCommitRef == null ? '--root' : lastRetainedCommitRef
611
- const result = await git(
612
- [
613
- '-c',
614
- // This replaces interactive todo with contents of file at pathOfGeneratedTodo
615
- `sequence.editor=cat "${pathOfGeneratedTodo}" >`,
616
- 'rebase',
617
- ...(opts?.noVerify ? ['--no-verify'] : []),
618
- '-i',
619
- ref,
620
- ],
621
- repository.path,
622
- opts?.action ?? 'Interactive rebase',
623
- options
624
- )
625
-
626
- return parseRebaseResult(result)
627
- }