Skip to content

Commit ec36b41

Browse files
mournertmcw
authored andcommitted
feat: Add git submodules support to github linking (#1270)
* add git submodules support in github linking * more reliable submodule origin parsing
1 parent a1a2310 commit ec36b41

File tree

8 files changed

+103
-46
lines changed

8 files changed

+103
-46
lines changed

__tests__/lib/git/find_git.js

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,22 @@ const findGit = require('../../../src/git/find_git');
55

66
test('findGit', function() {
77
mock(mockRepo.master);
8+
const root =
9+
path.parse(__dirname).root + path.join('my', 'repository', 'path');
10+
const masterPaths = findGit(path.join(root, 'index.js'));
11+
mock.restore();
812

9-
const root = path.parse(__dirname).root;
10-
11-
expect(
12-
findGit(root + path.join('my', 'repository', 'path', 'index.js'))
13-
).toBe(root + path.join('my', 'repository', 'path', '.git'));
13+
expect(masterPaths).toEqual({
14+
git: path.join(root, '.git'),
15+
root
16+
});
1417

18+
mock(mockRepo.submodule);
19+
const submodulePaths = findGit(path.join(root, 'index.js'));
1520
mock.restore();
21+
22+
expect(submodulePaths).toEqual({
23+
git: path.join(path.dirname(root), '.git', 'modules', 'path'),
24+
root
25+
});
1626
});

__tests__/lib/git/url_prefix.js

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,33 @@ const parsePackedRefs = getGithubURLPrefix.parsePackedRefs;
55

66
test('getGithubURLPrefix', function() {
77
mock(mockRepo.master);
8-
9-
expect(getGithubURLPrefix('/my/repository/path/')).toBe(
10-
'https://github.com/foo/bar/blob/this_is_the_sha/'
11-
);
12-
8+
const masterUrl = getGithubURLPrefix({
9+
git: '/my/repository/path/.git',
10+
root: '/my/repository/path'
11+
});
1312
mock.restore();
1413

14+
expect(masterUrl).toBe('https://github.com/foo/bar/blob/this_is_the_sha/');
15+
1516
mock(mockRepo.detached);
17+
const detachedUrl = getGithubURLPrefix({
18+
git: '/my/repository/path/.git',
19+
root: '/my/repository/path'
20+
});
21+
mock.restore();
1622

17-
expect(getGithubURLPrefix('/my/repository/path/')).toBe(
23+
expect(detachedUrl).toBe(
1824
'https://github.com/foo/bar/blob/e4cb2ffe677571d0503e659e4e64e01f45639c62/'
1925
);
2026

27+
mock(mockRepo.submodule);
28+
const submoduleUrl = getGithubURLPrefix({
29+
git: '/my/repository/.git/modules/path',
30+
root: '/my/repository/path'
31+
});
2132
mock.restore();
33+
34+
expect(submoduleUrl).toBe('https://github.com/foo/bar/blob/this_is_the_sha/');
2235
});
2336

2437
test('parsePackedRefs', function() {

__tests__/utils.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,32 @@ module.exports.mockRepo = {
7474
}
7575
}
7676
},
77+
submodule: {
78+
'/my': {
79+
repository: {
80+
path: {
81+
'.git': 'gitdir: ../.git/modules/path',
82+
'index.js': 'module.exports = 42;'
83+
},
84+
'.git': {
85+
config:
86+
'[submodule "path"]\n' +
87+
'url = https://github.com/foo/bar\n' +
88+
'active = true',
89+
modules: {
90+
path: {
91+
HEAD: 'ref: refs/heads/master',
92+
refs: {
93+
heads: {
94+
master: 'this_is_the_sha'
95+
}
96+
}
97+
}
98+
}
99+
}
100+
}
101+
}
102+
},
77103
malformed: {
78104
'/my': {
79105
repository: {

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
"glob": "^7.1.2",
4646
"globals-docs": "^2.4.0",
4747
"highlight.js": "^9.15.5",
48+
"ini": "^1.3.5",
4849
"js-yaml": "^3.10.0",
4950
"lodash": "^4.17.10",
5051
"mdast-util-inject": "^1.1.0",
@@ -58,7 +59,6 @@
5859
"remark-html": "^8.0.0",
5960
"remark-reference-links": "^4.0.1",
6061
"remark-toc": "^5.0.0",
61-
"remote-origin-url": "0.4.0",
6262
"resolve": "^1.8.1",
6363
"stream-array": "^1.1.2",
6464
"strip-json-comments": "^2.0.1",

src/git/find_git.js

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,25 @@ const fs = require('fs');
55
* Given a full path to a single file, iterate upwards through the filesystem
66
* to find a directory with a .git file indicating that it is a git repository
77
* @param filename any file within a repository
8-
* @returns repository path
8+
* @returns repository root & its .git folder paths
99
*/
1010
function findGit(filename) {
11-
const paths = filename.split(path.sep);
12-
for (let i = paths.length; i > 0; i--) {
13-
const p = path.resolve(
14-
paths.slice(0, i).join(path.sep) + path.sep + '.git'
15-
);
16-
if (fs.existsSync(p)) {
17-
return p;
11+
let root = path.resolve(filename);
12+
while (root) {
13+
root = path.dirname(root);
14+
let git = path.join(root, '.git');
15+
if (!fs.existsSync(git)) continue;
16+
17+
if (fs.statSync(git).isFile()) {
18+
// git submodule
19+
const matches = fs.readFileSync(git, 'utf8').match(/gitdir: (.*)/);
20+
if (!matches) return null;
21+
git = path.join(root, matches[1]);
1822
}
23+
24+
return { root, git };
1925
}
26+
return null;
2027
}
2128

2229
module.exports = findGit;

src/git/url_prefix.js

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const fs = require('fs');
22
const path = require('path');
33
const gitUrlParse = require('git-url-parse');
4-
const getRemoteOrigin = require('remote-origin-url');
4+
const ini = require('ini');
55

66
/**
77
* Sometimes git will [pack refs](https://git-scm.com/docs/git-pack-refs)
@@ -32,15 +32,15 @@ function parsePackedRefs(packedRefs, branchName) {
3232
* @returns {string} base HTTPS url of the GitHub repository
3333
* @throws {Error} if the root is not a git repo
3434
*/
35-
function getGithubURLPrefix(root) {
35+
function getGithubURLPrefix({ git, root }) {
3636
let sha;
3737
try {
38-
const head = fs.readFileSync(path.join(root, '.git', 'HEAD'), 'utf8');
38+
const head = fs.readFileSync(path.join(git, 'HEAD'), 'utf8');
3939
const branch = head.match(/ref: (.*)/);
4040
if (branch) {
4141
const branchName = branch[1];
42-
const branchFileName = path.join(root, '.git', branchName);
43-
const packedRefsName = path.join(root, '.git', 'packed-refs');
42+
const branchFileName = path.join(git, branchName);
43+
const packedRefsName = path.join(git, 'packed-refs');
4444
if (fs.existsSync(branchFileName)) {
4545
sha = fs.readFileSync(branchFileName, 'utf8');
4646
} else if (fs.existsSync(packedRefsName)) {
@@ -57,7 +57,19 @@ function getGithubURLPrefix(root) {
5757
sha = head;
5858
}
5959
if (sha) {
60-
const parsed = gitUrlParse(getRemoteOrigin.sync(root));
60+
let origin;
61+
if (git.indexOf(root) === 0) {
62+
const config = ini.parse(
63+
fs.readFileSync(path.join(git, 'config'), 'utf8')
64+
);
65+
origin = config['remote "origin"'].url;
66+
} else {
67+
const config = ini.parse(
68+
fs.readFileSync(path.join(git, '..', '..', 'config'), 'utf8')
69+
);
70+
origin = config[`submodule "${path.basename(git)}"`].url;
71+
}
72+
const parsed = gitUrlParse(origin);
6173
parsed.git_suffix = false; // eslint-disable-line
6274
return parsed.toString('https') + '/blob/' + sha.trim() + '/';
6375
}

src/github.js

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,16 @@ const getGithubURLPrefix = require('./git/url_prefix');
1010
* @returns {Object} comment with github inferred
1111
*/
1212
module.exports = function(comment) {
13-
const repoPath = findGit(comment.context.file);
14-
const root = repoPath ? path.dirname(repoPath) : '.';
15-
const urlPrefix = getGithubURLPrefix(root);
16-
const fileRelativePath = comment.context.file
17-
.replace(root + path.sep, '')
18-
.split(path.sep)
19-
.join('/');
13+
const paths = findGit(comment.context.file);
14+
15+
const urlPrefix = paths && getGithubURLPrefix(paths);
2016

2117
if (urlPrefix) {
18+
const fileRelativePath = comment.context.file
19+
.replace(paths.root + path.sep, '')
20+
.split(path.sep)
21+
.join('/');
22+
2223
let startLine;
2324
let endLine;
2425

yarn.lock

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3076,7 +3076,7 @@ inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3:
30763076
version "2.0.3"
30773077
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
30783078

3079-
ini@^1.3.2, ini@^1.3.3, ini@~1.3.0:
3079+
ini@^1.3.2, ini@^1.3.5, ini@~1.3.0:
30803080
version "1.3.5"
30813081
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
30823082

@@ -4868,12 +4868,6 @@ parse-filepath@^1.0.2:
48684868
map-cache "^0.2.0"
48694869
path-root "^0.1.1"
48704870

4871-
parse-git-config@^0.2.0:
4872-
version "0.2.0"
4873-
resolved "https://registry.yarnpkg.com/parse-git-config/-/parse-git-config-0.2.0.tgz#272833fdd15fea146fb75d336d236b963b6ff706"
4874-
dependencies:
4875-
ini "^1.3.3"
4876-
48774871
parse-github-repo-url@^1.3.0:
48784872
version "1.4.1"
48794873
resolved "https://registry.yarnpkg.com/parse-github-repo-url/-/parse-github-repo-url-1.4.1.tgz#9e7d8bb252a6cb6ba42595060b7bf6df3dbc1f50"
@@ -5420,12 +5414,6 @@ remark@^9.0.0:
54205414
remark-stringify "^5.0.0"
54215415
unified "^6.0.0"
54225416

5423-
remote-origin-url@0.4.0:
5424-
version "0.4.0"
5425-
resolved "https://registry.yarnpkg.com/remote-origin-url/-/remote-origin-url-0.4.0.tgz#4d3e2902f34e2d37d1c263d87710b77eb4086a30"
5426-
dependencies:
5427-
parse-git-config "^0.2.0"
5428-
54295417
remove-bom-buffer@^3.0.0:
54305418
version "3.0.0"
54315419
resolved "https://registry.yarnpkg.com/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz#c2bf1e377520d324f623892e33c10cac2c252b53"

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy