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/reflog.ts
DELETED
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import { git } from './core.js'
|
|
2
|
-
import { Repository } from '../models/repository.js'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Get the `limit` most recently checked out branches.
|
|
6
|
-
*/
|
|
7
|
-
export async function getRecentBranches(
|
|
8
|
-
repository: Repository,
|
|
9
|
-
limit: number
|
|
10
|
-
): Promise<ReadonlyArray<string>> {
|
|
11
|
-
// "git reflog show" is just an alias for "git log -g --abbrev-commit --pretty=oneline"
|
|
12
|
-
// but by using log we can give it a max number which should prevent us from balling out
|
|
13
|
-
// of control when there's ginormous reflogs around (as in e.g. github/github).
|
|
14
|
-
const regex = new RegExp(
|
|
15
|
-
/.*? (renamed|checkout)(?:: moving from|\s*) (?:refs\/heads\/|\s*)(.*?) to (?:refs\/heads\/|\s*)(.*?)$/i
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
const result = await git(
|
|
19
|
-
[
|
|
20
|
-
'log',
|
|
21
|
-
'-g',
|
|
22
|
-
'--no-abbrev-commit',
|
|
23
|
-
'--pretty=oneline',
|
|
24
|
-
'HEAD',
|
|
25
|
-
'-n',
|
|
26
|
-
'2500',
|
|
27
|
-
'--',
|
|
28
|
-
],
|
|
29
|
-
repository.path,
|
|
30
|
-
'getRecentBranches',
|
|
31
|
-
{ successExitCodes: new Set([0, 128]) }
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
if (result.exitCode === 128) {
|
|
35
|
-
// error code 128 is returned if the branch is unborn
|
|
36
|
-
return []
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const lines = result.stdout.split('\n')
|
|
40
|
-
const names = new Set<string>()
|
|
41
|
-
const excludedNames = new Set<String>()
|
|
42
|
-
|
|
43
|
-
for (const line of lines) {
|
|
44
|
-
const result = regex.exec(line)
|
|
45
|
-
if (result && result.length === 4) {
|
|
46
|
-
const operationType = result[1]
|
|
47
|
-
const excludeBranchName = result[2]
|
|
48
|
-
const branchName = result[3]
|
|
49
|
-
|
|
50
|
-
if (operationType === 'renamed') {
|
|
51
|
-
// exclude intermediate-state renaming branch from recent branches
|
|
52
|
-
excludedNames.add(excludeBranchName)
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (!excludedNames.has(branchName)) {
|
|
56
|
-
names.add(branchName)
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (names.size === limit) {
|
|
61
|
-
break
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return [...names]
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const noCommitsOnBranchRe = new RegExp(
|
|
69
|
-
"fatal: your current branch '.*' does not have any commits yet"
|
|
70
|
-
)
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Gets the distinct list of branches that have been checked out after a specific date
|
|
74
|
-
* Returns a map keyed on branch names
|
|
75
|
-
*
|
|
76
|
-
* @param repository the repository who's reflog you want to check
|
|
77
|
-
* @param afterDate filters checkouts so that only those occurring on or after this date are returned
|
|
78
|
-
* @returns map of branch name -> checkout date
|
|
79
|
-
*/
|
|
80
|
-
export async function getBranchCheckouts(
|
|
81
|
-
repository: Repository,
|
|
82
|
-
afterDate: Date
|
|
83
|
-
): Promise<Map<string, Date>> {
|
|
84
|
-
//regexr.com/46n1v
|
|
85
|
-
const regex = new RegExp(
|
|
86
|
-
/^[a-z0-9]{40}\sHEAD@{(.*)}\scheckout: moving from\s.*\sto\s(.*)$/
|
|
87
|
-
)
|
|
88
|
-
const result = await git(
|
|
89
|
-
[
|
|
90
|
-
'reflog',
|
|
91
|
-
'--date=iso',
|
|
92
|
-
`--after="${afterDate.toISOString()}"`,
|
|
93
|
-
'--pretty=%H %gd %gs',
|
|
94
|
-
`--grep-reflog=checkout: moving from .* to .*$`,
|
|
95
|
-
'--',
|
|
96
|
-
],
|
|
97
|
-
repository.path,
|
|
98
|
-
'getCheckoutsAfterDate',
|
|
99
|
-
{ successExitCodes: new Set([0, 128]) }
|
|
100
|
-
)
|
|
101
|
-
|
|
102
|
-
const checkouts = new Map<string, Date>()
|
|
103
|
-
|
|
104
|
-
// edge case where orphaned branch is created but Git raises error when
|
|
105
|
-
// reading the reflog on this new branch as it has no commits
|
|
106
|
-
//
|
|
107
|
-
// see https://github.com/desktop/desktop/issues/7983 for more information
|
|
108
|
-
if (result.exitCode === 128 && noCommitsOnBranchRe.test(result.stderr)) {
|
|
109
|
-
return checkouts
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const lines = result.stdout.split('\n')
|
|
113
|
-
for (const line of lines) {
|
|
114
|
-
const parsedLine = regex.exec(line)
|
|
115
|
-
|
|
116
|
-
if (parsedLine === null || parsedLine.length !== 3) {
|
|
117
|
-
continue
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const [, timestamp, branchName] = parsedLine
|
|
121
|
-
if (!checkouts.has(branchName)) {
|
|
122
|
-
checkouts.set(branchName, new Date(timestamp))
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return checkouts
|
|
127
|
-
}
|
package/src/git/refs.ts
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { git } from './core.js'
|
|
2
|
-
import { Repository } from '../models/repository.js'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Format a local branch in the ref syntax, ensuring situations when the branch
|
|
6
|
-
* is ambiguous are handled.
|
|
7
|
-
*
|
|
8
|
-
* Examples:
|
|
9
|
-
* - main -> refs/heads/main
|
|
10
|
-
* - heads/Microsoft/main -> refs/heads/Microsoft/main
|
|
11
|
-
*
|
|
12
|
-
* @param branch The local branch name
|
|
13
|
-
*/
|
|
14
|
-
export function formatAsLocalRef(name: string): string {
|
|
15
|
-
if (name.startsWith('heads/')) {
|
|
16
|
-
// In some cases, Git will report this name explicitly to distinguish from
|
|
17
|
-
// a remote ref with the same name - this ensures we format it correctly.
|
|
18
|
-
return `refs/${name}`
|
|
19
|
-
} else if (!name.startsWith('refs/heads/')) {
|
|
20
|
-
// By default Git will drop the heads prefix unless absolutely necessary
|
|
21
|
-
// - include this to ensure the ref is fully qualified.
|
|
22
|
-
return `refs/heads/${name}`
|
|
23
|
-
} else {
|
|
24
|
-
return name
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Read a symbolic ref from the repository.
|
|
30
|
-
*
|
|
31
|
-
* Symbolic refs are used to point to other refs, similar to how symlinks work
|
|
32
|
-
* for files. Because refs can be removed easily from a Git repository,
|
|
33
|
-
* symbolic refs should only be used when absolutely necessary.
|
|
34
|
-
*
|
|
35
|
-
* @param repository The repository to lookup
|
|
36
|
-
* @param ref The symbolic ref to resolve
|
|
37
|
-
*
|
|
38
|
-
* @returns the canonical ref, if found, or `null` if `ref` cannot be found or
|
|
39
|
-
* is not a symbolic ref
|
|
40
|
-
*/
|
|
41
|
-
export async function getSymbolicRef(
|
|
42
|
-
repository: Repository,
|
|
43
|
-
ref: string
|
|
44
|
-
): Promise<string | null> {
|
|
45
|
-
const result = await git(
|
|
46
|
-
['symbolic-ref', '-q', ref],
|
|
47
|
-
repository.path,
|
|
48
|
-
'getSymbolicRef',
|
|
49
|
-
{
|
|
50
|
-
// - 1 is the exit code that Git throws in quiet mode when the ref is not a
|
|
51
|
-
// symbolic ref
|
|
52
|
-
// - 128 is the generic error code that Git returns when it can't find
|
|
53
|
-
// something
|
|
54
|
-
successExitCodes: new Set([0, 1, 128]),
|
|
55
|
-
}
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
if (result.exitCode === 1 || result.exitCode === 128) {
|
|
59
|
-
return null
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return result.stdout.trim()
|
|
63
|
-
}
|
package/src/git/remote.ts
DELETED
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
import { git } from './core.js'
|
|
2
|
-
import { GitError } from './exec.js'
|
|
3
|
-
|
|
4
|
-
import { Repository } from '../models/repository.js'
|
|
5
|
-
import { IRemote } from '../models/remote.js'
|
|
6
|
-
import { envForRemoteOperation } from './environment.js'
|
|
7
|
-
import { getSymbolicRef } from './refs.js'
|
|
8
|
-
import memoizeOne from 'memoize-one'
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* List the remotes, sorted alphabetically by `name`, for a repository.
|
|
12
|
-
*/
|
|
13
|
-
export async function getRemotes(
|
|
14
|
-
repository: Repository
|
|
15
|
-
): Promise<ReadonlyArray<IRemote>> {
|
|
16
|
-
return memoizedGetRemotesFromPath(repository.path)
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* List the remotes, sorted alphabetically by `name`, for a repository path.
|
|
21
|
-
*/
|
|
22
|
-
export async function getRemotesFromPath(
|
|
23
|
-
path: string
|
|
24
|
-
): Promise<ReadonlyArray<IRemote>> {
|
|
25
|
-
const result = await git(['remote', '-v'], path, 'getRemotes', {
|
|
26
|
-
expectedErrors: new Set([GitError.NotAGitRepository]),
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
if (result.gitError === GitError.NotAGitRepository) {
|
|
30
|
-
return []
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return [...result.stdout.matchAll(/^(.+)\t(.+)\s\(fetch\)/gm)].map(
|
|
34
|
-
([, name, url]) => ({ name, url })
|
|
35
|
-
)
|
|
36
|
-
}
|
|
37
|
-
export const memoizedGetRemotesFromPath = memoizeOne(getRemotesFromPath)
|
|
38
|
-
|
|
39
|
-
/** Add a new remote with the given URL. */
|
|
40
|
-
export async function addRemote(
|
|
41
|
-
repository: Repository,
|
|
42
|
-
name: string,
|
|
43
|
-
url: string
|
|
44
|
-
): Promise<IRemote> {
|
|
45
|
-
await git(['remote', 'add', name, url], repository.path, 'addRemote')
|
|
46
|
-
|
|
47
|
-
return { url, name }
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/** Removes an existing remote, or silently errors if it doesn't exist */
|
|
51
|
-
export async function removeRemote(
|
|
52
|
-
repository: Repository,
|
|
53
|
-
name: string
|
|
54
|
-
): Promise<void> {
|
|
55
|
-
const options = {
|
|
56
|
-
successExitCodes: new Set([0, 2, 128]),
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
await git(
|
|
60
|
-
['remote', 'remove', name],
|
|
61
|
-
repository.path,
|
|
62
|
-
'removeRemote',
|
|
63
|
-
options
|
|
64
|
-
)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/** Changes the URL for the remote that matches the given name */
|
|
68
|
-
export async function setRemoteURL(
|
|
69
|
-
repository: Repository,
|
|
70
|
-
name: string,
|
|
71
|
-
url: string
|
|
72
|
-
): Promise<true> {
|
|
73
|
-
await git(['remote', 'set-url', name, url], repository.path, 'setRemoteURL')
|
|
74
|
-
return true
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Get the URL for the remote that matches the given name.
|
|
79
|
-
*
|
|
80
|
-
* Returns null if the remote could not be found
|
|
81
|
-
*/
|
|
82
|
-
export async function getRemoteURL(
|
|
83
|
-
repository: Repository,
|
|
84
|
-
name: string
|
|
85
|
-
): Promise<string | null> {
|
|
86
|
-
const result = await git(
|
|
87
|
-
['remote', 'get-url', name],
|
|
88
|
-
repository.path,
|
|
89
|
-
'getRemoteURL',
|
|
90
|
-
{ successExitCodes: new Set([0, 2, 128]) }
|
|
91
|
-
)
|
|
92
|
-
|
|
93
|
-
if (result.exitCode !== 0) {
|
|
94
|
-
return null
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
return result.stdout
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Update the HEAD ref of the remote, which is the default branch.
|
|
102
|
-
*
|
|
103
|
-
* @param isBackgroundTask Whether the fetch is being performed as a
|
|
104
|
-
* background task as opposed to being user initiated
|
|
105
|
-
*/
|
|
106
|
-
export async function updateRemoteHEAD(
|
|
107
|
-
repository: Repository,
|
|
108
|
-
remote: IRemote,
|
|
109
|
-
isBackgroundTask: boolean
|
|
110
|
-
): Promise<void> {
|
|
111
|
-
const options = {
|
|
112
|
-
successExitCodes: new Set([0, 1, 128]),
|
|
113
|
-
env: await envForRemoteOperation(remote.url),
|
|
114
|
-
isBackgroundTask,
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
await git(
|
|
118
|
-
['remote', 'set-head', '-a', remote.name],
|
|
119
|
-
repository.path,
|
|
120
|
-
'updateRemoteHEAD',
|
|
121
|
-
options
|
|
122
|
-
)
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
export async function getRemoteHEAD(
|
|
126
|
-
repository: Repository,
|
|
127
|
-
remote: string
|
|
128
|
-
): Promise<string | null> {
|
|
129
|
-
const remoteNamespace = `refs/remotes/${remote}/`
|
|
130
|
-
const match = await getSymbolicRef(repository, `${remoteNamespace}HEAD`)
|
|
131
|
-
if (
|
|
132
|
-
match != null &&
|
|
133
|
-
match.length > remoteNamespace.length &&
|
|
134
|
-
match.startsWith(remoteNamespace)
|
|
135
|
-
) {
|
|
136
|
-
// strip out everything related to the remote because this
|
|
137
|
-
// is likely to be a tracked branch locally
|
|
138
|
-
// e.g. `main`, `develop`, etc
|
|
139
|
-
return match.substring(remoteNamespace.length)
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
return null
|
|
143
|
-
}
|
package/src/git/reorder.ts
DELETED
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
import { appendFile, rm } from 'fs/promises'
|
|
2
|
-
import { getCommits, revRange } from './index.js'
|
|
3
|
-
import { Commit } from '../models/commit.js'
|
|
4
|
-
import { MultiCommitOperationKind } from '../models/multi-commit-operation.js'
|
|
5
|
-
import { IMultiCommitOperationProgress } from '../models/progress.js'
|
|
6
|
-
import { Repository } from '../models/repository.js'
|
|
7
|
-
import { getTempFilePath } from '../lib/file-system.js'
|
|
8
|
-
import { rebaseInteractive, RebaseResult } from './rebase.js'
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Reorders provided commits by calling interactive rebase.
|
|
12
|
-
*
|
|
13
|
-
* Goal is to replay the commits in order from oldest to newest to reduce
|
|
14
|
-
* conflicts with toMove commits placed in the log at the location of the
|
|
15
|
-
* prior to the base commit.
|
|
16
|
-
*
|
|
17
|
-
* Example: A user's history from oldest to newest is A, B, C, D, E and they
|
|
18
|
-
* want to move A and E (toMove) before C. Our goal: B, A, E, C, D. Thus,
|
|
19
|
-
* maintaining that A came before E, placed in history before the the base
|
|
20
|
-
* commit C.
|
|
21
|
-
*
|
|
22
|
-
* @param toMove - commits to move
|
|
23
|
-
* @param beforeCommit - commits will be moved right before this commit. If it's
|
|
24
|
-
* null, the commits will be moved to the end of the history.
|
|
25
|
-
* @param lastRetainedCommitRef - sha of commit before commits to reorder or null
|
|
26
|
-
* if base commit for reordering is the root (first in history) of the branch
|
|
27
|
-
*/
|
|
28
|
-
export async function reorder(
|
|
29
|
-
repository: Repository,
|
|
30
|
-
toMove: ReadonlyArray<Commit>,
|
|
31
|
-
beforeCommit: Commit | null,
|
|
32
|
-
lastRetainedCommitRef: string | null,
|
|
33
|
-
progressCallback?: (progress: IMultiCommitOperationProgress) => void
|
|
34
|
-
): Promise<RebaseResult> {
|
|
35
|
-
let todoPath
|
|
36
|
-
let result: RebaseResult
|
|
37
|
-
|
|
38
|
-
try {
|
|
39
|
-
if (toMove.length === 0) {
|
|
40
|
-
throw new Error('[reorder] No commits provided to reorder.')
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const toMoveShas = new Set(toMove.map(c => c.sha))
|
|
44
|
-
|
|
45
|
-
const commits = await getCommits(
|
|
46
|
-
repository,
|
|
47
|
-
lastRetainedCommitRef === null
|
|
48
|
-
? undefined
|
|
49
|
-
: revRange(lastRetainedCommitRef, 'HEAD')
|
|
50
|
-
)
|
|
51
|
-
|
|
52
|
-
if (commits.length === 0) {
|
|
53
|
-
throw new Error(
|
|
54
|
-
'[reorder] Could not find commits in log for last retained commit ref.'
|
|
55
|
-
)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
todoPath = await getTempFilePath('reorderTodo')
|
|
59
|
-
let foundBaseCommitInLog = false
|
|
60
|
-
const toReplayBeforeBaseCommit = []
|
|
61
|
-
const toReplayAfterReorder = []
|
|
62
|
-
|
|
63
|
-
// Traversed in reverse so we do oldest to newest (replay commits)
|
|
64
|
-
for (let i = commits.length - 1; i >= 0; i--) {
|
|
65
|
-
const commit = commits[i]
|
|
66
|
-
if (toMoveShas.has(commit.sha)) {
|
|
67
|
-
// If it is toMove commit and we have found the base commit, we
|
|
68
|
-
// can go ahead and insert them (as we will hold any picks till after)
|
|
69
|
-
if (foundBaseCommitInLog) {
|
|
70
|
-
await appendFile(todoPath, `pick ${commit.sha} ${commit.summary}\n`)
|
|
71
|
-
} else {
|
|
72
|
-
// However, if we have not found the base commit yet we want to
|
|
73
|
-
// keep track of them in the order of the log. Thus, we use a new
|
|
74
|
-
// `toReplayBeforeBaseCommit` array and not trust that what was sent is in the
|
|
75
|
-
// order of the log.
|
|
76
|
-
toReplayBeforeBaseCommit.push(commit)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
continue
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// If it's the base commit, replay to the toMove in the order they
|
|
83
|
-
// appeared on the log to reduce potential conflicts.
|
|
84
|
-
if (beforeCommit !== null && commit.sha === beforeCommit.sha) {
|
|
85
|
-
foundBaseCommitInLog = true
|
|
86
|
-
toReplayAfterReorder.push(commit)
|
|
87
|
-
|
|
88
|
-
for (let j = 0; j < toReplayBeforeBaseCommit.length; j++) {
|
|
89
|
-
await appendFile(
|
|
90
|
-
todoPath,
|
|
91
|
-
`pick ${toReplayBeforeBaseCommit[j].sha} ${toReplayBeforeBaseCommit[j].summary}\n`
|
|
92
|
-
)
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
continue
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// We can't just replay a pick in case there is a commit from the toMove
|
|
99
|
-
// commits further up in history that need to be moved. Thus, we will keep
|
|
100
|
-
// track of these and replay after traversing the remainder of the log.
|
|
101
|
-
if (foundBaseCommitInLog) {
|
|
102
|
-
toReplayAfterReorder.push(commit)
|
|
103
|
-
continue
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// If it is not one toMove nor the base commit and have not found the base
|
|
107
|
-
// commit, we simply record it is an unchanged pick (before the base commit)
|
|
108
|
-
await appendFile(todoPath, `pick ${commit.sha} ${commit.summary}\n`)
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (toReplayAfterReorder.length > 0) {
|
|
112
|
-
for (let i = 0; i < toReplayAfterReorder.length; i++) {
|
|
113
|
-
await appendFile(
|
|
114
|
-
todoPath,
|
|
115
|
-
`pick ${toReplayAfterReorder[i].sha} ${toReplayAfterReorder[i].summary}\n`
|
|
116
|
-
)
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
if (beforeCommit === null) {
|
|
121
|
-
for (let i = 0; i < toReplayBeforeBaseCommit.length; i++) {
|
|
122
|
-
await appendFile(
|
|
123
|
-
todoPath,
|
|
124
|
-
`pick ${toReplayBeforeBaseCommit[i].sha} ${toReplayBeforeBaseCommit[i].summary}\n`
|
|
125
|
-
)
|
|
126
|
-
}
|
|
127
|
-
} else if (!foundBaseCommitInLog) {
|
|
128
|
-
throw new Error(
|
|
129
|
-
'[reorder] The base commit onto was not in the log. Continuing would result in dropping the commits in the toMove array.'
|
|
130
|
-
)
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
result = await rebaseInteractive(
|
|
134
|
-
repository,
|
|
135
|
-
todoPath,
|
|
136
|
-
lastRetainedCommitRef,
|
|
137
|
-
{
|
|
138
|
-
action: MultiCommitOperationKind.Reorder,
|
|
139
|
-
progressCallback,
|
|
140
|
-
commits,
|
|
141
|
-
}
|
|
142
|
-
)
|
|
143
|
-
} catch (e) {
|
|
144
|
-
console.error(e)
|
|
145
|
-
return RebaseResult.Error
|
|
146
|
-
} finally {
|
|
147
|
-
if (todoPath !== undefined) {
|
|
148
|
-
await rm(todoPath, { recursive: true, force: true })
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
return result
|
|
153
|
-
}
|
package/src/git/reset.ts
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import { git } from './core.js'
|
|
2
|
-
import { Repository } from '../models/repository.js'
|
|
3
|
-
import { assertNever } from '../lib/fatal-error.js'
|
|
4
|
-
|
|
5
|
-
/** The reset modes which are supported. */
|
|
6
|
-
export const enum GitResetMode {
|
|
7
|
-
/**
|
|
8
|
-
* Resets the index and working tree. Any changes to tracked files in the
|
|
9
|
-
* working tree since <commit> are discarded.
|
|
10
|
-
*/
|
|
11
|
-
Hard = 0,
|
|
12
|
-
/**
|
|
13
|
-
* Does not touch the index file or the working tree at all (but resets the
|
|
14
|
-
* head to <commit>, just like all modes do). This leaves all your changed
|
|
15
|
-
* files "Changes to be committed", as git status would put it.
|
|
16
|
-
*/
|
|
17
|
-
Soft,
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Resets the index but not the working tree (i.e., the changed files are
|
|
21
|
-
* preserved but not marked for commit) and reports what has not been updated.
|
|
22
|
-
* This is the default action for git reset.
|
|
23
|
-
*/
|
|
24
|
-
Mixed,
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function resetModeToArgs(mode: GitResetMode, ref: string): string[] {
|
|
28
|
-
switch (mode) {
|
|
29
|
-
case GitResetMode.Hard:
|
|
30
|
-
return ['reset', '--hard', ref]
|
|
31
|
-
case GitResetMode.Mixed:
|
|
32
|
-
return ['reset', ref]
|
|
33
|
-
case GitResetMode.Soft:
|
|
34
|
-
return ['reset', '--soft', ref]
|
|
35
|
-
default:
|
|
36
|
-
return assertNever(mode, `Unknown reset mode: ${mode}`)
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/** Reset with the mode to the ref. */
|
|
41
|
-
export async function reset(
|
|
42
|
-
repository: Repository,
|
|
43
|
-
mode: GitResetMode,
|
|
44
|
-
ref: string
|
|
45
|
-
): Promise<true> {
|
|
46
|
-
const args = resetModeToArgs(mode, ref)
|
|
47
|
-
await git(args, repository.path, 'reset')
|
|
48
|
-
return true
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Updates the index with information from a particular tree for a given
|
|
53
|
-
* set of paths.
|
|
54
|
-
*
|
|
55
|
-
* @param repository The repository in which to reset the index.
|
|
56
|
-
*
|
|
57
|
-
* @param mode Which mode to use when resetting, see the GitResetMode
|
|
58
|
-
* enum for more information.
|
|
59
|
-
*
|
|
60
|
-
* @param ref A string which resolves to a tree, for example 'HEAD' or a
|
|
61
|
-
* commit sha.
|
|
62
|
-
*
|
|
63
|
-
* @param paths The paths that should be updated in the index with information
|
|
64
|
-
* from the given tree
|
|
65
|
-
*/
|
|
66
|
-
export async function resetPaths(
|
|
67
|
-
repository: Repository,
|
|
68
|
-
mode: GitResetMode,
|
|
69
|
-
ref: string,
|
|
70
|
-
paths: ReadonlyArray<string>
|
|
71
|
-
): Promise<void> {
|
|
72
|
-
if (!paths.length) {
|
|
73
|
-
return
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const baseArgs = resetModeToArgs(mode, ref)
|
|
77
|
-
|
|
78
|
-
if (process.platform === 'win32' && mode === GitResetMode.Mixed) {
|
|
79
|
-
// Git for Windows has experimental support for reading paths to reset
|
|
80
|
-
// from standard input. This is helpful in situations where your file
|
|
81
|
-
// paths are greater than 32KB in length, because of shell limitations.
|
|
82
|
-
//
|
|
83
|
-
// This hasn't made it to Git core, so we fallback to the default behaviour
|
|
84
|
-
// as macOS and Linux don't have this same shell limitation. See
|
|
85
|
-
// https://github.com/desktop/desktop/issues/2833#issuecomment-331352952
|
|
86
|
-
// for more context.
|
|
87
|
-
const args = [...baseArgs, '--stdin', '-z', '--']
|
|
88
|
-
await git(args, repository.path, 'resetPaths', {
|
|
89
|
-
stdin: paths.join('\0'),
|
|
90
|
-
})
|
|
91
|
-
} else {
|
|
92
|
-
const args = [...baseArgs, '--', ...paths]
|
|
93
|
-
await git(args, repository.path, 'resetPaths')
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/** Unstage all paths. */
|
|
98
|
-
export async function unstageAll(repository: Repository): Promise<true> {
|
|
99
|
-
await git(['reset', '--', '.'], repository.path, 'unstageAll')
|
|
100
|
-
return true
|
|
101
|
-
}
|