-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathGitFacade.ts
More file actions
95 lines (78 loc) · 2.95 KB
/
Copy pathGitFacade.ts
File metadata and controls
95 lines (78 loc) · 2.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import simpleGit, { SimpleGit } from 'simple-git'
import { BranchName, Commit, ShortCommitHash } from './types'
export const NO_UPSTREAM = 'NO_UPSTREAM'
export class GitFacade {
private readonly git: SimpleGit
constructor() {
this.git = simpleGit()
}
updateWorkingDirectory(path: string): void {
this.git.cwd(path)
}
async hasStagedFiles(): Promise<boolean> {
const diff = await this.git.diff(['--name-only', '--cached']);
return (diff.trim() !== '')
}
async getCurrentBranch(): Promise<BranchName> {
const branchSummary = await this.git.branch()
return branchSummary.current
}
async getMainBranch(): Promise<BranchName> {
const CANDIDATES = ['main', 'master']
for (const candidate of CANDIDATES) {
if (await this.hasBranch(candidate)) {
return candidate
}
}
throw new Error('No main branch')
}
private async hasBranch(name: BranchName): Promise<boolean> {
const result = await this.git.raw(['branch', '-l', name])
return (result.trim() !== '')
}
async getFeatureBranchCommits(featureBranch: BranchName, mainBranch: BranchName): Promise<readonly Commit[]> {
return this.queryCommits(featureBranch, '--not', mainBranch)
}
async getMainBranchCommits(mainBranch: BranchName): Promise<readonly Commit[]> {
return await this.queryCommits(mainBranch)
}
async getCommitsNotInUpstream(): Promise<readonly Commit[] | typeof NO_UPSTREAM> {
try {
return (await this.queryCommits('@{u}..'))
} catch (e: any) {
if (e.message?.includes('no upstream configured')) {
return NO_UPSTREAM
}
throw e
}
}
async getLatestFixedCommit(): Promise<Commit> {
return (await this.queryCommits('-g', '-1', '--grep-reflog=rebase \(fixup\)'))[0]
}
private async queryCommits(...args: string[]): Promise<Commit[]> {
const lines = (await this.git.raw(['log', ...args, '--format=%h %s'])).trim().split('\n')
return lines.map((line) => {
const separatorIndex = line.indexOf(' ')
const hash = line.slice(0, separatorIndex)
const subject = line.slice(separatorIndex + 1)
return { hash, subject }
})
}
async commitFixup(hash: ShortCommitHash): Promise<void> {
await this.git.commit('', undefined, { '--fixup': hash })
}
async rebaseFixupCommit(hash: ShortCommitHash): Promise<boolean> {
try {
await this.git.env({
...process.env,
GIT_SEQUENCE_EDITOR: 'true' // bypass the interactive editor
}).rebase(['--autosquash', '--autostash', '-i', `${hash}~`])
return false
} catch (e: any) {
if (e.message?.includes('Merge conflict')) {
return true
}
throw e
}
}
}