Skip to content

Commit 9cfa193

Browse files
committed
Add support for linked worktrees
Within a linked worktree (ie a worktree created via `git worktree add`) `.git` is a file that points to a worktree git dir (within the repo's `.git`). This directory contains `HEAD` and a file that contains the path to the repo's git dir. `findRepo` now returns a POJO with two paths: the worktree git dir and the common git dir. For the base work tree, these paths will be the same. For a linked worktree, they will be different, eg ```javascript { worktreeGitDir: '<GIT_DIR>/worktrees/base', commonGitDir: '<GIT_DIR>', } ``` [Fix #25]
1 parent 409372f commit 9cfa193

File tree

6 files changed

+71
-19
lines changed

6 files changed

+71
-19
lines changed

index.js

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,29 @@ function changeGitDir(newDirName) {
1010
GIT_DIR = newDirName;
1111
}
1212

13+
function findRepoHandleLinkedWorktree(gitPath) {
14+
var stat = fs.statSync(gitPath);
15+
if (stat.isDirectory()) {
16+
return {
17+
// for the base (non-linked) dir, there is no distinction between where we
18+
// find the HEAD file and where we find the rest of .git
19+
worktreeGitDir: gitPath,
20+
commonGitDir: gitPath,
21+
};
22+
} else {
23+
var linkedGitDir = fs.readFileSync(gitPath).toString();
24+
var worktreeGitDir = /gitdir: (.*)/.exec(linkedGitDir)[1];
25+
var commonDirPath = path.join(worktreeGitDir, 'commondir');
26+
var commonDirRelative = fs.readFileSync(commonDirPath).toString().replace(/\r?\n$/, '');
27+
var commonDir = path.resolve(path.join(worktreeGitDir, commonDirRelative));
28+
29+
return {
30+
worktreeGitDir: path.resolve(worktreeGitDir),
31+
commonGitDir: commonDir,
32+
};
33+
}
34+
}
35+
1336
function findRepo(startingPath) {
1437
var gitPath, lastPath;
1538
var currentPath = startingPath;
@@ -20,7 +43,7 @@ function findRepo(startingPath) {
2043
gitPath = path.join(currentPath, GIT_DIR);
2144

2245
if (fs.existsSync(gitPath)) {
23-
return gitPath;
46+
return findRepoHandleLinkedWorktree(gitPath);
2447
}
2548

2649
lastPath = currentPath;
@@ -118,7 +141,7 @@ function findTag(gitPath, sha) {
118141
}
119142

120143
module.exports = function(gitPath) {
121-
gitPath = findRepo(gitPath);
144+
var gitPathInfo = findRepo(gitPath);
122145

123146
var result = {
124147
sha: null,
@@ -133,12 +156,12 @@ module.exports = function(gitPath) {
133156
root: null
134157
};
135158

136-
if (!gitPath) { return result; }
159+
if (!gitPathInfo) { return result; }
137160

138161
try {
139-
result.root = path.resolve(gitPath, '..');
162+
result.root = path.resolve(gitPathInfo.commonGitDir, '..');
140163

141-
var headFilePath = path.join(gitPath, 'HEAD');
164+
var headFilePath = path.join(gitPathInfo.worktreeGitDir, 'HEAD');
142165

143166
if (fs.existsSync(headFilePath)) {
144167
var headFile = fs.readFileSync(headFilePath, {encoding: 'utf8'});
@@ -151,13 +174,13 @@ module.exports = function(gitPath) {
151174
// Find branch and SHA
152175
if (refPath) {
153176
refPath = refPath.trim();
154-
var branchPath = path.join(gitPath, refPath);
177+
var branchPath = path.join(gitPathInfo.commonGitDir, refPath);
155178

156179
result.branch = branchName;
157180
if (fs.existsSync(branchPath)) {
158181
result.sha = fs.readFileSync(branchPath, { encoding: 'utf8' }).trim();
159182
} else {
160-
result.sha = findPackedCommit(gitPath, refPath);
183+
result.sha = findPackedCommit(gitPathInfo.commonGitDir, refPath);
161184
}
162185
} else {
163186
result.sha = branchName;
@@ -166,7 +189,7 @@ module.exports = function(gitPath) {
166189
result.abbreviatedSha = result.sha.slice(0,10);
167190

168191
// Find commit data
169-
var commitData = getCommitData(gitPath, result.sha);
192+
var commitData = getCommitData(gitPathInfo.commonGitDir, result.sha);
170193
if (commitData) {
171194
result = Object.keys(commitData).reduce(function(r, key) {
172195
result[key] = commitData[key];
@@ -175,7 +198,7 @@ module.exports = function(gitPath) {
175198
}
176199

177200
// Find tag
178-
var tag = findTag(gitPath, result.sha);
201+
var tag = findTag(gitPathInfo.commonGitDir, result.sha);
179202
if (tag) {
180203
result.tag = tag;
181204
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
9dac893d5a83c02344d91e79dad8904889aeacb1
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
409372f3bd07c11bfacee3963f48571d675268d7
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../..
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
gitdir: ../dot-git/worktrees/linked

tests/index.js

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,36 +28,61 @@ describe('git-repo-info', function() {
2828
it('finds a repo in the current directory', function() {
2929
process.chdir(repoRoot);
3030

31-
var foundPath = repoInfo._findRepo(repoRoot);
32-
assert.equal(foundPath, path.join(repoRoot, gitDir));
31+
var foundPathInfo = repoInfo._findRepo(repoRoot);
32+
assert.deepEqual(foundPathInfo, {
33+
worktreeGitDir: path.join(repoRoot, gitDir),
34+
commonGitDir: path.join(repoRoot, gitDir),
35+
});
3336
});
3437

3538
it('finds a repo in the parent directory', function() {
3639
process.chdir(path.join(repoRoot, 'foo'));
3740

38-
var foundPath = repoInfo._findRepo(repoRoot);
39-
assert.equal(foundPath, path.join(repoRoot, gitDir));
41+
var foundPathInfo = repoInfo._findRepo(repoRoot);
42+
assert.deepEqual(foundPathInfo, {
43+
worktreeGitDir: path.join(repoRoot, gitDir),
44+
commonGitDir: path.join(repoRoot, gitDir),
45+
});
4046
});
4147

4248
it('finds a repo 2 levels up', function() {
4349
process.chdir(path.join(repoRoot, 'foo', 'bar'));
4450

45-
var foundPath = repoInfo._findRepo(repoRoot);
46-
assert.equal(foundPath, path.join(repoRoot, gitDir));
51+
var foundPathInfo = repoInfo._findRepo(repoRoot);
52+
assert.deepEqual(foundPathInfo, {
53+
worktreeGitDir: path.join(repoRoot, gitDir),
54+
commonGitDir: path.join(repoRoot, gitDir),
55+
});
4756
});
4857

4958
it('finds a repo without an argument', function() {
5059
process.chdir(repoRoot);
5160

52-
var foundPath = repoInfo._findRepo();
53-
assert.equal(foundPath, path.join(repoRoot, gitDir));
61+
var foundPathInfo = repoInfo._findRepo();
62+
assert.deepEqual(foundPathInfo, {
63+
worktreeGitDir: path.join(repoRoot, gitDir),
64+
commonGitDir: path.join(repoRoot, gitDir),
65+
});
5466
});
5567

5668
it('finds a repo 2 levels up (without an argument)', function() {
5769
process.chdir(path.join(repoRoot, 'foo', 'bar'));
5870

59-
var foundPath = repoInfo._findRepo();
60-
assert.equal(foundPath, path.join(repoRoot, gitDir));
71+
var foundPathInfo = repoInfo._findRepo();
72+
assert.deepEqual(foundPathInfo, {
73+
worktreeGitDir: path.join(repoRoot, gitDir),
74+
commonGitDir: path.join(repoRoot, gitDir),
75+
});
76+
});
77+
78+
it('finds a repo via a linked worktree', function() {
79+
process.chdir(path.join(testFixturesPath, 'linked-worktree', 'linked'));
80+
81+
var foundPathInfo = repoInfo._findRepo();
82+
assert.deepEqual(foundPathInfo, {
83+
worktreeGitDir: path.join(testFixturesPath, 'linked-worktree', 'dot-git', 'worktrees', 'linked'),
84+
commonGitDir: path.join(testFixturesPath, 'linked-worktree', 'dot-git'),
85+
});
6186
});
6287
});
6388

0 commit comments

Comments
 (0)