• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

levibostian / new-deployment-tool / 16168759741

09 Jul 2025 12:02PM UTC coverage: 67.273% (+0.3%) from 66.972%
16168759741

push

github

levibostian
test: update snapshots to remove un-used ones

67 of 92 branches covered (72.83%)

Branch coverage included in aggregate %.

673 of 1008 relevant lines covered (66.77%)

7.92 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.77
/lib/git.ts
1
import { exec } from "./exec.ts"
2
import { Exec } from "./exec.ts"
3
import { GitHubCommit } from "./github-api.ts"
4
import * as log from "./log.ts"
2✔
5

6
export interface Git {
7
  checkoutBranch: (
8
    { exec, branch, createBranchIfNotExist }: { exec: Exec; branch: string; createBranchIfNotExist: boolean },
9
  ) => Promise<void>
10
  merge: (
11
    { exec, branchToMergeIn, commitTitle, commitMessage, fastForward }: {
12
      exec: Exec
13
      branchToMergeIn: string
14
      commitTitle: string
15
      commitMessage: string
16
      fastForward?: "--no-ff" | "--ff-only"
17
    },
18
  ) => Promise<void>
19
  pull: ({ exec }: { exec: Exec }) => Promise<void>
20
  setUser: (
21
    { exec, name, email }: { exec: Exec; name: string; email: string },
22
  ) => Promise<void>
23
  squash: (
24
    { exec, branchToSquash, branchMergingInto, commitTitle, commitMessage }: {
25
      exec: Exec
26
      branchToSquash: string
27
      branchMergingInto: string
28
      commitTitle: string
29
      commitMessage: string
30
    },
31
  ) => Promise<void>
32
  rebase: (
33
    { exec, branchToRebaseOnto }: { exec: Exec; branchToRebaseOnto: string },
34
  ) => Promise<void>
35
  getLatestCommitsSince({ exec, commit }: { exec: Exec; commit: GitHubCommit }): Promise<GitHubCommit[]>
36
  getLatestCommitOnBranch({ exec, branch }: { exec: Exec; branch: string }): Promise<GitHubCommit>
37
  createLocalBranchFromRemote: ({ exec, branch }: { exec: Exec; branch: string }) => Promise<void>
38
  getCommits: ({ exec, branch }: { exec: Exec; branch: string }) => Promise<GitHubCommit[]>
39
}
40

41
const checkoutBranch = async (
2✔
42
  { exec, branch, createBranchIfNotExist }: { exec: Exec; branch: string; createBranchIfNotExist: boolean },
2✔
43
): Promise<void> => {
44
  await exec.run({
16✔
45
    command: `git checkout ${createBranchIfNotExist ? "-b " : ""}${branch}`,
16✔
46
    input: undefined,
16✔
47
  })
16✔
48
}
2✔
49

50
const merge = async (
2✔
51
  { exec, branchToMergeIn, commitTitle, commitMessage, fastForward }: {
2✔
52
    exec: Exec
53
    branchToMergeIn: string
54
    commitTitle: string
55
    commitMessage: string
56
    fastForward?: "--no-ff" | "--ff-only"
57
  },
2✔
58
): Promise<void> => {
59
  await exec.run({
5✔
60
    command: `git merge ${branchToMergeIn} -m "${commitTitle}" -m "${commitMessage}" ${fastForward || ""}`,
×
61
    input: undefined,
5✔
62
  })
5✔
63
}
2✔
64

65
const pull = async ({ exec }: { exec: Exec }): Promise<void> => {
2✔
66
  const currentBranchName = (await exec.run({
8✔
67
    command: `git branch --show-current`,
8✔
68
    input: undefined,
8✔
69
  })).stdout.trim()
8✔
70

71
  await exec.run({
8✔
72
    command: `git pull origin ${currentBranchName}`,
8✔
73
    input: undefined,
8✔
74
  })
8✔
75
}
2✔
76

77
const setUser = async (
2✔
78
  { exec, name, email }: { exec: Exec; name: string; email: string },
2✔
79
): Promise<void> => {
80
  await exec.run({
5✔
81
    command: `git config user.name "${name}"`,
5✔
82
    input: undefined,
5✔
83
  })
5✔
84

85
  await exec.run({
5✔
86
    command: `git config user.email "${email}"`,
5✔
87
    input: undefined,
5✔
88
  })
5✔
89
}
2✔
90

91
// Squash all commits of a branch into 1 commit
92
const squash = async (
2✔
93
  { exec, branchToSquash, branchMergingInto, commitTitle, commitMessage }: {
2✔
94
    exec: Exec
95
    branchToSquash: string
96
    branchMergingInto: string
97
    commitTitle: string
98
    commitMessage: string
99
  },
2✔
100
): Promise<void> => {
101
  // We need to find out how many commits 1 branch is ahead of the other to find out how many unique commits there are.
102
  const { stdout } = await exec.run({
3✔
103
    command: `git rev-list --count ${branchMergingInto}..${branchToSquash}`,
3✔
104
    input: undefined,
3✔
105
  })
3✔
106

107
  const numberOfCommitsAheadOfBranchMergingInto = parseInt(stdout.trim())
3✔
108

109
  if (numberOfCommitsAheadOfBranchMergingInto === 0) {
×
110
    log.message(`Branches ${branchToSquash} and ${branchMergingInto} are already up to date. No commits to squash.`)
×
111
    return
×
112
  }
×
113

114
  // Now that we know how many commits are ahead, we can squash all of those commits into 1 commit.
115
  await exec.run({
3✔
116
    command: `git reset --soft HEAD~${numberOfCommitsAheadOfBranchMergingInto}`,
3✔
117
    input: undefined,
3✔
118
  })
3✔
119
  await exec.run({
3✔
120
    command: `git commit -m "${commitTitle}" -m "${commitMessage}"`,
3✔
121
    input: undefined,
3✔
122
  })
3✔
123
}
2✔
124

125
const rebase = async (
2✔
126
  { exec, branchToRebaseOnto }: { exec: Exec; branchToRebaseOnto: string },
2✔
127
): Promise<void> => {
128
  await exec.run({
4✔
129
    command: `git rebase ${branchToRebaseOnto}`,
4✔
130
    input: undefined,
4✔
131
  })
4✔
132
}
2✔
133

134
const getLatestCommitsSince = async (
2✔
135
  { exec, commit }: { exec: Exec; commit: GitHubCommit },
2✔
136
): Promise<GitHubCommit[]> => {
137
  const { stdout } = await exec.run({
5✔
138
    command: `git log --pretty=format:"%H|%s|%ci" ${commit.sha}..HEAD`,
5✔
139
    input: undefined,
5✔
140
  })
5✔
141

142
  return stdout.trim().split("\n").map((commitString) => {
5✔
143
    const [sha, message, dateString] = commitString.split("|")
8✔
144

145
    return { sha, message, date: new Date(dateString) }
40✔
146
  })
5✔
147
}
2✔
148

149
const getLatestCommitOnBranch = async (
2✔
150
  { exec, branch }: { exec: Exec; branch: string },
2✔
151
): Promise<GitHubCommit> => {
152
  const { stdout } = await exec.run({
5✔
153
    command: `git log -1 --pretty=format:"%H|%s|%ci" ${branch}`,
5✔
154
    input: undefined,
5✔
155
  })
5✔
156

157
  const [sha, message, dateString] = stdout.trim().split("|")
5✔
158

159
  return { sha, message, date: new Date(dateString) }
25✔
160
}
2✔
161

162
/**
163
 * Makes sure that we have a local branch that has all of the commits that the remote branch has.
164
 *
165
 * There are a lot of commands here, just to get a local branch of a remote branch. After many attempts to simplify this, we would hit many different errors.
166
 * I believe that complexity comes because we run this tool on a CI server where the git config might be different.
167
 * Running all of these commands and running each command by itself (example: not running `git checkout -b` to try and combine creating a branch and checking it out)
168
 * have given the most consistent results.
169
 */
170
const createLocalBranchFromRemote = async (
2✔
171
  { exec, branch }: { exec: Exec; branch: string },
2✔
172
): Promise<void> => {
173
  const currentBranchName = (await exec.run({
4✔
174
    command: `git branch --show-current`,
4✔
175
    input: undefined,
4✔
176
  })).stdout.trim()
4✔
177
  const doesBranchExist = (await exec.run({
5✔
178
    command: `git branch --list ${branch}`,
5✔
179
    input: undefined,
5✔
180
  })).stdout.trim() !== ""
5✔
181

182
  // Perform a fetch, otherwise you might get errors about origin branch not being found.
183
  await exec.run({
5✔
184
    command: `git fetch origin`,
5✔
185
    input: undefined,
5✔
186
  })
5✔
187

188
  // Only run if it doesn't exist locally. This is to avoid a error that crashes the tool: "fatal: a branch named '<branch-name>' already exists"
189
  if (!doesBranchExist) {
5✔
190
    await exec.run({
5✔
191
      command: `git branch --track ${branch} origin/${branch}`,
5✔
192
      input: undefined,
5✔
193
    })
5✔
194
  }
5✔
195

196
  // Checkout the branch so we can pull it.
197
  await exec.run({
5✔
198
    command: `git checkout ${branch}`,
5✔
199
    input: undefined,
5✔
200
  })
5✔
201

202
  // Pull the branch from the remote.
203
  // Adding --no-rebase to avoid an error that could happen when you run pull.
204
  // The error is: You have divergent branches and need to specify how to reconcile them.
205
  await exec.run({
5✔
206
    command: `git pull --no-rebase origin ${branch}`,
5✔
207
    input: undefined,
5✔
208
  })
5✔
209

210
  // Switch back to the branch we were on before.
211
  await exec.run({
5✔
212
    command: `git checkout ${currentBranchName}`,
5✔
213
    input: undefined,
5✔
214
  })
5✔
215
}
2✔
216

217
const getCommits = async (
×
218
  { exec, branch }: { exec: Exec; branch: string },
×
219
): Promise<GitHubCommit[]> => {
220
  const currentBranchName = (await exec.run({
×
221
    command: `git branch --show-current`,
×
222
    input: undefined,
×
223
  })).stdout.trim()
×
224

225
  await checkoutBranch({ exec, branch, createBranchIfNotExist: false })
×
226

227
  const { stdout } = await exec.run({
×
228
    command: `git log --pretty=format:"%H|%s|%ci"`,
×
229
    input: undefined,
×
230
  })
×
231

232
  const commits = stdout.trim().split("\n").map((commitString) => {
×
233
    const [sha, message, dateString] = commitString.split("|")
×
234

235
    return { sha, message, date: new Date(dateString) }
×
236
  })
×
237

238
  await checkoutBranch({ exec, branch: currentBranchName, createBranchIfNotExist: false })
×
239

240
  return commits
×
241
}
×
242

243
export const git: Git = {
2✔
244
  checkoutBranch,
2✔
245
  merge,
2✔
246
  pull,
2✔
247
  setUser,
2✔
248
  squash,
2✔
249
  rebase,
2✔
250
  getCommits,
2✔
251
  getLatestCommitsSince,
2✔
252
  getLatestCommitOnBranch,
2✔
253
  createLocalBranchFromRemote,
2✔
254
}
2✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc