Skip to content

Commit 5f0aa34

Browse files
authored
Merge pull request #11 from lambdalisue/fix/branch-slash
🐛 Fix remote detection with branch names containing slashes
2 parents 8e3367e + 48f729f commit 5f0aa34

File tree

3 files changed

+298
-9
lines changed

3 files changed

+298
-9
lines changed

process.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,14 @@ export type ExecuteOptions = Omit<
55
"args" | "stdin" | "stdout" | "stderr"
66
>;
77

8-
export async function execute(
8+
export function execute(
9+
args: string[],
10+
options: ExecuteOptions = {},
11+
): Promise<string> {
12+
return _internals.execute(args, options);
13+
}
14+
15+
async function _execute(
916
args: string[],
1017
options: ExecuteOptions = {},
1118
): Promise<string> {
@@ -49,3 +56,8 @@ export class ExecuteError extends Error {
4956
this.name = this.constructor.name;
5057
}
5158
}
59+
60+
// For internal stub testing
61+
export const _internals = {
62+
execute: _execute,
63+
};

util.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
1-
import { common } from "https://deno.land/[email protected]/path/mod.ts";
21
import { execute, ExecuteOptions } from "./process.ts";
32

43
export async function getRemoteContains(
54
commitish: string,
65
options: ExecuteOptions = {},
76
): Promise<string | undefined> {
8-
const stdout = await execute(
7+
const branches = (await execute(
98
["branch", "-r", "--contains", commitish, "--format=%(refname:short)"],
109
options,
11-
);
12-
const result = common(stdout.trim().split("\n"));
13-
if (!result) {
14-
return undefined;
15-
}
16-
return result?.substring(0, result.length - 1);
10+
)).trim().split("\n");
11+
const remotes = (await execute(["remote"], options)).trim().split("\n");
12+
const remoteContains = remotes.filter((remote) => {
13+
return branches.some((branch) => branch.startsWith(`${remote}/`));
14+
});
15+
// Prefer "origin" if it exists
16+
return remoteContains.includes("origin") ? "origin" : remoteContains.at(0);
1717
}
1818

1919
export async function getRemoteFetchURL(

util_test.ts

Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
import { stub } from "https://deno.land/[email protected]/testing/mock.ts";
2+
import {
3+
assertEquals,
4+
unreachable,
5+
} from "https://deno.land/[email protected]/assert/mod.ts";
6+
import { _internals } from "./process.ts";
7+
import {
8+
getCommitAbbrevRef,
9+
getCommitSHA1,
10+
getRemoteContains,
11+
getRemoteFetchURL,
12+
} from "./util.ts";
13+
14+
Deno.test("getRemoteContains", async (t) => {
15+
await t.step(
16+
"returns undefined if no remote contains the commitish",
17+
async () => {
18+
const executeStub = stub(_internals, "execute", (args, _options) => {
19+
if (args.at(0) === "remote") {
20+
return Promise.resolve("origin\nfork\n");
21+
}
22+
if (args.at(0) === "branch") {
23+
return Promise.resolve("\n");
24+
}
25+
unreachable();
26+
});
27+
try {
28+
const remote = await getRemoteContains("<<commitish>>");
29+
assertEquals(remote, undefined);
30+
} finally {
31+
executeStub.restore();
32+
}
33+
},
34+
);
35+
36+
await t.step(
37+
"returns 'origin' if 'origin/my-awesome-branch' contains the commitish",
38+
async () => {
39+
const executeStub = stub(_internals, "execute", (args, _options) => {
40+
if (args.at(0) === "remote") {
41+
return Promise.resolve("origin\nfork\n");
42+
}
43+
if (args.at(0) === "branch") {
44+
return Promise.resolve("origin/my-awesome-branch\n");
45+
}
46+
unreachable();
47+
});
48+
try {
49+
const remote = await getRemoteContains("<<commitish>>");
50+
assertEquals(remote, "origin");
51+
} finally {
52+
executeStub.restore();
53+
}
54+
},
55+
);
56+
57+
await t.step(
58+
"returns 'origin' if 'origin/HEAD' and 'origin/my-awesome-branch' contains the commitish",
59+
async () => {
60+
const executeStub = stub(_internals, "execute", (args, _options) => {
61+
if (args.at(0) === "remote") {
62+
return Promise.resolve("origin\nfork\n");
63+
}
64+
if (args.at(0) === "branch") {
65+
return Promise.resolve("origin/HEAD\norigin/my-awesome-branch\n");
66+
}
67+
unreachable();
68+
});
69+
try {
70+
const remote = await getRemoteContains("<<commitish>>");
71+
assertEquals(remote, "origin");
72+
} finally {
73+
executeStub.restore();
74+
}
75+
},
76+
);
77+
78+
await t.step(
79+
"returns 'fork' if 'fork/my-awesome-branch' contains the commitish",
80+
async () => {
81+
const executeStub = stub(_internals, "execute", (args, _options) => {
82+
if (args.at(0) === "remote") {
83+
return Promise.resolve("origin\nfork\n");
84+
}
85+
if (args.at(0) === "branch") {
86+
return Promise.resolve("fork/my-awesome-branch\n");
87+
}
88+
unreachable();
89+
});
90+
try {
91+
const remote = await getRemoteContains("<<commitish>>");
92+
assertEquals(remote, "fork");
93+
} finally {
94+
executeStub.restore();
95+
}
96+
},
97+
);
98+
99+
await t.step(
100+
"returns 'origin' if 'origin/feature/my-awesome-branch' contains the commitish (#9)",
101+
async () => {
102+
const executeStub = stub(_internals, "execute", (args, _options) => {
103+
if (args.at(0) === "remote") {
104+
return Promise.resolve("origin\nfork\n");
105+
}
106+
if (args.at(0) === "branch") {
107+
return Promise.resolve("origin/feature/my-awesome-branch\n");
108+
}
109+
unreachable();
110+
});
111+
try {
112+
const remote = await getRemoteContains("<<commitish>>");
113+
assertEquals(remote, "origin");
114+
} finally {
115+
executeStub.restore();
116+
}
117+
},
118+
);
119+
120+
await t.step(
121+
"returns 'fork/my-awesome-fork' if 'fork/my-awesome-fork/feature/my-awesome-branch' contains the commitish",
122+
async () => {
123+
const executeStub = stub(_internals, "execute", (args, _options) => {
124+
if (args.at(0) === "remote") {
125+
return Promise.resolve("origin\nfork/my-awesome-fork\n");
126+
}
127+
if (args.at(0) === "branch") {
128+
return Promise.resolve(
129+
"fork/my-awesome-fork/feature/my-awesome-branch\n",
130+
);
131+
}
132+
unreachable();
133+
});
134+
try {
135+
const remote = await getRemoteContains("<<commitish>>");
136+
assertEquals(remote, "fork/my-awesome-fork");
137+
} finally {
138+
executeStub.restore();
139+
}
140+
},
141+
);
142+
143+
await t.step(
144+
"returns 'origin' if 'origin/my-awesome-branch' and 'fork/my-awesome-branch' contains the commitish",
145+
async () => {
146+
const executeStub = stub(_internals, "execute", (args, _options) => {
147+
if (args.at(0) === "remote") {
148+
return Promise.resolve("origin\nfork\n");
149+
}
150+
if (args.at(0) === "branch") {
151+
return Promise.resolve(
152+
"fork/my-awesome-branch\norigin/my-awesome-branch\n",
153+
);
154+
}
155+
unreachable();
156+
});
157+
try {
158+
const remote = await getRemoteContains("<<commitish>>");
159+
assertEquals(remote, "origin");
160+
} finally {
161+
executeStub.restore();
162+
}
163+
},
164+
);
165+
});
166+
167+
Deno.test("getRemoteFetchURL", async (t) => {
168+
await t.step(
169+
"returns URL of the remote (HTTP URL)",
170+
async () => {
171+
const executeStub = stub(_internals, "execute", (args, _options) => {
172+
if (args.at(0) === "remote") {
173+
return Promise.resolve(
174+
"https://github.com/lambdalisue/deno-git-browse\n",
175+
);
176+
}
177+
unreachable();
178+
});
179+
try {
180+
const url = await getRemoteFetchURL("origin");
181+
assertEquals(
182+
url,
183+
new URL("https://github.com/lambdalisue/deno-git-browse"),
184+
);
185+
} finally {
186+
executeStub.restore();
187+
}
188+
},
189+
);
190+
191+
await t.step(
192+
"returns URL of the remote (SSH URL)",
193+
async () => {
194+
const executeStub = stub(_internals, "execute", (args, _options) => {
195+
if (args.at(0) === "remote") {
196+
return Promise.resolve(
197+
"ssh://[email protected]/lambdalisue/deno-git-browse\n",
198+
);
199+
}
200+
unreachable();
201+
});
202+
try {
203+
const url = await getRemoteFetchURL("origin");
204+
assertEquals(
205+
url,
206+
new URL("ssh://[email protected]/lambdalisue/deno-git-browse"),
207+
);
208+
} finally {
209+
executeStub.restore();
210+
}
211+
},
212+
);
213+
214+
await t.step(
215+
"returns URL of the remote (SSH Protocol)",
216+
async () => {
217+
const executeStub = stub(_internals, "execute", (args, _options) => {
218+
if (args.at(0) === "remote") {
219+
return Promise.resolve(
220+
"[email protected]:lambdalisue/deno-git-browse.git\n",
221+
);
222+
}
223+
unreachable();
224+
});
225+
try {
226+
const url = await getRemoteFetchURL("origin");
227+
assertEquals(
228+
url,
229+
new URL("ssh://[email protected]/lambdalisue/deno-git-browse"),
230+
);
231+
} finally {
232+
executeStub.restore();
233+
}
234+
},
235+
);
236+
});
237+
238+
Deno.test("getCommitSHA1", async (t) => {
239+
await t.step(
240+
"returns commit SHA1",
241+
async () => {
242+
const expect = "8e3367e40b91850d7f7864b4a8984d25f6e9419e";
243+
const executeStub = stub(_internals, "execute", (args, _options) => {
244+
if (args.at(0) === "rev-parse") {
245+
return Promise.resolve(`${expect}\n`);
246+
}
247+
unreachable();
248+
});
249+
try {
250+
const sha1 = await getCommitSHA1("<<commitish>>");
251+
assertEquals(sha1, expect);
252+
} finally {
253+
executeStub.restore();
254+
}
255+
},
256+
);
257+
});
258+
259+
Deno.test("getCommitAbbrevRef", async (t) => {
260+
await t.step(
261+
"returns commit SHA1",
262+
async () => {
263+
const executeStub = stub(_internals, "execute", (args, _options) => {
264+
if (args.at(0) === "rev-parse") {
265+
return Promise.resolve(`my-awesome-branch\n`);
266+
}
267+
unreachable();
268+
});
269+
try {
270+
const sha1 = await getCommitAbbrevRef("<<commitish>>");
271+
assertEquals(sha1, "my-awesome-branch");
272+
} finally {
273+
executeStub.restore();
274+
}
275+
},
276+
);
277+
});

0 commit comments

Comments
 (0)