From 8dfbef1d312583849c3a241da46fd8149a2ba151 Mon Sep 17 00:00:00 2001 From: Jarek Radosz Date: Fri, 25 Jun 2021 03:54:00 +0200 Subject: [PATCH] fix: compatibility with submodules (#48) Previously githubinator couldn't find git data for files inside a submodule. Now it generates URLs, for the correct repositories. There's a new test for `dir` function, but the actual blame URL generation was tested manually on https://github.com/discourse/all-the-plugins and its submodules. --- src/extension.ts | 11 ++++++++--- src/git.ts | 27 +++++++++++++++++++++++++-- src/test/suite/git.test.ts | 30 ++++++++++++++++++++++++++++++ src/utils.ts | 10 ++++++---- 4 files changed, 69 insertions(+), 9 deletions(-) create mode 100644 src/test/suite/git.test.ts diff --git a/src/extension.ts b/src/extension.ts index 45d5fb1..a318aeb 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -158,10 +158,15 @@ async function githubinator({ return err("could not find file") } - const gitDir = git.dir(editorConfig.uri.fsPath) - if (gitDir == null) { + const gitDirectories = git.dir(editorConfig.uri.fsPath) + + if (gitDirectories == null) { return err("Could not find .git directory.") } + + const gitDir = gitDirectories.git + const repoDir = gitDirectories.repository + let headBranch: [string, string | null] | null = null if (mainBranch) { const res = await findShaForBranches(gitDir) @@ -204,7 +209,7 @@ async function githubinator({ ? createSha(head) : createBranch(branchName), relativeFilePath: editorConfig.fileName - ? getRelativeFilePath(gitDir, editorConfig.fileName) + ? getRelativeFilePath(repoDir, editorConfig.fileName) : null, }) if (parsedUrl != null) { diff --git a/src/git.ts b/src/git.ts index fdb6119..66a073f 100644 --- a/src/git.ts +++ b/src/git.ts @@ -7,6 +7,11 @@ interface IRemote { url?: string } +interface IGitDirectories { + git: string + repository: string +} + export async function origin( gitDir: string, remote: string, @@ -100,12 +105,30 @@ export function dir(filePath: string) { function walkUpDirectories( file_path: string, file_or_folder: string, -): string | null { +): IGitDirectories | null { let directory = file_path while (true) { const newPath = path.resolve(directory, file_or_folder) if (fs.existsSync(newPath)) { - return newPath + if (fs.lstatSync(newPath).isFile()) { + const submoduleMatch = fs + .readFileSync(newPath, "utf8") + .match(/gitdir: (.+)/) + + if (submoduleMatch) { + return { + git: path.resolve(path.join(directory, submoduleMatch[1])), + repository: directory, + } + } else { + return null + } + } else { + return { + git: newPath, + repository: directory, + } + } } const newDirectory = path.dirname(directory) if (newDirectory === directory) { diff --git a/src/test/suite/git.test.ts b/src/test/suite/git.test.ts new file mode 100644 index 0000000..398d750 --- /dev/null +++ b/src/test/suite/git.test.ts @@ -0,0 +1,30 @@ +import { dir } from "../../git" +import * as assert from "assert" +import * as path from "path" +import * as fs from "fs" + +suite("git", async () => { + test("dir", () => { + const repoPath = path.normalize(path.join(__dirname, "../../..")) + const gitPath = path.join(repoPath, ".git") + + assert.deepStrictEqual(dir(__dirname), { + git: gitPath, + repository: repoPath, + }) + assert.deepStrictEqual(dir(repoPath), { + git: gitPath, + repository: repoPath, + }) + + const contents = "gitdir: ../../../../.git/modules/test_submodule" + const submodulePath = path.join(__dirname, "test_submodule") + fs.mkdirSync(submodulePath, { recursive: true }) + fs.writeFileSync(path.join(submodulePath, ".git"), contents) + + assert.deepStrictEqual(dir(submodulePath), { + git: path.join(repoPath, ".git/modules/test_submodule"), + repository: submodulePath, + }) + }) +}) diff --git a/src/utils.ts b/src/utils.ts index 94a83b0..954ef91 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,10 +1,12 @@ import * as path from "path" import * as fs from "fs" -/** Get path of file relative to git root. */ -export function getRelativeFilePath(gitDir: string, fileName: string): string { +/** Get path of file relative to repository root. */ +export function getRelativeFilePath( + repositoryDir: string, + fileName: string, +): string { const resolvedFileName = fs.realpathSync(fileName) - const gitProjectRoot = path.dirname(gitDir) + "/" - return resolvedFileName.replace(gitProjectRoot, "") + return resolvedFileName.replace(repositoryDir, "") } /** Convert url/hostname to hostname