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/environment.ts
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import { envForAuthentication } from './authentication.js'
|
|
2
|
-
import { resolveGitProxy as resolveGitProxyFn } from '../lib/resolve-git-proxy.js'
|
|
3
|
-
import { getHTMLURL } from '../lib/api.js'
|
|
4
|
-
import { Repository } from '../models/repository.js'
|
|
5
|
-
import { IRemote } from '../models/remote.js'
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* For many remote operations it's well known what the primary remote
|
|
9
|
-
* url is (clone, push, fetch etc). But in some cases it's not as easy.
|
|
10
|
-
*
|
|
11
|
-
* Two examples are checkout, and revert where neither would need to
|
|
12
|
-
* hit the network in vanilla Git usage but do need to when LFS gets
|
|
13
|
-
* involved.
|
|
14
|
-
*
|
|
15
|
-
* What's the primary url when using LFS then? Most likely it's gonna
|
|
16
|
-
* be on the same as the default remote but it could theoretically
|
|
17
|
-
* be on a different server as well. That's too advanced for our usage
|
|
18
|
-
* at the moment though so we'll just need to figure out some reasonable
|
|
19
|
-
* url to fall back on.
|
|
20
|
-
*
|
|
21
|
-
* @param branchName If the operation we're about to undertake is related to a
|
|
22
|
-
* local ref (i.e branch) then we can use that to resolve its
|
|
23
|
-
* upstream tracking branch (and thereby its remote) and use
|
|
24
|
-
* that as the probable url to resolve a proxy for.
|
|
25
|
-
*/
|
|
26
|
-
export function getFallbackUrlForProxyResolve(
|
|
27
|
-
_repository: Repository,
|
|
28
|
-
currentRemote: IRemote | null
|
|
29
|
-
) {
|
|
30
|
-
if (currentRemote) {
|
|
31
|
-
return currentRemote.url
|
|
32
|
-
}
|
|
33
|
-
return 'https://github.com'
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Create a set of environment variables to use when invoking a Git
|
|
38
|
-
* subcommand that needs to communicate with a remote (i.e. fetch, clone,
|
|
39
|
-
* push, pull, ls-remote, etc etc).
|
|
40
|
-
*
|
|
41
|
-
* The environment variables deal with setting up sane defaults, configuring
|
|
42
|
-
* authentication, and resolving proxy urls if necessary.
|
|
43
|
-
*
|
|
44
|
-
* @param account The authentication information (if available) to provide
|
|
45
|
-
* to Git for use when connecting to the remote
|
|
46
|
-
* @param remoteUrl The primary remote URL for this operation. Note that Git
|
|
47
|
-
* might connect to other remotes in order to fulfill the
|
|
48
|
-
* operation. As an example, a clone of
|
|
49
|
-
* https://github.com/desktop/desktop could contain a submodule
|
|
50
|
-
* pointing to another host entirely. Used to resolve which
|
|
51
|
-
* proxy (if any) should be used for the operation.
|
|
52
|
-
*/
|
|
53
|
-
export async function envForRemoteOperation(remoteUrl: string | null): Promise<Record<string, string | undefined>> {
|
|
54
|
-
return {
|
|
55
|
-
...envForAuthentication(),
|
|
56
|
-
...(await envForProxy(remoteUrl ?? 'https://github.com')),
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Not intended to be used directly. Exported only in order to
|
|
62
|
-
* allow for testing.
|
|
63
|
-
*
|
|
64
|
-
* @param remoteUrl The remote url to resolve a proxy for.
|
|
65
|
-
* @param env The current environment variables, defaults
|
|
66
|
-
* to `process.env`
|
|
67
|
-
* @param resolve The method to use when resolving the proxy url,
|
|
68
|
-
* defaults to `resolveGitProxy`
|
|
69
|
-
*/
|
|
70
|
-
export async function envForProxy(
|
|
71
|
-
remoteUrl: string,
|
|
72
|
-
env: NodeJS.ProcessEnv = process.env,
|
|
73
|
-
resolve: (url: string) => Promise<string | undefined> = async (url: string) => resolveGitProxyFn() ?? undefined
|
|
74
|
-
): Promise<Record<string, string | undefined> | undefined> {
|
|
75
|
-
const protocolMatch = /^(https?):\/\//i.exec(remoteUrl)
|
|
76
|
-
|
|
77
|
-
// We can only resolve and use a proxy for the protocols where cURL
|
|
78
|
-
// would be involved (i.e http and https). git:// relies on ssh.
|
|
79
|
-
if (protocolMatch === null) {
|
|
80
|
-
return
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Note that HTTPS here doesn't mean that the proxy is HTTPS, only
|
|
84
|
-
// that all requests to HTTPS protocols should be proxied. The
|
|
85
|
-
// proxy protocol is defined by the url returned by `this.resolve()`
|
|
86
|
-
const proto = protocolMatch[1].toLowerCase() // http or https
|
|
87
|
-
|
|
88
|
-
// We'll play it safe and say that if the user has configured
|
|
89
|
-
// the ALL_PROXY environment variable they probably know what
|
|
90
|
-
// they're doing and wouldn't want us to override it with a
|
|
91
|
-
// protocol-specific proxy. cURL supports both lower and upper
|
|
92
|
-
// case, see:
|
|
93
|
-
// https://github.com/curl/curl/blob/14916a82e/lib/url.c#L2180-L2185
|
|
94
|
-
if ('ALL_PROXY' in env || 'all_proxy' in env) {
|
|
95
|
-
console.info(`proxy url not resolved, ALL_PROXY already set`)
|
|
96
|
-
return
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Lower case environment variables due to
|
|
100
|
-
// https://ec.haxx.se/usingcurl/usingcurl-proxies#http_proxy-in-lower-case-only
|
|
101
|
-
const envKey = `${proto}_proxy` // http_proxy or https_proxy
|
|
102
|
-
|
|
103
|
-
// If the user has already configured a proxy in the environment
|
|
104
|
-
// for the protocol we're not gonna override it.
|
|
105
|
-
if (envKey in env || (proto === 'https' && 'HTTPS_PROXY' in env)) {
|
|
106
|
-
console.info(`proxy url not resolved, ${envKey} already set`)
|
|
107
|
-
return
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const proxyUrl = await resolve(remoteUrl).catch(err => {
|
|
111
|
-
console.error('Failed resolving Git proxy', err)
|
|
112
|
-
return undefined
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
return proxyUrl === undefined ? undefined : { [envKey]: proxyUrl }
|
|
116
|
-
}
|
package/src/git/exec.ts
DELETED
|
@@ -1,285 +0,0 @@
|
|
|
1
|
-
import { spawn, SpawnOptions } from 'child_process'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Git error types parsed from stderr output.
|
|
5
|
-
* This is a simplified version of dugite's GitError enum.
|
|
6
|
-
*/
|
|
7
|
-
export enum GitError {
|
|
8
|
-
// Authentication
|
|
9
|
-
SSHAuthenticationFailed = 'SSHAuthenticationFailed',
|
|
10
|
-
SSHPermissionDenied = 'SSHPermissionDenied',
|
|
11
|
-
HTTPSAuthenticationFailed = 'HTTPSAuthenticationFailed',
|
|
12
|
-
SSHKeyAuditUnverified = 'SSHKeyAuditUnverified',
|
|
13
|
-
|
|
14
|
-
// Remote
|
|
15
|
-
RemoteDisconnection = 'RemoteDisconnection',
|
|
16
|
-
HostDown = 'HostDown',
|
|
17
|
-
HTTPSRepositoryNotFound = 'HTTPSRepositoryNotFound',
|
|
18
|
-
SSHRepositoryNotFound = 'SSHRepositoryNotFound',
|
|
19
|
-
|
|
20
|
-
// Push / Pull
|
|
21
|
-
PushNotFastForward = 'PushNotFastForward',
|
|
22
|
-
PushWithFileSizeExceedingLimit = 'PushWithFileSizeExceedingLimit',
|
|
23
|
-
PushWithPrivateEmail = 'PushWithPrivateEmail',
|
|
24
|
-
PushWithSecretDetected = 'PushWithSecretDetected',
|
|
25
|
-
ForcePushRejected = 'ForcePushRejected',
|
|
26
|
-
ProtectedBranchForcePush = 'ProtectedBranchForcePush',
|
|
27
|
-
ProtectedBranchRequiresReview = 'ProtectedBranchRequiresReview',
|
|
28
|
-
ProtectedBranchDeleteRejected = 'ProtectedBranchDeleteRejected',
|
|
29
|
-
ProtectedBranchRequiredStatus = 'ProtectedBranchRequiredStatus',
|
|
30
|
-
|
|
31
|
-
// Branch
|
|
32
|
-
BranchDeletionFailed = 'BranchDeletionFailed',
|
|
33
|
-
DefaultBranchDeletionFailed = 'DefaultBranchDeletionFailed',
|
|
34
|
-
BranchAlreadyExists = 'BranchAlreadyExists',
|
|
35
|
-
BranchRenameFailed = 'BranchRenameFailed',
|
|
36
|
-
HexBranchNameRejected = 'HexBranchNameRejected',
|
|
37
|
-
InvalidRefLength = 'InvalidRefLength',
|
|
38
|
-
|
|
39
|
-
// Merge / Rebase
|
|
40
|
-
MergeConflicts = 'MergeConflicts',
|
|
41
|
-
RebaseConflicts = 'RebaseConflicts',
|
|
42
|
-
MergeWithLocalChanges = 'MergeWithLocalChanges',
|
|
43
|
-
RebaseWithLocalChanges = 'RebaseWithLocalChanges',
|
|
44
|
-
InvalidMerge = 'InvalidMerge',
|
|
45
|
-
InvalidRebase = 'InvalidRebase',
|
|
46
|
-
NonFastForwardMergeIntoEmptyHead = 'NonFastForwardMergeIntoEmptyHead',
|
|
47
|
-
CannotMergeUnrelatedHistories = 'CannotMergeUnrelatedHistories',
|
|
48
|
-
NoMergeToAbort = 'NoMergeToAbort',
|
|
49
|
-
RevertConflicts = 'RevertConflicts',
|
|
50
|
-
|
|
51
|
-
// Rebase
|
|
52
|
-
EmptyRebasePatch = 'EmptyRebasePatch',
|
|
53
|
-
|
|
54
|
-
// Commit
|
|
55
|
-
NothingToCommit = 'NothingToCommit',
|
|
56
|
-
GPGFailedToSignData = 'GPGFailedToSignData',
|
|
57
|
-
|
|
58
|
-
// Bad config
|
|
59
|
-
BadConfigValue = 'BadConfigValue',
|
|
60
|
-
ConfigLockFileAlreadyExists = 'ConfigLockFileAlreadyExists',
|
|
61
|
-
|
|
62
|
-
// Misc
|
|
63
|
-
NotAGitRepository = 'NotAGitRepository',
|
|
64
|
-
BadRevision = 'BadRevision',
|
|
65
|
-
LocalPermissionDenied = 'LocalPermissionDenied',
|
|
66
|
-
NoMatchingRemoteBranch = 'NoMatchingRemoteBranch',
|
|
67
|
-
NoExistingRemoteBranch = 'NoExistingRemoteBranch',
|
|
68
|
-
PatchDoesNotApply = 'PatchDoesNotApply',
|
|
69
|
-
NoSubmoduleMapping = 'NoSubmoduleMapping',
|
|
70
|
-
SubmoduleRepositoryDoesNotExist = 'SubmoduleRepositoryDoesNotExist',
|
|
71
|
-
InvalidSubmoduleSHA = 'InvalidSubmoduleSHA',
|
|
72
|
-
LockFileAlreadyExists = 'LockFileAlreadyExists',
|
|
73
|
-
UnresolvedConflicts = 'UnresolvedConflicts',
|
|
74
|
-
LocalChangesOverwritten = 'LocalChangesOverwritten',
|
|
75
|
-
RemoteAlreadyExists = 'RemoteAlreadyExists',
|
|
76
|
-
TagAlreadyExists = 'TagAlreadyExists',
|
|
77
|
-
PathDoesNotExist = 'PathDoesNotExist',
|
|
78
|
-
InvalidObjectName = 'InvalidObjectName',
|
|
79
|
-
OutsideRepository = 'OutsideRepository',
|
|
80
|
-
ConflictModifyDeletedInBranch = 'ConflictModifyDeletedInBranch',
|
|
81
|
-
MergeCommitNoMainlineOption = 'MergeCommitNoMainlineOption',
|
|
82
|
-
UnsafeDirectory = 'UnsafeDirectory',
|
|
83
|
-
PathExistsButNotInRef = 'PathExistsButNotInRef',
|
|
84
|
-
LFSAttributeDoesNotMatch = 'LFSAttributeDoesNotMatch',
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export class ExecError extends Error {
|
|
88
|
-
public readonly code: string
|
|
89
|
-
public readonly stdout: string | Buffer
|
|
90
|
-
public readonly stderr: string | Buffer
|
|
91
|
-
public readonly cause?: Error
|
|
92
|
-
|
|
93
|
-
constructor(
|
|
94
|
-
message: string,
|
|
95
|
-
stdout: string | Buffer,
|
|
96
|
-
stderr: string | Buffer,
|
|
97
|
-
cause?: Error
|
|
98
|
-
) {
|
|
99
|
-
super(message)
|
|
100
|
-
this.name = 'ExecError'
|
|
101
|
-
this.code = 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER'
|
|
102
|
-
this.stdout = stdout
|
|
103
|
-
this.stderr = stderr
|
|
104
|
-
this.cause = cause
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
export interface IGitResult {
|
|
109
|
-
readonly stdout: string | Buffer
|
|
110
|
-
readonly stderr: string | Buffer
|
|
111
|
-
readonly exitCode: number
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
export interface IGitExecutionOptions {
|
|
115
|
-
readonly env?: Record<string, string | undefined>
|
|
116
|
-
readonly cwd?: string
|
|
117
|
-
readonly stdin?: string
|
|
118
|
-
readonly maxBuffer?: number
|
|
119
|
-
readonly processCallback?: (process: any) => void
|
|
120
|
-
readonly encoding?: BufferEncoding
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
export interface IGitSpawnOptions extends SpawnOptions {
|
|
124
|
-
readonly stdin?: string
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Execute a git command and return the result.
|
|
129
|
-
*/
|
|
130
|
-
export async function exec(
|
|
131
|
-
args: string[],
|
|
132
|
-
path: string,
|
|
133
|
-
options?: IGitExecutionOptions
|
|
134
|
-
): Promise<IGitResult> {
|
|
135
|
-
return new Promise((resolve, reject) => {
|
|
136
|
-
const child = spawn('git', args, {
|
|
137
|
-
cwd: path,
|
|
138
|
-
env: {
|
|
139
|
-
...process.env,
|
|
140
|
-
TERM: 'dumb',
|
|
141
|
-
...options?.env,
|
|
142
|
-
} as Record<string, string>,
|
|
143
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
const stdoutChunks: Buffer[] = []
|
|
147
|
-
const stderrChunks: Buffer[] = []
|
|
148
|
-
|
|
149
|
-
child.stdout?.on('data', (chunk: Buffer) => stdoutChunks.push(chunk))
|
|
150
|
-
child.stderr?.on('data', (chunk: Buffer) => stderrChunks.push(chunk))
|
|
151
|
-
|
|
152
|
-
if (options?.processCallback) {
|
|
153
|
-
options.processCallback(child)
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
if (options?.stdin) {
|
|
157
|
-
child.stdin?.write(options.stdin)
|
|
158
|
-
child.stdin?.end()
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
child.on('close', (exitCode) => {
|
|
162
|
-
const stdoutBuf = Buffer.concat(stdoutChunks)
|
|
163
|
-
const stderrBuf = Buffer.concat(stderrChunks)
|
|
164
|
-
|
|
165
|
-
const enc = (options as any)?.encoding
|
|
166
|
-
const isBuffer = enc === 'buffer'
|
|
167
|
-
|
|
168
|
-
resolve({
|
|
169
|
-
stdout: isBuffer ? stdoutBuf : stdoutBuf.toString(options?.encoding || 'utf-8'),
|
|
170
|
-
stderr: isBuffer ? stderrBuf : stderrBuf.toString(options?.encoding || 'utf-8'),
|
|
171
|
-
exitCode: exitCode ?? -1,
|
|
172
|
-
})
|
|
173
|
-
})
|
|
174
|
-
|
|
175
|
-
child.on('error', (err: Error) => {
|
|
176
|
-
reject(err)
|
|
177
|
-
})
|
|
178
|
-
})
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* Spawn a git process, returning the ChildProcess for streaming access.
|
|
183
|
-
*/
|
|
184
|
-
export function spawnGit(
|
|
185
|
-
args: string[],
|
|
186
|
-
path: string,
|
|
187
|
-
options?: IGitSpawnOptions
|
|
188
|
-
) {
|
|
189
|
-
return spawn('git', args, {
|
|
190
|
-
...options,
|
|
191
|
-
cwd: path,
|
|
192
|
-
env: {
|
|
193
|
-
...process.env,
|
|
194
|
-
TERM: 'dumb',
|
|
195
|
-
...options?.env,
|
|
196
|
-
} as Record<string, string>,
|
|
197
|
-
})
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* Parse git stderr output into a GitError enum value.
|
|
202
|
-
*/
|
|
203
|
-
export function parseError(stderr: string): GitError | null {
|
|
204
|
-
if (!stderr) return null
|
|
205
|
-
|
|
206
|
-
// Authentication errors
|
|
207
|
-
if (stderr.includes('Permission denied (publickey)'))
|
|
208
|
-
return GitError.SSHPermissionDenied
|
|
209
|
-
if (stderr.includes('fatal: Could not read from remote repository'))
|
|
210
|
-
return GitError.SSHAuthenticationFailed
|
|
211
|
-
if (stderr.includes('Authentication failed'))
|
|
212
|
-
return GitError.HTTPSAuthenticationFailed
|
|
213
|
-
|
|
214
|
-
// Merge conflicts
|
|
215
|
-
if (stderr.includes('Automatic merge failed'))
|
|
216
|
-
return GitError.MergeConflicts
|
|
217
|
-
if (stderr.includes('merge failed')) return GitError.MergeConflicts
|
|
218
|
-
if (stderr.includes('conflict')) return GitError.MergeConflicts
|
|
219
|
-
|
|
220
|
-
// Rebase
|
|
221
|
-
if (stderr.includes('could not apply'))
|
|
222
|
-
return GitError.RebaseConflicts
|
|
223
|
-
if (stderr.includes('interactive rebase already started'))
|
|
224
|
-
return GitError.RebaseConflicts
|
|
225
|
-
|
|
226
|
-
// Push
|
|
227
|
-
if (stderr.includes('push is not fast forward') || stderr.includes('[rejected]'))
|
|
228
|
-
return GitError.PushNotFastForward
|
|
229
|
-
if (stderr.includes('failed to push'))
|
|
230
|
-
return GitError.PushNotFastForward
|
|
231
|
-
|
|
232
|
-
// Remote
|
|
233
|
-
if (stderr.includes('Could not resolve host'))
|
|
234
|
-
return GitError.RemoteDisconnection
|
|
235
|
-
if (stderr.includes('host down')) return GitError.HostDown
|
|
236
|
-
if (stderr.includes('Repository not found'))
|
|
237
|
-
return GitError.HTTPSRepositoryNotFound
|
|
238
|
-
if (stderr.includes('not found')) return GitError.HTTPSRepositoryNotFound
|
|
239
|
-
|
|
240
|
-
// Branch
|
|
241
|
-
if (stderr.includes('branch .* already exists'))
|
|
242
|
-
return GitError.BranchAlreadyExists
|
|
243
|
-
if (stderr.includes('not a valid branch'))
|
|
244
|
-
return GitError.BranchDeletionFailed
|
|
245
|
-
if (stderr.includes('could not delete'))
|
|
246
|
-
return GitError.BranchDeletionFailed
|
|
247
|
-
|
|
248
|
-
// Config
|
|
249
|
-
if (stderr.includes('bad config value'))
|
|
250
|
-
return GitError.BadConfigValue
|
|
251
|
-
if (stderr.includes('bad numeric config value'))
|
|
252
|
-
return GitError.BadConfigValue
|
|
253
|
-
if (stderr.includes('config file lock'))
|
|
254
|
-
return GitError.ConfigLockFileAlreadyExists
|
|
255
|
-
|
|
256
|
-
// Misc
|
|
257
|
-
if (stderr.includes('fatal: not a git repository'))
|
|
258
|
-
return GitError.NotAGitRepository
|
|
259
|
-
if (stderr.includes('bad revision'))
|
|
260
|
-
return GitError.BadRevision
|
|
261
|
-
if (stderr.includes('nothing to commit'))
|
|
262
|
-
return GitError.NothingToCommit
|
|
263
|
-
if (stderr.includes('Permission denied'))
|
|
264
|
-
return GitError.LocalPermissionDenied
|
|
265
|
-
if (stderr.includes('pathspec .* did not match'))
|
|
266
|
-
return GitError.PathDoesNotExist
|
|
267
|
-
if (stderr.includes('did not match any files'))
|
|
268
|
-
return GitError.PathDoesNotExist
|
|
269
|
-
|
|
270
|
-
return null
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* Parse information about a bad config value error.
|
|
275
|
-
*/
|
|
276
|
-
export function parseBadConfigValueErrorInfo(
|
|
277
|
-
stderr: string
|
|
278
|
-
): { key: string; value: string } | null {
|
|
279
|
-
const match = stderr.match(
|
|
280
|
-
/bad config value for '([^']+)' in ([^:]+):?\s*(.*)/
|
|
281
|
-
)
|
|
282
|
-
if (!match) return null
|
|
283
|
-
|
|
284
|
-
return { key: match[1], value: match[3]?.trim() || '' }
|
|
285
|
-
}
|
package/src/git/fetch.ts
DELETED
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
import { git, IGitStringExecutionOptions } from './core.js'
|
|
2
|
-
import { Repository } from '../models/repository.js'
|
|
3
|
-
import { IFetchProgress } from '../models/progress.js'
|
|
4
|
-
import { FetchProgressParser, executionOptionsWithProgress } from '../lib/progress/index.js'
|
|
5
|
-
import { IRemote } from '../models/remote.js'
|
|
6
|
-
import { ITrackingBranch } from '../models/branch.js'
|
|
7
|
-
import { envForRemoteOperation } from './environment.js'
|
|
8
|
-
|
|
9
|
-
async function getFetchArgs(
|
|
10
|
-
remote: string,
|
|
11
|
-
progressCallback?: (progress: IFetchProgress) => void
|
|
12
|
-
) {
|
|
13
|
-
return [
|
|
14
|
-
'fetch',
|
|
15
|
-
...(progressCallback ? ['--progress'] : []),
|
|
16
|
-
'--prune',
|
|
17
|
-
'--recurse-submodules=on-demand',
|
|
18
|
-
remote,
|
|
19
|
-
]
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Fetch from the given remote.
|
|
24
|
-
*
|
|
25
|
-
* @param repository - The repository to fetch into
|
|
26
|
-
*
|
|
27
|
-
* @param account - The account to use when authenticating with the remote
|
|
28
|
-
*
|
|
29
|
-
* @param remote - The remote to fetch from
|
|
30
|
-
*
|
|
31
|
-
* @param progressCallback - An optional function which will be invoked
|
|
32
|
-
* with information about the current progress
|
|
33
|
-
* of the fetch operation. When provided this enables
|
|
34
|
-
* the '--progress' command line flag for
|
|
35
|
-
* 'git fetch'.
|
|
36
|
-
* @param isBackgroundTask - Whether the fetch is being performed as a
|
|
37
|
-
* background task as opposed to being user initiated
|
|
38
|
-
*/
|
|
39
|
-
export async function fetch(
|
|
40
|
-
repository: Repository,
|
|
41
|
-
remote: IRemote,
|
|
42
|
-
progressCallback?: (progress: IFetchProgress) => void,
|
|
43
|
-
isBackgroundTask = false
|
|
44
|
-
): Promise<void> {
|
|
45
|
-
let opts: IGitStringExecutionOptions = {
|
|
46
|
-
successExitCodes: new Set([0]),
|
|
47
|
-
env: await envForRemoteOperation(remote.url),
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (progressCallback) {
|
|
51
|
-
const title = `Fetching ${remote.name}`
|
|
52
|
-
const kind = 'fetch'
|
|
53
|
-
|
|
54
|
-
opts = await executionOptionsWithProgress(
|
|
55
|
-
{ ...opts, trackLFSProgress: true, isBackgroundTask },
|
|
56
|
-
new FetchProgressParser(),
|
|
57
|
-
progress => {
|
|
58
|
-
// In addition to progress output from the remote end and from
|
|
59
|
-
// git itself, the stderr output from pull contains information
|
|
60
|
-
// about ref updates. We don't need to bring those into the progress
|
|
61
|
-
// stream so we'll just punt on anything we don't know about for now.
|
|
62
|
-
if (progress.kind === 'context') {
|
|
63
|
-
if (!progress.text.startsWith('remote: Counting objects')) {
|
|
64
|
-
return
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const description =
|
|
69
|
-
progress.kind === 'progress' ? progress.details.text : progress.text
|
|
70
|
-
const value = progress.percent
|
|
71
|
-
|
|
72
|
-
progressCallback({
|
|
73
|
-
kind,
|
|
74
|
-
title,
|
|
75
|
-
description,
|
|
76
|
-
value,
|
|
77
|
-
remote: remote.name,
|
|
78
|
-
})
|
|
79
|
-
}
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
// Initial progress
|
|
83
|
-
progressCallback({ kind, title, value: 0, remote: remote.name })
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const args = await getFetchArgs(remote.name, progressCallback)
|
|
87
|
-
|
|
88
|
-
await git(args, repository.path, 'fetch', opts)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/** Fetch a given refspec from the given remote. */
|
|
92
|
-
export async function fetchRefspec(
|
|
93
|
-
repository: Repository,
|
|
94
|
-
remote: IRemote,
|
|
95
|
-
refspec: string
|
|
96
|
-
): Promise<void> {
|
|
97
|
-
await git(['fetch', remote.name, refspec], repository.path, 'fetchRefspec', {
|
|
98
|
-
successExitCodes: new Set([0, 128]),
|
|
99
|
-
env: await envForRemoteOperation(remote.url),
|
|
100
|
-
})
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export async function fastForwardBranches(
|
|
104
|
-
repository: Repository,
|
|
105
|
-
branches: ReadonlyArray<ITrackingBranch>
|
|
106
|
-
): Promise<void> {
|
|
107
|
-
if (branches.length === 0) {
|
|
108
|
-
return
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const refPairs = branches.map(branch => `${branch.upstreamRef}:${branch.ref}`)
|
|
112
|
-
|
|
113
|
-
await git(
|
|
114
|
-
[
|
|
115
|
-
'fetch',
|
|
116
|
-
'.',
|
|
117
|
-
// Make sure we don't try to update branches that can't be fast-forwarded
|
|
118
|
-
// even if the user disabled this via the git config option
|
|
119
|
-
// `fetch.showForcedUpdates`
|
|
120
|
-
'--show-forced-updates',
|
|
121
|
-
// Prevent `git fetch` from touching the `FETCH_HEAD`
|
|
122
|
-
'--no-write-fetch-head',
|
|
123
|
-
// Take branch refs from stdin to circumvent shell max line length
|
|
124
|
-
// limitations (mainly on Windows)
|
|
125
|
-
'--stdin',
|
|
126
|
-
],
|
|
127
|
-
repository.path,
|
|
128
|
-
'fastForwardBranches',
|
|
129
|
-
{
|
|
130
|
-
// Fetch exits with an exit code of 1 if one or more refs failed to update
|
|
131
|
-
// which is what we expect will happen
|
|
132
|
-
successExitCodes: new Set([0, 1]),
|
|
133
|
-
env: {
|
|
134
|
-
// This will make sure the reflog entries are correct after
|
|
135
|
-
// fast-forwarding the branches.
|
|
136
|
-
GIT_REFLOG_ACTION: 'pull',
|
|
137
|
-
},
|
|
138
|
-
stdin: refPairs.join('\n'),
|
|
139
|
-
}
|
|
140
|
-
)
|
|
141
|
-
}
|
package/src/git/for-each-ref.ts
DELETED
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
import { git } from './core.js'
|
|
2
|
-
import { GitError } from './exec.js'
|
|
3
|
-
import { Repository } from '../models/repository.js'
|
|
4
|
-
import {
|
|
5
|
-
Branch,
|
|
6
|
-
BranchType,
|
|
7
|
-
IBranchTip,
|
|
8
|
-
ITrackingBranch,
|
|
9
|
-
} from '../models/branch.js'
|
|
10
|
-
import { createForEachRefParser } from './git-delimiter-parser.js'
|
|
11
|
-
|
|
12
|
-
/** Get all the branches. */
|
|
13
|
-
export async function getBranches(
|
|
14
|
-
repository: Repository,
|
|
15
|
-
...prefixes: string[]
|
|
16
|
-
): Promise<ReadonlyArray<Branch>> {
|
|
17
|
-
const { formatArgs, parse } = createForEachRefParser({
|
|
18
|
-
fullName: '%(refname)',
|
|
19
|
-
shortName: '%(refname:short)',
|
|
20
|
-
upstreamShortName: '%(upstream:short)',
|
|
21
|
-
upstreamTrackingBranch: '%(upstream:track)',
|
|
22
|
-
sha: '%(objectname)',
|
|
23
|
-
symRef: '%(symref)',
|
|
24
|
-
authorDate: '%(authordate:iso8601)',
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
if (!prefixes || !prefixes.length) {
|
|
28
|
-
prefixes = ['refs/heads', 'refs/remotes']
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// TODO: use expectedErrors here to handle a specific error
|
|
32
|
-
// see https://github.com/desktop/desktop/pull/5299#discussion_r206603442 for
|
|
33
|
-
// discussion about what needs to change
|
|
34
|
-
const result = await git(
|
|
35
|
-
['for-each-ref', ...formatArgs, ...prefixes],
|
|
36
|
-
repository.path,
|
|
37
|
-
'getBranches',
|
|
38
|
-
{ expectedErrors: new Set([GitError.NotAGitRepository]) }
|
|
39
|
-
)
|
|
40
|
-
|
|
41
|
-
if (result.gitError === GitError.NotAGitRepository) {
|
|
42
|
-
return []
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const branches = []
|
|
46
|
-
|
|
47
|
-
for (const ref of parse(result.stdout)) {
|
|
48
|
-
// excude symbolic refs from the branch list
|
|
49
|
-
if (ref.symRef.length > 0) {
|
|
50
|
-
continue
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const tip: IBranchTip = {
|
|
54
|
-
sha: ref.sha,
|
|
55
|
-
author: {
|
|
56
|
-
date: new Date(ref.authorDate),
|
|
57
|
-
},
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const type = ref.fullName.startsWith('refs/heads')
|
|
61
|
-
? BranchType.Local
|
|
62
|
-
: BranchType.Remote
|
|
63
|
-
|
|
64
|
-
const upstream =
|
|
65
|
-
ref.upstreamShortName.length > 0 ? ref.upstreamShortName : null
|
|
66
|
-
|
|
67
|
-
const isGone = ['[gone]', '(gone)'].includes(ref.upstreamTrackingBranch)
|
|
68
|
-
|
|
69
|
-
branches.push(
|
|
70
|
-
new Branch(ref.shortName, upstream, tip, type, ref.fullName, isGone)
|
|
71
|
-
)
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return branches
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Gets all branches that differ from their upstream (i.e. they're ahead,
|
|
79
|
-
* behind or both), excluding the current branch.
|
|
80
|
-
* Useful to narrow down a list of branches that could potentially be fast
|
|
81
|
-
* forwarded.
|
|
82
|
-
*
|
|
83
|
-
* @param repository Repository to get the branches from.
|
|
84
|
-
*/
|
|
85
|
-
export async function getBranchesDifferingFromUpstream(
|
|
86
|
-
repository: Repository
|
|
87
|
-
): Promise<ReadonlyArray<ITrackingBranch>> {
|
|
88
|
-
const { formatArgs, parse } = createForEachRefParser({
|
|
89
|
-
fullName: '%(refname)',
|
|
90
|
-
sha: '%(objectname)', // SHA
|
|
91
|
-
upstream: '%(upstream)',
|
|
92
|
-
symref: '%(symref)',
|
|
93
|
-
head: '%(HEAD)',
|
|
94
|
-
worktreePath: '%(worktreepath)',
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
const prefixes = ['refs/heads', 'refs/remotes']
|
|
98
|
-
|
|
99
|
-
const result = await git(
|
|
100
|
-
['for-each-ref', ...formatArgs, ...prefixes],
|
|
101
|
-
repository.path,
|
|
102
|
-
'getBranchesDifferingFromUpstream',
|
|
103
|
-
{ expectedErrors: new Set([GitError.NotAGitRepository]) }
|
|
104
|
-
)
|
|
105
|
-
|
|
106
|
-
if (result.gitError === GitError.NotAGitRepository) {
|
|
107
|
-
return []
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const localBranches = []
|
|
111
|
-
const remoteBranchShas = new Map<string, string>()
|
|
112
|
-
|
|
113
|
-
// First we need to collect the relevant info from the command output:
|
|
114
|
-
// - For local branches with upstream: name, ref, SHA and the upstream.
|
|
115
|
-
// - For remote branches we only need the sha (and the ref as key).
|
|
116
|
-
for (const ref of parse(result.stdout)) {
|
|
117
|
-
if (ref.symref.length > 0 || ref.head === '*') {
|
|
118
|
-
// Exclude symbolic refs and the current branch
|
|
119
|
-
continue
|
|
120
|
-
}
|
|
121
|
-
if (ref.worktreePath.length > 0 && ref.worktreePath !== repository.path) {
|
|
122
|
-
// Exclude branches checked out in other worktrees, since they can't be fast-forwarded from here
|
|
123
|
-
continue
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
if (ref.fullName.startsWith('refs/heads')) {
|
|
127
|
-
if (ref.upstream.length === 0) {
|
|
128
|
-
// Exclude local branches without upstream
|
|
129
|
-
continue
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
localBranches.push({
|
|
133
|
-
ref: ref.fullName,
|
|
134
|
-
sha: ref.sha,
|
|
135
|
-
upstream: ref.upstream,
|
|
136
|
-
})
|
|
137
|
-
} else {
|
|
138
|
-
remoteBranchShas.set(ref.fullName, ref.sha)
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const eligibleBranches = new Array<ITrackingBranch>()
|
|
143
|
-
|
|
144
|
-
// Compare the SHA of every local branch with the SHA of its upstream and
|
|
145
|
-
// collect the names of local branches that differ from their upstream.
|
|
146
|
-
for (const branch of localBranches) {
|
|
147
|
-
const remoteSha = remoteBranchShas.get(branch.upstream)
|
|
148
|
-
|
|
149
|
-
if (remoteSha !== undefined && remoteSha !== branch.sha) {
|
|
150
|
-
eligibleBranches.push({
|
|
151
|
-
ref: branch.ref,
|
|
152
|
-
sha: branch.sha,
|
|
153
|
-
upstreamRef: branch.upstream,
|
|
154
|
-
upstreamSha: remoteSha,
|
|
155
|
-
})
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
return eligibleBranches
|
|
160
|
-
}
|