Skip to content

Commit a5efd94

Browse files
authored
Merge pull request #1968 from SUI-Components/fix/enable-worktress-sui-precommit
Fix/enable worktrees sui precommit
2 parents ed89a67 + 1b602cf commit a5efd94

File tree

2 files changed

+86
-27
lines changed

2 files changed

+86
-27
lines changed

packages/sui-precommit/Readme.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ It provides:
1515
$ npm install @s-ui/precommit --save-dev
1616
```
1717

18+
## Git Worktree Support
19+
20+
`@s-ui/precommit` automatically detects and supports git worktrees. Whether you're working in a normal git repository or a worktree, the hooks will be installed in the correct location automatically.
21+
22+
When used in a worktree, hooks are installed in the main repository's worktree-specific hooks directory, ensuring proper git hook execution.
23+
1824
## CLI
1925

2026
### `$ sui-precommit`

packages/sui-precommit/bin/sui-precommit.js

Lines changed: 80 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
// @ts-check
44

5-
import {existsSync, readFileSync, writeFileSync} from 'fs'
5+
import {existsSync, mkdirSync, readFileSync, statSync, writeFileSync} from 'fs'
66
import {chmod, writeFile} from 'fs/promises'
77
import {join} from 'path'
88

@@ -22,42 +22,95 @@ const {name} = readPackageJson()
2222
** - for CI and the same precommit package
2323
** - for the `@s-ui/precommit` pkg itself */
2424

25+
/**
26+
* Get the actual git directory path, handling both normal repos and worktrees.
27+
* In a worktree, .git is a file containing a reference to the actual git directory.
28+
* @param {string} gitPath - Path to the .git file or directory
29+
* @returns {string} Path to the actual git directory
30+
*/
31+
function getGitDirectory(gitPath) {
32+
const gitStat = statSync(gitPath)
33+
34+
if (gitStat.isDirectory()) {
35+
// Normal git repository
36+
return gitPath
37+
} else if (gitStat.isFile()) {
38+
// Git worktree - read and parse the gitdir reference
39+
const gitFileContent = readFileSync(gitPath, {encoding: 'utf8'}).trim()
40+
41+
// Expected format: "gitdir: /path/to/actual/.git/worktrees/name"
42+
const match = gitFileContent.match(/^gitdir:\s*(.+)$/)
43+
44+
if (!match) {
45+
throw new Error(`Invalid .git file format: ${gitFileContent}`)
46+
}
47+
48+
const gitDir = match[1].trim()
49+
50+
// Validate the referenced directory exists
51+
if (!existsSync(gitDir)) {
52+
throw new Error(`Git directory referenced in .git file does not exist: ${gitDir}`)
53+
}
54+
55+
return gitDir
56+
} else {
57+
throw new Error('.git exists but is neither a file nor a directory')
58+
}
59+
}
60+
2561
if (CI === false && name !== '@s-ui/precommit') {
26-
const hooksPath = join(cwd, '.git')
62+
const gitPath = join(cwd, '.git')
2763

2864
/**
29-
* Check if hooks directory exists. If not, it means
65+
* Check if .git exists. If not, it means
3066
* the project is not a Git Repository and it doesn't
3167
* make sense to install Git hooks for now.
3268
*/
33-
if (!existsSync(hooksPath)) {
69+
if (!existsSync(gitPath)) {
3470
log('No .git folder found. Skipping precommit hooks installation...')
3571
process.exit(0)
3672
}
3773

38-
log('Installing precommit hooks...')
39-
40-
const commitMsgPath = `${hooksPath}/hooks/commit-msg`
41-
const preCommitPath = `${hooksPath}/hooks/pre-commit`
42-
const prePushPath = `${hooksPath}/hooks/pre-push`
43-
44-
Promise.all([
45-
writeFile(commitMsgPath, '#!/bin/sh\nnpm run commit-msg --if-present'),
46-
writeFile(preCommitPath, '#!/bin/sh\nnpm run pre-commit --if-present'),
47-
writeFile(prePushPath, '#!/bin/sh\nnpm run pre-push --if-present')
48-
]).then(() => Promise.all([chmod(commitMsgPath, '755'), chmod(preCommitPath, '755'), chmod(prePushPath, '755')]))
49-
50-
try {
51-
addToPackageJson('sui-lint js --staged && sui-lint sass --staged', 'scripts.lint', false)
52-
addToPackageJson('echo "Skipping tests as they are not present"', 'scripts.test', false)
53-
addToPackageJson('npm run lint', 'scripts.pre-commit', false)
54-
addToPackageJson('npm run test', 'scripts.pre-push', false)
55-
removeFromPackageJson('husky')
56-
} catch (err) {
57-
log(err.message)
58-
log('[@s-ui/precommit] Installation has FAILED.')
59-
process.exit(1)
60-
}
74+
Promise.resolve()
75+
.then(() => {
76+
// Get the actual git directory (handles both normal repos and worktrees)
77+
const gitDirectory = getGitDirectory(gitPath)
78+
const hooksPath = join(gitDirectory, 'hooks')
79+
80+
// Ensure hooks directory exists (important for worktrees)
81+
if (!existsSync(hooksPath)) {
82+
mkdirSync(hooksPath, {recursive: true})
83+
log('Created hooks directory...')
84+
}
85+
86+
log('Installing precommit hooks...')
87+
88+
const commitMsgPath = join(hooksPath, 'commit-msg')
89+
const preCommitPath = join(hooksPath, 'pre-commit')
90+
const prePushPath = join(hooksPath, 'pre-push')
91+
92+
return {commitMsgPath, preCommitPath, prePushPath}
93+
})
94+
.then(({commitMsgPath, preCommitPath, prePushPath}) =>
95+
Promise.all([
96+
writeFile(commitMsgPath, '#!/bin/sh\nnpm run commit-msg --if-present'),
97+
writeFile(preCommitPath, '#!/bin/sh\nnpm run pre-commit --if-present'),
98+
writeFile(prePushPath, '#!/bin/sh\nnpm run pre-push --if-present')
99+
]).then(() => Promise.all([chmod(commitMsgPath, '755'), chmod(preCommitPath, '755'), chmod(prePushPath, '755')]))
100+
)
101+
.then(() => {
102+
// Add package.json modifications
103+
addToPackageJson('sui-lint js --staged && sui-lint sass --staged', 'scripts.lint', false)
104+
addToPackageJson('echo "Skipping tests as they are not present"', 'scripts.test', false)
105+
addToPackageJson('npm run lint', 'scripts.pre-commit', false)
106+
addToPackageJson('npm run test', 'scripts.pre-push', false)
107+
removeFromPackageJson('husky')
108+
})
109+
.catch(err => {
110+
log(err.message)
111+
log('[@s-ui/precommit] Installation has FAILED.')
112+
process.exit(1)
113+
})
61114
}
62115

63116
function log(...args) {

0 commit comments

Comments
 (0)