diff --git a/src/commands/clone.ts b/src/commands/clone.ts index f73786c..b627b35 100644 --- a/src/commands/clone.ts +++ b/src/commands/clone.ts @@ -3,20 +3,9 @@ import GitUrlParse from 'git-url-parse'; import path from 'path'; import * as zx from 'zx'; +import { insideDir } from '../utils/filesystem.js'; import { Submodule } from '../utils/submodules.js'; -const insideDir = async (path: string, callback: () => Promise) => { - const pwd = (await zx.$`pwd`).stdout; - zx.cd(path); - await callback(); - - try { - zx.cd(pwd); - } catch { - /* ignored */ - } -}; - const convertToAuthURL = (url: string, githubToken: string): string => { const parsed = GitUrlParse(url); parsed.token = githubToken; @@ -29,10 +18,10 @@ type CloneOptions = { submodules: Submodule[]; }; export const clone = async ({ githubToken, depth, submodules }: CloneOptions) => { - const rootDir = (await zx.$`pwd`).stdout.trim(); + const topLevel = (await zx.$`git rev-parse --show-toplevel`).stdout.trim(); for (const submodule of submodules) { - const submoduleDir = path.join(rootDir, submodule.path); + const submoduleDir = path.join(topLevel, submodule.gitModulePath); const submoduleURL = !!githubToken ? convertToAuthURL(submodule.url, githubToken) : submodule.url; await zx.$`rm -rf ${submoduleDir}`.catch(() => { diff --git a/src/utils/filesystem.ts b/src/utils/filesystem.ts new file mode 100644 index 0000000..e4995b6 --- /dev/null +++ b/src/utils/filesystem.ts @@ -0,0 +1,21 @@ +import path from 'path'; +import * as zx from 'zx'; + +// NOTE: Assumes that current platform is case-sensitive +export const haveSamePath = (haystack: string[], needle: string) => + haystack.some((hay) => path.resolve(hay) === path.resolve(needle)); + +export const insideDir = async ( + path: string, + callback: () => Promise, +) => { + const pwd = (await zx.$`pwd`).stdout; + zx.cd(path); + await callback(); + + try { + zx.cd(pwd); + } catch { + /* ignored */ + } +}; diff --git a/src/utils/submodules.ts b/src/utils/submodules.ts index 6443e2c..129603d 100644 --- a/src/utils/submodules.ts +++ b/src/utils/submodules.ts @@ -1,49 +1,62 @@ -import path from 'path'; +import NodePath from 'path'; import * as zx from 'zx'; +import { haveSamePath, insideDir } from './filesystem.js'; + export type Submodule = { commitHash: string; path: string; url: string; + gitModulePath: string; }; -// NOTE: Assumes that current platform is case-sensitive -const haveSamePath = (haystack: string[], needle: string) => - haystack.some((hay) => path.resolve(hay) === path.resolve(needle)); - type FetchSubmodulesOptions = { paths: string[] | null; }; export const fetchSubmodules = async ( options: FetchSubmodulesOptions, ): Promise => { - const output = await zx.$`git submodule status --recursive`; - - const submodules = await Promise.all( - output.stdout - .split('\n') - .flatMap((rawLine) => { - const line = rawLine.trim(); - if (line.length > 0) { - let [commitHash, path] = line.split(' '); - if (!!options.paths && !haveSamePath(options.paths, path)) { - return []; - } - if (commitHash.startsWith('-')) { - commitHash = commitHash.slice(1); - } - return { commitHash, path }; - } + const topLevel = (await zx.$`git rev-parse --show-toplevel`).stdout.trim(); + let output: string = ''; + await insideDir(topLevel, async () => { + output = (await zx.$`git submodule status --recursive`).stdout; + }); + + let submodules: Submodule[] = []; + + const submoduleRefs = output.split('\n').flatMap((rawLine) => { + const line = rawLine.trim(); + if (line.length > 0) { + let [commitHash, path] = line.split(' '); + if (!!options.paths && !haveSamePath(options.paths, path)) { return []; - }) - .map(async ({ commitHash, path }) => { - const url = - await zx.$`git config --file .gitmodules --get submodule.${path}.url`.then( - (output) => output.stdout.trim(), - ); - return { commitHash, path, url }; - }), - ); + } + if (commitHash.startsWith('-')) { + commitHash = commitHash.slice(1); + } + return { + commitHash, + path: NodePath.join(topLevel, path), + gitModulePath: path, + }; + } + return []; + }); + + for (const { commitHash, path, gitModulePath } of submoduleRefs) { + await insideDir(topLevel, async () => { + let pathName = path.endsWith('/') + ? path.substring(0, path.lastIndexOf('/')) + : path; + pathName = pathName.split('/').slice(-1)[0]; + + const url = + await zx.$`git config --file .gitmodules --get submodule.${gitModulePath}.url`.then( + (output) => output.stdout.trim(), + ); + submodules.push({ commitHash, path, url, gitModulePath }); + }); + } return submodules; };