difit 3.0.2 → 3.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,6 +9,11 @@ export declare class FileWatcherService {
9
9
  constructor();
10
10
  start(diffMode: DiffMode, watchPath: string, debounceMs?: number, onCacheInvalidate?: () => void): Promise<void>;
11
11
  private setupWatchers;
12
+ /**
13
+ * Resolve the actual git directory path, handling git worktrees.
14
+ * Uses git rev-parse --git-dir which correctly handles both normal repos and worktrees.
15
+ */
16
+ private resolveGitDir;
12
17
  private shouldIgnoreEvent;
13
18
  private matchesPattern;
14
19
  private isRelevantGitFile;
@@ -1,4 +1,4 @@
1
- import { join } from 'path';
1
+ import { join, resolve } from 'path';
2
2
  import { subscribe } from '@parcel/watcher';
3
3
  import { simpleGit } from 'simple-git';
4
4
  import { DiffMode } from '../types/watch.js';
@@ -60,7 +60,11 @@ export class FileWatcherService {
60
60
  }
61
61
  async setupWatchers(modeConfig, basePath) {
62
62
  for (const watchPath of modeConfig.watchPaths) {
63
- const fullPath = join(basePath, watchPath);
63
+ let fullPath = join(basePath, watchPath);
64
+ // Resolve git worktree path for .git directory
65
+ if (watchPath === '.git') {
66
+ fullPath = await this.resolveGitDir(basePath);
67
+ }
64
68
  try {
65
69
  const subscription = (await subscribe(fullPath, async (err, events) => {
66
70
  if (err) {
@@ -101,6 +105,21 @@ export class FileWatcherService {
101
105
  }
102
106
  }
103
107
  }
108
+ /**
109
+ * Resolve the actual git directory path, handling git worktrees.
110
+ * Uses git rev-parse --git-dir which correctly handles both normal repos and worktrees.
111
+ */
112
+ async resolveGitDir(basePath) {
113
+ try {
114
+ const git = simpleGit(basePath);
115
+ const gitDir = await git.revparse(['--git-dir']);
116
+ return resolve(basePath, gitDir.trim());
117
+ }
118
+ catch {
119
+ // Fallback to default .git path
120
+ return join(basePath, '.git');
121
+ }
122
+ }
104
123
  shouldIgnoreEvent(filePath, ignorePatterns) {
105
124
  return ignorePatterns.some((pattern) => {
106
125
  // Handle negation patterns (e.g., "!.git/index")
@@ -9,6 +9,7 @@ vi.mock('@parcel/watcher', () => ({
9
9
  vi.mock('simple-git', () => ({
10
10
  simpleGit: vi.fn(() => ({
11
11
  checkIgnore: vi.fn(),
12
+ revparse: vi.fn().mockResolvedValue('.git'),
12
13
  })),
13
14
  }));
14
15
  const { subscribe } = await import('@parcel/watcher');
@@ -202,6 +203,7 @@ describe('FileWatcherService', () => {
202
203
  for (const { mode, expectedType } of modes) {
203
204
  const mockGit = {
204
205
  checkIgnore: vi.fn().mockRejectedValue(new Error('No ignored files')),
206
+ revparse: vi.fn().mockResolvedValue('.git'),
205
207
  };
206
208
  vi.mocked(simpleGit).mockReturnValue(mockGit);
207
209
  await fileWatcher.start(mode, '/test/path', 300);
@@ -222,4 +224,54 @@ describe('FileWatcherService', () => {
222
224
  }
223
225
  });
224
226
  });
227
+ describe('git worktree support', () => {
228
+ it('should use resolved git directory for normal repository', async () => {
229
+ const mockGit = {
230
+ checkIgnore: vi.fn(),
231
+ revparse: vi.fn().mockResolvedValue('.git'),
232
+ };
233
+ vi.mocked(simpleGit).mockReturnValue(mockGit);
234
+ await fileWatcher.start(DiffMode.DEFAULT, '/test/path', 300);
235
+ expect(mockGit.revparse).toHaveBeenCalledWith(['--git-dir']);
236
+ expect(subscribe).toHaveBeenCalledWith('/test/path/.git', expect.any(Function), {
237
+ ignore: ['.git/objects/**', '.git/refs/**', 'node_modules/**'],
238
+ });
239
+ });
240
+ it('should resolve worktree git directory path', async () => {
241
+ const mockGit = {
242
+ checkIgnore: vi.fn(),
243
+ revparse: vi.fn().mockResolvedValue('/main/repo/.git/worktrees/feature'),
244
+ };
245
+ vi.mocked(simpleGit).mockReturnValue(mockGit);
246
+ await fileWatcher.start(DiffMode.DEFAULT, '/worktree/path', 300);
247
+ expect(mockGit.revparse).toHaveBeenCalledWith(['--git-dir']);
248
+ expect(subscribe).toHaveBeenCalledWith('/main/repo/.git/worktrees/feature', expect.any(Function), {
249
+ ignore: ['.git/objects/**', '.git/refs/**', 'node_modules/**'],
250
+ });
251
+ });
252
+ it('should resolve relative git directory path', async () => {
253
+ const mockGit = {
254
+ checkIgnore: vi.fn(),
255
+ revparse: vi.fn().mockResolvedValue('../.git/worktrees/feature'),
256
+ };
257
+ vi.mocked(simpleGit).mockReturnValue(mockGit);
258
+ await fileWatcher.start(DiffMode.DEFAULT, '/repos/worktree', 300);
259
+ expect(mockGit.revparse).toHaveBeenCalledWith(['--git-dir']);
260
+ // path.resolve('/repos/worktree', '../.git/worktrees/feature') = '/repos/.git/worktrees/feature'
261
+ expect(subscribe).toHaveBeenCalledWith('/repos/.git/worktrees/feature', expect.any(Function), {
262
+ ignore: ['.git/objects/**', '.git/refs/**', 'node_modules/**'],
263
+ });
264
+ });
265
+ it('should fallback to default .git path on error', async () => {
266
+ const mockGit = {
267
+ checkIgnore: vi.fn(),
268
+ revparse: vi.fn().mockRejectedValue(new Error('Not a git repository')),
269
+ };
270
+ vi.mocked(simpleGit).mockReturnValue(mockGit);
271
+ await fileWatcher.start(DiffMode.DEFAULT, '/test/path', 300);
272
+ expect(subscribe).toHaveBeenCalledWith('/test/path/.git', expect.any(Function), {
273
+ ignore: ['.git/objects/**', '.git/refs/**', 'node_modules/**'],
274
+ });
275
+ });
276
+ });
225
277
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "difit",
3
- "version": "3.0.2",
3
+ "version": "3.0.3",
4
4
  "description": "A lightweight command-line tool that spins up a local web server to display Git commit diffs in a GitHub-like Files changed view",
5
5
  "type": "module",
6
6
  "engines": {